diff --git a/SPECS/kernel-headers/kernel-headers.signatures.json b/SPECS/kernel-headers/kernel-headers.signatures.json index f6160bf56..6cee9b206 100644 --- a/SPECS/kernel-headers/kernel-headers.signatures.json +++ b/SPECS/kernel-headers/kernel-headers.signatures.json @@ -1,5 +1,5 @@ { "Signatures": { - "linux-6.17.11.tar.gz": "820dd3cacc1d853becb9d1051c4ba5e75442633378a63c244fdf179d0b28f4ac" + "linux-6.18.15.tar.gz": "9d18995c14c96a269e18777be65e8d7712c47f56f9709bbceca846aae58c7fe6" } } diff --git a/SPECS/kernel-headers/kernel-headers.spec b/SPECS/kernel-headers/kernel-headers.spec index 3d93ec975..76f22ad99 100644 --- a/SPECS/kernel-headers/kernel-headers.spec +++ b/SPECS/kernel-headers/kernel-headers.spec @@ -13,14 +13,14 @@ Summary: Linux API header files Name: kernel-headers -Version: 6.17.11 -Release: 2%{?dist} +Version: 6.18.15 +Release: 1%{?dist} License: GPLv2 Vendor: Intel Corporation Distribution: Edge Microvisor Toolkit Group: System Environment/Kernel URL: https://www.kernel.org/pub/linux/kernel -Source0: https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.17.11.tar.gz +Source0: https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.18.15.tar.gz # Historical name shipped by other distros Provides: glibc-kernheaders = %{version}-%{release} BuildArch: noarch @@ -41,7 +41,7 @@ cross-glibc package. %endif %prep -%setup -q -n linux-6.17.11 +%setup -q -n linux-6.18.15 %build make mrproper @@ -76,6 +76,10 @@ done %endif %changelog +* Mon Mar 16 2026 Lishan Liu - 6.18.15-1 +- Update kernel to 6.18.15-1 +- lts-v6.18.15-emt-260310T050801Z + * Tue Feb 03 2026 Lishan Liu - 6.17.11-2 - Update kernel to 6.17.11-2 - mainline-v6.17.11-emt-overlay-cve-260128T080735Z diff --git a/SPECS/kernel-rt/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio b/SPECS/kernel-rt/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio index f2781d98b..ccce3904b 100644 --- a/SPECS/kernel-rt/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio +++ b/SPECS/kernel-rt/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio @@ -1,7 +1,7 @@ -From e8f667cf51d2a63c169c148e9ab807f298b38c9d Mon Sep 17 00:00:00 2001 +From 759ecfe0aec70fff292da099330c5f1794815e74 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Tue, 11 Nov 2025 18:35:12 +0800 -Subject: [PATCH] ASoC: Intel: sof_rt5682: Add quirk override support +Subject: [PATCH 1/2] ASoC: Intel: sof_rt5682: Add quirk override support adding quirk override support to configure different quirk configuration at runtime. @@ -15,7 +15,7 @@ Signed-off-by: Mark Brown 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c -index 4994aaccc583..6ca114c30ae1 100644 +index 4994aaccc583a..6ca114c30ae16 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -35,6 +35,10 @@ diff --git a/SPECS/kernel-rt/0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio b/SPECS/kernel-rt/0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio deleted file mode 100644 index fda9f6c36..000000000 --- a/SPECS/kernel-rt/0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio +++ /dev/null @@ -1,75 +0,0 @@ -From 0664046ef563fcf1999732063a467a223544b79f Mon Sep 17 00:00:00 2001 -From: Balamurugan C -Date: Tue, 18 Nov 2025 13:53:41 +0530 -Subject: [PATCH] ASoC: SOF: Intel: hda: Only check SSP MCLK mask in case of - IPC3 - -IPC4 is using the NHLT blob itself and sends the SSP blob from it -directly to the firmware, there is no need for the MCLK quirk -based on the SSP blob since the SSP blob is in use. - -At the same time reword the error, info and debug messages for clarity. - -Signed-off-by: Peter Ujfalusi -Signed-off-by: Balamurugan C ---- - sound/soc/sof/intel/hda.c | 36 ++++++++++++++++++++++-------------- - 1 file changed, 22 insertions(+), 14 deletions(-) - -diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c -index c387efec41e9..46b1892e173b 100644 ---- a/sound/soc/sof/intel/hda.c -+++ b/sound/soc/sof/intel/hda.c -@@ -1403,7 +1403,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) - mach->mach_params.i2s_link_mask) { - const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); - int ssp_num; -- int mclk_mask; - - if (hweight_long(mach->mach_params.i2s_link_mask) > 1 && - !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB)) -@@ -1428,19 +1427,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) - - sof_pdata->tplg_filename = tplg_filename; - -- mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num); -- -- if (mclk_mask < 0) { -- dev_err(sdev->dev, "Invalid MCLK configuration\n"); -- return NULL; -- } -- -- dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask); -- -- if (mclk_mask) { -- dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask); -- sdev->mclk_id_override = true; -- sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; -+ if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) { -+ int mclk_mask = check_nhlt_ssp_mclk_mask(sdev, -+ ssp_num); -+ -+ if (mclk_mask < 0) { -+ dev_err(sdev->dev, -+ "Invalid MCLK configuration for SSP%d\n", -+ ssp_num); -+ return NULL; -+ } -+ -+ if (mclk_mask) { -+ sdev->mclk_id_override = true; -+ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; -+ dev_info(sdev->dev, -+ "SSP%d to use MCLK id %d (mask: %#x)\n", -+ ssp_num, sdev->mclk_id_quirk, mclk_mask); -+ } else { -+ dev_dbg(sdev->dev, -+ "MCLK mask is empty for SSP%d in NHLT\n", -+ ssp_num); -+ } - } - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-Add-security.md-file.security b/SPECS/kernel-rt/0001-Add-security.md-file.security index 31f700a7d..fed8a5fe7 100644 --- a/SPECS/kernel-rt/0001-Add-security.md-file.security +++ b/SPECS/kernel-rt/0001-Add-security.md-file.security @@ -1,7 +1,7 @@ -From d765be87faa2993a69b07d474d062c37bddb686f Mon Sep 17 00:00:00 2001 +From d8be3f2811d6b2942f2c0ddfeb938db5fa6f18df Mon Sep 17 00:00:00 2001 From: Lili Li Date: Tue, 24 Oct 2023 10:33:36 +0800 -Subject: [PATCH] Add security.md file +Subject: [PATCH 1/2] Add security.md file Add security.md file to open source repository. @@ -13,7 +13,7 @@ Signed-off-by: Lili Li diff --git a/security.md b/security.md new file mode 100644 -index 000000000000..98bca5b7ed9f +index 0000000000000..98bca5b7ed9fa --- /dev/null +++ b/security.md @@ -0,0 +1,5 @@ diff --git a/SPECS/kernel-rt/0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security b/SPECS/kernel-rt/0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security deleted file mode 100644 index 1050ec751..000000000 --- a/SPECS/kernel-rt/0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security +++ /dev/null @@ -1,343 +0,0 @@ -From 69c9d8b08c91b9a8341588e5e20873856a84d255 Mon Sep 17 00:00:00 2001 -From: Adam Pawlicki -Date: Thu, 13 Nov 2025 10:32:40 +0100 -Subject: [PATCH] Add updated TPR (TXT Protected Regions) support to kernel. - -This commit applies a TPR-support patch from Tboot Live Image project. -Its intent is to make kernel aware of the TPR memory and to disable -it before proceeding with system boot so that kernel can establish its -own DMA protection. - -Signed-off-by: AdamX Pawlicki -Signed-off-by: Mateusz Mowka ---- - arch/x86/kernel/tboot.c | 109 +++++++++++++++++++++++++++++++++++- - drivers/iommu/intel/dmar.c | 8 +++ - drivers/iommu/intel/iommu.c | 7 ++- - include/acpi/actbl1.h | 86 ++++++++++++++++++++++++++++ - include/linux/tboot.h | 6 ++ - 5 files changed, 214 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c -index 46b8f1f16676..04929aba6a5c 100644 ---- a/arch/x86/kernel/tboot.c -+++ b/arch/x86/kernel/tboot.c -@@ -18,6 +18,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -453,8 +454,14 @@ struct sha1_hash { - u8 hash[SHA1_SIZE]; - }; - -+struct heap_ext_data_elt { -+ u32 type; -+ u32 size; -+ u8 data[]; -+} __packed; -+ - struct sinit_mle_data { -- u32 version; /* currently 6 */ -+ u32 version; /* currently 9 */ - struct sha1_hash bios_acm_id; - u32 edx_senter_flags; - u64 mseg_valid; -@@ -469,8 +476,18 @@ struct sinit_mle_data { - u32 mdrs_off; - u32 num_vtd_dmars; - u32 vtd_dmars_off; -+ u32 proc_scrtm_status; /* version 8 or later only*/ -+ struct heap_ext_data_elt ext_data_elts[]; - } __packed; - -+#define HEAP_EXTDATA_TYPE_DTPR 14 -+ -+struct acpi_dtpr_serialize_req { -+ u64 sts : 1; -+ u64 ctrl : 1; -+ u64 unused : 62; -+}; -+ - struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl) - { - void *heap_base, *heap_ptr, *config; -@@ -514,3 +531,93 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb - - return dmar_tbl; - } -+ -+struct acpi_table_dtpr *tboot_get_dtpr_table(void) -+{ -+ void *heap_base, *heap_ptr, *config; -+ struct sinit_mle_data *sinit_mle; -+ struct heap_ext_data_elt *elt; -+ u64 sinit_mle_size; -+ -+ if (!tboot_enabled()) -+ return NULL; -+ /* -+ * ACPI tables may not be DMA protected by tboot, so use DMAR copy -+ * SINIT saved in SinitMleData in TXT heap (which is DMA protected) -+ */ -+ -+ /* map config space in order to get heap addr */ -+ config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES * -+ PAGE_SIZE); -+ if (!config) -+ return NULL; -+ -+ /* now map TXT heap */ -+ heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE), -+ *(u64 *)(config + TXTCR_HEAP_SIZE)); -+ iounmap(config); -+ if (!heap_base) -+ return NULL; -+ -+ /* walk heap to SinitMleData */ -+ /* skip BiosData */ -+ heap_ptr = heap_base + *(u64 *)heap_base; -+ /* skip OsMleData */ -+ heap_ptr += *(u64 *)heap_ptr; -+ /* skip OsSinitData */ -+ heap_ptr += *(u64 *)heap_ptr; -+ /* now points to SinitMleDataSize; set to SinitMleData */ -+ sinit_mle_size = *(u64 *)heap_ptr; -+ heap_ptr += sizeof(u64); -+ -+ sinit_mle = (struct sinit_mle_data *)heap_ptr; -+ if (sinit_mle->version < 9) -+ return NULL; -+ -+ elt = sinit_mle->ext_data_elts; -+ while (elt->type != HEAP_EXTDATA_TYPE_DTPR) { -+ elt = (void *)elt + elt->size; -+ if ((u64)elt > (u64)sinit_mle + sinit_mle_size) -+ return NULL; -+ } -+ return (struct acpi_table_dtpr *)elt->data; -+} -+ -+static bool tboot_tpr_enabled; -+void tboot_parse_dtpr_table(void) -+{ -+ struct acpi_table_dtpr *dtpr; -+ struct acpi_dtpr_instance *tpr_inst; -+ u32 *instance_cnt; -+ u32 i, j; -+ -+ if (!tboot_enabled()) -+ return; -+ dtpr = tboot_get_dtpr_table(); -+ if (dtpr == NULL) -+ return; -+ tboot_tpr_enabled = 1; -+ instance_cnt = (u32 *)(dtpr + 1); -+ tpr_inst = (struct acpi_dtpr_instance *)(instance_cnt + 1); -+ for (i = 0; i < *instance_cnt; ++i) { -+ for (j = 0; j < tpr_inst->tpr_cnt; ++j) { -+ uint64_t *base = ioremap(tpr_inst->tpr_array[j].base, 16); -+ -+ if (base != NULL) { -+ pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", -+ i, j, readq(base), readq(base + 1)); -+ *base |= (1 << 4); -+ iounmap(base); -+ } -+ } -+ tpr_inst = (struct acpi_dtpr_instance *)((u8 *)tpr_inst -+ + sizeof(*tpr_inst) + j*sizeof(*(tpr_inst->tpr_array))); -+ } -+ if (tboot_tpr_enabled) -+ pr_debug("TPR protection detected, PMR will be disabled\n"); -+} -+ -+bool tboot_is_tpr_enabled(void) -+{ -+ return tboot_tpr_enabled; -+} -diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c -index ec975c73cfe6..9c6f93cf1b3f 100644 ---- a/drivers/iommu/intel/dmar.c -+++ b/drivers/iommu/intel/dmar.c -@@ -635,6 +635,7 @@ static int __init - parse_dmar_table(void) - { - struct acpi_table_dmar *dmar; -+ struct acpi_table_dtpr *dtpr; - int drhd_count = 0; - int ret; - struct dmar_res_callback cb = { -@@ -670,6 +671,13 @@ parse_dmar_table(void) - return -EINVAL; - } - -+ dtpr = tboot_get_dtpr_table(); -+ if (dtpr) { -+ //TPR is enabled -+ //This will also tell not to establish IOMMU PMRs -+ tboot_parse_dtpr_table(); -+ } -+ - pr_info("Host address width %d\n", dmar->width + 1); - ret = dmar_walk_dmar_table(dmar, &cb); - if (ret == 0 && drhd_count == 0) -diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c -index dff2d895b8ab..ce84b5e0a9a4 100644 ---- a/drivers/iommu/intel/iommu.c -+++ b/drivers/iommu/intel/iommu.c -@@ -3017,6 +3017,11 @@ static __init int tboot_force_iommu(void) - if (!tboot_enabled()) - return 0; - -+ //If TPR is enabled we don't need to force IOMMU, -+ //TPR set by SINIT ACM will take care of DMA protection -+ if (tboot_is_tpr_enabled()) -+ return 0; -+ - if (no_iommu || dmar_disabled) - pr_warn("Forcing Intel-IOMMU to enabled\n"); - -@@ -3074,7 +3079,7 @@ int __init intel_iommu_init(void) - * calling SENTER, but the kernel is expected to reset/tear - * down the PMRs. - */ -- if (intel_iommu_tboot_noforce) { -+ if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) { - for_each_iommu(iommu, drhd) - iommu_disable_protect_mem_regions(iommu); - } -diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h -index 99fd1588ff38..0fc38879c428 100644 ---- a/include/acpi/actbl1.h -+++ b/include/acpi/actbl1.h -@@ -47,6 +47,7 @@ - #define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ - #define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */ - #define ACPI_SIG_MSCT "MSCT" /* Maximum System Characteristics Table */ -+#define ACPI_SIG_DTPR "DTPR" /* TXT DMA Protection Ranges reporting table */ - - #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ - #define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ -@@ -1972,6 +1973,91 @@ struct acpi_ibft_target { - u16 reverse_chap_secret_offset; - }; - -+/******************************************************************************* -+ * -+ * DTPR - DMA TPR Reporting -+ * Version 1 -+ * -+ * Conforms to "Intel TXT DMA Protection Ranges", -+ * Version xxx, April 2021 -+ * -+ ******************************************************************************/ -+ -+struct acpi_table_dtpr { -+ struct acpi_table_header header; -+ u32 flags; // 36 -+}; -+ -+struct acpi_tpr_array { -+ u64 base; -+}; -+ -+struct acpi_dtpr_instance { -+ u32 flags; -+ u32 tpr_cnt; -+ struct acpi_tpr_array tpr_array[]; -+}; -+ -+/******************************************************************************* -+ * TPRn_BASE -+ * -+ * Specifies the start address of TPRn region. TPR region address and size must -+ * be with 1MB resolution. These bits are compared with the result of the -+ * TPRn_LIMIT[63:20] * applied to the incoming address, to determine if an -+ * access fall within the TPRn defined region. -+ ******************************************************************************/ -+struct acpi_dtprn_base_reg { -+ u64 reserved0 : 3; -+ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) -+ u64 enable : 1; // 0 == range enabled, 1 == range disabled -+ u64 reserved1 : 15; -+ // Minimal TPRn_Base resolution is 1MB. -+ // Applied to the incoming address, to determine if an access -+ // fall within the TPRn defined region. -+ // Width is determined by a bus width which can be obtained -+ // via CPUID function 0x80000008. -+ u64 tpr_base_rw : 44; -+ //u64 unused : 1; -+}; -+ -+/******************************************************************************* -+ * TPRn_LIMIT -+ * -+ * This register defines an isolated region of memory that can be enabled -+ * to prohibit certain system agents from accessing memory. When an agent -+ * sends a request upstream, whether snooped or not, a TPR prevents that -+ * transaction from changing the state of memory. -+ ******************************************************************************/ -+ -+struct acpi_dtprn_limit_reg { -+ u64 reserved0 : 3; -+ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) -+ u64 enable : 1; // 0 == range enabled, 1 == range disabled -+ u64 reserved1 : 15; -+ // Minimal TPRn_Limit resolution is 1MB. -+ // These bits define TPR limit address. -+ // Width is determined by a bus width. -+ u64 tpr_limit_rw : 44; -+ //u64 unused : 1; -+}; -+ -+/******************************************************************************* -+ * SERIALIZE_REQUEST -+ * -+ * This register is used to request serialization of non-coherent DMA -+ * transactions. OS shall issue it before changing of TPR settings -+ * (base / size). -+ ******************************************************************************/ -+ -+struct acpi_tpr_serialize_request { -+ u64 sts : 1; // Status of serialization request (RO) -+ // 0 == register idle, 1 == serialization in progress -+ u64 ctrl : 1; // Control field to initiate serialization (RW) -+ // 0 == normal, 1 == initialize serialization -+ // (self-clear to allow multiple serialization requests) -+ u64 unused : 62; -+}; -+ - /* Reset to default packing */ - - #pragma pack() -diff --git a/include/linux/tboot.h b/include/linux/tboot.h -index d2279160ef39..95f784b0bde0 100644 ---- a/include/linux/tboot.h -+++ b/include/linux/tboot.h -@@ -126,6 +126,9 @@ extern void tboot_probe(void); - extern void tboot_shutdown(u32 shutdown_type); - extern struct acpi_table_header *tboot_get_dmar_table( - struct acpi_table_header *dmar_tbl); -+extern struct acpi_table_dtpr *tboot_get_dtpr_table(void); -+extern void tboot_parse_dtpr_table(void); -+extern bool tboot_is_tpr_enabled(void); - - #else - -@@ -135,6 +138,9 @@ extern struct acpi_table_header *tboot_get_dmar_table( - #define tboot_sleep(sleep_state, pm1a_control, pm1b_control) \ - do { } while (0) - #define tboot_get_dmar_table(dmar_tbl) (dmar_tbl) -+#define tboot_get_dtpr_table() 0 -+#define tboot_parse_dtpr_table() do { } while (0) -+#define tboot_is_tpr_enabled() 0 - - #endif /* !CONFIG_INTEL_TXT */ - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss b/SPECS/kernel-rt/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss deleted file mode 100644 index 0bbe3cb75..000000000 --- a/SPECS/kernel-rt/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss +++ /dev/null @@ -1,72 +0,0 @@ -From 3a57f124f860a2bdaeb36797ef296be6191bcbfb Mon Sep 17 00:00:00 2001 -From: kalladax -Date: Thu, 21 Aug 2025 12:34:05 +0300 -Subject: [PATCH] Added spi_set_cs() for more stable r/w operations in SPI Mode - 3 - -Signed-off-by: kalladax ---- - drivers/spi/spi-pxa2xx.c | 4 ++++ - drivers/spi/spi.c | 3 ++- - include/linux/spi/spi.h | 2 ++ - 3 files changed, 8 insertions(+), 1 deletion(-) - -diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c -index 06711a62fa3d..84e3eba21e77 100644 ---- a/drivers/spi/spi-pxa2xx.c -+++ b/drivers/spi/spi-pxa2xx.c -@@ -1047,6 +1047,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, TIMOUT_DFLT); - -+ spi_set_cs(spi, false, false); -+ - /* First set CR1 without interrupt and service enables */ - pxa2xx_spi_update(drv_data, SSCR1, change_mask, cr1); - -@@ -1056,6 +1058,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, - /* Restart the SSP */ - pxa_ssp_enable(drv_data->ssp); - -+ spi_set_cs(spi, true, false); -+ - if (is_mmp2_ssp(drv_data)) { - u8 tx_level = read_SSSR_bits(drv_data, SSSR_TFL_MASK) >> 8; - -diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c -index a388f372b27a..31dac99b496e 100644 ---- a/drivers/spi/spi.c -+++ b/drivers/spi/spi.c -@@ -1067,7 +1067,7 @@ static void spi_toggle_csgpiod(struct spi_device *spi, u8 idx, bool enable, bool - spi_delay_exec(&spi->cs_inactive, NULL); - } - --static void spi_set_cs(struct spi_device *spi, bool enable, bool force) -+void spi_set_cs(struct spi_device *spi, bool enable, bool force) - { - bool activate = enable; - u8 idx; -@@ -1120,6 +1120,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) - spi_delay_exec(&spi->cs_inactive, NULL); - } - } -+EXPORT_SYMBOL_GPL(spi_set_cs); - - #ifdef CONFIG_HAS_DMA - static int spi_map_buf_attrs(struct spi_controller *ctlr, struct device *dev, -diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h -index e9ea43234d9a..0dd8b227ab12 100644 ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -828,6 +828,8 @@ extern int spi_controller_resume(struct spi_controller *ctlr); - extern struct spi_message *spi_get_next_queued_message(struct spi_controller *ctlr); - extern void spi_finalize_current_message(struct spi_controller *ctlr); - extern void spi_finalize_current_transfer(struct spi_controller *ctlr); -+/* SPI driver calls this function to assert/deassert the chip select */ -+extern void spi_set_cs(struct spi_device *spi, bool enable, bool force); - - /* Helper calls for driver to timestamp transfer */ - void spi_take_timestamp_pre(struct spi_controller *ctlr, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss b/SPECS/kernel-rt/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss new file mode 100644 index 000000000..fbae38532 --- /dev/null +++ b/SPECS/kernel-rt/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss @@ -0,0 +1,72 @@ +From d3d0312fa3d24aa193d72fdfc9cac413e88c51b6 Mon Sep 17 00:00:00 2001 +From: Balamurugan C +Date: Tue, 24 Feb 2026 12:03:21 +0530 +Subject: [PATCH] Added spi_set_cs() for more stable r/w operations in SPI mode + 3 + +Signed-off-by: Balamurugan C +--- + drivers/spi/spi-pxa2xx.c | 4 ++++ + drivers/spi/spi.c | 3 ++- + include/linux/spi/spi.h | 2 ++ + 3 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c +index ec7117a94d5f1..d71304cdb6c79 100644 +--- a/drivers/spi/spi-pxa2xx.c ++++ b/drivers/spi/spi-pxa2xx.c +@@ -1047,6 +1047,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, + if (!pxa25x_ssp_comp(drv_data)) + pxa2xx_spi_write(drv_data, SSTO, TIMOUT_DFLT); + ++ spi_set_cs(spi, false, false); ++ + /* First set CR1 without interrupt and service enables */ + pxa2xx_spi_update(drv_data, SSCR1, change_mask, cr1); + +@@ -1056,6 +1058,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, + /* Restart the SSP */ + pxa_ssp_enable(drv_data->ssp); + ++ spi_set_cs(spi, true, false); ++ + if (is_mmp2_ssp(drv_data)) { + u8 tx_level = read_SSSR_bits(drv_data, SSSR_TFL_MASK) >> 8; + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index e25df9990f82d..2a0862c815480 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1059,7 +1059,7 @@ static void spi_toggle_csgpiod(struct spi_device *spi, u8 idx, bool enable, bool + spi_delay_exec(&spi->cs_inactive, NULL); + } + +-static void spi_set_cs(struct spi_device *spi, bool enable, bool force) ++void spi_set_cs(struct spi_device *spi, bool enable, bool force) + { + bool activate = enable; + u8 idx; +@@ -1116,6 +1116,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + spi_delay_exec(&spi->cs_inactive, NULL); + } + } ++EXPORT_SYMBOL_GPL(spi_set_cs); + + #ifdef CONFIG_HAS_DMA + static int spi_map_buf_attrs(struct spi_controller *ctlr, struct device *dev, +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index cb2c2df310899..bb97882277619 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -830,6 +830,8 @@ extern int spi_controller_resume(struct spi_controller *ctlr); + extern struct spi_message *spi_get_next_queued_message(struct spi_controller *ctlr); + extern void spi_finalize_current_message(struct spi_controller *ctlr); + extern void spi_finalize_current_transfer(struct spi_controller *ctlr); ++/* SPI driver calls this function to assert/deassert the chip select */ ++extern void spi_set_cs(struct spi_device *spi, bool enable, bool force); + + /* Helper calls for driver to timestamp transfer */ + void spi_take_timestamp_pre(struct spi_controller *ctlr, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac b/SPECS/kernel-rt/0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac deleted file mode 100644 index 564f871e2..000000000 --- a/SPECS/kernel-rt/0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +++ /dev/null @@ -1,54 +0,0 @@ -From 62386e735a1df2dace2fd46af3216633fb73a64a Mon Sep 17 00:00:00 2001 -From: Lili Li -Date: Tue, 11 Mar 2025 11:00:23 +0800 -Subject: [PATCH] EDAC/igen6: Add more Intel Panther Lake-H SoCs support - -Add more Intel Panther Lake-H SoC compute die IDs for EDAC support. - -Reviewed-by: Qiuxu Zhuo -Signed-off-by: Lili Li ---- - drivers/edac/igen6_edac.c | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - -diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c -index 1fcb66d9994a..4e8d71c54987 100644 ---- a/drivers/edac/igen6_edac.c -+++ b/drivers/edac/igen6_edac.c -@@ -275,6 +275,16 @@ static struct work_struct ecclog_work; - #define DID_PTL_H_SKU1 0xb000 - #define DID_PTL_H_SKU2 0xb001 - #define DID_PTL_H_SKU3 0xb002 -+#define DID_PTL_H_SKU4 0xb003 -+#define DID_PTL_H_SKU5 0xb004 -+#define DID_PTL_H_SKU6 0xb005 -+#define DID_PTL_H_SKU7 0xb008 -+#define DID_PTL_H_SKU8 0xb011 -+#define DID_PTL_H_SKU9 0xb014 -+#define DID_PTL_H_SKU10 0xb015 -+#define DID_PTL_H_SKU11 0xb028 -+#define DID_PTL_H_SKU12 0xb029 -+#define DID_PTL_H_SKU13 0xb02a - - /* Compute die IDs for Wildcat Lake with IBECC */ - #define DID_WCL_SKU1 0xfd00 -@@ -637,6 +647,16 @@ static struct pci_device_id igen6_pci_tbl[] = { - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, - { }, - }; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac b/SPECS/kernel-rt/0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac new file mode 100644 index 000000000..91db3404a --- /dev/null +++ b/SPECS/kernel-rt/0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac @@ -0,0 +1,40 @@ +From 9e49f45e679c30933899b12bc043e8cebda29da1 Mon Sep 17 00:00:00 2001 +From: Qiuxu Zhuo +Date: Mon, 24 Nov 2025 14:54:56 +0800 +Subject: [PATCH 1/3] EDAC/igen6: Add two Intel Amston Lake SoCs support + +Intel Amston Lake SoCs with IBECC (In-Band ECC) capability share the same +IBECC registers as Alder Lake-N SoCs. Add two new compute die IDs for +Amston Lake SoC products to enable EDAC support. + +Tested-by: Jianfeng Gao +Signed-off-by: Qiuxu Zhuo +--- + drivers/edac/igen6_edac.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 61c0e285bd9b6..092397a154d99 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -246,6 +246,8 @@ static struct work_struct ecclog_work; + + /* Compute did IDs for Amston Lake with IBECC */ + #define DID_ASL_SKU1 0x464a ++#define DID_ASL_SKU2 0x4646 ++#define DID_ASL_SKU3 0x4652 + + /* Compute die IDs for Raptor Lake-P with IBECC */ + #define DID_RPL_P_SKU1 0xa706 +@@ -618,6 +620,8 @@ static struct pci_device_id igen6_pci_tbl[] = { + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_AZB_SKU1), (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU1), (kernel_ulong_t)&adl_n_cfg }, ++ { PCI_VDEVICE(INTEL, DID_ASL_SKU2), (kernel_ulong_t)&adl_n_cfg }, ++ { PCI_VDEVICE(INTEL, DID_ASL_SKU3), (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), (kernel_ulong_t)&rpl_p_cfg }, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-IPU7-media-pci-Add-platform-data-config.ipu b/SPECS/kernel-rt/0001-IPU7-media-pci-Add-platform-data-config.ipu deleted file mode 100644 index 5eedf04f2..000000000 --- a/SPECS/kernel-rt/0001-IPU7-media-pci-Add-platform-data-config.ipu +++ /dev/null @@ -1,30 +0,0 @@ -From 1f44f78a75b6651770fcfbaca9b3e8f3f8b781bc Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 13 Oct 2025 10:54:32 +0800 -Subject: [PATCH] IPU7: media: pci: Add platform data config. - -Adding missing platform data config in IPU Kconfig. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/Kconfig | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index a92a7fabeac9..f6b1d7915f83 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -19,3 +19,9 @@ config IPU_BRIDGE - - Microsoft Surface models (except Surface Pro 3) - - The Lenovo Miix line (for example the 510, 520, 710 and 720) - - Dell 7285 -+ -+config VIDEO_INTEL_IPU_USE_PLATFORMDATA -+ bool "Enable platform data" -+ default y -+ help -+ Enable platform data in IPU. -\ No newline at end of file --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi b/SPECS/kernel-rt/0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi deleted file mode 100644 index aae4ecedb..000000000 --- a/SPECS/kernel-rt/0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi +++ /dev/null @@ -1,66 +0,0 @@ -From 1ab8bf60a090e5bbdf64d8b3b7762aef308db022 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 26 Jun 2025 10:16:01 -0700 -Subject: [PATCH 01/44] KVM: VMX: Add host MSR read/write helpers to - consolidate preemption handling - -Add host MSR read/write helpers to consolidate preemption handling to -prepare for adding FRED RSP0 access functions without duplicating the -preemption handling code. - -Signed-off-by: Xin Li (Intel) -Link: https://lore.kernel.org/r/20250626171601.2293914-1-xin@zytor.com -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 25 +++++++++++++++++++------ - 1 file changed, 19 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 779f73d09e68..bec76fbd0b09 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1346,22 +1346,35 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx) - } - - #ifdef CONFIG_X86_64 --static u64 vmx_read_guest_kernel_gs_base(struct vcpu_vmx *vmx) -+static u64 vmx_read_guest_host_msr(struct vcpu_vmx *vmx, u32 msr, u64 *cache) - { - preempt_disable(); - if (vmx->vt.guest_state_loaded) -- rdmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base); -+ *cache = read_msr(msr); - preempt_enable(); -- return vmx->msr_guest_kernel_gs_base; -+ return *cache; - } - --static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) -+static void vmx_write_guest_host_msr(struct vcpu_vmx *vmx, u32 msr, u64 data, -+ u64 *cache) - { - preempt_disable(); - if (vmx->vt.guest_state_loaded) -- wrmsrq(MSR_KERNEL_GS_BASE, data); -+ wrmsrns(msr, data); - preempt_enable(); -- vmx->msr_guest_kernel_gs_base = data; -+ *cache = data; -+} -+ -+static u64 vmx_read_guest_kernel_gs_base(struct vcpu_vmx *vmx) -+{ -+ return vmx_read_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, -+ &vmx->msr_guest_kernel_gs_base); -+} -+ -+static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) -+{ -+ vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data, -+ &vmx->msr_guest_kernel_gs_base); - } - #endif - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi b/SPECS/kernel-rt/0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi new file mode 100644 index 000000000..c03133546 --- /dev/null +++ b/SPECS/kernel-rt/0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi @@ -0,0 +1,220 @@ +From f577c87f393a52df70ba2b905ee3806ea54802d2 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 15:51:20 -0700 +Subject: [PATCH 01/44] KVM: VMX: Enable support for secondary VM exit controls + +Introduce infrastructure to support secondary VM exit controls. + +Always load the controls when supported by hardware, though all control +bits remain clear in this patch. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Changes in v4: +* Fix clearing VM_EXIT_ACTIVATE_SECONDARY_CONTROLS (Chao Gao). +* Check VM exit/entry consistency based on the new macro from Sean + Christopherson. + +Change in v3: +* Do FRED controls consistency checks in the VM exit/entry consistency + check framework (Sean Christopherson). + +Change in v2: +* Always load the secondary VM exit controls (Sean Christopherson). +--- + arch/x86/include/asm/msr-index.h | 1 + + arch/x86/include/asm/vmx.h | 3 +++ + arch/x86/kvm/vmx/capabilities.h | 9 ++++++++- + arch/x86/kvm/vmx/vmcs.h | 1 + + arch/x86/kvm/vmx/vmx.c | 29 +++++++++++++++++++++++++++-- + arch/x86/kvm/vmx/vmx.h | 7 ++++++- + 6 files changed, 46 insertions(+), 4 deletions(-) + +diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h +index 9e1720d73244f..baf5e16484185 100644 +--- a/arch/x86/include/asm/msr-index.h ++++ b/arch/x86/include/asm/msr-index.h +@@ -1225,6 +1225,7 @@ + #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 + #define MSR_IA32_VMX_VMFUNC 0x00000491 + #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 ++#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 + + /* Resctrl MSRs: */ + /* - Intel: */ +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index c85c500195239..1f60c04d11fbd 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -107,6 +107,7 @@ + #define VM_EXIT_PT_CONCEAL_PIP 0x01000000 + #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 + #define VM_EXIT_LOAD_CET_STATE 0x10000000 ++#define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 + + #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff + +@@ -262,6 +263,8 @@ enum vmcs_field { + SHARED_EPT_POINTER = 0x0000203C, + PID_POINTER_TABLE = 0x00002042, + PID_POINTER_TABLE_HIGH = 0x00002043, ++ SECONDARY_VM_EXIT_CONTROLS = 0x00002044, ++ SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, + GUEST_PHYSICAL_ADDRESS = 0x00002400, + GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, + VMCS_LINK_POINTER = 0x00002800, +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index 02aadb9d730e7..6bd67c40ca3b8 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -55,8 +55,9 @@ struct vmcs_config { + u32 cpu_based_exec_ctrl; + u32 cpu_based_2nd_exec_ctrl; + u64 cpu_based_3rd_exec_ctrl; +- u32 vmexit_ctrl; + u32 vmentry_ctrl; ++ u32 vmexit_ctrl; ++ u64 vmexit_2nd_ctrl; + u64 misc; + struct nested_vmx_msrs nested; + }; +@@ -141,6 +142,12 @@ static inline bool cpu_has_tertiary_exec_ctrls(void) + CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + } + ++static inline bool cpu_has_secondary_vmexit_ctrls(void) ++{ ++ return vmcs_config.vmexit_ctrl & ++ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; ++} ++ + static inline bool cpu_has_vmx_virtualize_apic_accesses(void) + { + return vmcs_config.cpu_based_2nd_exec_ctrl & +diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h +index b256253146588..ae152a9d19631 100644 +--- a/arch/x86/kvm/vmx/vmcs.h ++++ b/arch/x86/kvm/vmx/vmcs.h +@@ -47,6 +47,7 @@ struct vmcs_host_state { + struct vmcs_controls_shadow { + u32 vm_entry; + u32 vm_exit; ++ u64 secondary_vm_exit; + u32 pin; + u32 exec; + u32 secondary_exec; +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 91b6f2f3edc2a..f27d29c836298 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -2595,8 +2595,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + u32 _cpu_based_exec_control = 0; + u32 _cpu_based_2nd_exec_control = 0; + u64 _cpu_based_3rd_exec_control = 0; +- u32 _vmexit_control = 0; + u32 _vmentry_control = 0; ++ u32 _vmexit_control = 0; ++ u64 _vmexit2_control = 0; + u64 basic_msr; + u64 misc_msr; + +@@ -2617,6 +2618,12 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + { VM_ENTRY_LOAD_CET_STATE, VM_EXIT_LOAD_CET_STATE }, + }; + ++ struct { ++ u32 entry_control; ++ u64 exit_control; ++ } const vmcs_entry_exit2_pairs[] = { ++ }; ++ + memset(vmcs_conf, 0, sizeof(*vmcs_conf)); + + if (adjust_vmx_controls(KVM_REQUIRED_VMX_CPU_BASED_VM_EXEC_CONTROL, +@@ -2703,10 +2710,19 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + &_vmentry_control)) + return -EIO; + ++ if (_vmexit_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) ++ _vmexit2_control = ++ adjust_vmx_controls64(KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS, ++ MSR_IA32_VMX_EXIT_CTLS2); ++ + if (vmx_check_entry_exit_pairs(vmcs_entry_exit_pairs, + _vmentry_control, _vmexit_control)) + return -EIO; + ++ if (vmx_check_entry_exit_pairs(vmcs_entry_exit2_pairs, ++ _vmentry_control, _vmexit2_control)) ++ return -EIO; ++ + /* + * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they + * can't be used due to an errata where VM Exit may incorrectly clear +@@ -2755,8 +2771,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control; + vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control; + vmcs_conf->cpu_based_3rd_exec_ctrl = _cpu_based_3rd_exec_control; +- vmcs_conf->vmexit_ctrl = _vmexit_control; + vmcs_conf->vmentry_ctrl = _vmentry_control; ++ vmcs_conf->vmexit_ctrl = _vmexit_control; ++ vmcs_conf->vmexit_2nd_ctrl = _vmexit2_control; + vmcs_conf->misc = misc_msr; + + #if IS_ENABLED(CONFIG_HYPERV) +@@ -4410,6 +4427,11 @@ static u32 vmx_get_initial_vmexit_ctrl(void) + ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER); + } + ++static u64 vmx_secondary_vmexit_ctrl(void) ++{ ++ return vmcs_config.vmexit_2nd_ctrl; ++} ++ + void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) + { + struct vcpu_vmx *vmx = to_vmx(vcpu); +@@ -4752,6 +4774,9 @@ static void init_vmcs(struct vcpu_vmx *vmx) + + vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl()); + ++ if (cpu_has_secondary_vmexit_ctrls()) ++ secondary_vm_exit_controls_set(vmx, vmx_secondary_vmexit_ctrl()); ++ + /* 22.2.1, 20.8.1 */ + vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl()); + +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index ea93121029f91..b2724aab48d2b 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -511,7 +511,11 @@ static inline u8 vmx_get_rvi(void) + VM_EXIT_CLEAR_BNDCFGS | \ + VM_EXIT_PT_CONCEAL_PIP | \ + VM_EXIT_CLEAR_IA32_RTIT_CTL | \ +- VM_EXIT_LOAD_CET_STATE) ++ VM_EXIT_LOAD_CET_STATE | \ ++ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) ++ ++#define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) ++#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) + + #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ + (PIN_BASED_EXT_INTR_MASK | \ +@@ -624,6 +628,7 @@ static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u## + } + BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) + BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) ++BUILD_CONTROLS_SHADOW(secondary_vm_exit, SECONDARY_VM_EXIT_CONTROLS, 64) + BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL, 32) + BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL, 32) + BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL, 32) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet b/SPECS/kernel-rt/0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet deleted file mode 100644 index 7638eec22..000000000 --- a/SPECS/kernel-rt/0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet +++ /dev/null @@ -1,223 +0,0 @@ -From bbc92ecd075fa66a2f3488e8b52d1e23c11d987e Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:32 -0700 -Subject: [PATCH] KVM: x86: Rename kvm_{g,s}et_msr()* to show that they emulate - guest accesses - -Rename - kvm_{g,s}et_msr_with_filter() - kvm_{g,s}et_msr() -to - kvm_emulate_msr_{read,write} - __kvm_emulate_msr_{read,write} - -to make it more obvious that KVM uses these helpers to emulate guest -behaviors, i.e., host_initiated == false in these helpers. - -Suggested-by: Sean Christopherson -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Signed-off-by: Sean Christopherson -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 8 ++++---- - arch/x86/kvm/smm.c | 4 ++-- - arch/x86/kvm/vmx/nested.c | 14 +++++++------- - arch/x86/kvm/x86.c | 28 +++++++++++++++------------- - 4 files changed, 28 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 9215d5cda609..b3fff43e0510 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -2156,11 +2156,11 @@ void kvm_prepare_event_vectoring_exit(struct kvm_vcpu *vcpu, gpa_t gpa); - - void kvm_enable_efer_bits(u64); - bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer); --int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data); --int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data); -+int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); -+int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); - int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiated); --int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data); --int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data); -+int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); -+int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); - int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu); - int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg); - int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu); -diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c -index 9864c057187d..5dd8a1646800 100644 ---- a/arch/x86/kvm/smm.c -+++ b/arch/x86/kvm/smm.c -@@ -529,7 +529,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, - - vcpu->arch.smbase = smstate->smbase; - -- if (kvm_set_msr(vcpu, MSR_EFER, smstate->efer & ~EFER_LMA)) -+ if (__kvm_emulate_msr_write(vcpu, MSR_EFER, smstate->efer & ~EFER_LMA)) - return X86EMUL_UNHANDLEABLE; - - rsm_load_seg_64(vcpu, &smstate->tr, VCPU_SREG_TR); -@@ -620,7 +620,7 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt) - - /* And finally go back to 32-bit mode. */ - efer = 0; -- kvm_set_msr(vcpu, MSR_EFER, efer); -+ __kvm_emulate_msr_write(vcpu, MSR_EFER, efer); - } - #endif - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 1c8319916255..33e54402908e 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -1030,7 +1030,7 @@ static u32 nested_vmx_load_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count) - __func__, i, e.index, e.reserved); - goto fail; - } -- if (kvm_set_msr_with_filter(vcpu, e.index, e.value)) { -+ if (kvm_emulate_msr_write(vcpu, e.index, e.value)) { - pr_debug_ratelimited( - "%s cannot write MSR (%u, 0x%x, 0x%llx)\n", - __func__, i, e.index, e.value); -@@ -1066,7 +1066,7 @@ static bool nested_vmx_get_vmexit_msr_value(struct kvm_vcpu *vcpu, - } - } - -- if (kvm_get_msr_with_filter(vcpu, msr_index, data)) { -+ if (kvm_emulate_msr_read(vcpu, msr_index, data)) { - pr_debug_ratelimited("%s cannot read MSR (0x%x)\n", __func__, - msr_index); - return false; -@@ -2803,8 +2803,8 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - - if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL) && - kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu)) && -- WARN_ON_ONCE(kvm_set_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -- vmcs12->guest_ia32_perf_global_ctrl))) { -+ WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -+ vmcs12->guest_ia32_perf_global_ctrl))) { - *entry_failure_code = ENTRY_FAIL_DEFAULT; - return -EINVAL; - } -@@ -4791,8 +4791,8 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - } - if ((vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL) && - kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu))) -- WARN_ON_ONCE(kvm_set_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -- vmcs12->host_ia32_perf_global_ctrl)); -+ WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -+ vmcs12->host_ia32_perf_global_ctrl)); - - /* Set L1 segment info according to Intel SDM - 27.5.2 Loading Host Segment and Descriptor-Table Registers */ -@@ -4970,7 +4970,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu) - goto vmabort; - } - -- if (kvm_set_msr_with_filter(vcpu, h.index, h.value)) { -+ if (kvm_emulate_msr_write(vcpu, h.index, h.value)) { - pr_debug_ratelimited( - "%s WRMSR failed (%u, 0x%x, 0x%llx)\n", - __func__, j, h.index, h.value); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 2bcfdcd45b59..dbf9685db5b3 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1943,33 +1943,33 @@ static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu, - __kvm_get_msr); - } - --int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ)) - return KVM_MSR_RET_FILTERED; - return kvm_get_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_get_msr_with_filter); -+EXPORT_SYMBOL_GPL(kvm_emulate_msr_read); - --int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data) -+int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE)) - return KVM_MSR_RET_FILTERED; - return kvm_set_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_set_msr_with_filter); -+EXPORT_SYMBOL_GPL(kvm_emulate_msr_write); - --int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) - { - return kvm_get_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_get_msr); -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_read); - --int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data) -+int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) - { - return kvm_set_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_set_msr); -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_write); - - static void complete_userspace_rdmsr(struct kvm_vcpu *vcpu) - { -@@ -2050,7 +2050,8 @@ static int __kvm_emulate_rdmsr(struct kvm_vcpu *vcpu, u32 msr, int reg, - u64 data; - int r; - -- r = kvm_get_msr_with_filter(vcpu, msr, &data); -+ r = kvm_emulate_msr_read(vcpu, msr, &data); -+ - if (!r) { - trace_kvm_msr_read(msr, data); - -@@ -2090,7 +2091,8 @@ static int __kvm_emulate_wrmsr(struct kvm_vcpu *vcpu, u32 msr, u64 data) - { - int r; - -- r = kvm_set_msr_with_filter(vcpu, msr, data); -+ r = kvm_emulate_msr_write(vcpu, msr, data); -+ - if (!r) { - trace_kvm_msr_write(msr, data); - } else { -@@ -8378,7 +8380,7 @@ static int emulator_get_msr_with_filter(struct x86_emulate_ctxt *ctxt, - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - int r; - -- r = kvm_get_msr_with_filter(vcpu, msr_index, pdata); -+ r = kvm_emulate_msr_read(vcpu, msr_index, pdata); - if (r < 0) - return X86EMUL_UNHANDLEABLE; - -@@ -8401,7 +8403,7 @@ static int emulator_set_msr_with_filter(struct x86_emulate_ctxt *ctxt, - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - int r; - -- r = kvm_set_msr_with_filter(vcpu, msr_index, data); -+ r = kvm_emulate_msr_write(vcpu, msr_index, data); - if (r < 0) - return X86EMUL_UNHANDLEABLE; - -@@ -8421,7 +8423,7 @@ static int emulator_set_msr_with_filter(struct x86_emulate_ctxt *ctxt, - static int emulator_get_msr(struct x86_emulate_ctxt *ctxt, - u32 msr_index, u64 *pdata) - { -- return kvm_get_msr(emul_to_vcpu(ctxt), msr_index, pdata); -+ return __kvm_emulate_msr_read(emul_to_vcpu(ctxt), msr_index, pdata); - } - - static int emulator_check_rdpmc_early(struct x86_emulate_ctxt *ctxt, u32 pmc) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf b/SPECS/kernel-rt/0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf deleted file mode 100644 index a58cb46a4..000000000 --- a/SPECS/kernel-rt/0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf +++ /dev/null @@ -1,76 +0,0 @@ -From aa22513ad69e7f0001a05cdcf34cf3effc5be5bd Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 10 Oct 2025 08:52:39 +0800 -Subject: [PATCH] KVM: x86/pmu: Fix the warning in - perf_get_x86_pmu_capability() - -When load KVM module in Intel hybrid platforms, the warning below is -observed. - -<4>[ 10.973827] ------------[ cut here ]------------ -<4>[ 10.973841] WARNING: arch/x86/events/core.c:3089 at -perf_get_x86_pmu_capability+0xd/0xc0, CPU#15: (udev-worker)/386 -... -<4>[ 10.974028] Call Trace: -<4>[ 10.974030] -<4>[ 10.974033] ? kvm_init_pmu_capability+0x2b/0x190 [kvm] -<4>[ 10.974154] kvm_x86_vendor_init+0x1b0/0x1a40 [kvm] -<4>[ 10.974248] vmx_init+0xdb/0x260 [kvm_intel] -<4>[ 10.974278] ? __pfx_vt_init+0x10/0x10 [kvm_intel] -<4>[ 10.974296] vt_init+0x12/0x9d0 [kvm_intel] -<4>[ 10.974309] ? __pfx_vt_init+0x10/0x10 [kvm_intel] -<4>[ 10.974322] do_one_initcall+0x60/0x3f0 -<4>[ 10.974335] do_init_module+0x97/0x2b0 -<4>[ 10.974345] load_module+0x2d08/0x2e30 -<4>[ 10.974349] ? __kernel_read+0x158/0x2f0 -<4>[ 10.974370] ? kernel_read_file+0x2b1/0x320 -<4>[ 10.974381] init_module_from_file+0x96/0xe0 -<4>[ 10.974384] ? init_module_from_file+0x96/0xe0 -<4>[ 10.974399] idempotent_init_module+0x117/0x330 -<4>[ 10.974415] __x64_sys_finit_module+0x73/0xe0 - -The root cause is the helper perf_get_x86_pmu_capability() is called -unconditionally but it's supposed to be called only on non-hybrid -platforms. - -This patch fixes this warning by only calling -perf_get_x86_pmu_capability() on non-hybrid platforms. - -Reported-by: Chaitanya Kumar Borah -Closes: https://lore.kernel.org/all/70b64347-2aca-4511-af78-a767d5fa8226@intel.com/ -Fixes: 51f34b1e650f ("KVM: x86/pmu: Snapshot host (i.e. perf's) reported PMU capabilities") -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi ---- - arch/x86/kvm/pmu.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 0e5048ae86fa..07784b95767d 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -108,16 +108,18 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops) - bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; - int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; - -- perf_get_x86_pmu_capability(&kvm_host_pmu); -- - /* - * Hybrid PMUs don't play nice with virtualization without careful - * configuration by userspace, and KVM's APIs for reporting supported - * vPMU features do not account for hybrid PMUs. Disable vPMU support - * for hybrid PMUs until KVM gains a way to let userspace opt-in. - */ -- if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) -+ if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) { - enable_pmu = false; -+ memset(&kvm_host_pmu, 0, sizeof(kvm_host_pmu)); -+ } else { -+ perf_get_x86_pmu_capability(&kvm_host_pmu); -+ } - - if (enable_pmu) { - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss b/SPECS/kernel-rt/0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss deleted file mode 100644 index 222a054e2..000000000 --- a/SPECS/kernel-rt/0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss +++ /dev/null @@ -1,32 +0,0 @@ -From d46104a8fa52726a96700faba6f781b1a42edcf7 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Wed, 5 Apr 2023 16:49:57 +0300 -Subject: [PATCH 01/16] PCI: Apply ASPM L1 latency quirk to Intel DG2 Audio - endpoints as well - -In order to reduce power consumption these links can go into L1 when -idle but they too have the latency set to < 1us so we need to apply the -same quirk we use with the GPU endpoints of DG2. - -Signed-off-by: Mika Westerberg ---- - drivers/pci/quirks.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index d97335a40193..afdaa2a0629b 100644 ---- a/drivers/pci/quirks.c -+++ b/drivers/pci/quirks.c -@@ -6229,6 +6229,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f85, aspm_l1_acceptable_latency - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f86, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f87, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f88, aspm_l1_acceptable_latency); -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f90, aspm_l1_acceptable_latency); -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f91, aspm_l1_acceptable_latency); -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f92, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5690, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5691, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5692, aspm_l1_acceptable_latency); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-Remove-IPU7-drivers-from-pci-directory.ipu b/SPECS/kernel-rt/0001-Remove-IPU7-drivers-from-pci-directory.ipu deleted file mode 100644 index 777748657..000000000 --- a/SPECS/kernel-rt/0001-Remove-IPU7-drivers-from-pci-directory.ipu +++ /dev/null @@ -1,21461 +0,0 @@ -From 76561db385e3dc5efdf4621c9244930ed2b34bea Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 27 Jan 2026 13:58:42 +0800 -Subject: [PATCH 01/21] Remove IPU7 drivers from pci directory - -Latest IPU7 drivers are located in drivers/staging/media/ipu7 -This is to avoid confusion from having multiple IPU7 -drivers exist in kernel 6.17. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/ipu7/Kconfig | 45 - - drivers/media/pci/intel/ipu7/Makefile | 37 - - .../pci/intel/ipu7/abi/ipu7_fw_boot_abi.h | 163 - - .../pci/intel/ipu7/abi/ipu7_fw_common_abi.h | 175 - - .../pci/intel/ipu7/abi/ipu7_fw_config_abi.h | 19 - - .../intel/ipu7/abi/ipu7_fw_insys_config_abi.h | 19 - - .../pci/intel/ipu7/abi/ipu7_fw_isys_abi.h | 459 --- - .../pci/intel/ipu7/abi/ipu7_fw_msg_abi.h | 465 --- - .../intel/ipu7/abi/ipu7_fw_psys_config_abi.h | 24 - - .../pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h | 49 - - drivers/media/pci/intel/ipu7/ipu7-boot.c | 431 --- - drivers/media/pci/intel/ipu7/ipu7-boot.h | 25 - - drivers/media/pci/intel/ipu7/ipu7-bus.c | 158 - - drivers/media/pci/intel/ipu7/ipu7-bus.h | 69 - - .../media/pci/intel/ipu7/ipu7-buttress-regs.h | 461 --- - drivers/media/pci/intel/ipu7/ipu7-buttress.c | 1193 ------- - drivers/media/pci/intel/ipu7/ipu7-buttress.h | 77 - - drivers/media/pci/intel/ipu7/ipu7-cpd.c | 277 -- - drivers/media/pci/intel/ipu7/ipu7-cpd.h | 16 - - drivers/media/pci/intel/ipu7/ipu7-dma.c | 478 --- - drivers/media/pci/intel/ipu7/ipu7-dma.h | 46 - - drivers/media/pci/intel/ipu7/ipu7-fw-isys.c | 389 --- - drivers/media/pci/intel/ipu7/ipu7-fw-isys.h | 42 - - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 1034 ------ - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.h | 16 - - .../pci/intel/ipu7/ipu7-isys-csi2-regs.h | 1197 ------- - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c | 544 ---- - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h | 64 - - .../media/pci/intel/ipu7/ipu7-isys-queue.c | 1198 ------- - .../media/pci/intel/ipu7/ipu7-isys-queue.h | 75 - - .../media/pci/intel/ipu7/ipu7-isys-subdev.c | 348 -- - .../media/pci/intel/ipu7/ipu7-isys-subdev.h | 56 - - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c | 693 ---- - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h | 70 - - .../media/pci/intel/ipu7/ipu7-isys-video.c | 1311 -------- - .../media/pci/intel/ipu7/ipu7-isys-video.h | 125 - - drivers/media/pci/intel/ipu7/ipu7-isys.c | 1625 ---------- - drivers/media/pci/intel/ipu7/ipu7-isys.h | 187 -- - drivers/media/pci/intel/ipu7/ipu7-mmu.c | 854 ----- - drivers/media/pci/intel/ipu7/ipu7-mmu.h | 414 --- - .../media/pci/intel/ipu7/ipu7-platform-regs.h | 82 - - drivers/media/pci/intel/ipu7/ipu7-syscom.c | 79 - - drivers/media/pci/intel/ipu7/ipu7-syscom.h | 35 - - drivers/media/pci/intel/ipu7/ipu7.c | 2864 ----------------- - drivers/media/pci/intel/ipu7/ipu7.h | 259 -- - drivers/media/pci/intel/ipu7/psys/Makefile | 18 - - drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 1545 --------- - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.c | 603 ---- - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.h | 42 - - drivers/media/pci/intel/ipu7/psys/ipu7-psys.c | 398 --- - drivers/media/pci/intel/ipu7/psys/ipu7-psys.h | 184 -- - 51 files changed, 21037 deletions(-) - delete mode 100644 drivers/media/pci/intel/ipu7/Kconfig - delete mode 100644 drivers/media/pci/intel/ipu7/Makefile - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-platform-regs.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7.h - delete mode 100644 drivers/media/pci/intel/ipu7/psys/Makefile - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu-psys.c - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.c - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.h - -diff --git a/drivers/media/pci/intel/ipu7/Kconfig b/drivers/media/pci/intel/ipu7/Kconfig -deleted file mode 100644 -index 91954fdadc8b..000000000000 ---- a/drivers/media/pci/intel/ipu7/Kconfig -+++ /dev/null -@@ -1,45 +0,0 @@ --config VIDEO_INTEL_IPU7 -- tristate "Intel IPU7 driver" -- depends on ACPI || COMPILE_TEST -- depends on VIDEO_DEV -- depends on X86 && HAS_DMA -- depends on IPU_BRIDGE || !IPU_BRIDGE -- # -- # This driver incorrectly tries to override the dma_ops. It should -- # never have done that, but for now keep it working on architectures -- # that use dma ops -- # -- depends on ARCH_HAS_DMA_OPS -- select AUXILIARY_BUS -- select IOMMU_IOVA -- select VIDEO_V4L2_SUBDEV_API -- select MEDIA_CONTROLLER -- select VIDEOBUF2_DMA_SG -- select V4L2_FWNODE -- help -- This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs -- and used for capturing images and video from camera sensors. -- -- To compile this driver, say Y here! It contains 3 modules - -- intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. -- --config VIDEO_INTEL_IPU7_MGC -- bool "Compile for IPU7 MGC driver" -- depends on VIDEO_INTEL_IPU7 -- help -- If selected, MGC device nodes would be created. -- -- Recommended for driver developers only. -- -- If you want to the MGC devices exposed to user as media entity, -- you must select this option, otherwise no. -- --config VIDEO_INTEL_IPU7_ISYS_RESET -- bool "IPU7 ISYS RESET" -- depends on VIDEO_INTEL_IPU7 -- default n -- help -- This option enables IPU7 ISYS reset feature to support -- HDMI-MIPI converter hot-plugging. -- -- If doubt, say N here. -diff --git a/drivers/media/pci/intel/ipu7/Makefile b/drivers/media/pci/intel/ipu7/Makefile -deleted file mode 100644 -index 50417017da3a..000000000000 ---- a/drivers/media/pci/intel/ipu7/Makefile -+++ /dev/null -@@ -1,37 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --# Copyright (c) 2017 - 2025 Intel Corporation. -- --is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) --ifeq ($(is_kernel_lt_6_10), 1) --ifneq ($(EXTERNAL_BUILD), 1) --src := $(srctree)/$(src) --endif --endif -- --intel-ipu7-objs += ipu7.o \ -- ipu7-bus.o \ -- ipu7-dma.o \ -- ipu7-mmu.o \ -- ipu7-buttress.o \ -- ipu7-cpd.o \ -- ipu7-syscom.o \ -- ipu7-boot.o -- --obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7.o -- --intel-ipu7-isys-objs += ipu7-isys.o \ -- ipu7-isys-csi2.o \ -- ipu7-isys-csi-phy.o \ -- ipu7-fw-isys.o \ -- ipu7-isys-video.o \ -- ipu7-isys-queue.o \ -- ipu7-isys-subdev.o -- --ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --intel-ipu7-isys-objs += ipu7-isys-tpg.o --endif --obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o -- --obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ -- --ccflags-y += -I$(src)/ -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -deleted file mode 100644 -index a1519c4fe661..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -+++ /dev/null -@@ -1,163 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_BOOT_ABI_H --#define IPU7_FW_BOOT_ABI_H -- --#include "ipu7_fw_common_abi.h" --#include "ipu7_fw_syscom_abi.h" -- --#define IA_GOFO_FWLOG_SEVERITY_CRIT (0U) --#define IA_GOFO_FWLOG_SEVERITY_ERROR (1U) --#define IA_GOFO_FWLOG_SEVERITY_WARNING (2U) --#define IA_GOFO_FWLOG_SEVERITY_INFO (3U) --#define IA_GOFO_FWLOG_SEVERITY_DEBUG (4U) --#define IA_GOFO_FWLOG_SEVERITY_VERBOSE (5U) --#define IA_GOFO_FWLOG_MAX_LOGGER_SOURCES (64U) -- --#define LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK BIT(0) --#define LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK BIT(1) --#define LOGGER_CONFIG_CHANNEL_ENABLE_ALL_BITMASK \ -- (LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK | \ -- LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK) -- --struct ia_gofo_logger_config { -- u8 use_source_severity; -- u8 source_severity[IA_GOFO_FWLOG_MAX_LOGGER_SOURCES]; -- u8 use_channels_enable_bitmask; -- u8 channels_enable_bitmask; -- u8 padding[1]; -- ia_gofo_addr_t hw_printf_buffer_base_addr; -- u32 hw_printf_buffer_size_bytes; --}; -- --#pragma pack(push, 1) -- --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP \ -- ((u32)IA_GOFO_FW_BOOT_ID_MAX) --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET (0U) --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PS_OFFSET \ -- ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ -- (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) --#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) --#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) --#define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP) --#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) --#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) -- --#define IA_GOFO_BOOT_RESERVED_SIZE (58U) --#define IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE (IA_GOFO_BOOT_RESERVED_SIZE) --#define IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS \ -- (sizeof(ia_gofo_addr_t) + sizeof(ia_gofo_addr_t) + sizeof(u32)) -- --enum ia_gofo_buttress_reg_id { -- IA_GOFO_FW_BOOT_CONFIG_ID = 0, -- IA_GOFO_FW_BOOT_STATE_ID = 1, -- IA_GOFO_FW_BOOT_RESERVED1_ID = IA_GOFO_FW_BOOT_STATE_ID, -- IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2, -- IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3, -- IA_GOFO_FW_BOOT_RESERVED0_ID = IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID, -- IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID = 4, -- IA_GOFO_FW_BOOT_ID_MAX --}; -- --enum ia_gofo_boot_uc_tile_frequency_units { -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ = 0, -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_HZ = 1, -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_N --}; -- --#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ -- (0xdead0000U == ((boot_state) & 0xffff0000U)) -- --struct ia_gofo_boot_config { -- u32 length; -- struct ia_gofo_version_s config_version; -- struct ia_gofo_msg_version_list client_version_support; -- ia_gofo_addr_t pkg_dir; -- ia_gofo_addr_t subsys_config; -- u32 uc_tile_frequency; -- u16 checksum; -- u8 uc_tile_frequency_units; -- u8 padding[1]; -- u32 reserved[IA_GOFO_BOOT_RESERVED_SIZE]; -- struct syscom_config_s syscom_context_config; --}; -- --struct ia_gofo_secondary_boot_config { -- u32 length; -- struct ia_gofo_version_s config_version; -- struct ia_gofo_msg_version_list client_version_support; -- u8 reserved1[IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS]; -- u16 checksum; -- u8 padding[2]; -- u32 reserved2[IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE]; -- struct syscom_config_s syscom_context_config; --}; -- --#pragma pack(pop) -- --#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U --#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U --#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U --#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U --#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U --#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U --#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U --#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U --#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U -- --enum ia_gofo_boot_state { -- IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, -- IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, -- IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, -- IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, -- IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, -- IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, -- IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, -- IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, -- IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, -- IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, -- IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, -- IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, -- IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, -- IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, -- IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, -- IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, -- IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, -- IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = -- IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DIRTY = -- IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DTAG = -- IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_CACHE = -- IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_DOUBLE_EXCEPTION = -- IA_GOFO_DOUBLE_EXCEPTION_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = -- IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, -- IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, -- IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = -- 0xdead1012U, -- IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, -- IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, -- IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, -- IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, -- IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, -- IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, -- IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, -- IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -deleted file mode 100644 -index 7bb6fac585a3..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -+++ /dev/null -@@ -1,175 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_COMMOM_ABI_H --#define IPU7_FW_COMMOM_ABI_H -- --#include -- --#pragma pack(push, 1) --typedef u32 ia_gofo_addr_t; -- --#define IA_GOFO_ADDR_NULL (0U) -- --struct ia_gofo_version_s { -- u8 patch; -- u8 subminor; -- u8 minor; -- u8 major; --}; -- --#define IA_GOFO_MSG_VERSION_INIT(major_val, minor_val, subminor_val, patch_val)\ -- {.major = (major_val), .minor = (minor_val), .subminor = \ -- (subminor_val), .patch = (patch_val)} -- --#define IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES (3U) --#define IA_GOFO_MSG_RESERVED_SIZE (3U) -- --struct ia_gofo_msg_version_list { -- u8 num_versions; -- u8 reserved[IA_GOFO_MSG_RESERVED_SIZE]; -- struct ia_gofo_version_s versions[IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES]; --}; -- --#pragma pack(pop) -- --#define TLV_TYPE_PADDING (0U) -- --#pragma pack(push, 1) -- --#define IA_GOFO_ABI_BITS_PER_BYTE (8U) -- --struct ia_gofo_tlv_header { -- u16 tlv_type; -- u16 tlv_len32; --}; -- --struct ia_gofo_tlv_list { -- u16 num_elems; -- u16 head_offset; --}; -- --#define TLV_ITEM_ALIGNMENT ((u32)sizeof(u32)) --#define TLV_MSG_ALIGNMENT ((u32)sizeof(u64)) --#define TLV_LIST_ALIGNMENT TLV_ITEM_ALIGNMENT --#pragma pack(pop) -- --#define IA_GOFO_MODULO(dividend, divisor) ((dividend) % (divisor)) -- --#define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) --#define IA_GOFO_MSG_ERR_OK (0U) --#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) --#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) --#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) -- --#pragma pack(push, 1) --struct ia_gofo_msg_err { -- u32 err_group; -- u32 err_code; -- u32 err_detail[IA_GOFO_MSG_ERR_MAX_DETAILS]; --}; -- --#pragma pack(pop) -- --#define IA_GOFO_MSG_ERR_GROUP_APP_EXT_START (16U) --#define IA_GOFO_MSG_ERR_GROUP_MAX (31U) --#define IA_GOFO_MSG_ERR_GROUP_INTERNAL_START (IA_GOFO_MSG_ERR_GROUP_MAX + 1U) --#define IA_GOFO_MSG_ERR_GROUP_RESERVED IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED --#define IA_GOFO_MSG_ERR_GROUP_GENERAL 1 -- --enum ia_gofo_msg_err_general { -- IA_GOFO_MSG_ERR_GENERAL_OK = IA_GOFO_MSG_ERR_OK, -- IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_SMALL = 1, -- IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_LARGE = 2, -- IA_GOFO_MSG_ERR_GENERAL_DEVICE_STATE = 3, -- IA_GOFO_MSG_ERR_GENERAL_ALIGNMENT = 4, -- IA_GOFO_MSG_ERR_GENERAL_INDIRECT_REF_PTR_INVALID = 5, -- IA_GOFO_MSG_ERR_GENERAL_INVALID_MSG_TYPE = 6, -- IA_GOFO_MSG_ERR_GENERAL_SYSCOM_FAIL = 7, -- IA_GOFO_MSG_ERR_GENERAL_N --}; -- --#pragma pack(push, 1) --#define IA_GOFO_MSG_TYPE_RESERVED 0 --#define IA_GOFO_MSG_TYPE_INDIRECT 1 --#define IA_GOFO_MSG_TYPE_LOG 2 --#define IA_GOFO_MSG_TYPE_GENERAL_ERR 3 -- --struct ia_gofo_msg_header { -- struct ia_gofo_tlv_header tlv_header; -- struct ia_gofo_tlv_list msg_options; -- u64 user_token; --}; -- --struct ia_gofo_msg_header_ack { -- struct ia_gofo_msg_header header; -- struct ia_gofo_msg_err err; -- --}; -- --struct ia_gofo_msg_general_err { -- struct ia_gofo_msg_header_ack header; --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --enum ia_gofo_msg_link_streaming_mode { -- IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF = 0, -- IA_GOFO_MSG_LINK_STREAMING_MODE_DOFF = 1, -- IA_GOFO_MSG_LINK_STREAMING_MODE_BCLM = 2, -- IA_GOFO_MSG_LINK_STREAMING_MODE_BCSM_FIX = 3, -- IA_GOFO_MSG_LINK_STREAMING_MODE_N --}; -- --enum ia_gofo_soc_pbk_instance_id { -- IA_GOFO_SOC_PBK_ID0 = 0, -- IA_GOFO_SOC_PBK_ID1 = 1, -- IA_GOFO_SOC_PBK_ID_N --}; -- --#define IA_GOFO_MSG_LINK_PBK_MAX_SLOTS (2U) -- --struct ia_gofo_msg_indirect { -- struct ia_gofo_msg_header header; -- struct ia_gofo_tlv_header ref_header; -- ia_gofo_addr_t ref_msg_ptr; --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --#define IA_GOFO_MSG_LOG_MAX_PARAMS (4U) --#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) -- --#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) --#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) -- --struct ia_gofo_msg_log_info { -- u16 log_counter; -- u8 msg_parameter_types; -- /* [0:0] is_out_of_order, [1:3] logger_channel, [4:7] reserved */ -- u8 logger_opts; -- u32 fmt_id; -- u32 params[IA_GOFO_MSG_LOG_MAX_PARAMS]; --}; -- --struct ia_gofo_msg_log_info_ts { -- u64 msg_ts; -- struct ia_gofo_msg_log_info log_info; --}; -- --struct ia_gofo_msg_log { -- struct ia_gofo_msg_header header; -- struct ia_gofo_msg_log_info_ts log_info_ts; --}; -- --#pragma pack(pop) -- --#define IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID (0U) --#define IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID (1U) --#define IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID (2U) -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -deleted file mode 100644 -index c3f62aaedd86..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -+++ /dev/null -@@ -1,19 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2021 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_CONFIG_ABI_H --#define IPU7_FW_CONFIG_ABI_H -- --#include -- --#define IPU_CONFIG_ABI_WDT_TIMER_DISABLED 0U --#define IPU_CONFIG_ABI_CMD_TIMER_DISABLED 0U -- --struct ipu7_wdt_abi { -- u32 wdt_timer1_us; -- u32 wdt_timer2_us; --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -deleted file mode 100644 -index f161a605c500..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -+++ /dev/null -@@ -1,19 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2021 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_INSYS_CONFIG_ABI_H --#define IPU7_FW_INSYS_CONFIG_ABI_H -- --#include "ipu7_fw_boot_abi.h" --#include "ipu7_fw_config_abi.h" --#include "ipu7_fw_isys_abi.h" -- --struct ipu7_insys_config { -- u32 timeout_val_ms; -- struct ia_gofo_logger_config logger_config; -- struct ipu7_wdt_abi wdt_config; --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -deleted file mode 100644 -index 45db85eb13ec..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -+++ /dev/null -@@ -1,459 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_ISYS_ABI_H --#define IPU7_FW_ISYS_ABI_H -- --#include "ipu7_fw_common_abi.h" --#include "ipu7_fw_isys_abi.h" -- --#define IPU_INSYS_MAX_OUTPUT_QUEUES (3U) --#define IPU_INSYS_STREAM_ID_MAX (16U) -- --#define IPU_INSYS_MAX_INPUT_QUEUES (IPU_INSYS_STREAM_ID_MAX + 1U) --#define IPU_INSYS_OUTPUT_FIRST_QUEUE (0U) --#define IPU_INSYS_OUTPUT_LAST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES - 1U) --#define IPU_INSYS_OUTPUT_MSG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE) --#define IPU_INSYS_OUTPUT_LOG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE + 1U) --#define IPU_INSYS_OUTPUT_RESERVED_QUEUE (IPU_INSYS_OUTPUT_LAST_QUEUE) --#define IPU_INSYS_INPUT_FIRST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES) --#define IPU_INSYS_INPUT_LAST_QUEUE \ -- (IPU_INSYS_INPUT_FIRST_QUEUE + IPU_INSYS_MAX_INPUT_QUEUES - 1U) --#define IPU_INSYS_INPUT_DEV_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE) --#define IPU_INSYS_INPUT_MSG_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE + 1U) --#define IPU_INSYS_INPUT_MSG_MAX_QUEUE (IPU_INSYS_MAX_INPUT_QUEUES - 1U) -- --#define MAX_OPINS_FOR_SINGLE_IPINS (3U) --#define DEV_SEND_QUEUE_SIZE (IPU_INSYS_STREAM_ID_MAX) -- --#define PIN_PLANES_MAX (4U) -- --#define INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_INPUT \ -- INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES -- --typedef u64 ipu7_insys_return_token; -- --enum ipu7_insys_resp_type { -- IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0, -- IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1, -- IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2, -- IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3, -- IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4, -- IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5, -- IPU_INSYS_RESP_TYPE_PIN_DATA_READY = 6, -- IPU_INSYS_RESP_TYPE_FRAME_SOF = 7, -- IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, -- IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, -- IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, -- IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, -- N_IPU_INSYS_RESP_TYPE --}; -- --enum ipu7_insys_send_type { -- IPU_INSYS_SEND_TYPE_STREAM_OPEN = 0, -- IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1, -- IPU_INSYS_SEND_TYPE_STREAM_CAPTURE = 2, -- IPU_INSYS_SEND_TYPE_STREAM_ABORT = 3, -- IPU_INSYS_SEND_TYPE_STREAM_FLUSH = 4, -- IPU_INSYS_SEND_TYPE_STREAM_CLOSE = 5, -- N_IPU_INSYS_SEND_TYPE --}; -- --enum ipu7_insys_mipi_vc { -- IPU_INSYS_MIPI_VC_0 = 0, -- IPU_INSYS_MIPI_VC_1 = 1, -- IPU_INSYS_MIPI_VC_2 = 2, -- IPU_INSYS_MIPI_VC_3 = 3, -- IPU_INSYS_MIPI_VC_4 = 4, -- IPU_INSYS_MIPI_VC_5 = 5, -- IPU_INSYS_MIPI_VC_6 = 6, -- IPU_INSYS_MIPI_VC_7 = 7, -- IPU_INSYS_MIPI_VC_8 = 8, -- IPU_INSYS_MIPI_VC_9 = 9, -- IPU_INSYS_MIPI_VC_10 = 10, -- IPU_INSYS_MIPI_VC_11 = 11, -- IPU_INSYS_MIPI_VC_12 = 12, -- IPU_INSYS_MIPI_VC_13 = 13, -- IPU_INSYS_MIPI_VC_14 = 14, -- IPU_INSYS_MIPI_VC_15 = 15, -- N_IPU_INSYS_MIPI_VC --}; -- --enum ipu7_insys_mipi_port { -- IPU_INSYS_MIPI_PORT_0 = 0, -- IPU_INSYS_MIPI_PORT_1 = 1, -- IPU_INSYS_MIPI_PORT_2 = 2, -- IPU_INSYS_MIPI_PORT_3 = 3, -- IPU_INSYS_MIPI_PORT_4 = 4, -- IPU_INSYS_MIPI_PORT_5 = 5, -- NA_IPU_INSYS_MIPI_PORT --}; -- --enum ipu7_insys_frame_format_type { -- IPU_INSYS_FRAME_FORMAT_NV11 = 0, -- IPU_INSYS_FRAME_FORMAT_NV12 = 1, -- IPU_INSYS_FRAME_FORMAT_NV12_16 = 2, -- IPU_INSYS_FRAME_FORMAT_NV12_TILEY = 3, -- IPU_INSYS_FRAME_FORMAT_NV16 = 4, -- IPU_INSYS_FRAME_FORMAT_NV21 = 5, -- IPU_INSYS_FRAME_FORMAT_NV61 = 6, -- IPU_INSYS_FRAME_FORMAT_YV12 = 7, -- IPU_INSYS_FRAME_FORMAT_YV16 = 8, -- IPU_INSYS_FRAME_FORMAT_YUV420 = 9, -- IPU_INSYS_FRAME_FORMAT_YUV420_10 = 10, -- IPU_INSYS_FRAME_FORMAT_YUV420_12 = 11, -- IPU_INSYS_FRAME_FORMAT_YUV420_14 = 12, -- IPU_INSYS_FRAME_FORMAT_YUV420_16 = 13, -- IPU_INSYS_FRAME_FORMAT_YUV422 = 14, -- IPU_INSYS_FRAME_FORMAT_YUV422_16 = 15, -- IPU_INSYS_FRAME_FORMAT_UYVY = 16, -- IPU_INSYS_FRAME_FORMAT_YUYV = 17, -- IPU_INSYS_FRAME_FORMAT_YUV444 = 18, -- IPU_INSYS_FRAME_FORMAT_YUV_LINE = 19, -- IPU_INSYS_FRAME_FORMAT_RAW8 = 20, -- IPU_INSYS_FRAME_FORMAT_RAW10 = 21, -- IPU_INSYS_FRAME_FORMAT_RAW12 = 22, -- IPU_INSYS_FRAME_FORMAT_RAW14 = 23, -- IPU_INSYS_FRAME_FORMAT_RAW16 = 24, -- IPU_INSYS_FRAME_FORMAT_RGB565 = 25, -- IPU_INSYS_FRAME_FORMAT_PLANAR_RGB888 = 26, -- IPU_INSYS_FRAME_FORMAT_RGBA888 = 27, -- IPU_INSYS_FRAME_FORMAT_QPLANE6 = 28, -- IPU_INSYS_FRAME_FORMAT_BINARY_8 = 29, -- IPU_INSYS_FRAME_FORMAT_Y_8 = 30, -- IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, -- IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, -- IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, -- N_IPU_INSYS_FRAME_FORMAT --}; -- --#define IPU_INSYS_FRAME_FORMAT_RAW (IPU_INSYS_FRAME_FORMAT_RAW16) --#define N_IPU_INSYS_MIPI_DATA_TYPE 0x40 -- --enum ipu7_insys_mipi_dt_rename_mode { -- IPU_INSYS_MIPI_DT_NO_RENAME = 0, -- IPU_INSYS_MIPI_DT_RENAMED_MODE = 1, -- N_IPU_INSYS_MIPI_DT_MODE --}; -- --#define IPU_INSYS_SEND_MSG_ENABLED 1U --#define IPU_INSYS_SEND_MSG_DISABLED 0U -- --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7) --#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_RESP ( \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED) --#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_IRQ ( \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED) -- --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9) --#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK) --#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK) -- --#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0) --#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1) --#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2) --#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3) --#define IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4) --#define IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5) --#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \ -- IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \ -- IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \ -- IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY) --#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \ -- IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \ -- IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \ -- IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY) -- --enum ipu7_insys_output_link_dest { -- IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, -- IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, -- IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 --}; -- --enum ipu7_insys_dpcm_type { -- IPU_INSYS_DPCM_TYPE_DISABLED = 0, -- IPU_INSYS_DPCM_TYPE_10_8_10 = 1, -- IPU_INSYS_DPCM_TYPE_12_8_12 = 2, -- IPU_INSYS_DPCM_TYPE_12_10_12 = 3, -- N_IPU_INSYS_DPCM_TYPE --}; -- --enum ipu7_insys_dpcm_predictor { -- IPU_INSYS_DPCM_PREDICTOR_1 = 0, -- IPU_INSYS_DPCM_PREDICTOR_2 = 1, -- N_IPU_INSYS_DPCM_PREDICTOR --}; -- --enum ipu7_insys_send_queue_token_flag { -- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, -- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 --}; -- --#pragma pack(push, 1) --struct ipu7_insys_resolution { -- u32 width; -- u32 height; --}; -- --struct ipu7_insys_capture_output_pin_payload { -- u64 user_token; -- ia_gofo_addr_t addr; -- u8 pad[4]; --}; -- --struct ipu7_insys_output_link { -- u32 buffer_lines; -- u16 foreign_key; -- u16 granularity_pointer_update; -- u8 msg_link_streaming_mode; -- u8 pbk_id; -- u8 pbk_slot_id; -- u8 dest; -- u8 use_sw_managed; -- u8 is_snoop; -- u8 pad[2]; --}; -- --struct ipu7_insys_output_cropping { -- u16 line_top; -- u16 line_bottom; --#ifdef IPU8_INSYS_NEW_ABI -- u16 column_left; -- u16 column_right; --#endif --}; -- --struct ipu7_insys_output_dpcm { -- u8 enable; -- u8 type; -- u8 predictor; -- u8 pad; --}; -- --#ifdef IPU8_INSYS_NEW_ABI --enum ipu_insys_cfa_dim { -- IPU_INSYS_CFA_DIM_2x2 = 0, -- IPU_INSYS_CFA_DIM_4x4 = 1, -- N_IPU_INSYS_CFA_DIM --}; -- --#define IPU_INSYS_MAX_BINNING_FACTOR (4U) --#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) --#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) --#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) --#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) -- --struct ipu7_insys_upipe_output_pin { -- ia_gofo_addr_t opaque_pin_cfg; -- u16 plane_offset_1; -- u16 plane_offset_2; -- u8 single_uob_fifo; -- u8 shared_uob_fifo; -- u8 pad[2]; --}; -- --struct ipu7_insys_capture_output_pin_cfg { -- struct ipu7_insys_capture_output_pin_payload pin_payload; -- ia_gofo_addr_t upipe_capture_cfg; --}; -- --#endif --struct ipu7_insys_output_pin { -- struct ipu7_insys_output_link link; -- struct ipu7_insys_output_cropping crop; -- struct ipu7_insys_output_dpcm dpcm; --#ifdef IPU8_INSYS_NEW_ABI -- struct ipu7_insys_upipe_output_pin upipe_pin_cfg; --#endif -- u32 stride; -- u16 ft; --#ifdef IPU8_INSYS_NEW_ABI -- u8 upipe_enable; --#endif -- u8 send_irq; -- u8 input_pin_id; -- u8 early_ack_en; --#ifdef IPU8_INSYS_NEW_ABI -- u8 cfa_dim; -- u8 binning_factor; --#else -- u8 pad[3]; --#endif --}; -- --struct ipu7_insys_input_pin { -- struct ipu7_insys_resolution input_res; -- u16 sync_msg_map; -- u8 dt; -- u8 disable_mipi_unpacking; -- u8 dt_rename_mode; -- u8 mapped_dt; -- u8 pad[2]; --}; -- --struct ipu7_insys_stream_cfg { -- struct ipu7_insys_input_pin input_pins[4]; -- struct ipu7_insys_output_pin output_pins[4]; -- u16 stream_msg_map; -- u8 port_id; -- u8 vc; -- u8 nof_input_pins; -- u8 nof_output_pins; -- u8 pad[2]; --}; -- --struct ipu7_insys_buffset { --#ifdef IPU8_INSYS_NEW_ABI -- struct ipu7_insys_capture_output_pin_cfg output_pins[4]; --#else -- struct ipu7_insys_capture_output_pin_payload output_pins[4]; --#endif -- u8 capture_msg_map; -- u8 frame_id; -- u8 skip_frame; -- u8 pad[5]; --}; -- --struct ipu7_insys_resp { -- u64 buf_id; -- struct ipu7_insys_capture_output_pin_payload pin; -- struct ia_gofo_msg_err error_info; -- u32 timestamp[2]; -- u8 type; -- u8 msg_link_streaming_mode; -- u8 stream_id; -- u8 pin_id; -- u8 frame_id; -- u8 skip_frame; -- u8 pad[2]; --}; -- --struct ipu7_insys_resp_queue_token { -- struct ipu7_insys_resp resp_info; --}; -- --struct ipu7_insys_send_queue_token { -- u64 buf_handle; -- ia_gofo_addr_t addr; -- u16 stream_id; -- u8 send_type; -- u8 flag; --}; -- --#pragma pack(pop) -- --enum insys_msg_err_stream { -- INSYS_MSG_ERR_STREAM_OK = IA_GOFO_MSG_ERR_OK, -- INSYS_MSG_ERR_STREAM_STREAM_ID = 1, -- INSYS_MSG_ERR_STREAM_MAX_OPINS = 2, -- INSYS_MSG_ERR_STREAM_MAX_IPINS = 3, -- INSYS_MSG_ERR_STREAM_STREAM_MESSAGES_MAP = 4, -- INSYS_MSG_ERR_STREAM_SYNC_MESSAGES_MAP = 5, -- INSYS_MSG_ERR_STREAM_SENSOR_TYPE = 6, -- INSYS_MSG_ERR_STREAM_FOREIGN_KEY = 7, -- INSYS_MSG_ERR_STREAM_STREAMING_MODE = 8, -- INSYS_MSG_ERR_STREAM_DPCM_EN = 9, -- INSYS_MSG_ERR_STREAM_DPCM_TYPE = 10, -- INSYS_MSG_ERR_STREAM_DPCM_PREDICTOR = 11, -- INSYS_MSG_ERR_STREAM_GRANULARITY_POINTER_UPDATE = 12, -- INSYS_MSG_ERR_STREAM_MPF_LUT_ENTRY_RESOURCES_BUSY = 13, -- INSYS_MSG_ERR_STREAM_MPF_DEV_ID = 14, -- INSYS_MSG_ERR_STREAM_BUFFER_LINES = 15, -- INSYS_MSG_ERR_STREAM_IPIN_ID = 16, -- INSYS_MSG_ERR_STREAM_DATA_TYPE = 17, -- INSYS_MSG_ERR_STREAM_STREAMING_PROTOCOL_STATE = 18, -- INSYS_MSG_ERR_STREAM_SYSCOM_FLUSH = 19, -- INSYS_MSG_ERR_STREAM_MIPI_VC = 20, -- INSYS_MSG_ERR_STREAM_STREAM_SRC = 21, -- INSYS_MSG_ERR_STREAM_PBK_ID = 22, -- INSYS_MSG_ERR_STREAM_CMD_QUEUE_DEALLOCATE = 23, -- INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES = 24, -- INSYS_MSG_ERR_STREAM_IPIN_CONFIGURATION = 25, -- INSYS_MSG_ERR_STREAM_INVALID_STATE = 26, -- INSYS_MSG_ERR_STREAM_SW_MANAGED = 27, -- INSYS_MSG_ERR_STREAM_PBK_SLOT_ID = 28, -- INSYS_MSG_ERR_STREAM_FLUSH_TIMEOUT = 29, -- INSYS_MSG_ERR_STREAM_IPIN_WIDTH = 30, -- INSYS_MSG_ERR_STREAM_IPIN_HEIGHT = 31, -- INSYS_MSG_ERR_STREAM_OUTPUT_PIN_EARLY_ACK_EN = 32, -- INSYS_MSG_ERR_STREAM_INCONSISTENT_PARAMS = 33, -- INSYS_MSG_ERR_STREAM_PLANE_COUNT = 34, -- INSYS_MSG_ERR_STREAM_FRAME_FORMAT_TYPE = 35, -- INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36, -- INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37, -- INSYS_MSG_ERR_STREAM_CLOSED = 38, -- INSYS_MSG_ERR_STREAM_N --}; -- --enum insys_msg_err_capture { -- INSYS_MSG_ERR_CAPTURE_OK = IA_GOFO_MSG_ERR_OK, -- INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1, -- INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2, -- INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3, -- INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4, -- INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5, -- INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6, -- INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7, -- INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8, -- INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9, -- INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10, -- INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11, -- INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12, -- INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21, -- INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22, -- INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23, -- INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24, -- INSYS_MSG_ERR_CAPTURE_N --}; -- --enum insys_msg_err_groups { -- INSYS_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -- INSYS_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -- INSYS_MSG_ERR_GROUP_STREAM = 2, -- INSYS_MSG_ERR_GROUP_CAPTURE = 3, -- INSYS_MSG_ERR_GROUP_N, --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -deleted file mode 100644 -index 8a78dd0936df..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -+++ /dev/null -@@ -1,465 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_MSG_ABI_H --#define IPU7_FW_MSG_ABI_H -- --#include "ipu7_fw_common_abi.h" -- --#pragma pack(push, 1) --enum ipu7_msg_type { -- IPU_MSG_TYPE_RESERVED = IA_GOFO_MSG_TYPE_RESERVED, -- IPU_MSG_TYPE_INDIRECT = IA_GOFO_MSG_TYPE_INDIRECT, -- IPU_MSG_TYPE_DEV_LOG = IA_GOFO_MSG_TYPE_LOG, -- IPU_MSG_TYPE_GENERAL_ERR = IA_GOFO_MSG_TYPE_GENERAL_ERR, -- IPU_MSG_TYPE_DEV_OPEN = 4, -- IPU_MSG_TYPE_DEV_OPEN_ACK = 5, -- IPU_MSG_TYPE_GRAPH_OPEN = 6, -- IPU_MSG_TYPE_GRAPH_OPEN_ACK = 7, -- IPU_MSG_TYPE_TASK_REQ = 8, -- IPU_MSG_TYPE_TASK_DONE = 9, -- IPU_MSG_TYPE_GRAPH_CLOSE = 10, -- IPU_MSG_TYPE_GRAPH_CLOSE_ACK = 11, -- IPU_MSG_TYPE_DEV_CLOSE = 12, -- IPU_MSG_TYPE_DEV_CLOSE_ACK = 13, -- IPU_MSG_TYPE_TERM_EVENT = 14, -- IPU_MSG_TYPE_N, --}; -- --#define IPU_MSG_MAX_NODE_TERMS (64U) --#define IPU_MSG_MAX_FRAGS (7U) -- --enum ipu7_msg_node_type { -- IPU_MSG_NODE_TYPE_PAD = 0, -- IPU_MSG_NODE_TYPE_BASE, -- IPU_MSG_NODE_TYPE_N --}; -- --#define IPU_MSG_NODE_MAX_DEVICES (128U) --#define DEB_NUM_UINT32 (IPU_MSG_NODE_MAX_DEVICES / (sizeof(u32) * 8U)) -- --typedef u32 ipu7_msg_teb_t[2]; --typedef u32 ipu7_msg_deb_t[DEB_NUM_UINT32]; -- --#define IPU_MSG_NODE_MAX_ROUTE_ENABLES (128U) --#define RBM_NUM_UINT32 (IPU_MSG_NODE_MAX_ROUTE_ENABLES / (sizeof(u32) * 8U)) -- --typedef u32 ipu7_msg_rbm_t[RBM_NUM_UINT32]; -- --enum ipu7_msg_node_profile_type { -- IPU_MSG_NODE_PROFILE_TYPE_PAD = 0, -- IPU_MSG_NODE_PROFILE_TYPE_BASE, -- IPU_MSG_NODE_PROFILE_TYPE_CB, -- IPU_MSG_NODE_PROFILE_TYPE_N --}; -- --struct ipu7_msg_node_profile { -- struct ia_gofo_tlv_header tlv_header; -- ipu7_msg_teb_t teb; --}; -- --struct ipu7_msg_cb_profile { -- struct ipu7_msg_node_profile profile_base; -- ipu7_msg_deb_t deb; -- ipu7_msg_rbm_t rbm; -- ipu7_msg_rbm_t reb; --}; -- --#define IPU_MSG_NODE_MAX_PROFILES (2U) --#define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) --#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) -- --#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) --#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) --#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) -- --struct ipu7_msg_node { -- struct ia_gofo_tlv_header tlv_header; -- u8 node_rsrc_id; -- u8 node_ctx_id; -- u8 num_frags; -- u8 reserved[1]; -- struct ia_gofo_tlv_list profiles_list; -- struct ia_gofo_tlv_list terms_list; -- struct ia_gofo_tlv_list node_options; --}; -- --enum ipu7_msg_node_option_types { -- IPU_MSG_NODE_OPTION_TYPES_PADDING = 0, -- IPU_MSG_NODE_OPTION_TYPES_N --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) -- --enum ipu7_msg_link_type { -- IPU_MSG_LINK_TYPE_PAD = 0, -- IPU_MSG_LINK_TYPE_GENERIC = 1, -- IPU_MSG_LINK_TYPE_N --}; -- --enum ipu7_msg_link_option_types { -- IPU_MSG_LINK_OPTION_TYPES_PADDING = 0, -- IPU_MSG_LINK_OPTION_TYPES_CMPRS = 1, -- IPU_MSG_LINK_OPTION_TYPES_N --}; -- --enum ipu7_msg_link_cmprs_option_bit_depth { -- IPU_MSG_LINK_CMPRS_OPTION_8BPP = 0, -- IPU_MSG_LINK_CMPRS_OPTION_10BPP = 1, -- IPU_MSG_LINK_CMPRS_OPTION_12BPP = 2, --}; -- --#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM (128U) --#define IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE (5U) --#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_NUM_MAX \ -- (IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM - 1U) -- --struct ipu7_msg_link_cmprs_plane_desc { -- u8 plane_enable; -- u8 cmprs_enable; -- u8 encoder_plane_id; -- u8 decoder_plane_id; -- u8 cmprs_is_lossy; -- u8 cmprs_is_footprint; -- u8 bit_depth; -- u8 space_saving_numerator; -- u32 pixels_offset; -- u32 ts_offset; -- u32 tile_row_to_tile_row_stride; -- u32 rows_of_tiles; -- u32 lossy_cfg[IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE]; --}; -- --#define IPU_MSG_LINK_CMPRS_MAX_PLANES (2U) --#define IPU_MSG_LINK_CMPRS_NO_ALIGN_INTERVAL (0U) --#define IPU_MSG_LINK_CMPRS_MIN_ALIGN_INTERVAL (16U) --#define IPU_MSG_LINK_CMPRS_MAX_ALIGN_INTERVAL (1024U) --struct ipu7_msg_link_cmprs_option { -- struct ia_gofo_tlv_header header; -- u32 cmprs_buf_size; -- u16 align_interval; -- u8 reserved[2]; -- struct ipu7_msg_link_cmprs_plane_desc plane_descs[2]; --}; -- --struct ipu7_msg_link_ep { -- u8 node_ctx_id; -- u8 term_id; --}; -- --struct ipu7_msg_link_ep_pair { -- struct ipu7_msg_link_ep ep_src; -- struct ipu7_msg_link_ep ep_dst; --}; -- --#define IPU_MSG_LINK_FOREIGN_KEY_NONE (65535U) --#define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) --#define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) --#define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) --#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) -- --struct ipu7_msg_link { -- struct ia_gofo_tlv_header tlv_header; -- struct ipu7_msg_link_ep_pair endpoints; -- u16 foreign_key; -- u8 streaming_mode; -- u8 pbk_id; -- u8 pbk_slot_id; -- u8 delayed_link; -- u8 reserved[2]; -- struct ia_gofo_tlv_list link_options; --}; -- --#pragma pack(pop) -- --enum ipu7_msg_dev_state { -- IPU_MSG_DEV_STATE_CLOSED = 0, -- IPU_MSG_DEV_STATE_OPEN_WAIT = 1, -- IPU_MSG_DEV_STATE_OPEN = 2, -- IPU_MSG_DEV_STATE_CLOSE_WAIT = 3, -- IPU_MSG_DEV_STATE_N --}; -- --enum ipu7_msg_graph_state { -- IPU_MSG_GRAPH_STATE_CLOSED = 0, -- IPU_MSG_GRAPH_STATE_OPEN_WAIT = 1, -- IPU_MSG_GRAPH_STATE_OPEN = 2, -- IPU_MSG_GRAPH_STATE_CLOSE_WAIT = 3, -- IPU_MSG_GRAPH_STATE_N --}; -- --enum ipu7_msg_task_state { -- IPU_MSG_TASK_STATE_DONE = 0, -- IPU_MSG_TASK_STATE_WAIT_DONE = 1, -- IPU_MSG_TASK_STATE_N --}; -- --enum ipu7_msg_err_groups { -- IPU_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -- IPU_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -- IPU_MSG_ERR_GROUP_DEVICE = 2, -- IPU_MSG_ERR_GROUP_GRAPH = 3, -- IPU_MSG_ERR_GROUP_TASK = 4, -- IPU_MSG_ERR_GROUP_N, --}; -- --#pragma pack(push, 1) --struct ipu7_msg_task { -- struct ia_gofo_msg_header header; -- u8 graph_id; -- u8 profile_idx; -- u8 node_ctx_id; -- u8 frame_id; -- u8 frag_id; -- u8 req_done_msg; -- u8 req_done_irq; -- u8 reserved[1]; -- ipu7_msg_teb_t payload_reuse_bm; -- ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; --}; -- --struct ipu7_msg_task_done { -- struct ia_gofo_msg_header_ack header; -- u8 graph_id; -- u8 frame_id; -- u8 node_ctx_id; -- u8 profile_idx; -- u8 frag_id; -- u8 reserved[3]; --}; -- --enum ipu7_msg_err_task { -- IPU_MSG_ERR_TASK_OK = IA_GOFO_MSG_ERR_OK, -- IPU_MSG_ERR_TASK_GRAPH_ID = 1, -- IPU_MSG_ERR_TASK_NODE_CTX_ID = 2, -- IPU_MSG_ERR_TASK_PROFILE_IDX = 3, -- IPU_MSG_ERR_TASK_CTX_MEMORY_TASK = 4, -- IPU_MSG_ERR_TASK_TERM_PAYLOAD_PTR = 5, -- IPU_MSG_ERR_TASK_FRAME_ID = 6, -- IPU_MSG_ERR_TASK_FRAG_ID = 7, -- IPU_MSG_ERR_TASK_EXEC_EXT = 8, -- IPU_MSG_ERR_TASK_EXEC_SBX = 9, -- IPU_MSG_ERR_TASK_EXEC_INT = 10, -- IPU_MSG_ERR_TASK_EXEC_UNKNOWN = 11, -- IPU_MSG_ERR_TASK_PRE_EXEC = 12, -- IPU_MSG_ERR_TASK_N --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --enum ipu7_msg_term_type { -- IPU_MSG_TERM_TYPE_PAD = 0, -- IPU_MSG_TERM_TYPE_BASE, -- IPU_MSG_TERM_TYPE_N, --}; -- --#define IPU_MSG_TERM_EVENT_TYPE_NONE 0U --#define IPU_MSG_TERM_EVENT_TYPE_PROGRESS 1U --#define IPU_MSG_TERM_EVENT_TYPE_N (IPU_MSG_TERM_EVENT_TYPE_PROGRESS + 1U) -- --struct ipu7_msg_term { -- struct ia_gofo_tlv_header tlv_header; -- u8 term_id; -- u8 event_req_bm; -- u8 reserved[2]; -- u32 payload_size; -- struct ia_gofo_tlv_list term_options; --}; -- --enum ipu7_msg_term_option_types { -- IPU_MSG_TERM_OPTION_TYPES_PADDING = 0, -- IPU_MSG_TERM_OPTION_TYPES_N --}; -- --struct ipu7_msg_term_event { -- struct ia_gofo_msg_header header; -- u8 graph_id; -- u8 frame_id; -- u8 node_ctx_id; -- u8 profile_idx; -- u8 frag_id; -- u8 term_id; -- u8 event_type; -- u8 reserved[1]; -- u64 event_ts; --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --#define IPU_MSG_DEVICE_SEND_MSG_ENABLED 1U --#define IPU_MSG_DEVICE_SEND_MSG_DISABLED 0U -- --#define IPU_MSG_DEVICE_OPEN_SEND_RESP BIT(0) --#define IPU_MSG_DEVICE_OPEN_SEND_IRQ BIT(1) -- --#define IPU_MSG_DEVICE_CLOSE_SEND_RESP BIT(0) --#define IPU_MSG_DEVICE_CLOSE_SEND_IRQ BIT(1) -- --struct ipu7_msg_dev_open { -- struct ia_gofo_msg_header header; -- u32 max_graphs; -- u8 dev_msg_map; -- u8 enable_power_gating; -- u8 reserved[2]; --}; -- --struct ipu7_msg_dev_open_ack { -- struct ia_gofo_msg_header_ack header; --}; -- --struct ipu7_msg_dev_close { -- struct ia_gofo_msg_header header; -- u8 dev_msg_map; -- u8 reserved[7]; --}; -- --struct ipu7_msg_dev_close_ack { -- struct ia_gofo_msg_header_ack header; --}; -- --enum ipu7_msg_err_device { -- IPU_MSG_ERR_DEVICE_OK = IA_GOFO_MSG_ERR_OK, -- IPU_MSG_ERR_DEVICE_MAX_GRAPHS = 1, -- IPU_MSG_ERR_DEVICE_MSG_MAP = 2, -- IPU_MSG_ERR_DEVICE_N --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) --#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U --#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U -- --#define IPU_MSG_GRAPH_OPEN_SEND_RESP BIT(0) --#define IPU_MSG_GRAPH_OPEN_SEND_IRQ BIT(1) -- --#define IPU_MSG_GRAPH_CLOSE_SEND_RESP BIT(0) --#define IPU_MSG_GRAPH_CLOSE_SEND_IRQ BIT(1) -- --struct ipu7_msg_graph_open { -- struct ia_gofo_msg_header header; -- struct ia_gofo_tlv_list nodes; -- struct ia_gofo_tlv_list links; -- u8 graph_id; -- u8 graph_msg_map; -- u8 reserved[6]; --}; -- --enum ipu7_msg_graph_ack_option_types { -- IPU_MSG_GRAPH_ACK_OPTION_TYPES_PADDING = 0, -- IPU_MSG_GRAPH_ACK_TASK_Q_INFO, -- IPU_MSG_GRAPH_ACK_OPTION_TYPES_N --}; -- --struct ipu7_msg_graph_open_ack_task_q_info { -- struct ia_gofo_tlv_header header; -- u8 node_ctx_id; -- u8 q_id; -- u8 reserved[2]; --}; -- --struct ipu7_msg_graph_open_ack { -- struct ia_gofo_msg_header_ack header; -- u8 graph_id; -- u8 reserved[7]; --}; -- --struct ipu7_msg_graph_close { -- struct ia_gofo_msg_header header; -- u8 graph_id; -- u8 graph_msg_map; -- u8 reserved[6]; --}; -- --struct ipu7_msg_graph_close_ack { -- struct ia_gofo_msg_header_ack header; -- u8 graph_id; -- u8 reserved[7]; --}; -- --enum ipu7_msg_err_graph { -- IPU_MSG_ERR_GRAPH_OK = IA_GOFO_MSG_ERR_OK, -- IPU_MSG_ERR_GRAPH_GRAPH_STATE = 1, -- IPU_MSG_ERR_GRAPH_MAX_GRAPHS = 2, -- IPU_MSG_ERR_GRAPH_GRAPH_ID = 3, -- IPU_MSG_ERR_GRAPH_NODE_CTX_ID = 4, -- IPU_MSG_ERR_GRAPH_NODE_RSRC_ID = 5, -- IPU_MSG_ERR_GRAPH_PROFILE_IDX = 6, -- IPU_MSG_ERR_GRAPH_TERM_ID = 7, -- IPU_MSG_ERR_GRAPH_TERM_PAYLOAD_SIZE = 8, -- IPU_MSG_ERR_GRAPH_LINK_NODE_CTX_ID = 9, -- IPU_MSG_ERR_GRAPH_LINK_TERM_ID = 10, -- IPU_MSG_ERR_GRAPH_PROFILE_TYPE = 11, -- IPU_MSG_ERR_GRAPH_NUM_FRAGS = 12, -- IPU_MSG_ERR_GRAPH_QUEUE_ID_USAGE = 13, -- IPU_MSG_ERR_GRAPH_QUEUE_OPEN = 14, -- IPU_MSG_ERR_GRAPH_QUEUE_CLOSE = 15, -- IPU_MSG_ERR_GRAPH_QUEUE_ID_TASK_REQ_MISMATCH = 16, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_FGRAPH = 17, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE = 18, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE_PROFILE = 19, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_TERM = 20, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_LINK = 21, -- IPU_MSG_ERR_GRAPH_CTX_MSG_MAP = 22, -- IPU_MSG_ERR_GRAPH_CTX_FOREIGN_KEY = 23, -- IPU_MSG_ERR_GRAPH_CTX_STREAMING_MODE = 24, -- IPU_MSG_ERR_GRAPH_CTX_PBK_RSRC = 25, -- IPU_MSG_ERR_GRAPH_UNSUPPORTED_EVENT_TYPE = 26, -- IPU_MSG_ERR_GRAPH_TOO_MANY_EVENTS = 27, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_CMPRS = 28, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_ALIGN_INTERVAL = 29, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_PLANE_ID = 30, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_UNSUPPORTED_MODE = 31, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_BIT_DEPTH = 32, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_STRIDE_ALIGNMENT = 33, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_SUB_BUFFER_ALIGNMENT = 34, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_ORDER = 35, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_OVERLAP = 36, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_BUFFER_TOO_SMALL = 37, -- IPU_MSG_ERR_GRAPH_CTX_DELAYED_LINK = 38, -- IPU_MSG_ERR_GRAPH_N --}; -- --#pragma pack(pop) -- --#define FWPS_MSG_ABI_MAX_INPUT_QUEUES (60U) --#define FWPS_MSG_ABI_MAX_OUTPUT_QUEUES (2U) --#define FWPS_MSG_ABI_MAX_QUEUES \ -- (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES + FWPS_MSG_ABI_MAX_INPUT_QUEUES) -- --#define FWPS_MSG_ABI_OUT_ACK_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID) --#define FWPS_MSG_ABI_OUT_LOG_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID) --#if (FWPS_MSG_ABI_OUT_LOG_QUEUE_ID >= FWPS_MSG_ABI_MAX_OUTPUT_QUEUES) --#error "Maximum output queues configuration is too small to fit ACK and LOG \ --queues" --#endif --#define FWPS_MSG_ABI_IN_DEV_QUEUE_ID (IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID) --#define FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID (3U) --#define FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID \ -- (FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID + 1U) -- --#if (FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID >= FWPS_MSG_ABI_MAX_INPUT_QUEUES) --#error "Maximum queues configuration is too small to fit minimum number of \ --useful queues" --#endif -- --#define FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID (FWPS_MSG_ABI_MAX_QUEUES - 1U) --#define FWPS_MSG_ABI_IN_MAX_TASK_QUEUES \ -- (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID - \ -- FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID + 1U) --#define FWPS_MSG_ABI_OUT_FIRST_QUEUE_ID (FWPS_MSG_ABI_OUT_ACK_QUEUE_ID) --#define FWPS_MSG_ABI_OUT_LAST_QUEUE_ID (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES - 1U) --#define FWPS_MSG_ABI_IN_FIRST_QUEUE_ID (FWPS_MSG_ABI_IN_DEV_QUEUE_ID) --#define FWPS_MSG_ABI_IN_LAST_QUEUE_ID (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID) -- --#define FWPS_MSG_HOST2FW_MAX_SIZE (2U * 1024U) --#define FWPS_MSG_FW2HOST_MAX_SIZE (256U) -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -deleted file mode 100644 -index 0af04c8c6a88..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -+++ /dev/null -@@ -1,24 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ --#define IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ -- --#include -- --#include "ipu7_fw_boot_abi.h" --#include "ipu7_fw_config_abi.h" -- --struct ipu7_psys_config { -- u32 use_debug_manifest; -- u32 timeout_val_ms; -- u32 compression_support_enabled; -- struct ia_gofo_logger_config logger_config; -- struct ipu7_wdt_abi wdt_config; -- u8 ipu_psys_debug_bitmask; -- u8 padding[3]; --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -deleted file mode 100644 -index bfa5258d5b97..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -+++ /dev/null -@@ -1,49 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_SYSCOM_ABI_H --#define IPU7_FW_SYSCOM_ABI_H -- --#include -- --#include "ipu7_fw_common_abi.h" -- --#pragma pack(push, 1) --#define SYSCOM_QUEUE_MIN_CAPACITY 2U -- --struct syscom_queue_params_config { -- ia_gofo_addr_t token_array_mem; -- u16 token_size_in_bytes; -- u16 max_capacity; --}; -- --struct syscom_config_s { -- u16 max_output_queues; -- u16 max_input_queues; --}; -- --#pragma pack(pop) -- --static inline struct syscom_queue_params_config * --syscom_config_get_queue_configs(struct syscom_config_s *config) --{ -- return (struct syscom_queue_params_config *)(&config[1]); --} -- --static inline const struct syscom_queue_params_config * --syscom_config_get_queue_configs_const(const struct syscom_config_s *config) --{ -- return (const struct syscom_queue_params_config *)(&config[1]); --} -- --#pragma pack(push, 1) --struct syscom_queue_indices_s { -- u32 read_index; -- u32 write_index; --}; -- --#pragma pack(pop) -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.c b/drivers/media/pci/intel/ipu7/ipu7-boot.c -deleted file mode 100644 -index a88182d70173..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-boot.c -+++ /dev/null -@@ -1,431 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2022 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_boot_abi.h" -- --#include "ipu7.h" --#include "ipu7-boot.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-dma.h" --#include "ipu7-platform-regs.h" --#include "ipu7-syscom.h" -- --#define IPU_FW_START_STOP_TIMEOUT 2000 --#define IPU_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC) --#define BOOT_STATE_IS_CRITICAL(s) IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s) --#define BOOT_STATE_IS_READY(s) ((s) == IA_GOFO_FW_BOOT_STATE_READY) --#define BOOT_STATE_IS_INACTIVE(s) ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE) -- --struct ipu7_boot_context { -- u32 base; -- u32 dmem_address; -- u32 status_ctrl_reg; -- u32 fw_start_address_reg; -- u32 fw_code_base_reg; --}; -- --static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = { -- { -- /* ISYS */ -- .dmem_address = IPU_ISYS_DMEM_OFFSET, -- .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS, -- .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR, -- .fw_code_base_reg = IS_UC_CTRL_BASE -- }, -- { -- /* PSYS */ -- .dmem_address = IPU_PSYS_DMEM_OFFSET, -- .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS, -- .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR, -- .fw_code_base_reg = PS_UC_CTRL_BASE -- } --}; -- --static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev, -- enum ia_gofo_buttress_reg_id reg) --{ -- u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX; -- -- return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg); --} -- --static void write_fw_boot_param(const struct ipu7_bus_device *adev, -- enum ia_gofo_buttress_reg_id reg, -- u32 val) --{ -- void __iomem *base = adev->isp->base; -- -- dev_dbg(&adev->auxdev.dev, -- "write boot param reg: %d addr: %x val: 0x%x\n", -- reg, get_fw_boot_reg_addr(adev, reg), val); -- writel(val, base + get_fw_boot_reg_addr(adev, reg)); --} -- --static u32 read_fw_boot_param(const struct ipu7_bus_device *adev, -- enum ia_gofo_buttress_reg_id reg) --{ -- void __iomem *base = adev->isp->base; -- -- return readl(base + get_fw_boot_reg_addr(adev, reg)); --} -- --static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- const struct device *dev = &adev->auxdev.dev; -- u32 ucx_ctrl_status = ctx->status_ctrl_reg; -- u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT; -- void __iomem *base = adev->isp->base; -- u32 val, val2; -- int ret; -- -- dev_dbg(dev, "cell enter reset...\n"); -- val = readl(base + ucx_ctrl_status); -- dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n", -- ucx_ctrl_status, val); -- -- dev_dbg(dev, "force cell reset...\n"); -- val |= UCX_CTL_RESET; -- val &= ~UCX_CTL_RUN; -- -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ucx_ctrl_status, val); -- writel(val, base + ucx_ctrl_status); -- -- ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -- (val2 & 0x3U) == (val & 0x3U), 100, timeout); -- if (ret) { -- dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2); -- return -ETIMEDOUT; -- } -- -- dev_dbg(dev, "cell exit reset...\n"); -- val = readl(base + ucx_ctrl_status); -- WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN), -- "cell status 0x%x", val); -- -- val &= ~(UCX_CTL_RESET | UCX_CTL_RUN); -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ucx_ctrl_status, val); -- writel(val, base + ucx_ctrl_status); -- -- ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -- (val2 & 0x3U) == (val & 0x3U), 100, timeout); -- if (ret) { -- dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2); -- return -ETIMEDOUT; -- } -- -- return 0; --} -- --static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- void __iomem *base = adev->isp->base; -- const struct device *dev = &adev->auxdev.dev; -- u32 val; -- -- dev_dbg(dev, "starting cell...\n"); -- val = readl(base + ctx->status_ctrl_reg); -- WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN)); -- -- val &= ~UCX_CTL_RESET; -- val |= UCX_CTL_RUN; -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ctx->status_ctrl_reg, val); -- writel(val, base + ctx->status_ctrl_reg); --} -- --static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- void __iomem *base = adev->isp->base; -- const struct device *dev = &adev->auxdev.dev; -- u32 val; -- -- dev_dbg(dev, "stopping cell...\n"); -- -- val = readl(base + ctx->status_ctrl_reg); -- val &= ~UCX_CTL_RUN; -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ctx->status_ctrl_reg, val); -- writel(val, base + ctx->status_ctrl_reg); -- -- /* Wait for uC transactions complete */ -- usleep_range(10, 20); -- -- val = readl(base + ctx->status_ctrl_reg); -- val |= UCX_CTL_RESET; -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ctx->status_ctrl_reg, val); -- writel(val, base + ctx->status_ctrl_reg); --} -- --static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- void __iomem *base = adev->isp->base; -- -- dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n", -- ctx->fw_start_address_reg, adev->fw_entry); -- writel(adev->fw_entry, base + ctx->fw_start_address_reg); -- -- return ipu7_boot_cell_reset(adev); --} -- --static void init_boot_config(struct ia_gofo_boot_config *boot_config, -- u32 length, u8 major) --{ -- /* syscom version, new syscom2 version */ -- boot_config->length = length; -- boot_config->config_version.major = 1U; -- boot_config->config_version.minor = 0U; -- boot_config->config_version.subminor = 0U; -- boot_config->config_version.patch = 0U; -- -- /* msg version for task interface */ -- boot_config->client_version_support.num_versions = 1U; -- boot_config->client_version_support.versions[0].major = major; -- boot_config->client_version_support.versions[0].minor = 0U; -- boot_config->client_version_support.versions[0].subminor = 0U; -- boot_config->client_version_support.versions[0].patch = 0U; --} -- --int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -- struct syscom_queue_config *qconfigs, -- int num_queues, u32 uc_freq, -- dma_addr_t subsys_config, u8 major) --{ -- u32 total_queue_size_aligned = 0; -- struct ipu7_syscom_context *syscom = adev->syscom; -- struct ia_gofo_boot_config *boot_config; -- struct syscom_queue_params_config *cfgs; -- struct device *dev = &adev->auxdev.dev; -- struct syscom_config_s *syscfg; -- dma_addr_t queue_mem_dma_ptr; -- void *queue_mem_ptr; -- unsigned int i; -- -- dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n", -- num_queues, uc_freq, &subsys_config); -- /* Allocate boot config. */ -- adev->boot_config_size = -- sizeof(*cfgs) * num_queues + sizeof(*boot_config); -- adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size, -- &adev->boot_config_dma_addr, -- GFP_KERNEL, 0); -- if (!adev->boot_config) { -- dev_err(dev, "Failed to allocate boot config.\n"); -- return -ENOMEM; -- } -- -- boot_config = adev->boot_config; -- memset(boot_config, 0, sizeof(struct ia_gofo_boot_config)); -- init_boot_config(boot_config, adev->boot_config_size, major); -- boot_config->subsys_config = subsys_config; -- -- boot_config->uc_tile_frequency = uc_freq; -- boot_config->uc_tile_frequency_units = -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ; -- boot_config->syscom_context_config.max_output_queues = -- syscom->num_output_queues; -- boot_config->syscom_context_config.max_input_queues = -- syscom->num_input_queues; -- -- ipu7_dma_sync_single(adev, adev->boot_config_dma_addr, -- adev->boot_config_size); -- -- for (i = 0; i < num_queues; i++) { -- u32 queue_size = qconfigs[i].max_capacity * -- qconfigs[i].token_size_in_bytes; -- -- queue_size = ALIGN(queue_size, 64U); -- total_queue_size_aligned += queue_size; -- qconfigs[i].queue_size = queue_size; -- } -- -- /* Allocate queue memory */ -- syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned, -- &syscom->queue_mem_dma_addr, -- GFP_KERNEL, 0); -- if (!syscom->queue_mem) { -- dev_err(dev, "Failed to allocate queue memory.\n"); -- return -ENOMEM; -- } -- syscom->queue_mem_size = total_queue_size_aligned; -- -- syscfg = &boot_config->syscom_context_config; -- cfgs = ipu7_syscom_get_queue_config(syscfg); -- queue_mem_ptr = syscom->queue_mem; -- queue_mem_dma_ptr = syscom->queue_mem_dma_addr; -- for (i = 0; i < num_queues; i++) { -- cfgs[i].token_array_mem = queue_mem_dma_ptr; -- cfgs[i].max_capacity = qconfigs[i].max_capacity; -- cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes; -- qconfigs[i].token_array_mem = queue_mem_ptr; -- queue_mem_dma_ptr += qconfigs[i].queue_size; -- queue_mem_ptr += qconfigs[i].queue_size; -- } -- -- ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr, -- total_queue_size_aligned); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7"); -- --void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev) --{ -- struct ipu7_syscom_context *syscom = adev->syscom; -- -- if (syscom->queue_mem) { -- ipu7_dma_free(adev, syscom->queue_mem_size, -- syscom->queue_mem, -- syscom->queue_mem_dma_addr, 0); -- syscom->queue_mem = NULL; -- syscom->queue_mem_dma_addr = 0; -- } -- -- if (adev->boot_config) { -- ipu7_dma_free(adev, adev->boot_config_size, -- adev->boot_config, -- adev->boot_config_dma_addr, 0); -- adev->boot_config = NULL; -- adev->boot_config_dma_addr = 0; -- } --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7"); -- --int ipu7_boot_start_fw(const struct ipu7_bus_device *adev) --{ -- const struct device *dev = &adev->auxdev.dev; -- u32 timeout = IPU_FW_START_STOP_TIMEOUT; -- void __iomem *base = adev->isp->base; -- u32 boot_state, last_boot_state; -- u32 indices_addr, msg_ver, id; -- int ret; -- -- ret = ipu7_boot_cell_init(adev); -- if (ret) -- return ret; -- -- dev_dbg(dev, "start booting fw...\n"); -- /* store "uninit" state to syscom/boot state reg */ -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -- IA_GOFO_FW_BOOT_STATE_UNINIT); -- /* -- * Set registers to zero -- * (not strictly required, but recommended for diagnostics) -- */ -- write_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0); -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0); -- /* store firmware configuration address */ -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID, -- adev->boot_config_dma_addr); -- -- /* Kick uC, then wait for boot complete */ -- ipu7_boot_cell_start(adev); -- -- last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT; -- while (timeout--) { -- boot_state = read_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_STATE_ID); -- if (boot_state != last_boot_state) { -- dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n", -- last_boot_state, boot_state); -- last_boot_state = boot_state; -- } -- if (BOOT_STATE_IS_CRITICAL(boot_state) || -- BOOT_STATE_IS_READY(boot_state)) -- break; -- usleep_range(1000, 1200); -- } -- -- if (BOOT_STATE_IS_CRITICAL(boot_state)) { -- ipu7_dump_fw_error_log(adev); -- dev_err(dev, "critical boot state error 0x%x\n", boot_state); -- return -EINVAL; -- } else if (!BOOT_STATE_IS_READY(boot_state)) { -- dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state); -- return -ETIMEDOUT; -- } -- dev_dbg(dev, "fw boot done.\n"); -- -- /* Get FW syscom queue indices addr */ -- id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID; -- indices_addr = read_fw_boot_param(adev, id); -- adev->syscom->queue_indices = base + indices_addr; -- dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr); -- -- /* Get message version. */ -- msg_ver = read_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID); -- dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7"); -- --int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev) --{ -- const struct device *dev = &adev->auxdev.dev; -- u32 timeout = IPU_FW_START_STOP_TIMEOUT; -- u32 boot_state; -- -- boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); -- if (BOOT_STATE_IS_CRITICAL(boot_state) || -- !BOOT_STATE_IS_READY(boot_state)) { -- dev_err(dev, "fw not ready for shutdown, state 0x%x\n", -- boot_state); -- return -EBUSY; -- } -- -- /* Issue shutdown to start shutdown process */ -- dev_dbg(dev, "stopping fw...\n"); -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -- IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD); -- while (timeout--) { -- boot_state = read_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_STATE_ID); -- if (BOOT_STATE_IS_CRITICAL(boot_state) || -- BOOT_STATE_IS_INACTIVE(boot_state)) -- break; -- usleep_range(1000, 1200); -- } -- -- if (BOOT_STATE_IS_CRITICAL(boot_state)) { -- ipu7_dump_fw_error_log(adev); -- dev_err(dev, "critical boot state error 0x%x\n", boot_state); -- return -EINVAL; -- } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) { -- dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state); -- return -ETIMEDOUT; -- } -- -- ipu7_boot_cell_stop(adev); -- dev_dbg(dev, "stop fw done.\n"); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7"); -- --u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev) --{ -- return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.h b/drivers/media/pci/intel/ipu7/ipu7-boot.h -deleted file mode 100644 -index 5600be849931..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-boot.h -+++ /dev/null -@@ -1,25 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2022 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BOOT_H --#define IPU7_BOOT_H -- --#include -- --struct ipu7_bus_device; --struct syscom_queue_config; -- --#define FW_QUEUE_CONFIG_SIZE(num_queues) \ -- (sizeof(struct syscom_queue_config) * (num_queues)) -- --int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -- struct syscom_queue_config *qconfigs, -- int num_queues, u32 uc_freq, -- dma_addr_t subsys_config, u8 major); --void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev); --int ipu7_boot_start_fw(const struct ipu7_bus_device *adev); --int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev); --u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.c b/drivers/media/pci/intel/ipu7/ipu7-bus.c -deleted file mode 100644 -index 7da44fde002a..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-bus.c -+++ /dev/null -@@ -1,158 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-boot.h" --#include "ipu7-dma.h" -- --static int bus_pm_runtime_suspend(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- int ret; -- -- ret = pm_generic_runtime_suspend(dev); -- if (ret) -- return ret; -- -- ret = ipu_buttress_powerdown(dev, adev->ctrl); -- if (!ret) -- return 0; -- -- dev_err(dev, "power down failed!\n"); -- -- /* Powering down failed, attempt to resume device now */ -- ret = pm_generic_runtime_resume(dev); -- if (!ret) -- return -EBUSY; -- -- return -EIO; --} -- --static int bus_pm_runtime_resume(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- int ret; -- -- ret = ipu_buttress_powerup(dev, adev->ctrl); -- if (ret) -- return ret; -- -- ret = pm_generic_runtime_resume(dev); -- if (ret) -- goto out_err; -- -- return 0; -- --out_err: -- ipu_buttress_powerdown(dev, adev->ctrl); -- -- return -EBUSY; --} -- --static struct dev_pm_domain ipu7_bus_pm_domain = { -- .ops = { -- .runtime_suspend = bus_pm_runtime_suspend, -- .runtime_resume = bus_pm_runtime_resume, -- }, --}; -- --static DEFINE_MUTEX(ipu7_bus_mutex); --static void ipu7_bus_release(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- -- kfree(adev->pdata); -- kfree(adev); --} -- --struct ipu7_bus_device * --ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -- void *pdata, const struct ipu_buttress_ctrl *ctrl, -- const char *name) --{ -- struct auxiliary_device *auxdev; -- struct ipu7_bus_device *adev; -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- int ret; -- -- adev = kzalloc(sizeof(*adev), GFP_KERNEL); -- if (!adev) -- return ERR_PTR(-ENOMEM); -- -- adev->isp = isp; -- adev->ctrl = ctrl; -- adev->pdata = pdata; -- auxdev = &adev->auxdev; -- auxdev->name = name; -- auxdev->id = (pci_domain_nr(pdev->bus) << 16) | -- PCI_DEVID(pdev->bus->number, pdev->devfn); -- -- auxdev->dev.parent = parent; -- auxdev->dev.release = ipu7_bus_release; -- -- ret = auxiliary_device_init(auxdev); -- if (ret < 0) { -- dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", -- ret); -- kfree(adev); -- return ERR_PTR(ret); -- } -- -- dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain); -- -- pm_runtime_forbid(&adev->auxdev.dev); -- pm_runtime_enable(&adev->auxdev.dev); -- -- return adev; --} -- --int ipu7_bus_add_device(struct ipu7_bus_device *adev) --{ -- struct auxiliary_device *auxdev = &adev->auxdev; -- int ret; -- -- ret = auxiliary_device_add(auxdev); -- if (ret) { -- auxiliary_device_uninit(auxdev); -- return ret; -- } -- -- mutex_lock(&ipu7_bus_mutex); -- list_add(&adev->list, &adev->isp->devices); -- mutex_unlock(&ipu7_bus_mutex); -- -- pm_runtime_allow(&auxdev->dev); -- -- return 0; --} -- --void ipu7_bus_del_devices(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu7_bus_device *adev, *save; -- -- mutex_lock(&ipu7_bus_mutex); -- -- list_for_each_entry_safe(adev, save, &isp->devices, list) { -- pm_runtime_disable(&adev->auxdev.dev); -- list_del(&adev->list); -- auxiliary_device_delete(&adev->auxdev); -- auxiliary_device_uninit(&adev->auxdev); -- } -- -- mutex_unlock(&ipu7_bus_mutex); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.h b/drivers/media/pci/intel/ipu7/ipu7-bus.h -deleted file mode 100644 -index 45157df16e90..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-bus.h -+++ /dev/null -@@ -1,69 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BUS_H --#define IPU7_BUS_H -- --#include --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_boot_abi.h" -- --#include "ipu7-syscom.h" -- --struct pci_dev; --struct ipu_buttress_ctrl; --struct ipu7_mmu; --struct ipu7_device; -- --enum ipu7_subsys { -- IPU_IS = 0, -- IPU_PS = 1, -- IPU_SUBSYS_NUM = 2, --}; -- --struct ipu7_bus_device { -- struct auxiliary_device auxdev; -- const struct auxiliary_driver *auxdrv; -- const struct ipu7_auxdrv_data *auxdrv_data; -- struct list_head list; -- enum ipu7_subsys subsys; -- void *pdata; -- struct ipu7_mmu *mmu; -- struct ipu7_device *isp; -- const struct ipu_buttress_ctrl *ctrl; -- u64 dma_mask; -- struct sg_table fw_sgt; -- u32 fw_entry; -- struct ipu7_syscom_context *syscom; -- struct ia_gofo_boot_config *boot_config; -- dma_addr_t boot_config_dma_addr; -- u32 boot_config_size; --}; -- --struct ipu7_auxdrv_data { -- irqreturn_t (*isr)(struct ipu7_bus_device *adev); -- irqreturn_t (*isr_threaded)(struct ipu7_bus_device *adev); -- bool wake_isr_thread; --}; -- --#define to_ipu7_bus_device(_dev) \ -- container_of(to_auxiliary_dev(_dev), struct ipu7_bus_device, auxdev) --#define auxdev_to_adev(_auxdev) \ -- container_of(_auxdev, struct ipu7_bus_device, auxdev) --#define ipu7_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) -- --struct ipu7_bus_device * --ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -- void *pdata, const struct ipu_buttress_ctrl *ctrl, -- const char *name); --int ipu7_bus_add_device(struct ipu7_bus_device *adev); --void ipu7_bus_del_devices(struct pci_dev *pdev); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h b/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -deleted file mode 100644 -index 3eafd6a3813d..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -+++ /dev/null -@@ -1,461 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BUTTRESS_REGS_H --#define IPU7_BUTTRESS_REGS_H -- --#define BUTTRESS_REG_IRQ_STATUS 0x2000 --#define BUTTRESS_REG_IRQ_STATUS_UNMASKED 0x2004 --#define BUTTRESS_REG_IRQ_ENABLE 0x2008 --#define BUTTRESS_REG_IRQ_CLEAR 0x200c --#define BUTTRESS_REG_IRQ_MASK 0x2010 --#define BUTTRESS_REG_TSC_CMD 0x2014 --#define BUTTRESS_REG_TSC_CTL 0x2018 --#define BUTTRESS_REG_TSC_LO 0x201c --#define BUTTRESS_REG_TSC_HI 0x2020 -- --/* valid for PTL */ --#define BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030 --#define BUTTRESS_REG_PB_TIMESTAMP_HI 0x2034 --#define BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038 -- --#define BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100 --#define BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104 --#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ 0x2108 --#define BUTTRESS_REG_PS_DOMAINS_STATUS 0x2110 --#define BUTTRESS_REG_PWR_STATUS 0x2114 --#define BUTTRESS_REG_PS_WORKPOINT_REQ_SHADOW 0x2120 --#define BUTTRESS_REG_IS_WORKPOINT_REQ_SHADOW 0x2124 --#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ_SHADOW 0x2128 --#define BUTTRESS_REG_ISPS_WORKPOINT_DOWNLOAD 0x212c --#define BUTTRESS_REG_PG_FLOW_OVERRIDE 0x2180 --#define BUTTRESS_REG_GLOBAL_OVERRIDE_UNGATE_CTL 0x2184 --#define BUTTRESS_REG_PWR_FSM_CTL 0x2188 --#define BUTTRESS_REG_IDLE_WDT 0x218c --#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_EN 0x2190 --#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_ADDR 0x2194 --#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_DATA 0x2198 --#define BUTTRESS_REG_POWER_EN_DELAY 0x219c --#define IPU7_BUTTRESS_REG_LTR_CONTROL 0x21a0 --#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4 --#define IPU7_BUTTRESS_REG_INT_FRM_PUNIT 0x21a8 --#define IPU8_BUTTRESS_REG_LTR_CONTROL 0x21a4 --#define IPU8_BUTTRESS_REG_NDE_CONTROL 0x21a8 --#define IPU8_BUTTRESS_REG_INT_FRM_PUNIT 0x21ac --#define BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0 --#define BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4 --#define BUTTRESS_REG_DVFS_FSM_STATUS 0x21b8 --#define BUTTRESS_REG_PS_PLL_ENABLE 0x21bc --#define BUTTRESS_REG_D2D_CTL 0x21d4 --#define BUTTRESS_REG_IB_CLK_CTL 0x21d8 --#define BUTTRESS_REG_IB_CRO_CLK_CTL 0x21dc --#define BUTTRESS_REG_FUNC_FUSES 0x21e0 --#define BUTTRESS_REG_ISOCH_CTL 0x21e4 --#define BUTTRESS_REG_WORKPOINT_CTL 0x21f0 --#define BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS 0x2200 --#define BUTTRESS_REG_DRV_IS_UCX_START_ADDR 0x2204 --#define BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS 0x2208 --#define BUTTRESS_REG_DRV_PS_UCX_START_ADDR 0x220c --#define BUTTRESS_REG_DRV_UCX_RESET_CFG 0x2210 -- --/* configured by CSE */ --#define BUTTRESS_REG_CSE_IS_UCX_CONTROL_STATUS 0x2300 --#define BUTTRESS_REG_CSE_IS_UCX_START_ADDR 0x2304 --#define BUTTRESS_REG_CSE_PS_UCX_CONTROL_STATUS 0x2308 --#define BUTTRESS_REG_CSE_PS_UCX_START_ADDR 0x230c -- --#define BUTTRESS_REG_CAMERA_MASK 0x2310 --#define BUTTRESS_REG_FW_CTL 0x2314 --#define BUTTRESS_REG_SECURITY_CTL 0x2318 --#define BUTTRESS_REG_FUNCTIONAL_FW_SETUP 0x231c --#define BUTTRESS_REG_FW_BASE 0x2320 --#define BUTTRESS_REG_FW_BASE_LIMIT 0x2324 --#define BUTTRESS_REG_FW_SCRATCH_BASE 0x2328 --#define BUTTRESS_REG_FW_SCRATCH_LIMIT 0x232c --#define BUTTRESS_REG_CSE_ACTION 0x2330 -- --/* configured by SW */ --#define BUTTRESS_REG_FW_RESET_CTL 0x2334 --#define BUTTRESS_REG_FW_SOURCE_SIZE 0x2338 --#define BUTTRESS_REG_FW_SOURCE_BASE 0x233c -- --#define BUTTRESS_REG_IPU_SEC_CP_LSB 0x2400 --#define BUTTRESS_REG_IPU_SEC_CP_MSB 0x2404 --#define BUTTRESS_REG_IPU_SEC_WAC_LSB 0x2408 --#define BUTTRESS_REG_IPU_SEC_WAC_MSB 0x240c --#define BUTTRESS_REG_IPU_SEC_RAC_LSB 0x2410 --#define BUTTRESS_REG_IPU_SEC_RAC_MSB 0x2414 --#define BUTTRESS_REG_IPU_DRV_CP_LSB 0x2418 --#define BUTTRESS_REG_IPU_DRV_CP_MSB 0x241c --#define BUTTRESS_REG_IPU_DRV_WAC_LSB 0x2420 --#define BUTTRESS_REG_IPU_DRV_WAC_MSB 0x2424 --#define BUTTRESS_REG_IPU_DRV_RAC_LSB 0x2428 --#define BUTTRESS_REG_IPU_DRV_RAC_MSB 0x242c --#define BUTTRESS_REG_IPU_FW_CP_LSB 0x2430 --#define BUTTRESS_REG_IPU_FW_CP_MSB 0x2434 --#define BUTTRESS_REG_IPU_FW_WAC_LSB 0x2438 --#define BUTTRESS_REG_IPU_FW_WAC_MSB 0x243c --#define BUTTRESS_REG_IPU_FW_RAC_LSB 0x2440 --#define BUTTRESS_REG_IPU_FW_RAC_MSB 0x2444 --#define BUTTRESS_REG_IPU_BIOS_SEC_CP_LSB 0x2448 --#define BUTTRESS_REG_IPU_BIOS_SEC_CP_MSB 0x244c --#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_LSB 0x2450 --#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_MSB 0x2454 --#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_LSB 0x2458 --#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_MSB 0x245c --#define BUTTRESS_REG_IPU_DFD_CP_LSB 0x2460 --#define BUTTRESS_REG_IPU_DFD_CP_MSB 0x2464 --#define BUTTRESS_REG_IPU_DFD_WAC_LSB 0x2468 --#define BUTTRESS_REG_IPU_DFD_WAC_MSB 0x246c --#define BUTTRESS_REG_IPU_DFD_RAC_LSB 0x2470 --#define BUTTRESS_REG_IPU_DFD_RAC_MSB 0x2474 --#define BUTTRESS_REG_CSE2IUDB0 0x2500 --#define BUTTRESS_REG_CSE2IUDATA0 0x2504 --#define BUTTRESS_REG_CSE2IUCSR 0x2508 --#define BUTTRESS_REG_IU2CSEDB0 0x250c --#define BUTTRESS_REG_IU2CSEDATA0 0x2510 --#define BUTTRESS_REG_IU2CSECSR 0x2514 --#define BUTTRESS_REG_CSE2IUDB0_CR_SHADOW 0x2520 --#define BUTTRESS_REG_CSE2IUDATA0_CR_SHADOW 0x2524 --#define BUTTRESS_REG_CSE2IUCSR_CR_SHADOW 0x2528 --#define BUTTRESS_REG_IU2CSEDB0_CR_SHADOW 0x252c --#define BUTTRESS_REG_DVFS_FSM_SURVIVABILITY 0x2900 --#define BUTTRESS_REG_FLOWS_FSM_SURVIVABILITY 0x2904 --#define BUTTRESS_REG_FABRICS_FSM_SURVIVABILITY 0x2908 --#define BUTTRESS_REG_PS_SUB1_PM_FSM_SURVIVABILITY 0x290c --#define BUTTRESS_REG_PS_SUB0_PM_FSM_SURVIVABILITY 0x2910 --#define BUTTRESS_REG_PS_PM_FSM_SURVIVABILITY 0x2914 --#define BUTTRESS_REG_IS_PM_FSM_SURVIVABILITY 0x2918 --#define BUTTRESS_REG_FLR_RST_FSM_SURVIVABILITY 0x291c --#define BUTTRESS_REG_FW_RST_FSM_SURVIVABILITY 0x2920 --#define BUTTRESS_REG_RESETPREP_FSM_SURVIVABILITY 0x2924 --#define BUTTRESS_REG_POWER_FSM_DOMAIN_STATUS 0x3000 --#define BUTTRESS_REG_IDLEREQ_STATUS1 0x3004 --#define BUTTRESS_REG_POWER_FSM_STATUS_IS_PS 0x3008 --#define BUTTRESS_REG_POWER_ACK_B_STATUS 0x300c --#define BUTTRESS_REG_DOMAIN_RETENTION_CTL 0x3010 --#define BUTTRESS_REG_CG_CTRL_BITS 0x3014 --#define BUTTRESS_REG_IS_IFC_STATUS0 0x3018 --#define BUTTRESS_REG_IS_IFC_STATUS1 0x301c --#define BUTTRESS_REG_PS_IFC_STATUS0 0x3020 --#define BUTTRESS_REG_PS_IFC_STATUS1 0x3024 --#define BUTTRESS_REG_BTRS_IFC_STATUS0 0x3028 --#define BUTTRESS_REG_BTRS_IFC_STATUS1 0x302c --#define BUTTRESS_REG_IPU_SKU 0x3030 --#define BUTTRESS_REG_PS_IDLEACK 0x3034 --#define BUTTRESS_REG_IS_IDLEACK 0x3038 --#define BUTTRESS_REG_SPARE_REGS_0 0x303c --#define BUTTRESS_REG_SPARE_REGS_1 0x3040 --#define BUTTRESS_REG_SPARE_REGS_2 0x3044 --#define BUTTRESS_REG_SPARE_REGS_3 0x3048 --#define BUTTRESS_REG_IUNIT_ACV 0x304c --#define BUTTRESS_REG_CHICKEN_BITS 0x3050 --#define BUTTRESS_REG_SBENDPOINT_CFG 0x3054 --#define BUTTRESS_REG_ECC_ERR_LOG 0x3058 --#define BUTTRESS_REG_POWER_FSM_STATUS 0x3070 --#define BUTTRESS_REG_RESET_FSM_STATUS 0x3074 --#define BUTTRESS_REG_IDLE_STATUS 0x3078 --#define BUTTRESS_REG_IDLEACK_STATUS 0x307c --#define BUTTRESS_REG_IPU_DEBUG 0x3080 -- --#define BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 --#define BUTTRESS_REG_FW_BOOT_PARAMS1 0x4004 --#define BUTTRESS_REG_FW_BOOT_PARAMS2 0x4008 --#define BUTTRESS_REG_FW_BOOT_PARAMS3 0x400c --#define BUTTRESS_REG_FW_BOOT_PARAMS4 0x4010 --#define BUTTRESS_REG_FW_BOOT_PARAMS5 0x4014 --#define BUTTRESS_REG_FW_BOOT_PARAMS6 0x4018 --#define BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c --#define BUTTRESS_REG_FW_BOOT_PARAMS8 0x4020 --#define BUTTRESS_REG_FW_BOOT_PARAMS9 0x4024 --#define BUTTRESS_REG_FW_BOOT_PARAMS10 0x4028 --#define BUTTRESS_REG_FW_BOOT_PARAMS11 0x402c --#define BUTTRESS_REG_FW_BOOT_PARAMS12 0x4030 --#define BUTTRESS_REG_FW_BOOT_PARAMS13 0x4034 --#define BUTTRESS_REG_FW_BOOT_PARAMS14 0x4038 --#define BUTTRESS_REG_FW_BOOT_PARAMS15 0x403c -- --#define BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \ -- (BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U)) --#define BUTTRESS_REG_FW_GP(i) (0x4040 + 0x4 * (i)) --#define BUTTRESS_REG_FPGA_SUPPORT(i) (0x40c0 + 0x4 * (i)) -- --#define BUTTRESS_REG_FW_GP8 0x4060 --#define BUTTRESS_REG_FW_GP24 0x40a0 -- --#define BUTTRESS_REG_GPIO_0_PADCFG_ADDR_CR 0x4100 --#define BUTTRESS_REG_GPIO_1_PADCFG_ADDR_CR 0x4104 --#define BUTTRESS_REG_GPIO_2_PADCFG_ADDR_CR 0x4108 --#define BUTTRESS_REG_GPIO_3_PADCFG_ADDR_CR 0x410c --#define BUTTRESS_REG_GPIO_4_PADCFG_ADDR_CR 0x4110 --#define BUTTRESS_REG_GPIO_5_PADCFG_ADDR_CR 0x4114 --#define BUTTRESS_REG_GPIO_6_PADCFG_ADDR_CR 0x4118 --#define BUTTRESS_REG_GPIO_7_PADCFG_ADDR_CR 0x411c --#define BUTTRESS_REG_GPIO_ENABLE 0x4140 --#define BUTTRESS_REG_GPIO_VALUE_CR 0x4144 -- --#define BUTTRESS_REG_IS_MEM_CORRECTABLE_ERROR_STATUS 0x5000 --#define BUTTRESS_REG_IS_MEM_FATAL_ERROR_STATUS 0x5004 --#define BUTTRESS_REG_IS_MEM_NON_FATAL_ERROR_STATUS 0x5008 --#define BUTTRESS_REG_IS_MEM_CHECK_PASSED 0x500c --#define BUTTRESS_REG_IS_MEM_ERROR_INJECT 0x5010 --#define BUTTRESS_REG_IS_MEM_ERROR_CLEAR 0x5014 --#define BUTTRESS_REG_PS_MEM_CORRECTABLE_ERROR_STATUS 0x5040 --#define BUTTRESS_REG_PS_MEM_FATAL_ERROR_STATUS 0x5044 --#define BUTTRESS_REG_PS_MEM_NON_FATAL_ERROR_STATUS 0x5048 --#define BUTTRESS_REG_PS_MEM_CHECK_PASSED 0x504c --#define BUTTRESS_REG_PS_MEM_ERROR_INJECT 0x5050 --#define BUTTRESS_REG_PS_MEM_ERROR_CLEAR 0x5054 -- --#define BUTTRESS_REG_IS_AB_REGION_MIN_ADDRESS(i) (0x6000 + 0x8 * (i)) --#define BUTTRESS_REG_IS_AB_REGION_MAX_ADDRESS(i) (0x6004 + 0x8 * (i)) --#define BUTTRESS_REG_IS_AB_VIOLATION_LOG0 0x6080 --#define BUTTRESS_REG_IS_AB_VIOLATION_LOG1 0x6084 --#define BUTTRESS_REG_PS_AB_REGION_MIN_ADDRESS(i) (0x6100 + 0x8 * (i)) --#define BUTTRESS_REG_PS_AB_REGION_MAX_ADDRESS0 (0x6104 + 0x8 * (i)) --#define BUTTRESS_REG_PS_AB_VIOLATION_LOG0 0x6180 --#define BUTTRESS_REG_PS_AB_VIOLATION_LOG1 0x6184 --#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG0 0x6200 --#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG1 0x6204 --#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG0 0x6208 --#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG1 0x620c --#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG0 0x6210 --#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG1 0x6214 --#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG0 0x6218 --#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG1 0x621c --#define BUTTRESS_REG_AB_ENABLE 0x6220 --#define BUTTRESS_REG_AB_DEFAULT_ACCESS 0x6230 -- --/* Indicates CSE has received an IPU driver IPC transaction */ --#define BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0) --/* Indicates an IPC transaction from CSE has arrived */ --#define BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1) --/* Indicates a CSR update from CSE has arrived */ --#define BUTTRESS_IRQ_CSE_CSR_SET BIT(2) --/* Indicates an interrupt set by Punit (not in use at this time) */ --#define BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ BIT(3) --/* Indicates an SAI violation was detected on access to IB registers */ --#define BUTTRESS_IRQ_SAI_VIOLATION BIT(4) --/* Indicates a transaction to IS was not able to pass the access blocker */ --#define BUTTRESS_IRQ_IS_AB_VIOLATION BIT(5) --/* Indicates a transaction to PS was not able to pass the access blocker */ --#define BUTTRESS_IRQ_PS_AB_VIOLATION BIT(6) --/* Indicates an error response was detected by the IB config NoC */ --#define BUTTRESS_IRQ_IB_CFG_NOC_ERR_IRQ BIT(7) --/* Indicates an error response was detected by the IB data NoC */ --#define BUTTRESS_IRQ_IB_DATA_NOC_ERR_IRQ BIT(8) --/* Transaction to DVP regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_IB_DVP_AB_VIOLATION BIT(9) --/* Transaction to ATB2DTF regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_ATB2DTF_AB_VIOLATION BIT(10) --/* Transaction to IS debug regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_IS_DEBUG_AB_VIOLATION BIT(11) --/* Transaction to PS debug regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_PS_DEBUG_AB_VIOLATION BIT(12) --/* Indicates timeout occurred waiting for a response from a target */ --#define BUTTRESS_IRQ_IB_CFG_NOC_TIMEOUT_IRQ BIT(13) --/* Set when any correctable ECC error input wire to buttress is set */ --#define BUTTRESS_IRQ_ECC_CORRECTABLE BIT(14) --/* Any noncorrectable-nonfatal ECC error input wire to buttress is set */ --#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_NONFATAL BIT(15) --/* Set when any noncorrectable-fatal ECC error input wire to buttress is set */ --#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_FATAL BIT(16) --/* Set when timeout occurred waiting for a response from a target */ --#define BUTTRESS_IRQ_IS_CFG_NOC_TIMEOUT_IRQ BIT(17) --#define BUTTRESS_IRQ_PS_CFG_NOC_TIMEOUT_IRQ BIT(18) --#define BUTTRESS_IRQ_LB_CFG_NOC_TIMEOUT_IRQ BIT(19) --/* IS FW double exception event */ --#define BUTTRESS_IRQ_IS_UC_PFATAL_ERROR BIT(26) --/* PS FW double exception event */ --#define BUTTRESS_IRQ_PS_UC_PFATAL_ERROR BIT(27) --/* IS FW watchdog event */ --#define BUTTRESS_IRQ_IS_WATCHDOG BIT(28) --/* PS FW watchdog event */ --#define BUTTRESS_IRQ_PS_WATCHDOG BIT(29) --/* IS IRC irq out */ --#define BUTTRESS_IRQ_IS_IRQ BIT(30) --/* PS IRC irq out */ --#define BUTTRESS_IRQ_PS_IRQ BIT(31) -- --/* buttress irq */ --#define BUTTRESS_PWR_STATUS_HH_STATE_IDLE 0U --#define BUTTRESS_PWR_STATUS_HH_STATE_IN_PRGS 1U --#define BUTTRESS_PWR_STATUS_HH_STATE_DONE 2U --#define BUTTRESS_PWR_STATUS_HH_STATE_ERR 3U -- --#define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) --#define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 --#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) --#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) --/* new for PTL */ --#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) --#define BUTTRESS_IRQS (BUTTRESS_IRQ_IS_IRQ | \ -- BUTTRESS_IRQ_PS_IRQ | \ -- BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \ -- BUTTRESS_IRQ_CSE_CSR_SET | \ -- BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE | \ -- BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -- --/* Iunit to CSE regs */ --#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) --#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27 --#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10 --#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 -- --#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 --#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 --#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 --#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 -- --#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) --#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) --#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) --#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) -- --#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) --#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) --#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) --#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) --#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) --#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) -- --/* 0x20 == NACK, 0xf == unknown command */ --#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 --#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff -- --/* IS/PS freq control */ --#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU --#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU -- --#define IPU7_IS_FREQ_MAX 450 --#define IPU7_IS_FREQ_MIN 50 --#define IPU7_PS_FREQ_MAX 750 --#define BUTTRESS_PS_FREQ_RATIO_STEP 25U --/* valid for IPU8 */ --#define BUTTRESS_IS_FREQ_RATIO_STEP 25U -- --/* IS: 400mhz, PS: 500mhz */ --#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b --#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14 --/* IS: 400mhz, PS: 400mhz */ --#define IPU8_IS_FREQ_CTL_DEFAULT_RATIO 0x10 --#define IPU8_PS_FREQ_CTL_DEFAULT_RATIO 0x10 -- --#define IPU_FREQ_CTL_CDYN 0x80 --#define IPU_FREQ_CTL_RATIO_SHIFT 0x0 --#define IPU_FREQ_CTL_CDYN_SHIFT 0x8 -- --/* buttree power status */ --#define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 --#define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ -- (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) -- --#define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 --#define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ -- (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) -- --#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 --#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 --#define IPU_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 --#define IPU_BUTTRESS_PWR_STATE_UP_DONE 0x3 -- --#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 --#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3 << 3) -- --#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 --#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3 << 6) -- --#define PS_FSM_CG BIT(3) -- --#define BUTTRESS_OVERRIDE_IS_CLK BIT(1) --#define BUTTRESS_OVERRIDE_PS_CLK BIT(2) --/* ps_pll only valid for ipu8 */ --#define BUTTRESS_OWN_ACK_PS_PLL BIT(8) --#define BUTTRESS_OWN_ACK_IS_CLK BIT(9) --#define BUTTRESS_OWN_ACK_PS_CLK BIT(10) -- --/* FW reset ctrl */ --#define BUTTRESS_FW_RESET_CTL_START BIT(0) --#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) -- --/* security */ --#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) --#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) -- --#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) --#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) --#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) -- --/* D2D */ --#define BUTTRESS_D2D_PWR_EN BIT(0) --#define BUTTRESS_D2D_PWR_ACK BIT(4) -- --/* NDE */ --#define NDE_VAL_MASK GENMASK(9, 0) --#define NDE_SCALE_MASK GENMASK(12, 10) --#define NDE_VALID_MASK BIT(13) --#define NDE_RESVEC_MASK GENMASK(19, 16) --#define NDE_IN_VBLANK_DIS_MASK BIT(31) -- --#define BUTTRESS_NDE_VAL_ACTIVE 48 --#define BUTTRESS_NDE_SCALE_ACTIVE 2 --#define BUTTRESS_NDE_VALID_ACTIVE 1 -- --#define BUTTRESS_NDE_VAL_DEFAULT 1023 --#define BUTTRESS_NDE_SCALE_DEFAULT 2 --#define BUTTRESS_NDE_VALID_DEFAULT 0 -- --/* IS and PS UCX control */ --#define UCX_CTL_RESET BIT(0) --#define UCX_CTL_RUN BIT(1) --#define UCX_CTL_WAKEUP BIT(2) --#define UCX_CTL_SPARE GENMASK(7, 3) --#define UCX_STS_PWR GENMASK(17, 16) --#define UCX_STS_SLEEPING BIT(18) -- --/* offset from PHY base */ --#define PHY_CSI_CFG 0xc0 --#define PHY_CSI_RCOMP_CONTROL 0xc8 --#define PHY_CSI_BSCAN_EXCLUDE 0xd8 -- --#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) --#define PHY_DPHY_DLL_OVRD(x) (0x14c + 0x100 * (x)) --#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) --#define PHY_CPHY_RX_CONTROL2(x) (0x114 + 0x100 * (x)) --#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) --#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) -- --/* PB registers */ --#define INTERRUPT_STATUS 0x0 --#define BTRS_LOCAL_INTERRUPT_MASK 0x4 --#define GLOBAL_INTERRUPT_MASK 0x8 --#define HM_ATS 0xc --#define ATS_ERROR_LOG1 0x10 --#define ATS_ERROR_LOG2 0x14 --#define ATS_ERROR_CLEAR 0x18 --#define CFI_0_ERROR_LOG 0x1c --#define CFI_0_ERROR_CLEAR 0x20 --#define HASH_CONFIG 0x2c --#define TLBID_HASH_ENABLE_31_0 0x30 --#define TLBID_HASH_ENABLE_63_32 0x34 --#define TLBID_HASH_ENABLE_95_64 0x38 --#define TLBID_HASH_ENABLE_127_96 0x3c --#define CFI_1_ERROR_LOGGING 0x40 --#define CFI_1_ERROR_CLEAR 0x44 --#define IMR_ERROR_LOGGING_LOW 0x48 --#define IMR_ERROR_LOGGING_HIGH 0x4c --#define IMR_ERROR_CLEAR 0x50 --#define PORT_ARBITRATION_WEIGHTS 0x54 --#define IMR_ERROR_LOGGING_CFI_1_LOW 0x58 --#define IMR_ERROR_LOGGING_CFI_1_HIGH 0x5c --#define IMR_ERROR_CLEAR_CFI_1 0x60 --#define BAR2_MISC_CONFIG 0x64 --#define RSP_ID_CONFIG_AXI2CFI_0 0x68 --#define RSP_ID_CONFIG_AXI2CFI_1 0x6c --#define PB_DRIVER_PCODE_MAILBOX_STATUS 0x70 --#define PB_DRIVER_PCODE_MAILBOX_INTERFACE 0x74 --#define PORT_ARBITRATION_WEIGHTS_ATS 0x78 -- --#endif /* IPU7_BUTTRESS_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.c b/drivers/media/pci/intel/ipu7/ipu7-buttress.c -deleted file mode 100644 -index b350ca5678d0..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-buttress.c -+++ /dev/null -@@ -1,1193 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-buttress-regs.h" -- --#define BOOTLOADER_STATUS_OFFSET BUTTRESS_REG_FW_BOOT_PARAMS7 -- --#define BOOTLOADER_MAGIC_KEY 0xb00710adU -- --#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 --#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 --#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE -- --#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10U -- --#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) -- --#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) --#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) --#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) -- --#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC --#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC --#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) --#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) -- --#define BUTTRESS_IPC_RESET_RETRY 2000U --#define BUTTRESS_CSE_IPC_RESET_RETRY 4U --#define BUTTRESS_IPC_CMD_SEND_RETRY 1U -- --struct ipu7_ipc_buttress_msg { -- u32 cmd; -- u32 expected_resp; -- bool require_resp; -- u8 cmd_size; --}; -- --static const u32 ipu7_adev_irq_mask[2] = { -- BUTTRESS_IRQ_IS_IRQ, -- BUTTRESS_IRQ_PS_IRQ --}; -- --int ipu_buttress_ipc_reset(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc) --{ -- unsigned int retries = BUTTRESS_IPC_RESET_RETRY; -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- u32 val = 0, csr_in_clr; -- -- if (!isp->secure_mode) { -- dev_dbg(dev, "Skip IPC reset for non-secure mode\n"); -- return 0; -- } -- -- mutex_lock(&b->ipc_mutex); -- -- /* Clear-by-1 CSR (all bits), corresponding internal states. */ -- val = readl(isp->base + ipc->csr_in); -- writel(val, isp->base + ipc->csr_in); -- -- /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ -- writel(ENTRY, isp->base + ipc->csr_out); -- /* -- * Clear-by-1 all CSR bits EXCEPT following -- * bits: -- * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -- * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- * C. Possibly custom bits, depending on -- * their role. -- */ -- csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | -- BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | -- BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; -- -- do { -- usleep_range(400, 500); -- val = readl(isp->base + ipc->csr_in); -- switch (val) { -- case ENTRY | EXIT: -- case ENTRY | EXIT | QUERY: -- /* -- * 1) Clear-by-1 CSR bits -- * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -- * IPC_PEER_COMP_ACTIONS_RST_PHASE2). -- * 2) Set peer CSR bit -- * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -- */ -- writel(ENTRY | EXIT, isp->base + ipc->csr_in); -- writel(QUERY, isp->base + ipc->csr_out); -- break; -- case ENTRY: -- case ENTRY | QUERY: -- /* -- * 1) Clear-by-1 CSR bits -- * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -- * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). -- * 2) Set peer CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE1. -- */ -- writel(ENTRY | QUERY, isp->base + ipc->csr_in); -- writel(ENTRY, isp->base + ipc->csr_out); -- break; -- case EXIT: -- case EXIT | QUERY: -- /* -- * Clear-by-1 CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- * 1) Clear incoming doorbell. -- * 2) Clear-by-1 all CSR bits EXCEPT following -- * bits: -- * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -- * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- * C. Possibly custom bits, depending on -- * their role. -- * 3) Set peer CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- */ -- writel(EXIT, isp->base + ipc->csr_in); -- writel(0, isp->base + ipc->db0_in); -- writel(csr_in_clr, isp->base + ipc->csr_in); -- writel(EXIT, isp->base + ipc->csr_out); -- -- /* -- * Read csr_in again to make sure if RST_PHASE2 is done. -- * If csr_in is QUERY, it should be handled again. -- */ -- usleep_range(200, 300); -- val = readl(isp->base + ipc->csr_in); -- if (val & QUERY) { -- dev_dbg(dev, -- "RST_PHASE2 retry csr_in = %x\n", val); -- break; -- } -- mutex_unlock(&b->ipc_mutex); -- return 0; -- case QUERY: -- /* -- * 1) Clear-by-1 CSR bit -- * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -- * 2) Set peer CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE1 -- */ -- writel(QUERY, isp->base + ipc->csr_in); -- writel(ENTRY, isp->base + ipc->csr_out); -- break; -- default: -- dev_dbg_ratelimited(dev, "Unexpected CSR 0x%x\n", val); -- break; -- } -- } while (retries--); -- -- mutex_unlock(&b->ipc_mutex); -- dev_err(dev, "Timed out while waiting for CSE\n"); -- -- return -ETIMEDOUT; --} -- --static void ipu_buttress_ipc_validity_close(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc) --{ -- writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, -- isp->base + ipc->csr_out); --} -- --static int --ipu_buttress_ipc_validity_open(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc) --{ -- unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; -- void __iomem *addr; -- int ret; -- u32 val; -- -- writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, -- isp->base + ipc->csr_out); -- -- addr = isp->base + ipc->csr_in; -- ret = readl_poll_timeout(addr, val, val & mask, 200, -- BUTTRESS_IPC_VALIDITY_TIMEOUT_US); -- if (ret) { -- dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); -- ipu_buttress_ipc_validity_close(isp, ipc); -- } -- -- return ret; --} -- --static void ipu_buttress_ipc_recv(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc, u32 *ipc_msg) --{ -- if (ipc_msg) -- *ipc_msg = readl(isp->base + ipc->data0_in); -- writel(0, isp->base + ipc->db0_in); --} -- --static int ipu_buttress_ipc_send_msg(struct ipu7_device *isp, -- struct ipu7_ipc_buttress_msg *msg) --{ -- unsigned long tx_timeout_jiffies, rx_timeout_jiffies; -- unsigned int retry = BUTTRESS_IPC_CMD_SEND_RETRY; -- struct ipu_buttress *b = &isp->buttress; -- struct ipu_buttress_ipc *ipc = &b->cse; -- struct device *dev = &isp->pdev->dev; -- int tout; -- u32 val; -- int ret; -- -- mutex_lock(&b->ipc_mutex); -- -- ret = ipu_buttress_ipc_validity_open(isp, ipc); -- if (ret) { -- dev_err(dev, "IPC validity open failed\n"); -- goto out; -- } -- -- tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); -- rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); -- --try: -- reinit_completion(&ipc->send_complete); -- if (msg->require_resp) -- reinit_completion(&ipc->recv_complete); -- -- dev_dbg(dev, "IPC command: 0x%x\n", msg->cmd); -- writel(msg->cmd, isp->base + ipc->data0_out); -- val = BUTTRESS_IU2CSEDB0_BUSY | msg->cmd_size; -- writel(val, isp->base + ipc->db0_out); -- -- tout = wait_for_completion_timeout(&ipc->send_complete, -- tx_timeout_jiffies); -- if (!tout) { -- dev_err(dev, "send IPC response timeout\n"); -- if (!retry--) { -- ret = -ETIMEDOUT; -- goto out; -- } -- -- /* Try again if CSE is not responding on first try */ -- writel(0, isp->base + ipc->db0_out); -- goto try; -- } -- -- if (!msg->require_resp) { -- ret = -EIO; -- goto out; -- } -- -- tout = wait_for_completion_timeout(&ipc->recv_complete, -- rx_timeout_jiffies); -- if (!tout) { -- dev_err(dev, "recv IPC response timeout\n"); -- ret = -ETIMEDOUT; -- goto out; -- } -- -- if (ipc->nack_mask && -- (ipc->recv_data & ipc->nack_mask) == ipc->nack) { -- dev_err(dev, "IPC NACK for cmd 0x%x\n", msg->cmd); -- ret = -EIO; -- goto out; -- } -- -- if (ipc->recv_data != msg->expected_resp) { -- dev_err(dev, -- "expected resp: 0x%x, IPC response: 0x%x\n", -- msg->expected_resp, ipc->recv_data); -- ret = -EIO; -- goto out; -- } -- -- dev_dbg(dev, "IPC commands done\n"); -- --out: -- ipu_buttress_ipc_validity_close(isp, ipc); -- mutex_unlock(&b->ipc_mutex); -- -- return ret; --} -- --static int ipu_buttress_ipc_send(struct ipu7_device *isp, -- u32 ipc_msg, u32 size, bool require_resp, -- u32 expected_resp) --{ -- struct ipu7_ipc_buttress_msg msg = { -- .cmd = ipc_msg, -- .cmd_size = size, -- .require_resp = require_resp, -- .expected_resp = expected_resp, -- }; -- -- return ipu_buttress_ipc_send_msg(isp, &msg); --} -- --static irqreturn_t ipu_buttress_call_isr(struct ipu7_bus_device *adev) --{ -- irqreturn_t ret = IRQ_WAKE_THREAD; -- -- if (!adev || !adev->auxdrv || !adev->auxdrv_data) -- return IRQ_NONE; -- -- if (adev->auxdrv_data->isr) -- ret = adev->auxdrv_data->isr(adev); -- -- if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) -- ret = IRQ_NONE; -- -- return ret; --} -- --irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) --{ -- struct ipu7_device *isp = isp_ptr; -- struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- irqreturn_t ret = IRQ_NONE; -- u32 pb_irq, pb_local_irq; -- u32 disable_irqs = 0; -- u32 irq_status; -- unsigned int i; -- -- pm_runtime_get_noresume(dev); -- -- pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); -- writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); -- -- /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ -- pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); -- if (pb_local_irq & ~BIT(0)) { -- dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, -- pb_local_irq); -- dev_warn(dev, "Details: %x %x %x %x %x %x %x %x\n", -- readl(isp->pb_base + ATS_ERROR_LOG1), -- readl(isp->pb_base + ATS_ERROR_LOG2), -- readl(isp->pb_base + CFI_0_ERROR_LOG), -- readl(isp->pb_base + CFI_1_ERROR_LOGGING), -- readl(isp->pb_base + IMR_ERROR_LOGGING_LOW), -- readl(isp->pb_base + IMR_ERROR_LOGGING_HIGH), -- readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_LOW), -- readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_HIGH)); -- } -- -- irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -- if (!irq_status) { -- pm_runtime_put_noidle(dev); -- return IRQ_NONE; -- } -- -- do { -- writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR); -- -- for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask); i++) { -- irqreturn_t r = ipu_buttress_call_isr(adev[i]); -- -- if (!(irq_status & ipu7_adev_irq_mask[i])) -- continue; -- -- if (r == IRQ_WAKE_THREAD) { -- ret = IRQ_WAKE_THREAD; -- disable_irqs |= ipu7_adev_irq_mask[i]; -- } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { -- ret = IRQ_HANDLED; -- } -- } -- -- if (irq_status & (BUTTRESS_IRQS | BUTTRESS_IRQ_SAI_VIOLATION) && -- ret == IRQ_NONE) -- ret = IRQ_HANDLED; -- -- if (irq_status & BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING) { -- dev_dbg(dev, "BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING\n"); -- ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); -- complete(&b->cse.recv_complete); -- } -- -- if (irq_status & BUTTRESS_IRQ_CSE_CSR_SET) -- dev_dbg(dev, "BUTTRESS_IRQ_CSE_CSR_SET\n"); -- -- if (irq_status & BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) { -- dev_dbg(dev, "BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE\n"); -- complete(&b->cse.send_complete); -- } -- -- if (irq_status & BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -- dev_dbg(dev, "BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ\n"); -- -- if (irq_status & BUTTRESS_IRQ_SAI_VIOLATION && -- ipu_buttress_get_secure_mode(isp)) -- dev_err(dev, "BUTTRESS_IRQ_SAI_VIOLATION\n"); -- -- irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -- } while (irq_status); -- -- if (disable_irqs) -- writel(BUTTRESS_IRQS & ~disable_irqs, -- isp->base + BUTTRESS_REG_IRQ_ENABLE); -- -- pm_runtime_put(dev); -- -- return ret; --} -- --irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) --{ -- struct ipu7_device *isp = isp_ptr; -- struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -- const struct ipu7_auxdrv_data *drv_data = NULL; -- irqreturn_t ret = IRQ_NONE; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask) && adev[i]; i++) { -- drv_data = adev[i]->auxdrv_data; -- if (!drv_data) -- continue; -- -- if (drv_data->wake_isr_thread && -- drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) -- ret = IRQ_HANDLED; -- } -- -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -- -- return ret; --} -- --static int isys_d2d_power(struct device *dev, bool on) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- int ret = 0; -- u32 target = on ? BUTTRESS_D2D_PWR_ACK : 0U; -- u32 val; -- -- dev_dbg(dev, "power %s isys d2d.\n", on ? "UP" : "DOWN"); -- val = readl(isp->base + BUTTRESS_REG_D2D_CTL); -- if ((val & BUTTRESS_D2D_PWR_ACK) == target) { -- dev_info(dev, "d2d already in %s state.\n", -- on ? "UP" : "DOWN"); -- return 0; -- } -- -- val = on ? val | BUTTRESS_D2D_PWR_EN : val & (~BUTTRESS_D2D_PWR_EN); -- writel(val, isp->base + BUTTRESS_REG_D2D_CTL); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_D2D_CTL, -- val, (val & BUTTRESS_D2D_PWR_ACK) == target, -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) -- dev_err(dev, "power %s d2d timeout. status: 0x%x\n", -- on ? "UP" : "DOWN", val); -- -- return ret; --} -- --static void isys_nde_control(struct device *dev, bool on) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, value, scale, valid, resvec; -- u32 nde_reg; -- -- if (on) { -- value = BUTTRESS_NDE_VAL_ACTIVE; -- scale = BUTTRESS_NDE_SCALE_ACTIVE; -- valid = BUTTRESS_NDE_VALID_ACTIVE; -- } else { -- value = BUTTRESS_NDE_VAL_DEFAULT; -- scale = BUTTRESS_NDE_SCALE_DEFAULT; -- valid = BUTTRESS_NDE_VALID_DEFAULT; -- } -- -- /* only set the fabrics resource ownership for ipu8 */ -- nde_reg = is_ipu8(isp->hw_ver) ? IPU8_BUTTRESS_REG_NDE_CONTROL : -- IPU7_BUTTRESS_REG_NDE_CONTROL; -- resvec = is_ipu8(isp->hw_ver) ? 0x2 : 0xe; -- val = FIELD_PREP(NDE_VAL_MASK, value) | -- FIELD_PREP(NDE_SCALE_MASK, scale) | -- FIELD_PREP(NDE_VALID_MASK, valid) | -- FIELD_PREP(NDE_RESVEC_MASK, resvec); -- -- writel(val, isp->base + nde_reg); --} -- --static int ipu7_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- -- exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -- if (ctrl->subsys_id == IPU_IS) { -- ret = isys_d2d_power(dev, true); -- if (ret) -- goto out_power; -- isys_nde_control(dev, true); -- } -- -- /* request clock resource ownership */ -- val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- val |= ctrl->ovrd_clk; -- writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS, -- val, (val & ctrl->own_clk_ack), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) -- dev_warn(dev, "request clk ownership timeout. status 0x%x\n", -- val); -- -- val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -- -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power up timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- -- /* release clock resource ownership */ -- val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- val &= ~ctrl->ovrd_clk; -- writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- --out_power: -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --static int ipu7_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- -- exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -- val = 0x8U << ctrl->ratio_shift; -- -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power down timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); --out_power: -- if (ctrl->subsys_id == IPU_IS && !ret) { -- isys_d2d_power(dev, false); -- isys_nde_control(dev, false); -- } -- -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --static int ipu8_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 sleep_level_reg = BUTTRESS_REG_SLEEP_LEVEL_STS; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -- if (ctrl->subsys_id == IPU_IS) { -- ret = isys_d2d_power(dev, true); -- if (ret) -- goto out_power; -- isys_nde_control(dev, true); -- } -- -- /* request ps_pll when psys freq > 400Mhz */ -- if (ctrl->subsys_id == IPU_PS && ctrl->ratio > 0x10) { -- writel(1, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -- ret = readl_poll_timeout(isp->base + sleep_level_reg, -- val, (val & ctrl->own_clk_ack), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) -- dev_warn(dev, "ps_pll req ack timeout. status 0x%x\n", -- val); -- } -- -- val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power up timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); --out_power: -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --static int ipu8_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -- -- if (ctrl->subsys_id == IPU_PS) -- val = 0x10U << ctrl->ratio_shift; -- else -- val = 0x8U << ctrl->ratio_shift; -- -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power down timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); --out_power: -- if (ctrl->subsys_id == IPU_IS && !ret) { -- isys_d2d_power(dev, false); -- isys_nde_control(dev, false); -- } -- -- if (ctrl->subsys_id == IPU_PS) { -- val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS); -- if (val & ctrl->own_clk_ack) -- writel(0, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -- } -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --int ipu_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- -- if (is_ipu8(isp->hw_ver)) -- return ipu8_buttress_powerup(dev, ctrl); -- -- return ipu7_buttress_powerup(dev, ctrl); --} -- --int ipu_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- -- if (is_ipu8(isp->hw_ver)) -- return ipu8_buttress_powerdown(dev, ctrl); -- -- return ipu7_buttress_powerdown(dev, ctrl); --} -- --bool ipu_buttress_get_secure_mode(struct ipu7_device *isp) --{ -- u32 val; -- -- val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -- -- return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; --} -- --bool ipu_buttress_auth_done(struct ipu7_device *isp) --{ -- u32 val; -- -- if (!isp->secure_mode) -- return true; -- -- val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -- val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); -- -- return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_auth_done, "INTEL_IPU7"); -- --int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq) --{ -- u32 reg_val; -- int ret; -- -- ret = pm_runtime_get_sync(&isp->isys->auxdev.dev); -- if (ret < 0) { -- pm_runtime_put(&isp->isys->auxdev.dev); -- dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -- return ret; -- } -- -- reg_val = readl(isp->base + BUTTRESS_REG_IS_WORKPOINT_REQ); -- -- pm_runtime_put(&isp->isys->auxdev.dev); -- -- if (is_ipu8(isp->hw_ver)) -- *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 25; -- else -- *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 50 / 3; -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_isys_freq, "INTEL_IPU7"); -- --int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq) --{ -- u32 reg_val; -- int ret; -- -- ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -- if (ret < 0) { -- pm_runtime_put(&isp->psys->auxdev.dev); -- dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -- return ret; -- } -- -- reg_val = readl(isp->base + BUTTRESS_REG_PS_WORKPOINT_REQ); -- -- pm_runtime_put(&isp->psys->auxdev.dev); -- -- reg_val &= BUTTRESS_PS_FREQ_CTL_RATIO_MASK; -- *freq = BUTTRESS_PS_FREQ_RATIO_STEP * reg_val; -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_psys_freq, "INTEL_IPU7"); -- --int ipu_buttress_reset_authentication(struct ipu7_device *isp) --{ -- struct device *dev = &isp->pdev->dev; -- int ret; -- u32 val; -- -- if (!isp->secure_mode) { -- dev_dbg(dev, "Skip auth for non-secure mode\n"); -- return 0; -- } -- -- writel(BUTTRESS_FW_RESET_CTL_START, isp->base + -- BUTTRESS_REG_FW_RESET_CTL); -- -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, -- val & BUTTRESS_FW_RESET_CTL_DONE, 500, -- BUTTRESS_CSE_FWRESET_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "Time out while resetting authentication state\n"); -- return ret; -- } -- -- dev_dbg(dev, "FW reset for authentication done\n"); -- writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); -- /* leave some time for HW restore */ -- usleep_range(800, 1000); -- -- return 0; --} -- --int ipu_buttress_authenticate(struct ipu7_device *isp) --{ -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- u32 data, mask, done, fail; -- int ret; -- -- if (!isp->secure_mode) { -- dev_dbg(dev, "Skip auth for non-secure mode\n"); -- return 0; -- } -- -- mutex_lock(&b->auth_mutex); -- -- if (ipu_buttress_auth_done(isp)) { -- ret = 0; -- goto out_unlock; -- } -- -- /* -- * BUTTRESS_REG_FW_SOURCE_BASE needs to be set with FW CPD -- * package address for secure mode. -- */ -- -- writel(isp->cpd_fw->size, isp->base + BUTTRESS_REG_FW_SOURCE_SIZE); -- writel(sg_dma_address(isp->psys->fw_sgt.sgl), -- isp->base + BUTTRESS_REG_FW_SOURCE_BASE); -- -- /* -- * Write boot_load into IU2CSEDATA0 -- * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -- * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -- */ -- dev_info(dev, "Sending BOOT_LOAD to CSE\n"); -- ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, -- 1, true, -- BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); -- if (ret) { -- dev_err(dev, "CSE boot_load failed\n"); -- goto out_unlock; -- } -- -- mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; -- done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; -- fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -- ((data & mask) == done || -- (data & mask) == fail), 500, -- BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "CSE boot_load timeout\n"); -- goto out_unlock; -- } -- -- if ((data & mask) == fail) { -- dev_err(dev, "CSE auth failed\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- ret = readl_poll_timeout(isp->base + BOOTLOADER_STATUS_OFFSET, -- data, data == BOOTLOADER_MAGIC_KEY, 500, -- BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "Unexpected magic number 0x%x\n", data); -- goto out_unlock; -- } -- -- /* -- * Write authenticate_run into IU2CSEDATA0 -- * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -- * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -- */ -- dev_info(dev, "Sending AUTHENTICATE_RUN to CSE\n"); -- ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, -- 1, true, -- BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); -- if (ret) { -- dev_err(dev, "CSE authenticate_run failed\n"); -- goto out_unlock; -- } -- -- done = BUTTRESS_SECURITY_CTL_AUTH_DONE; -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -- ((data & mask) == done || -- (data & mask) == fail), 500, -- BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "CSE authenticate timeout\n"); -- goto out_unlock; -- } -- -- if ((data & mask) == fail) { -- dev_err(dev, "CSE boot_load failed\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- dev_info(dev, "CSE authenticate_run done\n"); -- --out_unlock: -- mutex_unlock(&b->auth_mutex); -- -- return ret; --} -- --static int ipu_buttress_send_tsc_request(struct ipu7_device *isp) --{ -- u32 val, mask, done; -- int ret; -- -- mask = BUTTRESS_PWR_STATUS_HH_STATUS_MASK; -- -- writel(BUTTRESS_TSC_CMD_START_TSC_SYNC, -- isp->base + BUTTRESS_REG_TSC_CMD); -- -- val = readl(isp->base + BUTTRESS_REG_PWR_STATUS); -- val = FIELD_GET(mask, val); -- if (val == BUTTRESS_PWR_STATUS_HH_STATE_ERR) { -- dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); -- return -EINVAL; -- } -- -- done = BUTTRESS_PWR_STATUS_HH_STATE_DONE; -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, val, -- FIELD_GET(mask, val) == done, 500, -- BUTTRESS_TSC_SYNC_TIMEOUT_US); -- if (ret) -- dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); -- -- return ret; --} -- --int ipu_buttress_start_tsc_sync(struct ipu7_device *isp) --{ -- void __iomem *base = isp->base; -- unsigned int i; -- u32 val; -- -- if (is_ipu8(isp->hw_ver)) { -- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -- val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -- if (val == 1) -- return 0; -- usleep_range(40, 50); -- } -- -- dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -- return -ETIMEDOUT; -- } -- -- if (is_ipu7p5(isp->hw_ver)) { -- val = readl(base + BUTTRESS_REG_TSC_CTL); -- val |= BUTTRESS_SEL_PB_TIMESTAMP; -- writel(val, base + BUTTRESS_REG_TSC_CTL); -- -- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -- val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -- if (val == 1) -- return 0; -- usleep_range(40, 50); -- } -- -- dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -- -- return -ETIMEDOUT; -- } -- -- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -- int ret; -- -- ret = ipu_buttress_send_tsc_request(isp); -- if (ret != -ETIMEDOUT) -- return ret; -- -- val = readl(base + BUTTRESS_REG_TSC_CTL); -- val = val | BUTTRESS_TSW_WA_SOFT_RESET; -- writel(val, base + BUTTRESS_REG_TSC_CTL); -- val = val & (~BUTTRESS_TSW_WA_SOFT_RESET); -- writel(val, base + BUTTRESS_REG_TSC_CTL); -- } -- -- dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); -- -- return -ETIMEDOUT; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_start_tsc_sync, "INTEL_IPU7"); -- --void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val) --{ -- unsigned long flags; -- u32 tsc_hi, tsc_lo; -- -- local_irq_save(flags); -- if (is_ipu7(isp->hw_ver)) { -- tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); -- tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI); -- } else { -- tsc_lo = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_LO); -- tsc_hi = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_HI); -- } -- *val = (u64)tsc_hi << 32 | tsc_lo; -- local_irq_restore(flags); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_read, "INTEL_IPU7"); -- --u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp) --{ -- u64 ns = ticks * 10000; -- -- /* -- * converting TSC tick count to ns is calculated by: -- * Example (TSC clock frequency is 19.2MHz): -- * ns = ticks * 1000 000 000 / 19.2Mhz -- * = ticks * 1000 000 000 / 19200000Hz -- * = ticks * 10000 / 192 ns -- */ -- return div_u64(ns, isp->buttress.ref_clk); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_ticks_to_ns, "INTEL_IPU7"); -- --/* trigger uc control to wakeup fw */ --void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp) --{ -- u32 val; -- -- val = readl(isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); -- val |= UCX_CTL_WAKEUP; -- writel(val, isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_is_uc, "INTEL_IPU7"); -- --void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp) --{ -- u32 val; -- -- val = readl(isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); -- val |= UCX_CTL_WAKEUP; -- writel(val, isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_ps_uc, "INTEL_IPU7"); -- --static const struct x86_cpu_id ipu_misc_cfg_exclusion[] = { -- X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0), -- {}, --}; -- --static void ipu_buttress_setup(struct ipu7_device *isp) --{ -- struct device *dev = &isp->pdev->dev; -- u32 val; -- -- /* program PB BAR */ --#define WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19) -- writel(0, isp->pb_base + GLOBAL_INTERRUPT_MASK); -- val = readl(isp->pb_base + BAR2_MISC_CONFIG); -- if (is_ipu7(isp->hw_ver) || x86_match_cpu(ipu_misc_cfg_exclusion)) -- val |= 0x100U; -- else -- val |= FIELD_PREP(WRXREQOP_OVRD_VAL_MASK, 0xf) | -- BIT(18) | 0x100U; -- -- writel(val, isp->pb_base + BAR2_MISC_CONFIG); -- val = readl(isp->pb_base + BAR2_MISC_CONFIG); -- -- if (is_ipu8(isp->hw_ver)) { -- writel(BIT(13), isp->pb_base + TLBID_HASH_ENABLE_63_32); -- writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -- dev_dbg(dev, "IPU8 TLBID_HASH %x %x\n", -- readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -- readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -- } else if (is_ipu7p5(isp->hw_ver)) { -- writel(BIT(14), isp->pb_base + TLBID_HASH_ENABLE_63_32); -- writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -- dev_dbg(dev, "IPU7P5 TLBID_HASH %x %x\n", -- readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -- readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -- } else { -- writel(BIT(22), isp->pb_base + TLBID_HASH_ENABLE_63_32); -- writel(BIT(1), isp->pb_base + TLBID_HASH_ENABLE_127_96); -- dev_dbg(dev, "TLBID_HASH %x %x\n", -- readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -- readl(isp->pb_base + TLBID_HASH_ENABLE_127_96)); -- } -- -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_CLEAR); -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_MASK); -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -- /* LNL SW workaround for PS PD hang when PS sub-domain during PD */ -- writel(PS_FSM_CG, isp->base + BUTTRESS_REG_CG_CTRL_BITS); --} -- --void ipu_buttress_restore(struct ipu7_device *isp) --{ -- struct ipu_buttress *b = &isp->buttress; -- -- ipu_buttress_setup(isp); -- -- writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_IDLE_WDT); --} -- --int ipu_buttress_init(struct ipu7_device *isp) --{ -- int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- u32 val; -- -- mutex_init(&b->power_mutex); -- mutex_init(&b->auth_mutex); -- mutex_init(&b->cons_mutex); -- mutex_init(&b->ipc_mutex); -- init_completion(&b->cse.send_complete); -- init_completion(&b->cse.recv_complete); -- -- b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; -- b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; -- b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; -- b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; -- b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; -- b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; -- b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; -- b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; -- -- isp->secure_mode = ipu_buttress_get_secure_mode(isp); -- val = readl(isp->base + BUTTRESS_REG_IPU_SKU); -- dev_info(dev, "IPU%u SKU %u in %s mode mask 0x%x\n", val & 0xfU, -- (val >> 4) & 0x7U, isp->secure_mode ? "secure" : "non-secure", -- readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); -- b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_IDLE_WDT); -- b->ref_clk = 384; -- -- ipu_buttress_setup(isp); -- -- /* Retry couple of times in case of CSE initialization is delayed */ -- do { -- ret = ipu_buttress_ipc_reset(isp, &b->cse); -- if (ret) { -- dev_warn(dev, "IPC reset protocol failed, retrying\n"); -- } else { -- dev_dbg(dev, "IPC reset done\n"); -- return 0; -- } -- } while (ipc_reset_retry--); -- -- dev_err(dev, "IPC reset protocol failed\n"); -- -- mutex_destroy(&b->power_mutex); -- mutex_destroy(&b->auth_mutex); -- mutex_destroy(&b->cons_mutex); -- mutex_destroy(&b->ipc_mutex); -- -- return ret; --} -- --void ipu_buttress_exit(struct ipu7_device *isp) --{ -- struct ipu_buttress *b = &isp->buttress; -- -- writel(0, isp->base + BUTTRESS_REG_IRQ_ENABLE); -- mutex_destroy(&b->power_mutex); -- mutex_destroy(&b->auth_mutex); -- mutex_destroy(&b->cons_mutex); -- mutex_destroy(&b->ipc_mutex); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.h b/drivers/media/pci/intel/ipu7/ipu7-buttress.h -deleted file mode 100644 -index 8da7dd612575..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-buttress.h -+++ /dev/null -@@ -1,77 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BUTTRESS_H --#define IPU7_BUTTRESS_H -- --#include --#include --#include --#include -- --struct device; --struct ipu7_device; -- --struct ipu_buttress_ctrl { -- u32 subsys_id; -- u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; -- u32 ratio; -- u32 ratio_shift; -- u32 cdyn; -- u32 cdyn_shift; -- u32 ovrd_clk; -- u32 own_clk_ack; --}; -- --struct ipu_buttress_ipc { -- struct completion send_complete; -- struct completion recv_complete; -- u32 nack; -- u32 nack_mask; -- u32 recv_data; -- u32 csr_out; -- u32 csr_in; -- u32 db0_in; -- u32 db0_out; -- u32 data0_out; -- u32 data0_in; --}; -- --struct ipu_buttress { -- struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; -- struct ipu_buttress_ipc cse; -- u32 psys_min_freq; -- u32 wdt_cached_value; -- u8 psys_force_ratio; -- bool force_suspend; -- u32 ref_clk; --}; -- --int ipu_buttress_ipc_reset(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc); --int ipu_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl); --int ipu_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl); --bool ipu_buttress_get_secure_mode(struct ipu7_device *isp); --int ipu_buttress_authenticate(struct ipu7_device *isp); --int ipu_buttress_reset_authentication(struct ipu7_device *isp); --bool ipu_buttress_auth_done(struct ipu7_device *isp); --int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq); --int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq); --int ipu_buttress_start_tsc_sync(struct ipu7_device *isp); --void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val); --u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp); -- --irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr); --irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr); --int ipu_buttress_init(struct ipu7_device *isp); --void ipu_buttress_exit(struct ipu7_device *isp); --void ipu_buttress_csi_port_config(struct ipu7_device *isp, -- u32 legacy, u32 combo); --void ipu_buttress_restore(struct ipu7_device *isp); --void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp); --void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp); --#endif /* IPU7_BUTTRESS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.c b/drivers/media/pci/intel/ipu7/ipu7-cpd.c -deleted file mode 100644 -index 25dd71e1809e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-cpd.c -+++ /dev/null -@@ -1,277 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2015 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-cpd.h" -- --/* $CPD */ --#define CPD_HDR_MARK 0x44504324 -- --/* Maximum size is 4K DWORDs or 16KB */ --#define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) -- --#define CPD_MANIFEST_IDX 0 --#define CPD_BINARY_START_IDX 1U --#define CPD_METADATA_START_IDX 2U --#define CPD_BINARY_NUM 2U /* ISYS + PSYS */ --/* -- * Entries include: -- * 1 manifest entry. -- * 1 metadata entry for each sub system(ISYS and PSYS). -- * 1 binary entry for each sub system(ISYS and PSYS). -- */ --#define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U) -- --#define CPD_METADATA_ATTR 0xa --#define CPD_METADATA_IPL 0x1c --#define ONLINE_METADATA_SIZE 128U --#define ONLINE_METADATA_LINES 6U -- --struct ipu7_cpd_hdr { -- u32 hdr_mark; -- u32 ent_cnt; -- u8 hdr_ver; -- u8 ent_ver; -- u8 hdr_len; -- u8 rsvd; -- u8 partition_name[4]; -- u32 crc32; --} __packed; -- --struct ipu7_cpd_ent { -- u8 name[12]; -- u32 offset; -- u32 len; -- u8 rsvd[4]; --} __packed; -- --struct ipu7_cpd_metadata_hdr { -- u32 type; -- u32 len; --} __packed; -- --struct ipu7_cpd_metadata_attr { -- struct ipu7_cpd_metadata_hdr hdr; -- u8 compression_type; -- u8 encryption_type; -- u8 rsvd[2]; -- u32 uncompressed_size; -- u32 compressed_size; -- u32 module_id; -- u8 hash[48]; --} __packed; -- --struct ipu7_cpd_metadata_ipl { -- struct ipu7_cpd_metadata_hdr hdr; -- u32 param[4]; -- u8 rsvd[8]; --} __packed; -- --struct ipu7_cpd_metadata { -- struct ipu7_cpd_metadata_attr attr; -- struct ipu7_cpd_metadata_ipl ipl; --} __packed; -- --static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx) --{ -- const struct ipu7_cpd_hdr *cpd_hdr = cpd; -- -- return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx; --} -- --#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0) -- --static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) --{ -- struct ipu7_cpd_ent *cpd_ent = -- ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -- -- return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); --} -- --static int ipu7_cpd_validate_cpd(struct ipu7_device *isp, -- const void *cpd, unsigned long data_size) --{ -- const struct ipu7_cpd_hdr *cpd_hdr = cpd; -- struct device *dev = &isp->pdev->dev; -- struct ipu7_cpd_ent *ent; -- unsigned int i; -- u8 len; -- -- len = cpd_hdr->hdr_len; -- -- /* Ensure cpd hdr is within moduledata */ -- if (data_size < len) { -- dev_err(dev, "Invalid CPD moduledata size\n"); -- return -EINVAL; -- } -- -- /* Check for CPD file marker */ -- if (cpd_hdr->hdr_mark != CPD_HDR_MARK) { -- dev_err(dev, "Invalid CPD header marker\n"); -- return -EINVAL; -- } -- -- /* Sanity check for CPD entry header */ -- if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) { -- dev_err(dev, "Invalid CPD entry number %d\n", -- cpd_hdr->ent_cnt); -- return -EINVAL; -- } -- if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { -- dev_err(dev, "Invalid CPD entry headers\n"); -- return -EINVAL; -- } -- -- /* Ensure that all entries are within moduledata */ -- ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len); -- for (i = 0; i < cpd_hdr->ent_cnt; i++) { -- if (data_size < ent->offset || -- data_size - ent->offset < ent->len) { -- dev_err(dev, "Invalid CPD entry %d\n", i); -- return -EINVAL; -- } -- ent++; -- } -- -- return 0; --} -- --static int ipu7_cpd_validate_metadata(struct ipu7_device *isp, -- const void *cpd, int idx) --{ -- const struct ipu7_cpd_ent *cpd_ent = -- ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -- const struct ipu7_cpd_metadata *metadata = -- ipu7_cpd_get_metadata(cpd, idx); -- struct device *dev = &isp->pdev->dev; -- -- /* Sanity check for metadata size */ -- if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { -- dev_err(dev, "Invalid metadata size\n"); -- return -EINVAL; -- } -- -- /* Validate type and length of metadata sections */ -- if (metadata->attr.hdr.type != CPD_METADATA_ATTR) { -- dev_err(dev, "Invalid metadata attr type (%d)\n", -- metadata->attr.hdr.type); -- return -EINVAL; -- } -- if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { -- dev_err(dev, "Invalid metadata attr size (%d)\n", -- metadata->attr.hdr.len); -- return -EINVAL; -- } -- if (metadata->ipl.hdr.type != CPD_METADATA_IPL) { -- dev_err(dev, "Invalid metadata ipl type (%d)\n", -- metadata->ipl.hdr.type); -- return -EINVAL; -- } -- if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { -- dev_err(dev, "Invalid metadata ipl size (%d)\n", -- metadata->ipl.hdr.len); -- return -EINVAL; -- } -- -- return 0; --} -- --int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file, -- unsigned long cpd_file_size) --{ -- struct device *dev = &isp->pdev->dev; -- struct ipu7_cpd_ent *ent; -- unsigned int i; -- int ret; -- char *buf; -- -- ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size); -- if (ret) { -- dev_err(dev, "Invalid CPD in file\n"); -- return -EINVAL; -- } -- -- /* Sanity check for manifest size */ -- ent = ipu7_cpd_get_manifest(cpd_file); -- if (ent->len > MAX_MANIFEST_SIZE) { -- dev_err(dev, "Invalid manifest size\n"); -- return -EINVAL; -- } -- -- /* Validate metadata */ -- for (i = 0; i < CPD_BINARY_NUM; i++) { -- ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); -- if (ret) { -- dev_err(dev, "Invalid metadata%d\n", i); -- return ret; -- } -- } -- -- /* Get fw binary version. */ -- buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL); -- if (!buf) -- return -ENOMEM; -- for (i = 0; i < CPD_BINARY_NUM; i++) { -- char *lines[ONLINE_METADATA_LINES]; -- char *info = buf; -- unsigned int l; -- -- ent = ipu7_cpd_get_entry(cpd_file, -- CPD_BINARY_START_IDX + i * 2U); -- memcpy(info, (u8 *)cpd_file + ent->offset + ent->len - -- ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE); -- for (l = 0; l < ONLINE_METADATA_LINES; l++) { -- lines[l] = strsep((char **)&info, "\n"); -- if (!lines[l]) -- break; -- } -- if (l < ONLINE_METADATA_LINES) { -- dev_err(dev, "Failed to parse fw binary%d info.\n", i); -- continue; -- } -- dev_info(dev, "FW binary%d info:\n", i); -- dev_info(dev, "Name: %s\n", lines[1]); -- dev_info(dev, "Version: %s\n", lines[2]); -- dev_info(dev, "Timestamp: %s\n", lines[3]); -- dev_info(dev, "Commit: %s\n", lines[4]); -- } -- kfree(buf); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7"); -- --int ipu7_cpd_copy_binary(const void *cpd, const char *name, -- void *code_region, u32 *entry) --{ -- unsigned int i; -- -- for (i = 0; i < CPD_BINARY_NUM; i++) { -- const struct ipu7_cpd_ent *binary = -- ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U); -- const struct ipu7_cpd_metadata *metadata = -- ipu7_cpd_get_metadata(cpd, i); -- -- if (!strncmp(binary->name, name, sizeof(binary->name))) { -- memcpy(code_region + metadata->ipl.param[0], -- cpd + binary->offset, binary->len); -- *entry = metadata->ipl.param[2]; -- return 0; -- } -- } -- -- return -ENOENT; --} --EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.h b/drivers/media/pci/intel/ipu7/ipu7-cpd.h -deleted file mode 100644 -index b4178848c6b9..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-cpd.h -+++ /dev/null -@@ -1,16 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2015 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_CPD_H --#define IPU7_CPD_H -- --struct ipu7_device; -- --int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, -- const void *cpd_file, -- unsigned long cpd_file_size); --int ipu7_cpd_copy_binary(const void *cpd, const char *name, -- void *code_region, u32 *entry); --#endif /* IPU7_CPD_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.c b/drivers/media/pci/intel/ipu7/ipu7-dma.c -deleted file mode 100644 -index d974e13e7933..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-dma.c -+++ /dev/null -@@ -1,478 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-mmu.h" -- --struct vm_info { -- struct list_head list; -- struct page **pages; -- dma_addr_t ipu7_iova; -- void *vaddr; -- unsigned long size; --}; -- --static struct vm_info *get_vm_info(struct ipu7_mmu *mmu, dma_addr_t iova) --{ -- struct vm_info *info, *save; -- -- list_for_each_entry_safe(info, save, &mmu->vma_list, list) { -- if (iova >= info->ipu7_iova && -- iova < (info->ipu7_iova + info->size)) -- return info; -- } -- -- return NULL; --} -- --static void __clear_buffer(struct page *page, size_t size, unsigned long attrs) --{ -- void *ptr; -- -- if (!page) -- return; -- /* -- * Ensure that the allocated pages are zeroed, and that any data -- * lurking in the kernel direct-mapped region is invalidated. -- */ -- ptr = page_address(page); -- memset(ptr, 0, size); -- if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -- clflush_cache_range(ptr, size); --} -- --static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs) --{ -- unsigned int count = PHYS_PFN(size); -- unsigned int array_size = count * sizeof(struct page *); -- struct page **pages; -- int i = 0; -- -- pages = kvzalloc(array_size, GFP_KERNEL); -- if (!pages) -- return NULL; -- -- gfp |= __GFP_NOWARN; -- -- while (count) { -- int j, order = __fls(count); -- -- pages[i] = alloc_pages(gfp, order); -- while (!pages[i] && order) -- pages[i] = alloc_pages(gfp, --order); -- if (!pages[i]) -- goto error; -- -- if (order) { -- split_page(pages[i], order); -- j = 1U << order; -- while (j--) -- pages[i + j] = pages[i] + j; -- } -- -- __clear_buffer(pages[i], PAGE_SIZE << order, attrs); -- i += 1U << order; -- count -= 1U << order; -- } -- -- return pages; --error: -- while (i--) -- if (pages[i]) -- __free_pages(pages[i], 0); -- kvfree(pages); -- return NULL; --} -- --static void __free_buffer(struct page **pages, size_t size, unsigned long attrs) --{ -- unsigned int count = PHYS_PFN(size); -- unsigned int i; -- -- for (i = 0; i < count && pages[i]; i++) { -- __clear_buffer(pages[i], PAGE_SIZE, attrs); -- __free_pages(pages[i], 0); -- } -- -- kvfree(pages); --} -- --void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -- size_t size) --{ -- void *vaddr; -- u32 offset; -- struct vm_info *info; -- struct ipu7_mmu *mmu = sys->mmu; -- -- info = get_vm_info(mmu, dma_handle); -- if (WARN_ON(!info)) -- return; -- -- offset = dma_handle - info->ipu7_iova; -- if (WARN_ON(size > (info->size - offset))) -- return; -- -- vaddr = info->vaddr + offset; -- clflush_cache_range(vaddr, size); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_single, "INTEL_IPU7"); -- --void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- unsigned int nents) --{ -- struct scatterlist *sg; -- int i; -- -- for_each_sg(sglist, sg, nents, i) -- clflush_cache_range(sg_virt(sg), sg->length); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sg, "INTEL_IPU7"); -- --void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt) --{ -- ipu7_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sgtable, "INTEL_IPU7"); -- --void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -- dma_addr_t *dma_handle, gfp_t gfp, -- unsigned long attrs) --{ -- struct device *dev = &sys->auxdev.dev; -- struct pci_dev *pdev = sys->isp->pdev; -- dma_addr_t pci_dma_addr, ipu7_iova; -- struct ipu7_mmu *mmu = sys->mmu; -- struct vm_info *info; -- unsigned long count; -- struct page **pages; -- struct iova *iova; -- unsigned int i; -- int ret; -- -- info = kzalloc(sizeof(*info), GFP_KERNEL); -- if (!info) -- return NULL; -- -- size = PAGE_ALIGN(size); -- count = PHYS_PFN(size); -- -- iova = alloc_iova(&mmu->dmap->iovad, count, -- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -- if (!iova) -- goto out_kfree; -- -- pages = __alloc_buffer(size, gfp, attrs); -- if (!pages) -- goto out_free_iova; -- -- dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", -- size, iova->pfn_lo, iova->pfn_hi); -- for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { -- pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, -- PAGE_SIZE, DMA_BIDIRECTIONAL, -- attrs); -- dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", -- &pci_dma_addr); -- if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { -- dev_err(dev, "pci_dma_mapping for page[%d] failed", i); -- goto out_unmap; -- } -- -- ret = ipu7_mmu_map(mmu->dmap->mmu_info, -- PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, -- PAGE_SIZE); -- if (ret) { -- dev_err(dev, "ipu7_mmu_map for pci_dma[%d] %pad failed", -- i, &pci_dma_addr); -- dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, -- PAGE_SIZE, DMA_BIDIRECTIONAL, -- attrs); -- goto out_unmap; -- } -- } -- -- info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); -- if (!info->vaddr) -- goto out_unmap; -- -- *dma_handle = PFN_PHYS(iova->pfn_lo); -- -- info->pages = pages; -- info->ipu7_iova = *dma_handle; -- info->size = size; -- list_add(&info->list, &mmu->vma_list); -- -- return info->vaddr; -- --out_unmap: -- while (i--) { -- ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -- pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -- ipu7_iova); -- dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -- DMA_BIDIRECTIONAL, attrs); -- -- ipu7_mmu_unmap(mmu->dmap->mmu_info, ipu7_iova, PAGE_SIZE); -- } -- -- __free_buffer(pages, size, attrs); -- --out_free_iova: -- __free_iova(&mmu->dmap->iovad, iova); --out_kfree: -- kfree(info); -- -- return NULL; --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_alloc, "INTEL_IPU7"); -- --void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -- dma_addr_t dma_handle, unsigned long attrs) --{ -- struct ipu7_mmu *mmu = sys->mmu; -- struct pci_dev *pdev = sys->isp->pdev; -- struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); -- dma_addr_t pci_dma_addr, ipu7_iova; -- struct vm_info *info; -- struct page **pages; -- unsigned int i; -- -- if (WARN_ON(!iova)) -- return; -- -- info = get_vm_info(mmu, dma_handle); -- if (WARN_ON(!info)) -- return; -- -- if (WARN_ON(!info->vaddr)) -- return; -- -- if (WARN_ON(!info->pages)) -- return; -- -- list_del(&info->list); -- -- size = PAGE_ALIGN(size); -- -- pages = info->pages; -- -- vunmap(vaddr); -- -- for (i = 0; i < PHYS_PFN(size); i++) { -- ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -- pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -- ipu7_iova); -- dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -- DMA_BIDIRECTIONAL, attrs); -- } -- -- ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- -- __free_buffer(pages, size, attrs); -- -- mmu->tlb_invalidate(mmu); -- -- __free_iova(&mmu->dmap->iovad, iova); -- -- kfree(info); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_free, "INTEL_IPU7"); -- --int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -- void *addr, dma_addr_t iova, size_t size, -- unsigned long attrs) --{ -- struct ipu7_mmu *mmu = sys->mmu; -- size_t count = PFN_UP(size); -- struct vm_info *info; -- size_t i; -- int ret; -- -- info = get_vm_info(mmu, iova); -- if (!info) -- return -EFAULT; -- -- if (!info->vaddr) -- return -EFAULT; -- -- if (vma->vm_start & ~PAGE_MASK) -- return -EINVAL; -- -- if (size > info->size) -- return -EFAULT; -- -- for (i = 0; i < count; i++) { -- ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), -- info->pages[i]); -- if (ret < 0) -- return ret; -- } -- -- return 0; --} -- --void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_mmu *mmu = sys->mmu; -- struct iova *iova = find_iova(&mmu->dmap->iovad, -- PHYS_PFN(sg_dma_address(sglist))); -- struct scatterlist *sg; -- dma_addr_t pci_dma_addr; -- unsigned int i; -- -- if (!nents) -- return; -- -- if (WARN_ON(!iova)) -- return; -- -- /* -- * Before IPU7 mmu unmap, return the pci dma address back to sg -- * assume the nents is less than orig_nents as the least granule -- * is 1 SZ_4K page -- */ -- dev_dbg(dev, "trying to unmap concatenated %u ents\n", nents); -- for_each_sg(sglist, sg, nents, i) { -- dev_dbg(dev, "unmap sg[%d] %pad size %u\n", i, -- &sg_dma_address(sg), sg_dma_len(sg)); -- pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -- sg_dma_address(sg)); -- dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", -- &pci_dma_addr, i); -- sg_dma_address(sg) = pci_dma_addr; -- } -- -- dev_dbg(dev, "ipu7_mmu_unmap low pfn %lu high pfn %lu\n", -- iova->pfn_lo, iova->pfn_hi); -- ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- -- mmu->tlb_invalidate(mmu); -- __free_iova(&mmu->dmap->iovad, iova); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7"); -- --int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_mmu *mmu = sys->mmu; -- struct scatterlist *sg; -- struct iova *iova; -- size_t npages = 0; -- unsigned long iova_addr; -- int i; -- -- for_each_sg(sglist, sg, nents, i) { -- if (sg->offset) { -- dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", -- i, sg->offset); -- return -EFAULT; -- } -- } -- -- for_each_sg(sglist, sg, nents, i) -- npages += PFN_UP(sg_dma_len(sg)); -- -- dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n", -- nents, npages); -- -- if (attrs & DMA_ATTR_RESERVE_REGION) { -- /* -- * Reserve iova with size aligned to IPU_FW_CODE_REGION_SIZE. -- * Only apply for non-secure mode. -- */ -- unsigned long lo, hi; -- -- lo = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_START); -- hi = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_END) - 1U; -- iova = reserve_iova(&mmu->dmap->iovad, lo, hi); -- if (!iova) { -- dev_err(dev, "Reserve iova[%lx:%lx] failed.\n", lo, hi); -- return -ENOMEM; -- } -- dev_dbg(dev, "iova[%lx:%lx] reserved for FW code.\n", lo, hi); -- } else { -- iova = alloc_iova(&mmu->dmap->iovad, npages, -- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), -- 0); -- if (!iova) -- return 0; -- } -- -- dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, -- iova->pfn_hi); -- -- iova_addr = iova->pfn_lo; -- for_each_sg(sglist, sg, nents, i) { -- phys_addr_t iova_pa; -- int ret; -- -- iova_pa = PFN_PHYS(iova_addr); -- dev_dbg(dev, "mapping entry %d: iova %pap phy %pap size %d\n", -- i, &iova_pa, &sg_dma_address(sg), sg_dma_len(sg)); -- -- ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -- sg_dma_address(sg), -- PAGE_ALIGN(sg_dma_len(sg))); -- if (ret) -- goto out_fail; -- -- sg_dma_address(sg) = PFN_PHYS(iova_addr); -- -- iova_addr += PFN_UP(sg_dma_len(sg)); -- } -- -- dev_dbg(dev, "dmamap %d ents %zu pages mapped\n", nents, npages); -- -- return nents; -- --out_fail: -- ipu7_dma_unmap_sg(sys, sglist, i, dir, attrs); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sg, "INTEL_IPU7"); -- --int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs) --{ -- int nents; -- -- nents = ipu7_dma_map_sg(sys, sgt->sgl, sgt->nents, dir, attrs); -- if (nents < 0) -- return nents; -- -- sgt->nents = nents; -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sgtable, "INTEL_IPU7"); -- --void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs) --{ -- ipu7_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sgtable, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.h b/drivers/media/pci/intel/ipu7/ipu7-dma.h -deleted file mode 100644 -index fe789af5e664..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-dma.h -+++ /dev/null -@@ -1,46 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* Copyright (C) 2013--2025 Intel Corporation */ -- --#ifndef IPU7_DMA_H --#define IPU7_DMA_H -- --#include --#include --#include --#include --#include -- --#include "ipu7-bus.h" -- --#define DMA_ATTR_RESERVE_REGION BIT(31) --struct ipu7_mmu_info; -- --struct ipu7_dma_mapping { -- struct ipu7_mmu_info *mmu_info; -- struct iova_domain iovad; --}; -- --void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -- size_t size); --void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- unsigned int nents); --void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt); --void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -- dma_addr_t *dma_handle, gfp_t gfp, -- unsigned long attrs); --void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -- dma_addr_t dma_handle, unsigned long attrs); --int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -- void *addr, dma_addr_t iova, size_t size, -- unsigned long attrs); --int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs); --void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs); --int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs); --void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs); --#endif /* IPU7_DMA_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -deleted file mode 100644 -index 2958e39a359e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -+++ /dev/null -@@ -1,389 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_insys_config_abi.h" --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7.h" --#include "ipu7-boot.h" --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-fw-isys.h" --#include "ipu7-isys.h" --#include "ipu7-platform-regs.h" --#include "ipu7-syscom.h" -- --static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = { -- "STREAM_OPEN", -- "STREAM_START_AND_CAPTURE", -- "STREAM_CAPTURE", -- "STREAM_ABORT", -- "STREAM_FLUSH", -- "STREAM_CLOSE" --}; -- --int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, -- void *cpu_mapped_buf, -- dma_addr_t dma_mapped_buf, -- size_t size, u16 send_type) --{ -- struct ipu7_syscom_context *ctx = isys->adev->syscom; -- struct device *dev = &isys->adev->auxdev.dev; -- struct ipu7_insys_send_queue_token *token; -- -- if (send_type >= N_IPU_INSYS_SEND_TYPE) -- return -EINVAL; -- -- dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); -- -- /* -- * Time to flush cache in case we have some payload. Not all messages -- * have that -- */ -- if (cpu_mapped_buf) -- clflush_cache_range(cpu_mapped_buf, size); -- -- token = ipu7_syscom_get_token(ctx, stream_handle + -- IPU_INSYS_INPUT_MSG_QUEUE); -- if (!token) -- return -EBUSY; -- -- token->addr = dma_mapped_buf; -- token->buf_handle = (unsigned long)cpu_mapped_buf; -- token->send_type = send_type; -- token->stream_id = stream_handle; -- token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE; -- -- ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE); -- /* now wakeup FW */ -- ipu_buttress_wakeup_is_uc(isys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, u16 send_type) --{ -- return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, -- send_type); --} -- --int ipu7_fw_isys_init(struct ipu7_isys *isys) --{ -- struct syscom_queue_config *queue_configs; -- struct ipu7_bus_device *adev = isys->adev; -- struct device *dev = &adev->auxdev.dev; -- struct ipu7_insys_config *isys_config; -- struct ipu7_syscom_context *syscom; -- dma_addr_t isys_config_dma_addr; -- unsigned int i, num_queues; -- u32 freq; -- u8 major; -- int ret; -- -- /* Allocate and init syscom context. */ -- syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -- GFP_KERNEL); -- if (!syscom) -- return -ENOMEM; -- -- adev->syscom = syscom; -- syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; -- syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; -- num_queues = syscom->num_input_queues + syscom->num_output_queues; -- queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -- GFP_KERNEL); -- if (!queue_configs) { -- ipu7_fw_isys_release(isys); -- return -ENOMEM; -- } -- syscom->queue_configs = queue_configs; -- queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity = -- IPU_ISYS_SIZE_RECV_QUEUE; -- queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = -- sizeof(struct ipu7_insys_resp); -- queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity = -- IPU_ISYS_SIZE_LOG_QUEUE; -- queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = -- sizeof(struct ipu7_insys_resp); -- queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; -- queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; -- -- queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity = -- IPU_ISYS_MAX_STREAMS; -- queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes = -- sizeof(struct ipu7_insys_send_queue_token); -- -- for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) { -- queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE; -- queue_configs[i].token_size_in_bytes = -- sizeof(struct ipu7_insys_send_queue_token); -- } -- -- /* Allocate ISYS subsys config. */ -- isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config), -- &isys_config_dma_addr, GFP_KERNEL, 0); -- if (!isys_config) { -- dev_err(dev, "Failed to allocate isys subsys config.\n"); -- ipu7_fw_isys_release(isys); -- return -ENOMEM; -- } -- isys->subsys_config = isys_config; -- isys->subsys_config_dma_addr = isys_config_dma_addr; -- memset(isys_config, 0, sizeof(struct ipu7_insys_config)); -- isys_config->logger_config.use_source_severity = 0; -- isys_config->logger_config.use_channels_enable_bitmask = 1; -- isys_config->logger_config.channels_enable_bitmask = -- LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK; -- isys_config->logger_config.hw_printf_buffer_base_addr = 0U; -- isys_config->logger_config.hw_printf_buffer_size_bytes = 0U; -- isys_config->wdt_config.wdt_timer1_us = 0; -- isys_config->wdt_config.wdt_timer2_us = 0; -- ret = ipu_buttress_get_isys_freq(adev->isp, &freq); -- if (ret) { -- dev_err(dev, "Failed to get ISYS frequency.\n"); -- ipu7_fw_isys_release(isys); -- return ret; -- } -- -- ipu7_dma_sync_single(adev, isys_config_dma_addr, -- sizeof(struct ipu7_insys_config)); -- -- major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U; -- ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -- freq, isys_config_dma_addr, major); -- if (ret) -- ipu7_fw_isys_release(isys); -- -- return ret; --} -- --void ipu7_fw_isys_release(struct ipu7_isys *isys) --{ -- struct ipu7_bus_device *adev = isys->adev; -- -- ipu7_boot_release_boot_config(adev); -- if (isys->subsys_config) { -- ipu7_dma_free(adev, -- sizeof(struct ipu7_insys_config), -- isys->subsys_config, -- isys->subsys_config_dma_addr, 0); -- isys->subsys_config = NULL; -- isys->subsys_config_dma_addr = 0; -- } --} -- --int ipu7_fw_isys_open(struct ipu7_isys *isys) --{ -- return ipu7_boot_start_fw(isys->adev); --} -- --int ipu7_fw_isys_close(struct ipu7_isys *isys) --{ -- return ipu7_boot_stop_fw(isys->adev); --} -- --struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys) --{ -- return (struct ipu7_insys_resp *) -- ipu7_syscom_get_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_MSG_QUEUE); --} -- --void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) --{ -- ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); --} -- --#ifdef ENABLE_FW_OFFLINE_LOGGER --int ipu7_fw_isys_get_log(struct ipu7_isys *isys) --{ -- u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -- struct device *dev = &isys->adev->auxdev.dev; -- struct isys_fw_log *fw_log = isys->fw_log; -- struct ia_gofo_msg_log *log_msg; -- u8 msg_type, msg_len; -- u32 count, fmt_id; -- void *token; -- -- token = ipu7_syscom_get_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- if (!token) -- return -ENODATA; -- -- while (token) { -- log_msg = (struct ia_gofo_msg_log *)token; -- -- msg_type = log_msg->header.tlv_header.tlv_type; -- msg_len = log_msg->header.tlv_header.tlv_len32; -- if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -- dev_warn(dev, "Invalid msg data from Log queue!\n"); -- -- count = log_msg->log_info_ts.log_info.log_counter; -- fmt_id = log_msg->log_info_ts.log_info.fmt_id; -- if (count > fw_log->count + 1) -- dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -- count, fw_log->count); -- -- if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -- dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -- ipu7_syscom_put_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- return -EIO; -- } -- -- if (log_size + fw_log->head - fw_log->addr > -- FW_LOG_BUF_SIZE) -- fw_log->head = fw_log->addr; -- -- memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -- sizeof(struct ia_gofo_msg_log_info_ts)); -- -- fw_log->count = count; -- fw_log->head += log_size; -- fw_log->size += log_size; -- -- ipu7_syscom_put_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- -- token = ipu7_syscom_get_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- }; -- -- return 0; --} -- --#endif --void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -- struct ipu7_insys_stream_cfg *cfg) --{ -- unsigned int i; -- -- dev_dbg(dev, "---------------------------\n"); -- dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); -- -- dev_dbg(dev, ".port id %d\n", cfg->port_id); -- dev_dbg(dev, ".vc %d\n", cfg->vc); -- dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins); -- dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins); -- dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map); -- -- for (i = 0; i < cfg->nof_input_pins; i++) { -- dev_dbg(dev, ".input_pin[%d]:\n", i); -- dev_dbg(dev, "\t.dt = 0x%0x\n", -- cfg->input_pins[i].dt); -- dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n", -- cfg->input_pins[i].disable_mipi_unpacking); -- dev_dbg(dev, "\t.dt_rename_mode = %d\n", -- cfg->input_pins[i].dt_rename_mode); -- dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", -- cfg->input_pins[i].mapped_dt); -- dev_dbg(dev, "\t.input_res = %d x %d\n", -- cfg->input_pins[i].input_res.width, -- cfg->input_pins[i].input_res.height); -- dev_dbg(dev, "\t.sync_msg_map = 0x%x\n", -- cfg->input_pins[i].sync_msg_map); -- } -- -- for (i = 0; i < cfg->nof_output_pins; i++) { -- dev_dbg(dev, ".output_pin[%d]:\n", i); -- dev_dbg(dev, "\t.input_pin_id = %d\n", -- cfg->output_pins[i].input_pin_id); -- dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); -- dev_dbg(dev, "\t.send_irq = %d\n", -- cfg->output_pins[i].send_irq); -- dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); -- -- dev_dbg(dev, "\t.link.buffer_lines = %d\n", -- cfg->output_pins[i].link.buffer_lines); -- dev_dbg(dev, "\t.link.foreign_key = %d\n", -- cfg->output_pins[i].link.foreign_key); -- dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n", -- cfg->output_pins[i].link.granularity_pointer_update); -- dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n", -- cfg->output_pins[i].link.msg_link_streaming_mode); -- dev_dbg(dev, "\t.link.pbk_id = %d\n", -- cfg->output_pins[i].link.pbk_id); -- dev_dbg(dev, "\t.link.pbk_slot_id = %d\n", -- cfg->output_pins[i].link.pbk_slot_id); -- dev_dbg(dev, "\t.link.dest = %d\n", -- cfg->output_pins[i].link.dest); -- dev_dbg(dev, "\t.link.use_sw_managed = %d\n", -- cfg->output_pins[i].link.use_sw_managed); -- dev_dbg(dev, "\t.link.is_snoop = %d\n", -- cfg->output_pins[i].link.is_snoop); -- -- dev_dbg(dev, "\t.crop.line_top = %d\n", -- cfg->output_pins[i].crop.line_top); -- dev_dbg(dev, "\t.crop.line_bottom = %d\n", -- cfg->output_pins[i].crop.line_bottom); --#ifdef IPU8_INSYS_NEW_ABI -- dev_dbg(dev, "\t.crop.column_left = %d\n", -- cfg->output_pins[i].crop.column_left); -- dev_dbg(dev, "\t.crop.colunm_right = %d\n", -- cfg->output_pins[i].crop.column_right); --#endif -- -- dev_dbg(dev, "\t.dpcm_enable = %d\n", -- cfg->output_pins[i].dpcm.enable); -- dev_dbg(dev, "\t.dpcm.type = %d\n", -- cfg->output_pins[i].dpcm.type); -- dev_dbg(dev, "\t.dpcm.predictor = %d\n", -- cfg->output_pins[i].dpcm.predictor); --#ifdef IPU8_INSYS_NEW_ABI -- dev_dbg(dev, "\t.upipe_enable = %d\n", -- cfg->output_pins[i].upipe_enable); -- dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); -- dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); -- dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); -- dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); -- dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); --#endif -- } -- dev_dbg(dev, "---------------------------\n"); --} -- --void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -- struct ipu7_insys_buffset *buf, -- unsigned int outputs) --{ -- unsigned int i; -- -- dev_dbg(dev, "--------------------------\n"); -- dev_dbg(dev, "IPU_ISYS_BUFF_SET\n"); -- dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map); -- dev_dbg(dev, ".frame_id = %d\n", buf->frame_id); -- dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame); -- -- for (i = 0; i < outputs; i++) { -- dev_dbg(dev, ".output_pin[%d]:\n", i); --#ifndef IPU8_INSYS_NEW_ABI -- dev_dbg(dev, "\t.user_token = %llx\n", -- buf->output_pins[i].user_token); -- dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); --#else -- dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", -- buf->output_pins[i].pin_payload.user_token); -- dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", -- buf->output_pins[i].pin_payload.addr); -- dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", -- buf->output_pins[i].upipe_capture_cfg); --#endif -- } -- dev_dbg(dev, "---------------------------\n"); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -deleted file mode 100644 -index 1235adc9694e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -+++ /dev/null -@@ -1,42 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_ISYS_H --#define IPU7_FW_ISYS_H -- --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --struct device; --struct ipu7_insys_buffset; --struct ipu7_insys_stream_cfg; --struct ipu7_isys; -- --/* From here on type defines not coming from the ISYSAPI interface */ -- --int ipu7_fw_isys_init(struct ipu7_isys *isys); --void ipu7_fw_isys_release(struct ipu7_isys *isys); --int ipu7_fw_isys_open(struct ipu7_isys *isys); --int ipu7_fw_isys_close(struct ipu7_isys *isys); -- --void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -- struct ipu7_insys_stream_cfg *cfg); --void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -- struct ipu7_insys_buffset *buf, -- unsigned int outputs); --int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, u16 send_type); --int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, -- void *cpu_mapped_buf, -- dma_addr_t dma_mapped_buf, -- size_t size, u16 send_type); --struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); --void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); --#ifdef ENABLE_FW_OFFLINE_LOGGER --int ipu7_fw_isys_get_log(struct ipu7_isys *isys); --#endif --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -deleted file mode 100644 -index b8c5db7ae300..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -+++ /dev/null -@@ -1,1034 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include -- --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-platform-regs.h" --#include "ipu7-isys-csi-phy.h" -- --#define PORT_A 0U --#define PORT_B 1U --#define PORT_C 2U --#define PORT_D 3U -- --#define N_DATA_IDS 8U --static DECLARE_BITMAP(data_ids, N_DATA_IDS); -- --struct ddlcal_counter_ref_s { -- u16 min_mbps; -- u16 max_mbps; -- -- u16 ddlcal_counter_ref; --}; -- --struct ddlcal_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 oa_lanex_hsrx_cdphy_sel_fast; -- u16 ddlcal_max_phase; -- u16 phase_bound; -- u16 ddlcal_dll_fbk; -- u16 ddlcal_ddl_coarse_bank; -- u16 fjump_deskew; -- u16 min_eye_opening_deskew; --}; -- --struct i_thssettle_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 i_thssettle; --}; -- -- /* lane2 for 4l3t, lane1 for 2l2t */ --struct oa_lane_clk_div_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 oa_lane_hsrx_hs_clk_div; --}; -- --struct cdr_fbk_cap_prog_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 val; --}; -- --static const struct ddlcal_counter_ref_s table0[] = { -- { 1500, 1999, 118 }, -- { 2000, 2499, 157 }, -- { 2500, 3499, 196 }, -- { 3500, 4499, 274 }, -- { 4500, 4500, 352 }, -- { } --}; -- --static const struct ddlcal_params table1[] = { -- { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 }, -- { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 }, -- { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 }, -- { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 }, -- { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 }, -- { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 }, -- { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 }, -- { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 }, -- { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 }, -- { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 }, -- { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 }, -- { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 }, -- { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 }, -- { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 }, -- { } --}; -- --static const struct i_thssettle_params table2[] = { -- { 80, 124, 24 }, -- { 125, 249, 20 }, -- { 250, 499, 16 }, -- { 500, 749, 14 }, -- { 750, 1499, 13 }, -- { 1500, 4500, 12 }, -- { } --}; -- --static const struct oa_lane_clk_div_params table6[] = { -- { 80, 159, 0x1 }, -- { 160, 319, 0x2 }, -- { 320, 639, 0x3 }, -- { 640, 1279, 0x4 }, -- { 1280, 2560, 0x5 }, -- { 2561, 4500, 0x6 }, -- { } --}; -- --static const struct cdr_fbk_cap_prog_params table7[] = { -- { 80, 919, 0 }, -- { 920, 1029, 1 }, -- { 1030, 1169, 2 }, -- { 1170, 1349, 3 }, -- { 1350, 1589, 4 }, -- { 1590, 1949, 5 }, -- { 1950, 2499, 6 }, -- { } --}; -- --static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -- -- dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x", -- base + addr - isys_base, data); -- writew(data, base + addr); --} -- --static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -- u16 data; -- -- data = readw(base + addr); -- dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x", -- base + addr - isys_base, data); -- -- return data; --} -- --static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -- struct device *dev = &isys->adev->auxdev.dev; -- -- dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, data); -- writel(data, base + addr); -- dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, readl(base + addr)); --} -- --static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) --{ -- void __iomem *isys_base = isys->pdata->base; -- u32 gpreg = isys->pdata->ipdata->csi2.gpreg; -- void __iomem *base = isys_base + gpreg + 0x1000 * id; -- struct device *dev = &isys->adev->auxdev.dev; -- -- dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, data); -- writel(data, base + addr); -- dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, readl(base + addr)); --} -- --static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -- u32 data; -- -- data = readl(base + addr); -- dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x", -- base + addr - isys_base, data); -- -- return data; --} -- --static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -- u16 val, u8 lo, u8 hi) --{ -- u32 temp, mask; -- -- WARN_ON(lo > hi); -- WARN_ON(hi > 15); -- -- mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -- temp = dwc_phy_read(isys, id, addr); -- temp &= ~mask; -- temp |= (val << lo) & mask; -- dwc_phy_write(isys, id, addr, temp); --} -- --static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -- u32 val, u8 hi, u8 lo) --{ -- u32 temp, mask; -- -- WARN_ON(lo > hi); -- -- mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -- temp = dwc_csi_read(isys, id, addr); -- temp &= ~mask; -- temp |= (val << lo) & mask; -- dwc_csi_write(isys, id, addr, temp); --} -- --static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- u32 id, lanes, phy_mode; -- u32 val; -- -- id = csi2->port; -- lanes = csi2->nlanes; -- phy_mode = csi2->phy_mode; -- dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u", -- id, lanes, phy_mode); -- -- val = dwc_csi_read(isys, id, VERSION); -- dev_dbg(dev, "csi-%d controller version = 0x%x", id, val); -- -- /* num of active data lanes */ -- dwc_csi_write(isys, id, N_LANES, lanes - 1); -- dwc_csi_write(isys, id, CDPHY_MODE, phy_mode); -- dwc_csi_write(isys, id, VC_EXTENSION, 0); -- -- /* only mask PHY_FATAL and PKT_FATAL interrupts */ -- dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff); -- dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3); -- dwc_csi_write(isys, id, INT_MSK_PHY, 0x0); -- dwc_csi_write(isys, id, INT_MSK_LINE, 0x0); -- dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0); -- dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0); --} -- --static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id) --{ -- dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0); -- dwc_csi_write(isys, id, DPHY_RSTZ, 0); -- dwc_csi_write(isys, id, CSI2_RESETN, 0); -- gpreg_write(isys, id, PHY_RESET, 0); -- gpreg_write(isys, id, PHY_SHUTDOWN, 0); --} -- --/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */ --static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) --{ -- struct ipu7_isys *isys = csi2->isys; -- u32 reg, n; -- u8 lo, hi; -- int ret; -- -- dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", -- id, vc, dt); -- -- dwc_csi_write(isys, id, VC_EXTENSION, 0x0); -- n = find_first_zero_bit(data_ids, N_DATA_IDS); -- if (n == N_DATA_IDS) -- return -ENOSPC; -- -- ret = test_and_set_bit(n, data_ids); -- if (ret) -- return -EBUSY; -- -- reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2; -- lo = (n % 4) * 8; -- hi = lo + 4; -- dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo); -- -- reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2; -- lo = (n % 4) * 8; -- hi = lo + 5; -- dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo); -- -- return 0; --} -- --static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) --{ -- struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- struct v4l2_mbus_frame_desc desc; -- struct v4l2_subdev *ext_sd; -- struct media_pad *pad; -- unsigned int i; -- int ret; -- -- pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -- if (IS_ERR(pad)) { -- dev_warn(dev, "can't get remote source pad of %s (%ld)\n", -- csi2->asd.sd.name, PTR_ERR(pad)); -- return PTR_ERR(pad); -- } -- -- ext_sd = media_entity_to_v4l2_subdev(pad->entity); -- if (WARN(!ext_sd, "Failed to get subdev for entity %s\n", -- pad->entity->name)) -- return -ENODEV; -- -- ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc); -- if (ret) -- return ret; -- -- if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -- dev_warn(dev, "Unsupported frame descriptor type\n"); -- return -EINVAL; -- } -- -- for (i = 0; i < desc.num_entries; i++) { -- desc_entry = &desc.entry[i]; -- if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { -- ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, -- desc_entry->bus.csi2.dt); -- if (ret) -- return ret; -- } -- } -- -- return 0; --} -- --#define CDPHY_TIMEOUT 5000000U --static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id) --{ -- void __iomem *isys_base = isys->pdata->base; -- u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; -- void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; -- struct device *dev = &isys->adev->auxdev.dev; -- unsigned int i; -- u32 phy_ready; -- u32 reg, rext; -- int ret; -- -- dev_dbg(dev, "waiting phy ready...\n"); -- ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready, -- phy_ready & BIT(0) && phy_ready != ~0U, -- 100, CDPHY_TIMEOUT); -- dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY)); -- dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id, -- dwc_csi_read(isys, id, PHY_RX)); -- dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id, -- dwc_csi_read(isys, id, PHY_STOPSTATE)); -- dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id, -- dwc_csi_read(isys, id, PHY_CAL)); -- for (i = 0; i < 4U; i++) { -- reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U); -- dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n", -- id, i, dwc_phy_read(isys, id, reg)); -- } -- dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5)); -- dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0)); -- dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB)); -- dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB)); -- dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT)); -- -- if (!ret) { -- if (id) { -- dev_dbg(dev, "ignore phy %u rext\n", id); -- return 0; -- } -- -- rext = dwc_phy_read(isys, id, -- CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU; -- dev_dbg(dev, "phy %u rext value = %u\n", id, rext); -- isys->phy_rext_cal = (rext ? rext : 5); -- -- return 0; -- } -- -- dev_err(dev, "wait phy ready timeout!\n"); -- -- return ret; --} -- --static int lookup_table1(u64 mbps) --{ -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(table1); i++) { -- if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps) -- return i; -- } -- -- return -ENXIO; --} -- --static const u16 deskew_fine_mem[] = { -- 0x0404, 0x040c, 0x0414, 0x041c, -- 0x0423, 0x0429, 0x0430, 0x043a, -- 0x0445, 0x044a, 0x0450, 0x045a, -- 0x0465, 0x0469, 0x0472, 0x047a, -- 0x0485, 0x0489, 0x0490, 0x049a, -- 0x04a4, 0x04ac, 0x04b4, 0x04bc, -- 0x04c4, 0x04cc, 0x04d4, 0x04dc, -- 0x04e4, 0x04ec, 0x04f4, 0x04fc, -- 0x0504, 0x050c, 0x0514, 0x051c, -- 0x0523, 0x0529, 0x0530, 0x053a, -- 0x0545, 0x054a, 0x0550, 0x055a, -- 0x0565, 0x0569, 0x0572, 0x057a, -- 0x0585, 0x0589, 0x0590, 0x059a, -- 0x05a4, 0x05ac, 0x05b4, 0x05bc, -- 0x05c4, 0x05cc, 0x05d4, 0x05dc, -- 0x05e4, 0x05ec, 0x05f4, 0x05fc, -- 0x0604, 0x060c, 0x0614, 0x061c, -- 0x0623, 0x0629, 0x0632, 0x063a, -- 0x0645, 0x064a, 0x0650, 0x065a, -- 0x0665, 0x0669, 0x0672, 0x067a, -- 0x0685, 0x0689, 0x0690, 0x069a, -- 0x06a4, 0x06ac, 0x06b4, 0x06bc, -- 0x06c4, 0x06cc, 0x06d4, 0x06dc, -- 0x06e4, 0x06ec, 0x06f4, 0x06fc, -- 0x0704, 0x070c, 0x0714, 0x071c, -- 0x0723, 0x072a, 0x0730, 0x073a, -- 0x0745, 0x074a, 0x0750, 0x075a, -- 0x0765, 0x0769, 0x0772, 0x077a, -- 0x0785, 0x0789, 0x0790, 0x079a, -- 0x07a4, 0x07ac, 0x07b4, 0x07bc, -- 0x07c4, 0x07cc, 0x07d4, 0x07dc, -- 0x07e4, 0x07ec, 0x07f4, 0x07fc, --}; -- --static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -- bool aggregation, u64 mbps) --{ -- u16 hsrxval0 = 0; -- u16 hsrxval1 = 0; -- u16 hsrxval2 = 0; -- int index; -- u16 reg; -- u16 val; -- u32 i; -- -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9); -- if (mbps > 1500) -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -- 40, 0, 7); -- else -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -- 104, 0, 7); -- -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6); -- -- for (i = 0; i < ARRAY_SIZE(table0); i++) { -- if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) { -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3, -- table0[i].ddlcal_counter_ref, -- 0, 9); -- break; -- } -- } -- -- index = lookup_table1(mbps); -- if (index >= 0) { -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, -- table1[index].phase_bound, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -- table1[index].ddlcal_dll_fbk, 4, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -- table1[index].ddlcal_ddl_coarse_bank, 0, 3); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8; -- val = table1[index].oa_lanex_hsrx_cdphy_sel_fast; -- for (i = 0; i < lanes + 1; i++) -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -- 12, 12); -- } -- -- reg = CORE_DIG_DLANE_0_RW_LP_0; -- for (i = 0; i < lanes; i++) -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, -- 0, 0, 0); -- if (!is_ipu7(isys->adev->isp->hw_ver) || -- id == PORT_B || id == PORT_C) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -- 1, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -- 0, 0, 0); -- } else { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -- 0, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -- 1, 0, 0); -- } -- -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -- 0, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -- 0, 0, 0); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12; -- val = (mbps > 1500) ? 0 : 1; -- for (i = 0; i < lanes + 1; i++) { -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3); -- } -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13; -- val = (mbps > 1500) ? 0 : 1; -- for (i = 0; i < lanes + 1; i++) { -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3); -- } -- -- if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C) -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9; -- else -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9; -- -- for (i = 0; i < ARRAY_SIZE(table6); i++) { -- if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) { -- dwc_phy_write_mask(isys, id, reg, -- table6[i].oa_lane_hsrx_hs_clk_div, -- 5, 7); -- break; -- } -- } -- -- if (aggregation) { -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, -- 1, 1); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15; -- dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -- -- val = (id == PORT_A) ? 3 : 0; -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15; -- dwc_phy_write_mask(isys, id, reg, val, 3, 4); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15; -- dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7); -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_0; -- for (i = 0; i < ARRAY_SIZE(table2); i++) { -- if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) { -- u8 j; -- -- for (j = 0; j < lanes; j++) -- dwc_phy_write_mask(isys, id, reg + (j * 0x400), -- table2[i].i_thssettle, -- 8, 15); -- break; -- } -- } -- -- /* deskew */ -- for (i = 0; i < lanes; i++) { -- reg = CORE_DIG_DLANE_0_RW_CFG_1; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), -- ((mbps > 1500) ? 0x1 : 0x2), 2, 3); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_2; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), -- ((mbps > 2500) ? 0 : 1), 15, 15); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12); -- -- reg = CORE_DIG_DLANE_0_RW_LP_0; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15); -- -- reg = CORE_DIG_DLANE_0_RW_LP_2; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_1; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_3; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2); -- index = lookup_table1(mbps); -- if (index >= 0) { -- val = table1[index].fjump_deskew; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -- 3, 8); -- } -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_4; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_5; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_6; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7); -- index = lookup_table1(mbps); -- if (index >= 0) { -- val = table1[index].min_eye_opening_deskew; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -- 8, 15); -- } -- reg = CORE_DIG_DLANE_0_RW_HS_RX_7; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_9; -- index = lookup_table1(mbps); -- if (index >= 0) { -- val = table1[index].ddlcal_max_phase; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), -- val, 0, 7); -- } -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15); -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0); -- -- for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++) -- dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM, -- deskew_fine_mem[i], 0, 15); -- -- if (mbps > 1500) { -- hsrxval0 = 4; -- hsrxval2 = 3; -- } -- -- if (mbps > 2500) -- hsrxval1 = 2; -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -- hsrxval0, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -- hsrxval0, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -- hsrxval0, 0, 2); -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -- hsrxval0, 0, 2); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -- hsrxval0, 0, 2); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -- hsrxval1, 3, 4); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -- hsrxval1, 3, 4); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -- hsrxval1, 3, 4); -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -- hsrxval1, 3, 4); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -- hsrxval1, 3, 4); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15, -- hsrxval2, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15, -- hsrxval2, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15, -- hsrxval2, 0, 2); -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15, -- hsrxval2, 0, 2); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15, -- hsrxval2, 0, 2); -- } -- -- /* force and override rext */ -- if (isys->phy_rext_cal && id) { -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, -- isys->phy_rext_cal, 0, 3); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -- 1, 11, 11); -- } --} -- --static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -- bool aggregation, u64 mbps) --{ -- u8 trios = 2; -- u16 coarse_target; -- u16 deass_thresh; -- u16 delay_thresh; -- u16 reset_thresh; -- u16 cap_prog = 6U; -- u16 reg; -- u16 val; -- u32 i; -- u64 r64; -- u32 r; -- -- if (is_ipu7p5(isys->adev->isp->hw_ver)) -- val = 0x15; -- else -- val = 0x155; -- -- if (is_ipu7(isys->adev->isp->hw_ver)) -- trios = 3; -- -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); -- -- reg = CORE_DIG_CLANE_0_RW_LP_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -- -- val = (mbps > 900U) ? 1U : 0U; -- for (i = 0; i < trios; i++) { -- reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_1; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_5; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_6; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15); -- } -- -- /* -- * Below 900Msps, always use the same value. -- * The formula is suitable for data rate 80-3500Msps. -- * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5 -- */ -- if (mbps >= 80U) -- coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1; -- else -- coarse_target = 56; -- -- for (i = 0; i < trios; i++) { -- reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; -- dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); -- } -- -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); -- -- if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -- 1, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -- 0, 0, 0); -- } -- -- for (i = 0; i < trios; i++) { -- reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; -- dwc_phy_write_mask(isys, id, reg, 1, 6, 8); -- dwc_phy_write_mask(isys, id, reg, 1, 3, 5); -- dwc_phy_write_mask(isys, id, reg, 2, 0, 2); -- } -- -- deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1; -- if (r64 != 0) -- deass_thresh++; -- -- reg = CORE_DIG_RW_TRIO0_2; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, -- deass_thresh, 0, 7); -- -- delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u; -- -- if (delay_thresh < 1) -- delay_thresh = 1; -- -- reg = CORE_DIG_RW_TRIO0_1; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, -- delay_thresh, 0, 15); -- -- reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r); -- if (!r) -- reset_thresh--; -- -- if (reset_thresh < 1) -- reset_thresh = 1; -- -- reg = CORE_DIG_RW_TRIO0_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, -- reset_thresh, 9, 11); -- -- reg = CORE_DIG_CLANE_0_RW_LP_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); -- -- reg = CORE_DIG_CLANE_0_RW_LP_2; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); -- -- for (i = 0; i < ARRAY_SIZE(table7); i++) { -- if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) { -- cap_prog = table7[i].val; -- break; -- } -- } -- -- for (i = 0; i < (lanes + 1); i++) { -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; -- dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); -- dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; -- dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); -- } --} -- --static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -- bool aggregation) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- u32 phy_mode; -- s64 link_freq; -- u64 mbps; -- -- if (aggregation) -- link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]); -- else -- link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]); -- -- if (link_freq < 0) { -- dev_err(dev, "get link freq failed (%lld)\n", link_freq); -- return link_freq; -- } -- -- mbps = div_u64(link_freq, 500000); -- dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n", -- id, lanes, aggregation, mbps); -- -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7); -- dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2, -- 1, 12, 13); -- dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0, -- 63, 2, 7); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1, -- 563, 0, 11); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7); -- /* bypass the RCAL state (bit6) */ -- if (aggregation && id != PORT_A) -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45, -- 0, 7); -- -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8); -- dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6); -- dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1); -- dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -- 0, 10, 10); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -- 1, 10, 10); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -- 0, 15, 15); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3, -- 3, 8, 9); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -- 0, 15, 15); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6, -- 7, 12, 14); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -- 0, 8, 10); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, -- 0, 8, 8); -- -- if (aggregation) -- phy_mode = isys->csi2[0].phy_mode; -- else -- phy_mode = isys->csi2[id].phy_mode; -- -- if (phy_mode == PHY_MODE_DPHY) { -- ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps); -- } else if (phy_mode == PHY_MODE_CPHY) { -- ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps); -- } else { -- dev_err(dev, "unsupported phy mode %d!\n", -- isys->csi2[id].phy_mode); -- } -- -- return 0; --} -- --int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- u32 lanes = csi2->nlanes; -- bool aggregation = false; -- u32 id = csi2->port; -- int ret; -- -- /* lanes remapping for aggregation (port AB) mode */ -- if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { -- aggregation = true; -- lanes = 2; -- } -- -- ipu7_isys_csi_phy_reset(isys, id); -- gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); -- gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -- gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); -- gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); -- gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); -- -- /* config PORT_B if aggregation mode */ -- if (aggregation) { -- ipu7_isys_csi_phy_reset(isys, PORT_B); -- gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0); -- gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3); -- gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -- gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf); -- gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode); -- } -- -- ipu7_isys_csi_ctrl_cfg(csi2); -- ipu7_isys_csi_ctrl_dids_config(csi2, id); -- -- ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, id, PHY_RESET, 1); -- gpreg_write(isys, id, PHY_SHUTDOWN, 1); -- dwc_csi_write(isys, id, DPHY_RSTZ, 1); -- dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1); -- dwc_csi_write(isys, id, CSI2_RESETN, 1); -- -- ret = ipu7_isys_phy_ready(isys, id); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0); -- gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0); -- -- /* config PORT_B if aggregation mode */ -- if (aggregation) { -- ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, PORT_B, PHY_RESET, 1); -- gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1); -- dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1); -- dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1); -- dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1); -- ret = ipu7_isys_phy_ready(isys, PORT_B); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0); -- gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0); -- } -- -- return 0; --} -- --void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- -- ipu7_isys_csi_phy_reset(isys, csi2->port); -- if (!is_ipu7(isys->adev->isp->hw_ver) && -- csi2->nlanes > 2U && csi2->port == PORT_A) -- ipu7_isys_csi_phy_reset(isys, PORT_B); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -deleted file mode 100644 -index dfdcb61540c4..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -+++ /dev/null -@@ -1,16 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_CSI_PHY_H --#define IPU7_ISYS_CSI_PHY_H -- --struct ipu7_isys; -- --#define PHY_MODE_DPHY 0U --#define PHY_MODE_CPHY 1U -- --int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2); --void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -deleted file mode 100644 -index aad52c44a005..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -+++ /dev/null -@@ -1,1197 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_CSI2_REG_H --#define IPU7_ISYS_CSI2_REG_H -- --/* IS main regs base */ --#define IS_MAIN_BASE 0x240000 --#define IS_MAIN_S2B_BASE (IS_MAIN_BASE + 0x22000) --#define IS_MAIN_B2O_BASE (IS_MAIN_BASE + 0x26000) --#define IS_MAIN_ISD_M0_BASE (IS_MAIN_BASE + 0x2b000) --#define IS_MAIN_ISD_M1_BASE (IS_MAIN_BASE + 0x2b100) --#define IS_MAIN_ISD_INT_BASE (IS_MAIN_BASE + 0x2b200) --#define IS_MAIN_GDA_BASE (IS_MAIN_BASE + 0x32000) --#define IS_MAIN_GPREGS_MAIN_BASE (IS_MAIN_BASE + 0x32500) --#define IS_MAIN_IRQ_CTRL_BASE (IS_MAIN_BASE + 0x32700) --#define IS_MAIN_PWM_CTRL_BASE (IS_MAIN_BASE + 0x32b00) -- --#define S2B_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_S2B_BASE + 0x1c) --#define S2B_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_S2B_BASE + 0x20) --#define S2B_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_S2B_BASE + 0x24) --#define S2B_IID_IRQ_CTL_STATUS(iid) (IS_MAIN_S2B_BASE + 0x94 + \ -- 0x100 * (iid)) -- --#define B2O_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_B2O_BASE + 0x30) --#define B2O_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_B2O_BASE + 0x34) --#define B2O_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_B2O_BASE + 0x38) --#define B2O_IID_IRQ_CTL_STATUS(oid) (IS_MAIN_B2O_BASE + 0x3dc + \ -- 0x200 * (oid)) -- --#define ISD_M0_IRQ_CTL_STATUS (IS_MAIN_ISD_M0_BASE + 0x1c) --#define ISD_M0_IRQ_CTL_CLEAR (IS_MAIN_ISD_M0_BASE + 0x20) --#define ISD_M0_IRQ_CTL_ENABLE (IS_MAIN_ISD_M0_BASE + 0x24) -- --#define ISD_M1_IRQ_CTL_STATUS (IS_MAIN_ISD_M1_BASE + 0x1c) --#define ISD_M1_IRQ_CTL_CLEAR (IS_MAIN_ISD_M1_BASE + 0x20) --#define ISD_M1_IRQ_CTL_ENABLE (IS_MAIN_ISD_M1_BASE + 0x24) -- --#define ISD_INT_IRQ_CTL_STATUS (IS_MAIN_ISD_INT_BASE + 0x1c) --#define ISD_INT_IRQ_CTL_CLEAR (IS_MAIN_ISD_INT_BASE + 0x20) --#define ISD_INT_IRQ_CTL_ENABLE (IS_MAIN_ISD_INT_BASE + 0x24) -- --#define GDA_IRQ_CTL_STATUS (IS_MAIN_GDA_BASE + 0x1c) --#define GDA_IRQ_CTL_CLEAR (IS_MAIN_GDA_BASE + 0x20) --#define GDA_IRQ_CTL_ENABLE (IS_MAIN_GDA_BASE + 0x24) -- --#define IS_MAIN_IRQ_CTL_EDGE IS_MAIN_IRQ_CTRL_BASE --#define IS_MAIN_IRQ_CTL_MASK (IS_MAIN_IRQ_CTRL_BASE + 0x4) --#define IS_MAIN_IRQ_CTL_STATUS (IS_MAIN_IRQ_CTRL_BASE + 0x8) --#define IS_MAIN_IRQ_CTL_CLEAR (IS_MAIN_IRQ_CTRL_BASE + 0xc) --#define IS_MAIN_IRQ_CTL_ENABLE (IS_MAIN_IRQ_CTRL_BASE + 0x10) --#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IS_MAIN_IRQ_CTRL_BASE + 0x14) -- --/* IS IO regs base */ --#define IS_PHY_NUM 4U --#define IS_IO_BASE 0x280000 -- --/* dwc csi cdphy registers */ --#define IS_IO_CDPHY_BASE(i) (IS_IO_BASE + 0x10000 * (i)) --#define PPI_STARTUP_RW_COMMON_DPHY_0 0x1800 --#define PPI_STARTUP_RW_COMMON_DPHY_1 0x1802 --#define PPI_STARTUP_RW_COMMON_DPHY_2 0x1804 --#define PPI_STARTUP_RW_COMMON_DPHY_3 0x1806 --#define PPI_STARTUP_RW_COMMON_DPHY_4 0x1808 --#define PPI_STARTUP_RW_COMMON_DPHY_5 0x180a --#define PPI_STARTUP_RW_COMMON_DPHY_6 0x180c --#define PPI_STARTUP_RW_COMMON_DPHY_7 0x180e --#define PPI_STARTUP_RW_COMMON_DPHY_8 0x1810 --#define PPI_STARTUP_RW_COMMON_DPHY_9 0x1812 --#define PPI_STARTUP_RW_COMMON_DPHY_A 0x1814 --#define PPI_STARTUP_RW_COMMON_DPHY_10 0x1820 --#define PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822 --#define PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824 --#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a --#define PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c --#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e --#define PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850 --#define PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852 --#define PPI_CALIBCTRL_R_ADC_DEBUG 0x1854 --#define PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00 --#define PPI_RW_LPDCOCAL_TIMEBASE 0x1c02 --#define PPI_RW_LPDCOCAL_NREF 0x1c04 --#define PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06 --#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08 --#define PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a --#define PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c --#define PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e --#define PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18 --#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a --#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c --#define PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e --#define PPI_RW_LB_TIMEBASE_CONFIG 0x1c20 --#define PPI_RW_LB_STARTCMU_CONFIG 0x1c22 --#define PPI_R_LBPULSE_COUNTER_RB 0x1c24 --#define PPI_R_LB_START_CMU_RB 0x1c26 --#define PPI_RW_LB_DPHY_BURST_START 0x1c28 --#define PPI_RW_LB_CPHY_BURST_START 0x1c2a --#define PPI_RW_DDLCAL_CFG_0 0x1c40 --#define PPI_RW_DDLCAL_CFG_1 0x1c42 --#define PPI_RW_DDLCAL_CFG_2 0x1c44 --#define PPI_RW_DDLCAL_CFG_3 0x1c46 --#define PPI_RW_DDLCAL_CFG_4 0x1c48 --#define PPI_RW_DDLCAL_CFG_5 0x1c4a --#define PPI_RW_DDLCAL_CFG_6 0x1c4c --#define PPI_RW_DDLCAL_CFG_7 0x1c4e --#define PPI_R_DDLCAL_DEBUG_0 0x1c50 --#define PPI_R_DDLCAL_DEBUG_1 0x1c52 --#define PPI_RW_PARITY_TEST 0x1c60 --#define PPI_RW_STARTUP_OVR_0 0x1c62 --#define PPI_RW_STARTUP_STATE_OVR_1 0x1c64 --#define PPI_RW_DTB_SELECTOR 0x1c66 --#define PPI_RW_DPHY_CLK_SPARE 0x1c6a --#define PPI_RW_COMMON_CFG 0x1c6c --#define PPI_RW_TERMCAL_CFG_0 0x1c80 --#define PPI_R_TERMCAL_DEBUG_0 0x1c82 --#define PPI_RW_TERMCAL_CTRL_0 0x1c84 --#define PPI_RW_OFFSETCAL_CFG_0 0x1ca0 --#define PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2 --#define PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4 --#define PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6 --#define PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8 --#define PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa --#define PPI_RW_HSDCOCAL_CFG_O 0x1d00 --#define PPI_RW_HSDCOCAL_CFG_1 0x1d02 --#define PPI_RW_HSDCOCAL_CFG_2 0x1d04 --#define PPI_RW_HSDCOCAL_CFG_3 0x1d06 --#define PPI_RW_HSDCOCAL_CFG_4 0x1d08 --#define PPI_RW_HSDCOCAL_CFG_5 0x1d0a --#define PPI_RW_HSDCOCAL_CFG_6 0x1d0c --#define PPI_RW_HSDCOCAL_CFG_7 0x1d0e --#define PPI_RW_HSDCOCAL_CFG_8 0x1d10 --#define PPI_R_HSDCOCAL_DEBUG_RB 0x1d12 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8 --#define CORE_DIG_RW_TRIO0_0 0x2100 --#define CORE_DIG_RW_TRIO0_1 0x2102 --#define CORE_DIG_RW_TRIO0_2 0x2104 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8 --#define CORE_DIG_RW_TRIO1_0 0x2500 --#define CORE_DIG_RW_TRIO1_1 0x2502 --#define CORE_DIG_RW_TRIO1_2 0x2504 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8 --#define CORE_DIG_RW_TRIO2_0 0x2900 --#define CORE_DIG_RW_TRIO2_1 0x2902 --#define CORE_DIG_RW_TRIO2_2 0x2904 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856 --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858 --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860 --#define CORE_DIG_RW_COMMON_0 0x3880 --#define CORE_DIG_RW_COMMON_1 0x3882 --#define CORE_DIG_RW_COMMON_2 0x3884 --#define CORE_DIG_RW_COMMON_3 0x3886 --#define CORE_DIG_RW_COMMON_4 0x3888 --#define CORE_DIG_RW_COMMON_5 0x388a --#define CORE_DIG_RW_COMMON_6 0x388c --#define CORE_DIG_RW_COMMON_7 0x388e --#define CORE_DIG_RW_COMMON_8 0x3890 --#define CORE_DIG_RW_COMMON_9 0x3892 --#define CORE_DIG_RW_COMMON_10 0x3894 --#define CORE_DIG_RW_COMMON_11 0x3896 --#define CORE_DIG_RW_COMMON_12 0x3898 --#define CORE_DIG_RW_COMMON_13 0x389a --#define CORE_DIG_RW_COMMON_14 0x389c --#define CORE_DIG_RW_COMMON_15 0x389e --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0 --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2 --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4 --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6 --#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0 --#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2 --#define PPI_RW_DPHY_LANE0_LBERT_0 0x4000 --#define PPI_RW_DPHY_LANE0_LBERT_1 0x4002 --#define PPI_R_DPHY_LANE0_LBERT_0 0x4004 --#define PPI_R_DPHY_LANE0_LBERT_1 0x4006 --#define PPI_RW_DPHY_LANE0_SPARE 0x4008 --#define PPI_RW_DPHY_LANE1_LBERT_0 0x4400 --#define PPI_RW_DPHY_LANE1_LBERT_1 0x4402 --#define PPI_R_DPHY_LANE1_LBERT_0 0x4404 --#define PPI_R_DPHY_LANE1_LBERT_1 0x4406 --#define PPI_RW_DPHY_LANE1_SPARE 0x4408 --#define PPI_RW_DPHY_LANE2_LBERT_0 0x4800 --#define PPI_RW_DPHY_LANE2_LBERT_1 0x4802 --#define PPI_R_DPHY_LANE2_LBERT_0 0x4804 --#define PPI_R_DPHY_LANE2_LBERT_1 0x4806 --#define PPI_RW_DPHY_LANE2_SPARE 0x4808 --#define PPI_RW_DPHY_LANE3_LBERT_0 0x4c00 --#define PPI_RW_DPHY_LANE3_LBERT_1 0x4c02 --#define PPI_R_DPHY_LANE3_LBERT_0 0x4c04 --#define PPI_R_DPHY_LANE3_LBERT_1 0x4c06 --#define PPI_RW_DPHY_LANE3_SPARE 0x4c08 --#define CORE_DIG_DLANE_0_RW_CFG_0 0x6000 --#define CORE_DIG_DLANE_0_RW_CFG_1 0x6002 --#define CORE_DIG_DLANE_0_RW_CFG_2 0x6004 --#define CORE_DIG_DLANE_0_RW_LP_0 0x6080 --#define CORE_DIG_DLANE_0_RW_LP_1 0x6082 --#define CORE_DIG_DLANE_0_RW_LP_2 0x6084 --#define CORE_DIG_DLANE_0_R_LP_0 0x60a0 --#define CORE_DIG_DLANE_0_R_LP_1 0x60a2 --#define CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0 --#define CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100 --#define CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102 --#define CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104 --#define CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106 --#define CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108 --#define CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a --#define CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c --#define CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e --#define CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110 --#define CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112 --#define CORE_DIG_DLANE_0_R_HS_RX_0 0x6120 --#define CORE_DIG_DLANE_0_R_HS_RX_1 0x6122 --#define CORE_DIG_DLANE_0_R_HS_RX_2 0x6124 --#define CORE_DIG_DLANE_0_R_HS_RX_3 0x6126 --#define CORE_DIG_DLANE_0_R_HS_RX_4 0x6128 --#define CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200 --#define CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202 --#define CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204 --#define CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206 --#define CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208 --#define CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a --#define CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c --#define CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e --#define CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210 --#define CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212 --#define CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214 --#define CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216 --#define CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218 --#define CORE_DIG_DLANE_1_RW_CFG_0 0x6400 --#define CORE_DIG_DLANE_1_RW_CFG_1 0x6402 --#define CORE_DIG_DLANE_1_RW_CFG_2 0x6404 --#define CORE_DIG_DLANE_1_RW_LP_0 0x6480 --#define CORE_DIG_DLANE_1_RW_LP_1 0x6482 --#define CORE_DIG_DLANE_1_RW_LP_2 0x6484 --#define CORE_DIG_DLANE_1_R_LP_0 0x64a0 --#define CORE_DIG_DLANE_1_R_LP_1 0x64a2 --#define CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0 --#define CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500 --#define CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502 --#define CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504 --#define CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506 --#define CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508 --#define CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a --#define CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c --#define CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e --#define CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510 --#define CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512 --#define CORE_DIG_DLANE_1_R_HS_RX_0 0x6520 --#define CORE_DIG_DLANE_1_R_HS_RX_1 0x6522 --#define CORE_DIG_DLANE_1_R_HS_RX_2 0x6524 --#define CORE_DIG_DLANE_1_R_HS_RX_3 0x6526 --#define CORE_DIG_DLANE_1_R_HS_RX_4 0x6528 --#define CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600 --#define CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602 --#define CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604 --#define CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606 --#define CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608 --#define CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a --#define CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c --#define CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e --#define CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610 --#define CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612 --#define CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614 --#define CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616 --#define CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618 --#define CORE_DIG_DLANE_2_RW_CFG_0 0x6800 --#define CORE_DIG_DLANE_2_RW_CFG_1 0x6802 --#define CORE_DIG_DLANE_2_RW_CFG_2 0x6804 --#define CORE_DIG_DLANE_2_RW_LP_0 0x6880 --#define CORE_DIG_DLANE_2_RW_LP_1 0x6882 --#define CORE_DIG_DLANE_2_RW_LP_2 0x6884 --#define CORE_DIG_DLANE_2_R_LP_0 0x68a0 --#define CORE_DIG_DLANE_2_R_LP_1 0x68a2 --#define CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0 --#define CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900 --#define CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902 --#define CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904 --#define CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906 --#define CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908 --#define CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a --#define CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c --#define CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e --#define CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910 --#define CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912 --#define CORE_DIG_DLANE_2_R_HS_RX_0 0x6920 --#define CORE_DIG_DLANE_2_R_HS_RX_1 0x6922 --#define CORE_DIG_DLANE_2_R_HS_RX_2 0x6924 --#define CORE_DIG_DLANE_2_R_HS_RX_3 0x6926 --#define CORE_DIG_DLANE_2_R_HS_RX_4 0x6928 --#define CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00 --#define CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02 --#define CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04 --#define CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06 --#define CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08 --#define CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a --#define CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c --#define CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e --#define CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10 --#define CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12 --#define CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14 --#define CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16 --#define CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18 --#define CORE_DIG_DLANE_3_RW_CFG_0 0x6c00 --#define CORE_DIG_DLANE_3_RW_CFG_1 0x6c02 --#define CORE_DIG_DLANE_3_RW_CFG_2 0x6c04 --#define CORE_DIG_DLANE_3_RW_LP_0 0x6c80 --#define CORE_DIG_DLANE_3_RW_LP_1 0x6c82 --#define CORE_DIG_DLANE_3_RW_LP_2 0x6c84 --#define CORE_DIG_DLANE_3_R_LP_0 0x6ca0 --#define CORE_DIG_DLANE_3_R_LP_1 0x6ca2 --#define CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0 --#define CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00 --#define CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02 --#define CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04 --#define CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06 --#define CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08 --#define CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a --#define CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c --#define CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e --#define CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10 --#define CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12 --#define CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20 --#define CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22 --#define CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24 --#define CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26 --#define CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28 --#define CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00 --#define CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02 --#define CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04 --#define CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06 --#define CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08 --#define CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a --#define CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c --#define CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e --#define CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10 --#define CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12 --#define CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14 --#define CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16 --#define CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18 --#define CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000 --#define CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002 --#define CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004 --#define CORE_DIG_DLANE_CLK_RW_LP_0 0x7080 --#define CORE_DIG_DLANE_CLK_RW_LP_1 0x7082 --#define CORE_DIG_DLANE_CLK_RW_LP_2 0x7084 --#define CORE_DIG_DLANE_CLK_R_LP_0 0x70a0 --#define CORE_DIG_DLANE_CLK_R_LP_1 0x70a2 --#define CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a --#define CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c --#define CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e --#define CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112 --#define CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120 --#define CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122 --#define CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124 --#define CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126 --#define CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a --#define CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c --#define CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e --#define CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218 --#define PPI_RW_CPHY_TRIO0_LBERT_0 0x8000 --#define PPI_RW_CPHY_TRIO0_LBERT_1 0x8002 --#define PPI_R_CPHY_TRIO0_LBERT_0 0x8004 --#define PPI_R_CPHY_TRIO0_LBERT_1 0x8006 --#define PPI_RW_CPHY_TRIO0_SPARE 0x8008 --#define PPI_RW_CPHY_TRIO1_LBERT_0 0x8400 --#define PPI_RW_CPHY_TRIO1_LBERT_1 0x8402 --#define PPI_R_CPHY_TRIO1_LBERT_0 0x8404 --#define PPI_R_CPHY_TRIO1_LBERT_1 0x8406 --#define PPI_RW_CPHY_TRIO1_SPARE 0x8408 --#define PPI_RW_CPHY_TRIO2_LBERT_0 0x8800 --#define PPI_RW_CPHY_TRIO2_LBERT_1 0x8802 --#define PPI_R_CPHY_TRIO2_LBERT_0 0x8804 --#define PPI_R_CPHY_TRIO2_LBERT_1 0x8806 --#define PPI_RW_CPHY_TRIO2_SPARE 0x8808 --#define CORE_DIG_CLANE_0_RW_CFG_0 0xa000 --#define CORE_DIG_CLANE_0_RW_CFG_2 0xa004 --#define CORE_DIG_CLANE_0_RW_LP_0 0xa080 --#define CORE_DIG_CLANE_0_RW_LP_1 0xa082 --#define CORE_DIG_CLANE_0_RW_LP_2 0xa084 --#define CORE_DIG_CLANE_0_R_LP_0 0xa0a0 --#define CORE_DIG_CLANE_0_R_LP_1 0xa0a2 --#define CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100 --#define CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102 --#define CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104 --#define CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106 --#define CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108 --#define CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a --#define CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c --#define CORE_DIG_CLANE_0_R_RX_0 0xa120 --#define CORE_DIG_CLANE_0_R_RX_1 0xa122 --#define CORE_DIG_CLANE_0_R_TX_0 0xa124 --#define CORE_DIG_CLANE_0_R_RX_2 0xa126 --#define CORE_DIG_CLANE_0_R_RX_3 0xa128 --#define CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200 --#define CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202 --#define CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204 --#define CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206 --#define CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208 --#define CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a --#define CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c --#define CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e --#define CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210 --#define CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212 --#define CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214 --#define CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216 --#define CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218 --#define CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a --#define CORE_DIG_CLANE_1_RW_CFG_0 0xa400 --#define CORE_DIG_CLANE_1_RW_CFG_2 0xa404 --#define CORE_DIG_CLANE_1_RW_LP_0 0xa480 --#define CORE_DIG_CLANE_1_RW_LP_1 0xa482 --#define CORE_DIG_CLANE_1_RW_LP_2 0xa484 --#define CORE_DIG_CLANE_1_R_LP_0 0xa4a0 --#define CORE_DIG_CLANE_1_R_LP_1 0xa4a2 --#define CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500 --#define CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502 --#define CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504 --#define CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506 --#define CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508 --#define CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a --#define CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c --#define CORE_DIG_CLANE_1_R_RX_0 0xa520 --#define CORE_DIG_CLANE_1_R_RX_1 0xa522 --#define CORE_DIG_CLANE_1_R_TX_0 0xa524 --#define CORE_DIG_CLANE_1_R_RX_2 0xa526 --#define CORE_DIG_CLANE_1_R_RX_3 0xa528 --#define CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600 --#define CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602 --#define CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604 --#define CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606 --#define CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608 --#define CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a --#define CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c --#define CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e --#define CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610 --#define CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612 --#define CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614 --#define CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616 --#define CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618 --#define CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a --#define CORE_DIG_CLANE_2_RW_CFG_0 0xa800 --#define CORE_DIG_CLANE_2_RW_CFG_2 0xa804 --#define CORE_DIG_CLANE_2_RW_LP_0 0xa880 --#define CORE_DIG_CLANE_2_RW_LP_1 0xa882 --#define CORE_DIG_CLANE_2_RW_LP_2 0xa884 --#define CORE_DIG_CLANE_2_R_LP_0 0xa8a0 --#define CORE_DIG_CLANE_2_R_LP_1 0xa8a2 --#define CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900 --#define CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902 --#define CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904 --#define CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906 --#define CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908 --#define CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a --#define CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c --#define CORE_DIG_CLANE_2_R_RX_0 0xa920 --#define CORE_DIG_CLANE_2_R_RX_1 0xa922 --#define CORE_DIG_CLANE_2_R_TX_0 0xa924 --#define CORE_DIG_CLANE_2_R_RX_2 0xa926 --#define CORE_DIG_CLANE_2_R_RX_3 0xa928 --#define CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00 --#define CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02 --#define CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04 --#define CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06 --#define CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08 --#define CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a --#define CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c --#define CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e --#define CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10 --#define CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12 --#define CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14 --#define CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16 --#define CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18 --#define CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a -- --/* dwc csi host controller registers */ --#define IS_IO_CSI2_HOST_BASE(i) (IS_IO_BASE + 0x40000 + \ -- 0x2000 * (i)) --#define VERSION 0x0 --#define N_LANES 0x4 --#define CSI2_RESETN 0x8 --#define INT_ST_MAIN 0xc --#define DATA_IDS_1 0x10 --#define DATA_IDS_2 0x14 --#define CDPHY_MODE 0x1c --#define DATA_IDS_VC_1 0x30 --#define DATA_IDS_VC_2 0x34 --#define PHY_SHUTDOWNZ 0x40 --#define DPHY_RSTZ 0x44 --#define PHY_RX 0x48 --#define PHY_STOPSTATE 0x4c --#define PHY_TEST_CTRL0 0x50 --#define PHY_TEST_CTRL1 0x54 --#define PPI_PG_PATTERN_VRES 0x60 --#define PPI_PG_PATTERN_HRES 0x64 --#define PPI_PG_CONFIG 0x68 --#define PPI_PG_ENABLE 0x6c --#define PPI_PG_STATUS 0x70 --#define VC_EXTENSION 0xc8 --#define PHY_CAL 0xcc --#define INT_ST_PHY_FATAL 0xe0 --#define INT_MSK_PHY_FATAL 0xe4 --#define INT_FORCE_PHY_FATAL 0xe8 --#define INT_ST_PKT_FATAL 0xf0 --#define INT_MSK_PKT_FATAL 0xf4 --#define INT_FORCE_PKT_FATAL 0xf8 --#define INT_ST_PHY 0x110 --#define INT_MSK_PHY 0x114 --#define INT_FORCE_PHY 0x118 --#define INT_ST_LINE 0x130 --#define INT_MSK_LINE 0x134 --#define INT_FORCE_LINE 0x138 --#define INT_ST_BNDRY_FRAME_FATAL 0x280 --#define INT_MSK_BNDRY_FRAME_FATAL 0x284 --#define INT_FORCE_BNDRY_FRAME_FATAL 0x288 --#define INT_ST_SEQ_FRAME_FATAL 0x290 --#define INT_MSK_SEQ_FRAME_FATAL 0x294 --#define INT_FORCE_SEQ_FRAME_FATAL 0x298 --#define INT_ST_CRC_FRAME_FATAL 0x2a0 --#define INT_MSK_CRC_FRAME_FATAL 0x2a4 --#define INT_FORCE_CRC_FRAME_FATAL 0x2a8 --#define INT_ST_PLD_CRC_FATAL 0x2b0 --#define INT_MSK_PLD_CRC_FATAL 0x2b4 --#define INT_FORCE_PLD_CRC_FATAL 0x2b8 --#define INT_ST_DATA_ID 0x2c0 --#define INT_MSK_DATA_ID 0x2c4 --#define INT_FORCE_DATA_ID 0x2c8 --#define INT_ST_ECC_CORRECTED 0x2d0 --#define INT_MSK_ECC_CORRECTED 0x2d4 --#define INT_FORCE_ECC_CORRECTED 0x2d8 --#define SCRAMBLING 0x300 --#define SCRAMBLING_SEED1 0x304 --#define SCRAMBLING_SEED2 0x308 --#define SCRAMBLING_SEED3 0x30c --#define SCRAMBLING_SEED4 0x310 --#define SCRAMBLING 0x300 -- --#define IS_IO_CSI2_ADPL_PORT_BASE(i) (IS_IO_BASE + 0x40800 + \ -- 0x2000 * (i)) --#define CSI2_ADPL_INPUT_MODE 0x0 --#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4 --#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8 --#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc --#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4 --#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8 --#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac --#define CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc --#define CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0 --#define CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4 --#define CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8 --#define CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc --#define CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0 -- --/* software control the legacy csi irq */ --#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40c00 + \ -- 0x2000 * (i)) --#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \ -- 0x2000 * (i)) --#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IS_IO_BASE + 0x49000) --#define IS_IO_CSI2_IRQ_CTRL_BASE (IS_IO_BASE + 0x4e100) -- --#define IRQ_CTL_EDGE 0x0 --#define IRQ_CTL_MASK 0x4 --#define IRQ_CTL_STATUS 0x8 --#define IRQ_CTL_CLEAR 0xc --#define IRQ_CTL_ENABLE 0x10 --/* FE irq for PTL */ --#define IRQ1_CTL_MASK 0x14 --#define IRQ1_CTL_STATUS 0x18 --#define IRQ1_CTL_CLEAR 0x1c --#define IRQ1_CTL_ENABLE 0x20 -- --/* software to set the clock gate to use the port or mgc */ --#define IS_IO_GPREGS_BASE (IS_IO_BASE + 0x49200) --#define SRST_PORT_ARB 0x0 --#define SRST_MGC 0x4 --#define SRST_WIDTH_CONV 0x8 --#define SRST_CSI_IRQ 0xc --#define SRST_CSI_LEGACY_IRQ 0x10 --#define CLK_EN_TXCLKESC 0x14 --#define CLK_DIV_FACTOR_IS_CLK 0x18 --#define CLK_DIV_FACTOR_APB_CLK 0x1c --#define CSI_PORT_CLK_GATE 0x20 --#define CSI_PORTAB_AGGREGATION 0x24 --#define MGC_CLK_GATE 0x2c --#define CG_CTRL_BITS 0x3c --#define SPARE_RW 0xf8 --#define SPARE_RO 0xfc -- --#define IS_IO_CSI2_MPF_PORT_BASE(i) (IS_IO_BASE + 0x53000 + \ -- 0x1000 * (i)) --#define MPF_16_IRQ_CNTRL_STATUS 0x238 --#define MPF_16_IRQ_CNTRL_CLEAR 0x23c --#define MPF_16_IRQ_CNTRL_ENABLE 0x240 -- --/* software config the phy */ --#define IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x53400) --#define IPU8_IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x40e00) --#define CSI_ADAPT_LAYER_SRST 0x0 --#define MPF_SRST_RST 0x4 --#define CSI_ERR_IRQ_CTRL_SRST 0x8 --#define CSI_SYNC_RC_SRST 0xc --#define CSI_CG_CTRL_BITS 0x10 --#define SOC_CSI2HOST_SELECT 0x14 --#define PHY_RESET 0x18 --#define PHY_SHUTDOWN 0x1c --#define PHY_MODE 0x20 --#define PHY_READY 0x24 --#define PHY_CLK_LANE_FORCE_CONTROL 0x28 --#define PHY_CLK_LANE_CONTROL 0x2c --#define PHY_CLK_LANE_STATUS 0x30 --#define PHY_LANE_RX_ESC_REQ 0x34 --#define PHY_LANE_RX_ESC_DATA 0x38 --#define PHY_LANE_TURNDISABLE 0x3c --#define PHY_LANE_DIRECTION 0x40 --#define PHY_LANE_FORCE_CONTROL 0x44 --#define PHY_LANE_CONTROL_EN 0x48 --#define PHY_LANE_CONTROL_DATAWIDTH 0x4c --#define PHY_LANE_STATUS 0x50 --#define PHY_LANE_ERR 0x54 --#define PHY_LANE_RXALP 0x58 --#define PHY_LANE_RXALP_NIBBLE 0x5c --#define PHY_PARITY_ERROR 0x60 --#define PHY_DEBUG_REGS_CLK_GATE_EN 0x64 --#define SPARE_RW 0xf8 --#define SPARE_RO 0xfc -- --/* software not touch */ --#define PORT_ARB_BASE (IS_IO_BASE + 0x4e000) --#define PORT_ARB_IRQ_CTL_STATUS 0x4 --#define PORT_ARB_IRQ_CTL_CLEAR 0x8 --#define PORT_ARB_IRQ_CTL_ENABLE 0xc -- --#define MGC_PPC 4U --#define MGC_DTYPE_RAW(i) (((i) - 8) / 2) --#define IS_IO_MGC_BASE (IS_IO_BASE + 0x48000) --#define MGC_KICK 0x0 --#define MGC_ASYNC_STOP 0x4 --#define MGC_PORT_OFFSET 0x100 --#define MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4) --#define MGC_MG_PORT(i) (IS_IO_MGC_BASE + \ -- (i) * MGC_PORT_OFFSET) --/* per mgc instance */ --#define MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28 --#define MGC_MG_MODE 0x2c --#define MGC_MG_INIT_COUNTER 0x30 --#define MGC_MG_MIPI_VC 0x34 --#define MGC_MG_MIPI_DTYPES 0x38 --#define MGC_MG_MULTI_DTYPES_MODE 0x3c --#define MGC_MG_NOF_FRAMES 0x40 --#define MGC_MG_FRAME_DIM 0x44 --#define MGC_MG_HBLANK 0x48 --#define MGC_MG_VBLANK 0x4c --#define MGC_MG_TPG_MODE 0x50 --#define MGC_MG_TPG_R0 0x54 --#define MGC_MG_TPG_G0 0x58 --#define MGC_MG_TPG_B0 0x5c --#define MGC_MG_TPG_R1 0x60 --#define MGC_MG_TPG_G1 0x64 --#define MGC_MG_TPG_B1 0x68 --#define MGC_MG_TPG_FACTORS 0x6c --#define MGC_MG_TPG_MASKS 0x70 --#define MGC_MG_TPG_XY_MASK 0x74 --#define MGC_MG_TPG_TILE_DIM 0x78 --#define MGC_MG_PRBS_LFSR_INIT_0 0x7c --#define MGC_MG_PRBS_LFSR_INIT_1 0x80 --#define MGC_MG_SYNC_STOP_POINT 0x84 --#define MGC_MG_SYNC_STOP_POINT_LOC 0x88 --#define MGC_MG_ERR_INJECT 0x8c --#define MGC_MG_ERR_LOCATION 0x90 --#define MGC_MG_DTO_SPEED_CTRL_EN 0x94 --#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98 --#define MGC_MG_HOR_LOC_STTS 0x9c --#define MGC_MG_VER_LOC_STTS 0xa0 --#define MGC_MG_FRAME_NUM_STTS 0xa4 --#define MGC_MG_BUSY_STTS 0xa8 --#define MGC_MG_STOPPED_STTS 0xac --/* tile width and height in pixels for Chess board and Color palette */ --#define MGC_TPG_TILE_WIDTH 64U --#define MGC_TPG_TILE_HEIGHT 64U -- --#define IPU_CSI_PORT_A_ADDR_OFFSET 0x0 --#define IPU_CSI_PORT_B_ADDR_OFFSET 0x0 --#define IPU_CSI_PORT_C_ADDR_OFFSET 0x0 --#define IPU_CSI_PORT_D_ADDR_OFFSET 0x0 -- --/* -- * 0 - CSI RX Port 0 interrupt; -- * 1 - MPF Port 0 interrupt; -- * 2 - CSI RX Port 1 interrupt; -- * 3 - MPF Port 1 interrupt; -- * 4 - CSI RX Port 2 interrupt; -- * 5 - MPF Port 2 interrupt; -- * 6 - CSI RX Port 3 interrupt; -- * 7 - MPF Port 3 interrupt; -- * 8 - Port ARB FIFO 0 overflow; -- * 9 - Port ARB FIFO 1 overflow; -- * 10 - Port ARB FIFO 2 overflow; -- * 11 - Port ARB FIFO 3 overflow; -- * 12 - isys_cfgnoc_err_probe_intl; -- * 13-15 - reserved -- */ --#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff -- --/* Adapter layer irq */ --#define IPU7_CSI_ADPL_IRQ_MASK 0xffff -- --/* sw irq from legacy irq control -- * legacy irq status -- * IPU7 -- * 0 - CSI Port 0 error interrupt -- * 1 - CSI Port 0 sync interrupt -- * 2 - CSI Port 1 error interrupt -- * 3 - CSI Port 1 sync interrupt -- * 4 - CSI Port 2 error interrupt -- * 5 - CSI Port 2 sync interrupt -- * 6 - CSI Port 3 error interrupt -- * 7 - CSI Port 3 sync interrupt -- * IPU7P5 -- * 0 - CSI Port 0 error interrupt -- * 1 - CSI Port 0 fs interrupt -- * 2 - CSI Port 0 fe interrupt -- * 3 - CSI Port 1 error interrupt -- * 4 - CSI Port 1 fs interrupt -- * 5 - CSI Port 1 fe interrupt -- * 6 - CSI Port 2 error interrupt -- * 7 - CSI Port 2 fs interrupt -- * 8 - CSI Port 2 fe interrupt -- */ --#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff -- --/* legacy error status per port -- * 0 - Error handler FIFO full; -- * 1 - Reserved Short Packet encoding detected; -- * 2 - Reserved Long Packet encoding detected; -- * 3 - Received packet is too short (fewer data words than specified in header); -- * 4 - Received packet is too long (more data words than specified in header); -- * 5 - Short packet discarded due to errors; -- * 6 - Long packet discarded due to errors; -- * 7 - CSI Combo Rx interrupt; -- * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0; -- */ --#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff -- --/* -- * 0 - VC0 frame start received -- * 1 - VC0 frame end received -- * 2 - VC1 frame start received -- * 3 - VC1 frame end received -- * 4 - VC2 frame start received -- * 5 - VC2 frame end received -- * 6 - VC3 frame start received -- * 7 - VC3 frame end received -- * 8 - VC4 frame start received -- * 9 - VC4 frame end received -- * 10 - VC5 frame start received -- * 11 - VC5 frame end received -- * 12 - VC6 frame start received -- * 13 - VC6 frame end received -- * 14 - VC7 frame start received -- * 15 - VC7 frame end received -- * 16 - VC8 frame start received -- * 17 - VC8 frame end received -- * 18 - VC9 frame start received -- * 19 - VC9 frame end received -- * 20 - VC10 frame start received -- * 21 - VC10 frame end received -- * 22 - VC11 frame start received -- * 23 - VC11 frame end received -- * 24 - VC12 frame start received -- * 25 - VC12 frame end received -- * 26 - VC13 frame start received -- * 27 - VC13 frame end received -- * 28 - VC14 frame start received -- * 29 - VC14 frame end received -- * 30 - VC15 frame start received -- * 31 - VC15 frame end received -- */ -- --#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0 --#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0 -- --#define CSI_RX_NUM_ERRORS_IN_IRQ 12U --#define CSI_RX_NUM_SYNC_IN_IRQ 32U -- --enum CSI_FE_MODE_TYPE { -- CSI_FE_DPHY_MODE = 0, -- CSI_FE_CPHY_MODE = 1, --}; -- --enum CSI_FE_INPUT_MODE { -- CSI_SENSOR_INPUT = 0, -- CSI_MIPIGEN_INPUT = 1, --}; -- --enum MGC_CSI_ADPL_TYPE { -- MGC_MAPPED_2_LANES = 0, -- MGC_MAPPED_4_LANES = 1, --}; -- --enum CSI2HOST_SELECTION { -- CSI2HOST_SEL_SOC = 0, -- CSI2HOST_SEL_CSI2HOST = 1, --}; -- --#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port)) --#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port)) -- --/* ---------------------------------------------------------------- */ --#define CSI_REG_BASE 0x220000 --#define CSI_REG_BASE_PORT(id) ((id) * 0x1000) -- --/* CSI Port General Purpose Registers */ --#define CSI_REG_PORT_GPREG_SRST 0x0 --#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 --#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 -- --#define CSI_RX_NUM_IRQ 32U -- --#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555 --#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa --#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff --#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff -- --#endif /* IPU7_ISYS_CSI2_REG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -deleted file mode 100644 -index 0fe4299222e8..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -+++ /dev/null -@@ -1,544 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-isys-csi-phy.h" -- --static const u32 csi2_supported_codes[] = { -- MEDIA_BUS_FMT_Y10_1X10, -- MEDIA_BUS_FMT_RGB565_1X16, -- MEDIA_BUS_FMT_RGB888_1X24, -- MEDIA_BUS_FMT_UYVY8_1X16, -- MEDIA_BUS_FMT_YUYV8_1X16, -- MEDIA_BUS_FMT_YUYV10_1X20, -- MEDIA_BUS_FMT_SBGGR10_1X10, -- MEDIA_BUS_FMT_SGBRG10_1X10, -- MEDIA_BUS_FMT_SGRBG10_1X10, -- MEDIA_BUS_FMT_SRGGB10_1X10, -- MEDIA_BUS_FMT_SBGGR12_1X12, -- MEDIA_BUS_FMT_SGBRG12_1X12, -- MEDIA_BUS_FMT_SGRBG12_1X12, -- MEDIA_BUS_FMT_SRGGB12_1X12, -- MEDIA_BUS_FMT_SBGGR8_1X8, -- MEDIA_BUS_FMT_SGBRG8_1X8, -- MEDIA_BUS_FMT_SGRBG8_1X8, -- MEDIA_BUS_FMT_SRGGB8_1X8, -- 0, --}; -- --s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) --{ -- struct media_pad *src_pad; -- -- src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -- if (IS_ERR(src_pad)) { -- dev_err(&csi2->isys->adev->auxdev.dev, -- "can't get source pad of %s (%ld)\n", -- csi2->asd.sd.name, PTR_ERR(src_pad)); -- return PTR_ERR(src_pad); -- } -- -- return v4l2_get_link_freq(src_pad, 0, 0); --} -- --static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -- struct v4l2_event_subscription *sub) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- -- dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", -- sub->type, sub->id); -- -- switch (sub->type) { -- case V4L2_EVENT_FRAME_SYNC: -- return v4l2_event_subscribe(fh, sub, 10, NULL); -- case V4L2_EVENT_CTRL: -- return v4l2_ctrl_subscribe_event(fh, sub); -- default: -- return -EINVAL; -- } --} -- --static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { -- .subscribe_event = csi2_subscribe_event, -- .unsubscribe_event = v4l2_event_subdev_unsubscribe, --}; -- --static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_device *isp = csi2->isys->adev->isp; -- unsigned int offset, mask; -- -- /* enable CSI2 legacy error irq */ -- offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(mask, csi2->base + offset + IRQ_CTL_MASK); -- writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -- -- /* enable CSI2 legacy sync irq */ -- offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(mask, csi2->base + offset + IRQ_CTL_MASK); -- writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -- -- mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK; -- if (!is_ipu7(isp->hw_ver)) { -- writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -- writel(mask, csi2->base + offset + IRQ1_CTL_MASK); -- writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE); -- } --} -- --static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_device *isp = csi2->isys->adev->isp; -- unsigned int offset, mask; -- -- /* disable CSI2 legacy error irq */ -- offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(0, csi2->base + offset + IRQ_CTL_MASK); -- writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -- -- /* disable CSI2 legacy sync irq */ -- offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(0, csi2->base + offset + IRQ_CTL_MASK); -- writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -- -- if (!is_ipu7(isp->hw_ver)) { -- writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -- writel(0, csi2->base + offset + IRQ1_CTL_MASK); -- writel(0, csi2->base + offset + IRQ1_CTL_ENABLE); -- } --} -- --static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- void __iomem *isys_base = isys->pdata->base; -- -- ipu7_isys_csi_phy_powerdown(csi2); -- -- writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); -- csi2_irq_disable(csi2); --} -- --static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- void __iomem *isys_base = isys->pdata->base; -- unsigned int port, nlanes, offset; -- int ret; -- -- port = csi2->port; -- nlanes = csi2->nlanes; -- -- offset = IS_IO_GPREGS_BASE; -- writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK); -- dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", -- port, readl(isys_base + offset + CSI_PORT_CLK_GATE), -- readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); -- if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { -- dev_dbg(dev, "CSI port %u in aggregation mode\n", port); -- writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); -- } -- -- /* input is coming from CSI receiver (sensor) */ -- offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -- writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE); -- writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); -- -- ret = ipu7_isys_csi_phy_powerup(csi2); -- if (ret) { -- dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret); -- return ret; -- } -- -- csi2_irq_enable(csi2); -- -- return 0; --} -- --static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_selection *sel) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct device *dev = &asd->isys->adev->auxdev.dev; -- struct v4l2_mbus_framefmt *sink_ffmt; -- struct v4l2_mbus_framefmt *src_ffmt; -- struct v4l2_rect *crop; -- -- if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) -- return -EINVAL; -- -- sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -- sel->pad, -- sel->stream); -- if (!sink_ffmt) -- return -EINVAL; -- -- src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); -- if (!src_ffmt) -- return -EINVAL; -- -- crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -- if (!crop) -- return -EINVAL; -- -- /* Only vertical cropping is supported */ -- sel->r.left = 0; -- sel->r.width = sink_ffmt->width; -- /* Non-bayer formats can't be single line cropped */ -- if (!ipu7_isys_is_bayer_format(sink_ffmt->code)) -- sel->r.top &= ~1U; -- sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT, -- sink_ffmt->height - sel->r.top); -- *crop = sel->r; -- -- /* update source pad format */ -- src_ffmt->width = sel->r.width; -- src_ffmt->height = sel->r.height; -- if (ipu7_isys_is_bayer_format(sink_ffmt->code)) -- src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code, -- sel->r.left, -- sel->r.top); -- dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", -- sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, -- src_ffmt->code); -- -- return 0; --} -- --static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_selection *sel) --{ -- struct v4l2_mbus_framefmt *sink_ffmt; -- struct v4l2_rect *crop; -- int ret = 0; -- -- if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) -- return -EINVAL; -- -- sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -- sel->pad, -- sel->stream); -- if (!sink_ffmt) -- return -EINVAL; -- -- crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -- if (!crop) -- return -EINVAL; -- -- switch (sel->target) { -- case V4L2_SEL_TGT_CROP_DEFAULT: -- case V4L2_SEL_TGT_CROP_BOUNDS: -- sel->r.left = 0; -- sel->r.top = 0; -- sel->r.width = sink_ffmt->width; -- sel->r.height = sink_ffmt->height; -- break; -- case V4L2_SEL_TGT_CROP: -- sel->r = *crop; -- break; -- default: -- ret = -EINVAL; -- } -- -- return ret; --} -- --/* -- * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set -- * of streams. -- */ --#define CSI2_SUBDEV_MAX_STREAM_ID 63 -- --static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- u32 pad, u64 streams_mask) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -- struct v4l2_subdev *r_sd; -- struct media_pad *rp; -- u32 sink_pad, sink_stream; -- int ret, i; -- -- if (!csi2->stream_count) { -- dev_dbg(&csi2->isys->adev->auxdev.dev, -- "stream on CSI2-%u with %u lanes\n", csi2->port, -- csi2->nlanes); -- ret = ipu7_isys_csi2_enable_stream(csi2); -- if (ret) -- return ret; -- } -- -- for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -- if (streams_mask & BIT_ULL(i)) -- break; -- } -- -- ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -- &sink_pad, &sink_stream); -- if (ret) -- return ret; -- -- rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -- r_sd = media_entity_to_v4l2_subdev(rp->entity); -- -- ret = v4l2_subdev_enable_streams(r_sd, rp->index, -- BIT_ULL(sink_stream)); -- if (!ret) { -- csi2->stream_count++; -- return 0; -- } -- -- if (!csi2->stream_count) -- ipu7_isys_csi2_disable_stream(csi2); -- -- return ret; --} -- --static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- u32 pad, u64 streams_mask) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -- struct v4l2_subdev *r_sd; -- struct media_pad *rp; -- u32 sink_pad, sink_stream; -- int ret, i; -- -- for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -- if (streams_mask & BIT_ULL(i)) -- break; -- } -- -- ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -- &sink_pad, &sink_stream); -- if (ret) -- return ret; -- -- rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -- r_sd = media_entity_to_v4l2_subdev(rp->entity); -- -- v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream)); -- -- if (--csi2->stream_count) -- return 0; -- -- dev_dbg(&csi2->isys->adev->auxdev.dev, -- "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); -- -- ipu7_isys_csi2_disable_stream(csi2); -- -- return 0; --} -- --static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { -- .get_fmt = v4l2_subdev_get_fmt, -- .set_fmt = ipu7_isys_subdev_set_fmt, -- .get_selection = ipu7_isys_csi2_get_sel, -- .set_selection = ipu7_isys_csi2_set_sel, -- .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -- .enable_streams = ipu7_isys_csi2_enable_streams, -- .disable_streams = ipu7_isys_csi2_disable_streams, -- .set_routing = ipu7_isys_subdev_set_routing, --}; -- --static const struct v4l2_subdev_ops csi2_sd_ops = { -- .core = &csi2_sd_core_ops, -- .pad = &csi2_sd_pad_ops, --}; -- --static const struct media_entity_operations csi2_entity_ops = { -- .link_validate = v4l2_subdev_link_validate, -- .has_pad_interdep = v4l2_subdev_has_pad_interdep, --}; -- --void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2) --{ -- if (!csi2->isys) -- return; -- -- v4l2_device_unregister_subdev(&csi2->asd.sd); -- v4l2_subdev_cleanup(&csi2->asd.sd); -- ipu7_isys_subdev_cleanup(&csi2->asd); -- csi2->isys = NULL; --} -- --int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, -- struct ipu7_isys *isys, -- void __iomem *base, unsigned int index) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- int ret; -- -- csi2->isys = isys; -- csi2->base = base; -- csi2->port = index; -- -- if (!is_ipu7(isys->adev->isp->hw_ver)) -- csi2->legacy_irq_mask = 0x7U << (index * 3U); -- else -- csi2->legacy_irq_mask = 0x3U << (index * 2U); -- -- dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index, -- csi2->legacy_irq_mask); -- -- csi2->asd.sd.entity.ops = &csi2_entity_ops; -- csi2->asd.isys = isys; -- -- ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, -- IPU7_NR_OF_CSI2_SINK_PADS, -- IPU7_NR_OF_CSI2_SRC_PADS); -- if (ret) -- return ret; -- -- csi2->asd.source = (int)index; -- csi2->asd.supported_codes = csi2_supported_codes; -- snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), -- IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index); -- v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); -- -- ret = v4l2_subdev_init_finalize(&csi2->asd.sd); -- if (ret) { -- dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret); -- goto isys_subdev_cleanup; -- } -- -- ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); -- if (ret) { -- dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret); -- goto v4l2_subdev_cleanup; -- } -- -- return 0; -- --v4l2_subdev_cleanup: -- v4l2_subdev_cleanup(&csi2->asd.sd); --isys_subdev_cleanup: -- ipu7_isys_subdev_cleanup(&csi2->asd); -- -- return ret; --} -- --void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct video_device *vdev = csi2->asd.sd.devnode; -- struct v4l2_event ev = { -- .type = V4L2_EVENT_FRAME_SYNC, -- }; -- -- ev.id = stream->vc; -- ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -- v4l2_event_queue(vdev, &ev); -- -- dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", -- csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); --} -- --void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- struct device *dev = &stream->isys->adev->auxdev.dev; -- u32 frame_sequence = atomic_read(&stream->sequence); -- -- dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", -- csi2->port, frame_sequence); --} -- --int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -- struct ipu7_isys_csi2 *csi2, -- struct media_entity *source_entity, -- struct v4l2_mbus_frame_desc_entry *entry, -- int *nr_queues) --{ -- struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- struct v4l2_mbus_frame_desc desc; -- struct v4l2_subdev *source; -- struct media_pad *pad; -- unsigned int i; -- int ret; -- -- source = media_entity_to_v4l2_subdev(source_entity); -- if (!source) -- return -EPIPE; -- -- pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); -- if (!pad) -- return -EPIPE; -- -- ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); -- if (ret) -- return ret; -- -- if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -- dev_err(dev, "Unsupported frame descriptor type\n"); -- return -EINVAL; -- } -- -- for (i = 0; i < desc.num_entries; i++) { -- if (source_stream == desc.entry[i].stream) { -- desc_entry = &desc.entry[i]; -- break; -- } -- } -- -- if (!desc_entry) { -- dev_err(dev, "Failed to find stream %u from remote subdev\n", -- source_stream); -- return -EINVAL; -- } -- -- if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) { -- dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); -- return -EINVAL; -- } -- -- *entry = *desc_entry; -- -- for (i = 0; i < desc.num_entries; i++) { -- if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) -- (*nr_queues)++; -- } -- -- return 0; --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -deleted file mode 100644 -index 6c23b80f92a2..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -+++ /dev/null -@@ -1,64 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_CSI2_H --#define IPU7_ISYS_CSI2_H -- --#include --#include -- --#include "ipu7-isys-subdev.h" --#include "ipu7-isys-video.h" -- --struct ipu7_isys; --struct ipu7_isys_csi2_pdata; --struct ipu7_isys_stream; -- --#define IPU7_NR_OF_CSI2_VC 16U --#define INVALID_VC_ID -1 --#define IPU7_NR_OF_CSI2_SINK_PADS 1U --#define IPU7_CSI2_PAD_SINK 0U --#define IPU7_NR_OF_CSI2_SRC_PADS 8U --#define IPU7_CSI2_PAD_SRC 1U --#define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \ -- IPU7_NR_OF_CSI2_SRC_PADS) -- --/* -- * struct ipu7_isys_csi2 -- * -- * @nlanes: number of lanes in the receiver -- */ --struct ipu7_isys_csi2 { -- struct ipu7_isys_subdev asd; -- struct ipu7_isys_csi2_pdata *pdata; -- struct ipu7_isys *isys; -- struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS]; -- -- void __iomem *base; -- u32 receiver_errors; -- u32 legacy_irq_mask; -- unsigned int nlanes; -- unsigned int port; -- unsigned int phy_mode; -- unsigned int stream_count; --}; -- --#define ipu7_isys_subdev_to_csi2(__sd) \ -- container_of(__sd, struct ipu7_isys_csi2, asd) -- --#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd) -- --s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2); --int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys, -- void __iomem *base, unsigned int index); --void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2); --void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream); --void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream); --int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -- struct ipu7_isys_csi2 *csi2, -- struct media_entity *source_entity, -- struct v4l2_mbus_frame_desc_entry *entry, -- int *nr_queues); --#endif /* IPU7_ISYS_CSI2_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -deleted file mode 100644 -index 43955c6fa8cf..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -+++ /dev/null -@@ -1,1198 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#include --#endif -- --#include --#include --#include --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-fw-isys.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-isys-video.h" --#include "ipu7-platform-regs.h" --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#include "ipu7-cpd.h" --#endif -- --#define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) -- --static int ipu7_isys_buf_init(struct vb2_buffer *vb) --{ -- struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -- struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- int ret; -- -- ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); -- if (ret) -- return ret; -- -- ivb->dma_addr = sg_dma_address(sg->sgl); -- -- return 0; --} -- --static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb) --{ -- struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -- struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- -- ivb->dma_addr = 0; -- ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); --} -- --static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, -- unsigned int *num_planes, unsigned int sizes[], -- struct device *alloc_devs[]) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- u32 size = av->pix_fmt.sizeimage; -- -- /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ -- if (!*num_planes) { -- sizes[0] = size; -- } else if (sizes[0] < size) { -- dev_dbg(dev, "%s: queue setup: size %u < %u\n", -- av->vdev.name, sizes[0], size); -- return -EINVAL; -- } -- -- *num_planes = 1; -- -- return 0; --} -- --static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -- struct device *dev = &av->isys->adev->auxdev.dev; -- u32 bytesperline = av->pix_fmt.bytesperline; -- u32 height = av->pix_fmt.height; -- -- dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", -- av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0)); -- -- if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0)) -- return -EINVAL; -- -- dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n", -- av->vdev.name, bytesperline, height); -- vb2_set_plane_payload(vb, 0, bytesperline * height); -- -- /* assume IPU is not DMA coherent */ -- ipu7_dma_sync_sgtable(isys->adev, sg); -- -- return 0; --} -- --/* -- * Queue a buffer list back to incoming or active queues. The buffers -- * are removed from the buffer list. -- */ --void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -- unsigned long op_flags, -- enum vb2_buffer_state state) --{ -- struct ipu7_isys_buffer *ib, *ib_safe; -- unsigned long flags; -- bool first = true; -- -- if (!bl) -- return; -- -- WARN_ON_ONCE(!bl->nbufs); -- WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE && -- op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING); -- -- list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { -- struct ipu7_isys_video *av; -- -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- struct ipu7_isys_queue *aq = -- vb2_queue_to_isys_queue(vb->vb2_queue); -- -- av = ipu7_isys_queue_to_video(aq); -- spin_lock_irqsave(&aq->lock, flags); -- list_del(&ib->head); -- if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) -- list_add(&ib->head, &aq->active); -- else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) -- list_add_tail(&ib->head, &aq->incoming); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE) -- vb2_buffer_done(vb, state); -- -- if (first) { -- dev_dbg(&av->isys->adev->auxdev.dev, -- "queue buf list %p flags %lx, s %d, %d bufs\n", -- bl, op_flags, state, bl->nbufs); -- first = false; -- } -- -- bl->nbufs--; -- } -- -- WARN_ON(bl->nbufs); --} -- --/* -- * flush_firmware_streamon_fail() - Flush in cases where requests may -- * have been queued to firmware and the *firmware streamon fails for a -- * reason or another. -- */ --static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_queue *aq; -- unsigned long flags; -- -- lockdep_assert_held(&stream->mutex); -- -- list_for_each_entry(aq, &stream->queues, node) { -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_buffer *ib, *ib_safe; -- -- spin_lock_irqsave(&aq->lock, flags); -- list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { -- struct vb2_buffer *vb = -- ipu7_isys_buffer_to_vb2_buffer(ib); -- -- list_del(&ib->head); -- if (av->streaming) { -- dev_dbg(dev, -- "%s: queue buffer %u back to incoming\n", -- av->vdev.name, vb->index); -- /* Queue already streaming, return to driver. */ -- list_add(&ib->head, &aq->incoming); -- continue; -- } -- /* Queue not yet streaming, return to user. */ -- dev_dbg(dev, "%s: return %u back to videobuf2\n", -- av->vdev.name, vb->index); -- vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib), -- VB2_BUF_STATE_QUEUED); -- } -- spin_unlock_irqrestore(&aq->lock, flags); -- } --} -- --/* -- * Attempt obtaining a buffer list from the incoming queues, a list of buffers -- * that contains one entry from each video buffer queue. If a buffer can't be -- * obtained from every queue, the buffers are returned back to the queue. -- */ --static int buffer_list_get(struct ipu7_isys_stream *stream, -- struct ipu7_isys_buffer_list *bl) --{ -- unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING; -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct ipu7_isys_queue *aq; -- unsigned long flags; -- -- bl->nbufs = 0; -- INIT_LIST_HEAD(&bl->head); -- -- list_for_each_entry(aq, &stream->queues, node) { -- struct ipu7_isys_buffer *ib; -- -- spin_lock_irqsave(&aq->lock, flags); -- if (list_empty(&aq->incoming)) { -- spin_unlock_irqrestore(&aq->lock, flags); -- if (!list_empty(&bl->head)) -- ipu7_isys_buffer_list_queue(bl, buf_flag, 0); -- return -ENODATA; -- } -- -- ib = list_last_entry(&aq->incoming, -- struct ipu7_isys_buffer, head); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- -- if (av->skipframe) { -- atomic_set(&ib->skipframe_flag, 1); -- av->skipframe--; -- } else { -- atomic_set(&ib->skipframe_flag, 0); -- } --#endif -- dev_dbg(dev, "buffer: %s: buffer %u\n", -- ipu7_isys_queue_to_video(aq)->vdev.name, -- ipu7_isys_buffer_to_vb2_buffer(ib)->index); -- list_del(&ib->head); -- list_add(&ib->head, &bl->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- bl->nbufs++; -- } -- -- dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); -- -- return 0; --} -- --static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, -- struct ipu7_insys_buffset *set) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- --#ifndef IPU8_INSYS_NEW_ABI -- set->output_pins[aq->fw_output].addr = ivb->dma_addr; -- set->output_pins[aq->fw_output].user_token = (uintptr_t)set; --#else -- set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; -- set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; -- set->output_pins[aq->fw_output].upipe_capture_cfg = 0; --#endif --} -- --/* -- * Convert a buffer list to a isys fw ABI framebuffer set. The -- * buffer list is not modified. -- */ --#define IPU_ISYS_FRAME_NUM_THRESHOLD (30) --void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -- struct ipu7_isys_stream *stream, -- struct ipu7_isys_buffer_list *bl) --{ -- struct ipu7_isys_buffer *ib; -- u32 buf_id; -- -- WARN_ON(!bl->nbufs); -- -- set->skip_frame = 0; -- set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP | -- IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ; -- -- buf_id = atomic_fetch_inc(&stream->buf_id); -- set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER; -- -- list_for_each_entry(ib, &bl->head, head) { -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- ipu7_isys_buf_to_fw_frame_buf_pin(vb, set); -- } --} -- --/* Start streaming for real. The buffer list must be available. */ --static int ipu7_isys_stream_start(struct ipu7_isys_video *av, -- struct ipu7_isys_buffer_list *bl, bool error) --{ -- struct ipu7_isys_stream *stream = av->stream; -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct ipu7_isys_buffer_list __bl; -- int ret; -- -- mutex_lock(&stream->isys->stream_mutex); -- -- ret = ipu7_isys_video_set_streaming(av, 1, bl); -- mutex_unlock(&stream->isys->stream_mutex); -- if (ret) -- goto out_requeue; -- -- stream->streaming = 1; -- -- bl = &__bl; -- -- do { -- struct ipu7_insys_buffset *buf = NULL; -- struct isys_fw_msgs *msg; -- enum ipu7_insys_send_type send_type = -- IPU_INSYS_SEND_TYPE_STREAM_CAPTURE; -- -- ret = buffer_list_get(stream, bl); -- if (ret < 0) -- break; -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) -- return -ENOMEM; -- -- buf = &msg->fw_msg.frame; -- -- ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -- -- ipu7_fw_isys_dump_frame_buff_set(dev, buf, -- stream->nr_output_pins); -- -- ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, -- 0); -- -- ret = ipu7_fw_isys_complex_cmd(stream->isys, -- stream->stream_handle, buf, -- msg->dma_addr, sizeof(*buf), -- send_type); -- } while (!WARN_ON(ret)); -- -- return 0; -- --out_requeue: -- if (bl && bl->nbufs) -- ipu7_isys_buffer_list_queue(bl, -- IPU_ISYS_BUFFER_LIST_FL_INCOMING | -- (error ? -- IPU_ISYS_BUFFER_LIST_FL_SET_STATE : -- 0), error ? VB2_BUF_STATE_ERROR : -- VB2_BUF_STATE_QUEUED); -- flush_firmware_streamon_fail(stream); -- -- return ret; --} -- --static void buf_queue(struct vb2_buffer *vb) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- struct media_pipeline *media_pipe = -- media_entity_pipeline(&av->vdev.entity); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_isys_buffer *ib = &ivb->ib; -- struct ipu7_insys_buffset *buf = NULL; -- struct ipu7_isys_buffer_list bl; -- struct isys_fw_msgs *msg; -- unsigned long flags; -- dma_addr_t dma; -- int ret; -- -- dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); -- -- dma = ivb->dma_addr; -- dev_dbg(dev, "iova: iova %pad\n", &dma); -- -- spin_lock_irqsave(&aq->lock, flags); -- list_add(&ib->head, &aq->incoming); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- if (!media_pipe || !vb->vb2_queue->start_streaming_called) { -- dev_dbg(dev, "media pipeline is not ready for %s\n", -- av->vdev.name); -- return; -- } -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_lock(&av->isys->reset_mutex); -- if (av->isys->state & RESET_STATE_IN_RESET) { -- dev_dbg(dev, "in reset, adding to incoming\n"); -- mutex_unlock(&av->isys->reset_mutex); -- return; -- } -- mutex_unlock(&av->isys->reset_mutex); -- -- /* ip may be cleared in ipu reset */ -- stream = av->stream; --#endif -- mutex_lock(&stream->mutex); -- -- if (stream->nr_streaming != stream->nr_queues) { -- dev_dbg(dev, "not streaming yet, adding to incoming\n"); -- goto out; -- } -- -- /* -- * We just put one buffer to the incoming list of this queue -- * (above). Let's see whether all queues in the pipeline would -- * have a buffer. -- */ -- ret = buffer_list_get(stream, &bl); -- if (ret < 0) { -- dev_dbg(dev, "No buffers available\n"); -- goto out; -- } -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) { -- ret = -ENOMEM; -- goto out; -- } -- -- buf = &msg->fw_msg.frame; -- -- ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl); -- -- ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); -- -- if (!stream->streaming) { -- ret = ipu7_isys_stream_start(av, &bl, true); -- if (ret) -- dev_err(dev, "stream start failed.\n"); -- goto out; -- } -- -- /* -- * We must queue the buffers in the buffer list to the -- * appropriate video buffer queues BEFORE passing them to the -- * firmware since we could get a buffer event back before we -- * have queued them ourselves to the active queue. -- */ -- ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -- -- ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle, -- buf, msg->dma_addr, sizeof(*buf), -- IPU_INSYS_SEND_TYPE_STREAM_CAPTURE); -- if (ret < 0) -- dev_err(dev, "send stream capture failed\n"); -- --out: -- mutex_unlock(&stream->mutex); --} -- --static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) --{ -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct media_pad *remote_pad = -- media_pad_remote_pad_first(av->vdev.entity.pads); -- struct v4l2_mbus_framefmt format; -- struct v4l2_subdev *sd; -- u32 r_stream, code; -- int ret; -- -- if (!remote_pad) -- return -ENOTCONN; -- -- sd = media_entity_to_v4l2_subdev(remote_pad->entity); -- r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index); -- -- ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, -- &format); -- if (ret) { -- dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", -- sd->entity.name, remote_pad->index, r_stream); -- return ret; -- } -- -- if (format.width != av->pix_fmt.width || -- format.height != av->pix_fmt.height) { -- dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", -- av->pix_fmt.width, av->pix_fmt.height, format.width, -- format.height); -- return -EINVAL; -- } -- -- code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -- if (format.code != code) { -- dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", -- code, format.code); -- return -EINVAL; -- } -- -- return 0; --} -- --static void return_buffers(struct ipu7_isys_queue *aq, -- enum vb2_buffer_state state) --{ --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- bool need_reset = false; -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); --#endif -- struct ipu7_isys_buffer *ib; -- struct vb2_buffer *vb; -- unsigned long flags; -- -- spin_lock_irqsave(&aq->lock, flags); -- /* -- * Something went wrong (FW crash / HW hang / not all buffers -- * returned from isys) if there are still buffers queued in active -- * queue. We have to clean up places a bit. -- */ -- while (!list_empty(&aq->active)) { -- ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -- head); -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- vb2_buffer_done(vb, state); -- -- spin_lock_irqsave(&aq->lock, flags); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- need_reset = true; --#endif -- } -- -- while (!list_empty(&aq->incoming)) { -- ib = list_last_entry(&aq->incoming, struct ipu7_isys_buffer, -- head); -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- vb2_buffer_done(vb, state); -- -- spin_lock_irqsave(&aq->lock, flags); -- } -- -- spin_unlock_irqrestore(&aq->lock, flags); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- -- if (need_reset) { -- mutex_lock(&av->isys->reset_mutex); -- av->isys->need_reset = true; -- mutex_unlock(&av->isys->reset_mutex); -- } --#endif --} -- --static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) --{ -- video_device_pipeline_stop(&av->vdev); -- ipu7_isys_put_stream(av->stream); -- av->stream = NULL; --} -- --static int start_streaming(struct vb2_queue *q, unsigned int count) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- struct ipu7_isys_buffer_list __bl, *bl = NULL; -- struct ipu7_isys_stream *stream; -- struct media_entity *source_entity = NULL; -- int nr_queues, ret; -- -- dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", -- av->vdev.name, av->pix_fmt.width, av->pix_fmt.height, -- pfmt->css_pixelformat); -- -- ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -- if (ret < 0) { -- dev_dbg(dev, "failed to setup video\n"); -- goto out_return_buffers; -- } -- -- ret = ipu7_isys_link_fmt_validate(aq); -- if (ret) { -- dev_dbg(dev, -- "%s: link format validation failed (%d)\n", -- av->vdev.name, ret); -- goto out_pipeline_stop; -- } -- -- stream = av->stream; -- mutex_lock(&stream->mutex); -- if (!stream->nr_streaming) { -- ret = ipu7_isys_video_prepare_stream(av, source_entity, -- nr_queues); -- if (ret) { -- mutex_unlock(&stream->mutex); -- goto out_pipeline_stop; -- } -- } -- -- stream->nr_streaming++; -- dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -- stream->nr_queues); -- -- list_add(&aq->node, &stream->queues); -- -- if (stream->nr_streaming != stream->nr_queues) -- goto out; -- -- bl = &__bl; -- ret = buffer_list_get(stream, bl); -- if (ret < 0) { -- dev_warn(dev, "no buffer available, DRIVER BUG?\n"); -- goto out; -- } -- -- ret = ipu7_isys_fw_open(av->isys); -- if (ret) -- goto out_stream_start; -- -- ipu7_isys_setup_hw(av->isys); -- -- ret = ipu7_isys_stream_start(av, bl, false); -- if (ret) -- goto out_isys_fw_close; -- --out: -- mutex_unlock(&stream->mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- av->start_streaming = 1; --#endif -- -- return 0; -- --out_isys_fw_close: -- ipu7_isys_fw_close(av->isys); -- --out_stream_start: -- list_del(&aq->node); -- stream->nr_streaming--; -- mutex_unlock(&stream->mutex); -- --out_pipeline_stop: -- ipu7_isys_stream_cleanup(av); -- --out_return_buffers: -- return_buffers(aq, VB2_BUF_STATE_QUEUED); -- -- return ret; --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --static void reset_stop_streaming(struct ipu7_isys_video *av) --{ -- struct ipu7_isys_queue *aq = &av->aq; -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_isys_buffer *ib; -- struct vb2_buffer *vb; -- unsigned long flags; -- -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- -- mutex_lock(&stream->mutex); -- stream->nr_streaming--; -- list_del(&aq->node); -- stream->streaming = 0; -- mutex_unlock(&stream->mutex); -- -- ipu7_isys_stream_cleanup(av); -- -- spin_lock_irqsave(&aq->lock, flags); -- while (!list_empty(&aq->active)) { -- ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -- head); -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -- -- spin_lock_irqsave(&aq->lock, flags); -- } -- spin_unlock_irqrestore(&aq->lock, flags); -- -- ipu7_isys_fw_close(av->isys); --} -- --static int reset_start_streaming(struct ipu7_isys_video *av) --{ -- struct ipu7_isys_queue *aq = &av->aq; -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_buffer_list __bl, *bl = NULL; -- struct ipu7_isys_stream *stream; -- struct media_entity *source_entity = NULL; -- int nr_queues; -- int ret; -- -- dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); -- -- av->skipframe = 1; -- -- ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -- if (ret < 0) { -- dev_dbg(dev, "failed to setup video\n"); -- goto out_return_buffers; -- } -- -- ret = ipu7_isys_link_fmt_validate(aq); -- if (ret) { -- dev_dbg(dev, -- "%s: link format validation failed (%d)\n", -- av->vdev.name, ret); -- goto out_pipeline_stop; -- } -- -- stream = av->stream; -- mutex_lock(&stream->mutex); -- if (!stream->nr_streaming) { -- ret = ipu7_isys_video_prepare_stream(av, source_entity, -- nr_queues); -- if (ret) { -- mutex_unlock(&stream->mutex); -- goto out_pipeline_stop; -- } -- } -- -- stream->nr_streaming++; -- dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -- stream->nr_queues); -- -- list_add(&aq->node, &stream->queues); -- -- if (stream->nr_streaming != stream->nr_queues) -- goto out; -- -- bl = &__bl; -- ret = buffer_list_get(stream, bl); -- /* -- * In reset start streaming and no buffer available, -- * it is considered that gstreamer has been closed, -- * and reset start is no needed, not driver bug. -- */ -- if (ret) { -- dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- -- goto out_stream_start; -- } -- -- ret = ipu7_isys_fw_open(av->isys); -- if (ret) -- goto out_stream_start; -- -- ipu7_isys_setup_hw(av->isys); -- -- ret = ipu7_isys_stream_start(av, bl, false); -- if (ret) -- goto out_isys_fw_close; -- --out: -- mutex_unlock(&stream->mutex); -- av->start_streaming = 1; -- return 0; -- --out_isys_fw_close: -- ipu7_isys_fw_close(av->isys); -- --out_stream_start: -- list_del(&aq->node); -- stream->nr_streaming--; -- mutex_unlock(&stream->mutex); -- --out_pipeline_stop: -- ipu7_isys_stream_cleanup(av); -- --out_return_buffers: -- return_buffers(aq, VB2_BUF_STATE_QUEUED); -- av->start_streaming = 0; -- dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); -- return ret; --} -- --static int ipu_isys_reset(struct ipu7_isys_video *self_av, -- struct ipu7_isys_stream *self_stream) --{ -- struct ipu7_isys *isys = self_av->isys; -- struct ipu7_bus_device *adev = isys->adev; -- struct ipu7_isys_video *av = NULL; -- struct ipu7_isys_stream *stream = NULL; -- struct device *dev = &adev->auxdev.dev; -- int i, j; -- int has_streaming = 0; -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- -- mutex_lock(&isys->reset_mutex); -- if (isys->state & RESET_STATE_IN_RESET) { -- mutex_unlock(&isys->reset_mutex); -- return 0; -- } -- isys->state |= RESET_STATE_IN_RESET; -- dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); -- -- while (isys->state & RESET_STATE_IN_STOP_STREAMING) { -- dev_dbg(dev, "isys reset: %s: wait for stop\n", -- self_av->vdev.name); -- mutex_unlock(&isys->reset_mutex); -- usleep_range(10000, 11000); -- mutex_lock(&isys->reset_mutex); -- } -- -- mutex_unlock(&isys->reset_mutex); -- -- dev_dbg(dev, "reset stop streams\n"); -- for (i = 0; i < csi2_pdata->nports; i++) { -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -- av = &isys->csi2[i].av[j]; -- if (av == self_av) -- continue; -- -- stream = av->stream; -- if (!stream || stream == self_stream) -- continue; -- -- if (!stream->streaming && !stream->nr_streaming) -- continue; -- -- av->reset = true; -- has_streaming = true; -- reset_stop_streaming(av); -- } -- } -- -- if (!has_streaming) -- goto end_of_reset; -- -- ipu7_cleanup_fw_msg_bufs(isys); -- -- dev_dbg(dev, "reset start streams\n"); -- -- for (j = 0; j < csi2_pdata->nports; j++) { -- for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { -- av = &isys->csi2[j].av[i]; -- if (!av->reset) -- continue; -- -- av->reset = false; -- reset_start_streaming(av); -- } -- } -- --end_of_reset: -- mutex_lock(&isys->reset_mutex); -- isys->state &= ~RESET_STATE_IN_RESET; -- mutex_unlock(&isys->reset_mutex); -- dev_dbg(dev, "reset done\n"); -- -- return 0; --} -- --static void stop_streaming(struct vb2_queue *q) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct ipu7_isys_stream *stream = av->stream; -- int ret = 0; -- -- struct device *dev = &av->isys->adev->auxdev.dev; -- bool need_reset; -- -- dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); -- -- mutex_lock(&av->isys->reset_mutex); -- while (av->isys->state) { -- mutex_unlock(&av->isys->reset_mutex); -- dev_dbg(dev, "stop: %s: wait for rest or stop, isys->state = %d\n", -- av->vdev.name, av->isys->state); -- usleep_range(10000, 11000); -- mutex_lock(&av->isys->reset_mutex); -- } -- -- if (!av->start_streaming) { -- mutex_unlock(&av->isys->reset_mutex); -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- return; -- } -- -- av->isys->state |= RESET_STATE_IN_STOP_STREAMING; -- mutex_unlock(&av->isys->reset_mutex); -- -- stream = av->stream; -- if (!stream) { -- dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- mutex_lock(&av->isys->reset_mutex); -- av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -- mutex_unlock(&av->isys->reset_mutex); -- return; -- } -- -- mutex_lock(&stream->mutex); -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ret = ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- if (ret) { -- dev_err(dev, "stop: video set streaming failed\n"); -- mutex_unlock(&stream->mutex); -- return; -- } -- -- stream->nr_streaming--; -- list_del(&aq->node); -- stream->streaming = 0; -- -- mutex_unlock(&stream->mutex); -- -- ipu7_isys_stream_cleanup(av); -- -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- -- ipu7_isys_fw_close(av->isys); -- -- av->start_streaming = 0; -- mutex_lock(&av->isys->reset_mutex); -- av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -- need_reset = av->isys->need_reset; -- mutex_unlock(&av->isys->reset_mutex); -- -- if (need_reset) { -- if (!stream->nr_streaming) { -- ipu_isys_reset(av, stream); -- } else { -- mutex_lock(&av->isys->reset_mutex); -- av->isys->need_reset = false; -- mutex_unlock(&av->isys->reset_mutex); -- } -- } -- -- dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); --} --#else --static void stop_streaming(struct vb2_queue *q) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct ipu7_isys_stream *stream = av->stream; -- int ret = 0; -- -- mutex_lock(&stream->mutex); -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ret = ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- if (ret) { -- dev_err(&av->isys->adev->auxdev.dev, -- "stop: video set streaming failed\n"); -- mutex_unlock(&stream->mutex); -- return; -- } -- -- stream->nr_streaming--; -- list_del(&aq->node); -- stream->streaming = 0; -- -- mutex_unlock(&stream->mutex); -- -- ipu7_isys_stream_cleanup(av); -- -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- -- ipu7_isys_fw_close(av->isys); --} --#endif -- --static unsigned int --get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) --{ -- struct ipu7_isys *isys = stream->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- unsigned int i; -- -- /* -- * The timestamp is invalid as no TSC in some FPGA platform, -- * so get the sequence from pipeline directly in this case. -- */ -- if (time == 0) -- return atomic_read(&stream->sequence) - 1; -- -- for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -- if (time == stream->seq[i].timestamp) { -- dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n", -- stream->seq[i].sequence, time); -- return stream->seq[i].sequence; -- } -- -- dev_dbg(dev, "SOF: looking for %llu\n", time); -- for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -- dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n", -- stream->seq[i].sequence, stream->seq[i].timestamp); -- dev_dbg(dev, "SOF sequence number not found\n"); -- -- return atomic_read(&stream->sequence) - 1; --} -- --static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time) --{ -- struct ipu7_bus_device *adev = av->isys->adev; -- struct ipu7_device *isp = adev->isp; -- u64 delta, tsc_now; -- -- ipu_buttress_tsc_read(isp, &tsc_now); -- if (!tsc_now) -- return 0; -- -- delta = tsc_now - time; -- -- return ipu_buttress_tsc_ticks_to_ns(delta, isp); --} -- --static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib, -- u64 time) --{ -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- u64 ns; -- u32 sequence; -- -- ns = ktime_get_ns() - get_sof_ns_delta(av, time); -- sequence = get_sof_sequence_by_timestamp(stream, time); -- -- vbuf->vb2_buf.timestamp = ns; -- vbuf->sequence = sequence; -- -- dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", -- av->vdev.name, ktime_get_ns(), sequence); -- dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, -- vbuf->vb2_buf.timestamp); --} -- --static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) --{ -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- if (atomic_read(&ib->str2mmio_flag)) { -- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -- /* -- * Operation on buffer is ended with error and will be reported -- * to the userspace when it is de-queued -- */ -- atomic_set(&ib->str2mmio_flag, 0); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- } else if (atomic_read(&ib->skipframe_flag)) { -- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -- atomic_set(&ib->skipframe_flag, 0); --#endif -- } else { -- vb2_buffer_done(vb, VB2_BUF_STATE_DONE); -- } --} -- --void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -- struct ipu7_insys_resp *info) --{ -- struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq; -- u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]); -- struct ipu7_isys *isys = stream->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- struct ipu7_isys_buffer *ib; -- struct vb2_buffer *vb; -- unsigned long flags; -- bool first = true; -- struct vb2_v4l2_buffer *buf; -- -- dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n", -- ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr, -- info->frame_id); -- -- spin_lock_irqsave(&aq->lock, flags); -- if (list_empty(&aq->active)) { -- spin_unlock_irqrestore(&aq->lock, flags); -- dev_err(dev, "active queue empty\n"); -- return; -- } -- -- list_for_each_entry_reverse(ib, &aq->active, head) { -- struct ipu7_isys_video_buffer *ivb; -- struct vb2_v4l2_buffer *vvb; -- dma_addr_t addr; -- -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- vvb = to_vb2_v4l2_buffer(vb); -- ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- addr = ivb->dma_addr; -- -- if (info->pin.addr != addr) { -- if (first) -- dev_err(dev, "Unexpected buffer address %pad\n", -- &addr); -- -- first = false; -- continue; -- } -- -- dev_dbg(dev, "buffer: found buffer %pad\n", &addr); -- -- buf = to_vb2_v4l2_buffer(vb); -- buf->field = V4L2_FIELD_NONE; -- -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- ipu7_isys_buf_calc_sequence_time(ib, time); -- -- ipu7_isys_queue_buf_done(ib); -- -- return; -- } -- -- dev_err(dev, "Failed to find a matching video buffer\n"); -- -- spin_unlock_irqrestore(&aq->lock, flags); --} -- --static const struct vb2_ops ipu7_isys_queue_ops = { -- .queue_setup = ipu7_isys_queue_setup, -- .buf_init = ipu7_isys_buf_init, -- .buf_prepare = ipu7_isys_buf_prepare, -- .buf_cleanup = ipu7_isys_buf_cleanup, -- .start_streaming = start_streaming, -- .stop_streaming = stop_streaming, -- .buf_queue = buf_queue, --}; -- --int ipu7_isys_queue_init(struct ipu7_isys_queue *aq) --{ -- struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys; -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct ipu7_bus_device *adev = isys->adev; -- int ret; -- -- if (!aq->vbq.io_modes) -- aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; -- -- aq->vbq.drv_priv = isys; -- aq->vbq.ops = &ipu7_isys_queue_ops; -- aq->vbq.lock = &av->mutex; -- aq->vbq.mem_ops = &vb2_dma_sg_memops; -- aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -- aq->vbq.min_queued_buffers = 1; -- aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -- -- ret = vb2_queue_init(&aq->vbq); -- if (ret) -- return ret; -- -- aq->dev = &adev->auxdev.dev; -- aq->vbq.dev = &adev->isp->pdev->dev; -- spin_lock_init(&aq->lock); -- INIT_LIST_HEAD(&aq->active); -- INIT_LIST_HEAD(&aq->incoming); -- -- return 0; --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -deleted file mode 100644 -index 5a909c3a78d2..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -+++ /dev/null -@@ -1,75 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_QUEUE_H --#define IPU7_ISYS_QUEUE_H -- --#include --#include --#include --#include -- --#include -- --struct device; --struct ipu7_isys_stream; --struct ipu7_insys_resp; --struct ipu7_insys_buffset; -- --struct ipu7_isys_queue { -- struct vb2_queue vbq; -- struct list_head node; -- struct device *dev; -- spinlock_t lock; -- struct list_head active; -- struct list_head incoming; -- unsigned int fw_output; --}; -- --struct ipu7_isys_buffer { -- struct list_head head; -- atomic_t str2mmio_flag; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- atomic_t skipframe_flag; --#endif --}; -- --struct ipu7_isys_video_buffer { -- struct vb2_v4l2_buffer vb_v4l2; -- struct ipu7_isys_buffer ib; -- dma_addr_t dma_addr; --}; -- --#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) --#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) --#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) -- --struct ipu7_isys_buffer_list { -- struct list_head head; -- unsigned int nbufs; --}; -- --#define vb2_queue_to_isys_queue(__vb2) \ -- container_of(__vb2, struct ipu7_isys_queue, vbq) -- --#define ipu7_isys_to_isys_video_buffer(__ib) \ -- container_of(__ib, struct ipu7_isys_video_buffer, ib) -- --#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb) \ -- container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2) -- --#define ipu7_isys_buffer_to_vb2_buffer(__ib) \ -- (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) -- --void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -- unsigned long op_flags, -- enum vb2_buffer_state state); --void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -- struct ipu7_isys_stream *stream, -- struct ipu7_isys_buffer_list *bl); --void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -- struct ipu7_insys_resp *info); --int ipu7_isys_queue_init(struct ipu7_isys_queue *aq); --#endif /* IPU7_ISYS_QUEUE_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -deleted file mode 100644 -index 98b6ef6a2f21..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -+++ /dev/null -@@ -1,348 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include -- --#include --#include --#include --#include -- --#include -- --#include "ipu7-bus.h" --#include "ipu7-isys.h" --#include "ipu7-isys-subdev.h" -- --unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) --{ -- switch (code) { -- case MEDIA_BUS_FMT_RGB565_1X16: -- return MIPI_CSI2_DT_RGB565; -- case MEDIA_BUS_FMT_RGB888_1X24: -- return MIPI_CSI2_DT_RGB888; -- case MEDIA_BUS_FMT_YUYV10_1X20: -- return MIPI_CSI2_DT_YUV422_10B; -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- return MIPI_CSI2_DT_YUV422_8B; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- return MIPI_CSI2_DT_RAW12; -- case MEDIA_BUS_FMT_Y10_1X10: -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- return MIPI_CSI2_DT_RAW10; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- return MIPI_CSI2_DT_RAW8; -- default: -- WARN_ON(1); -- return 0xff; -- } --} -- --bool ipu7_isys_is_bayer_format(u32 code) --{ -- switch (ipu7_isys_mbus_code_to_mipi(code)) { -- case MIPI_CSI2_DT_RAW8: -- case MIPI_CSI2_DT_RAW10: -- case MIPI_CSI2_DT_RAW12: -- case MIPI_CSI2_DT_RAW14: -- case MIPI_CSI2_DT_RAW16: -- case MIPI_CSI2_DT_RAW20: -- case MIPI_CSI2_DT_RAW24: -- case MIPI_CSI2_DT_RAW28: -- return true; -- default: -- return false; -- } --} -- --u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y) --{ -- static const u32 code_map[] = { -- MEDIA_BUS_FMT_SRGGB8_1X8, -- MEDIA_BUS_FMT_SGRBG8_1X8, -- MEDIA_BUS_FMT_SGBRG8_1X8, -- MEDIA_BUS_FMT_SBGGR8_1X8, -- MEDIA_BUS_FMT_SRGGB10_1X10, -- MEDIA_BUS_FMT_SGRBG10_1X10, -- MEDIA_BUS_FMT_SGBRG10_1X10, -- MEDIA_BUS_FMT_SBGGR10_1X10, -- MEDIA_BUS_FMT_SRGGB12_1X12, -- MEDIA_BUS_FMT_SGRBG12_1X12, -- MEDIA_BUS_FMT_SGBRG12_1X12, -- MEDIA_BUS_FMT_SBGGR12_1X12, -- }; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(code_map); i++) -- if (code_map[i] == code) -- break; -- -- if (WARN_ON(i == ARRAY_SIZE(code_map))) -- return code; -- -- return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))]; --} -- --int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_format *format) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- u32 code = asd->supported_codes[0]; -- struct v4l2_mbus_framefmt *fmt; -- u32 other_pad, other_stream; -- struct v4l2_rect *crop; -- unsigned int i; -- int ret; -- -- /* No transcoding, source and sink formats must match. */ -- if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && -- sd->entity.num_pads > 1) -- return v4l2_subdev_get_fmt(sd, state, format); -- -- format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH, -- IPU_ISYS_MAX_WIDTH); -- format->format.height = clamp(format->format.height, -- IPU_ISYS_MIN_HEIGHT, -- IPU_ISYS_MAX_HEIGHT); -- -- for (i = 0; asd->supported_codes[i]; i++) { -- if (asd->supported_codes[i] == format->format.code) { -- code = asd->supported_codes[i]; -- break; -- } -- } -- format->format.code = code; -- format->format.field = V4L2_FIELD_NONE; -- -- /* Store the format and propagate it to the source pad. */ -- fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); -- if (!fmt) -- return -EINVAL; -- -- *fmt = format->format; -- -- if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) -- return 0; -- -- /* propagate format to following source pad */ -- fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, -- format->stream); -- if (!fmt) -- return -EINVAL; -- -- *fmt = format->format; -- -- ret = v4l2_subdev_routing_find_opposite_end(&state->routing, -- format->pad, -- format->stream, -- &other_pad, -- &other_stream); -- if (ret) -- return -EINVAL; -- -- crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); -- /* reset crop */ -- crop->left = 0; -- crop->top = 0; -- crop->width = fmt->width; -- crop->height = fmt->height; -- -- return 0; --} -- --int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_mbus_code_enum *code) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- const u32 *supported_codes = asd->supported_codes; -- u32 index; -- -- for (index = 0; supported_codes[index]; index++) { -- if (index == code->index) { -- code->code = supported_codes[index]; -- return 0; -- } -- } -- -- return -EINVAL; --} -- --static int subdev_set_routing(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_krouting *routing) --{ -- static const struct v4l2_mbus_framefmt fmt = { -- .width = 4096, -- .height = 3072, -- .code = MEDIA_BUS_FMT_SGRBG10_1X10, -- .field = V4L2_FIELD_NONE, -- }; -- int ret; -- -- ret = v4l2_subdev_routing_validate(sd, routing, -- V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); -- if (ret) -- return ret; -- -- return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); --} -- --int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -- struct v4l2_mbus_framefmt *format) --{ -- struct v4l2_subdev_state *state; -- struct v4l2_mbus_framefmt *fmt; -- -- if (!sd || !format) -- return -EINVAL; -- -- state = v4l2_subdev_lock_and_get_active_state(sd); -- fmt = v4l2_subdev_state_get_format(state, pad, stream); -- if (fmt) -- *format = *fmt; -- v4l2_subdev_unlock_state(state); -- -- return fmt ? 0 : -EINVAL; --} -- --u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) --{ -- struct v4l2_subdev_state *state; -- struct v4l2_subdev_route *routes; -- u32 source_stream = 0; -- unsigned int i; -- -- state = v4l2_subdev_lock_and_get_active_state(sd); -- if (!state) -- return 0; -- -- routes = state->routing.routes; -- for (i = 0; i < state->routing.num_routes; i++) { -- if (routes[i].source_pad == pad) { -- source_stream = routes[i].source_stream; -- break; -- } -- } -- -- v4l2_subdev_unlock_state(state); -- -- return source_stream; --} -- --static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state) --{ -- struct v4l2_subdev_route route = { -- .sink_pad = 0, -- .sink_stream = 0, -- .source_pad = 1, -- .source_stream = 0, -- .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -- }; -- struct v4l2_subdev_krouting routing = { -- .num_routes = 1, -- .routes = &route, -- }; -- -- return subdev_set_routing(sd, state, &routing); --} -- --int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- enum v4l2_subdev_format_whence which, -- struct v4l2_subdev_krouting *routing) --{ -- return subdev_set_routing(sd, state, routing); --} -- --static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { -- .init_state = ipu7_isys_subdev_init_state, --}; -- --int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -- const struct v4l2_subdev_ops *ops, -- unsigned int nr_ctrls, -- unsigned int num_sink_pads, -- unsigned int num_source_pads) --{ -- unsigned int num_pads = num_sink_pads + num_source_pads; -- unsigned int i; -- int ret; -- -- v4l2_subdev_init(&asd->sd, ops); -- -- asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -- V4L2_SUBDEV_FL_HAS_EVENTS | -- V4L2_SUBDEV_FL_STREAMS; -- asd->sd.owner = THIS_MODULE; -- asd->sd.dev = &asd->isys->adev->auxdev.dev; -- asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -- asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; -- -- asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, -- sizeof(*asd->pad), GFP_KERNEL); -- if (!asd->pad) -- return -ENOMEM; -- -- for (i = 0; i < num_sink_pads; i++) -- asd->pad[i].flags = MEDIA_PAD_FL_SINK | -- MEDIA_PAD_FL_MUST_CONNECT; -- -- for (i = num_sink_pads; i < num_pads; i++) -- asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; -- -- ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); -- if (ret) { -- pr_err("isys subdev init failed %d.\n", ret); -- return ret; -- } -- -- if (asd->ctrl_init) { -- ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); -- if (ret) -- goto out_media_entity_cleanup; -- -- asd->ctrl_init(&asd->sd); -- if (asd->ctrl_handler.error) { -- ret = asd->ctrl_handler.error; -- goto out_v4l2_ctrl_handler_free; -- } -- -- asd->sd.ctrl_handler = &asd->ctrl_handler; -- } -- -- asd->source = -1; -- -- return 0; -- --out_v4l2_ctrl_handler_free: -- v4l2_ctrl_handler_free(&asd->ctrl_handler); -- --out_media_entity_cleanup: -- media_entity_cleanup(&asd->sd.entity); -- -- return ret; --} -- --void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) --{ -- media_entity_cleanup(&asd->sd.entity); -- v4l2_ctrl_handler_free(&asd->ctrl_handler); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -deleted file mode 100644 -index aeefbb515807..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -+++ /dev/null -@@ -1,56 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_SUBDEV_H --#define IPU7_ISYS_SUBDEV_H -- --#include -- --#include --#include --#include -- --struct ipu7_isys; -- --struct ipu7_isys_subdev { -- struct v4l2_subdev sd; -- struct ipu7_isys *isys; -- u32 const *supported_codes; -- struct media_pad *pad; -- struct v4l2_ctrl_handler ctrl_handler; -- void (*ctrl_init)(struct v4l2_subdev *sd); -- int source; /* SSI stream source; -1 if unset */ --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- bool is_tpg; --#endif --}; -- --#define to_ipu7_isys_subdev(__sd) \ -- container_of(__sd, struct ipu7_isys_subdev, sd) --unsigned int ipu7_isys_mbus_code_to_mipi(u32 code); --bool ipu7_isys_is_bayer_format(u32 code); --u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y); -- --int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_format *format); --int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_mbus_code_enum -- *code); --u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); --int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -- struct v4l2_mbus_framefmt *format); --int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- enum v4l2_subdev_format_whence which, -- struct v4l2_subdev_krouting *routing); --int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -- const struct v4l2_subdev_ops *ops, -- unsigned int nr_ctrls, -- unsigned int num_sink_pads, -- unsigned int num_source_pads); --void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd); --#endif /* IPU7_ISYS_SUBDEV_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -deleted file mode 100644 -index 35b6298e4f8c..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -+++ /dev/null -@@ -1,693 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include -- --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-isys.h" --#include "ipu7-isys-subdev.h" --#include "ipu7-isys-tpg.h" --#include "ipu7-isys-video.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-platform-regs.h" -- --static const u32 tpg_supported_codes[] = { -- MEDIA_BUS_FMT_SBGGR8_1X8, -- MEDIA_BUS_FMT_SGBRG8_1X8, -- MEDIA_BUS_FMT_SGRBG8_1X8, -- MEDIA_BUS_FMT_SRGGB8_1X8, -- MEDIA_BUS_FMT_SBGGR10_1X10, -- MEDIA_BUS_FMT_SGBRG10_1X10, -- MEDIA_BUS_FMT_SGRBG10_1X10, -- MEDIA_BUS_FMT_SRGGB10_1X10, -- MEDIA_BUS_FMT_SBGGR12_1X12, -- MEDIA_BUS_FMT_SGBRG12_1X12, -- MEDIA_BUS_FMT_SGRBG12_1X12, -- MEDIA_BUS_FMT_SRGGB12_1X12, -- 0, --}; -- --#define IPU_ISYS_FREQ 533000000UL -- --static u32 isys_mbus_code_to_bpp(u32 code) --{ -- switch (code) { -- case MEDIA_BUS_FMT_RGB888_1X24: -- return 24; -- case MEDIA_BUS_FMT_YUYV10_1X20: -- return 20; -- case MEDIA_BUS_FMT_Y10_1X10: -- case MEDIA_BUS_FMT_RGB565_1X16: -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- return 16; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- return 12; -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- return 10; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- return 8; -- default: -- WARN_ON(1); -- return 0; -- } --} -- --static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { -- .s_stream = tpg_set_stream, --}; -- --static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) --{ -- struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, -- struct -- ipu7_isys_subdev, -- ctrl_handler), -- struct ipu7_isys_tpg, asd); -- switch (ctrl->id) { -- case V4L2_CID_HBLANK: -- writel(ctrl->val, tpg->base + MGC_MG_HBLANK); -- break; -- case V4L2_CID_VBLANK: -- writel(ctrl->val, tpg->base + MGC_MG_VBLANK); -- break; -- case V4L2_CID_TEST_PATTERN: -- writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); -- break; -- } -- -- return 0; --} -- --static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { -- .s_ctrl = ipu7_isys_tpg_s_ctrl, --}; -- --static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) --{ -- return MGC_PPC * IPU_ISYS_FREQ / bpp; --} -- --static const char *const tpg_mode_items[] = { -- "Ramp", -- "Checkerboard", -- "Monochrome per frame", -- "Color palette", --}; -- --static struct v4l2_ctrl_config tpg_mode = { -- .ops = &ipu7_isys_tpg_ctrl_ops, -- .id = V4L2_CID_TEST_PATTERN, -- .name = "Test Pattern", -- .type = V4L2_CTRL_TYPE_MENU, -- .min = TPG_MODE_RAMP, -- .max = ARRAY_SIZE(tpg_mode_items) - 1, -- .def = TPG_MODE_COLOR_PALETTE, -- .menu_skip_mask = 0x2, -- .qmenu = tpg_mode_items, --}; -- --static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) --{ -- struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -- int hblank; -- u64 default_pixel_rate; -- -- hblank = 1024; -- -- tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -- &ipu7_isys_tpg_ctrl_ops, -- V4L2_CID_HBLANK, 8, 65535, 1, hblank); -- -- tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -- &ipu7_isys_tpg_ctrl_ops, -- V4L2_CID_VBLANK, 8, 65535, 1, 1024); -- -- default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); -- tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -- &ipu7_isys_tpg_ctrl_ops, -- V4L2_CID_PIXEL_RATE, -- default_pixel_rate, -- default_pixel_rate, -- 1, default_pixel_rate); -- if (tpg->pixel_rate) { -- tpg->pixel_rate->cur.val = default_pixel_rate; -- tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; -- } -- -- v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); --} -- --static int tpg_sd_init_cfg(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state) --{ -- struct v4l2_subdev_route routes[] = { -- { -- .source_pad = 0, -- .source_stream = 0, -- .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -- } -- }; -- -- struct v4l2_subdev_krouting routing = { -- .num_routes = 1, -- .routes = routes, -- }; -- -- static const struct v4l2_mbus_framefmt format = { -- .width = 1920, -- .height = 1080, -- .code = MEDIA_BUS_FMT_SBGGR10_1X10, -- .field = V4L2_FIELD_NONE, -- }; -- -- return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); --} -- --static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { -- .init_state = tpg_sd_init_cfg, --}; -- --static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { -- .get_fmt = v4l2_subdev_get_fmt, -- .set_fmt = ipu7_isys_subdev_set_fmt, -- .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, --}; -- --static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -- struct v4l2_event_subscription *sub) --{ -- switch (sub->type) { -- case V4L2_EVENT_FRAME_SYNC: -- return v4l2_event_subscribe(fh, sub, 10, NULL); -- case V4L2_EVENT_CTRL: -- return v4l2_ctrl_subscribe_event(fh, sub); -- default: -- return -EINVAL; -- } --}; -- --/* V4L2 subdev core operations */ --static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { -- .subscribe_event = subscribe_event, -- .unsubscribe_event = v4l2_event_subdev_unsubscribe, --}; -- --static const struct v4l2_subdev_ops tpg_sd_ops = { -- .core = &tpg_sd_core_ops, -- .video = &tpg_sd_video_ops, -- .pad = &tpg_sd_pad_ops, --}; -- --static struct media_entity_operations tpg_entity_ops = { -- .link_validate = v4l2_subdev_link_validate, --}; -- --void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -- struct video_device *vdev = tpg->asd.sd.devnode; -- struct v4l2_event ev = { -- .type = V4L2_EVENT_FRAME_SYNC, -- }; -- -- ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -- -- v4l2_event_queue(vdev, &ev); -- -- dev_dbg(&stream->isys->adev->auxdev.dev, -- "sof_event::tpg-%i sequence: %i\n", tpg->index, -- ev.u.frame_sync.frame_sequence); --} -- --void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -- u32 frame_sequence = atomic_read(&stream->sequence); -- -- dev_dbg(&stream->isys->adev->auxdev.dev, -- "eof_event::tpg-%i sequence: %i\n", -- tpg->index, frame_sequence); --} -- --#define DEFAULT_VC_ID 0 --static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) --{ -- return false; --} -- --static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, -- void __iomem *mg_base) --{ -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- -- dev_dbg(dev, "---------MGC REG DUMP START----------"); -- -- dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", -- MGC_MG_CSI_ADAPT_LAYER_TYPE, -- readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); -- dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", -- MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); -- dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", -- MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); -- dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", -- MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); -- dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", -- MGC_MG_MULTI_DTYPES_MODE, -- readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); -- dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", -- MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); -- dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", -- MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); -- dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", -- MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); -- dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", -- MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); -- dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", -- MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); -- dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", -- readl(mg_base + MGC_MG_TPG_R0), -- readl(mg_base + MGC_MG_TPG_G0), -- readl(mg_base + MGC_MG_TPG_B0)); -- dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", -- readl(mg_base + MGC_MG_TPG_R1), -- readl(mg_base + MGC_MG_TPG_G1), -- readl(mg_base + MGC_MG_TPG_B1)); -- dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", -- MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); -- dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", -- MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); -- dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", -- MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); -- dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", -- MGC_MG_DTO_SPEED_CTRL_EN, -- readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); -- dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", -- MGC_MG_DTO_SPEED_CTRL_INCR_VAL, -- readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); -- dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", -- MGC_MG_FRAME_NUM_STTS, -- readl(mg_base + MGC_MG_FRAME_NUM_STTS)); -- -- dev_dbg(dev, "---------MGC REG DUMP END----------"); --} -- --#define TPG_STOP_TIMEOUT 500000 --static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) --{ -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- int ret; -- unsigned int port; -- u32 status; -- void __iomem *reg; -- void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -- void __iomem *mg_base = tpg->base; -- -- port = 1 << tpg->index; -- -- dev_dbg(dev, "MG%d generated %u frames", tpg->index, -- readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); -- writel(port, mgc_base + MGC_ASYNC_STOP); -- -- dev_dbg(dev, "wait for MG%d stop", tpg->index); -- -- reg = mg_base + MGC_MG_STOPPED_STTS; -- ret = readl_poll_timeout(reg, status, status & 0x1, 200, -- TPG_STOP_TIMEOUT); -- if (ret < 0) { -- dev_err(dev, "mgc stop timeout"); -- return ret; -- } -- -- dev_dbg(dev, "MG%d STOPPED", tpg->index); -- -- return 0; --} -- --#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) --#define TPG_FRAME_RATE 30 --#define TPG_BLANK_RATIO (4 / 3) --static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, -- u32 *hblank_cycles, u32 *vblank_cycles) --{ -- struct v4l2_mbus_framefmt format; -- u32 width, height; -- u32 code; -- u32 bpp; -- u32 bits_per_line; -- u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; -- u64 vblank_us, hblank_us; -- u32 ref_clk; -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- u32 dto_incr_val = 0x100; -- int ret; -- -- ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -- 0, 0, &format); -- if (ret) -- return; -- -- width = format.width; -- height = format.height; -- code = format.code; -- -- bpp = isys_mbus_code_to_bpp(code); -- if (!bpp) -- return; -- -- dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); -- bits_per_line = width * bpp * TPG_BLANK_RATIO; -- -- cycles = div_u64(bits_per_line, 64); -- dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, -- bits_per_line, cycles); -- -- do { -- dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, -- dto_incr_val); -- rate = div_u64(1 << 16, dto_incr_val); -- ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); -- dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, -- ns_per_cycle); -- -- line_time_ns = cycles * ns_per_cycle; -- frame_time_us = line_time_ns * height / 1000; -- dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", -- tpg->index, line_time_ns, frame_time_us); -- -- if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) -- break; -- -- /* dto incr val step 0x100 */ -- dto_incr_val += 0x100; -- } while (dto_incr_val < (1 << 16)); -- -- if (dto_incr_val >= (1 << 16)) { -- dev_warn(dev, "No DTO_INCR_VAL found\n"); -- hblank_us = 10; /* 10us */ -- vblank_us = 10000; /* 10ms */ -- dto_incr_val = 0x1000; -- } else { -- hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; -- vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; -- } -- -- dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", -- hblank_us, vblank_us, dto_incr_val); -- -- ref_clk = tpg->isys->adev->isp->buttress.ref_clk; -- -- *dto = dto_incr_val; -- *hblank_cycles = hblank_us * ref_clk / 10; -- *vblank_cycles = vblank_us * ref_clk / 10; -- dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", -- *hblank_cycles, *vblank_cycles); --} -- --static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) --{ -- struct v4l2_mbus_framefmt format; -- u32 port_map; -- u32 csi_port; -- u32 code, bpp; -- u32 width, height; -- u32 dto, hblank, vblank; -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -- void __iomem *mg_base = tpg->base; -- int ret; -- -- ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -- 0, 0, &format); -- if (ret) -- return ret; -- -- width = format.width; -- height = format.height; -- code = format.code; -- dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", -- tpg->index, code, width, height); -- bpp = isys_mbus_code_to_bpp(code); -- if (!bpp) -- return -EINVAL; -- -- csi_port = tpg->index; -- if (csi_port >= 4) -- dev_err(dev, "invalid tpg index %u\n", tpg->index); -- -- dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", -- DEFAULT_VC_ID, csi_port); -- -- /* config port map -- * TODO: add VC support and TPG with multiple -- * source pads. Currently, for simplicity, only map 1 mg to 1 csi port -- */ -- port_map = 1 << tpg->index; -- writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); -- -- /* configure adapt layer type */ -- writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); -- -- /* configure MGC mode -- * 0 - Disable MGC -- * 1 - Enable PRBS -- * 2 - Enable TPG -- * 3 - Reserved [Write phase: SW/FW debug] -- */ -- writel(2, mg_base + MGC_MG_MODE); -- -- /* config mg init counter */ -- writel(0, mg_base + MGC_MG_INIT_COUNTER); -- -- /* -- * configure virtual channel -- * TODO: VC support if need -- * currently each MGC just uses 1 virtual channel -- */ -- writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); -- -- /* -- * configure data type and multi dtypes mode -- * TODO: it needs to add the metedata flow. -- */ -- if (is_metadata_enabled(tpg)) { -- writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -- mg_base + MGC_MG_MIPI_DTYPES); -- writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); -- } else { -- writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -- mg_base + MGC_MG_MIPI_DTYPES); -- writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); -- } -- -- /* -- * configure frame information -- */ -- writel(0, mg_base + MGC_MG_NOF_FRAMES); -- writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); -- -- tpg_get_timing(tpg, &dto, &hblank, &vblank); -- writel(hblank, mg_base + MGC_MG_HBLANK); -- writel(vblank, mg_base + MGC_MG_VBLANK); -- -- /* -- * configure tpg mode, colors, mask, tile dimension -- * Mode was set by user configuration -- * 0 - Ramp mode -- * 1 - Checkerboard -- * 2 - Monochrome per frame -- * 3 - Color palette -- */ -- writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); -- -- /* red and green for checkerboard, n/a for other modes */ -- writel(58, mg_base + MGC_MG_TPG_R0); -- writel(122, mg_base + MGC_MG_TPG_G0); -- writel(46, mg_base + MGC_MG_TPG_B0); -- writel(123, mg_base + MGC_MG_TPG_R1); -- writel(85, mg_base + MGC_MG_TPG_G1); -- writel(67, mg_base + MGC_MG_TPG_B1); -- -- writel(0x0, mg_base + MGC_MG_TPG_FACTORS); -- -- /* hor_mask [15:0] ver_mask [31:16] */ -- writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); -- /* xy_mask [11:0] */ -- writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); -- writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), -- mg_base + MGC_MG_TPG_TILE_DIM); -- -- writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); -- writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); -- -- /* disable err_injection */ -- writel(0, mg_base + MGC_MG_ERR_INJECT); -- writel(0, mg_base + MGC_MG_ERR_LOCATION); -- -- ipu7_mipigen_regdump(tpg, mg_base); -- -- dev_dbg(dev, "starting MG%d streaming...\n", csi_port); -- -- /* kick and start */ -- writel(port_map, mgc_base + MGC_KICK); -- -- return 0; --} -- --static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) --{ -- struct ipu7_isys_csi2 *csi2; -- u32 offset; -- struct ipu7_isys *isys = tpg->isys; -- -- csi2 = &isys->csi2[tpg->index]; -- offset = IS_IO_GPREGS_BASE; -- -- /* MGC is in use by SW or not */ -- if (enable) -- writel(1, csi2->base + offset + MGC_CLK_GATE); -- else -- writel(0, csi2->base + offset + MGC_CLK_GATE); --} -- --static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) --{ -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- struct ipu7_isys *isys = tpg->isys; -- struct ipu7_isys_csi2 *csi2; -- u32 port, offset, val; -- void __iomem *isys_base = isys->pdata->base; -- -- port = tpg->index; -- csi2 = &isys->csi2[port]; -- -- offset = IS_IO_GPREGS_BASE; -- val = readl(isys_base + offset + CSI_PORT_CLK_GATE); -- dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); -- -- if (!enable) { -- writel(~(1 << port) & val, -- isys_base + offset + CSI_PORT_CLK_GATE); -- return; -- } -- -- /* set csi port is using by SW */ -- writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); -- /* input is coming from MGC */ -- offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -- writel(CSI_MIPIGEN_INPUT, -- csi2->base + offset + CSI2_ADPL_INPUT_MODE); --} -- --/* TODO: add the processing of vc */ --int tpg_set_stream(struct v4l2_subdev *sd, int enable) --{ -- struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -- struct ipu7_isys_stream *stream = tpg->av->stream; -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- int ret; -- -- if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { -- dev_err(dev, "invalid MGC index %d\n", tpg->index); -- return -EINVAL; -- } -- -- if (!enable) { -- /* Stop MGC */ -- stream->asd->is_tpg = false; -- stream->asd = NULL; -- ipu7_isys_mgc_csi2_s_stream(tpg, enable); -- ret = tpg_stop_stream(tpg); -- ipu7_isys_ungate_mgc(tpg, enable); -- -- return ret; -- } -- -- stream->asd = &tpg->asd; -- /* ungate the MGC clock to program */ -- ipu7_isys_ungate_mgc(tpg, enable); -- /* Start MGC */ -- ret = tpg_start_stream(tpg); -- v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); -- ipu7_isys_mgc_csi2_s_stream(tpg, enable); -- -- return ret; --} -- --void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) --{ -- v4l2_device_unregister_subdev(&tpg->asd.sd); -- ipu7_isys_subdev_cleanup(&tpg->asd); --} -- --int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, -- void __iomem *base, void __iomem *sel, -- unsigned int index) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- int ret; -- -- tpg->isys = isys; -- tpg->base = base; -- tpg->sel = sel; -- tpg->index = index; -- -- tpg->asd.sd.entity.ops = &tpg_entity_ops; -- tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; -- tpg->asd.isys = isys; -- -- ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, -- NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); -- if (ret) -- return ret; -- -- tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; -- tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -- tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -- -- tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; -- tpg->asd.supported_codes = tpg_supported_codes; -- tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; -- -- snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), -- IPU_ISYS_ENTITY_PREFIX " TPG %u", index); -- v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); -- -- ret = v4l2_subdev_init_finalize(&tpg->asd.sd); -- if (ret) { -- dev_err(dev, "failed to finalize subdev (%d)\n", ret); -- goto fail; -- } -- -- ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); -- if (ret) { -- dev_info(dev, "can't register v4l2 subdev\n"); -- goto fail; -- } -- -- return 0; -- --fail: -- ipu7_isys_tpg_cleanup(tpg); -- -- return ret; --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -deleted file mode 100644 -index e2542a6472e3..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -+++ /dev/null -@@ -1,70 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_TPG_H --#define IPU7_ISYS_TPG_H -- --#include --#include --#include -- --#include "ipu7-isys-subdev.h" --#include "ipu7-isys-video.h" --#include "ipu7-isys-queue.h" -- --struct ipu7_isys_tpg_pdata; --struct ipu7_isys; -- --#define TPG_PAD_SOURCE 0 --#define NR_OF_TPG_PADS 1 --#define NR_OF_TPG_SOURCE_PADS 1 --#define NR_OF_TPG_SINK_PADS 0 --#define NR_OF_TPG_STREAMS 1 -- --enum isys_tpg_mode { -- TPG_MODE_RAMP = 0, -- TPG_MODE_CHECKERBOARD = 1, -- TPG_MODE_MONO = 2, -- TPG_MODE_COLOR_PALETTE = 3, --}; -- --/* -- * struct ipu7_isys_tpg -- * -- * @nlanes: number of lanes in the receiver -- */ --struct ipu7_isys_tpg { -- struct ipu7_isys_subdev asd; -- struct ipu7_isys_tpg_pdata *pdata; -- struct ipu7_isys *isys; -- struct ipu7_isys_video *av; -- -- /* MG base not MGC */ -- void __iomem *base; -- void __iomem *sel; -- unsigned int index; -- -- struct v4l2_ctrl *hblank; -- struct v4l2_ctrl *vblank; -- struct v4l2_ctrl *pixel_rate; --}; -- --#define ipu7_isys_subdev_to_tpg(__sd) \ -- container_of(__sd, struct ipu7_isys_tpg, asd) -- --#define to_ipu7_isys_tpg(sd) \ -- container_of(to_ipu7_isys_subdev(sd), \ -- struct ipu7_isys_tpg, asd) -- --void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); --void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); --int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, -- struct ipu7_isys *isys, -- void __iomem *base, void __iomem *sel, -- unsigned int index); --void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); --int tpg_set_stream(struct v4l2_subdev *sd, int enable); -- --#endif /* IPU7_ISYS_TPG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -deleted file mode 100644 -index 12fdf556c656..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -+++ /dev/null -@@ -1,1311 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#include --#endif -- --#include --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-fw-isys.h" --#include "ipu7-isys.h" --#include "ipu7-isys-video.h" --#include "ipu7-platform-regs.h" -- --const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { -- {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -- IPU_INSYS_FRAME_FORMAT_UYVY}, -- {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -- IPU_INSYS_FRAME_FORMAT_YUYV}, -- {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -- IPU_INSYS_FRAME_FORMAT_RGB565}, -- {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -- IPU_INSYS_FRAME_FORMAT_RGBA888}, --}; -- --static int video_open(struct file *file) --{ --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- struct ipu7_isys_video *av = video_drvdata(file); -- struct ipu7_isys *isys = av->isys; -- struct ipu7_bus_device *adev = isys->adev; -- -- mutex_lock(&isys->reset_mutex); -- if (isys->need_reset) { -- mutex_unlock(&isys->reset_mutex); -- dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -- return -EIO; -- } -- mutex_unlock(&isys->reset_mutex); -- --#endif -- return v4l2_fh_open(file); --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --static int video_release(struct file *file) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- dev_dbg(&av->isys->adev->auxdev.dev, -- "release: %s: enter\n", av->vdev.name); -- mutex_lock(&av->isys->reset_mutex); -- while (av->isys->state & RESET_STATE_IN_RESET) { -- mutex_unlock(&av->isys->reset_mutex); -- dev_dbg(&av->isys->adev->auxdev.dev, -- "release: %s: wait for reset\n", av->vdev.name); -- usleep_range(10000, 11000); -- mutex_lock(&av->isys->reset_mutex); -- } -- mutex_unlock(&av->isys->reset_mutex); -- return vb2_fop_release(file); --} -- --#endif --const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) --{ -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -- const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i]; -- -- if (pfmt->pixelformat == pixelformat) -- return pfmt; -- } -- -- return &ipu7_isys_pfmts[0]; --} -- --static int ipu7_isys_vidioc_querycap(struct file *file, void *fh, -- struct v4l2_capability *cap) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); -- strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); -- -- return 0; --} -- --static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, -- struct v4l2_fmtdesc *f) --{ -- unsigned int i, num_found; -- -- for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -- continue; -- -- if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code) -- continue; -- -- if (num_found < f->index) { -- num_found++; -- continue; -- } -- -- f->flags = 0; -- f->pixelformat = ipu7_isys_pfmts[i].pixelformat; -- -- return 0; -- } -- -- return -EINVAL; --} -- --static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, -- struct v4l2_frmsizeenum *fsize) --{ -- unsigned int i; -- -- if (fsize->index > 0) -- return -EINVAL; -- -- for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -- if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) -- continue; -- -- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; -- fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH; -- fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH; -- fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT; -- fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT; -- fsize->stepwise.step_width = 2; -- fsize->stepwise.step_height = 2; -- -- return 0; -- } -- -- return -EINVAL; --} -- --static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, -- struct v4l2_format *f) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- f->fmt.pix = av->pix_fmt; -- -- return 0; --} -- --static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type, -- u32 *format, u32 *width, u32 *height, -- u32 *bytesperline, u32 *sizeimage) --{ -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(*format); -- -- *format = pfmt->pixelformat; -- *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH); -- *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); -- -- if (pfmt->bpp != pfmt->bpp_packed) -- *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); -- else -- *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); -- -- *bytesperline = ALIGN(*bytesperline, 64U); -- -- /* -- * (height + 1) * bytesperline due to a hardware issue: the DMA unit -- * is a power of two, and a line should be transferred as few units -- * as possible. The result is that up to line length more data than -- * the image size may be transferred to memory after the image. -- * Another limitation is the GDA allocation unit size. For low -- * resolution it gives a bigger number. Use larger one to avoid -- * memory corruption. -- */ -- *sizeimage = *bytesperline * *height + -- max(*bytesperline, -- av->isys->pdata->ipdata->isys_dma_overshoot); --} -- --static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, -- struct v4l2_format *f) --{ -- ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, -- &f->fmt.pix.width, &f->fmt.pix.height, -- &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); -- -- f->fmt.pix.field = V4L2_FIELD_NONE; -- f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; -- f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -- f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; -- f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; --} -- --static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, -- struct v4l2_format *f) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- if (vb2_is_busy(&av->aq.vbq)) -- return -EBUSY; -- -- __ipu_isys_vidioc_try_fmt_vid_cap(av, f); -- -- return 0; --} -- --static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, -- struct v4l2_format *f) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); -- av->pix_fmt = f->fmt.pix; -- -- return 0; --} -- --static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv, -- struct v4l2_requestbuffers *p) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- int ret; -- -- av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); -- av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); -- -- ret = vb2_queue_change_type(&av->aq.vbq, p->type); -- if (ret) -- return ret; -- -- return vb2_ioctl_reqbufs(file, priv, p); --} -- --static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv, -- struct v4l2_create_buffers *p) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- int ret; -- -- av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); -- av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); -- -- ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); -- if (ret) -- return ret; -- -- return vb2_ioctl_create_bufs(file, priv, p); --} -- --static int link_validate(struct media_link *link) --{ -- struct ipu7_isys_video *av = -- container_of(link->sink, struct ipu7_isys_video, pad); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct v4l2_subdev_state *s_state; -- struct v4l2_mbus_framefmt *s_fmt; -- struct v4l2_subdev *s_sd; -- struct media_pad *s_pad; -- u32 s_stream, code; -- int ret = -EPIPE; -- -- if (!link->source->entity) -- return ret; -- -- s_sd = media_entity_to_v4l2_subdev(link->source->entity); -- s_state = v4l2_subdev_get_unlocked_active_state(s_sd); -- if (!s_state) -- return ret; -- -- dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", -- link->source->entity->name, link->source->index, -- link->sink->entity->name); -- -- s_pad = media_pad_remote_pad_first(&av->pad); -- s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); -- -- v4l2_subdev_lock_state(s_state); -- -- s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); -- if (!s_fmt) { -- dev_err(dev, "failed to get source pad format\n"); -- goto unlock; -- } -- -- code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -- -- if (s_fmt->width != av->pix_fmt.width || -- s_fmt->height != av->pix_fmt.height || s_fmt->code != code) { -- dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", -- s_fmt->width, s_fmt->height, s_fmt->code, -- av->pix_fmt.width, av->pix_fmt.height, code); -- goto unlock; -- } -- -- v4l2_subdev_unlock_state(s_state); -- -- return 0; --unlock: -- v4l2_subdev_unlock_state(s_state); -- -- return ret; --} -- --static void get_stream_opened(struct ipu7_isys_video *av) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&av->isys->streams_lock, flags); -- av->isys->stream_opened++; -- spin_unlock_irqrestore(&av->isys->streams_lock, flags); --} -- --static void put_stream_opened(struct ipu7_isys_video *av) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&av->isys->streams_lock, flags); -- av->isys->stream_opened--; -- spin_unlock_irqrestore(&av->isys->streams_lock, flags); --} -- --static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, -- struct ipu7_insys_stream_cfg *cfg) --{ -- struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); -- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); -- struct ipu7_isys_stream *stream = av->stream; -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- struct ipu7_insys_output_pin *output_pin; -- struct ipu7_insys_input_pin *input_pin; -- int input_pins = cfg->nof_input_pins++; -- struct ipu7_isys_queue *aq = &av->aq; -- struct ipu7_isys *isys = av->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- struct v4l2_mbus_framefmt fmt; -- int output_pins; -- u32 src_stream; -- int ret; -- -- src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index); -- ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, -- &fmt); -- if (ret < 0) { -- dev_err(dev, "can't get stream format (%d)\n", ret); -- return ret; -- } -- -- input_pin = &cfg->input_pins[input_pins]; -- input_pin->input_res.width = fmt.width; -- input_pin->input_res.height = fmt.height; -- input_pin->dt = av->dt; -- input_pin->disable_mipi_unpacking = 0; -- pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) -- input_pin->disable_mipi_unpacking = 1; -- input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE; -- input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME; -- /* if enable polling isys interrupt, the follow values maybe set */ -- input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; -- -- output_pins = cfg->nof_output_pins++; -- aq->fw_output = output_pins; -- stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready; -- stream->output_pins[output_pins].aq = aq; -- -- output_pin = &cfg->output_pins[output_pins]; -- /* output pin msg link */ -- output_pin->link.buffer_lines = 0; -- output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE; -- output_pin->link.granularity_pointer_update = 0; -- output_pin->link.msg_link_streaming_mode = -- IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF; -- -- output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE; -- output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE; -- output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM; -- output_pin->link.use_sw_managed = 1; -- /* TODO: set the snoop bit for metadata capture */ -- output_pin->link.is_snoop = 0; -- -- /* output pin crop */ -- output_pin->crop.line_top = 0; -- output_pin->crop.line_bottom = 0; --#ifdef IPU8_INSYS_NEW_ABI -- output_pin->crop.column_left = 0; -- output_pin->crop.column_right = 0; --#endif -- -- /* output de-compression */ -- output_pin->dpcm.enable = 0; -- --#ifdef IPU8_INSYS_NEW_ABI -- /* upipe_cfg */ -- output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; -- output_pin->upipe_pin_cfg.plane_offset_1 = 0; -- output_pin->upipe_pin_cfg.plane_offset_2 = 0; -- output_pin->upipe_pin_cfg.single_uob_fifo = 0; -- output_pin->upipe_pin_cfg.shared_uob_fifo = 0; -- output_pin->upipe_enable = 0; -- output_pin->binning_factor = 0; -- /* stupid setting, even unused, SW still need to set a valid value */ -- output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; --#endif -- -- /* frame format type */ -- pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- output_pin->ft = (u16)pfmt->css_pixelformat; -- -- /* stride in bytes */ -- output_pin->stride = av->pix_fmt.bytesperline; -- output_pin->send_irq = 1; -- output_pin->early_ack_en = 0; -- -- /* input pin id */ -- output_pin->input_pin_id = input_pins; -- -- return 0; --} -- --/* Create stream and start it using the CSS FW ABI. */ --static int start_stream_firmware(struct ipu7_isys_video *av, -- struct ipu7_isys_buffer_list *bl) --{ -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_insys_stream_cfg *stream_cfg; -- struct ipu7_insys_buffset *buf = NULL; -- struct isys_fw_msgs *msg = NULL; -- struct ipu7_isys_queue *aq; -- int ret, retout, tout; -- u16 send_type; -- -- if (WARN_ON(!bl)) -- return -EIO; -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) -- return -ENOMEM; -- -- stream_cfg = &msg->fw_msg.stream; -- stream_cfg->port_id = stream->stream_source; -- stream_cfg->vc = stream->vc; -- stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP | -- IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; -- -- list_for_each_entry(aq, &stream->queues, node) { -- struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq); -- -- ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg); -- if (ret < 0) { -- ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -- return ret; -- } -- } -- -- ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg); -- -- stream->nr_output_pins = stream_cfg->nof_output_pins; -- -- reinit_completion(&stream->stream_open_completion); -- -- ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, -- stream_cfg, msg->dma_addr, -- sizeof(*stream_cfg), -- IPU_INSYS_SEND_TYPE_STREAM_OPEN); -- if (ret < 0) { -- dev_err(dev, "can't open stream (%d)\n", ret); -- ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -- return ret; -- } -- -- get_stream_opened(av); -- -- tout = wait_for_completion_timeout(&stream->stream_open_completion, -- FW_CALL_TIMEOUT_JIFFIES); -- -- ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -- -- if (!tout) { -- dev_err(dev, "stream open time out\n"); -- ret = -ETIMEDOUT; -- goto out_put_stream_opened; -- } -- if (stream->error) { -- dev_err(dev, "stream open error: %d\n", stream->error); -- ret = -EIO; -- goto out_put_stream_opened; -- } -- dev_dbg(dev, "start stream: open complete\n"); -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) { -- ret = -ENOMEM; -- goto out_put_stream_opened; -- } -- buf = &msg->fw_msg.frame; -- -- ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -- ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -- -- reinit_completion(&stream->stream_start_completion); -- -- send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE; -- ipu7_fw_isys_dump_frame_buff_set(dev, buf, -- stream_cfg->nof_output_pins); -- ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf, -- msg->dma_addr, sizeof(*buf), -- send_type); -- if (ret < 0) { -- dev_err(dev, "can't start streaming (%d)\n", ret); -- goto out_stream_close; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_start_completion, -- FW_CALL_TIMEOUT_JIFFIES); -- if (!tout) { -- dev_err(dev, "stream start time out\n"); -- ret = -ETIMEDOUT; -- goto out_stream_close; -- } -- if (stream->error) { -- dev_err(dev, "stream start error: %d\n", stream->error); -- ret = -EIO; -- goto out_stream_close; -- } -- dev_dbg(dev, "start stream: complete\n"); -- -- return 0; -- --out_stream_close: -- reinit_completion(&stream->stream_close_completion); -- -- retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -- IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -- if (retout < 0) { -- dev_dbg(dev, "can't close stream (%d)\n", retout); -- goto out_put_stream_opened; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_close_completion, -- FW_CALL_TIMEOUT_JIFFIES); -- if (!tout) -- dev_err(dev, "stream close time out with error %d\n", -- stream->error); -- else -- dev_dbg(dev, "stream close complete\n"); -- --out_put_stream_opened: -- put_stream_opened(av); -- -- return ret; --} -- --static void stop_streaming_firmware(struct ipu7_isys_video *av) --{ -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- int ret, tout; -- -- reinit_completion(&stream->stream_stop_completion); -- -- ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -- IPU_INSYS_SEND_TYPE_STREAM_FLUSH); -- if (ret < 0) { -- dev_err(dev, "can't stop stream (%d)\n", ret); -- return; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_stop_completion, --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- FW_CALL_TIMEOUT_JIFFIES_RESET); --#else -- FW_CALL_TIMEOUT_JIFFIES); --#endif -- if (!tout) -- dev_warn(dev, "stream stop time out\n"); -- else if (stream->error) -- dev_warn(dev, "stream stop error: %d\n", stream->error); -- else -- dev_dbg(dev, "stop stream: complete\n"); --} -- --static void close_streaming_firmware(struct ipu7_isys_video *av) --{ -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- int ret, tout; -- -- reinit_completion(&stream->stream_close_completion); -- -- ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -- IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -- if (ret < 0) { -- dev_err(dev, "can't close stream (%d)\n", ret); -- return; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_close_completion, --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- FW_CALL_TIMEOUT_JIFFIES_RESET); --#else -- FW_CALL_TIMEOUT_JIFFIES); --#endif -- if (!tout) -- dev_warn(dev, "stream close time out\n"); -- else if (stream->error) -- dev_warn(dev, "stream close error: %d\n", stream->error); -- else -- dev_dbg(dev, "close stream: complete\n"); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- stream->last_sequence = atomic_read(&stream->sequence); -- dev_dbg(dev, "ip->last_sequence = %d\n", -- stream->last_sequence); -- --#endif -- put_stream_opened(av); --} -- --int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -- struct media_entity *source_entity, -- int nr_queues) --{ -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_isys_csi2 *csi2; -- -- if (WARN_ON(stream->nr_streaming)) -- return -EINVAL; -- -- stream->nr_queues = nr_queues; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- if (av->isys->state & RESET_STATE_IN_RESET) { -- atomic_set(&stream->sequence, stream->last_sequence); -- dev_dbg(&av->isys->adev->auxdev.dev, -- "atomic_set : stream->last_sequence = %d\n", -- stream->last_sequence); -- } else { -- atomic_set(&stream->sequence, 0); -- } --#else -- atomic_set(&stream->sequence, 0); --#endif -- atomic_set(&stream->buf_id, 0); -- -- stream->seq_index = 0; -- memset(stream->seq, 0, sizeof(stream->seq)); -- -- if (WARN_ON(!list_empty(&stream->queues))) -- return -EINVAL; -- -- stream->stream_source = stream->asd->source; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (!stream->asd->is_tpg) { -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- csi2->receiver_errors = 0; -- } --#else -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- csi2->receiver_errors = 0; --#endif -- stream->source_entity = source_entity; -- -- dev_dbg(&av->isys->adev->auxdev.dev, -- "prepare stream: external entity %s\n", -- stream->source_entity->name); -- -- return 0; --} -- --void ipu7_isys_put_stream(struct ipu7_isys_stream *stream) --{ -- unsigned long flags; -- struct device *dev; -- unsigned int i; -- -- if (!stream) { -- pr_err("ipu7-isys: no available stream\n"); -- return; -- } -- -- dev = &stream->isys->adev->auxdev.dev; -- -- spin_lock_irqsave(&stream->isys->streams_lock, flags); -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (&stream->isys->streams[i] == stream) { -- if (stream->isys->streams_ref_count[i] > 0) -- stream->isys->streams_ref_count[i]--; -- else -- dev_warn(dev, "invalid stream %d\n", i); -- -- break; -- } -- } -- spin_unlock_irqrestore(&stream->isys->streams_lock, flags); --} -- --static struct ipu7_isys_stream * --ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd) --{ -- struct ipu7_isys_stream *stream = NULL; -- struct ipu7_isys *isys = av->isys; -- unsigned long flags; -- unsigned int i; -- u8 vc = av->vc; -- -- if (!isys) -- return NULL; -- -- spin_lock_irqsave(&isys->streams_lock, flags); -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && -- isys->streams[i].asd == asd) { -- isys->streams_ref_count[i]++; -- stream = &isys->streams[i]; -- break; -- } -- } -- -- if (!stream) { -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (!isys->streams_ref_count[i]) { -- isys->streams_ref_count[i]++; -- stream = &isys->streams[i]; -- stream->vc = vc; -- stream->asd = asd; -- break; -- } -- } -- } -- spin_unlock_irqrestore(&isys->streams_lock, flags); -- -- return stream; --} -- --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle) --{ -- unsigned long flags; -- struct ipu7_isys_stream *stream = NULL; -- -- if (!isys) -- return NULL; -- -- if (stream_handle >= IPU_ISYS_MAX_STREAMS) { -- dev_err(&isys->adev->auxdev.dev, -- "stream_handle %d is invalid\n", stream_handle); -- return NULL; -- } -- -- spin_lock_irqsave(&isys->streams_lock, flags); -- if (isys->streams_ref_count[stream_handle] > 0) { -- isys->streams_ref_count[stream_handle]++; -- stream = &isys->streams[stream_handle]; -- } -- spin_unlock_irqrestore(&isys->streams_lock, flags); -- -- return stream; --} -- --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc) --{ -- struct ipu7_isys_stream *stream = NULL; -- unsigned long flags; -- unsigned int i; -- -- if (!isys) -- return NULL; -- -- if (source < 0) { -- dev_err(&isys->adev->auxdev.dev, -- "query stream with invalid port number\n"); -- return NULL; -- } -- -- spin_lock_irqsave(&isys->streams_lock, flags); -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (!isys->streams_ref_count[i]) -- continue; -- -- if (isys->streams[i].stream_source == source && -- isys->streams[i].vc == vc) { -- stream = &isys->streams[i]; -- isys->streams_ref_count[i]++; -- break; -- } -- } -- spin_unlock_irqrestore(&isys->streams_lock, flags); -- -- return stream; --} -- --static u32 get_remote_pad_stream(struct media_pad *r_pad) --{ -- struct v4l2_subdev_state *state; -- struct v4l2_subdev *sd; -- u32 stream_id = 0; -- unsigned int i; -- -- sd = media_entity_to_v4l2_subdev(r_pad->entity); -- state = v4l2_subdev_lock_and_get_active_state(sd); -- if (!state) -- return 0; -- -- for (i = 0; i < state->stream_configs.num_configs; i++) { -- struct v4l2_subdev_stream_config *cfg = -- &state->stream_configs.configs[i]; -- if (cfg->pad == r_pad->index) { -- stream_id = cfg->stream; -- break; -- } -- } -- -- v4l2_subdev_unlock_state(state); -- -- return stream_id; --} -- --int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -- struct ipu7_isys_buffer_list *bl) --{ -- struct ipu7_isys_stream *stream = av->stream; -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct media_pad *r_pad; -- struct v4l2_subdev *sd; -- u32 r_stream; -- int ret = 0; -- -- dev_dbg(dev, "set stream: %d\n", state); -- -- if (WARN(!stream->source_entity, "No source entity for stream\n")) -- return -ENODEV; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (stream->asd->is_tpg) { -- sd = &stream->asd->sd; -- r_pad = media_pad_remote_pad_first(&av->pad); -- r_stream = -- ipu7_isys_get_src_stream_by_src_pad(sd, r_pad->index); -- -- if (!state) { -- stop_streaming_firmware(av); -- dev_dbg(dev, "disable streams 0x%lx of %s\n", -- BIT(r_stream), sd->name); -- ret = v4l2_subdev_disable_streams(sd, r_pad->index, -- BIT(r_stream)); -- if (ret) -- dev_err(dev, "disable streams of %s failed\n", -- sd->name); -- -- close_streaming_firmware(av); -- } else { -- ret = start_stream_firmware(av, bl); -- if (ret) { -- dev_err(dev, "start FW stream failed\n"); -- return ret; -- } -- -- dev_dbg(dev, "set stream: source %d, handle %d\n", -- stream->stream_source, stream->stream_handle); -- -- dev_dbg(dev, "enable streams 0x%lx of %s\n", -- BIT(r_stream), sd->name); -- /* start sub-device which connects with video */ -- ret = v4l2_subdev_enable_streams(sd, r_pad->index, -- BIT(r_stream)); -- if (ret) { -- dev_err(dev, "enable streams of %s failed\n", -- sd->name); -- goto out_media_entity_stop_streaming_firmware; -- } -- } -- av->streaming = state; -- -- return 0; -- } -- --#endif -- sd = &stream->asd->sd; -- r_pad = media_pad_remote_pad_first(&av->pad); -- r_stream = get_remote_pad_stream(r_pad); -- if (!state) { -- stop_streaming_firmware(av); -- -- /* stop sub-device which connects with video */ -- dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n", -- sd->name, r_pad->index, BIT_ULL(r_stream)); -- ret = v4l2_subdev_disable_streams(sd, r_pad->index, -- BIT_ULL(r_stream)); -- if (ret) { -- dev_err(dev, "disable streams %s failed with %d\n", -- sd->name, ret); -- return ret; -- } -- -- close_streaming_firmware(av); -- } else { -- ret = start_stream_firmware(av, bl); -- if (ret) { -- dev_err(dev, "start stream of firmware failed\n"); -- return ret; -- } -- -- /* start sub-device which connects with video */ -- dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n", -- sd->name, r_pad->index, BIT_ULL(r_stream)); -- ret = v4l2_subdev_enable_streams(sd, r_pad->index, -- BIT_ULL(r_stream)); -- if (ret) { -- dev_err(dev, "enable streams %s failed with %d\n", -- sd->name, ret); -- goto out_media_entity_stop_streaming_firmware; -- } -- } -- -- av->streaming = state; -- -- return 0; -- --out_media_entity_stop_streaming_firmware: -- stop_streaming_firmware(av); -- -- return ret; --} -- --static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { -- .vidioc_querycap = ipu7_isys_vidioc_querycap, -- .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, -- .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes, -- .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap, -- .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap, -- .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap, -- .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs, -- .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs, -- .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -- .vidioc_querybuf = vb2_ioctl_querybuf, -- .vidioc_qbuf = vb2_ioctl_qbuf, -- .vidioc_dqbuf = vb2_ioctl_dqbuf, -- .vidioc_streamon = vb2_ioctl_streamon, -- .vidioc_streamoff = vb2_ioctl_streamoff, -- .vidioc_expbuf = vb2_ioctl_expbuf, --}; -- --static const struct media_entity_operations entity_ops = { -- .link_validate = link_validate, --}; -- --static const struct v4l2_file_operations isys_fops = { -- .owner = THIS_MODULE, -- .poll = vb2_fop_poll, -- .unlocked_ioctl = video_ioctl2, -- .mmap = vb2_fop_mmap, -- .open = video_open, --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- .release = video_release, --#else -- .release = vb2_fop_release, --#endif --}; -- --int ipu7_isys_fw_open(struct ipu7_isys *isys) --{ -- struct ipu7_bus_device *adev = isys->adev; -- int ret; -- -- ret = pm_runtime_resume_and_get(&adev->auxdev.dev); -- if (ret < 0) -- return ret; -- -- mutex_lock(&isys->mutex); -- -- if (isys->ref_count++) -- goto unlock; -- -- /* -- * Buffers could have been left to wrong queue at last closure. -- * Move them now back to empty buffer queue. -- */ -- ipu7_cleanup_fw_msg_bufs(isys); -- -- ret = ipu7_fw_isys_open(isys); -- if (ret < 0) -- goto out; -- --unlock: -- mutex_unlock(&isys->mutex); -- -- return 0; --out: -- isys->ref_count--; -- mutex_unlock(&isys->mutex); -- pm_runtime_put(&adev->auxdev.dev); -- -- return ret; --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --void ipu7_isys_fw_close(struct ipu7_isys *isys) --{ -- int ret = 0; -- -- mutex_lock(&isys->mutex); -- isys->ref_count--; -- if (!isys->ref_count) { -- /* need reset when fw close is abnormal */ -- ret = ipu7_fw_isys_close(isys); -- if (ret) { -- mutex_lock(&isys->reset_mutex); -- isys->need_reset = true; -- mutex_unlock(&isys->reset_mutex); -- } -- } -- -- mutex_unlock(&isys->mutex); -- -- mutex_lock(&isys->reset_mutex); -- if (isys->need_reset) { -- mutex_unlock(&isys->reset_mutex); -- pm_runtime_put_sync(&isys->adev->auxdev.dev); -- } else { -- mutex_unlock(&isys->reset_mutex); -- pm_runtime_put(&isys->adev->auxdev.dev); -- } --} --#else --void ipu7_isys_fw_close(struct ipu7_isys *isys) --{ -- mutex_lock(&isys->mutex); -- -- isys->ref_count--; -- -- if (!isys->ref_count) -- ipu7_fw_isys_close(isys); -- -- mutex_unlock(&isys->mutex); -- pm_runtime_put(&isys->adev->auxdev.dev); --} --#endif -- --int ipu7_isys_setup_video(struct ipu7_isys_video *av, -- struct media_entity **source_entity, int *nr_queues) --{ -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct media_pad *source_pad, *remote_pad; -- struct v4l2_mbus_frame_desc_entry entry; -- struct v4l2_subdev_route *route = NULL; -- struct v4l2_subdev_route *r; -- struct v4l2_subdev_state *state; -- struct ipu7_isys_subdev *asd; -- struct v4l2_subdev *remote_sd; -- struct media_pipeline *pipeline; -- int ret = -EINVAL; -- -- *nr_queues = 0; -- -- remote_pad = media_pad_remote_pad_unique(&av->pad); -- if (IS_ERR(remote_pad)) { -- dev_dbg(dev, "failed to get remote pad\n"); -- return PTR_ERR(remote_pad); -- } -- -- remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -- asd = to_ipu7_isys_subdev(remote_sd); --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- -- if (strncmp(remote_pad->entity->name, "Intel IPU7 TPG", -- strlen("Intel IPU7 TPG")) == 0) { -- dev_dbg(dev, "Find TPG:%s stream\n", remote_sd->name); -- -- av->vc = 0; -- av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -- ret = video_device_pipeline_alloc_start(&av->vdev); -- if (ret < 0) { -- dev_dbg(dev, "media pipeline start failed\n"); -- return ret; -- } -- -- *source_entity = remote_pad->entity; -- av->stream = ipu7_isys_get_stream(av, asd); -- if (!av->stream) { -- video_device_pipeline_stop(&av->vdev); -- dev_err(dev, "no available stream for firmware\n"); -- return -EINVAL; -- } -- -- av->stream->asd->is_tpg = true; -- *nr_queues = 1; -- -- return 0; -- } --#endif -- -- source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); -- if (!source_pad) { -- dev_dbg(dev, "No external source entity\n"); -- return -ENODEV; -- } -- -- *source_entity = source_pad->entity; -- -- state = v4l2_subdev_lock_and_get_active_state(remote_sd); -- for_each_active_route(&state->routing, r) { -- if (r->source_pad == remote_pad->index) -- route = r; -- } -- -- if (!route) { -- v4l2_subdev_unlock_state(state); -- dev_dbg(dev, "Failed to find route\n"); -- return -ENODEV; -- } -- -- v4l2_subdev_unlock_state(state); -- -- ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, -- to_ipu7_isys_csi2(asd), -- *source_entity, &entry, -- nr_queues); -- if (ret == -ENOIOCTLCMD) { -- av->vc = 0; -- av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -- if (av->dt == 0xff) -- return -EINVAL; -- *nr_queues = 1; -- } else if (*nr_queues && !ret) { -- dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", -- entry.stream, entry.length, entry.bus.csi2.vc, -- entry.bus.csi2.dt); -- -- av->vc = entry.bus.csi2.vc; -- av->dt = entry.bus.csi2.dt; -- } else { -- dev_err(dev, "failed to get remote frame desc\n"); -- return ret; -- } -- -- pipeline = media_entity_pipeline(&av->vdev.entity); -- if (!pipeline) -- ret = video_device_pipeline_alloc_start(&av->vdev); -- else -- ret = video_device_pipeline_start(&av->vdev, pipeline); -- if (ret < 0) { -- dev_dbg(dev, "media pipeline start failed\n"); -- return ret; -- } -- -- av->stream = ipu7_isys_get_stream(av, asd); -- if (!av->stream) { -- video_device_pipeline_stop(&av->vdev); -- dev_err(dev, "no available stream for firmware\n"); -- return -EINVAL; -- } -- -- return 0; --} -- --/* -- * Do everything that's needed to initialise things related to video -- * buffer queue, video node, and the related media entity. The caller -- * is expected to assign isys field and set the name of the video -- * device. -- */ --int ipu7_isys_video_init(struct ipu7_isys_video *av) --{ -- struct v4l2_format format = { -- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, -- .fmt.pix = { -- .width = 1920, -- .height = 1080, -- }, -- }; -- int ret; -- -- mutex_init(&av->mutex); -- av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | -- V4L2_CAP_VIDEO_CAPTURE; -- av->vdev.vfl_dir = VFL_DIR_RX; -- -- ret = ipu7_isys_queue_init(&av->aq); -- if (ret) -- goto out_mutex_destroy; -- -- av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; -- ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); -- if (ret) -- goto out_vb2_queue_cleanup; -- -- av->vdev.entity.ops = &entity_ops; -- av->vdev.release = video_device_release_empty; -- av->vdev.fops = &isys_fops; -- av->vdev.v4l2_dev = &av->isys->v4l2_dev; -- av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev; -- av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops; -- av->vdev.queue = &av->aq.vbq; -- av->vdev.lock = &av->mutex; -- -- __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); -- av->pix_fmt = format.fmt.pix; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- av->reset = false; -- av->skipframe = 0; -- av->start_streaming = 0; --#endif -- -- set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); -- video_set_drvdata(&av->vdev, av); -- -- ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); -- if (ret) -- goto out_media_entity_cleanup; -- -- return ret; -- --out_media_entity_cleanup: -- vb2_video_unregister_device(&av->vdev); -- media_entity_cleanup(&av->vdev.entity); -- --out_vb2_queue_cleanup: -- vb2_queue_release(&av->aq.vbq); -- --out_mutex_destroy: -- mutex_destroy(&av->mutex); -- -- return ret; --} -- --void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) --{ -- vb2_video_unregister_device(&av->vdev); -- media_entity_cleanup(&av->vdev.entity); -- mutex_destroy(&av->mutex); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.h b/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -deleted file mode 100644 -index e6d1da2b7b47..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -+++ /dev/null -@@ -1,125 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_VIDEO_H --#define IPU7_ISYS_VIDEO_H -- --#include --#include --#include --#include --#include -- --#include --#include -- --#include "ipu7-isys-queue.h" -- --#define IPU_INSYS_OUTPUT_PINS 11U --#define IPU_ISYS_MAX_PARALLEL_SOF 2U -- --struct file; --struct ipu7_isys; --struct ipu7_isys_csi2; --struct ipu7_insys_stream_cfg; --struct ipu7_isys_subdev; -- --struct ipu7_isys_pixelformat { -- u32 pixelformat; -- u32 bpp; -- u32 bpp_packed; -- u32 code; -- u32 css_pixelformat; --}; -- --struct sequence_info { -- unsigned int sequence; -- u64 timestamp; --}; -- --struct output_pin_data { -- void (*pin_ready)(struct ipu7_isys_stream *stream, -- struct ipu7_insys_resp *info); -- struct ipu7_isys_queue *aq; --}; -- --/* -- * Align with firmware stream. Each stream represents a CSI virtual channel. -- * May map to multiple video devices -- */ --struct ipu7_isys_stream { -- struct mutex mutex; -- struct media_entity *source_entity; -- atomic_t sequence; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- int last_sequence; --#endif -- atomic_t buf_id; -- unsigned int seq_index; -- struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; -- int stream_source; -- int stream_handle; -- unsigned int nr_output_pins; -- struct ipu7_isys_subdev *asd; -- -- int nr_queues; /* Number of capture queues */ -- int nr_streaming; -- int streaming; -- struct list_head queues; -- struct completion stream_open_completion; -- struct completion stream_close_completion; -- struct completion stream_start_completion; -- struct completion stream_stop_completion; -- struct ipu7_isys *isys; -- -- struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS]; -- int error; -- u8 vc; --}; -- --struct ipu7_isys_video { -- struct ipu7_isys_queue aq; -- /* Serialise access to other fields in the struct. */ -- struct mutex mutex; -- struct media_pad pad; -- struct video_device vdev; -- struct v4l2_pix_format pix_fmt; -- struct ipu7_isys *isys; -- struct ipu7_isys_csi2 *csi2; -- struct ipu7_isys_stream *stream; -- unsigned int streaming; -- u8 vc; -- u8 dt; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- unsigned int reset; -- unsigned int skipframe; -- unsigned int start_streaming; --#endif --}; -- --#define ipu7_isys_queue_to_video(__aq) \ -- container_of(__aq, struct ipu7_isys_video, aq) -- --extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[]; -- --const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat); --int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -- struct media_entity *source_entity, -- int nr_queues); --int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -- struct ipu7_isys_buffer_list *bl); --int ipu7_isys_fw_open(struct ipu7_isys *isys); --void ipu7_isys_fw_close(struct ipu7_isys *isys); --int ipu7_isys_setup_video(struct ipu7_isys_video *av, -- struct media_entity **source_entity, int *nr_queues); --int ipu7_isys_video_init(struct ipu7_isys_video *av); --void ipu7_isys_video_cleanup(struct ipu7_isys_video *av); --void ipu7_isys_put_stream(struct ipu7_isys_stream *stream); --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, -- u8 stream_handle); --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc); --#endif /* IPU7_ISYS_VIDEO_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c -deleted file mode 100644 -index 44e860d3db24..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys.c -+++ /dev/null -@@ -1,1625 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#ifdef CONFIG_DEBUG_FS --#include --#endif --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-cpd.h" --#include "ipu7-dma.h" --#include "ipu7-fw-isys.h" --#include "ipu7-mmu.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2.h" --#include "ipu7-isys-csi-phy.h" --#include "ipu7-isys-csi2-regs.h" --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --#include "ipu7-isys-tpg.h" --#endif --#include "ipu7-isys-video.h" --#include "ipu7-platform-regs.h" -- --#define ISYS_PM_QOS_VALUE 300 -- --static int --isys_complete_ext_device_registration(struct ipu7_isys *isys, -- struct v4l2_subdev *sd, -- struct ipu7_isys_csi2_config *csi2) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- unsigned int i; -- int ret; -- -- v4l2_set_subdev_hostdata(sd, csi2); -- -- for (i = 0; i < sd->entity.num_pads; i++) { -- if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -- break; -- } -- -- if (i == sd->entity.num_pads) { -- dev_warn(dev, "no source pad in external entity\n"); -- ret = -ENOENT; -- goto skip_unregister_subdev; -- } -- -- ret = media_create_pad_link(&sd->entity, i, -- &isys->csi2[csi2->port].asd.sd.entity, -- 0, MEDIA_LNK_FL_ENABLED | -- MEDIA_LNK_FL_IMMUTABLE); -- if (ret) { -- dev_warn(dev, "can't create link\n"); -- goto skip_unregister_subdev; -- } -- -- isys->csi2[csi2->port].nlanes = csi2->nlanes; -- if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) -- isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; -- else -- isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; -- -- return 0; -- --skip_unregister_subdev: -- v4l2_device_unregister_subdev(sd); -- return ret; --} -- --struct isys_i2c_test { -- u8 bus_nr; -- u16 addr; -- struct i2c_client *client; --}; -- --static int isys_i2c_test(struct device *dev, void *priv) --{ -- struct i2c_client *client = i2c_verify_client(dev); -- struct isys_i2c_test *test = priv; -- -- if (!client) -- return 0; -- -- if (i2c_adapter_id(client->adapter) != test->bus_nr || -- client->addr != test->addr) -- return 0; -- -- test->client = client; -- -- return 0; --} -- --static --struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -- struct ipu7_isys_subdev_info *sd_info) --{ -- struct i2c_board_info *info = &sd_info->i2c.board_info; -- struct isys_i2c_test test = { -- .bus_nr = i2c_adapter_id(adapter), -- .addr = info->addr, -- }; -- int ret; -- -- ret = i2c_for_each_dev(&test, isys_i2c_test); -- if (ret || !test.client) -- return NULL; -- return test.client; --} -- --static int isys_register_ext_subdev(struct ipu7_isys *isys, -- struct ipu7_isys_subdev_info *sd_info) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct i2c_adapter *adapter; -- struct v4l2_subdev *sd; -- struct i2c_client *client; -- int ret; -- int bus; -- -- bus = sd_info->i2c.i2c_adapter_id; -- adapter = i2c_get_adapter(bus); -- if (!adapter) { -- dev_warn(dev, "can't find adapter\n"); -- return -ENOENT; -- } -- -- dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -- sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -- bus); -- -- if (sd_info->csi2) { -- dev_info(dev, "sensor device on CSI port: %d\n", -- sd_info->csi2->port); -- if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -- !isys->csi2[sd_info->csi2->port].isys) { -- dev_warn(dev, "invalid csi2 port %u\n", -- sd_info->csi2->port); -- ret = -EINVAL; -- goto skip_put_adapter; -- } -- } else { -- dev_info(dev, "No camera subdevice\n"); -- } -- -- client = isys_find_i2c_subdev(adapter, sd_info); -- if (client) { -- dev_warn(dev, "Device exists\n"); --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -- /* TODO: remove i2c_unregister_device() */ -- i2c_unregister_device(client); --#else -- ret = 0; -- goto skip_put_adapter; --#endif -- } -- -- sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -- &sd_info->i2c.board_info, NULL); -- if (!sd) { -- dev_warn(dev, "can't create new i2c subdev\n"); -- ret = -EINVAL; -- goto skip_put_adapter; -- } -- -- if (!sd_info->csi2) -- return 0; -- -- return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -- --skip_put_adapter: -- i2c_put_adapter(adapter); -- -- return ret; --} -- --static void isys_register_ext_subdevs(struct ipu7_isys *isys) --{ -- struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -- struct ipu7_isys_subdev_info **sd_info; -- -- if (!spdata) { -- dev_info(&isys->adev->auxdev.dev, -- "no subdevice info provided\n"); -- return; -- } -- for (sd_info = spdata->subdevs; *sd_info; sd_info++) -- isys_register_ext_subdev(isys, *sd_info); --} -- --static void isys_stream_init(struct ipu7_isys *isys) --{ -- unsigned int i; -- -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- mutex_init(&isys->streams[i].mutex); -- init_completion(&isys->streams[i].stream_open_completion); -- init_completion(&isys->streams[i].stream_close_completion); -- init_completion(&isys->streams[i].stream_start_completion); -- init_completion(&isys->streams[i].stream_stop_completion); -- INIT_LIST_HEAD(&isys->streams[i].queues); -- isys->streams[i].isys = isys; -- isys->streams[i].stream_handle = i; -- isys->streams[i].vc = INVALID_VC_ID; -- } --} -- --static int isys_fw_log_init(struct ipu7_isys *isys) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct isys_fw_log *fw_log; -- void *log_buf; -- -- if (isys->fw_log) -- return 0; -- -- fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -- if (!fw_log) -- return -ENOMEM; -- -- mutex_init(&fw_log->mutex); -- -- log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!log_buf) -- return -ENOMEM; -- -- fw_log->head = log_buf; -- fw_log->addr = log_buf; -- fw_log->count = 0; -- fw_log->size = 0; -- -- isys->fw_log = fw_log; -- -- return 0; --} -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --/* The .bound() notifier callback when a match is found */ --static int isys_notifier_bound(struct v4l2_async_notifier *notifier, -- struct v4l2_subdev *sd, -- struct v4l2_async_connection *asc) --{ -- struct ipu7_isys *isys = container_of(notifier, -- struct ipu7_isys, notifier); -- struct sensor_async_sd *s_asd = -- container_of(asc, struct sensor_async_sd, asc); -- struct device *dev = &isys->adev->auxdev.dev; -- int ret; -- -- ret = ipu_bridge_instantiate_vcm(sd->dev); -- if (ret) { -- dev_err(dev, "instantiate vcm failed\n"); -- return ret; -- } -- -- dev_info(dev, "bind %s nlanes is %d port is %d\n", -- sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); -- isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); -- -- return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); --} -- --static int isys_notifier_complete(struct v4l2_async_notifier *notifier) --{ -- struct ipu7_isys *isys = container_of(notifier, -- struct ipu7_isys, notifier); -- -- dev_info(&isys->adev->auxdev.dev, -- "All sensor registration completed.\n"); -- -- return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); --} -- --static const struct v4l2_async_notifier_operations isys_async_ops = { -- .bound = isys_notifier_bound, -- .complete = isys_notifier_complete, --}; -- --static int isys_notifier_init(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2 = -- &isys->pdata->ipdata->csi2; -- struct ipu7_device *isp = isys->adev->isp; -- struct device *dev = &isp->pdev->dev; -- unsigned int i; -- int ret; -- -- v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); -- -- for (i = 0; i < csi2->nports; i++) { -- struct v4l2_fwnode_endpoint vep = { -- .bus_type = V4L2_MBUS_UNKNOWN -- }; -- struct sensor_async_sd *s_asd; -- struct fwnode_handle *ep; -- -- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, -- FWNODE_GRAPH_ENDPOINT_NEXT); -- if (!ep) -- continue; -- -- ret = v4l2_fwnode_endpoint_parse(ep, &vep); -- if (ret) -- goto err_parse; -- -- if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && -- vep.bus_type != V4L2_MBUS_CSI2_CPHY) { -- ret = -EINVAL; -- dev_err(dev, "unsupported bus type %d!\n", -- vep.bus_type); -- goto err_parse; -- } -- -- s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, -- struct -- sensor_async_sd); -- if (IS_ERR(s_asd)) { -- ret = PTR_ERR(s_asd); -- goto err_parse; -- } -- -- s_asd->csi2.port = vep.base.port; -- s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; -- s_asd->csi2.bus_type = vep.bus_type; -- -- fwnode_handle_put(ep); -- -- continue; -- --err_parse: -- fwnode_handle_put(ep); -- return ret; -- } -- -- if (list_empty(&isys->notifier.waiting_list)) { -- /* isys probe could continue with async subdevs missing */ -- dev_warn(dev, "no subdev found in graph\n"); -- return 0; -- } -- -- isys->notifier.ops = &isys_async_ops; -- ret = v4l2_async_nf_register(&isys->notifier); -- if (ret) { -- dev_err(dev, "failed to register async notifier(%d)\n", ret); -- v4l2_async_nf_cleanup(&isys->notifier); -- } -- -- return ret; --} -- --static void isys_notifier_cleanup(struct ipu7_isys *isys) --{ -- v4l2_async_nf_unregister(&isys->notifier); -- v4l2_async_nf_cleanup(&isys->notifier); --} --#endif -- --static void isys_unregister_video_devices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- unsigned int i, j; -- -- for (i = 0; i < csi2_pdata->nports; i++) -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) -- ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); --} -- --static int isys_register_video_devices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- unsigned int i, j; -- int ret; -- -- for (i = 0; i < csi2_pdata->nports; i++) { -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -- struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -- -- snprintf(av->vdev.name, sizeof(av->vdev.name), -- IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u", -- i * IPU7_NR_OF_CSI2_SRC_PADS + j); -- av->isys = isys; -- av->aq.vbq.buf_struct_size = -- sizeof(struct ipu7_isys_video_buffer); -- -- ret = ipu7_isys_video_init(av); -- if (ret) -- goto fail; -- } -- } -- -- return 0; -- --fail: -- i = i + 1U; -- while (i--) { -- while (j--) -- ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); -- j = IPU7_NR_OF_CSI2_SRC_PADS; -- } -- -- return ret; --} -- --static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2 = -- &isys->pdata->ipdata->csi2; -- unsigned int i; -- -- for (i = 0; i < csi2->nports; i++) -- ipu7_isys_csi2_cleanup(&isys->csi2[i]); --} -- --static int isys_csi2_register_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- unsigned int i; -- int ret; -- -- for (i = 0; i < csi2_pdata->nports; i++) { -- ret = ipu7_isys_csi2_init(&isys->csi2[i], isys, -- isys->pdata->base + -- csi2_pdata->offsets[i], i); -- if (ret) -- goto fail; -- } -- -- isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK; -- -- return 0; -- --fail: -- while (i--) -- ipu7_isys_csi2_cleanup(&isys->csi2[i]); -- -- return ret; --} -- --static int isys_csi2_create_media_links(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- struct device *dev = &isys->adev->auxdev.dev; -- struct media_entity *sd; -- unsigned int i, j; -- int ret; -- -- for (i = 0; i < csi2_pdata->nports; i++) { -- sd = &isys->csi2[i].asd.sd.entity; -- -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -- struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -- -- ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j, -- &av->vdev.entity, 0, 0); -- if (ret) { -- dev_err(dev, "CSI2 can't create link\n"); -- return ret; -- } -- -- av->csi2 = &isys->csi2[i]; -- } -- } -- -- return 0; --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --static void isys_tpg_unregister_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -- &isys->pdata->ipdata->tpg; -- unsigned int i; -- -- if (!isys->tpg) -- return; -- -- for (i = 0; i < tpg_pdata->ntpgs; i++) -- ipu7_isys_tpg_cleanup(&isys->tpg[i]); -- -- kfree(isys->tpg); -- isys->tpg = NULL; --} -- --/* --static int isys_tpg_register_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -- &isys->pdata->ipdata->tpg; -- unsigned int i; -- int ret; -- -- isys->tpg = kcalloc(tpg_pdata->ntpgs, sizeof(*isys->tpg), GFP_KERNEL); -- if (!isys->tpg) -- return -ENOMEM; -- -- for (i = 0; i < tpg_pdata->ntpgs; i++) { -- ret = ipu7_isys_tpg_init(&isys->tpg[i], isys, -- isys->pdata->base + -- tpg_pdata->offsets[i], -- tpg_pdata->sels ? -- (isys->pdata->base + -- tpg_pdata->sels[i]) : NULL, i); -- if (ret) -- goto fail; -- } -- -- return 0; -- --fail: -- while (i--) -- ipu7_isys_tpg_cleanup(&isys->tpg[i]); -- -- kfree(isys->tpg); -- isys->tpg = NULL; -- -- return ret; --} -- --static int isys_tpg_create_media_links(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -- &isys->pdata->ipdata->tpg; -- struct device *dev = &isys->adev->auxdev.dev; -- struct ipu7_isys_tpg *tpg; -- struct media_entity *sd; -- unsigned int i; -- int ret; -- -- for (i = 0; i < tpg_pdata->ntpgs; i++) { -- tpg = &isys->tpg[i]; -- sd = &tpg->asd.sd.entity; -- tpg->av = &isys->csi2[tpg->index].av[0]; -- -- ret = media_create_pad_link(sd, TPG_PAD_SOURCE, -- &tpg->av->vdev.entity, -- TPG_PAD_SOURCE, 0); -- if (ret) { -- dev_err(dev, "TPG can't create link\n"); -- return ret; -- } -- } -- -- return 0; --} --*/ --#endif -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --static int isys_register_devices(struct ipu7_isys *isys) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct pci_dev *pdev = isys->adev->isp->pdev; -- int ret; -- -- media_device_pci_init(&isys->media_dev, -- pdev, IPU_MEDIA_DEV_MODEL_NAME); -- -- strscpy(isys->v4l2_dev.name, isys->media_dev.model, -- sizeof(isys->v4l2_dev.name)); -- -- ret = media_device_register(&isys->media_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- isys->v4l2_dev.mdev = &isys->media_dev; -- isys->v4l2_dev.ctrl_handler = NULL; -- -- ret = v4l2_device_register(dev, &isys->v4l2_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- ret = isys_register_video_devices(isys); -- if (ret) -- goto out_v4l2_device_unregister; -- -- ret = isys_csi2_register_subdevices(isys); -- if (ret) -- goto out_video_unregister_device; -- -- ret = isys_csi2_create_media_links(isys); -- if (ret) -- goto out_csi2_unregister_subdevices; -- -- if (!isys->pdata->spdata) { -- ret = isys_notifier_init(isys); -- if (ret) -- goto out_csi2_unregister_subdevices; -- } else { -- isys_register_ext_subdevs(isys); -- ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -- if (ret) -- goto out_csi2_unregister_subdevices; -- } -- -- return 0; -- --out_csi2_unregister_subdevices: -- isys_csi2_unregister_subdevices(isys); -- --out_video_unregister_device: -- isys_unregister_video_devices(isys); -- --out_v4l2_device_unregister: -- v4l2_device_unregister(&isys->v4l2_dev); -- --out_media_device_unregister: -- media_device_unregister(&isys->media_dev); -- media_device_cleanup(&isys->media_dev); -- -- dev_err(dev, "failed to register isys devices\n"); -- -- return ret; --} --#else --static int isys_register_devices(struct ipu7_isys *isys) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct pci_dev *pdev = isys->adev->isp->pdev; -- int ret; -- -- media_device_pci_init(&isys->media_dev, -- pdev, IPU_MEDIA_DEV_MODEL_NAME); -- -- strscpy(isys->v4l2_dev.name, isys->media_dev.model, -- sizeof(isys->v4l2_dev.name)); -- -- ret = media_device_register(&isys->media_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- isys->v4l2_dev.mdev = &isys->media_dev; -- isys->v4l2_dev.ctrl_handler = NULL; -- -- ret = v4l2_device_register(dev, &isys->v4l2_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- ret = isys_register_video_devices(isys); -- if (ret) -- goto out_v4l2_device_unregister; -- -- ret = isys_csi2_register_subdevices(isys); -- if (ret) -- goto out_video_unregister_device; -- -- ret = isys_csi2_create_media_links(isys); -- if (ret) -- goto out_csi2_unregister_subdevices; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- ret = isys_tpg_register_subdevices(isys); -- if (!ret) -- ret = isys_tpg_create_media_links(isys); -- -- if (ret) -- goto out_tpg_unregister_subdevices; -- --#endif -- -- isys_register_ext_subdevs(isys); -- ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -- if (ret) --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- goto out_tpg_unregister_subdevices; --#else -- goto out_csi2_unregister_subdevices; --#endif -- -- return 0; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --out_tpg_unregister_subdevices: -- isys_tpg_unregister_subdevices(isys); --#endif --out_csi2_unregister_subdevices: -- isys_csi2_unregister_subdevices(isys); -- --out_video_unregister_device: -- isys_unregister_video_devices(isys); -- --out_v4l2_device_unregister: -- v4l2_device_unregister(&isys->v4l2_dev); -- --out_media_device_unregister: -- media_device_unregister(&isys->media_dev); -- media_device_cleanup(&isys->media_dev); -- -- dev_err(dev, "failed to register isys devices\n"); -- -- return ret; --} --#endif -- --static void isys_unregister_devices(struct ipu7_isys *isys) --{ -- isys_unregister_video_devices(isys); -- isys_csi2_unregister_subdevices(isys); --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- isys_tpg_unregister_subdevices(isys); --#endif -- v4l2_device_unregister(&isys->v4l2_dev); -- media_device_unregister(&isys->media_dev); -- media_device_cleanup(&isys->media_dev); --} -- --static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable) --{ -- u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -- void __iomem *base = isys->pdata->base; -- u32 mask = isys->isr_csi2_mask; -- -- if (!enable) { -- writel(mask, base + offset + IRQ_CTL_CLEAR); -- writel(0, base + offset + IRQ_CTL_ENABLE); -- return; -- } -- -- writel(mask, base + offset + IRQ_CTL_EDGE); -- writel(mask, base + offset + IRQ_CTL_CLEAR); -- writel(mask, base + offset + IRQ_CTL_MASK); -- writel(mask, base + offset + IRQ_CTL_ENABLE); --} -- --static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable) --{ -- void __iomem *base = isys->pdata->base; -- u32 mask = IS_UC_TO_SW_IRQ_MASK; -- u32 offset = IS_UC_CTRL_BASE; -- -- if (!enable) { -- writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE); -- return; -- } -- -- writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR); -- writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N); -- writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE); --} -- --void ipu7_isys_setup_hw(struct ipu7_isys *isys) --{ -- u32 offset; -- void __iomem *base = isys->pdata->base; -- -- /* soft reset */ -- offset = IS_IO_GPREGS_BASE; -- -- writel(0x0, base + offset + CLK_EN_TXCLKESC); -- /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */ -- writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK); -- /* correct the initial printf configuration */ -- writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -- -- enable_to_sw_irq(isys, 1); -- enable_csi2_legacy_irq(isys, 1); --} -- --static void isys_cleanup_hw(struct ipu7_isys *isys) --{ -- enable_csi2_legacy_irq(isys, 0); -- enable_to_sw_irq(isys, 0); --} -- --static int isys_runtime_pm_resume(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- struct ipu7_device *isp = adev->isp; -- unsigned long flags; -- int ret; -- -- if (!isys) -- return 0; -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- return ret; -- -- cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); -- -- ret = ipu_buttress_start_tsc_sync(isp); -- if (ret) -- return ret; -- -- spin_lock_irqsave(&isys->power_lock, flags); -- isys->power = 1; -- spin_unlock_irqrestore(&isys->power_lock, flags); -- -- return 0; --} -- --static int isys_runtime_pm_suspend(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- unsigned long flags; -- -- if (!isys) -- return 0; -- -- isys_cleanup_hw(isys); -- -- spin_lock_irqsave(&isys->power_lock, flags); -- isys->power = 0; -- spin_unlock_irqrestore(&isys->power_lock, flags); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_lock(&isys->reset_mutex); -- isys->need_reset = false; -- mutex_unlock(&isys->reset_mutex); -- --#endif -- cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- -- return 0; --} -- --static int isys_suspend(struct device *dev) --{ -- struct ipu7_isys *isys = dev_get_drvdata(dev); -- -- /* If stream is open, refuse to suspend */ -- if (isys->stream_opened) -- return -EBUSY; -- -- return 0; --} -- --static int isys_resume(struct device *dev) --{ -- return 0; --} -- --static const struct dev_pm_ops isys_pm_ops = { -- .runtime_suspend = isys_runtime_pm_suspend, -- .runtime_resume = isys_runtime_pm_resume, -- .suspend = isys_suspend, -- .resume = isys_resume, --}; -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --static void isys_remove(struct auxiliary_device *auxdev) --{ -- struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -- struct isys_fw_msgs *fwmsg, *safe; -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- --#ifdef CONFIG_DEBUG_FS -- if (adev->isp->ipu7_dir) -- debugfs_remove_recursive(isys->debugfsdir); --#endif -- for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -- mutex_destroy(&isys->streams[i].mutex); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- if (!isys->pdata->spdata) -- isys_notifier_cleanup(isys); -- -- isys_unregister_devices(isys); -- -- cpu_latency_qos_remove_request(&isys->pm_qos); -- -- mutex_destroy(&isys->stream_mutex); -- mutex_destroy(&isys->mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_destroy(&isys->reset_mutex); --#endif --} --#else --static void isys_remove(struct auxiliary_device *auxdev) --{ -- struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -- struct isys_fw_msgs *fwmsg, *safe; -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- --#ifdef CONFIG_DEBUG_FS -- if (adev->isp->ipu7_dir) -- debugfs_remove_recursive(isys->debugfsdir); --#endif -- for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -- mutex_destroy(&isys->streams[i].mutex); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- isys_unregister_devices(isys); -- -- cpu_latency_qos_remove_request(&isys->pm_qos); -- -- mutex_destroy(&isys->stream_mutex); -- mutex_destroy(&isys->mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_destroy(&isys->reset_mutex); --#endif --} --#endif -- --#ifdef CONFIG_DEBUG_FS --static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -- loff_t *pos) --{ -- struct ipu7_isys *isys = file->private_data; -- struct isys_fw_log *fw_log = isys->fw_log; -- struct device *dev = &isys->adev->auxdev.dev; -- u32 log_size; -- int ret = 0; -- void *buf; -- -- if (!fw_log) -- return 0; -- -- buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!buf) -- return -ENOMEM; -- -- mutex_lock(&fw_log->mutex); -- if (!fw_log->size) { -- dev_warn(dev, "no available fw log\n"); -- mutex_unlock(&fw_log->mutex); -- goto free_and_return; -- } -- -- if (fw_log->size > FW_LOG_BUF_SIZE) -- log_size = FW_LOG_BUF_SIZE; -- else -- log_size = fw_log->size; -- -- memcpy(buf, fw_log->addr, log_size); -- dev_info(dev, "copy %d bytes fw log to user...\n", log_size); -- mutex_unlock(&fw_log->mutex); -- -- ret = simple_read_from_buffer(userbuf, size, pos, buf, -- log_size); --free_and_return: -- kvfree(buf); -- -- return ret; --} -- --static const struct file_operations isys_fw_log_fops = { -- .open = simple_open, -- .owner = THIS_MODULE, -- .read = fwlog_read, -- .llseek = default_llseek, --}; -- --static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) --{ -- struct dentry *file; -- struct dentry *dir; -- -- dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); -- if (IS_ERR(dir)) -- return -ENOMEM; -- -- file = debugfs_create_file("fwlog", 0400, -- dir, isys, &isys_fw_log_fops); -- if (IS_ERR(file)) -- goto err; -- -- isys->debugfsdir = dir; -- -- return 0; --err: -- debugfs_remove_recursive(dir); -- return -ENOMEM; --} --#endif -- --static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) --{ -- struct ipu7_bus_device *adev = isys->adev; -- struct isys_fw_msgs *addr; -- dma_addr_t dma_addr; -- unsigned long flags; -- unsigned int i; -- -- for (i = 0; i < amount; i++) { -- addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs), -- &dma_addr, GFP_KERNEL, 0); -- if (!addr) -- break; -- addr->dma_addr = dma_addr; -- -- spin_lock_irqsave(&isys->listlock, flags); -- list_add(&addr->head, &isys->framebuflist); -- spin_unlock_irqrestore(&isys->listlock, flags); -- } -- -- if (i == amount) -- return 0; -- -- spin_lock_irqsave(&isys->listlock, flags); -- while (!list_empty(&isys->framebuflist)) { -- addr = list_first_entry(&isys->framebuflist, -- struct isys_fw_msgs, head); -- list_del(&addr->head); -- spin_unlock_irqrestore(&isys->listlock, flags); -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- addr, addr->dma_addr, 0); -- spin_lock_irqsave(&isys->listlock, flags); -- } -- spin_unlock_irqrestore(&isys->listlock, flags); -- -- return -ENOMEM; --} -- --struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) --{ -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct ipu7_isys *isys = stream->isys; -- struct isys_fw_msgs *msg; -- unsigned long flags; -- int ret; -- -- spin_lock_irqsave(&isys->listlock, flags); -- if (list_empty(&isys->framebuflist)) { -- spin_unlock_irqrestore(&isys->listlock, flags); -- dev_dbg(dev, "Frame buffer list empty\n"); -- -- ret = alloc_fw_msg_bufs(isys, 5); -- if (ret < 0) -- return NULL; -- -- spin_lock_irqsave(&isys->listlock, flags); -- if (list_empty(&isys->framebuflist)) { -- spin_unlock_irqrestore(&isys->listlock, flags); -- dev_err(dev, "Frame list empty\n"); -- return NULL; -- } -- } -- msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); -- list_move(&msg->head, &isys->framebuflist_fw); -- spin_unlock_irqrestore(&isys->listlock, flags); -- memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); -- -- return msg; --} -- --void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys) --{ -- struct isys_fw_msgs *fwmsg, *fwmsg0; -- unsigned long flags; -- -- spin_lock_irqsave(&isys->listlock, flags); -- list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) -- list_move(&fwmsg->head, &isys->framebuflist); -- spin_unlock_irqrestore(&isys->listlock, flags); --} -- --void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data) --{ -- struct isys_fw_msgs *msg; -- void *ptr = (void *)data; -- unsigned long flags; -- -- if (WARN_ON_ONCE(!ptr)) -- return; -- -- spin_lock_irqsave(&isys->listlock, flags); -- msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); -- list_move(&msg->head, &isys->framebuflist); -- spin_unlock_irqrestore(&isys->listlock, flags); --} -- --static int isys_probe(struct auxiliary_device *auxdev, -- const struct auxiliary_device_id *auxdev_id) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata; -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- struct ipu7_device *isp = adev->isp; -- struct ipu7_isys *isys; -- int ret = 0; -- -- if (!isp->ipu7_bus_ready_to_probe) -- return -EPROBE_DEFER; -- -- isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); -- if (!isys) -- return -ENOMEM; -- -- ret = pm_runtime_resume_and_get(&auxdev->dev); -- if (ret < 0) -- return ret; -- -- adev->auxdrv_data = -- (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -- adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); -- isys->adev = adev; -- isys->pdata = adev->pdata; -- -- INIT_LIST_HEAD(&isys->requests); -- csi2_pdata = &isys->pdata->ipdata->csi2; -- -- isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports, -- sizeof(*isys->csi2), GFP_KERNEL); -- if (!isys->csi2) { -- ret = -ENOMEM; -- goto out_runtime_put; -- } -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- goto out_runtime_put; -- -- spin_lock_init(&isys->streams_lock); -- spin_lock_init(&isys->power_lock); -- isys->power = 0; -- -- mutex_init(&isys->mutex); -- mutex_init(&isys->stream_mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_init(&isys->reset_mutex); -- isys->state = 0; --#endif -- -- spin_lock_init(&isys->listlock); -- INIT_LIST_HEAD(&isys->framebuflist); -- INIT_LIST_HEAD(&isys->framebuflist_fw); -- -- dev_set_drvdata(&auxdev->dev, isys); -- -- isys->icache_prefetch = 0; -- isys->phy_rext_cal = 0; -- -- isys_stream_init(isys); -- --#ifdef CONFIG_DEBUG_FS -- /* Debug fs failure is not fatal. */ -- ipu7_isys_init_debugfs(isys); --#endif -- -- cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -- ret = alloc_fw_msg_bufs(isys, 40); -- if (ret < 0) -- goto out_cleanup_isys; -- -- ret = ipu7_fw_isys_init(isys); -- if (ret) -- goto out_cleanup_isys; -- -- ret = isys_register_devices(isys); -- if (ret) -- goto out_cleanup_fw; -- -- ret = isys_fw_log_init(isys); -- if (ret) -- goto out_cleanup; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_destroy(&isys->reset_mutex); --#endif -- ipu7_mmu_hw_cleanup(adev->mmu); -- pm_runtime_put(&auxdev->dev); -- -- return 0; -- --out_cleanup: -- isys_unregister_devices(isys); --out_cleanup_fw: -- ipu7_fw_isys_release(isys); --out_cleanup_isys: -- cpu_latency_qos_remove_request(&isys->pm_qos); -- -- for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -- mutex_destroy(&isys->streams[i].mutex); -- -- mutex_destroy(&isys->mutex); -- mutex_destroy(&isys->stream_mutex); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- --out_runtime_put: -- pm_runtime_put(&auxdev->dev); -- -- return ret; --} -- --struct ipu7_csi2_error { -- const char *error_string; -- bool is_info_only; --}; -- --/* -- * Strings corresponding to CSI-2 receiver errors are here. -- * Corresponding macros are defined in the header file. -- */ --static const struct ipu7_csi2_error dphy_rx_errors[] = { -- { "Error handler FIFO full", false }, -- { "Reserved Short Packet encoding detected", true }, -- { "Reserved Long Packet encoding detected", true }, -- { "Received packet is too short", false}, -- { "Received packet is too long", false}, -- { "Short packet discarded due to errors", false }, -- { "Long packet discarded due to errors", false }, -- { "CSI Combo Rx interrupt", false }, -- { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false }, -- { "Received NULL packet", true }, -- { "Received blanking packet", true }, -- { "Tie to 0", true }, -- { } --}; -- --static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2) --{ -- u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -- u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS); -- u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -- -- if (!status) -- return; -- -- dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n", -- csi2->port, status); -- -- writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR); -- csi2->receiver_errors |= status & mask; --} -- --static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_csi2_error const *errors; -- unsigned int i; -- u32 status; -- -- /* Register errors once more in case of error interrupts are disabled */ -- ipu7_isys_register_errors(csi2); -- status = csi2->receiver_errors; -- csi2->receiver_errors = 0; -- errors = dphy_rx_errors; -- -- for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { -- if (status & BIT(i)) -- dev_err_ratelimited(&csi2->isys->adev->auxdev.dev, -- "csi2-%i error: %s\n", -- csi2->port, -- errors[i].error_string); -- } --} -- --struct resp_to_msg { -- enum ipu7_insys_resp_type type; -- const char *msg; --}; -- --static const struct resp_to_msg is_fw_msg[] = { -- {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE, -- "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"}, -- {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"}, -- {IPU_INSYS_RESP_TYPE_PIN_DATA_READY, -- "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"}, -- {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"}, -- {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"}, -- {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, -- "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"}, -- {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, -- "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"}, -- {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"}, --}; -- --int isys_isr_one(struct ipu7_bus_device *adev) --{ -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- struct ipu7_isys_stream *stream = NULL; -- struct device *dev = &adev->auxdev.dev; -- struct ipu7_isys_csi2 *csi2 = NULL; -- struct ia_gofo_msg_err err_info; -- struct ipu7_insys_resp *resp; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- struct ipu7_isys_tpg *tpg = NULL; --#endif -- u64 ts; -- -- if (!isys->adev->syscom) -- return 1; -- --#ifdef ENABLE_FW_OFFLINE_LOGGER -- ipu7_fw_isys_get_log(isys); --#endif -- -- resp = ipu7_fw_isys_get_resp(isys); -- if (!resp) -- return 1; -- if (resp->type >= N_IPU_INSYS_RESP_TYPE) { -- dev_err(dev, "Unknown response type %u stream %u\n", -- resp->type, resp->stream_id); -- ipu7_fw_isys_put_resp(isys); -- return 1; -- } -- -- err_info = resp->error_info; -- ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0]; -- if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE && -- err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) { -- /* receive a sp w/o command, firmware drop it */ -- dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n", -- resp->type, is_fw_msg[resp->type].msg, -- resp->stream_id); -- dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n", -- resp->pin_id, resp->buf_id, resp->frame_id); -- dev_dbg(dev, "\terror group %u code %u details [%u %u]\n", -- err_info.err_group, err_info.err_code, -- err_info.err_detail[0], err_info.err_detail[1]); -- } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) { -- dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -- resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -- resp->pin_id, resp->buf_id, resp->frame_id); -- dev_err(dev, "\terror group %u code %u details [%u %u]\n", -- err_info.err_group, err_info.err_code, -- err_info.err_detail[0], err_info.err_detail[1]); -- } else { -- dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -- resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -- resp->pin_id, resp->buf_id, resp->frame_id); -- dev_dbg(dev, "\tts %llu\n", ts); -- } -- -- if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) { -- dev_err(dev, "bad stream handle %u\n", -- resp->stream_id); -- goto leave; -- } -- -- stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id); -- if (!stream) { -- dev_err(dev, "stream of stream_handle %u is unused\n", -- resp->stream_id); -- goto leave; -- } -- -- stream->error = err_info.err_code; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (stream->asd) { -- if (stream->asd->is_tpg) -- tpg = ipu7_isys_subdev_to_tpg(stream->asd); -- else -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- } --#else -- if (stream->asd) -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); --#endif -- -- switch (resp->type) { -- case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE: -- complete(&stream->stream_open_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK: -- complete(&stream->stream_close_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: -- complete(&stream->stream_start_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK: -- complete(&stream->stream_stop_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK: -- complete(&stream->stream_stop_completion); -- break; -- case IPU_INSYS_RESP_TYPE_PIN_DATA_READY: -- /* -- * firmware only release the capture msg until software -- * get pin_data_ready event -- */ -- ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id); -- if (resp->pin_id < IPU_INSYS_OUTPUT_PINS && -- stream->output_pins[resp->pin_id].pin_ready) -- stream->output_pins[resp->pin_id].pin_ready(stream, -- resp); -- else -- dev_err(dev, "No handler for pin %u ready\n", -- resp->pin_id); -- if (csi2) -- ipu7_isys_csi2_error(csi2); -- -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK: -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: -- case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE: -- break; -- case IPU_INSYS_RESP_TYPE_FRAME_SOF: -- if (csi2) -- ipu7_isys_csi2_sof_event_by_stream(stream); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (tpg) -- ipu7_isys_tpg_sof_event_by_stream(stream); -- --#endif -- stream->seq[stream->seq_index].sequence = -- atomic_read(&stream->sequence) - 1U; -- stream->seq[stream->seq_index].timestamp = ts; -- dev_dbg(dev, -- "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n", -- resp->stream_id, resp->frame_id, -- stream->seq[stream->seq_index].sequence, ts); -- stream->seq_index = (stream->seq_index + 1U) -- % IPU_ISYS_MAX_PARALLEL_SOF; -- break; -- case IPU_INSYS_RESP_TYPE_FRAME_EOF: -- if (csi2) -- ipu7_isys_csi2_eof_event_by_stream(stream); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (tpg) -- ipu7_isys_tpg_eof_event_by_stream(stream); -- --#endif -- dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n", -- resp->stream_id, -- stream->seq[stream->seq_index].sequence, ts); -- break; -- default: -- dev_err(dev, "Unknown response type %u stream %u\n", -- resp->type, resp->stream_id); -- break; -- } -- -- ipu7_isys_put_stream(stream); --leave: -- ipu7_fw_isys_put_resp(isys); -- -- return 0; --} -- --static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2) --{ -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- struct ipu7_device *isp = csi2->isys->adev->isp; -- struct ipu7_isys_stream *s; -- u32 sync, offset; -- u32 fe = 0; -- u8 vc; -- -- ipu7_isys_register_errors(csi2); -- -- offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -- sync = readl(csi2->base + offset + IRQ_CTL_STATUS); -- writel(sync, csi2->base + offset + IRQ_CTL_CLEAR); -- dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync); -- -- if (!is_ipu7(isp->hw_ver)) { -- fe = readl(csi2->base + offset + IRQ1_CTL_STATUS); -- writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR); -- dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe); -- } -- -- for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) { -- s = ipu7_isys_query_stream_by_source(csi2->isys, -- csi2->asd.source, vc); -- if (!s) -- continue; -- -- if (!is_ipu7(isp->hw_ver)) { -- if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc)) -- ipu7_isys_csi2_sof_event_by_stream(s); -- -- if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc)) -- ipu7_isys_csi2_eof_event_by_stream(s); -- } else { -- if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2))) -- ipu7_isys_csi2_sof_event_by_stream(s); -- -- if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2))) -- ipu7_isys_csi2_eof_event_by_stream(s); -- } -- } --} -- --static irqreturn_t isys_isr(struct ipu7_bus_device *adev) --{ -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- u32 status_csi, status_sw, csi_offset, sw_offset; -- struct device *dev = &isys->adev->auxdev.dev; -- void __iomem *base = isys->pdata->base; -- -- spin_lock(&isys->power_lock); -- if (!isys->power) { -- spin_unlock(&isys->power_lock); -- return IRQ_NONE; -- } -- -- csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -- sw_offset = IS_BASE; -- -- status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -- status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -- if (!status_csi && !status_sw) { -- spin_unlock(&isys->power_lock); -- return IRQ_NONE; -- } -- -- if (status_csi) -- dev_dbg(dev, "status csi 0x%08x\n", status_csi); -- if (status_sw) -- dev_dbg(dev, "status to_sw 0x%08x\n", status_sw); -- -- do { -- writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR); -- writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR); -- -- if (isys->isr_csi2_mask & status_csi) { -- unsigned int i; -- -- for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { -- /* irq from not enabled port */ -- if (!isys->csi2[i].base) -- continue; -- if (status_csi & isys->csi2[i].legacy_irq_mask) -- ipu7_isys_csi2_isr(&isys->csi2[i]); -- } -- } -- -- if (!isys_isr_one(adev)) -- status_sw = TO_SW_IRQ_FW; -- else -- status_sw = 0; -- -- status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -- status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -- } while ((status_csi & isys->isr_csi2_mask) || -- (status_sw & TO_SW_IRQ_FW)); -- -- writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N); -- -- spin_unlock(&isys->power_lock); -- -- return IRQ_HANDLED; --} -- --static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = { -- .isr = isys_isr, -- .isr_threaded = NULL, -- .wake_isr_thread = false, --}; -- --static const struct auxiliary_device_id ipu7_isys_id_table[] = { -- { -- .name = "intel_ipu7.isys", -- .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data, -- }, -- { } --}; --MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table); -- --static struct auxiliary_driver isys_driver = { -- .name = IPU_ISYS_NAME, -- .probe = isys_probe, -- .remove = isys_remove, -- .id_table = ipu7_isys_id_table, -- .driver = { -- .pm = &isys_pm_ops, -- }, --}; -- --module_auxiliary_driver(isys_driver); -- --MODULE_AUTHOR("Bingbu Cao "); --MODULE_AUTHOR("Tianshu Qiu "); --MODULE_AUTHOR("Qingwu Zhang "); --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION("Intel ipu7 input system driver"); --MODULE_IMPORT_NS("INTEL_IPU7"); --MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.h b/drivers/media/pci/intel/ipu7/ipu7-isys.h -deleted file mode 100644 -index c9ca2fbb4d1e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys.h -+++ /dev/null -@@ -1,187 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_H --#define IPU7_ISYS_H -- --#include --#include --#include --#include --#include --#include -- --#include --#include --#include --#include -- --#include "abi/ipu7_fw_msg_abi.h" --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7.h" --#include "ipu7-isys-csi2.h" --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --#include "ipu7-isys-tpg.h" --#endif --#include "ipu7-isys-video.h" -- --#ifdef CONFIG_DEBUG_FS --struct dentry; -- --#endif --#define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" -- --/* FW support max 16 streams */ --#define IPU_ISYS_MAX_STREAMS 16U -- --/* -- * Current message queue configuration. These must be big enough -- * so that they never gets full. Queues are located in system memory -- */ --#define IPU_ISYS_SIZE_RECV_QUEUE 40U --#define IPU_ISYS_SIZE_LOG_QUEUE 256U --#define IPU_ISYS_SIZE_SEND_QUEUE 40U --#define IPU_ISYS_NUM_RECV_QUEUE 1U -- --#define IPU_ISYS_MIN_WIDTH 2U --#define IPU_ISYS_MIN_HEIGHT 2U --#define IPU_ISYS_MAX_WIDTH 8160U --#define IPU_ISYS_MAX_HEIGHT 8190U -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#define RESET_STATE_IN_RESET 1U --#define RESET_STATE_IN_STOP_STREAMING 2U -- --#endif --#define FW_CALL_TIMEOUT_JIFFIES \ -- msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) --#endif -- --struct isys_fw_log { -- struct mutex mutex; /* protect whole struct */ -- void *head; -- void *addr; -- u32 count; /* running counter of log */ -- u32 size; /* actual size of log content, in bits */ --}; -- --/* -- * struct ipu7_isys -- * -- * @media_dev: Media device -- * @v4l2_dev: V4L2 device -- * @adev: ISYS bus device -- * @power: Is ISYS powered on or not? -- * @isr_bits: Which bits does the ISR handle? -- * @power_lock: Serialise access to power (power state in general) -- * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers -- * @streams_lock: serialise access to streams -- * @streams: streams per firmware stream ID -- * @syscom: fw communication layer context -- #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- * @need_reset: Isys requires d0i0->i3 transition -- #endif -- * @ref_count: total number of callers fw open -- * @mutex: serialise access isys video open/release related operations -- * @stream_mutex: serialise stream start and stop, queueing requests -- * @pdata: platform data pointer -- * @csi2: CSI-2 receivers -- #ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- * @tpg: test pattern generators -- #endif -- */ --struct ipu7_isys { -- struct media_device media_dev; -- struct v4l2_device v4l2_dev; -- struct ipu7_bus_device *adev; -- -- int power; -- spinlock_t power_lock; /* Serialise access to power */ -- u32 isr_csi2_mask; -- u32 csi2_rx_ctrl_cached; -- spinlock_t streams_lock; -- struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS]; -- int streams_ref_count[IPU_ISYS_MAX_STREAMS]; -- u32 phy_rext_cal; -- bool icache_prefetch; -- bool csi2_cse_ipc_not_supported; -- unsigned int ref_count; -- unsigned int stream_opened; -- --#ifdef CONFIG_DEBUG_FS -- struct dentry *debugfsdir; --#endif -- struct mutex mutex; /* Serialise isys video open/release related */ -- struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ -- -- struct ipu7_isys_pdata *pdata; -- -- struct ipu7_isys_csi2 *csi2; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- struct ipu7_isys_tpg *tpg; --#endif -- struct isys_fw_log *fw_log; -- -- struct list_head requests; -- struct pm_qos_request pm_qos; -- spinlock_t listlock; /* Protect framebuflist */ -- struct list_head framebuflist; -- struct list_head framebuflist_fw; -- struct v4l2_async_notifier notifier; -- -- struct ipu7_insys_config *subsys_config; -- dma_addr_t subsys_config_dma_addr; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- struct mutex reset_mutex; -- bool need_reset; -- int state; --#endif --}; -- --struct isys_fw_msgs { -- union { -- u64 dummy; -- struct ipu7_insys_buffset frame; -- struct ipu7_insys_stream_cfg stream; -- } fw_msg; -- struct list_head head; -- dma_addr_t dma_addr; --}; -- --struct ipu7_isys_csi2_config { -- unsigned int nlanes; -- unsigned int port; -- enum v4l2_mbus_type bus_type; --}; -- --struct ipu7_isys_subdev_i2c_info { -- struct i2c_board_info board_info; -- int i2c_adapter_id; -- char i2c_adapter_bdf[32]; --}; -- --struct ipu7_isys_subdev_info { -- struct ipu7_isys_csi2_config *csi2; -- struct ipu7_isys_subdev_i2c_info i2c; --}; -- --struct ipu7_isys_subdev_pdata { -- struct ipu7_isys_subdev_info **subdevs; --}; -- --struct sensor_async_sd { -- struct v4l2_async_connection asc; -- struct ipu7_isys_csi2_config csi2; --}; -- --struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream); --void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data); --void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys); --int isys_isr_one(struct ipu7_bus_device *adev); --void ipu7_isys_setup_hw(struct ipu7_isys *isys); --#endif /* IPU7_ISYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.c b/drivers/media/pci/intel/ipu7/ipu7-mmu.c -deleted file mode 100644 -index e6989e3e59a1..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-mmu.c -+++ /dev/null -@@ -1,854 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-dma.h" --#include "ipu7-mmu.h" --#include "ipu7-platform-regs.h" -- --#define ISP_PAGE_SHIFT 12 --#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) --#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1U)) -- --#define ISP_L1PT_SHIFT 22 --#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) -- --#define ISP_L2PT_SHIFT 12 --#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) -- --#define ISP_L1PT_PTES 1024U --#define ISP_L2PT_PTES 1024U -- --#define ISP_PADDR_SHIFT 12 -- --#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ --#define REG_INFO 0x0008 -- --#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) -- --#define MMU_TLB_INVALIDATE_TIMEOUT 2000 -- --static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) --{ -- unsigned int i; -- u32 irq_cause; -- -- for (i = 0; i < mmu->nr_mmus; i++) { -- irq_cause = readl(mmu->mmu_hw[i].base + MMU_REG_IRQ_CAUSE); -- pr_info("mmu %s irq_cause = 0x%x", mmu->mmu_hw[i].name, -- irq_cause); -- writel(0x1ffff, mmu->mmu_hw[i].base + MMU_REG_IRQ_CLEAR); -- } --} -- --static void tlb_invalidate(struct ipu7_mmu *mmu) --{ -- unsigned long flags; -- unsigned int i; -- int ret; -- u32 val; -- -- spin_lock_irqsave(&mmu->ready_lock, flags); -- if (!mmu->ready) { -- spin_unlock_irqrestore(&mmu->ready_lock, flags); -- return; -- } -- -- for (i = 0; i < mmu->nr_mmus; i++) { -- writel(0xffffffffU, mmu->mmu_hw[i].base + -- MMU_REG_INVALIDATE_0); -- -- /* Need check with HW, use l1streams or l2streams */ -- if (mmu->mmu_hw[i].nr_l2streams > 32) -- writel(0xffffffffU, mmu->mmu_hw[i].base + -- MMU_REG_INVALIDATE_1); -- -- /* -- * The TLB invalidation is a "single cycle" (IOMMU clock cycles) -- * When the actual MMIO write reaches the IPU TLB Invalidate -- * register, wmb() will force the TLB invalidate out if the CPU -- * attempts to update the IOMMU page table (or sooner). -- */ -- wmb(); -- -- /* wait invalidation done */ -- ret = readl_poll_timeout_atomic(mmu->mmu_hw[i].base + -- MMU_REG_INVALIDATION_STATUS, -- val, !(val & 0x1U), 500, -- MMU_TLB_INVALIDATE_TIMEOUT); -- if (ret) -- dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i); -- } -- -- spin_unlock_irqrestore(&mmu->ready_lock, flags); --} -- --static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr) --{ -- dma_addr_t dma; -- -- dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); -- if (dma_mapping_error(mmu_info->dev, dma)) -- return 0; -- -- return dma; --} -- --static int get_dummy_page(struct ipu7_mmu_info *mmu_info) --{ -- void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- dma_addr_t dma; -- -- if (!pt) -- return -ENOMEM; -- -- dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); -- -- dma = map_single(mmu_info, pt); -- if (!dma) { -- dev_err(mmu_info->dev, "Failed to map dummy page\n"); -- goto err_free_page; -- } -- -- mmu_info->dummy_page = pt; -- mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; -- -- return 0; -- --err_free_page: -- free_page((unsigned long)pt); -- return -ENOMEM; --} -- --static void free_dummy_page(struct ipu7_mmu_info *mmu_info) --{ -- dma_unmap_single(mmu_info->dev, -- TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->dummy_page); --} -- --static int alloc_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) --{ -- u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- dma_addr_t dma; -- unsigned int i; -- -- if (!pt) -- return -ENOMEM; -- -- dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); -- -- dma = map_single(mmu_info, pt); -- if (!dma) { -- dev_err(mmu_info->dev, "Failed to map l2pt page\n"); -- goto err_free_page; -- } -- -- for (i = 0; i < ISP_L2PT_PTES; i++) -- pt[i] = mmu_info->dummy_page_pteval; -- -- mmu_info->dummy_l2_pt = pt; -- mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; -- -- return 0; -- --err_free_page: -- free_page((unsigned long)pt); -- return -ENOMEM; --} -- --static void free_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) --{ -- dma_unmap_single(mmu_info->dev, -- TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->dummy_l2_pt); --} -- --static u32 *alloc_l1_pt(struct ipu7_mmu_info *mmu_info) --{ -- u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- dma_addr_t dma; -- unsigned int i; -- -- if (!pt) -- return NULL; -- -- dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); -- -- for (i = 0; i < ISP_L1PT_PTES; i++) -- pt[i] = mmu_info->dummy_l2_pteval; -- -- dma = map_single(mmu_info, pt); -- if (!dma) { -- dev_err(mmu_info->dev, "Failed to map l1pt page\n"); -- goto err_free_page; -- } -- -- mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; -- dev_dbg(mmu_info->dev, "l1 pt %p mapped at %pad\n", pt, &dma); -- -- return pt; -- --err_free_page: -- free_page((unsigned long)pt); -- return NULL; --} -- --static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info) --{ -- u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- unsigned int i; -- -- if (!pt) -- return NULL; -- -- dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); -- -- for (i = 0; i < ISP_L1PT_PTES; i++) -- pt[i] = mmu_info->dummy_page_pteval; -- -- return pt; --} -- --static void l2_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t dummy, size_t size) --{ -- unsigned int l2_entries; -- unsigned int l2_idx; -- unsigned long flags; -- u32 l1_idx; -- u32 *l2_pt; -- -- spin_lock_irqsave(&mmu_info->lock, flags); -- for (l1_idx = iova >> ISP_L1PT_SHIFT; -- size > 0U && l1_idx < ISP_L1PT_PTES; l1_idx++) { -- dev_dbg(mmu_info->dev, -- "unmapping l2 pgtable (l1 index %u (iova 0x%8.8lx))\n", -- l1_idx, iova); -- -- if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { -- dev_err(mmu_info->dev, -- "unmap not mapped iova 0x%8.8lx l1 index %u\n", -- iova, l1_idx); -- continue; -- } -- l2_pt = mmu_info->l2_pts[l1_idx]; -- -- l2_entries = 0; -- for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -- size > 0U && l2_idx < ISP_L2PT_PTES; l2_idx++) { -- phys_addr_t pteval = TBL_PHYS_ADDR(l2_pt[l2_idx]); -- -- dev_dbg(mmu_info->dev, -- "unmap l2 index %u with pteval 0x%p\n", -- l2_idx, &pteval); -- l2_pt[l2_idx] = mmu_info->dummy_page_pteval; -- -- iova += ISP_PAGE_SIZE; -- size -= ISP_PAGE_SIZE; -- -- l2_entries++; -- } -- -- WARN_ON_ONCE(!l2_entries); -- clflush_cache_range(&l2_pt[l2_idx - l2_entries], -- sizeof(l2_pt[0]) * l2_entries); -- } -- -- WARN_ON_ONCE(size); -- spin_unlock_irqrestore(&mmu_info->lock, flags); --} -- --static int l2_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size) --{ -- struct device *dev = mmu_info->dev; -- unsigned int l2_entries; -- u32 *l2_pt, *l2_virt; -- unsigned int l2_idx; -- unsigned long flags; -- size_t mapped = 0; -- dma_addr_t dma; -- u32 l1_entry; -- u32 l1_idx; -- int err = 0; -- -- spin_lock_irqsave(&mmu_info->lock, flags); -- -- paddr = ALIGN(paddr, ISP_PAGE_SIZE); -- for (l1_idx = iova >> ISP_L1PT_SHIFT; -- size && l1_idx < ISP_L1PT_PTES; l1_idx++) { -- dev_dbg(dev, -- "mapping l2 page table for l1 index %u (iova %8.8x)\n", -- l1_idx, (u32)iova); -- -- l1_entry = mmu_info->l1_pt[l1_idx]; -- if (l1_entry == mmu_info->dummy_l2_pteval) { -- l2_virt = mmu_info->l2_pts[l1_idx]; -- if (likely(!l2_virt)) { -- l2_virt = alloc_l2_pt(mmu_info); -- if (!l2_virt) { -- err = -ENOMEM; -- goto error; -- } -- } -- -- dma = map_single(mmu_info, l2_virt); -- if (!dma) { -- dev_err(dev, "Failed to map l2pt page\n"); -- free_page((unsigned long)l2_virt); -- err = -EINVAL; -- goto error; -- } -- -- l1_entry = dma >> ISP_PADDR_SHIFT; -- -- dev_dbg(dev, "page for l1_idx %u %p allocated\n", -- l1_idx, l2_virt); -- mmu_info->l1_pt[l1_idx] = l1_entry; -- mmu_info->l2_pts[l1_idx] = l2_virt; -- -- clflush_cache_range(&mmu_info->l1_pt[l1_idx], -- sizeof(mmu_info->l1_pt[l1_idx])); -- } -- -- l2_pt = mmu_info->l2_pts[l1_idx]; -- l2_entries = 0; -- -- for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -- size && l2_idx < ISP_L2PT_PTES; l2_idx++) { -- l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; -- -- dev_dbg(dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, -- l2_pt[l2_idx]); -- -- iova += ISP_PAGE_SIZE; -- paddr += ISP_PAGE_SIZE; -- mapped += ISP_PAGE_SIZE; -- size -= ISP_PAGE_SIZE; -- -- l2_entries++; -- } -- -- WARN_ON_ONCE(!l2_entries); -- clflush_cache_range(&l2_pt[l2_idx - l2_entries], -- sizeof(l2_pt[0]) * l2_entries); -- } -- -- spin_unlock_irqrestore(&mmu_info->lock, flags); -- -- return 0; -- --error: -- spin_unlock_irqrestore(&mmu_info->lock, flags); -- /* unroll mapping in case something went wrong */ -- if (mapped) -- l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped); -- -- return err; --} -- --static int __ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size) --{ -- u32 iova_start = round_down(iova, ISP_PAGE_SIZE); -- u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); -- -- dev_dbg(mmu_info->dev, -- "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr %pap\n", -- iova_start, iova_end, size, &paddr); -- -- return l2_map(mmu_info, iova_start, paddr, size); --} -- --static void __ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, -- unsigned long iova, size_t size) --{ -- l2_unmap(mmu_info, iova, 0, size); --} -- --static int allocate_trash_buffer(struct ipu7_mmu *mmu) --{ -- unsigned int n_pages = PFN_UP(IPU_MMUV2_TRASH_RANGE); -- unsigned long iova_addr; -- struct iova *iova; -- unsigned int i; -- dma_addr_t dma; -- int ret; -- -- /* Allocate 8MB in iova range */ -- iova = alloc_iova(&mmu->dmap->iovad, n_pages, -- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -- if (!iova) { -- dev_err(mmu->dev, "cannot allocate iova range for trash\n"); -- return -ENOMEM; -- } -- -- dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { -- dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); -- ret = -ENOMEM; -- goto out_free_iova; -- } -- -- mmu->pci_trash_page = dma; -- -- /* -- * Map the 8MB iova address range to the same physical trash page -- * mmu->trash_page which is already reserved at the probe -- */ -- iova_addr = iova->pfn_lo; -- for (i = 0; i < n_pages; i++) { -- ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -- mmu->pci_trash_page, PAGE_SIZE); -- if (ret) { -- dev_err(mmu->dev, -- "mapping trash buffer range failed\n"); -- goto out_unmap; -- } -- -- iova_addr++; -- } -- -- mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); -- dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", -- mmu->mmid, (unsigned int)mmu->iova_trash_page); -- return 0; -- --out_unmap: -- ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, -- PAGE_SIZE, DMA_BIDIRECTIONAL); --out_free_iova: -- __free_iova(&mmu->dmap->iovad, iova); -- return ret; --} -- --static void __mmu_at_init(struct ipu7_mmu *mmu) --{ -- struct ipu7_mmu_info *mmu_info; -- unsigned int i; -- -- mmu_info = mmu->dmap->mmu_info; -- for (i = 0; i < mmu->nr_mmus; i++) { -- struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -- unsigned int j; -- -- /* Write page table address per MMU */ -- writel((phys_addr_t)mmu_info->l1_pt_dma, -- mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR); -- dev_dbg(mmu->dev, "mmu %s base was set as %x\n", mmu_hw->name, -- readl(mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR)); -- -- /* Set info bits and axi_refill per MMU */ -- writel(mmu_hw->info_bits, -- mmu_hw->base + MMU_REG_USER_INFO_BITS); -- writel(mmu_hw->refill, mmu_hw->base + MMU_REG_AXI_REFILL_IF_ID); -- writel(mmu_hw->collapse_en_bitmap, -- mmu_hw->base + MMU_REG_COLLAPSE_ENABLE_BITMAP); -- -- dev_dbg(mmu->dev, "mmu %s info_bits was set as %x\n", -- mmu_hw->name, -- readl(mmu_hw->base + MMU_REG_USER_INFO_BITS)); -- -- if (mmu_hw->at_sp_arb_cfg) -- writel(mmu_hw->at_sp_arb_cfg, -- mmu_hw->base + MMU_REG_AT_SP_ARB_CFG); -- -- /* default irq configuration */ -- writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_MASK); -- writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_ENABLE); -- -- /* Configure MMU TLB stream configuration for L1/L2 */ -- for (j = 0; j < mmu_hw->nr_l1streams; j++) { -- writel(mmu_hw->l1_block_sz[j], mmu_hw->base + -- mmu_hw->l1_block + 4U * j); -- } -- -- for (j = 0; j < mmu_hw->nr_l2streams; j++) { -- writel(mmu_hw->l2_block_sz[j], mmu_hw->base + -- mmu_hw->l2_block + 4U * j); -- } -- -- for (j = 0; j < mmu_hw->uao_p_num; j++) { -- if (!mmu_hw->uao_p2tlb[j]) -- continue; -- writel(mmu_hw->uao_p2tlb[j], mmu_hw->uao_base + 4U * j); -- } -- } --} -- --static void __mmu_zlx_init(struct ipu7_mmu *mmu) --{ -- unsigned int i; -- -- dev_dbg(mmu->dev, "mmu zlx init\n"); -- -- for (i = 0; i < mmu->nr_mmus; i++) { -- struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -- unsigned int j; -- -- dev_dbg(mmu->dev, "mmu %s zlx init\n", mmu_hw->name); -- for (j = 0; j < IPU_ZLX_POOL_NUM; j++) { -- if (!mmu_hw->zlx_axi_pool[j]) -- continue; -- writel(mmu_hw->zlx_axi_pool[j], -- mmu_hw->zlx_base + ZLX_REG_AXI_POOL + j * 0x4U); -- } -- -- for (j = 0; j < mmu_hw->zlx_nr; j++) { -- if (!mmu_hw->zlx_conf[j]) -- continue; -- -- writel(mmu_hw->zlx_conf[j], -- mmu_hw->zlx_base + ZLX_REG_CONF + j * 0x8U); -- } -- -- for (j = 0; j < mmu_hw->zlx_nr; j++) { -- if (!mmu_hw->zlx_en[j]) -- continue; -- -- writel(mmu_hw->zlx_en[j], -- mmu_hw->zlx_base + ZLX_REG_EN + j * 0x8U); -- } -- } --} -- --int ipu7_mmu_hw_init(struct ipu7_mmu *mmu) --{ -- unsigned long flags; -- -- dev_dbg(mmu->dev, "IPU mmu hardware init\n"); -- -- /* Initialise the each MMU and ZLX */ -- __mmu_at_init(mmu); -- __mmu_zlx_init(mmu); -- -- if (!mmu->trash_page) { -- int ret; -- -- mmu->trash_page = alloc_page(GFP_KERNEL); -- if (!mmu->trash_page) { -- dev_err(mmu->dev, "insufficient memory for trash buffer\n"); -- return -ENOMEM; -- } -- -- ret = allocate_trash_buffer(mmu); -- if (ret) { -- __free_page(mmu->trash_page); -- mmu->trash_page = NULL; -- dev_err(mmu->dev, "trash buffer allocation failed\n"); -- return ret; -- } -- } -- -- spin_lock_irqsave(&mmu->ready_lock, flags); -- mmu->ready = true; -- spin_unlock_irqrestore(&mmu->ready_lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_init, "INTEL_IPU7"); -- --static struct ipu7_mmu_info *ipu7_mmu_alloc(struct ipu7_device *isp) --{ -- struct ipu7_mmu_info *mmu_info; -- int ret; -- -- mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); -- if (!mmu_info) -- return NULL; -- -- if (isp->secure_mode) { -- mmu_info->aperture_start = IPU_FW_CODE_REGION_END; -- mmu_info->aperture_end = -- (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS); -- } else { -- mmu_info->aperture_start = IPU_FW_CODE_REGION_START; -- mmu_info->aperture_end = -- (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS_NON_SECURE); -- } -- -- mmu_info->pgsize_bitmap = SZ_4K; -- mmu_info->dev = &isp->pdev->dev; -- -- ret = get_dummy_page(mmu_info); -- if (ret) -- goto err_free_info; -- -- ret = alloc_dummy_l2_pt(mmu_info); -- if (ret) -- goto err_free_dummy_page; -- -- mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); -- if (!mmu_info->l2_pts) -- goto err_free_dummy_l2_pt; -- -- /* -- * We always map the L1 page table (a single page as well as -- * the L2 page tables). -- */ -- mmu_info->l1_pt = alloc_l1_pt(mmu_info); -- if (!mmu_info->l1_pt) -- goto err_free_l2_pts; -- -- spin_lock_init(&mmu_info->lock); -- -- dev_dbg(mmu_info->dev, "domain initialised\n"); -- -- return mmu_info; -- --err_free_l2_pts: -- vfree(mmu_info->l2_pts); --err_free_dummy_l2_pt: -- free_dummy_l2_pt(mmu_info); --err_free_dummy_page: -- free_dummy_page(mmu_info); --err_free_info: -- kfree(mmu_info); -- -- return NULL; --} -- --void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&mmu->ready_lock, flags); -- mmu->ready = false; -- spin_unlock_irqrestore(&mmu->ready_lock, flags); --} --EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_cleanup, "INTEL_IPU7"); -- --static struct ipu7_dma_mapping *alloc_dma_mapping(struct ipu7_device *isp) --{ -- struct ipu7_dma_mapping *dmap; -- unsigned long base_pfn; -- -- dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); -- if (!dmap) -- return NULL; -- -- dmap->mmu_info = ipu7_mmu_alloc(isp); -- if (!dmap->mmu_info) { -- kfree(dmap); -- return NULL; -- } -- -- /* 0~64M is forbidden for uctile controller */ -- base_pfn = max_t(unsigned long, 1, -- PFN_DOWN(dmap->mmu_info->aperture_start)); -- init_iova_domain(&dmap->iovad, SZ_4K, base_pfn); -- dmap->mmu_info->dmap = dmap; -- -- dev_dbg(&isp->pdev->dev, "alloc mapping\n"); -- -- iova_cache_get(); -- -- return dmap; --} -- --phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -- dma_addr_t iova) --{ -- phys_addr_t phy_addr; -- unsigned long flags; -- u32 *l2_pt; -- -- spin_lock_irqsave(&mmu_info->lock, flags); -- l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; -- phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; -- phy_addr <<= ISP_PAGE_SHIFT; -- spin_unlock_irqrestore(&mmu_info->lock, flags); -- -- return phy_addr; --} -- --void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- size_t size) --{ -- unsigned int min_pagesz; -- -- dev_dbg(mmu_info->dev, "unmapping iova 0x%lx size 0x%zx\n", iova, size); -- -- /* find out the minimum page size supported */ -- min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -- -- /* -- * The virtual address and the size of the mapping must be -- * aligned (at least) to the size of the smallest page supported -- * by the hardware -- */ -- if (!IS_ALIGNED(iova | size, min_pagesz)) { -- dev_err(mmu_info->dev, -- "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", -- iova, size, min_pagesz); -- return; -- } -- -- __ipu7_mmu_unmap(mmu_info, iova, size); --} -- --int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size) --{ -- unsigned int min_pagesz; -- -- if (mmu_info->pgsize_bitmap == 0UL) -- return -ENODEV; -- -- /* find out the minimum page size supported */ -- min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -- -- /* -- * both the virtual address and the physical one, as well as -- * the size of the mapping, must be aligned (at least) to the -- * size of the smallest page supported by the hardware -- */ -- if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { -- dev_err(mmu_info->dev, -- "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", -- iova, &paddr, size, min_pagesz); -- return -EINVAL; -- } -- -- dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", -- iova, &paddr, size); -- -- return __ipu7_mmu_map(mmu_info, iova, paddr, size); --} -- --static void ipu7_mmu_destroy(struct ipu7_mmu *mmu) --{ -- struct ipu7_dma_mapping *dmap = mmu->dmap; -- struct ipu7_mmu_info *mmu_info = dmap->mmu_info; -- struct iova *iova; -- u32 l1_idx; -- -- if (mmu->iova_trash_page) { -- iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); -- if (iova) { -- /* unmap and free the trash buffer iova */ -- ipu7_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- __free_iova(&dmap->iovad, iova); -- } else { -- dev_err(mmu->dev, "trash buffer iova not found.\n"); -- } -- -- mmu->iova_trash_page = 0; -- dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- mmu->pci_trash_page = 0; -- __free_page(mmu->trash_page); -- } -- -- for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { -- if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { -- dma_unmap_single(mmu_info->dev, -- TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->l2_pts[l1_idx]); -- } -- } -- -- vfree(mmu_info->l2_pts); -- free_dummy_page(mmu_info); -- dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->dummy_l2_pt); -- free_page((unsigned long)mmu_info->l1_pt); -- kfree(mmu_info); --} -- --struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -- void __iomem *base, int mmid, -- const struct ipu7_hw_variants *hw) --{ -- struct ipu7_device *isp = pci_get_drvdata(to_pci_dev(dev)); -- struct ipu7_mmu_pdata *pdata; -- struct ipu7_mmu *mmu; -- unsigned int i; -- -- if (hw->nr_mmus > IPU_MMU_MAX_NUM) -- return ERR_PTR(-EINVAL); -- -- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -- if (!pdata) -- return ERR_PTR(-ENOMEM); -- -- for (i = 0; i < hw->nr_mmus; i++) { -- struct ipu7_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; -- const struct ipu7_mmu_hw *src_mmu = &hw->mmu_hw[i]; -- -- if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS || -- src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS) -- return ERR_PTR(-EINVAL); -- -- *pdata_mmu = *src_mmu; -- pdata_mmu->base = base + src_mmu->offset; -- pdata_mmu->zlx_base = base + src_mmu->zlx_offset; -- pdata_mmu->uao_base = base + src_mmu->uao_offset; -- } -- -- mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); -- if (!mmu) -- return ERR_PTR(-ENOMEM); -- -- mmu->mmid = mmid; -- mmu->mmu_hw = pdata->mmu_hw; -- mmu->nr_mmus = hw->nr_mmus; -- mmu->tlb_invalidate = tlb_invalidate; -- mmu->ready = false; -- INIT_LIST_HEAD(&mmu->vma_list); -- spin_lock_init(&mmu->ready_lock); -- -- mmu->dmap = alloc_dma_mapping(isp); -- if (!mmu->dmap) { -- dev_err(dev, "can't alloc dma mapping\n"); -- return ERR_PTR(-ENOMEM); -- } -- -- return mmu; --} -- --void ipu7_mmu_cleanup(struct ipu7_mmu *mmu) --{ -- struct ipu7_dma_mapping *dmap = mmu->dmap; -- -- ipu7_mmu_destroy(mmu); -- mmu->dmap = NULL; -- iova_cache_put(); -- put_iova_domain(&dmap->iovad); -- kfree(dmap); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.h b/drivers/media/pci/intel/ipu7/ipu7-mmu.h -deleted file mode 100644 -index d85bb8ffc711..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-mmu.h -+++ /dev/null -@@ -1,414 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_MMU_H --#define IPU7_MMU_H -- --#include --#include --#include --#include -- --struct device; --struct page; --struct ipu7_hw_variants; --struct ipu7_mmu; --struct ipu7_mmu_info; -- --#define ISYS_MMID 0x1 --#define PSYS_MMID 0x0 -- --/* IPU7 for LNL */ --/* IS MMU Cmd RD */ --#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 --#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3 --#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Cmd WR */ --#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000 --#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3 --#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Data WR Snoop */ --#define IPU7_IS_MMU_M0_OFFSET 0x276000 --#define IPU7_IS_MMU_M0_STREAM_NUM 8 --#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74 -- --/* IS MMU Data WR ISOC */ --#define IPU7_IS_MMU_M1_OFFSET 0x277000 --#define IPU7_IS_MMU_M1_STREAM_NUM 16 --#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -- --/* PS MMU FW RD */ --#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000 --#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20 --#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4 -- --/* PS MMU FW WR */ --#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000 --#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10 --#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -- --/* PS MMU FW Data RD VC0 */ --#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000 --#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40 --#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4 -- --/* PS MMU FW Data WR VC0 */ --#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000 --#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40 --#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4 -- --/* IS UAO UC RD */ --#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000 --#define IPU7_IS_UAO_UC_RD_PLANENUM 4 -- --/* IS UAO UC WR */ --#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000 --#define IPU7_IS_UAO_UC_WR_PLANENUM 4 -- --/* IS UAO M0 WR */ --#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000 --#define IPU7_IS_UAO_M0_WR_PLANENUM 8 -- --/* IS UAO M1 WR */ --#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000 --#define IPU7_IS_UAO_M1_WR_PLANENUM 16 -- --/* PS UAO FW RD */ --#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000 --#define IPU7_PS_UAO_FW_RD_PLANENUM 20 -- --/* PS UAO FW WR */ --#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000 --#define IPU7_PS_UAO_FW_WR_PLANENUM 16 -- --/* PS UAO SRT RD */ --#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000 --#define IPU7_PS_UAO_SRT_RD_PLANENUM 40 -- --/* PS UAO SRT WR */ --#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000 --#define IPU7_PS_UAO_SRT_WR_PLANENUM 40 -- --#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000 --#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000 --#define IPU7_IS_ZLX_M0_OFFSET 0x27a000 --#define IPU7_IS_ZLX_M1_OFFSET 0x27b000 --#define IPU7_IS_ZLX_UC_RD_NUM 4 --#define IPU7_IS_ZLX_UC_WR_NUM 4 --#define IPU7_IS_ZLX_M0_NUM 8 --#define IPU7_IS_ZLX_M1_NUM 16 -- --#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000 --#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000 --#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000 --#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000 --#define IPU7_PS_ZLX_DATA_RD_NUM 32 --#define IPU7_PS_ZLX_DATA_WR_NUM 32 --#define IPU7_PS_ZLX_FW_RD_NUM 16 --#define IPU7_PS_ZLX_FW_WR_NUM 10 -- --/* IPU7P5 for PTL */ --/* IS MMU Cmd RD */ --#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000 --#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3 --#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Cmd WR */ --#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000 --#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3 --#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Data WR Snoop */ --#define IPU7P5_IS_MMU_M0_OFFSET 0x276000 --#define IPU7P5_IS_MMU_M0_STREAM_NUM 16 --#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -- --/* IS MMU Data WR ISOC */ --#define IPU7P5_IS_MMU_M1_OFFSET 0x277000 --#define IPU7P5_IS_MMU_M1_STREAM_NUM 16 --#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -- --/* PS MMU FW RD */ --#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000 --#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16 --#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94 -- --/* PS MMU FW WR */ --#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000 --#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10 --#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -- --/* PS MMU FW Data RD VC0 */ --#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000 --#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22 --#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac -- --/* PS MMU FW Data WR VC0 */ --#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000 --#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32 --#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4 -- --/* IS UAO UC RD */ --#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000 --#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4 -- --/* IS UAO UC WR */ --#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000 --#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4 -- --/* IS UAO M0 WR */ --#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000 --#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16 -- --/* IS UAO M1 WR */ --#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000 --#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16 -- --/* PS UAO FW RD */ --#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000 --#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16 -- --/* PS UAO FW WR */ --#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000 --#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10 -- --/* PS UAO SRT RD */ --#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000 --#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22 -- --/* PS UAO SRT WR */ --#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000 --#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32 -- --#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000 --#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000 --#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000 --#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000 --#define IPU7P5_IS_ZLX_UC_RD_NUM 4 --#define IPU7P5_IS_ZLX_UC_WR_NUM 4 --#define IPU7P5_IS_ZLX_M0_NUM 16 --#define IPU7P5_IS_ZLX_M1_NUM 16 -- --#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000 --#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000 --#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000 --#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000 --#define IPU7P5_PS_ZLX_DATA_RD_NUM 22 --#define IPU7P5_PS_ZLX_DATA_WR_NUM 32 --#define IPU7P5_PS_ZLX_FW_RD_NUM 16 --#define IPU7P5_PS_ZLX_FW_WR_NUM 10 -- --/* IS MMU Cmd RD */ --#define IPU8_IS_MMU_FW_RD_OFFSET 0x270000 --#define IPU8_IS_MMU_FW_RD_STREAM_NUM 3 --#define IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Cmd WR */ --#define IPU8_IS_MMU_FW_WR_OFFSET 0x271000 --#define IPU8_IS_MMU_FW_WR_STREAM_NUM 3 --#define IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Data WR Snoop */ --#define IPU8_IS_MMU_M0_OFFSET 0x272000 --#define IPU8_IS_MMU_M0_STREAM_NUM 16 --#define IPU8_IS_MMU_M0_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -- --/* IS MMU Data WR ISOC */ --#define IPU8_IS_MMU_M1_OFFSET 0x273000 --#define IPU8_IS_MMU_M1_STREAM_NUM 16 --#define IPU8_IS_MMU_M1_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -- --/* IS MMU UPIPE ISOC */ --#define IPU8_IS_MMU_UPIPE_OFFSET 0x274000 --#define IPU8_IS_MMU_UPIPE_STREAM_NUM 6 --#define IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG 0x6c -- --/* PS MMU FW RD */ --#define IPU8_PS_MMU_FW_RD_OFFSET 0x148000 --#define IPU8_PS_MMU_FW_RD_STREAM_NUM 12 --#define IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x84 -- --/* PS MMU FW WR */ --#define IPU8_PS_MMU_FW_WR_OFFSET 0x149000 --#define IPU8_PS_MMU_FW_WR_STREAM_NUM 8 --#define IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x74 -- --/* PS MMU FW Data RD VC0 */ --#define IPU8_PS_MMU_SRT_RD_OFFSET 0x14a000 --#define IPU8_PS_MMU_SRT_RD_STREAM_NUM 26 --#define IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xbc -- --/* PS MMU FW Data WR VC0 */ --#define IPU8_PS_MMU_SRT_WR_OFFSET 0x14b000 --#define IPU8_PS_MMU_SRT_WR_STREAM_NUM 26 --#define IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xbc -- --/* IS UAO UC RD */ --#define IPU8_IS_UAO_UC_RD_OFFSET 0x27a000 --#define IPU8_IS_UAO_UC_RD_PLANENUM 4 -- --/* IS UAO UC WR */ --#define IPU8_IS_UAO_UC_WR_OFFSET 0x27b000 --#define IPU8_IS_UAO_UC_WR_PLANENUM 4 -- --/* IS UAO M0 WR */ --#define IPU8_IS_UAO_M0_WR_OFFSET 0x27c000 --#define IPU8_IS_UAO_M0_WR_PLANENUM 16 -- --/* IS UAO M1 WR */ --#define IPU8_IS_UAO_M1_WR_OFFSET 0x27d000 --#define IPU8_IS_UAO_M1_WR_PLANENUM 16 -- --/* IS UAO UPIPE */ --#define IPU8_IS_UAO_UPIPE_OFFSET 0x27e000 --#define IPU8_IS_UAO_UPIPE_PLANENUM 6 -- --/* PS UAO FW RD */ --#define IPU8_PS_UAO_FW_RD_OFFSET 0x156000 --#define IPU8_PS_UAO_FW_RD_PLANENUM 12 -- --/* PS UAO FW WR */ --#define IPU8_PS_UAO_FW_WR_OFFSET 0x157000 --#define IPU8_PS_UAO_FW_WR_PLANENUM 8 -- --/* PS UAO SRT RD */ --#define IPU8_PS_UAO_SRT_RD_OFFSET 0x154000 --#define IPU8_PS_UAO_SRT_RD_PLANENUM 26 -- --/* PS UAO SRT WR */ --#define IPU8_PS_UAO_SRT_WR_OFFSET 0x155000 --#define IPU8_PS_UAO_SRT_WR_PLANENUM 26 -- --#define IPU8_IS_ZLX_UC_RD_OFFSET 0x275000 --#define IPU8_IS_ZLX_UC_WR_OFFSET 0x276000 --#define IPU8_IS_ZLX_M0_OFFSET 0x277000 --#define IPU8_IS_ZLX_M1_OFFSET 0x278000 --#define IPU8_IS_ZLX_UPIPE_OFFSET 0x279000 --#define IPU8_IS_ZLX_UC_RD_NUM 4 --#define IPU8_IS_ZLX_UC_WR_NUM 4 --#define IPU8_IS_ZLX_M0_NUM 16 --#define IPU8_IS_ZLX_M1_NUM 16 --#define IPU8_IS_ZLX_UPIPE_NUM 6 -- --#define IPU8_PS_ZLX_DATA_RD_OFFSET 0x14e000 --#define IPU8_PS_ZLX_DATA_WR_OFFSET 0x14f000 --#define IPU8_PS_ZLX_FW_RD_OFFSET 0x150000 --#define IPU8_PS_ZLX_FW_WR_OFFSET 0x151000 --#define IPU8_PS_ZLX_DATA_RD_NUM 26 --#define IPU8_PS_ZLX_DATA_WR_NUM 26 --#define IPU8_PS_ZLX_FW_RD_NUM 12 --#define IPU8_PS_ZLX_FW_WR_NUM 8 -- --#define MMU_REG_INVALIDATE_0 0x00 --#define MMU_REG_INVALIDATE_1 0x04 --#define MMU_REG_PAGE_TABLE_BASE_ADDR 0x08 --#define MMU_REG_USER_INFO_BITS 0x0c --#define MMU_REG_AXI_REFILL_IF_ID 0x10 --#define MMU_REG_PW_EN_BITMAP 0x14 --#define MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18 --#define MMU_REG_GENERAL_REG 0x1c --#define MMU_REG_AT_SP_ARB_CFG 0x20 --#define MMU_REG_INVALIDATION_STATUS 0x24 --#define MMU_REG_IRQ_LEVEL_NO_PULSE 0x28 --#define MMU_REG_IRQ_MASK 0x2c --#define MMU_REG_IRQ_ENABLE 0x30 --#define MMU_REG_IRQ_EDGE 0x34 --#define MMU_REG_IRQ_CLEAR 0x38 --#define MMU_REG_IRQ_CAUSE 0x3c --#define MMU_REG_CG_CTRL_BITS 0x40 --#define MMU_REG_RD_FIFOS_STATUS 0x44 --#define MMU_REG_WR_FIFOS_STATUS 0x48 --#define MMU_REG_COMMON_FIFOS_STATUS 0x4c --#define MMU_REG_FSM_STATUS 0x50 -- --#define ZLX_REG_AXI_POOL 0x0 --#define ZLX_REG_EN 0x20 --#define ZLX_REG_CONF 0x24 --#define ZLX_REG_CG_CTRL 0x900 --#define ZLX_REG_FORCE_BYPASS 0x904 -- --struct ipu7_mmu_info { -- struct device *dev; -- -- u32 *l1_pt; -- u32 l1_pt_dma; -- u32 **l2_pts; -- -- u32 *dummy_l2_pt; -- u32 dummy_l2_pteval; -- void *dummy_page; -- u32 dummy_page_pteval; -- -- dma_addr_t aperture_start; -- dma_addr_t aperture_end; -- unsigned long pgsize_bitmap; -- -- spinlock_t lock; /* Serialize access to users */ -- struct ipu7_dma_mapping *dmap; --}; -- --struct ipu7_mmu { -- struct list_head node; -- -- struct ipu7_mmu_hw *mmu_hw; -- unsigned int nr_mmus; -- unsigned int mmid; -- -- phys_addr_t pgtbl; -- struct device *dev; -- -- struct ipu7_dma_mapping *dmap; -- struct list_head vma_list; -- -- struct page *trash_page; -- dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ -- dma_addr_t iova_trash_page; /* IOVA for IPU child nodes to use */ -- -- bool ready; -- spinlock_t ready_lock; /* Serialize access to bool ready */ -- -- void (*tlb_invalidate)(struct ipu7_mmu *mmu); --}; -- --struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -- void __iomem *base, int mmid, -- const struct ipu7_hw_variants *hw); --void ipu7_mmu_cleanup(struct ipu7_mmu *mmu); --int ipu7_mmu_hw_init(struct ipu7_mmu *mmu); --void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu); --int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size); --void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- size_t size); --phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -- dma_addr_t iova); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h b/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -deleted file mode 100644 -index eeadc886a8cf..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -+++ /dev/null -@@ -1,82 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2018 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_PLATFORM_REGS_H --#define IPU7_PLATFORM_REGS_H -- --#define IS_BASE 0x230000 --#define IS_UC_CTRL_BASE (IS_BASE + 0x0) -- --#define PS_BASE 0x130000 --#define PS_UC_CTRL_BASE (PS_BASE + 0x0) -- --/* -- * bit 0: IRQ from FW, -- * bit 1, 2 and 3: IRQ from HW -- */ --#define TO_SW_IRQ_MASK 0xf --#define TO_SW_IRQ_FW BIT(0) -- --#define FW_CODE_BASE 0x0 --#define FW_DATA_BASE 0x4 --#define PRINTF_EN_THROUGH_TRACE 0x3004 --#define PRINTF_EN_DIRECTLY_TO_DDR 0x3008 --#define PRINTF_DDR_BASE_ADDR 0x300c --#define PRINTF_DDR_SIZE 0x3010 --#define PRINTF_DDR_NEXT_ADDR 0x3014 --#define PRINTF_STATUS 0x3018 --#define PRINTF_AXI_CNTL 0x301c --#define PRINTF_MSG_LENGTH 0x3020 --#define TO_SW_IRQ_CNTL_EDGE 0x4000 --#define TO_SW_IRQ_CNTL_MASK_N 0x4004 --#define TO_SW_IRQ_CNTL_STATUS 0x4008 --#define TO_SW_IRQ_CNTL_CLEAR 0x400c --#define TO_SW_IRQ_CNTL_ENABLE 0x4010 --#define TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x4014 --#define ERR_IRQ_CNTL_EDGE 0x4018 --#define ERR_IRQ_CNTL_MASK_N 0x401c --#define ERR_IRQ_CNTL_STATUS 0x4020 --#define ERR_IRQ_CNTL_CLEAR 0x4024 --#define ERR_IRQ_CNTL_ENABLE 0x4028 --#define ERR_IRQ_CNTL_LEVEL_NOT_PULSE 0x402c --#define LOCAL_DMEM_BASE_ADDR 0x1300000 -- --/* -- * IS_UC_TO_SW irqs -- * bit 0: IRQ from local FW -- * bit 1~3: IRQ from HW -- */ --#define IS_UC_TO_SW_IRQ_MASK 0xf -- --#define IPU_ISYS_SPC_OFFSET 0x210000 --#define IPU7_PSYS_SPC_OFFSET 0x118000 --#define IPU_ISYS_DMEM_OFFSET 0x200000 --#define IPU_PSYS_DMEM_OFFSET 0x100000 -- --#define IPU7_ISYS_CSI_PORT_NUM 4 -- --/* IRQ-related registers in PSYS */ --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_EDGE 0x134000 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK 0x134004 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS 0x134008 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR 0x13400c --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE 0x134010 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x134014 --#define IRQ_FROM_LOCAL_FW BIT(0) -- --/* -- * psys subdomains power request regs -- */ --enum ipu7_device_buttress_psys_domain_pos { -- IPU_PSYS_SUBDOMAIN_LB = 0, -- IPU_PSYS_SUBDOMAIN_BB = 1, --}; -- --#define IPU7_PSYS_DOMAIN_POWER_MASK (BIT(IPU_PSYS_SUBDOMAIN_LB) | \ -- BIT(IPU_PSYS_SUBDOMAIN_BB)) --#define IPU8_PSYS_DOMAIN_POWER_MASK BIT(IPU_PSYS_SUBDOMAIN_LB) --#define IPU_PSYS_DOMAIN_POWER_IN_PROGRESS BIT(31) -- --#endif /* IPU7_PLATFORM_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.c b/drivers/media/pci/intel/ipu7/ipu7-syscom.c -deleted file mode 100644 -index 1a1c8cb02d83..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-syscom.c -+++ /dev/null -@@ -1,79 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include -- --#include "abi/ipu7_fw_syscom_abi.h" -- --#include "ipu7.h" --#include "ipu7-syscom.h" -- --static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx, -- u32 q) --{ -- return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s)); --} -- --void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q) --{ -- struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -- void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -- u32 write_index = readl(queue_indices + -- offsetof(struct syscom_queue_indices_s, -- write_index)); -- u32 read_index = readl(queue_indices + -- offsetof(struct syscom_queue_indices_s, -- read_index)); -- void *token = NULL; -- -- if (q < ctx->num_output_queues) { -- /* Output queue */ -- bool empty = (write_index == read_index); -- -- if (!empty) -- token = queue_params->token_array_mem + -- read_index * -- queue_params->token_size_in_bytes; -- } else { -- /* Input queue */ -- bool full = (read_index == ((write_index + 1U) % -- (u32)queue_params->max_capacity)); -- -- if (!full) -- token = queue_params->token_array_mem + -- write_index * queue_params->token_size_in_bytes; -- } -- return token; --} --EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7"); -- --void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q) --{ -- struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -- void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -- u32 offset, index; -- -- if (q < ctx->num_output_queues) -- /* Output queue */ -- offset = offsetof(struct syscom_queue_indices_s, read_index); -- -- else -- /* Input queue */ -- offset = offsetof(struct syscom_queue_indices_s, write_index); -- -- index = readl(queue_indices + offset); -- writel((index + 1U) % queue_params->max_capacity, -- queue_indices + offset); --} --EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7"); -- --struct syscom_queue_params_config * --ipu7_syscom_get_queue_config(struct syscom_config_s *config) --{ -- return (struct syscom_queue_params_config *)(&config[1]); --} --EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.h b/drivers/media/pci/intel/ipu7/ipu7-syscom.h -deleted file mode 100644 -index e1fbe3b7914e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-syscom.h -+++ /dev/null -@@ -1,35 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_SYSCOM_H --#define IPU7_SYSCOM_H -- --#include -- --struct syscom_config_s; --struct syscom_queue_params_config; -- --struct syscom_queue_config { -- void *token_array_mem; -- u32 queue_size; -- u16 token_size_in_bytes; -- u16 max_capacity; --}; -- --struct ipu7_syscom_context { -- u16 num_input_queues; -- u16 num_output_queues; -- struct syscom_queue_config *queue_configs; -- void __iomem *queue_indices; -- dma_addr_t queue_mem_dma_addr; -- void *queue_mem; -- u32 queue_mem_size; --}; -- --void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q); --void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q); --struct syscom_queue_params_config * --ipu7_syscom_get_queue_config(struct syscom_config_s *config); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7.c b/drivers/media/pci/intel/ipu7/ipu7.c -deleted file mode 100644 -index a76863eea356..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7.c -+++ /dev/null -@@ -1,2864 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#ifdef CONFIG_DEBUG_FS --#include --#endif --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include -- --#include "abi/ipu7_fw_common_abi.h" -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-cpd.h" --#include "ipu7-dma.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-mmu.h" --#include "ipu7-platform-regs.h" -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --#include -- --#endif --#define IPU_PCI_BAR 0 --#define IPU_PCI_PBBAR 4 -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --static const unsigned int ipu7_tpg_offsets[] = { -- MGC_MG_PORT(0), -- MGC_MG_PORT(1), -- MGC_MG_PORT(2), --}; --#endif -- --static const unsigned int ipu7_csi_offsets[] = { -- IPU_CSI_PORT_A_ADDR_OFFSET, -- IPU_CSI_PORT_B_ADDR_OFFSET, -- IPU_CSI_PORT_C_ADDR_OFFSET, -- IPU_CSI_PORT_D_ADDR_OFFSET, --}; -- --static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = { -- .csi2 = { -- .gpreg = IS_IO_CSI2_GPREGS_BASE, -- }, -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7P5_IS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "IS_FW_RD", -- .offset = IPU7P5_IS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET, -- .info_bits = 0x20005101, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0 -- }, -- .zlx_conf = { -- 0x0, -- }, -- .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004c, -- 0x0000004d, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_FW_WR", -- .offset = IPU7P5_IS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET, -- .info_bits = 0x20005001, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x00010101, -- 0x00010101, -- 0x0, -- }, -- .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004a, -- 0x0000004b, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_DATA_WR_ISOC", -- .offset = IPU7P5_IS_MMU_M0_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET, -- .info_bits = 0x20004e01, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_M0_NUM, -- .zlx_axi_pool = { -- 0x00000f10, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- }, -- .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- }, -- }, -- { -- .name = "IS_DATA_WR_SNOOP", -- .offset = IPU7P5_IS_MMU_M1_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET, -- .info_bits = 0x20004f01, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_M1_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- }, -- .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- }, -- }, -- }, -- .cdc_fifos = 3, -- .cdc_fifo_threshold = {6, 8, 2}, -- .dmem_offset = IPU_ISYS_DMEM_OFFSET, -- .spc_offset = IPU_ISYS_SPC_OFFSET, -- }, -- .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, --}; -- --static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = { -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7P5_PS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "PS_FW_RD", -- .offset = IPU7P5_PS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET, -- .info_bits = 0x20004001, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000d, -- 0x0000000f, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001a, -- 0x0000001a, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0, 1, 1, 0, 0, -- 0, 1, 1, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00000000, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00010101, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00010101, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM, -- .uao_p2tlb = { -- 0x0000002e, -- 0x00000035, -- 0x00000036, -- 0x00000031, -- 0x00000037, -- 0x00000038, -- 0x00000039, -- 0x00000032, -- 0x00000033, -- 0x0000003a, -- 0x0000003b, -- 0x0000003c, -- 0x00000034, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_FW_WR", -- .offset = IPU7P5_PS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET, -- .info_bits = 0x20003e01, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000010, -- 0x00000010, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00000000, -- 0x00010101, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000002e, -- 0x0000002f, -- 0x00000030, -- 0x00000031, -- 0x00000032, -- 0x00000033, -- 0x00000034, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_DATA_RD", -- .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET, -- .info_bits = 0x20003f01, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000b, -- 0x0000000d, -- 0x0000000f, -- 0x00000013, -- 0x00000017, -- 0x00000019, -- 0x0000001b, -- 0x0000001d, -- 0x0000001f, -- 0x0000002b, -- 0x00000033, -- 0x0000003f, -- 0x00000047, -- 0x00000049, -- 0x0000004b, -- 0x0000004c, -- 0x0000004d, -- 0x0000004e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00030303, -- 0x00010101, -- 0x00010101, -- 0x00030202, -- 0x00010101, -- 0x00010101, -- 0x00030303, -- 0x00030303, -- 0x00010101, -- 0x00030800, -- 0x00030500, -- 0x00020101, -- 0x00042000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00020400, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM, -- .uao_p2tlb = { -- 0x0000001c, -- 0x0000001d, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- 0x00000022, -- 0x00000023, -- 0x00000024, -- 0x00000025, -- 0x00000026, -- 0x00000027, -- 0x00000028, -- 0x00000029, -- 0x0000002a, -- 0x0000002b, -- 0x0000002c, -- 0x0000002d, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- }, -- { -- .name = "PS_DATA_WR", -- .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET, -- .info_bits = 0x20003d01, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000006, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000028, -- 0x0000002a, -- 0x00000036, -- 0x0000003e, -- 0x00000040, -- 0x00000042, -- 0x0000004e, -- 0x00000056, -- 0x0000005c, -- 0x00000068, -- 0x00000070, -- 0x00000076, -- 0x00000077, -- 0x00000078, -- 0x00000079, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000006, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000028, -- 0x0000002a, -- 0x00000036, -- 0x0000003e, -- 0x00000040, -- 0x00000042, -- 0x0000004e, -- 0x00000056, -- 0x0000005c, -- 0x00000068, -- 0x00000070, -- 0x00000076, -- 0x00000077, -- 0x00000078, -- 0x00000079, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f50, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 0, 0, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00010102, -- 0x00030103, -- 0x00030103, -- 0x00010101, -- 0x00010101, -- 0x00030101, -- 0x00010101, -- 0x38010101, -- 0x00000000, -- 0x00000000, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x00030303, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00010101, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000000, -- 0x00000001, -- 0x00000002, -- 0x00000003, -- 0x00000004, -- 0x00000005, -- 0x00000006, -- 0x00000007, -- 0x00000008, -- 0x00000009, -- 0x0000000a, -- 0x0000000b, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000015, -- 0x00000016, -- 0x00000017, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001b, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- }, -- }, -- .dmem_offset = IPU_PSYS_DMEM_OFFSET, -- }, --}; -- --static struct ipu_isys_internal_pdata ipu7_isys_ipdata = { -- .csi2 = { -- .gpreg = IS_IO_CSI2_GPREGS_BASE, -- }, -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7_IS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "IS_FW_RD", -- .offset = IPU7_IS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET, -- .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET, -- .info_bits = 0x20006701, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 0, 0, 0 -- }, -- .zlx_conf = { -- 0x0, 0x0, 0x0, 0x0, -- }, -- .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000061, -- 0x00000064, -- 0x00000065, -- }, -- }, -- { -- .name = "IS_FW_WR", -- .offset = IPU7_IS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET, -- .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET, -- .info_bits = 0x20006801, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000061, -- 0x00000062, -- 0x00000063, -- }, -- }, -- { -- .name = "IS_DATA_WR_ISOC", -- .offset = IPU7_IS_MMU_M0_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_M0_OFFSET, -- .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET, -- .info_bits = 0x20006601, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, -- }, -- .zlx_nr = IPU7_IS_ZLX_M0_NUM, -- .zlx_axi_pool = { -- 0x00000f10, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004a, -- 0x0000004b, -- 0x0000004c, -- 0x0000004d, -- 0x0000004e, -- 0x0000004f, -- 0x00000050, -- }, -- }, -- { -- .name = "IS_DATA_WR_SNOOP", -- .offset = IPU7_IS_MMU_M1_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_M1_OFFSET, -- .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET, -- .info_bits = 0x20006901, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x3, 0x6, 0x9, 0xc, -- 0xe, 0x10, 0x12, 0x14, 0x16, -- 0x18, 0x1a, 0x1c, 0x1e, 0x20, -- 0x22, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, -- }, -- .zlx_nr = IPU7_IS_ZLX_M1_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000051, -- 0x00000052, -- 0x00000053, -- 0x00000054, -- 0x00000055, -- 0x00000056, -- 0x00000057, -- 0x00000058, -- 0x00000059, -- 0x0000005a, -- 0x0000005b, -- 0x0000005c, -- 0x0000005d, -- 0x0000005e, -- 0x0000005f, -- 0x00000060, -- }, -- }, -- }, -- .cdc_fifos = 3, -- .cdc_fifo_threshold = {6, 8, 2}, -- .dmem_offset = IPU_ISYS_DMEM_OFFSET, -- .spc_offset = IPU_ISYS_SPC_OFFSET, -- }, -- .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, --}; -- --static struct ipu_psys_internal_pdata ipu7_psys_ipdata = { -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7_PS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "PS_FW_RD", -- .offset = IPU7_PS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET, -- .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET, -- .info_bits = 0x20004801, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0, 0x8, 0xa, 0xc, 0xd, -- 0xf, 0x11, 0x12, 0x13, 0x14, -- 0x16, 0x18, 0x19, 0x1a, 0x1a, -- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, 0x20, 0x22, 0x24, 0x26, -- }, -- .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x0, -- }, -- .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000036, -- 0x0000003d, -- 0x0000003e, -- 0x00000039, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x0000003a, -- 0x0000003b, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x0000003c, -- }, -- }, -- { -- .name = "PS_FW_WR", -- .offset = IPU7_PS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET, -- .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET, -- .info_bits = 0x20004601, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0, 0x8, 0xa, 0xc, 0xd, -- 0xe, 0xf, 0x10, 0x10, 0x10, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- }, -- .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, 0, 0, 0, 0, -- 0, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000036, -- 0x00000037, -- 0x00000038, -- 0x00000039, -- 0x0000003a, -- 0x0000003b, -- 0x0000003c, -- }, -- }, -- { -- .name = "PS_DATA_RD", -- .offset = IPU7_PS_MMU_SRT_RD_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET, -- .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET, -- .info_bits = 0x20004701, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x4, 0x6, 0x8, 0xb, -- 0xd, 0xf, 0x11, 0x13, 0x15, -- 0x17, 0x23, 0x2b, 0x37, 0x3f, -- 0x41, 0x43, 0x44, 0x45, 0x46, -- 0x47, 0x48, 0x49, 0x4a, 0x4b, -- 0x4c, 0x4d, 0x4e, 0x4f, 0x50, -- 0x51, 0x52, 0x53, 0x55, 0x57, -- 0x59, 0x5b, 0x5d, 0x5f, 0x61, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, 0x20, 0x22, 0x24, 0x26, -- 0x28, 0x2a, 0x2c, 0x2e, 0x30, -- 0x32, 0x34, 0x36, 0x38, 0x3a, -- 0x3c, 0x3e, 0x40, 0x42, 0x44, -- 0x46, 0x48, 0x4a, 0x4c, 0x4e, -- }, -- .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00030303, -- 0x00010101, -- 0x00010101, -- 0x00030202, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00030800, -- 0x00030500, -- 0x00020101, -- 0x00042000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00020400, -- 0x00010101, -- }, -- .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000022, -- 0x00000023, -- 0x00000024, -- 0x00000025, -- 0x00000026, -- 0x00000027, -- 0x00000028, -- 0x00000029, -- 0x0000002a, -- 0x0000002b, -- 0x0000002c, -- 0x0000002d, -- 0x0000002e, -- 0x0000002f, -- 0x00000030, -- 0x00000031, -- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- 0x00000032, -- 0x00000033, -- 0x00000034, -- 0x00000035, -- }, -- }, -- { -- .name = "PS_DATA_WR", -- .offset = IPU7_PS_MMU_SRT_WR_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET, -- .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET, -- .info_bits = 0x20004501, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x2, 0x6, 0xa, 0xc, -- 0xe, 0x10, 0x12, 0x14, 0x16, -- 0x18, 0x1a, 0x1c, 0x1e, 0x20, -- 0x22, 0x24, 0x26, 0x32, 0x3a, -- 0x3c, 0x3e, 0x4a, 0x52, 0x58, -- 0x64, 0x6c, 0x72, 0x7e, 0x86, -- 0x8c, 0x8d, 0x8e, 0x8f, 0x90, -- 0x91, 0x92, 0x94, 0x96, 0x98, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, 0x20, 0x22, 0x24, 0x26, -- 0x28, 0x2a, 0x2c, 0x2e, 0x30, -- 0x32, 0x34, 0x36, 0x38, 0x3a, -- 0x3c, 0x3e, 0x40, 0x42, 0x44, -- 0x46, 0x48, 0x4a, 0x4c, 0x4e, -- }, -- .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f50, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 0, 0, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 0, 0, -- }, -- .zlx_conf = { -- 0x00010102, -- 0x00030103, -- 0x00030103, -- 0x00010101, -- 0x00010101, -- 0x00030101, -- 0x00010101, -- 0x38010101, -- 0x0, -- 0x0, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00010101, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x0, -- 0x0, -- }, -- .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000000, -- 0x00000001, -- 0x00000002, -- 0x00000003, -- 0x00000004, -- 0x00000005, -- 0x00000006, -- 0x00000007, -- 0x00000008, -- 0x00000009, -- 0x0000000a, -- 0x0000000b, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000015, -- 0x00000016, -- 0x00000017, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001b, -- 0x0000001c, -- 0x0000001d, -- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- }, -- }, -- }, -- .dmem_offset = IPU_PSYS_DMEM_OFFSET, -- }, --}; -- --static struct ipu_isys_internal_pdata ipu8_isys_ipdata = { -- .csi2 = { -- .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE, -- }, -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU8_IS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "IS_FW_RD", -- .offset = IPU8_IS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_UC_RD_OFFSET, -- .uao_offset = IPU8_IS_UAO_UC_RD_OFFSET, -- .info_bits = 0x20005101, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU8_IS_ZLX_UC_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0 -- }, -- .zlx_conf = { -- 0, 2, 0, 0 -- }, -- .uao_p_num = IPU8_IS_UAO_UC_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004c, -- 0x0000004d, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_FW_WR", -- .offset = IPU8_IS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_UC_WR_OFFSET, -- .uao_offset = IPU8_IS_UAO_UC_WR_OFFSET, -- .info_bits = 0x20005001, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU8_IS_ZLX_UC_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x2, -- 0x2, -- 0x0, -- }, -- .uao_p_num = IPU8_IS_UAO_UC_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004a, -- 0x0000004b, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_DATA_WR_ISOC", -- .offset = IPU8_IS_MMU_M0_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_M0_OFFSET, -- .uao_offset = IPU8_IS_UAO_M0_WR_OFFSET, -- .info_bits = 0x20004e01, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_M0_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_M0_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_M0_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_M0_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU8_IS_ZLX_M0_NUM, -- .zlx_axi_pool = { -- 0x00000f10, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- }, -- .uao_p_num = IPU8_IS_UAO_M0_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- }, -- }, -- { -- .name = "IS_DATA_WR_SNOOP", -- .offset = IPU8_IS_MMU_M1_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_M1_OFFSET, -- .uao_offset = IPU8_IS_UAO_M1_WR_OFFSET, -- .info_bits = 0x20004f01, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_M1_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_M1_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_M1_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_M1_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU8_IS_ZLX_M1_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- }, -- .uao_p_num = IPU8_IS_UAO_M1_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- }, -- }, -- { -- .name = "IS_UPIPE", -- .offset = IPU8_IS_MMU_UPIPE_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_UPIPE_OFFSET, -- .uao_offset = IPU8_IS_UAO_UPIPE_OFFSET, -- .info_bits = 0x20005201, -- .refill = 0x00002928, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- }, -- .zlx_nr = IPU8_IS_ZLX_UPIPE_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- }, -- .uao_p_num = IPU8_IS_UAO_UPIPE_PLANENUM, -- .uao_p2tlb = { -- 0x00000043, -- 0x00000044, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- }, -- }, -- }, -- .cdc_fifos = 3, -- .cdc_fifo_threshold = {6, 8, 2}, -- .dmem_offset = IPU_ISYS_DMEM_OFFSET, -- .spc_offset = IPU_ISYS_SPC_OFFSET, -- }, -- .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, --}; -- --static struct ipu_psys_internal_pdata ipu8_psys_ipdata = { -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU8_PS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "PS_FW_RD", -- .offset = IPU8_PS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_FW_RD_OFFSET, -- .uao_offset = IPU8_PS_UAO_FW_RD_OFFSET, -- .info_bits = 0x20003a01, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x00000018, -- 0x00000018, -- 0x00000018, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- }, -- .zlx_nr = IPU8_PS_ZLX_FW_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0, 1, 1, 0, 0, -- 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x2, -- 0x0, -- 0x0, -- 0x2, -- 0x2, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_FW_RD_PLANENUM, -- .uao_p2tlb = { -- 0x0000002d, -- 0x00000032, -- 0x00000033, -- 0x00000030, -- 0x00000034, -- 0x00000035, -- 0x00000036, -- 0x00000031, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_FW_WR", -- .offset = IPU8_PS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_FW_WR_OFFSET, -- .uao_offset = IPU8_PS_UAO_FW_WR_OFFSET, -- .info_bits = 0x20003901, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000010, -- 0x00000010, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- }, -- .zlx_nr = IPU8_PS_ZLX_FW_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x0, 0x2, 0x2, 0x0, -- 0x0, 0x0, 0x0, 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_FW_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000002d, -- 0x0000002e, -- 0x0000002f, -- 0x00000030, -- 0x00000031, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_DATA_RD", -- .offset = IPU8_PS_MMU_SRT_RD_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_DATA_RD_OFFSET, -- .uao_offset = IPU8_PS_UAO_SRT_RD_OFFSET, -- .info_bits = 0x20003801, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000014, -- 0x00000018, -- 0x0000001c, -- 0x0000001e, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- 0x0000002c, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- 0x00000036, -- 0x0000003a, -- 0x0000003c, -- 0x0000003c, -- 0x0000003c, -- 0x0000003c, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- 0x0000002c, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- }, -- .zlx_nr = IPU8_PS_ZLX_DATA_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 0, 0, -- 0, 0, -- }, -- .zlx_conf = { -- 0x6, 0x3, 0x3, 0x6, -- 0x2, 0x2, 0x6, 0x6, -- 0x6, 0x3, 0x6, 0x3, -- 0x3, 0x2, 0x2, 0x2, -- 0x2, 0x2, 0x2, 0x6, -- 0x6, 0x3, 0x0, 0x0, -- 0x0, 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_SRT_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000017, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001b, -- 0x0000001c, -- 0x0000001d, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- 0x00000022, -- 0x00000023, -- 0x00000024, -- 0x00000025, -- 0x00000026, -- 0x00000027, -- 0x00000028, -- 0x00000029, -- 0x0000002a, -- 0x0000002b, -- 0x0000002c, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_DATA_WR", -- .offset = IPU8_PS_MMU_SRT_WR_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_DATA_WR_OFFSET, -- .uao_offset = IPU8_PS_UAO_SRT_WR_OFFSET, -- .info_bits = 0x20003701, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001c, -- 0x0000001e, -- 0x00000022, -- 0x00000024, -- 0x00000028, -- 0x0000002a, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- 0x00000036, -- 0x00000038, -- 0x0000003a, -- 0x0000003a, -- 0x0000003a, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- 0x0000002c, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- }, -- .zlx_nr = IPU8_PS_ZLX_DATA_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f50, -- }, -- .zlx_en = { -- 1, 1, 1, 0, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 0, -- 0, 0, -- }, -- .zlx_conf = { -- 0x3, -- 0x6, -- 0x38000002, -- 0x38000000, -- 0x3, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x6, -- 0x3, -- 0x6, -- 0x3, -- 0x6, -- 0x3, -- 0x6, -- 0x3, -- 0x3, -- 0x6, -- 0x3, -- 0x3, -- 0x0, -- 0x0, -- 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_SRT_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000000, -- 0x00000001, -- 0x00000002, -- 0x00000003, -- 0x00000004, -- 0x00000005, -- 0x00000006, -- 0x00000007, -- 0x00000008, -- 0x00000009, -- 0x0000000a, -- 0x0000000b, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000015, -- 0x00000016, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- }, -- }, -- .dmem_offset = IPU_PSYS_DMEM_OFFSET, -- }, --}; -- --static const struct ipu_buttress_ctrl ipu7_isys_buttress_ctrl = { -- .subsys_id = IPU_IS, -- .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -- .ovrd_clk = BUTTRESS_OVERRIDE_IS_CLK, -- .own_clk_ack = BUTTRESS_OWN_ACK_IS_CLK, --}; -- --static const struct ipu_buttress_ctrl ipu7_psys_buttress_ctrl = { -- .subsys_id = IPU_PS, -- .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -- .ovrd_clk = BUTTRESS_OVERRIDE_PS_CLK, -- .own_clk_ack = BUTTRESS_OWN_ACK_PS_CLK, --}; -- --static const struct ipu_buttress_ctrl ipu8_isys_buttress_ctrl = { -- .subsys_id = IPU_IS, -- .ratio = IPU8_IS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, --}; -- --static const struct ipu_buttress_ctrl ipu8_psys_buttress_ctrl = { -- .subsys_id = IPU_PS, -- .ratio = IPU8_PS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -- .own_clk_ack = BUTTRESS_OWN_ACK_PS_PLL, --}; -- --void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -- struct ipu_psys_internal_pdata *psys_ipdata) --{ -- isys_ipdata->csi2.nports = ARRAY_SIZE(ipu7_csi_offsets); -- isys_ipdata->csi2.offsets = ipu7_csi_offsets; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- isys_ipdata->tpg.ntpgs = ARRAY_SIZE(ipu7_tpg_offsets); -- isys_ipdata->tpg.offsets = ipu7_tpg_offsets; -- isys_ipdata->tpg.sels = NULL; --#endif -- isys_ipdata->num_parallel_streams = IPU7_ISYS_NUM_STREAMS; -- psys_ipdata->hw_variant.spc_offset = IPU7_PSYS_SPC_OFFSET; --} -- --static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) --{ -- struct fwnode_handle *endpoint; -- -- if (IS_ERR_OR_NULL(fwnode)) -- return -EINVAL; -- -- endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); -- if (endpoint) { -- fwnode_handle_put(endpoint); -- return 0; -- } -- -- return ipu7_isys_check_fwnode_graph(fwnode->secondary); --} -- --static struct ipu7_bus_device * --ipu7_isys_init(struct pci_dev *pdev, struct device *parent, -- const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -- struct ipu7_isys_subdev_pdata *spdata, -- const struct ipu_isys_internal_pdata *ipdata, -- unsigned int nr) --{ -- struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -- struct ipu7_bus_device *isys_adev; -- struct device *dev = &pdev->dev; -- struct ipu7_isys_pdata *pdata; -- int ret; -- -- ret = ipu7_isys_check_fwnode_graph(fwnode); -- if (ret) { -- if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { -- dev_err(dev, -- "fwnode graph has no endpoints connection\n"); -- return ERR_PTR(-EINVAL); -- } -- -- ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); -- if (ret) { -- dev_err_probe(dev, ret, "IPU bridge init failed\n"); -- return ERR_PTR(ret); -- } -- } -- -- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -- if (!pdata) -- return ERR_PTR(-ENOMEM); -- -- pdata->base = base; -- pdata->ipdata = ipdata; -- pdata->spdata = spdata; -- -- isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -- IPU_ISYS_NAME); -- if (IS_ERR(isys_adev)) { -- dev_err_probe(dev, PTR_ERR(isys_adev), -- "ipu7_bus_initialize_device isys failed\n"); -- kfree(pdata); -- return ERR_CAST(isys_adev); -- } -- -- isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, -- &ipdata->hw_variant); -- if (IS_ERR(isys_adev->mmu)) { -- dev_err_probe(dev, PTR_ERR(isys_adev->mmu), -- "ipu7_mmu_init(isys_adev->mmu) failed\n"); -- put_device(&isys_adev->auxdev.dev); -- kfree(pdata); -- return ERR_CAST(isys_adev->mmu); -- } -- -- isys_adev->mmu->dev = &isys_adev->auxdev.dev; -- isys_adev->subsys = IPU_IS; -- -- ret = ipu7_bus_add_device(isys_adev); -- if (ret) { -- kfree(pdata); -- return ERR_PTR(ret); -- } -- -- return isys_adev; --} -- --static struct ipu7_bus_device * --ipu7_psys_init(struct pci_dev *pdev, struct device *parent, -- const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -- const struct ipu_psys_internal_pdata *ipdata, unsigned int nr) --{ -- struct ipu7_bus_device *psys_adev; -- struct ipu7_psys_pdata *pdata; -- int ret; -- -- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -- if (!pdata) -- return ERR_PTR(-ENOMEM); -- -- pdata->base = base; -- pdata->ipdata = ipdata; -- -- psys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -- IPU_PSYS_NAME); -- if (IS_ERR(psys_adev)) { -- dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), -- "ipu7_bus_initialize_device psys failed\n"); -- kfree(pdata); -- return ERR_CAST(psys_adev); -- } -- -- psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, -- &ipdata->hw_variant); -- if (IS_ERR(psys_adev->mmu)) { -- dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), -- "ipu7_mmu_init(psys_adev->mmu) failed\n"); -- put_device(&psys_adev->auxdev.dev); -- kfree(pdata); -- return ERR_CAST(psys_adev->mmu); -- } -- -- psys_adev->mmu->dev = &psys_adev->auxdev.dev; -- psys_adev->subsys = IPU_PS; -- -- ret = ipu7_bus_add_device(psys_adev); -- if (ret) { -- kfree(pdata); -- return ERR_PTR(ret); -- } -- -- return psys_adev; --} -- --static struct ia_gofo_msg_log_info_ts fw_error_log[IPU_SUBSYS_NUM]; --void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) --{ -- void __iomem *reg = adev->isp->base + ((adev->subsys == IPU_IS) ? -- BUTTRESS_REG_FW_GP24 : -- BUTTRESS_REG_FW_GP8); -- -- memcpy_fromio(&fw_error_log[adev->subsys], reg, -- sizeof(fw_error_log[adev->subsys])); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); -- --#ifdef CONFIG_DEBUG_FS --static struct debugfs_blob_wrapper isys_fw_error; --static struct debugfs_blob_wrapper psys_fw_error; -- --static int ipu7_init_debugfs(struct ipu7_device *isp) --{ -- struct dentry *file; -- struct dentry *dir; -- -- dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -- if (!dir) -- return -ENOMEM; -- -- isys_fw_error.data = &fw_error_log[IPU_IS]; -- isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); -- file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); -- if (!file) -- goto err; -- psys_fw_error.data = &fw_error_log[IPU_PS]; -- psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); -- file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); -- if (!file) -- goto err; -- -- isp->ipu7_dir = dir; -- -- return 0; --err: -- debugfs_remove_recursive(dir); -- return -ENOMEM; --} -- --static void ipu7_remove_debugfs(struct ipu7_device *isp) --{ -- /* -- * Since isys and psys debugfs dir will be created under ipu root dir, -- * mark its dentry to NULL to avoid duplicate removal. -- */ -- debugfs_remove_recursive(isp->ipu7_dir); -- isp->ipu7_dir = NULL; --} --#endif /* CONFIG_DEBUG_FS */ -- --static void ipu7_pci_config_setup(struct pci_dev *dev) --{ -- u16 pci_command; -- -- pci_read_config_word(dev, PCI_COMMAND, &pci_command); -- pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; -- pci_write_config_word(dev, PCI_COMMAND, pci_command); --} -- --static int ipu7_map_fw_code_region(struct ipu7_bus_device *sys, -- void *data, size_t size) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct sg_table *sgt = &sys->fw_sgt; -- struct ipu7_device *isp = adev->isp; -- struct pci_dev *pdev = isp->pdev; -- unsigned long n_pages, i; -- unsigned long attr = 0; -- struct page **pages; -- int ret; -- -- n_pages = PFN_UP(size); -- -- pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); -- if (!pages) -- return -ENOMEM; -- -- for (i = 0; i < n_pages; i++) { -- struct page *p = vmalloc_to_page(data); -- -- if (!p) { -- ret = -ENODEV; -- goto out; -- } -- -- pages[i] = p; -- data += PAGE_SIZE; -- } -- -- ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size, -- GFP_KERNEL); -- if (ret) { -- ret = -ENOMEM; -- goto out; -- } -- -- if (!isp->secure_mode) -- attr |= DMA_ATTR_RESERVE_REGION; -- -- ret = dma_map_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -- if (ret < 0) { -- dev_err(dev, "map fw code[%lu pages %u nents] failed\n", -- n_pages, sgt->nents); -- ret = -ENOMEM; -- sg_free_table(sgt); -- goto out; -- } -- -- ret = ipu7_dma_map_sgtable(sys, sgt, DMA_BIDIRECTIONAL, attr); -- if (ret) { -- dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -- sg_free_table(sgt); -- goto out; -- } -- -- ipu7_dma_sync_sgtable(sys, sgt); -- -- dev_dbg(dev, "fw code region mapped at 0x%pad entries %d\n", -- &sgt->sgl->dma_address, sgt->nents); -- --out: -- kfree(pages); -- -- return ret; --} -- --static void ipu7_unmap_fw_code_region(struct ipu7_bus_device *sys) --{ -- struct pci_dev *pdev = sys->isp->pdev; -- struct sg_table *sgt = &sys->fw_sgt; -- -- ipu7_dma_unmap_sgtable(sys, sgt, DMA_BIDIRECTIONAL, 0); -- dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -- sg_free_table(sgt); --} -- --static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, -- const char *sys_name) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_device *isp = sys->isp; -- int ret; -- -- /* Copy FW binaries to specific location. */ -- ret = ipu7_cpd_copy_binary(isp->cpd_fw->data, sys_name, -- isp->fw_code_region, &sys->fw_entry); -- if (ret) { -- dev_err(dev, "%s binary not found.\n", sys_name); -- return ret; -- } -- -- ret = pm_runtime_get_sync(dev); -- if (ret < 0) { -- dev_err(dev, "Failed to get runtime PM\n"); -- return ret; -- } -- -- ret = ipu7_mmu_hw_init(sys->mmu); -- if (ret) { -- dev_err(dev, "Failed to set mmu hw\n"); -- pm_runtime_put(dev); -- return ret; -- } -- -- /* Map code region. */ -- ret = ipu7_map_fw_code_region(sys, isp->fw_code_region, -- IPU_FW_CODE_REGION_SIZE); -- if (ret) -- dev_err(dev, "Failed to map fw code region for %s.\n", -- sys_name); -- -- ipu7_mmu_hw_cleanup(sys->mmu); -- pm_runtime_put(dev); -- -- return ret; --} -- --static int ipu7_init_fw_code_region(struct ipu7_device *isp) --{ -- int ret; -- -- /* -- * Allocate and map memory for FW execution. -- * Not required in secure mode, in which FW runs in IMR. -- */ -- isp->fw_code_region = vmalloc(IPU_FW_CODE_REGION_SIZE); -- if (!isp->fw_code_region) -- return -ENOMEM; -- -- ret = ipu7_init_fw_code_region_by_sys(isp->isys, "isys"); -- if (ret) -- goto fail_init; -- -- ret = ipu7_init_fw_code_region_by_sys(isp->psys, "psys"); -- if (ret) -- goto fail_init; -- -- return 0; -- --fail_init: -- vfree(isp->fw_code_region); -- -- return ret; --} -- --static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) --{ -- struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; -- struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -- const struct ipu_buttress_ctrl *isys_buttress_ctrl; -- const struct ipu_buttress_ctrl *psys_buttress_ctrl; -- struct ipu_isys_internal_pdata *isys_ipdata; -- struct ipu_psys_internal_pdata *psys_ipdata; -- unsigned int dma_mask = IPU_DMA_MASK; -- struct device *dev = &pdev->dev; -- void __iomem *isys_base = NULL; -- void __iomem *psys_base = NULL; -- phys_addr_t phys, pb_phys; -- struct ipu7_device *isp; -- u32 is_es; -- int ret; -- -- if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es)) -- is_es = 0; -- -- isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); -- if (!isp) -- return -ENOMEM; -- -- isp->pdev = pdev; -- INIT_LIST_HEAD(&isp->devices); -- -- ret = pcim_enable_device(pdev); -- if (ret) -- return dev_err_probe(dev, ret, "Enable PCI device failed\n"); -- -- dev_info(dev, "Device 0x%x (rev: 0x%x)\n", -- pdev->device, pdev->revision); -- -- phys = pci_resource_start(pdev, IPU_PCI_BAR); -- pb_phys = pci_resource_start(pdev, IPU_PCI_PBBAR); -- dev_info(dev, "IPU7 PCI BAR0 base %pap BAR2 base %pap\n", -- &phys, &pb_phys); -- -- isp->base = pcim_iomap_region(pdev, IPU_PCI_BAR, IPU_NAME); -- if (IS_ERR(isp->base)) -- return dev_err_probe(dev, PTR_ERR(isp->base), -- "Failed to I/O memory remapping bar %u\n", -- IPU_PCI_BAR); -- -- isp->pb_base = pcim_iomap_region(pdev, IPU_PCI_PBBAR, IPU_NAME); -- if (IS_ERR(isp->pb_base)) -- return dev_err_probe(dev, PTR_ERR(isp->pb_base), -- "Failed to I/O memory remapping bar %u\n", -- IPU_PCI_PBBAR); -- -- dev_info(dev, "IPU7 PCI BAR0 mapped at %p\n BAR2 mapped at %p\n", -- isp->base, isp->pb_base); -- -- pci_set_drvdata(pdev, isp); -- pci_set_master(pdev); -- -- switch (id->device) { -- case IPU7_PCI_ID: -- isp->hw_ver = IPU_VER_7; -- isp->cpd_fw_name = IPU7_FIRMWARE_NAME; -- isys_ipdata = &ipu7_isys_ipdata; -- psys_ipdata = &ipu7_psys_ipdata; -- isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -- psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -- break; -- case IPU7P5_PCI_ID: -- isp->hw_ver = IPU_VER_7P5; -- isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME; -- isys_ipdata = &ipu7p5_isys_ipdata; -- psys_ipdata = &ipu7p5_psys_ipdata; -- isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -- psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -- break; -- case IPU8_PCI_ID: -- isp->hw_ver = IPU_VER_8; -- isp->cpd_fw_name = IPU8_FIRMWARE_NAME; -- isys_ipdata = &ipu8_isys_ipdata; -- psys_ipdata = &ipu8_psys_ipdata; -- isys_buttress_ctrl = &ipu8_isys_buttress_ctrl; -- psys_buttress_ctrl = &ipu8_psys_buttress_ctrl; -- break; -- default: -- WARN(1, "Unsupported IPU device"); -- return -ENODEV; -- } -- -- ipu_internal_pdata_init(isys_ipdata, psys_ipdata); -- -- isys_base = isp->base + isys_ipdata->hw_variant.offset; -- psys_base = isp->base + psys_ipdata->hw_variant.offset; -- -- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_mask)); -- if (ret) -- return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); -- -- dma_set_max_seg_size(dev, UINT_MAX); -- -- ipu7_pci_config_setup(pdev); -- -- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); -- if (ret < 0) -- return dev_err_probe(dev, ret, "Failed to alloc irq vector\n"); -- -- ret = ipu_buttress_init(isp); -- if (ret) -- goto pci_irq_free; -- -- dev_info(dev, "firmware cpd file: %s\n", isp->cpd_fw_name); -- -- ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev); -- if (ret) { -- dev_err_probe(dev, ret, -- "Requesting signed firmware %s failed\n", -- isp->cpd_fw_name); -- goto buttress_exit; -- } -- -- ret = ipu7_cpd_validate_cpd_file(isp, isp->cpd_fw->data, -- isp->cpd_fw->size); -- if (ret) { -- dev_err_probe(dev, ret, "Failed to validate cpd\n"); -- goto out_ipu_bus_del_devices; -- } -- -- isys_ctrl = devm_kmemdup(dev, isys_buttress_ctrl, -- sizeof(*isys_buttress_ctrl), GFP_KERNEL); -- if (!isys_ctrl) { -- ret = -ENOMEM; -- goto out_ipu_bus_del_devices; -- } -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -- ipu_get_acpi_devices(&dev->platform_data); --#endif -- isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -- dev->platform_data, -- isys_ipdata, 0); -- if (IS_ERR(isp->isys)) { -- ret = PTR_ERR(isp->isys); -- goto out_ipu_bus_del_devices; -- } -- -- psys_ctrl = devm_kmemdup(dev, psys_buttress_ctrl, -- sizeof(*psys_buttress_ctrl), GFP_KERNEL); -- if (!psys_ctrl) { -- ret = -ENOMEM; -- goto out_ipu_bus_del_devices; -- } -- -- isp->psys = ipu7_psys_init(pdev, &isp->isys->auxdev.dev, -- psys_ctrl, psys_base, -- psys_ipdata, 0); -- if (IS_ERR(isp->psys)) { -- ret = PTR_ERR(isp->psys); -- goto out_ipu_bus_del_devices; -- } -- -- ret = devm_request_threaded_irq(dev, pdev->irq, -- ipu_buttress_isr, -- ipu_buttress_isr_threaded, -- IRQF_SHARED, IPU_NAME, isp); -- if (ret) -- goto out_ipu_bus_del_devices; -- -- if (!isp->secure_mode) { -- ret = ipu7_init_fw_code_region(isp); -- if (ret) -- goto out_ipu_bus_del_devices; -- } else { -- ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -- if (ret < 0) { -- dev_err(&isp->psys->auxdev.dev, -- "Failed to get runtime PM\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ret = ipu7_mmu_hw_init(isp->psys->mmu); -- if (ret) { -- dev_err_probe(&isp->pdev->dev, ret, -- "Failed to init MMU hardware\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ret = ipu7_map_fw_code_region(isp->psys, -- (void *)isp->cpd_fw->data, -- isp->cpd_fw->size); -- if (ret) { -- dev_err_probe(&isp->pdev->dev, ret, -- "failed to map fw image\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ret = ipu_buttress_authenticate(isp); -- if (ret) { -- dev_err_probe(&isp->pdev->dev, ret, -- "FW authentication failed\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ipu7_mmu_hw_cleanup(isp->psys->mmu); -- pm_runtime_put(&isp->psys->auxdev.dev); -- } -- --#ifdef CONFIG_DEBUG_FS -- ret = ipu7_init_debugfs(isp); -- if (ret) { -- dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); -- goto out_ipu_bus_del_devices; -- } --#endif -- pm_runtime_put_noidle(dev); -- pm_runtime_allow(dev); -- -- isp->ipu7_bus_ready_to_probe = true; -- -- return 0; -- --out_ipu_bus_del_devices: -- if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->isys); -- if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->psys); --#ifdef CONFIG_DEBUG_FS -- if (!IS_ERR_OR_NULL(isp->fw_code_region)) -- vfree(isp->fw_code_region); --#endif -- if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) -- ipu7_mmu_cleanup(isp->psys->mmu); -- if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -- ipu7_mmu_cleanup(isp->isys->mmu); -- if (!IS_ERR_OR_NULL(isp->psys)) -- pm_runtime_put(&isp->psys->auxdev.dev); -- ipu7_bus_del_devices(pdev); -- release_firmware(isp->cpd_fw); --buttress_exit: -- ipu_buttress_exit(isp); --pci_irq_free: -- pci_free_irq_vectors(pdev); -- -- return ret; --} -- --static void ipu7_pci_remove(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- --#ifdef CONFIG_DEBUG_FS -- ipu7_remove_debugfs(isp); --#endif -- if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->isys); -- if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->psys); -- -- if (!IS_ERR_OR_NULL(isp->fw_code_region)) -- vfree(isp->fw_code_region); -- -- ipu7_mmu_cleanup(isp->isys->mmu); -- ipu7_mmu_cleanup(isp->psys->mmu); -- -- ipu7_bus_del_devices(pdev); -- -- pm_runtime_forbid(&pdev->dev); -- pm_runtime_get_noresume(&pdev->dev); -- -- ipu_buttress_exit(isp); -- -- release_firmware(isp->cpd_fw); -- --} -- --static void ipu7_pci_reset_prepare(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- -- dev_warn(&pdev->dev, "FLR prepare\n"); -- pm_runtime_forbid(&isp->pdev->dev); --} -- --static void ipu7_pci_reset_done(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- -- ipu_buttress_restore(isp); -- if (isp->secure_mode) -- ipu_buttress_reset_authentication(isp); -- -- isp->ipc_reinit = true; -- pm_runtime_allow(&isp->pdev->dev); -- -- dev_warn(&pdev->dev, "FLR completed\n"); --} -- --/* -- * PCI base driver code requires driver to provide these to enable -- * PCI device level PM state transitions (D0<->D3) -- */ --static int ipu7_suspend(struct device *dev) --{ -- return 0; --} -- --static int ipu7_resume(struct device *dev) --{ -- struct pci_dev *pdev = to_pci_dev(dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu_buttress *b = &isp->buttress; -- int ret; -- -- isp->secure_mode = ipu_buttress_get_secure_mode(isp); -- dev_info(dev, "IPU7 in %s mode\n", -- isp->secure_mode ? "secure" : "non-secure"); -- -- ipu_buttress_restore(isp); -- -- ret = ipu_buttress_ipc_reset(isp, &b->cse); -- if (ret) -- dev_err(dev, "IPC reset protocol failed!\n"); -- -- ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -- if (ret < 0) { -- dev_err(dev, "Failed to get runtime PM\n"); -- return 0; -- } -- -- ret = ipu_buttress_authenticate(isp); -- if (ret) -- dev_err(dev, "FW authentication failed(%d)\n", ret); -- -- pm_runtime_put(&isp->psys->auxdev.dev); -- -- return 0; --} -- --static int ipu7_runtime_resume(struct device *dev) --{ -- struct pci_dev *pdev = to_pci_dev(dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- int ret; -- -- ipu_buttress_restore(isp); -- -- if (isp->ipc_reinit) { -- struct ipu_buttress *b = &isp->buttress; -- -- isp->ipc_reinit = false; -- ret = ipu_buttress_ipc_reset(isp, &b->cse); -- if (ret) -- dev_err(dev, "IPC reset protocol failed!\n"); -- } -- -- return 0; --} -- --static const struct dev_pm_ops ipu7_pm_ops = { -- SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) -- RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL) --}; -- --static const struct pci_device_id ipu7_pci_tbl[] = { -- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, -- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, -- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, -- {0,} --}; --MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); -- --static const struct pci_error_handlers pci_err_handlers = { -- .reset_prepare = ipu7_pci_reset_prepare, -- .reset_done = ipu7_pci_reset_done, --}; -- --static struct pci_driver ipu7_pci_driver = { -- .name = IPU_NAME, -- .id_table = ipu7_pci_tbl, -- .probe = ipu7_pci_probe, -- .remove = ipu7_pci_remove, -- .driver = { -- .pm = &ipu7_pm_ops, -- }, -- .err_handler = &pci_err_handlers, --}; -- --module_pci_driver(ipu7_pci_driver); -- --MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); --MODULE_AUTHOR("Bingbu Cao "); --MODULE_AUTHOR("Tianshu Qiu "); --MODULE_AUTHOR("Qingwu Zhang "); --MODULE_AUTHOR("Intel"); --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION("Intel ipu7 pci driver"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7.h b/drivers/media/pci/intel/ipu7/ipu7.h -deleted file mode 100644 -index 454d702ef3d4..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7.h -+++ /dev/null -@@ -1,259 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_H --#define IPU7_H -- --#include --#include --#include -- --#include "ipu7-buttress.h" -- --struct ipu7_bus_device; --struct pci_dev; --struct firmware; -- --#define IPU_NAME "intel-ipu7" --#define IPU_MEDIA_DEV_MODEL_NAME "ipu7" -- --#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin" --#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin" --#define IPU8_FIRMWARE_NAME "intel/ipu/ipu8_fw.bin" -- --#define IPU7_ISYS_NUM_STREAMS 12 -- --#define IPU7_PCI_ID 0x645d --#define IPU7P5_PCI_ID 0xb05d --#define IPU8_PCI_ID 0xd719 -- --#define FW_LOG_BUF_SIZE (2 * 1024 * 1024) -- --enum ipu_version { -- IPU_VER_INVALID = 0, -- IPU_VER_7 = 1, -- IPU_VER_7P5 = 2, -- IPU_VER_8 = 3, --}; -- --static inline bool is_ipu7p5(u8 hw_ver) --{ -- return hw_ver == IPU_VER_7P5; --} -- --static inline bool is_ipu7(u8 hw_ver) --{ -- return hw_ver == IPU_VER_7; --} -- --static inline bool is_ipu8(u8 hw_ver) --{ -- return hw_ver == IPU_VER_8; --} -- --#define IPU_UNIFIED_OFFSET 0 -- --/* -- * ISYS DMA can overshoot. For higher resolutions over allocation is one line -- * but it must be at minimum 1024 bytes. Value could be different in -- * different versions / generations thus provide it via platform data. -- */ --#define IPU_ISYS_OVERALLOC_MIN 1024 -- --#define IPU_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */ --#define IPU_FW_CODE_REGION_START 0x4000000 /* 64MB */ --#define IPU_FW_CODE_REGION_END (IPU_FW_CODE_REGION_START + \ -- IPU_FW_CODE_REGION_SIZE) /* 80MB */ -- --struct ipu7_device { -- struct pci_dev *pdev; -- struct list_head devices; -- struct ipu7_bus_device *isys; -- struct ipu7_bus_device *psys; -- struct ipu_buttress buttress; -- -- const struct firmware *cpd_fw; -- const char *cpd_fw_name; -- /* Only for non-secure mode. */ -- void *fw_code_region; -- -- void __iomem *base; -- void __iomem *pb_base; --#ifdef CONFIG_DEBUG_FS -- struct dentry *ipu7_dir; --#endif -- u8 hw_ver; -- bool ipc_reinit; -- bool secure_mode; -- bool ipu7_bus_ready_to_probe; --}; -- --#define IPU_DMA_MASK 39 --#define IPU_LIB_CALL_TIMEOUT_MS 2000 --#define IPU_PSYS_CMD_TIMEOUT_MS 2000 --#define IPU_PSYS_OPEN_CLOSE_TIMEOUT_US 50 --#define IPU_PSYS_OPEN_CLOSE_RETRY (10000 / IPU_PSYS_OPEN_CLOSE_TIMEOUT_US) -- --#define IPU_ISYS_NAME "isys" --#define IPU_PSYS_NAME "psys" -- --#define IPU_MMU_ADDR_BITS 32 --/* FW is accessible within the first 2 GiB only in non-secure mode. */ --#define IPU_MMU_ADDR_BITS_NON_SECURE 31 -- --#define IPU7_IS_MMU_NUM 4U --#define IPU7_PS_MMU_NUM 4U --#define IPU7P5_IS_MMU_NUM 4U --#define IPU7P5_PS_MMU_NUM 4U --#define IPU8_IS_MMU_NUM 5U --#define IPU8_PS_MMU_NUM 4U --#define IPU_MMU_MAX_NUM 5U /* max(IS, PS) */ --#define IPU_MMU_MAX_TLB_L1_STREAMS 40U --#define IPU_MMU_MAX_TLB_L2_STREAMS 40U --#define IPU_ZLX_MAX_NUM 32U --#define IPU_ZLX_POOL_NUM 8U --#define IPU_UAO_PLANE_MAX_NUM 64U -- --/* -- * To maximize the IOSF utlization, IPU need to send requests in bursts. -- * At the DMA interface with the buttress, there are CDC FIFOs with burst -- * collection capability. CDC FIFO burst collectors have a configurable -- * threshold and is configured based on the outcome of performance measurements. -- * -- * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 -- * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 -- * -- * Threshold values are pre-defined and are arrived at after performance -- * evaluations on a type of IPU -- */ --#define IPU_MAX_VC_IOSF_PORTS 4 -- --/* -- * IPU must configure correct arbitration mechanism related to the IOSF VC -- * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on -- * stall and 1 means stall until the request is completed. -- */ --#define IPU_BTRS_ARB_MODE_TYPE_REARB 0 --#define IPU_BTRS_ARB_MODE_TYPE_STALL 1 -- --/* Currently chosen arbitration mechanism for VC0 */ --#define IPU_BTRS_ARB_STALL_MODE_VC0 IPU_BTRS_ARB_MODE_TYPE_REARB -- --/* Currently chosen arbitration mechanism for VC1 */ --#define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB -- --struct ipu7_isys_subdev_pdata; -- --/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ --#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) --/* Max L2 blocks per stream */ --#define IPU_MMUV2_MAX_L2_BLOCKS 2 --/* Max L1 blocks per stream */ --#define IPU_MMUV2_MAX_L1_BLOCKS 16 --#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \ -- IPU_MMUV2_MAX_L2_BLOCKS) --/* Entries per L1 block */ --#define MMUV2_ENTRIES_PER_L1_BLOCK 16 --#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) --#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE -- --struct ipu7_mmu_hw { -- char name[32]; -- -- void __iomem *base; -- void __iomem *zlx_base; -- void __iomem *uao_base; -- -- u32 offset; -- u32 zlx_offset; -- u32 uao_offset; -- -- u32 info_bits; -- u32 refill; -- u32 collapse_en_bitmap; -- u32 at_sp_arb_cfg; -- -- u32 l1_block; -- u32 l2_block; -- -- u8 nr_l1streams; -- u8 nr_l2streams; -- u32 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS]; -- u32 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS]; -- -- u8 zlx_nr; -- u32 zlx_axi_pool[IPU_ZLX_POOL_NUM]; -- u32 zlx_en[IPU_ZLX_MAX_NUM]; -- u32 zlx_conf[IPU_ZLX_MAX_NUM]; -- -- u32 uao_p_num; -- u32 uao_p2tlb[IPU_UAO_PLANE_MAX_NUM]; --}; -- --struct ipu7_mmu_pdata { -- u32 nr_mmus; -- struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -- int mmid; --}; -- --struct ipu7_isys_csi2_pdata { -- void __iomem *base; --}; -- --struct ipu7_isys_internal_csi2_pdata { -- u32 nports; -- u32 const *offsets; -- u32 gpreg; --}; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --struct ipu7_isys_internal_tpg_pdata { -- u32 ntpgs; -- u32 const *offsets; -- u32 *sels; --}; --#endif -- --struct ipu7_hw_variants { -- unsigned long offset; -- u32 nr_mmus; -- struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -- u8 cdc_fifos; -- u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS]; -- u32 dmem_offset; -- u32 spc_offset; /* SPC offset from psys base */ --}; -- --struct ipu_isys_internal_pdata { -- struct ipu7_isys_internal_csi2_pdata csi2; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- struct ipu7_isys_internal_tpg_pdata tpg; --#endif -- struct ipu7_hw_variants hw_variant; -- u32 num_parallel_streams; -- u32 isys_dma_overshoot; --}; -- --struct ipu7_isys_pdata { -- void __iomem *base; -- const struct ipu_isys_internal_pdata *ipdata; -- struct ipu7_isys_subdev_pdata *spdata; --}; -- --struct ipu_psys_internal_pdata { -- struct ipu7_hw_variants hw_variant; --}; -- --struct ipu7_psys_pdata { -- void __iomem *base; -- const struct ipu_psys_internal_pdata *ipdata; --}; -- --int request_cpd_fw(const struct firmware **firmware_p, const char *name, -- struct device *device); --void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -- struct ipu_psys_internal_pdata *psys_ipdata); --void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev); --#endif /* IPU7_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/Makefile b/drivers/media/pci/intel/ipu7/psys/Makefile -deleted file mode 100644 -index 33eb383a14bc..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/Makefile -+++ /dev/null -@@ -1,18 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --# Copyright (c) 2017 - 2024 Intel Corporation. -- --is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) --ifeq ($(is_kernel_lt_6_10), 1) --ifneq ($(EXTERNAL_BUILD), 1) --src := $(srctree)/$(src) --endif --endif -- --intel-ipu7-psys-objs += ipu-psys.o \ -- ipu7-psys.o \ -- ipu7-fw-psys.o -- --obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o -- --ccflags-y += -I$(src)/ --ccflags-y += -I$(src)/../ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -deleted file mode 100644 -index 582e59c89d7b..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -+++ /dev/null -@@ -1,1545 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --// Copyright (C) 2013 - 2024 Intel Corporation -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include -- --#include "ipu7.h" --#include "ipu7-mmu.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-cpd.h" --#include "ipu7-dma.h" --#include "ipu7-fw-psys.h" --#include "ipu7-psys.h" --#include "ipu7-platform-regs.h" --#include "ipu7-syscom.h" --#include "ipu7-boot.h" -- --#define IPU_PSYS_NUM_DEVICES 4U -- --static int psys_runtime_pm_resume(struct device *dev); --static int psys_runtime_pm_suspend(struct device *dev); -- --#define IPU_FW_CALL_TIMEOUT_JIFFIES \ -- msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) -- --static dev_t ipu7_psys_dev_t; --static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); --static DEFINE_MUTEX(ipu7_psys_mutex); -- --static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) --{ -- struct vm_area_struct *vma; -- unsigned long start, end; -- int npages, array_size; -- struct page **pages; -- struct sg_table *sgt; -- int ret = -ENOMEM; -- int nr = 0; -- u32 flags; -- -- start = (unsigned long)attach->userptr; -- end = PAGE_ALIGN(start + attach->len); -- npages = PHYS_PFN(end - (start & PAGE_MASK)); -- array_size = npages * sizeof(struct page *); -- -- sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -- if (!sgt) -- return -ENOMEM; -- -- WARN_ON_ONCE(attach->npages); -- -- pages = kvzalloc(array_size, GFP_KERNEL); -- if (!pages) -- goto free_sgt; -- -- mmap_read_lock(current->mm); -- vma = vma_lookup(current->mm, start); -- if (unlikely(!vma)) { -- ret = -EFAULT; -- goto error_up_read; -- } -- mmap_read_unlock(current->mm); -- -- flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; -- nr = pin_user_pages_fast(start & PAGE_MASK, npages, -- flags, pages); -- if (nr < npages) -- goto error; -- -- attach->pages = pages; -- attach->npages = npages; -- -- ret = sg_alloc_table_from_pages(sgt, pages, npages, -- start & ~PAGE_MASK, attach->len, -- GFP_KERNEL); -- if (ret < 0) -- goto error; -- -- attach->sgt = sgt; -- -- return 0; -- --error_up_read: -- mmap_read_unlock(current->mm); --error: -- if (nr) -- unpin_user_pages(pages, nr); -- -- kvfree(pages); --free_sgt: -- kfree(sgt); -- -- pr_err("failed to get userpages:%d\n", ret); -- -- return ret; --} -- --static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) --{ -- if (!attach || !attach->userptr || !attach->sgt) -- return; -- -- unpin_user_pages(attach->pages, attach->npages); -- -- kvfree(attach->pages); -- -- sg_free_table(attach->sgt); -- kfree(attach->sgt); -- attach->sgt = NULL; --} -- --static int ipu7_dma_buf_attach(struct dma_buf *dbuf, -- struct dma_buf_attachment *attach) --{ -- struct ipu7_psys_kbuffer *kbuf = dbuf->priv; -- struct ipu7_dma_buf_attach *ipu7_attach; -- int ret; -- -- ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); -- if (!ipu7_attach) -- return -ENOMEM; -- -- ipu7_attach->len = kbuf->len; -- ipu7_attach->userptr = kbuf->userptr; -- -- attach->priv = ipu7_attach; -- -- ret = ipu7_psys_get_userpages(ipu7_attach); -- if (ret) { -- kfree(ipu7_attach); -- return ret; -- } -- -- return 0; --} -- --static void ipu7_dma_buf_detach(struct dma_buf *dbuf, -- struct dma_buf_attachment *attach) --{ -- struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -- -- ipu7_psys_put_userpages(ipu7_attach); -- kfree(ipu7_attach); -- attach->priv = NULL; --} -- --static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, -- enum dma_data_direction dir) --{ -- struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -- struct pci_dev *pdev = to_pci_dev(attach->dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu7_bus_device *adev = isp->psys; -- unsigned long attrs; -- int ret; -- -- attrs = DMA_ATTR_SKIP_CPU_SYNC; -- ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, -- attrs); -- if (ret) { -- dev_err(attach->dev, "pci buf map failed\n"); -- return ERR_PTR(-EIO); -- } -- -- dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, -- DMA_BIDIRECTIONAL); -- -- ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); -- if (ret) { -- dev_err(attach->dev, "ipu7 buf map failed\n"); -- return ERR_PTR(-EIO); -- } -- -- ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); -- -- return ipu7_attach->sgt; --} -- --static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, -- struct sg_table *sgt, -- enum dma_data_direction dir) --{ -- struct pci_dev *pdev = to_pci_dev(attach->dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu7_bus_device *adev = isp->psys; -- -- ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); -- dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); --} -- --static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) --{ -- return -ENOTTY; --} -- --static void ipu7_dma_buf_release(struct dma_buf *buf) --{ -- struct ipu7_psys_kbuffer *kbuf = buf->priv; -- -- if (!kbuf) -- return; -- -- if (kbuf->db_attach) -- ipu7_psys_put_userpages(kbuf->db_attach->priv); -- -- kfree(kbuf); --} -- --static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, -- enum dma_data_direction dir) --{ -- return -ENOTTY; --} -- --static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) --{ -- struct dma_buf_attachment *attach; -- struct ipu7_dma_buf_attach *ipu7_attach; -- -- if (list_empty(&dmabuf->attachments)) -- return -EINVAL; -- -- attach = list_last_entry(&dmabuf->attachments, -- struct dma_buf_attachment, node); -- ipu7_attach = attach->priv; -- -- if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) -- return -EINVAL; -- -- map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); -- map->is_iomem = false; -- if (!map->vaddr) -- return -EINVAL; -- -- return 0; --} -- --static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) --{ -- struct dma_buf_attachment *attach; -- struct ipu7_dma_buf_attach *ipu7_attach; -- -- if (WARN_ON(list_empty(&dmabuf->attachments))) -- return; -- -- attach = list_last_entry(&dmabuf->attachments, -- struct dma_buf_attachment, node); -- ipu7_attach = attach->priv; -- -- if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || -- !ipu7_attach->npages)) -- return; -- -- vm_unmap_ram(map->vaddr, ipu7_attach->npages); --} -- --static struct dma_buf_ops ipu7_dma_buf_ops = { -- .attach = ipu7_dma_buf_attach, -- .detach = ipu7_dma_buf_detach, -- .map_dma_buf = ipu7_dma_buf_map, -- .unmap_dma_buf = ipu7_dma_buf_unmap, -- .release = ipu7_dma_buf_release, -- .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, -- .mmap = ipu7_dma_buf_mmap, -- .vmap = ipu7_dma_buf_vmap, -- .vunmap = ipu7_dma_buf_vunmap, --}; -- --static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) --{ -- u8 graph_id = 0; -- -- for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { -- if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) -- break; -- } -- -- if (graph_id == IPU_PSYS_NUM_STREAMS) -- return -EBUSY; -- -- fh->psys->graph_id[graph_id] = graph_id; -- return graph_id; --} -- --static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) --{ -- fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; --} -- --static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, -- struct ipu7_bus_device *adev) --{ -- if (tq->msg_task) -- ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), -- tq->msg_task, tq->task_dma_addr, 0); -- -- list_del(&tq->list); -- kfree(tq); --} -- --static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, -- struct ipu7_bus_device *adev) --{ -- struct ipu_psys_task_ack *ack; -- struct ipu_psys_task_ack *event; -- struct ipu_psys_task_ack *tmp; -- -- struct ipu_psys_task_queue *tq; -- struct ipu_psys_task_queue *tq_tmp; -- -- mutex_destroy(&ip->event_mutex); -- mutex_destroy(&ip->task_mutex); -- -- list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -- ipu7_psys_free_msg_task(tq, adev); -- } -- -- list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { -- ipu7_psys_free_msg_task(tq, adev); -- } -- -- list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -- list_del(&event->list); -- kfree(event); -- } -- -- list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { -- list_del(&ack->list); -- kfree(ack); -- } --} -- --static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, -- struct ipu7_bus_device *adev) --{ -- struct device *dev = &adev->auxdev.dev; -- struct ipu_psys_task_ack *event; -- struct ipu_psys_task_ack *tmp; -- struct ipu_psys_task_queue *tq; -- struct ipu_psys_task_queue *tq_tmp; -- u8 i; -- -- INIT_LIST_HEAD(&ip->event_list); -- INIT_LIST_HEAD(&ip->ack_list); -- -- INIT_LIST_HEAD(&ip->tq_running_list); -- INIT_LIST_HEAD(&ip->tq_list); -- -- for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { -- event = kzalloc(sizeof(*event), GFP_KERNEL); -- if (!event) -- goto event_cleanup; -- -- list_add(&event->list, &ip->event_list); -- } -- -- for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { -- tq = kzalloc(sizeof(*tq), GFP_KERNEL); -- if (!tq) -- goto tq_cleanup; -- -- list_add(&tq->list, &ip->tq_list); -- -- tq->msg_task = -- ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), -- &tq->task_dma_addr, GFP_KERNEL, 0); -- -- if (!tq->msg_task) { -- dev_err(dev, "Failed to allocate msg task.\n"); -- goto tq_cleanup; -- } -- } -- -- init_completion(&ip->graph_open); -- init_completion(&ip->graph_close); -- -- return 0; -- --tq_cleanup: -- list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -- ipu7_psys_free_msg_task(tq, adev); -- } -- --event_cleanup: -- list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -- list_del(&event->list); -- kfree(event); -- } -- -- return -ENOMEM; --} -- --static int ipu7_psys_open(struct inode *inode, struct file *file) --{ -- struct ipu7_psys *psys = inode_to_ipu_psys(inode); -- struct device *dev = &psys->adev->auxdev.dev; -- struct ipu7_psys_fh *fh; -- struct ipu7_psys_stream *ip; -- int ret; -- -- fh = kzalloc(sizeof(*fh), GFP_KERNEL); -- if (!fh) -- return -ENOMEM; -- -- ip = kzalloc(sizeof(*ip), GFP_KERNEL); -- if (!ip) { -- ret = -ENOMEM; -- goto alloc_failed; -- } -- -- ret = ipu7_psys_stream_init(ip, psys->adev); -- if (ret) -- goto stream_init_failed; -- -- fh->ip = ip; -- ip->fh = fh; -- -- fh->psys = psys; -- -- file->private_data = fh; -- -- mutex_init(&fh->mutex); -- INIT_LIST_HEAD(&fh->bufmap); -- init_waitqueue_head(&fh->wait); -- -- mutex_init(&ip->task_mutex); -- mutex_init(&ip->event_mutex); -- -- mutex_lock(&psys->mutex); -- -- ret = ipu7_psys_get_graph_id(fh); -- if (ret < 0) -- goto open_failed; -- -- fh->ip->graph_id = ret; -- -- ret = pm_runtime_get_sync(dev); -- if (ret < 0) { -- dev_err(dev, "Runtime PM failed (%d)\n", ret); -- goto rpm_put; -- } -- -- list_add_tail(&fh->list, &psys->fhs); -- -- mutex_unlock(&psys->mutex); -- -- return 0; -- --rpm_put: -- pm_runtime_put(dev); -- ipu7_psys_put_graph_id(fh); -- --open_failed: -- ipu7_psys_stream_deinit(ip, psys->adev); -- -- mutex_destroy(&fh->mutex); -- -- mutex_unlock(&psys->mutex); -- --stream_init_failed: -- kfree(ip); -- --alloc_failed: -- kfree(fh); -- -- return ret; --} -- --static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, -- struct ipu7_psys_kbuffer *kbuf) --{ -- if (!kbuf) -- return; -- -- kbuf->valid = false; -- if (kbuf->kaddr) { -- struct iosys_map dmap; -- -- iosys_map_set_vaddr(&dmap, kbuf->kaddr); -- dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); -- } -- -- if (!kbuf->userptr) -- ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, -- DMA_BIDIRECTIONAL, 0); -- -- if (kbuf->sgt) -- dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -- kbuf->sgt, -- DMA_BIDIRECTIONAL); -- if (kbuf->db_attach) -- dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -- dma_buf_put(kbuf->dbuf); -- -- kbuf->db_attach = NULL; -- kbuf->dbuf = NULL; -- kbuf->sgt = NULL; --} -- --static int ipu7_psys_release(struct inode *inode, struct file *file) --{ -- struct ipu7_psys *psys = inode_to_ipu_psys(inode); -- struct ipu7_psys_fh *fh = file->private_data; -- struct ipu7_psys_kbuffer *kbuf, *kbuf0; -- struct dma_buf_attachment *dba; -- -- mutex_lock(&fh->mutex); -- /* clean up buffers */ -- if (!list_empty(&fh->bufmap)) { -- list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { -- list_del(&kbuf->list); -- dba = kbuf->db_attach; -- -- /* Unmap and release buffers */ -- if (kbuf->dbuf && dba) { -- ipu7_psys_kbuf_unmap(fh, kbuf); -- } else { -- if (dba) -- ipu7_psys_put_userpages(dba->priv); -- kfree(kbuf); -- } -- } -- } -- mutex_unlock(&fh->mutex); -- -- ipu7_psys_stream_deinit(fh->ip, psys->adev); -- -- mutex_lock(&psys->mutex); -- list_del(&fh->list); -- -- ipu7_psys_put_graph_id(fh); -- kfree(fh->ip); -- -- mutex_unlock(&psys->mutex); -- mutex_destroy(&fh->mutex); -- kfree(fh); -- -- pm_runtime_put_sync(&psys->adev->auxdev.dev); -- -- return 0; --} -- --static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, -- struct ipu7_psys_fh *fh) --{ -- struct device *dev = &fh->psys->adev->auxdev.dev; -- struct ipu7_psys_kbuffer *kbuf; -- -- DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -- struct dma_buf *dbuf; -- int ret; -- -- if (!buf->base.userptr) { -- dev_err(dev, "Buffer allocation not supported\n"); -- return -EINVAL; -- } -- -- if (!PAGE_ALIGNED(buf->base.userptr)) { -- dev_err(dev, "Not page-aligned userptr is not supported\n"); -- return -EINVAL; -- } -- -- kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -- if (!kbuf) -- return -ENOMEM; -- -- kbuf->len = buf->len; -- kbuf->userptr = buf->base.userptr; -- kbuf->flags = buf->flags; -- -- exp_info.ops = &ipu7_dma_buf_ops; -- exp_info.size = kbuf->len; -- exp_info.flags = O_RDWR; -- exp_info.priv = kbuf; -- -- dbuf = dma_buf_export(&exp_info); -- if (IS_ERR(dbuf)) { -- kfree(kbuf); -- return PTR_ERR(dbuf); -- } -- -- ret = dma_buf_fd(dbuf, 0); -- if (ret < 0) { -- dma_buf_put(dbuf); -- return ret; -- } -- -- kbuf->fd = ret; -- buf->base.fd = ret; -- buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; -- buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; -- kbuf->flags = buf->flags; -- -- mutex_lock(&fh->mutex); -- list_add(&kbuf->list, &fh->bufmap); -- mutex_unlock(&fh->mutex); -- -- dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", -- buf->base.userptr, buf->len, buf->base.fd); -- -- return 0; --} -- --static int --ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) --{ -- return 0; --} -- --static struct ipu7_psys_kbuffer * --ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) --{ -- struct ipu7_psys_kbuffer *kbuf; -- -- list_for_each_entry(kbuf, &fh->bufmap, list) { -- if (kbuf->fd == fd) -- return kbuf; -- } -- -- return NULL; --} -- --static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, -- struct ipu7_psys_kbuffer *kbuf) --{ -- struct device *dev = &fh->psys->adev->auxdev.dev; -- -- if (!kbuf || fd != kbuf->fd) { -- dev_err(dev, "invalid kbuffer\n"); -- return -EINVAL; -- } -- -- /* From now on it is not safe to use this kbuffer */ -- ipu7_psys_kbuf_unmap(fh, kbuf); -- -- list_del(&kbuf->list); -- -- if (!kbuf->userptr) -- kfree(kbuf); -- -- dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); -- -- return 0; --} -- --static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, -- struct ipu7_psys_kbuffer *kbuf) --{ -- struct ipu7_psys *psys = fh->psys; -- struct device *dev = &psys->adev->isp->pdev->dev; -- struct dma_buf *dbuf; -- struct iosys_map dmap; -- int ret; -- -- dbuf = dma_buf_get(fd); -- if (IS_ERR(dbuf)) -- return -EINVAL; -- -- if (!kbuf) { -- /* This fd isn't generated by ipu7_psys_getbuf, it -- * is a new fd. Create a new kbuf item for this fd, and -- * add this kbuf to bufmap list. -- */ -- kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -- if (!kbuf) { -- ret = -ENOMEM; -- goto mapbuf_fail; -- } -- -- list_add(&kbuf->list, &fh->bufmap); -- } -- -- /* fd valid and found, need remap */ -- if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { -- dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", -- fd, kbuf); -- ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -- if (ret) -- goto mapbuf_fail; -- -- kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -- /* changed external dmabuf */ -- if (!kbuf) { -- kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -- if (!kbuf) { -- ret = -ENOMEM; -- goto mapbuf_fail; -- } -- list_add(&kbuf->list, &fh->bufmap); -- } -- } -- -- if (kbuf->sgt) { -- dev_dbg(dev, "fd %d has been mapped!\n", fd); -- dma_buf_put(dbuf); -- goto mapbuf_end; -- } -- -- kbuf->dbuf = dbuf; -- -- if (kbuf->len == 0) -- kbuf->len = kbuf->dbuf->size; -- -- kbuf->fd = fd; -- -- kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); -- if (IS_ERR(kbuf->db_attach)) { -- ret = PTR_ERR(kbuf->db_attach); -- dev_err(dev, "dma buf attach failed\n"); -- goto attach_fail; -- } -- -- kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, -- DMA_BIDIRECTIONAL); -- if (IS_ERR_OR_NULL(kbuf->sgt)) { -- ret = -EINVAL; -- kbuf->sgt = NULL; -- dev_err(dev, "dma buf map attachment failed\n"); -- goto kbuf_map_fail; -- } -- -- if (!kbuf->userptr) { -- ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, -- DMA_BIDIRECTIONAL, 0); -- if (ret) { -- dev_dbg(dev, "ipu7 buf map failed\n"); -- goto kbuf_map_fail; -- } -- } -- -- kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); -- -- /* no need vmap for imported dmabufs */ -- if (!kbuf->userptr) -- goto mapbuf_end; -- -- dmap.is_iomem = false; -- ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); -- if (ret) { -- dev_err(dev, "dma buf vmap failed\n"); -- goto kbuf_map_fail; -- } -- kbuf->kaddr = dmap.vaddr; -- --mapbuf_end: -- dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", -- __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, -- kbuf->len); -- kbuf->valid = true; -- -- return 0; -- --kbuf_map_fail: -- if (!IS_ERR_OR_NULL(kbuf->sgt)) { -- if (!kbuf->userptr) -- ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, -- DMA_BIDIRECTIONAL, 0); -- dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -- kbuf->sgt, -- DMA_BIDIRECTIONAL); -- } -- dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -- --attach_fail: -- list_del(&kbuf->list); -- if (!kbuf->userptr) -- kfree(kbuf); -- --mapbuf_fail: -- dma_buf_put(dbuf); -- -- dev_err(dev, "%s failed for fd %d\n", __func__, fd); -- return ret; --} -- --static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) --{ -- long ret; -- struct ipu7_psys_kbuffer *kbuf; -- -- dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); -- -- mutex_lock(&fh->mutex); -- kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -- ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); -- mutex_unlock(&fh->mutex); -- -- return ret; --} -- --static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) --{ -- struct device *dev = &fh->psys->adev->auxdev.dev; -- struct ipu7_psys_kbuffer *kbuf; -- long ret; -- -- dev_dbg(dev, "IOC_UNMAPBUF\n"); -- -- mutex_lock(&fh->mutex); -- kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -- if (!kbuf) { -- dev_err(dev, -- "buffer with fd %d not found\n", fd); -- mutex_unlock(&fh->mutex); -- return -EINVAL; -- } -- ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -- mutex_unlock(&fh->mutex); -- -- return ret; --} -- --static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, -- struct ipu7_psys_fh *fh) --{ -- struct ipu7_psys *psys = fh->psys; -- int ret = 0; -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -- dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -- fh->ip->graph_state, fh->ip->graph_id); -- return -EINVAL; -- } -- -- if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { -- dev_err(&psys->dev, "nodes is wrong\n"); -- return -EINVAL; -- } -- -- if (copy_from_user(fh->ip->nodes, graph->nodes, -- graph->num_nodes * sizeof(*graph->nodes))) { -- dev_err(&psys->dev, "Failed to copy nodes\n"); -- return -EINVAL; -- } -- -- reinit_completion(&fh->ip->graph_open); -- -- ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); -- if (ret) { -- dev_err(&psys->dev, "Failed to open graph %d\n", -- fh->ip->graph_id); -- return ret; -- } -- -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; -- -- ret = wait_for_completion_timeout(&fh->ip->graph_open, -- IPU_FW_CALL_TIMEOUT_JIFFIES); -- if (!ret) { -- dev_err(&psys->dev, "Open graph %d timeout\n", -- fh->ip->graph_id); -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- return -ETIMEDOUT; -- } -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -- dev_err(&psys->dev, "Failed to set graph\n"); -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- return -EINVAL; -- } -- -- graph->graph_id = fh->ip->graph_id; -- -- return 0; --} -- --static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) --{ -- struct ipu7_psys *psys = fh->psys; -- int ret = 0; -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -- dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -- fh->ip->graph_state, fh->ip->graph_id); -- return -EINVAL; -- } -- -- reinit_completion(&fh->ip->graph_close); -- -- ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); -- if (ret) { -- dev_err(&psys->dev, "Failed to close graph %d\n", -- fh->ip->graph_id); -- return ret; -- } -- -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; -- -- ret = wait_for_completion_timeout(&fh->ip->graph_close, -- IPU_FW_CALL_TIMEOUT_JIFFIES); -- if (!ret) { -- dev_err(&psys->dev, "Close graph %d timeout\n", -- fh->ip->graph_id); -- return -ETIMEDOUT; -- } -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -- dev_err(&psys->dev, "Failed to close graph\n"); -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- return -EINVAL; -- } -- -- return 0; --} -- --static struct ipu_psys_task_queue * --ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, -- struct ipu_psys_task_request *task) --{ -- struct device *dev = &ip->fh->psys->dev; -- struct ipu7_psys_kbuffer *kbuf = NULL; -- struct ipu_psys_task_queue *tq; -- int fd, prevfd = -1; -- u32 i; -- -- if (task->term_buf_count > MAX_GRAPH_TERMINALS) { -- dev_err(dev, "num_teminal_buffer is too large\n"); -- return NULL; -- } -- -- mutex_lock(&ip->task_mutex); -- if (list_empty(&ip->tq_list)) { -- dev_err(dev, "No available take queue for stream %p\n", ip); -- goto unlock; -- } -- -- tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, -- list); -- -- if (copy_from_user(tq->task_buffers, -- task->task_buffers, -- task->term_buf_count * -- sizeof(*task->task_buffers))) { -- dev_err(dev, "failed to copy task buffers\n"); -- goto unlock; -- } -- -- for (i = 0; i < task->term_buf_count; i++) { -- fd = tq->task_buffers[i].term_buf.base.fd; -- kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); -- if (!kbuf) { -- dev_err(dev, "fd %d not found\n", fd); -- goto unlock; -- } -- tq->ipu7_addr[i] = kbuf->dma_addr -- + tq->task_buffers[i].term_buf.data_offset; -- -- if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & -- IPU_BUFFER_FLAG_NO_FLUSH)) -- continue; -- -- prevfd = fd; -- -- if (kbuf->kaddr) -- clflush_cache_range(kbuf->kaddr, kbuf->len); -- } -- -- dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); -- -- list_move_tail(&tq->list, &ip->tq_running_list); -- -- mutex_unlock(&ip->task_mutex); -- return tq; -- --unlock: -- mutex_unlock(&ip->task_mutex); -- return NULL; --} -- --static long ipu_psys_task_request(struct ipu_psys_task_request *task, -- struct ipu7_psys_fh *fh) --{ -- struct ipu7_psys *psys = fh->psys; -- struct ipu_psys_task_queue *tq; -- int ret = 0; -- -- if (task->term_buf_count == 0 || !task->task_buffers) { -- dev_err(&psys->dev, "task_buffer is NULL\n"); -- return -EINVAL; -- } -- -- tq = ipu7_psys_get_task_queue(fh->ip, task); -- if (!tq) { -- dev_err(&psys->dev, "Failed to get task queue\n"); -- return -EINVAL; -- } -- -- ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); -- if (ret) { -- dev_err(&psys->dev, "Failed to request task %d\n", -- fh->ip->graph_id); -- mutex_lock(&fh->ip->task_mutex); -- list_move_tail(&tq->list, &fh->ip->tq_list); -- mutex_unlock(&fh->ip->task_mutex); -- return ret; -- } -- -- tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; -- -- return 0; --} -- --static unsigned int ipu7_psys_poll(struct file *file, -- struct poll_table_struct *wait) --{ -- struct ipu7_psys_fh *fh = file->private_data; -- struct device *dev = &fh->psys->adev->auxdev.dev; -- struct ipu7_psys_stream *ip = fh->ip; -- unsigned int res = 0; -- -- dev_dbg(dev, "ipu psys poll\n"); -- -- poll_wait(file, &fh->wait, wait); -- -- mutex_lock(&ip->event_mutex); -- if (!list_empty(&ip->ack_list)) -- res = POLLIN; -- mutex_unlock(&ip->event_mutex); -- -- dev_dbg(dev, "ipu psys poll res %u\n", res); -- -- return res; --} -- --static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, -- unsigned long arg) --{ -- union { -- struct ipu_psys_graph_info graph; -- struct ipu_psys_task_request task; -- struct ipu_psys_buffer buf; -- struct ipu_psys_event ev; -- } karg; -- struct ipu7_psys_fh *fh = file->private_data; -- long err = 0; -- void __user *up = (void __user *)arg; -- bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && -- cmd != IPU_IOC_GRAPH_CLOSE); -- -- if (copy) { -- if (_IOC_SIZE(cmd) > sizeof(karg)) -- return -ENOTTY; -- -- if (_IOC_DIR(cmd) & _IOC_WRITE) { -- err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); -- if (err) -- return -EFAULT; -- } -- } -- -- switch (cmd) { -- case IPU_IOC_MAPBUF: -- err = ipu7_psys_mapbuf(arg, fh); -- break; -- case IPU_IOC_UNMAPBUF: -- err = ipu7_psys_unmapbuf(arg, fh); -- break; -- case IPU_IOC_GETBUF: -- err = ipu7_psys_getbuf(&karg.buf, fh); -- break; -- case IPU_IOC_PUTBUF: -- err = ipu7_psys_putbuf(&karg.buf, fh); -- break; -- case IPU_IOC_GRAPH_OPEN: -- err = ipu_psys_graph_open(&karg.graph, fh); -- break; -- case IPU_IOC_GRAPH_CLOSE: -- err = ipu_psys_graph_close(arg, fh); -- break; -- case IPU_IOC_TASK_REQUEST: -- err = ipu_psys_task_request(&karg.task, fh); -- break; -- case IPU_IOC_DQEVENT: -- err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); -- break; -- default: -- err = -ENOTTY; -- break; -- } -- -- if (err) -- return err; -- -- if (copy && _IOC_DIR(cmd) & _IOC_READ) -- if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) -- return -EFAULT; -- -- return 0; --} -- --static const struct file_operations ipu7_psys_fops = { -- .open = ipu7_psys_open, -- .release = ipu7_psys_release, -- .unlocked_ioctl = ipu7_psys_ioctl, -- .poll = ipu7_psys_poll, -- .owner = THIS_MODULE, --}; -- --static void ipu7_psys_dev_release(struct device *dev) --{ --} -- --static int psys_runtime_pm_resume(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -- unsigned long flags; -- int ret; -- -- if (!psys) -- return 0; -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- if (psys->ready) { -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- return 0; -- } -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- return ret; -- -- if (!ipu_buttress_auth_done(adev->isp)) { -- dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); -- return 0; -- } -- -- ipu7_psys_setup_hw(psys); -- -- ipu7_psys_subdomains_power(psys, 1); -- -- ret = ipu7_boot_start_fw(psys->adev); -- if (ret) { -- dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); -- return ret; -- } -- -- ret = ipu7_fw_psys_open(psys); -- if (ret) { -- dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); -- return ret; -- } -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- psys->ready = 1; -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- return 0; --} -- --static int psys_runtime_pm_suspend(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -- unsigned long flags; -- -- if (!psys) -- return 0; -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- if (!psys->ready) { -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- return 0; -- } -- psys->ready = 0; -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- ipu7_fw_psys_close(psys); -- -- ipu7_boot_stop_fw(psys->adev); -- -- ipu7_psys_subdomains_power(psys, 0); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- -- return 0; --} -- --/* The following PM callbacks are needed to enable runtime PM in IPU PCI -- * device resume, otherwise, runtime PM can't work in PCI resume from -- * S3 state. -- */ --static int psys_resume(struct device *dev) --{ -- return 0; --} -- --static int psys_suspend(struct device *dev) --{ -- struct ipu7_psys *psys = dev_get_drvdata(dev); -- unsigned long flags; -- int ret = 0; -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- if (psys->ready) -- ret = -EBUSY; -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- return ret; --} -- --static const struct dev_pm_ops psys_pm_ops = { -- .runtime_suspend = psys_runtime_pm_suspend, -- .runtime_resume = psys_runtime_pm_resume, -- .suspend = psys_suspend, -- .resume = psys_resume, --}; -- --#ifdef CONFIG_DEBUG_FS --static int psys_fw_log_init(struct ipu7_psys *psys) --{ -- struct device *dev = &psys->adev->auxdev.dev; -- struct psys_fw_log *fw_log; -- void *log_buf; -- -- if (psys->fw_log) -- return 0; -- -- fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -- if (!fw_log) -- return -ENOMEM; -- -- mutex_init(&fw_log->mutex); -- -- log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!log_buf) -- return -ENOMEM; -- -- fw_log->head = log_buf; -- fw_log->addr = log_buf; -- fw_log->count = 0; -- fw_log->size = 0; -- -- psys->fw_log = fw_log; -- -- return 0; --} -- --static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -- loff_t *pos) --{ -- struct ipu7_psys *psys = file->private_data; -- struct psys_fw_log *fw_log = psys->fw_log; -- struct device *dev = &psys->adev->auxdev.dev; -- u32 log_size; -- void *buf; -- int ret = 0; -- -- if (!fw_log) -- return 0; -- -- buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!buf) -- return -ENOMEM; -- -- mutex_lock(&fw_log->mutex); -- if (!fw_log->size) { -- dev_warn(dev, "no available fw log\n"); -- mutex_unlock(&fw_log->mutex); -- goto free_and_return; -- } -- -- if (fw_log->size > FW_LOG_BUF_SIZE) -- log_size = FW_LOG_BUF_SIZE; -- else -- log_size = fw_log->size; -- -- memcpy(buf, fw_log->addr, log_size); -- dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); -- mutex_unlock(&fw_log->mutex); -- -- ret = simple_read_from_buffer(userbuf, size, pos, buf, -- log_size); --free_and_return: -- kvfree(buf); -- -- return ret; --} -- --static const struct file_operations psys_fw_log_fops = { -- .open = simple_open, -- .owner = THIS_MODULE, -- .read = fwlog_read, -- .llseek = default_llseek, --}; -- --static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) --{ -- struct dentry *file; -- struct dentry *dir; -- -- dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -- if (IS_ERR(dir)) -- return -ENOMEM; -- -- file = debugfs_create_file("fwlog", 0400, -- dir, psys, &psys_fw_log_fops); -- if (IS_ERR(file)) -- goto err; -- -- psys->debugfsdir = dir; -- -- return 0; --err: -- debugfs_remove_recursive(dir); -- return -ENOMEM; --} --#endif -- --static const struct bus_type ipu7_psys_bus = { -- .name = "intel-ipu7-psys", --}; -- --static int ipu7_psys_probe(struct auxiliary_device *auxdev, -- const struct auxiliary_device_id *auxdev_id) --{ -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- struct device *dev = &auxdev->dev; -- struct ipu7_psys *psys; -- unsigned int minor; -- unsigned int i; -- int ret; -- -- if (!adev->isp->ipu7_bus_ready_to_probe) -- return -EPROBE_DEFER; -- -- ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, -- IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); -- if (ret) { -- dev_err(dev, "can't alloc psys chrdev region (%d)\n", -- ret); -- return ret; -- } -- -- ret = pm_runtime_resume_and_get(&auxdev->dev); -- if (ret < 0) -- return ret; -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- goto out_unregister_chr_region; -- -- mutex_lock(&ipu7_psys_mutex); -- -- minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); -- if (minor == IPU_PSYS_NUM_DEVICES) { -- dev_err(dev, "too many devices\n"); -- goto out_unlock; -- } -- -- psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); -- if (!psys) { -- ret = -ENOMEM; -- goto out_unlock; -- } -- -- for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) -- psys->graph_id[i] = INVALID_STREAM_ID; -- -- adev->auxdrv_data = -- (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -- adev->auxdrv = to_auxiliary_drv(dev->driver); -- -- psys->adev = adev; -- psys->pdata = adev->pdata; -- -- cdev_init(&psys->cdev, &ipu7_psys_fops); -- psys->cdev.owner = ipu7_psys_fops.owner; -- -- ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); -- if (ret) { -- dev_err(dev, "cdev_add failed (%d)\n", ret); -- goto out_unlock; -- } -- -- set_bit(minor, ipu7_psys_devices); -- -- spin_lock_init(&psys->ready_lock); -- -- psys->ready = 0; -- psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; -- -- mutex_init(&psys->mutex); -- INIT_LIST_HEAD(&psys->fhs); -- -- ret = ipu7_fw_psys_init(psys); -- if (ret) { -- dev_err(dev, "FW init failed(%d)\n", ret); -- goto out_mutex_destroy; -- } -- -- psys->dev.bus = &ipu7_psys_bus; -- psys->dev.parent = dev; -- psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); -- psys->dev.release = ipu7_psys_dev_release; -- dev_set_name(&psys->dev, "ipu7-psys%d", minor); -- ret = device_register(&psys->dev); -- if (ret < 0) { -- dev_err(&psys->dev, "psys device_register failed\n"); -- goto out_fw_release; -- } -- -- dev_set_drvdata(dev, psys); --#ifdef CONFIG_DEBUG_FS -- psys_fw_log_init(psys); -- ipu7_psys_init_debugfs(psys); --#endif -- dev_info(dev, "IPU psys probe done.\n"); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- pm_runtime_put(&auxdev->dev); -- -- return 0; -- --out_fw_release: -- ipu7_fw_psys_release(psys); --out_mutex_destroy: -- mutex_destroy(&psys->mutex); -- cdev_del(&psys->cdev); --out_unlock: -- /* Safe to call even if the init is not called */ -- mutex_unlock(&ipu7_psys_mutex); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- --out_unregister_chr_region: -- unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -- pm_runtime_put(&auxdev->dev); -- -- return ret; --} -- --static void ipu7_psys_remove(struct auxiliary_device *auxdev) --{ -- struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); -- struct device *dev = &auxdev->dev; --#ifdef CONFIG_DEBUG_FS -- struct ipu7_device *isp = psys->adev->isp; -- -- if (isp->ipu7_dir) -- debugfs_remove_recursive(psys->debugfsdir); --#endif -- -- mutex_lock(&ipu7_psys_mutex); -- ipu7_fw_psys_release(psys); -- device_unregister(&psys->dev); -- clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); -- cdev_del(&psys->cdev); -- mutex_unlock(&ipu7_psys_mutex); -- -- mutex_destroy(&psys->mutex); -- -- unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -- -- dev_info(dev, "removed\n"); --} -- --static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) --{ -- struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -- struct device *dev = &psys->adev->auxdev.dev; -- void __iomem *base = psys->pdata->base; -- u32 status, state; -- int r; -- -- mutex_lock(&psys->mutex); -- r = pm_runtime_get_if_in_use(dev); -- if (!r || WARN_ON_ONCE(r < 0)) { -- mutex_unlock(&psys->mutex); -- return IRQ_NONE; -- } -- -- state = ipu7_boot_get_boot_state(adev); -- if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { -- dev_warn(&psys->dev, "error state %u\n", state); -- } else { -- status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); -- writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -- -- if (status & IRQ_FROM_LOCAL_FW) -- ipu7_psys_handle_events(psys); -- } -- -- pm_runtime_put(dev); -- mutex_unlock(&psys->mutex); -- -- return IRQ_HANDLED; --} -- --static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { -- .isr_threaded = psys_isr_threaded, -- .wake_isr_thread = true, --}; -- --static const struct auxiliary_device_id ipu7_psys_id_table[] = { -- { -- .name = "intel_ipu7.psys", -- .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, -- }, -- { } --}; -- --MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); -- --static struct auxiliary_driver ipu7_psys_driver = { -- .name = IPU_PSYS_NAME, -- .probe = ipu7_psys_probe, -- .remove = ipu7_psys_remove, -- .id_table = ipu7_psys_id_table, -- .driver = { -- .pm = &psys_pm_ops, -- }, --}; -- --module_auxiliary_driver(ipu7_psys_driver); -- --MODULE_AUTHOR("Bingbu Cao "); --MODULE_AUTHOR("Qingwu Zhang "); --MODULE_AUTHOR("Tianshu Qiu "); -- --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION("Intel ipu7 processing system driver"); --MODULE_IMPORT_NS("INTEL_IPU7"); --MODULE_IMPORT_NS("DMA_BUF"); -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -deleted file mode 100644 -index 15ba548ecd83..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -+++ /dev/null -@@ -1,603 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --// Copyright (C) 2016 - 2024 Intel Corporation -- --#include --#include -- --#include -- --#include "abi/ipu7_fw_common_abi.h" --#include "abi/ipu7_fw_msg_abi.h" --#include "abi/ipu7_fw_psys_config_abi.h" -- --#include "ipu7-boot.h" --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-fw-psys.h" --#include "ipu7-syscom.h" --#include "ipu7-psys.h" -- --#define TLV_TYPE(type) ((u32)(type) & 0x3FU) --#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) -- --/* -- * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. -- */ --#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) -- --/* -- * Special node resource ID to identify a generic external node. -- * Required when there is a link to/from IPU and that node. -- */ --#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) -- --int ipu7_fw_psys_init(struct ipu7_psys *psys) --{ -- struct ipu7_bus_device *adev = psys->adev; -- struct device *dev = &adev->auxdev.dev; -- struct ipu7_syscom_context *syscom; -- struct ipu7_psys_config *psys_config; -- struct syscom_queue_config *queue_configs; -- dma_addr_t psys_config_dma_addr; -- u32 freq; -- int i, num_queues, ret; -- -- /* Allocate and init syscom context. */ -- syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -- GFP_KERNEL); -- if (!syscom) -- return -ENOMEM; -- -- adev->syscom = syscom; -- syscom->num_input_queues = FWPS_MSG_ABI_MAX_INPUT_QUEUES; -- syscom->num_output_queues = FWPS_MSG_ABI_MAX_OUTPUT_QUEUES; -- num_queues = syscom->num_input_queues + syscom->num_output_queues; -- queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -- GFP_KERNEL); -- if (!queue_configs) { -- ipu7_fw_psys_release(psys); -- return -ENOMEM; -- } -- syscom->queue_configs = queue_configs; -- queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = -- IPU_PSYS_ACK_QUEUE_SIZE; -- queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = -- IPU_PSYS_OUT_MSG_SIZE; -- queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = -- IPU_PSYS_LOG_QUEUE_SIZE; -- queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = -- IPU_PSYS_OUT_MSG_SIZE; -- queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = -- IPU_PSYS_CMD_QUEUE_SIZE; -- queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = -- FWPS_MSG_HOST2FW_MAX_SIZE; -- queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; -- queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = -- 0; -- -- for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { -- queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; -- queue_configs[i].token_size_in_bytes = -- sizeof(struct ia_gofo_msg_indirect); -- } -- -- /* Allocate PSYS subsys config. */ -- psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), -- &psys_config_dma_addr, GFP_KERNEL, 0); -- if (!psys_config) { -- dev_err(dev, "Failed to allocate psys subsys config.\n"); -- ipu7_fw_psys_release(psys); -- return -ENOMEM; -- } -- psys->subsys_config = psys_config; -- psys->subsys_config_dma_addr = psys_config_dma_addr; -- memset(psys_config, 0, sizeof(struct ipu7_psys_config)); -- ret = ipu_buttress_get_psys_freq(adev->isp, &freq); -- if (ret) { -- dev_err(dev, "Failed to get PSYS frequency.\n"); -- ipu7_fw_psys_release(psys); -- return ret; -- } -- -- ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -- freq, psys_config_dma_addr, 1U); -- if (ret) -- ipu7_fw_psys_release(psys); -- return ret; --} -- --void ipu7_fw_psys_release(struct ipu7_psys *psys) --{ -- struct ipu7_bus_device *adev = psys->adev; -- -- ipu7_boot_release_boot_config(adev); -- if (psys->subsys_config) { -- ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), -- psys->subsys_config, -- psys->subsys_config_dma_addr, 0); -- psys->subsys_config = NULL; -- psys->subsys_config_dma_addr = 0; -- } --} -- --static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) --{ -- const struct ia_gofo_msg_header_ack *ack_header; -- u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -- int ret; -- -- ret = ipu7_fw_psys_event_handle(psys, buffer); -- if (ret) -- return ret; -- -- ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -- -- if (ack_header->header.tlv_header.tlv_type == type) -- return 0; -- -- return -EAGAIN; --} -- --static int ipu7_fw_dev_open(struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_dev_open *token; -- -- dev_dbg(&psys->dev, "send_token: fw psys open\n"); -- -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); -- token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -- token->header.user_token = 0; -- -- token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; -- token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | -- IPU_MSG_DEVICE_OPEN_SEND_IRQ); -- token->enable_power_gating = 0; -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_open(struct ipu7_psys *psys) --{ -- u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -- int ret; -- -- ret = ipu7_fw_dev_open(psys); -- if (ret) { -- dev_err(&psys->dev, "failed to open PSYS dev.\n"); -- return ret; -- } -- psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; -- -- do { -- usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -- IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -- ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); -- if (!ret) { -- dev_dbg(&psys->dev, "dev open done.\n"); -- psys->dev_state = IPU_MSG_DEV_STATE_OPEN; -- return 0; -- } -- } while (retry--); -- -- if (!retry) -- dev_err(&psys->dev, "wait dev open timeout!\n"); -- -- return ret; --} -- --static int ipu7_fw_dev_close(struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_dev_close *token; -- -- dev_dbg(&psys->dev, "send_token: fw psys close\n"); -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); -- token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -- token->header.user_token = 0; -- -- token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | -- IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --void ipu7_fw_psys_close(struct ipu7_psys *psys) --{ -- u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -- int ret; -- -- ret = ipu7_fw_dev_close(psys); -- if (ret) { -- dev_err(&psys->dev, "failed to close PSYS dev.\n"); -- return; -- } -- -- psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; -- -- do { -- usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -- IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -- ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); -- if (!ret) { -- dev_dbg(&psys->dev, "dev close done.\n"); -- psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; -- return; -- } -- } while (retry--); -- -- if (!retry) -- dev_err(&psys->dev, "wait dev close timeout!\n"); --} -- --static void --ipu7_fw_psys_build_node_profile(const struct node_profile *profile, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_cb_profile *cb_profile = -- (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; -- u16 buf_size = sizeof(*cb_profile); -- -- memcpy(cb_profile->profile_base.teb, profile->teb, -- sizeof(cb_profile->profile_base.teb)); -- -- memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); -- memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); -- memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); -- -- cb_profile->profile_base.tlv_header.tlv_type = -- TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); -- cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- -- *buf_ptr_ptr += buf_size; --} -- --/* skip term, return false */ --static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; -- u16 buf_size = sizeof(*msg_term); -- -- if (!term->term_id && !term->buf_size) -- return false; -- -- memset(msg_term, 0, sizeof(*msg_term)); -- msg_term->term_id = term->term_id; -- /* Disable progress message on connect terminals */ -- msg_term->event_req_bm = 0U; -- msg_term->payload_size = term->buf_size; -- -- msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); -- msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- -- *buf_ptr_ptr += buf_size; -- return true; --} -- --/* When skip processing node, just return false */ --static bool ipu7_fw_psys_build_node(const struct graph_node *node, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; -- u16 buf_size = sizeof(*msg_node); -- bool ret = false; -- u8 i = 0; -- u8 max_terms = 0; -- -- memset(msg_node, 0, sizeof(*msg_node)); -- /** -- * Pass node info to FW, do not check for external IP and ISYS -- * As FW expects a external node -- */ -- if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && -- node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -- if (node->profiles[0].teb[0] == 0U) -- return false; -- } -- -- /** -- * Sanity check for dummy node, TEB should set to required one -- */ -- if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || -- node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -- if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || -- node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) -- return false; -- } -- -- msg_node->node_rsrc_id = node->node_rsrc_id; -- msg_node->node_ctx_id = node->node_ctx_id; -- msg_node->num_frags = 1; -- -- *buf_ptr_ptr += buf_size; -- -- msg_node->profiles_list.head_offset = -- (u16)((uintptr_t)*buf_ptr_ptr -- - (uintptr_t)&msg_node->profiles_list); -- for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { -- ipu7_fw_psys_build_node_profile(&node->profiles[i], -- buf_ptr_ptr); -- msg_node->profiles_list.num_elems++; -- } -- -- msg_node->terms_list.head_offset = -- (u16)((uintptr_t)*buf_ptr_ptr - -- (uintptr_t)&msg_node->terms_list); -- max_terms = ARRAY_SIZE(node->terminals); -- for (i = 0; i < max_terms && i < node->num_terms; i++) { -- ret = ipu7_fw_psys_build_node_term(&node->terminals[i], -- buf_ptr_ptr); -- if (ret) -- msg_node->terms_list.num_elems++; -- } -- -- buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; -- msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); -- msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- -- return true; --} -- --static bool ipu7_fw_psys_build_link(const struct graph_link *link, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; -- -- if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && -- !link->ep_src.term_id && !link->ep_dst.term_id) -- return false; -- -- msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; -- msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; -- -- msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; -- msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; -- -- msg_link->foreign_key = link->foreign_key; -- msg_link->streaming_mode = link->streaming_mode; -- msg_link->pbk_id = link->pbk_id; -- msg_link->pbk_slot_id = link->pbk_slot_id; -- msg_link->delayed_link = link->delayed_link; -- -- *buf_ptr_ptr += sizeof(*msg_link); -- -- msg_link->link_options.num_elems = 0; -- msg_link->link_options.head_offset = -- (u16)((uintptr_t)*buf_ptr_ptr - -- (uintptr_t)&msg_link->link_options); -- msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); -- msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); -- -- return true; --} -- --int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -- struct ipu7_psys *psys, -- struct ipu7_psys_stream *ip) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- void *buf_ptr; -- struct ipu7_msg_graph_open *graph_open; -- u32 buf_size = 0; -- bool ret = false; -- u8 i = 0; -- -- dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); -- buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!buf_ptr) -- return -ENODATA; -- -- graph_open = (struct ipu7_msg_graph_open *)buf_ptr; -- -- memset(graph_open, 0, sizeof(*graph_open)); -- graph_open->graph_id = ip->graph_id; -- graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP -- | IPU_MSG_GRAPH_OPEN_SEND_IRQ); -- -- buf_ptr += sizeof(*graph_open); -- graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr -- - (uintptr_t)&graph_open->nodes); -- for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { -- ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); -- if (ret) -- graph_open->nodes.num_elems++; -- } -- -- graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr -- - (uintptr_t)&graph_open->links); -- for (i = 0; i < ARRAY_SIZE(graph->links); i++) { -- ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); -- if (ret) -- graph_open->links.num_elems++; -- } -- -- buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); -- graph_open->header.tlv_header.tlv_type = -- TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); -- graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- graph_open->header.user_token = 0; -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_graph_close *token; -- -- dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); -- token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -- token->header.user_token = 0; -- -- token->graph_id = graph_id; -- token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP -- | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -- struct ipu7_psys_stream *ip, -- struct ipu_psys_task_queue *tq, -- struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_task *msg = tq->msg_task; -- struct ia_gofo_msg_indirect *ind; -- u32 node_q_id = ip->q_id[task->node_ctx_id]; -- u32 teb_hi, teb_lo; -- u64 teb; -- u8 i, term_id; -- u8 num_terms; -- -- ind = ipu7_syscom_get_token(ctx, node_q_id); -- if (!ind) -- return -ENODATA; -- -- memset(msg, 0, sizeof(*msg)); -- msg->graph_id = task->graph_id; -- msg->node_ctx_id = task->node_ctx_id; -- msg->profile_idx = 0U; /* Only one profile on HKR */ -- msg->frame_id = task->frame_id; -- msg->frag_id = 0U; /* No frag, set to 0 */ -- /* -- * Each task has a flag indicating if ack needed, it may be used to -- * reduce interrupts if multiple CBs supported. -- */ -- msg->req_done_msg = 1; -- msg->req_done_irq = 1; -- -- memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, -- sizeof(task->payload_reuse_bm)); -- -- teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; -- teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; -- teb = (teb_lo | (((u64)teb_hi) << 32)); -- -- num_terms = ip->nodes[msg->node_ctx_id].num_terms; -- for (i = 0U; i < num_terms; i++) { -- term_id = tq->task_buffers[i].term_id; -- if ((1U << term_id) & teb) -- msg->term_buffers[term_id] = tq->ipu7_addr[i]; -- } -- -- msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); -- msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); -- msg->header.user_token = (uintptr_t)tq; -- -- ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); -- ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); -- ind->header.msg_options.num_elems = 0; -- ind->header.msg_options.head_offset = 0; -- ind->ref_header = msg->header.tlv_header; -- ind->ref_msg_ptr = tq->task_dma_addr; -- -- ipu7_syscom_put_token(ctx, node_q_id); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- void *token; -- -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -- return 0; --} -- --int ipu7_fw_psys_get_log(struct ipu7_psys *psys) --{ -- void *token; -- struct ia_gofo_msg_log *log_msg; -- u8 msg_type, msg_len; -- u32 count, fmt_id; -- struct device *dev = &psys->adev->auxdev.dev; -- struct psys_fw_log *fw_log = psys->fw_log; -- u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -- -- token = ipu7_syscom_get_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- while (token) { -- log_msg = (struct ia_gofo_msg_log *)token; -- -- msg_type = log_msg->header.tlv_header.tlv_type; -- msg_len = log_msg->header.tlv_header.tlv_len32; -- if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -- dev_warn(dev, "Invalid msg data from Log queue!\n"); -- -- count = log_msg->log_info_ts.log_info.log_counter; -- fmt_id = log_msg->log_info_ts.log_info.fmt_id; -- if (count > fw_log->count + 1) -- dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -- count, fw_log->count); -- -- if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -- dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -- ipu7_syscom_put_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- return -EIO; -- } -- -- if (log_size + fw_log->head - fw_log->addr > -- FW_LOG_BUF_SIZE) -- fw_log->head = fw_log->addr; -- -- memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -- sizeof(struct ia_gofo_msg_log_info_ts)); -- -- fw_log->count = count; -- fw_log->head += log_size; -- fw_log->size += log_size; -- -- ipu7_syscom_put_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- -- token = ipu7_syscom_get_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- }; -- -- return 0; --} -- -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -deleted file mode 100644 -index c43f235386d1..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -+++ /dev/null -@@ -1,42 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2016 - 2024 Intel Corporation -- */ -- --#ifndef IPU7_FW_PSYS_H --#define IPU7_FW_PSYS_H -- --#include "abi/ipu7_fw_common_abi.h" --#include "abi/ipu7_fw_msg_abi.h" -- --#include "ipu7-syscom.h" -- --#include -- --#define IPU_PSYS_MAX_GRAPH_NUMS (8U) --#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS -- --struct ipu7_msg_to_str { -- const enum ipu7_msg_type type; -- const char *msg; --}; -- --struct ipu7_psys; --struct ipu7_psys_stream; --struct ipu_psys_task_queue; -- --int ipu7_fw_psys_init(struct ipu7_psys *psys); --void ipu7_fw_psys_release(struct ipu7_psys *psys); --int ipu7_fw_psys_open(struct ipu7_psys *psys); --void ipu7_fw_psys_close(struct ipu7_psys *psys); --int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -- struct ipu7_psys *psys, -- struct ipu7_psys_stream *ip); --int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); --int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -- struct ipu7_psys_stream *ip, -- struct ipu_psys_task_queue *tq, -- struct ipu7_psys *psys); --int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); --int ipu7_fw_psys_get_log(struct ipu7_psys *psys); --#endif /* IPU7_FW_PSYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -deleted file mode 100644 -index 1ce52ae75a4c..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -+++ /dev/null -@@ -1,398 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --// Copyright (C) 2020 - 2024 Intel Corporation -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-psys.h" --#include "ipu7-platform-regs.h" --#include "ipu7-fw-psys.h" -- --#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) -- --static const struct ipu7_msg_to_str ps_fw_msg[] = { -- {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, -- {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, -- {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, -- {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, -- {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, -- {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, -- {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, -- {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, -- {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, -- {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, -- {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, -- {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, -- {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, -- {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, -- {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, --}; -- --void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) --{ -- struct device *dev = &psys->adev->auxdev.dev; -- struct ipu7_device *isp = psys->adev->isp; -- void __iomem *base = isp->base; -- u32 mask; -- u32 val; -- u32 req; -- int ret; -- -- /* power domain req */ -- mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : -- IPU7_PSYS_DOMAIN_POWER_MASK; -- req = on ? mask : 0; -- val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); -- -- dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", -- on ? "UP" : "DOWN", val); -- if ((val & mask) == req) { -- dev_warn(dev, -- "psys sub-domains power already in request state.\n"); -- return; -- } -- writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); -- ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, -- val, -- !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && -- ((val & mask) == req), -- 100, DOMAIN_POWE_TIMEOUT_US); -- if (ret) -- dev_err(dev, -- "Psys sub-domains power %s timeout! status: 0x%x\n", -- on ? "UP" : "DOWN", val); --} -- --void ipu7_psys_setup_hw(struct ipu7_psys *psys) --{ -- void __iomem *base = psys->pdata->base; -- u32 val = IRQ_FROM_LOCAL_FW; -- -- /* Enable TO_SW IRQ from FW */ -- writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -- writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); -- writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -- -- /* correct the initial printf configuration */ -- writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); --} -- --static struct ipu7_psys_stream* --ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) --{ -- struct ipu7_psys_fh *fh; -- -- list_for_each_entry(fh, &psys->fhs, list) { -- if (fh->ip->graph_id == graph_id) -- return fh->ip; -- } -- -- return NULL; --} -- --static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, -- const void *buffer) --{ -- const struct ipu7_msg_graph_open_ack *ack_msg = -- (const struct ipu7_msg_graph_open_ack *)buffer; -- const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -- struct ipu7_psys_stream *ip; -- struct device *dev = &psys->dev; -- const struct ia_gofo_tlv_header *msg_tlv_item; -- u16 num_items; -- u16 head_offset; -- u32 i; -- -- dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); -- -- if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -- if (!ip) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { -- dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -- goto open_graph_exit; -- } -- -- num_items = ack_header->header.msg_options.num_elems; -- if (!num_items) { -- dev_err(dev, "%s, num_items is 0\n", __func__); -- goto open_graph_exit; -- } -- -- head_offset = ack_header->header.msg_options.head_offset; -- msg_tlv_item = (const struct ia_gofo_tlv_header *) -- ((u8 *)&ack_header->header.msg_options + head_offset); -- -- if (!msg_tlv_item) { -- dev_err(dev, "%s: failed to get tlv item\n", __func__); -- goto open_graph_exit; -- } -- -- for (i = 0U; i < num_items; i++) { -- u32 option_type = msg_tlv_item->tlv_type; -- u32 option_bytes = msg_tlv_item->tlv_len32 * -- TLV_ITEM_ALIGNMENT; -- struct ipu7_msg_graph_open_ack_task_q_info *msg_option = -- (void *)msg_tlv_item; -- -- switch (option_type) { -- case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: -- /* -- * Should do check that: -- * - Each managed node has a queue ID -- * - Queue ID's are sane -- */ -- dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", -- msg_option->node_ctx_id, msg_option->q_id); -- ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; -- break; -- default: -- /* -- * Only one option supported -- */ -- dev_err(dev, "not supported %u\n", option_type); -- break; -- } -- -- msg_tlv_item = (struct ia_gofo_tlv_header *) -- (((u8 *)msg_tlv_item) + option_bytes); -- } -- -- ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; -- --open_graph_exit: -- complete(&ip->graph_open); --} -- --static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, -- void *buffer, u32 error) --{ -- const struct ipu7_msg_task_done *ack_msg = -- (const struct ipu7_msg_task_done *)buffer; -- const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -- const struct ia_gofo_msg_header *msg_header = &ack_header->header; -- struct ipu_psys_task_queue *tq; -- struct device *dev = &psys->dev; -- struct ipu7_psys_stream *ip; -- struct ipu_psys_task_ack *event; -- -- if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return -EINVAL; -- } -- -- ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -- if (!ip) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return -EINVAL; -- } -- -- tq = (void *)(uintptr_t)msg_header->user_token; -- if (!tq) { -- dev_err(dev, "%s: task_token is NULL\n", __func__); -- return -EINVAL; -- } -- -- mutex_lock(&ip->task_mutex); -- if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { -- dev_err(dev, "%s: graph %d node %d error %d\n", __func__, -- ack_msg->graph_id, ack_msg->node_ctx_id, -- tq->task_state); -- -- list_move_tail(&tq->list, &ip->tq_list); -- mutex_unlock(&ip->task_mutex); -- return -ENOENT; -- } -- -- tq->task_state = IPU_MSG_TASK_STATE_DONE; -- dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); -- -- list_move_tail(&tq->list, &ip->tq_list); -- mutex_unlock(&ip->task_mutex); -- -- mutex_lock(&ip->event_mutex); -- if (!list_empty(&ip->event_list)) { -- event = list_first_entry(&ip->event_list, -- struct ipu_psys_task_ack, list); -- event->graph_id = ack_msg->graph_id; -- event->node_ctx_id = ack_msg->node_ctx_id; -- event->frame_id = ack_msg->frame_id; -- event->err_code = error; -- -- list_move_tail(&event->list, &ip->ack_list); -- } else { -- dev_dbg(dev, "event queue is full, add new one\n"); -- event = kzalloc(sizeof(*event), GFP_KERNEL); -- -- if (event) { -- event->graph_id = ack_msg->graph_id; -- event->node_ctx_id = ack_msg->node_ctx_id; -- event->frame_id = ack_msg->frame_id; -- event->err_code = error; -- -- list_add_tail(&event->list, &ip->ack_list); -- } else { -- dev_err(dev, "failed to alloc event buf\n"); -- } -- } -- mutex_unlock(&ip->event_mutex); -- -- wake_up_interruptible(&ip->fh->wait); -- -- return 0; --} -- --static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, -- void *buffer) --{ -- struct ipu7_msg_graph_close_ack *ack_msg = -- (struct ipu7_msg_graph_close_ack *)buffer; -- struct device *dev = &psys->dev; -- struct ipu7_psys_stream *ip; -- -- dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); -- -- if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -- if (!ip) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { -- dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -- goto graph_close_exit; -- } -- -- ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- --graph_close_exit: -- complete(&ip->graph_close); --} -- --void ipu7_psys_handle_events(struct ipu7_psys *psys) --{ -- const struct ia_gofo_msg_header_ack *ack_header; -- u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -- struct device *dev = &psys->dev; -- u32 error = 0; -- int ret = 0; -- -- do { --#ifdef ENABLE_FW_OFFLINE_LOGGER -- ipu7_fw_psys_get_log(psys); --#endif -- ret = ipu7_fw_psys_event_handle(psys, buffer); -- if (ret) -- break; -- -- ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -- -- dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, -- ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); -- -- if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { -- dev_err(dev, "group %d, code %d, detail: %d, %d\n", -- ack_header->err.err_group, -- ack_header->err.err_code, -- ack_header->err.err_detail[0], -- ack_header->err.err_detail[1]); -- error = (ack_header->err.err_group == -- IPU_MSG_ERR_GROUP_TASK) ? -- IPU_PSYS_EVT_ERROR_FRAME : -- IPU_PSYS_EVT_ERROR_INTERNAL; -- } -- -- switch (ack_header->header.tlv_header.tlv_type) { -- case IPU_MSG_TYPE_GRAPH_OPEN_ACK: -- ipu7_psys_handle_graph_open_ack(psys, buffer); -- break; -- case IPU_MSG_TYPE_TASK_DONE: -- ipu7_psys_handle_task_done(psys, buffer, error); -- break; -- case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: -- ipu7_psys_handle_graph_close_ack(psys, buffer); -- break; -- case IPU_MSG_TYPE_GENERAL_ERR: -- /* already printed the log above for general error */ -- break; -- default: -- dev_err(&psys->dev, "Unknown type %d\n", -- ack_header->header.tlv_header.tlv_type); -- } -- } while (1); --} -- --static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, -- struct ipu_psys_event *event) --{ -- struct ipu_psys_task_ack *ack; -- -- mutex_lock(&ip->event_mutex); -- /* Check if there is already an event in the list */ -- if (list_empty(&ip->ack_list)) { -- mutex_unlock(&ip->event_mutex); -- return -EAGAIN; -- } -- -- ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); -- -- event->graph_id = ack->graph_id; -- event->node_ctx_id = ack->node_ctx_id; -- event->frame_id = ack->frame_id; -- event->error = ack->err_code; -- -- list_move_tail(&ack->list, &ip->event_list); -- mutex_unlock(&ip->event_mutex); -- -- dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", -- event->graph_id, event->node_ctx_id, event->frame_id); -- -- return 0; --} -- --long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -- struct ipu7_psys_fh *fh, unsigned int f_flags) --{ -- struct ipu7_psys *psys = fh->psys; -- int ret = 0; -- -- dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); -- -- if (!(f_flags & O_NONBLOCK)) { -- ret = wait_event_interruptible(fh->wait, -- !ipu7_psys_get_event(fh->ip, -- event)); -- if (ret == -ERESTARTSYS) -- return ret; -- } else { -- ret = ipu7_psys_get_event(fh->ip, event); -- } -- -- return ret; --} -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -deleted file mode 100644 -index a72ce4a7c5b9..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -+++ /dev/null -@@ -1,184 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 */ --/* Copyright (C) 2013 - 2024 Intel Corporation */ -- --#ifndef IPU7_PSYS_H --#define IPU7_PSYS_H -- --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-fw-psys.h" -- --#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq -- --#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 --#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 --#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 --#define IPU_PSYS_LOG_QUEUE_SIZE 256 --#define IPU_PSYS_OUT_MSG_SIZE 256 -- --/** -- * Each event from FW will be first queued into a -- * event queue, define the queue depth here -- */ --#define TASK_EVENT_QUEUE_SIZE 3U --/** -- * Each task queue from user will be first queued into -- * a task queue, define the queue depth here -- */ --#define TASK_REQUEST_QUEUE_SIZE 8U -- --#define INVALID_STREAM_ID 0xFF --/** -- * Task request queues per stream -- * -- * Each task will first assigned a task queue buffer here, -- * all the nodes will share the same task queue, maximum -- * queue will be full there. -- */ --struct ipu_psys_task_queue { -- struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; -- dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; -- -- struct ipu7_msg_task *msg_task; -- dma_addr_t task_dma_addr; -- -- struct list_head list; -- -- /* task state of each task input, represent ipu7_msg_task_state */ -- enum ipu7_msg_task_state task_state; --}; -- --struct psys_fw_log { -- struct mutex mutex; /* protect whole struct */ -- void *head; -- void *addr; -- u32 count; /* running counter of log */ -- u32 size; /* actual size of log content, in bits */ --}; -- --/** -- * Task quest event context -- * -- * Each task request should get its event ack from FW and save -- * to this structure and for user dequeue purpose. -- */ --struct ipu_psys_task_ack { -- u8 graph_id; /* graph id of the task request */ -- u8 node_ctx_id; /* logical node id */ -- u8 frame_id; /* frame id of the original task request */ -- -- struct list_head list; -- -- u32 err_code; /* error indication to user */ --}; -- --/** -- * stream here is equal to pipe, each stream has -- * its dedicated graph_id, and task request queue. -- * -- * For multiple stream supported design. -- */ --struct ipu7_psys_stream { -- struct ipu7_psys_fh *fh; -- -- u8 graph_id; /* graph_id on this stream */ -- -- /* Handle events from FW */ -- struct mutex event_mutex; -- struct list_head event_list; /* Reserved event list */ -- struct list_head ack_list; /* Received ack from FW */ -- -- /* Serialize task queue */ -- struct mutex task_mutex; -- struct list_head tq_list; /* Reserved task queue list */ -- struct list_head tq_running_list; /* Running task sent to FW */ -- -- u8 num_nodes; /* Number of enabled nodes */ -- struct graph_node nodes[MAX_GRAPH_NODES]; -- u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ -- -- struct completion graph_open; -- struct completion graph_close; -- -- /* Graph state, represent enum ipu7_msg_graph_state */ -- enum ipu7_msg_graph_state graph_state; --}; -- --struct task_struct; --struct ipu7_psys { -- struct cdev cdev; -- struct device dev; -- -- struct mutex mutex; /* Psys various */ -- int ready; /* psys fw status */ -- spinlock_t ready_lock; /* protect psys firmware state */ -- -- struct list_head fhs; -- -- struct ipu7_psys_pdata *pdata; -- struct ipu7_bus_device *adev; --#ifdef CONFIG_DEBUG_FS -- struct dentry *debugfsdir; --#endif -- -- unsigned long timeout; -- -- struct psys_fw_log *fw_log; -- -- /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ -- u8 graph_id[IPU_PSYS_NUM_STREAMS]; -- -- /* Device state, represent enum ipu7_msg_dev_state */ -- enum ipu7_msg_dev_state dev_state; -- -- struct ipu7_psys_config *subsys_config; -- dma_addr_t subsys_config_dma_addr; --}; -- --struct ipu7_psys_fh { -- struct ipu7_psys *psys; -- struct mutex mutex; /* Protects bufmap & kcmds fields */ -- struct list_head list; -- struct list_head bufmap; -- wait_queue_head_t wait; -- -- struct ipu7_psys_stream *ip; --}; -- --struct ipu7_dma_buf_attach { -- struct device *dev; -- u64 len; -- uintptr_t *userptr; -- struct sg_table *sgt; -- struct page **pages; -- size_t npages; --}; -- --struct ipu7_psys_kbuffer { -- u64 len; -- uintptr_t *userptr; -- u32 flags; -- int fd; -- void *kaddr; -- struct list_head list; -- dma_addr_t dma_addr; -- struct sg_table *sgt; -- struct dma_buf_attachment *db_attach; -- struct dma_buf *dbuf; -- bool valid; /* True when buffer is usable */ --}; -- --#define inode_to_ipu_psys(inode) \ -- container_of((inode)->i_cdev, struct ipu7_psys, cdev) -- --void ipu7_psys_setup_hw(struct ipu7_psys *psys); --void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); --void ipu7_psys_handle_events(struct ipu7_psys *psys); -- --long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -- struct ipu7_psys_fh *fh, unsigned int f_flags); -- --#endif /* IPU7_PSYS_H */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu b/SPECS/kernel-rt/0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu deleted file mode 100644 index 57f1c1782..000000000 --- a/SPECS/kernel-rt/0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu +++ /dev/null @@ -1,173 +0,0 @@ -From 2e94f5756fed15080864a4cf953b6ef279e4fa38 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 4 Nov 2025 14:28:23 +0800 -Subject: [PATCH 1/2] Revert "media: i2c: max9x: uniform serdes driver - compilation" - -This reverts commit 9e46b764cbbd8def4373a51b40bf7abd62c736e3. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/isx031.c | 4 ++-- - drivers/media/i2c/max9x/max9295.c | 9 +++++---- - drivers/media/i2c/max9x/max9296.c | 2 +- - drivers/media/i2c/max9x/serdes.c | 22 ++++++++++++++++------ - 4 files changed, 24 insertions(+), 13 deletions(-) - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index 2d669e964b08..e613cdb06b83 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -370,7 +370,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); -+ ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); - return ret; - } - -@@ -411,7 +411,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return ret; - } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -- (u32)mode); -+ mode); - if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", - cur_mode, mode); -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index 8fbd4215e110..a5bacc3684a9 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -177,7 +177,7 @@ static int max9295_setup_gpio(struct max9x_common *common) - { - struct device *dev = common->dev; - int ret; -- struct max9x_gpio_pdata *gpio_pdata = NULL; -+ struct max9x_gpio_pdata *gpio_pdata; - - if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -@@ -697,14 +697,15 @@ static int max9295_remap_reset(struct max9x_common *common) - struct device *dev = common->dev; - struct max9x_pdata *pdata = dev->platform_data; - u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -- common->client->addr; -+ common->client->addr; - u32 virt_addr = common->client->addr; - - dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, - phys_addr); - -- TRY(ret, regmap_update_bits(common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -- FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ TRY(ret, regmap_update_bits( -+ common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); - - return 0; - } -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index a28aa190364d..41074d60cc01 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -730,7 +730,7 @@ static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned i - link_cfg = MAX9296_LINK_B; - else { - dev_err(dev, "No link was detected"); -- return -EINVAL; -+ return -1; - } - - dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index 8c1db87ed8c2..fce930a1fdea 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -133,7 +133,7 @@ static const struct of_device_id max9x_of_match[] = { - MODULE_DEVICE_TABLE(of, max9x_of_match); - - static const struct i2c_device_id max9x_id[] = { -- { "max9x", 0 }, -+ { "max9x", MAX9296 }, - { "max9296", MAX9296 }, - { "max96724", MAX96724 }, - { "max9295", MAX9295 }, -@@ -841,7 +841,6 @@ void max9x_destroy(struct max9x_common *common) - /* unregister devices? */ - - v4l2_async_unregister_subdev(&common->v4l.sd); -- v4l2_subdev_cleanup(&common->v4l.sd); - media_entity_cleanup(&common->v4l.sd.entity); - - i2c_mux_del_adapters(common->muxc); -@@ -1484,7 +1483,10 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); - -- return &common->v4l.ffmts[fmt->pad]; -+ if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -+ return &common->v4l.ffmts[fmt->pad]; -+ -+ return ERR_PTR(-EINVAL); - } - - static int max9x_get_fmt(struct v4l2_subdev *sd, -@@ -1560,7 +1562,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - for_each_active_route(&state->routing, route) { - if (route->source_pad != pad) - continue; -- if (unlikely(route->sink_pad >= common->v4l.num_pads)) { -+ if (route->sink_pad >= common->v4l.num_pads) { - ret = -EINVAL; - dev_err(common->dev, "Found invalid route sink_pad!"); - goto out_unlock; -@@ -2090,7 +2092,7 @@ int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) - struct device *dev = common->dev; - int ret; - -- if (unlikely(link_id >= common->num_serial_links)) -+ if (link_id >= common->num_serial_links) - return 0; - - serial_link = &common->serial_link[link_id]; -@@ -2411,9 +2413,13 @@ static int max9x_parse_subdev_pdata(struct max9x_common *common, - int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(10000); - -+ dev_dbg(common->dev, "try to select %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2427,7 +2433,7 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - usleep_range(1000, 1050); - - if (time_is_before_jiffies(timeout)) { -- dev_warn(common->dev, "select %d TIMEOUT", chan_id); -+ dev_dbg(common->dev, "select %d TIMEOUT", chan_id); - return -ETIMEDOUT; - } - } while (1); -@@ -2445,8 +2451,12 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - -+ dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt b/SPECS/kernel-rt/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt index ff861dba9..fbbb6d5a7 100644 --- a/SPECS/kernel-rt/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt +++ b/SPECS/kernel-rt/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt @@ -1,4 +1,4 @@ -From f0341e34032802a88510f2d9a1cab712bd56058e Mon Sep 17 00:00:00 2001 +From 4c6e62ab34fa8228916e9a9c3bd9a6cee5d3d84e Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Sat, 27 Feb 2016 08:09:11 +0100 Subject: [PATCH 1/9] drm/i915: Use preempt_disable/enable_rt() where @@ -33,10 +33,10 @@ Signed-off-by: Sebastian Andrzej Siewior 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c -index 70ba7aa26bf4..b82e9fc51af0 100644 +index c15234c1d96e7..d0d9e2c7d5476 100644 --- a/drivers/gpu/drm/i915/display/intel_vblank.c +++ b/drivers/gpu/drm/i915/display/intel_vblank.c -@@ -312,6 +312,20 @@ static void intel_vblank_section_exit(struct intel_display *display) +@@ -315,6 +315,20 @@ static void intel_vblank_section_exit(struct intel_display *display) struct drm_i915_private *i915 = to_i915(display->drm); spin_unlock(&i915->uncore.lock); } @@ -57,7 +57,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 #else static void intel_vblank_section_enter(struct intel_display *display) { -@@ -320,6 +334,17 @@ static void intel_vblank_section_enter(struct intel_display *display) +@@ -323,6 +337,17 @@ static void intel_vblank_section_enter(struct intel_display *display) static void intel_vblank_section_exit(struct intel_display *display) { } @@ -75,7 +75,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 #endif static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, -@@ -356,10 +381,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, +@@ -359,10 +384,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, * timing critical raw register reads, potentially with * preemption disabled, so the following code must not block. */ @@ -89,7 +89,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 /* Get optional system timestamp before query. */ if (stime) -@@ -423,10 +448,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, +@@ -426,10 +451,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, if (etime) *etime = ktime_get(); @@ -103,7 +103,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 /* * While in vblank, position will be negative -@@ -464,13 +489,11 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc) +@@ -467,13 +492,11 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc) unsigned long irqflags; int position; diff --git a/SPECS/kernel-rt/0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov b/SPECS/kernel-rt/0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov deleted file mode 100644 index 0db2923be..000000000 --- a/SPECS/kernel-rt/0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov +++ /dev/null @@ -1,35 +0,0 @@ -From b003ca4cc1194119da149d36f7fc54b4737edae6 Mon Sep 17 00:00:00 2001 -From: Dongwon Kim -Date: Wed, 10 Sep 2025 20:48:45 -0700 -Subject: [PATCH] drm/virtio: Wait until the control and cursor queues are - fully emptied - -It is needed to wait until the queues are fully emptied before -doing freeze/suspend. - -Signed-off-by: Dongwon Kim ---- - drivers/gpu/drm/virtio/virtgpu_drv.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c -index 8f333ed92836..b28ecc19581d 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.c -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c -@@ -181,6 +181,13 @@ static int virtgpu_freeze(struct virtio_device *vdev) - flush_work(&vgdev->ctrlq.dequeue_work); - flush_work(&vgdev->cursorq.dequeue_work); - flush_work(&vgdev->config_changed_work); -+ -+ wait_event(vgdev->ctrlq.ack_queue, -+ vgdev->ctrlq.vq->num_free == vgdev->ctrlq.vq->num_max); -+ -+ wait_event(vgdev->cursorq.ack_queue, -+ vgdev->cursorq.vq->num_free == vgdev->cursorq.vq->num_max); -+ - vdev->config->del_vqs(vdev); - - return 0; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm b/SPECS/kernel-rt/0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm deleted file mode 100644 index 28caeb708..000000000 --- a/SPECS/kernel-rt/0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm +++ /dev/null @@ -1,155 +0,0 @@ -From ba4ba68864fde80935d47423761930085e275d14 Mon Sep 17 00:00:00 2001 -From: Dongwon Kim -Date: Tue, 28 Jun 2022 11:23:10 -0700 -Subject: [PATCH 1/2] drm/virtio: freeze and restore hooks to support suspend - and resume - -virtio device needs to delete before VM suspend happens -then reinitialize all virtqueues again upon resume - -Signed-off-by: Dongwon Kim ---- - drivers/gpu/drm/virtio/virtgpu_drv.c | 53 +++++++++++++++++++++++++++- - drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + - drivers/gpu/drm/virtio/virtgpu_kms.c | 25 +++++++++---- - 3 files changed, 72 insertions(+), 7 deletions(-) - -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c -index 71c6ccad4b99..905246316dc9 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.c -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c -@@ -163,6 +163,53 @@ static unsigned int features[] = { - VIRTIO_GPU_F_RESOURCE_BLOB, - VIRTIO_GPU_F_CONTEXT_INIT, - }; -+ -+#ifdef CONFIG_PM_SLEEP -+static int virtgpu_freeze(struct virtio_device *vdev) -+{ -+ struct drm_device *dev = vdev->priv; -+ struct virtio_gpu_device *vgdev = dev->dev_private; -+ int error; -+ -+ error = drm_mode_config_helper_suspend(dev); -+ if (error) { -+ DRM_ERROR("suspend error %d\n", error); -+ return error; -+ } -+ -+ flush_work(&vgdev->obj_free_work); -+ flush_work(&vgdev->ctrlq.dequeue_work); -+ flush_work(&vgdev->cursorq.dequeue_work); -+ flush_work(&vgdev->config_changed_work); -+ vdev->config->del_vqs(vdev); -+ -+ return 0; -+} -+ -+static int virtgpu_restore(struct virtio_device *vdev) -+{ -+ struct drm_device *dev = vdev->priv; -+ struct virtio_gpu_device *vgdev = dev->dev_private; -+ int error; -+ -+ error = virtio_gpu_find_vqs(vgdev); -+ if (error) { -+ DRM_ERROR("failed to find virt queues\n"); -+ return error; -+ } -+ -+ virtio_device_ready(vdev); -+ -+ error = drm_mode_config_helper_resume(dev); -+ if (error) { -+ DRM_ERROR("resume error %d\n", error); -+ return error; -+ } -+ -+ return 0; -+} -+#endif -+ - static struct virtio_driver virtio_gpu_driver = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), -@@ -171,7 +218,11 @@ static struct virtio_driver virtio_gpu_driver = { - .probe = virtio_gpu_probe, - .remove = virtio_gpu_remove, - .shutdown = virtio_gpu_shutdown, -- .config_changed = virtio_gpu_config_changed -+ .config_changed = virtio_gpu_config_changed, -+#ifdef CONFIG_PM_SLEEP -+ .freeze = virtgpu_freeze, -+ .restore = virtgpu_restore, -+#endif - }; - - static int __init virtio_gpu_driver_init(void) -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h -index f17660a71a3e..1279f998c8e0 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.h -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h -@@ -300,6 +300,7 @@ void virtio_gpu_deinit(struct drm_device *dev); - void virtio_gpu_release(struct drm_device *dev); - int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); - void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); -+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev); - - /* virtgpu_gem.c */ - int virtio_gpu_gem_object_open(struct drm_gem_object *obj, -diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c -index 7dfb2006c561..46963f050b88 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_kms.c -+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c -@@ -114,15 +114,29 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, - vgdev->num_capsets = num_capsets; - } - --int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) -+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev) - { - struct virtqueue_info vqs_info[] = { - { "control", virtio_gpu_ctrl_ack }, - { "cursor", virtio_gpu_cursor_ack }, - }; -- struct virtio_gpu_device *vgdev; -- /* this will expand later */ -+ - struct virtqueue *vqs[2]; -+ int ret; -+ -+ ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); -+ if (ret) -+ return ret; -+ -+ vgdev->ctrlq.vq = vqs[0]; -+ vgdev->cursorq.vq = vqs[1]; -+ -+ return 0; -+} -+ -+int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) -+{ -+ struct virtio_gpu_device *vgdev; - u32 num_scanouts, num_capsets; - int ret = 0; - -@@ -206,13 +220,12 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) - DRM_INFO("features: %ccontext_init\n", - vgdev->has_context_init ? '+' : '-'); - -- ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); -+ ret = virtio_gpu_find_vqs(vgdev); - if (ret) { - DRM_ERROR("failed to find virt queues\n"); - goto err_vqs; - } -- vgdev->ctrlq.vq = vqs[0]; -- vgdev->cursorq.vq = vqs[1]; -+ - ret = virtio_gpu_alloc_vbufs(vgdev); - if (ret) { - DRM_ERROR("failed to alloc vbufs\n"); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm b/SPECS/kernel-rt/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm index 0de8c730e..7bd54b129 100644 --- a/SPECS/kernel-rt/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm +++ b/SPECS/kernel-rt/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm @@ -1,6 +1,6 @@ -From 31de56f202bc6575b2519e08e705213a0f292a76 Mon Sep 17 00:00:00 2001 +From 8154dcb40dcdfcae5595c1b8217e5fbee14e43ba Mon Sep 17 00:00:00 2001 From: "Wong, Chee Yin" -Date: Tue, 6 Jan 2026 09:23:07 +0800 +Date: Tue, 6 Jan 2026 09:05:12 +0800 Subject: [PATCH] drm/xe: Upgrade PTL and BMG GuC to 70.55.3, MTL,LNL,DG2 GuC to 70.53.0 @@ -12,7 +12,7 @@ Signed-off-by: Wong, Chee Yin 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c -index 1e90098b6655..1021b932850f 100644 +index 622b76078567d..3bff84ab4b5d8 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,11 +115,11 @@ struct fw_blobs_by_type { @@ -21,7 +21,7 @@ index 1e90098b6655..1021b932850f 100644 #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 49, 4)) \ - fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 49, 4)) \ -- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 49, 4)) \ +- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \ - fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \ - fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \ + fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 55, 3)) \ diff --git a/SPECS/kernel-rt/0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm b/SPECS/kernel-rt/0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm deleted file mode 100644 index fc378ac2c..000000000 --- a/SPECS/kernel-rt/0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm +++ /dev/null @@ -1,30 +0,0 @@ -From 34426e2fd079c65d4765b02fb4cacb912d0e833d Mon Sep 17 00:00:00 2001 -From: "Mazlan, Hazwan Arif" -Date: Mon, 3 Nov 2025 15:01:54 +0800 -Subject: [PATCH] drm/xe: Upgrade XE GuC to the latest upstream - -Signed-off-by: Mazlan, Hazwan Arif ---- - drivers/gpu/drm/xe/xe_uc_fw.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c -index 9bbdde604923..1e90098b6655 100644 ---- a/drivers/gpu/drm/xe/xe_uc_fw.c -+++ b/drivers/gpu/drm/xe/xe_uc_fw.c -@@ -115,9 +115,9 @@ struct fw_blobs_by_type { - #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED - - #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ -- fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 47, 0)) \ -- fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 45, 2)) \ -- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \ -+ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 49, 4)) \ -+ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 49, 4)) \ -+ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 49, 4)) \ - fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \ - fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \ - fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov b/SPECS/kernel-rt/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov deleted file mode 100644 index e77447566..000000000 --- a/SPECS/kernel-rt/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov +++ /dev/null @@ -1,35 +0,0 @@ -From 7bfac7617b764daf36532e6a4575b4341507ef00 Mon Sep 17 00:00:00 2001 -From: "Kooran Paul, Princy" -Date: Fri, 8 Aug 2025 16:04:16 +0800 -Subject: [PATCH] drm/xe/xe_vm: bypass vm_bind failure as wa to enable hwspice - -Xe driver fails during vm_bind as the HW spice encode pipeline is using -an imported dmabuf. That's why same failure is not seen in other h264 -encoding tests on BMG/PTL. - -While validating bo during vm_bind, update the return status to 0 -instead of error as WA to allow the HW spice encode pipeline continue -work. - -Signed-off-by: Kasireddy, Vivek -Signed-off-by: Goh, Wei Khang1 ---- - drivers/gpu/drm/xe/xe_vm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c -index ec04bef8ae40..3e3b9a5c3056 100644 ---- a/drivers/gpu/drm/xe/xe_vm.c -+++ b/drivers/gpu/drm/xe/xe_vm.c -@@ -3501,7 +3501,7 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, - * how it was mapped on the CPU. Just assume is it - * potentially cached on CPU side. - */ -- return -EINVAL; -+ return 0; - } - - /* If a BO is protected it can only be mapped if the key is still valid */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm b/SPECS/kernel-rt/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm new file mode 100644 index 000000000..7c388f538 --- /dev/null +++ b/SPECS/kernel-rt/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm @@ -0,0 +1,36 @@ +From 10f078fcb45e21c033d328b07df63b351f8ed529 Mon Sep 17 00:00:00 2001 +From: "Kooran Paul, Princy" +Date: Wed, 7 May 2025 15:21:15 +0800 +Subject: [PATCH 1/3] drm/xe/xe_vm: bypass vm_bind failure as wa to enable + hwspice + +Xe driver fails during vm_bind as the HW spice encode pipeline is using +an imported dmabuf. That's why same failure is not seen in other h264 +encoding tests on BMG/PTL. + +While validating bo during vm_bind, update the return status to 0 +instead of error as WA to allow the HW spice encode pipeline continue +work. + +Signed-off-by: Kasireddy, Vivek +Signed-off-by: Goh, Wei Khang1 +--- + drivers/gpu/drm/xe/xe_vm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c +index cdd1dc540a59e..25a2c95e9480f 100644 +--- a/drivers/gpu/drm/xe/xe_vm.c ++++ b/drivers/gpu/drm/xe/xe_vm.c +@@ -3479,7 +3479,7 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, + * how it was mapped on the CPU. Just assume is it + * potentially cached on CPU side. + */ +- return -EINVAL; ++ return 0; + } + + /* If a BO is protected it can only be mapped if the key is still valid */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu b/SPECS/kernel-rt/0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu deleted file mode 100644 index 4bc30d3ad..000000000 --- a/SPECS/kernel-rt/0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu +++ /dev/null @@ -1,62 +0,0 @@ -From 34c4e824b277073582084f12dcb4f85f955932d2 Mon Sep 17 00:00:00 2001 -From: Khai Wen Ng -Date: Thu, 11 Sep 2025 07:49:24 +0800 -Subject: [PATCH 1/6] i2c: add identifier for ATR and MUX adapters - -In ACPI, ATR and MUX adapter are created dynamically -by drivers, there are no pre-allocated resource in ACPI namespace. -Adding identifier so that acpi driver can handle devices that -resides on ATR and MUX adapter. - -Signed-off-by: Khai Wen Ng -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/i2c-atr.c | 2 ++ - drivers/i2c/i2c-mux.c | 2 ++ - include/linux/i2c.h | 4 ++++ - 3 files changed, 8 insertions(+) - -diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c -index dd194476b118..f998e5f43fc0 100644 ---- a/drivers/i2c/i2c-atr.c -+++ b/drivers/i2c/i2c-atr.c -@@ -825,6 +825,8 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) - chan->adap.timeout = parent->timeout; - chan->adap.quirks = parent->quirks; - chan->adap.lock_ops = &i2c_atr_lock_ops; -+ chan->adap.is_atr = true; -+ chan->adap.chan_id = chan_id; - - if (bus_handle) { - device_set_node(&chan->adap.dev, fwnode_handle_get(bus_handle)); -diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c -index 4d8690981a55..ace64e0b0491 100644 ---- a/drivers/i2c/i2c-mux.c -+++ b/drivers/i2c/i2c-mux.c -@@ -321,6 +321,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, - priv->adap.retries = parent->retries; - priv->adap.timeout = parent->timeout; - priv->adap.quirks = parent->quirks; -+ priv->adap.is_mux = true; -+ priv->adap.chan_id = chan_id; - if (muxc->mux_locked) - priv->adap.lock_ops = &i2c_mux_lock_ops; - else -diff --git a/include/linux/i2c.h b/include/linux/i2c.h -index 20fd41b51d5c..83288fe9bcb2 100644 ---- a/include/linux/i2c.h -+++ b/include/linux/i2c.h -@@ -765,6 +765,10 @@ struct i2c_adapter { - - /* 7bit address space */ - DECLARE_BITMAP(addrs_in_instantiation, 1 << 7); -+ -+ bool is_atr; -+ bool is_mux; -+ int chan_id; - }; - #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm b/SPECS/kernel-rt/0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm new file mode 100644 index 000000000..1d5076a11 --- /dev/null +++ b/SPECS/kernel-rt/0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm @@ -0,0 +1,48 @@ +From 16b46595039e12fe48cef4b96a78e3e8061508e5 Mon Sep 17 00:00:00 2001 +From: "Saravanan, Thineshkumar" +Date: Mon, 9 Mar 2026 13:04:27 +0800 +Subject: [PATCH] i915 and xe/gt: Update GuC versions accordingly + +Signed-off-by: Saravanan, Thineshkumar +--- + drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 2 +- + drivers/gpu/drm/xe/xe_uc_fw.c | 8 ++++---- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +index 20af322d35d7..1636023f2fa6 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c ++++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +@@ -96,7 +96,7 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, + fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 49, 4)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \ +- fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \ ++ fw_def(DG1, 0, guc_maj(dg1, 70, 49, 4)) \ + fw_def(ROCKETLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(TIGERLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(JASPERLAKE, 0, guc_mmp(ehl, 70, 1, 1)) \ +diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c +index 049eda2f085b..6aa25c614a55 100644 +--- a/drivers/gpu/drm/xe/xe_uc_fw.c ++++ b/drivers/gpu/drm/xe/xe_uc_fw.c +@@ -115,12 +115,12 @@ struct fw_blobs_by_type { + #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED + + #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ +- fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 55, 3)) \ +- fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 55, 3)) \ +- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ ++ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 58, 0)) \ ++ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 58, 0)) \ ++ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 58, 0)) \ + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ +- fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ ++ fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 49, 4)) \ + fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ + fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 49, 4)) \ + fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ +-- +2.34.1 + diff --git a/SPECS/kernel-rt/0001-i915-gt-GuC-for-legacy-platform.drm b/SPECS/kernel-rt/0001-i915-gt-GuC-for-legacy-platform.drm new file mode 100644 index 000000000..2f104bc44 --- /dev/null +++ b/SPECS/kernel-rt/0001-i915-gt-GuC-for-legacy-platform.drm @@ -0,0 +1,51 @@ +From 09505a121ef45e85d00049090bffbc621729d091 Mon Sep 17 00:00:00 2001 +From: "Mazlan, Hazwan Arif" +Date: Wed, 4 Feb 2026 10:09:52 +0800 +Subject: [PATCH] i915/gt: GuC for legacy platform + +Reference ID: +https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tag/?h=20260110 + +Signed-off-by: Mazlan, Hazwan Arif +--- + drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 4 ++-- + drivers/gpu/drm/xe/xe_uc_fw.c | 6 +++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +index 9852ea55e772e..20af322d35d76 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c ++++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +@@ -90,10 +90,10 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, + #define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \ + fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 53, 0)) \ + fw_def(DG2, 0, guc_maj(dg2, 70, 53, 0)) \ +- fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 49, 4)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 70, 1, 1)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 69, 0, 3)) \ +- fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 49, 4)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \ + fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \ +diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c +index 1b87684f45256..049eda2f085b7 100644 +--- a/drivers/gpu/drm/xe/xe_uc_fw.c ++++ b/drivers/gpu/drm/xe/xe_uc_fw.c +@@ -121,9 +121,9 @@ struct fw_blobs_by_type { + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ + fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ +- fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ +- fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 53, 0)) \ +- fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ ++ fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 49, 4)) \ ++ fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ + fw_def(ROCKETLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ + fw_def(TIGERLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm b/SPECS/kernel-rt/0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm new file mode 100644 index 000000000..2fc93404f --- /dev/null +++ b/SPECS/kernel-rt/0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm @@ -0,0 +1,55 @@ +From ce87e78b6e40fcbb601636cc4a9c42622d6ae4d4 Mon Sep 17 00:00:00 2001 +From: "Mazlan, Hazwan Arif" +Date: Tue, 27 Jan 2026 11:14:26 +0800 +Subject: [PATCH] i915/gt: Upgrade GuCs accordingly to 20260110 baseline + +Following kernel.org binaries latest changeset for i915/xe +https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tag/?h=20260110 + +Signed-off-by: Mazlan, Hazwan Arif +--- + drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 8 ++++---- + drivers/gpu/drm/xe/xe_uc_fw.c | 6 +++--- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +index e848a04a80dc2..9852ea55e772e 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c ++++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +@@ -88,12 +88,12 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, + * security fixes, etc. to be enabled. + */ + #define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \ +- fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 12, 1)) \ +- fw_def(DG2, 0, guc_maj(dg2, 70, 12, 1)) \ +- fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 12, 1)) \ ++ fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 53, 0)) \ ++ fw_def(DG2, 0, guc_maj(dg2, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 53, 0)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 70, 1, 1)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 69, 0, 3)) \ +- fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 12, 1)) \ ++ fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 53, 0)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \ + fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \ +diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c +index 3bff84ab4b5d8..1b87684f45256 100644 +--- a/drivers/gpu/drm/xe/xe_uc_fw.c ++++ b/drivers/gpu/drm/xe/xe_uc_fw.c +@@ -121,9 +121,9 @@ struct fw_blobs_by_type { + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ + fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ +- fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ +- fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 44, 1)) \ +- fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ ++ fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ + fw_def(ROCKETLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ + fw_def(TIGERLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet b/SPECS/kernel-rt/0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet new file mode 100644 index 000000000..039d48009 --- /dev/null +++ b/SPECS/kernel-rt/0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet @@ -0,0 +1,249 @@ +From 120d58bdd8591832aeb89e0f542b6b80f35e6122 Mon Sep 17 00:00:00 2001 +From: Vinicius Costa Gomes +Date: Tue, 4 May 2021 13:47:03 -0700 +Subject: [PATCH 01/14] igc: Add support for DMA timestamp for non-PTP packets + +For PTP traffic, timestamp is retrieved from TXSTMP register. +For all other packets, DMA timestamp field of the Transmit +Descriptor Write-back is used. + +This is required to work around the HW time stamp misses in +TXSTAMP register due to the HW limitation, when heavy traffic +is initiated. + +Signed-off-by: Vinicius Costa Gomes +Signed-off-by: Aravindhan Gunasekaran +Signed-off-by: Muhammad Husaini Zulkifli +--- + drivers/net/ethernet/intel/igc/igc.h | 3 + + drivers/net/ethernet/intel/igc/igc_base.h | 2 +- + drivers/net/ethernet/intel/igc/igc_defines.h | 2 + + drivers/net/ethernet/intel/igc/igc_main.c | 23 ++++++- + drivers/net/ethernet/intel/igc/igc_ptp.c | 72 ++++++++++++++++++++ + 5 files changed, 99 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index a427f05814c1a..b9f846389e75e 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -559,6 +559,7 @@ enum igc_tx_flags { + IGC_TX_FLAGS_TSTAMP_3 = 0x400, + + IGC_TX_FLAGS_TSTAMP_TIMER_1 = 0x800, ++ IGC_TX_FLAGS_DMA_TSTAMP = 0x200, + }; + + enum igc_boards { +@@ -780,6 +781,8 @@ int igc_ptp_hwtstamp_get(struct net_device *netdev, + int igc_ptp_hwtstamp_set(struct net_device *netdev, + struct kernel_hwtstamp_config *config, + struct netlink_ext_ack *extack); ++void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, ++ struct sk_buff *skb, u64 tstamp); + void igc_ptp_tx_hang(struct igc_adapter *adapter); + void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); + void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); +diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h +index eaf17cd031c3a..b05cdfaa5b945 100644 +--- a/drivers/net/ethernet/intel/igc/igc_base.h ++++ b/drivers/net/ethernet/intel/igc/igc_base.h +@@ -18,7 +18,7 @@ union igc_adv_tx_desc { + __le32 olinfo_status; + } read; + struct { +- __le64 rsvd; /* Reserved */ ++ __le64 dma_tstamp; + __le32 nxtseq_seed; + __le32 status; + } wb; +diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h +index 9482ab11f050f..56833df911f08 100644 +--- a/drivers/net/ethernet/intel/igc/igc_defines.h ++++ b/drivers/net/ethernet/intel/igc/igc_defines.h +@@ -315,6 +315,7 @@ + #define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ + #define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ + #define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */ ++#define IGC_TXD_STAT_TS_STAT 0x00000002 /* DMA Timestamp in packet */ + #define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */ + #define IGC_TXD_CMD_IP 0x02000000 /* IP packet */ + #define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +@@ -586,6 +587,7 @@ + /* Transmit Scheduling */ + #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001 + #define IGC_TQAVCTRL_PREEMPT_ENA 0x00000002 ++#define IGC_TQAVCTRL_1588_STAT_EN 0x00000004 + #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008 + #define IGC_TQAVCTRL_FUTSCDDIS 0x00000080 + #define IGC_TQAVCTRL_MIN_FRAG_MASK 0x0000C000 +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 728d7ca5338bf..9b52d60ca315c 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -1556,6 +1556,14 @@ static bool igc_request_tx_tstamp(struct igc_adapter *adapter, struct sk_buff *s + return false; + } + ++static bool igc_is_ptp_packet(struct sk_buff *skb) ++{ ++ __be16 protocol = vlan_get_protocol(skb); ++ ++ /* FIXME: also handle UDP packets */ ++ return protocol == htons(ETH_P_1588); ++} ++ + static int igc_insert_empty_frame(struct igc_ring *tx_ring) + { + struct igc_tx_buffer *empty_info; +@@ -1659,16 +1667,21 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, + + if (unlikely(test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags) && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { ++ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); ++ bool is_ptp = igc_is_ptp_packet(skb); + unsigned long flags; + u32 tstamp_flags; + + spin_lock_irqsave(&adapter->ptp_tx_lock, flags); +- if (igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { ++ if (is_ptp && igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + tx_flags |= IGC_TX_FLAGS_TSTAMP | tstamp_flags; + if (skb->sk && +- READ_ONCE(skb->sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC) ++ READ_ONCE(skb->sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC) + tx_flags |= IGC_TX_FLAGS_TSTAMP_TIMER_1; ++ } else if (!is_ptp) { ++ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; ++ tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; + } else { + adapter->tx_hwtstamp_skipped++; + } +@@ -3170,6 +3183,12 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + if (tx_buffer->type == IGC_TX_BUFFER_TYPE_XSK && + tx_buffer->xsk_pending_ts) + break; ++ if (eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_TS_STAT) && ++ tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { ++ u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); ++ ++ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); ++ } + + /* clear next_to_watch to prevent false hangs */ + tx_buffer->next_to_watch = NULL; +diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c +index 7aae83c108fd7..c1af5edae7aae 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ptp.c ++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c +@@ -446,6 +446,31 @@ static int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, + return 0; + } + ++static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, ++ struct skb_shared_hwtstamps *hwtstamps, ++ u64 systim) ++{ ++ struct igc_hw *hw = &adapter->hw; ++ u32 sec, nsec; ++ ++ /* FIXME: use a workqueue to read these values to avoid ++ * reading these registers in the hot path. ++ */ ++ nsec = rd32(IGC_SYSTIML); ++ sec = rd32(IGC_SYSTIMH); ++ ++ switch (adapter->hw.mac.type) { ++ case igc_i225: ++ memset(hwtstamps, 0, sizeof(*hwtstamps)); ++ ++ /* HACK */ ++ hwtstamps->hwtstamp = ktime_set(sec, systim & 0xFFFFFFFF); ++ break; ++ default: ++ break; ++ } ++} ++ + /** + * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer + * @adapter: Pointer to adapter the packet buffer belongs to +@@ -579,6 +604,7 @@ static void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter) + static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) + { + struct igc_hw *hw = &adapter->hw; ++ u32 tqavctrl; + int i; + + /* Clear the flags first to avoid new packets to be enqueued +@@ -593,14 +619,26 @@ static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) + /* Now we can clean the pending TX timestamp requests. */ + igc_ptp_clear_tx_tstamp(adapter); + ++ tqavctrl = rd32(IGC_TQAVCTRL); ++ tqavctrl &= ~IGC_TQAVCTRL_1588_STAT_EN; ++ ++ wr32(IGC_TQAVCTRL, tqavctrl); ++ + wr32(IGC_TSYNCTXCTL, 0); + } + + static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) + { + struct igc_hw *hw = &adapter->hw; ++ u32 tqavctrl; + int i; + ++ /* Enable DMA Fetch timestamping */ ++ tqavctrl = rd32(IGC_TQAVCTRL); ++ tqavctrl |= IGC_TQAVCTRL_1588_STAT_EN; ++ ++ wr32(IGC_TQAVCTRL, tqavctrl); ++ + wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); + + /* Read TXSTMP registers to discard any timestamp previously stored. */ +@@ -841,6 +879,40 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) + } + } + ++void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, ++ struct sk_buff *skb, u64 tstamp) ++{ ++ struct skb_shared_hwtstamps shhwtstamps; ++ int adjust = 0; ++ ++ if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) ++ return; ++ ++ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); ++ ++ /* FIXME: Use different latencies for DMA timestamps? */ ++ switch (adapter->link_speed) { ++ case SPEED_10: ++ adjust = IGC_I225_TX_LATENCY_10; ++ break; ++ case SPEED_100: ++ adjust = IGC_I225_TX_LATENCY_100; ++ break; ++ case SPEED_1000: ++ adjust = IGC_I225_TX_LATENCY_1000; ++ break; ++ case SPEED_2500: ++ adjust = IGC_I225_TX_LATENCY_2500; ++ break; ++ } ++ ++ shhwtstamps.hwtstamp = ++ ktime_add_ns(shhwtstamps.hwtstamp, adjust); ++ ++ /* Notify the stack and free the skb after we've unlocked */ ++ skb_tstamp_tx(skb, &shhwtstamps); ++} ++ + /** + * igc_ptp_tx_tstamp_event + * @adapter: board private structure +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet b/SPECS/kernel-rt/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet index c752057a9..af69304f9 100644 --- a/SPECS/kernel-rt/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet +++ b/SPECS/kernel-rt/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet @@ -1,7 +1,7 @@ -From eec452978215d4a46a735237cf74a8bfd7acdca6 Mon Sep 17 00:00:00 2001 +From def1018b29cd29ff0d8fc01068a119777bec7012 Mon Sep 17 00:00:00 2001 From: Malli C Date: Mon, 25 Jan 2021 17:43:51 -0800 -Subject: [PATCH 01/19] igc: Only dump registers if configured to dump HW +Subject: [PATCH 1/4] igc: Only dump registers if configured to dump HW information To avoid polluting the users logs with register dumps, only dump the @@ -20,7 +20,7 @@ Signed-off-by: Aravindhan Gunasekaran 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_dump.c b/drivers/net/ethernet/intel/igc/igc_dump.c -index c09c95cc5f70..4abbeb763374 100644 +index c09c95cc5f70e..4abbeb763374e 100644 --- a/drivers/net/ethernet/intel/igc/igc_dump.c +++ b/drivers/net/ethernet/intel/igc/igc_dump.c @@ -308,6 +308,9 @@ void igc_regs_dump(struct igc_adapter *adapter) diff --git a/SPECS/kernel-rt/0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet b/SPECS/kernel-rt/0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet deleted file mode 100644 index 6b8d933c9..000000000 --- a/SPECS/kernel-rt/0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet +++ /dev/null @@ -1,47 +0,0 @@ -From 67aa7e943ecb59b1fd526792049cd98eaaa95693 Mon Sep 17 00:00:00 2001 -From: Chwee-Lin Choong -Date: Thu, 4 Dec 2025 20:21:50 +0800 -Subject: [PATCH] igc: Reduce TSN TX packet buffer from 7KB to 5KB per queue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The previous 7 KB per queue caused TX unit hangs under heavy -timestamping load. Reducing to 5 KB avoids these hangs and matches -the TSN recommendation in I225/I226 SW User Manual Section 7.5.4. - -The 8 KB “freed” by this change is currently unused. This reduction -is not expected to impact throughput, as the i226 is PCIe-limited -for small TSN packets rather than TX-buffer-limited. - -Fixes: 0d58cdc902da ("igc: optimize TX packet buffer utilization for TSN mode") -Reported-by: Zdenek Bouska -Closes: https://lore.kernel.org/netdev/AS1PR10MB5675DBFE7CE5F2A9336ABFA4EBEAA@AS1PR10MB5675.EURPRD10.PROD.OUTLOOK.COM/ -Reviewed-by: Paul Menzel -Reviewed-by: Simon Horman -Reviewed-by: Aleksandr Loktionov -Signed-off-by: Chwee-Lin Choong ---- - drivers/net/ethernet/intel/igc/igc_defines.h | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h -index ea673df406a9..56833df911f0 100644 ---- a/drivers/net/ethernet/intel/igc/igc_defines.h -+++ b/drivers/net/ethernet/intel/igc/igc_defines.h -@@ -444,9 +444,10 @@ - #define IGC_TXPBSIZE_DEFAULT ( \ - IGC_TXPB0SIZE(20) | IGC_TXPB1SIZE(0) | IGC_TXPB2SIZE(0) | \ - IGC_TXPB3SIZE(0) | IGC_OS2BMCPBSIZE(4)) -+/* TSN value following I225/I226 SW User Manual Section 7.5.4 */ - #define IGC_TXPBSIZE_TSN ( \ -- IGC_TXPB0SIZE(7) | IGC_TXPB1SIZE(7) | IGC_TXPB2SIZE(7) | \ -- IGC_TXPB3SIZE(7) | IGC_OS2BMCPBSIZE(4)) -+ IGC_TXPB0SIZE(5) | IGC_TXPB1SIZE(5) | IGC_TXPB2SIZE(5) | \ -+ IGC_TXPB3SIZE(5) | IGC_OS2BMCPBSIZE(4)) - - #define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */ - #define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-igc-Remove-XDP-metadata-invalidation.ethernet b/SPECS/kernel-rt/0001-igc-Remove-XDP-metadata-invalidation.ethernet deleted file mode 100644 index 9d9e34a32..000000000 --- a/SPECS/kernel-rt/0001-igc-Remove-XDP-metadata-invalidation.ethernet +++ /dev/null @@ -1,32 +0,0 @@ -From 4485c4753d8ab46da9c1cee8e4f946b5d8c48a98 Mon Sep 17 00:00:00 2001 -From: KhaiWenTan -Date: Wed, 24 Dec 2025 15:13:12 +0800 -Subject: [PATCH] igc: Remove XDP metadata invalidation - -Delete the else branch that invalidates XDP metadata when RX -timestamping is off. Only set metadata when hardware RX timestamping -is enabled, simplifying the receive path logic. - -Fixes: c2697c3fa88d ("igc: Enable HW RX Timestamp for AF_XDP ZC") -Signed-off-by: KhaiWenTan -Signed-off-by: Song Yoong Siang ---- - drivers/net/ethernet/intel/igc/igc_main.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 68fd90e2839e..2a3f4cf2c4ca 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -2868,8 +2868,6 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) - md = bi->xdp->data - sizeof(*md); - md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); - bi->xdp->data_meta = md; -- } else { -- xdp_set_data_meta_invalid(bi->xdp); - } - - res = __igc_xdp_run_prog(adapter, prog, bi->xdp); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet b/SPECS/kernel-rt/0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet deleted file mode 100644 index ddfe2ded8..000000000 --- a/SPECS/kernel-rt/0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet +++ /dev/null @@ -1,121 +0,0 @@ -From 68f67a96c9ee707aa42bb6c5b4da02a8e3e4a935 Mon Sep 17 00:00:00 2001 -From: Chwee-Lin Choong -Date: Fri, 28 Nov 2025 18:53:04 +0800 -Subject: [PATCH] igc: fix race condition in TX timestamp read for register 0 -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The current HW bug workaround checks the TXTT_0 ready bit first, -then reads TXSTMPL_0 twice (before and after reading TXSTMPH_0) -to detect whether a new timestamp was captured by timestamp -register 0 during the workaround. - -This sequence has a race: if a new timestamp is captured after -checking the TXTT_0 bit but before the first TXSTMPL_0 read, the -detection fails because both the “old” and “new” values come from -the same timestamp. - -Fix by reading TXSTMPL_0 first to establish a baseline, then -checking the TXTT_0 bit. This ensures any timestamp captured -during the race window will be detected. - -Old sequence: - 1. Check TXTT_0 ready bit - 2. Read TXSTMPL_0 (baseline) - 3. Read TXSTMPH_0 (interrupt workaround) - 4. Read TXSTMPL_0 (detect changes vs baseline) - -New sequence: - 1. Read TXSTMPL_0 (baseline) - 2. Check TXTT_0 ready bit - 3. Read TXSTMPH_0 (interrupt workaround) - 4. Read TXSTMPL_0 (detect changes vs baseline) - -Fixes: c789ad7cbebc ("igc: Work around HW bug causing missing timestamps") -Suggested-by: Avi Shalev -Reviewed-by: Aleksandr Loktionov -Co-developed-by: Song Yoong Siang -Signed-off-by: Song Yoong Siang -Signed-off-by: Chwee-Lin Choong ---- - drivers/net/ethernet/intel/igc/igc_ptp.c | 43 ++++++++++++++---------- - 1 file changed, 25 insertions(+), 18 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index 4fef3f1abe29..23a55ca30677 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -815,36 +815,43 @@ static void igc_ptp_tx_reg_to_stamp(struct igc_adapter *adapter, - static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) - { - struct igc_hw *hw = &adapter->hw; -+ u32 txstmpl_old; - u64 regval; - u32 mask; - int i; - -+ /* Establish baseline of TXSTMPL_0 before checking TXTT_0. -+ * This baseline is used to detect if a new timestamp arrives in -+ * register 0 during the hardware bug workaround below. -+ */ -+ txstmpl_old = rd32(IGC_TXSTMPL); -+ - mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY; - if (mask & IGC_TSYNCTXCTL_TXTT_0) { - regval = rd32(IGC_TXSTMPL); - regval |= (u64)rd32(IGC_TXSTMPH) << 32; - } else { -- /* There's a bug in the hardware that could cause -- * missing interrupts for TX timestamping. The issue -- * is that for new interrupts to be triggered, the -- * IGC_TXSTMPH_0 register must be read. -+ /* TXTT_0 not set - register 0 has no new timestamp initially. -+ * -+ * Hardware bug: Future timestamp interrupts won't fire unless -+ * TXSTMPH_0 is read, even if the timestamp was captured in -+ * registers 1-3. - * -- * To avoid discarding a valid timestamp that just -- * happened at the "wrong" time, we need to confirm -- * that there was no timestamp captured, we do that by -- * assuming that no two timestamps in sequence have -- * the same nanosecond value. -+ * Workaround: Read TXSTMPH_0 here to enable future interrupts. -+ * However, this read clears TXTT_0. If a timestamp arrives in -+ * register 0 after checking TXTT_0 but before this read, it -+ * would be lost. - * -- * So, we read the "low" register, read the "high" -- * register (to latch a new timestamp) and read the -- * "low" register again, if "old" and "new" versions -- * of the "low" register are different, a valid -- * timestamp was captured, we can read the "high" -- * register again. -+ * To detect this race: We saved a baseline read of TXSTMPL_0 -+ * before TXTT_0 check. After performing the workaround read of -+ * TXSTMPH_0, we read TXSTMPL_0 again. Since consecutive -+ * timestamps never share the same nanosecond value, a change -+ * between the baseline and new TXSTMPL_0 indicates a timestamp -+ * arrived during the race window. If so, read the complete -+ * timestamp. - */ -- u32 txstmpl_old, txstmpl_new; -+ u32 txstmpl_new; - -- txstmpl_old = rd32(IGC_TXSTMPL); - rd32(IGC_TXSTMPH); - txstmpl_new = rd32(IGC_TXSTMPL); - -@@ -859,7 +866,7 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) - - done: - /* Now that the problematic first register was handled, we can -- * use retrieve the timestamps from the other registers -+ * retrieve the timestamps from the other registers - * (starting from '1') with less complications. - */ - for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-issei-initial-driver-skeleton.security b/SPECS/kernel-rt/0001-issei-initial-driver-skeleton.security index 5af327e63..33d70d129 100644 --- a/SPECS/kernel-rt/0001-issei-initial-driver-skeleton.security +++ b/SPECS/kernel-rt/0001-issei-initial-driver-skeleton.security @@ -1,7 +1,7 @@ -From 28c125e31e9c3aac24f3d4229282ee03ed5f05a9 Mon Sep 17 00:00:00 2001 +From b50d2a7550d5cf9d63171d44eda4bdc124c0dd1e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 17 Apr 2025 12:38:06 +0300 -Subject: [PATCH 1/5] issei: initial driver skeleton +Subject: [PATCH 1/7] issei: initial driver skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -21,19 +21,19 @@ Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- - Documentation/driver-api/issei/issei.rst | 138 ++++++++++++++++++ + Documentation/driver-api/issei/issei.rst | 138 ++++++++++++++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/issei/Kconfig | 13 ++ drivers/misc/issei/Makefile | 7 + - drivers/misc/issei/cdev.c | 160 +++++++++++++++++++++ - drivers/misc/issei/cdev.h | 12 ++ - drivers/misc/issei/dma.c | 172 ++++++++++++++++++++++ - drivers/misc/issei/dma.h | 58 ++++++++ - drivers/misc/issei/hw_msg.h | 176 +++++++++++++++++++++++ - drivers/misc/issei/issei_dev.h | 153 ++++++++++++++++++++ - include/uapi/linux/issei.h | 78 ++++++++++ - 12 files changed, 969 insertions(+) + drivers/misc/issei/cdev.c | 225 +++++++++++++++++++++++ + drivers/misc/issei/cdev.h | 18 ++ + drivers/misc/issei/dma.c | 172 +++++++++++++++++ + drivers/misc/issei/dma.h | 58 ++++++ + drivers/misc/issei/hw_msg.h | 176 ++++++++++++++++++ + drivers/misc/issei/issei_dev.h | 155 ++++++++++++++++ + include/uapi/linux/issei.h | 67 +++++++ + 12 files changed, 1031 insertions(+) create mode 100644 Documentation/driver-api/issei/issei.rst create mode 100644 drivers/misc/issei/Kconfig create mode 100644 drivers/misc/issei/Makefile @@ -47,7 +47,7 @@ Signed-off-by: Alexander Usyskin diff --git a/Documentation/driver-api/issei/issei.rst b/Documentation/driver-api/issei/issei.rst new file mode 100644 -index 000000000000..8bfbe1e0c6b6 +index 0000000000000..07b0412d1b3d9 --- /dev/null +++ b/Documentation/driver-api/issei/issei.rst @@ -0,0 +1,138 @@ @@ -74,7 +74,7 @@ index 000000000000..8bfbe1e0c6b6 +upon connection. + +Intel ISSEI Driver -+================ ++================== + +The driver exposes a character device with device nodes /dev/isseiX. + @@ -139,12 +139,12 @@ index 000000000000..8bfbe1e0c6b6 +User space API + +ioctl: -+======= ++====== + +The Intel ISSEI Driver supports the following ioctl commands: + +IOCTL_ISSEI_CONNECT_CLIENT -+------------------------- ++-------------------------- +Connect to firmware Feature/Client. + +.. code-block:: none @@ -176,7 +176,7 @@ index 000000000000..8bfbe1e0c6b6 + requests up to bytes 2k and received responses up to 2k bytes). + +IOCTL_ISSEI_DISCONNECT_CLIENT -+------------------------- ++----------------------------- +Disconnect from firmware Feature/Client. + +.. code-block:: none @@ -190,7 +190,7 @@ index 000000000000..8bfbe1e0c6b6 + ENODEV Device or Connection is not initialized or ready. + ENOTCONN Feature/Client is not connected. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index b9c11f67315f..43ab202e0b22 100644 +index b9c11f67315f0..43ab202e0b224 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -661,4 +661,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig" @@ -200,17 +200,17 @@ index b9c11f67315f..43ab202e0b22 100644 +source "drivers/misc/issei/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index e2e66f5f4fb8..60d004bcde29 100644 +index b32a2597d2467..01072ebecf829 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile -@@ -76,3 +76,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o +@@ -75,3 +75,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-$(CONFIG_INTEL_SSEI) += issei/ diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig new file mode 100644 -index 000000000000..ffd027e826df +index 0000000000000..ffd027e826dfd --- /dev/null +++ b/drivers/misc/issei/Kconfig @@ -0,0 +1,13 @@ @@ -229,7 +229,7 @@ index 000000000000..ffd027e826df + If in doubt, select N. diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile new file mode 100644 -index 000000000000..9e3ef22305ac +index 0000000000000..9e3ef22305ac9 --- /dev/null +++ b/drivers/misc/issei/Makefile @@ -0,0 +1,7 @@ @@ -242,18 +242,18 @@ index 000000000000..9e3ef22305ac +issei-objs += dma.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c new file mode 100644 -index 000000000000..26ac95e5f818 +index 0000000000000..c265012062c71 --- /dev/null +++ b/drivers/misc/issei/cdev.c -@@ -0,0 +1,160 @@ +@@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include +#include ++#include ++#include +#include +#include -+#include -+#include +#include + +#include "issei_dev.h" @@ -297,64 +297,129 @@ index 000000000000..26ac95e5f818 + if (ret >= 0) + idev->minor = ret; + else if (ret == -ENOSPC) -+ dev_err(idev->dev, "too many issei devices\n"); ++ dev_err(&idev->dev, "too many issei devices\n"); + + return ret; +} + -+static void issei_minor_free(struct issei_device *idev) ++static void issei_minor_free(int minor) +{ + guard(mutex)(&issei_minor_lock); + -+ idr_remove(&issei_idr, idev->minor); ++ idr_remove(&issei_idr, minor); ++} ++ ++static void issei_device_release(struct device *dev) ++{ ++ kfree(dev_get_drvdata(dev)); ++} ++ ++static void issei_device_init(struct issei_device *idev, struct device *parent, ++ const struct issei_dma_length *dma_length, ++ const struct issei_hw_ops *ops) ++{ ++ idev->parent = parent; ++ idev->power_down = false; ++ init_waitqueue_head(&idev->wait_rst_irq); ++ atomic_set(&idev->rst_irq, 0); ++ init_waitqueue_head(&idev->wait_rst_state); ++ idev->rst_state = ISSEI_RST_STATE_INIT; ++ ++ mutex_init(&idev->host_client_lock); ++ INIT_LIST_HEAD(&idev->host_client_list); ++ idev->host_client_last_id = 0; ++ idev->host_client_count = 0; ++ ++ mutex_init(&idev->fw_client_lock); ++ INIT_LIST_HEAD(&idev->fw_client_list); ++ ++ idev->dma.length = *dma_length; ++ ++ INIT_LIST_HEAD(&idev->write_queue); ++ ++ idev->ops = ops; +} + +/** + * issei_register: register issei character device -+ * @idev: the device structure ++ * @hw_size: size of the hardware structure to allocate + * @parent: parent device ++ * @dma_length: structure with DMA sizes ++ * @ops: hardware-related operations + * -+ * Return: 0 on sucess, <0 on failure ++ * Return: pointer allocated to issei_device structure, error on failure + */ -+int issei_register(struct issei_device *idev, struct device *parent) ++struct issei_device *issei_register(size_t hw_size, struct device *parent, ++ const struct issei_dma_length *dma_length, ++ const struct issei_hw_ops *ops) +{ -+ struct device *clsdev; ++ struct issei_device *idev; ++ int minor; + int ret, devno; + ++ idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); ++ if (!idev) ++ return ERR_PTR(-ENOMEM); ++ ++ issei_device_init(idev, parent, dma_length, ops); ++ + ret = issei_minor_get(idev); -+ if (ret < 0) -+ return ret; ++ if (ret < 0) { ++ kfree(idev); ++ return ERR_PTR(ret); ++ } ++ minor = idev->minor; + + devno = MKDEV(MAJOR(issei_devt), idev->minor); -+ cdev_init(&idev->cdev, &issei_fops); ++ ++ device_initialize(&idev->dev); ++ idev->dev.devt = devno; ++ idev->dev.class = issei_class; ++ idev->dev.parent = parent; ++ idev->dev.groups = issei_groups; ++ idev->dev.release = issei_device_release; ++ dev_set_drvdata(&idev->dev, idev); ++ ++ idev->cdev = cdev_alloc(); ++ if (!idev->cdev) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ idev->cdev->ops = &issei_fops; + if (parent->driver) -+ idev->cdev.owner = parent->driver->owner; ++ idev->cdev->owner = parent->driver->owner; ++ cdev_set_parent(idev->cdev, &idev->dev.kobj); + -+ ret = cdev_add(&idev->cdev, devno, 1); ++ ret = cdev_add(idev->cdev, devno, 1); + if (ret) { + dev_err(parent, "unable to add device %d:%d\n", + MAJOR(issei_devt), idev->minor); -+ goto err_dev_add; ++ goto err_del_cdev; + } + -+ clsdev = device_create_with_groups(issei_class, parent, devno, -+ idev, issei_groups, -+ "issei%d", idev->minor); -+ if (IS_ERR(clsdev)) { -+ dev_err(parent, "unable to create device %d:%d\n", -+ MAJOR(issei_devt), idev->minor); -+ ret = PTR_ERR(clsdev); -+ goto err_dev_create; ++ ret = dev_set_name(&idev->dev, "issei%d", idev->minor); ++ if (ret) { ++ dev_err(parent, "unable to set name to device %d:%d ret = %d\n", ++ MAJOR(issei_devt), idev->minor, ret); ++ goto err_del_cdev; + } + -+ return 0; ++ ret = device_add(&idev->dev); ++ if (ret) { ++ dev_err(parent, "unable to add device %d:%d ret = %d\n", ++ MAJOR(issei_devt), idev->minor, ret); ++ goto err_del_cdev; ++ } + -+err_dev_create: -+ cdev_del(&idev->cdev); -+err_dev_add: -+ issei_minor_free(idev); ++ return idev; + -+ return ret; ++err_del_cdev: ++ cdev_del(idev->cdev); ++err: ++ put_device(&idev->dev); ++ issei_minor_free(minor); ++ ++ return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(issei_register); + @@ -364,14 +429,14 @@ index 000000000000..26ac95e5f818 + */ +void issei_deregister(struct issei_device *idev) +{ -+ int devno; ++ int minor = idev->minor; ++ int devno = idev->cdev->dev; + -+ devno = idev->cdev.dev; -+ cdev_del(&idev->cdev); ++ cdev_del(idev->cdev); + + device_destroy(issei_class, devno); + -+ issei_minor_free(idev); ++ issei_minor_free(minor); +} +EXPORT_SYMBOL_GPL(issei_deregister); + @@ -408,25 +473,31 @@ index 000000000000..26ac95e5f818 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h new file mode 100644 -index 000000000000..dbdb0e868482 +index 0000000000000..d63541227e677 --- /dev/null +++ b/drivers/misc/issei/cdev.h -@@ -0,0 +1,12 @@ +@@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2025 Intel Corporation */ +#ifndef _ISSEI_CDEV_H_ +#define _ISSEI_CDEV_H_ + ++#include ++ +struct device; +struct issei_device; ++struct issei_dma_length; ++struct issei_hw_ops; + -+int issei_register(struct issei_device *idev, struct device *parent); ++struct issei_device *issei_register(size_t hw_size, struct device *parent, ++ const struct issei_dma_length *dma_length, ++ const struct issei_hw_ops *ops); +void issei_deregister(struct issei_device *idev); + +#endif /* _ISSEI_CDEV_H_ */ diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c new file mode 100644 -index 000000000000..8ad54f4636dc +index 0000000000000..2dd081d05a6bd --- /dev/null +++ b/drivers/misc/issei/dma.c @@ -0,0 +1,172 @@ @@ -476,7 +547,7 @@ index 000000000000..8ad54f4636dc + return 0; + } + -+ dma->vaddr = dmam_alloc_coherent(idev->dev, size, &dma->daddr, GFP_KERNEL | __GFP_ZERO); ++ dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr, GFP_KERNEL | __GFP_ZERO); + return dma->vaddr ? 0 : -ENOMEM; +} + @@ -503,7 +574,7 @@ index 000000000000..8ad54f4636dc +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + -+ dev_dbg(idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); ++ dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->f2h_counter_rd++; +} @@ -512,7 +583,7 @@ index 000000000000..8ad54f4636dc +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + -+ dev_dbg(idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); ++ dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->h2f_counter_wr++; +} @@ -530,16 +601,16 @@ index 000000000000..8ad54f4636dc + struct ham_message_header *hdr = (struct ham_message_header *)write_buf; + + if (data->length > idev->dma.length.h2f - sizeof(*hdr)) { -+ dev_err(idev->dev, "Message too big\n"); ++ dev_err(&idev->dev, "Message too big\n"); + return -EMSGSIZE; + } + + if (__issei_dma_is_write_busy(&idev->dma)) { + if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) { -+ dev_err(idev->dev, "Write stuck in queue\n"); ++ dev_err(&idev->dev, "Write stuck in queue\n"); + return -EIO; + } -+ dev_info(idev->dev, "Write is busy\n"); ++ dev_info(&idev->dev, "Write is busy\n"); + return -EBUSY; + } + @@ -586,14 +657,14 @@ index 000000000000..8ad54f4636dc +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data) +{ + if (!__issei_dma_is_read_busy(&idev->dma)) { -+ dev_dbg(idev->dev, "Nothing to read\n"); ++ dev_dbg(&idev->dev, "Nothing to read\n"); + return -ENODATA; + } + -+ dev_dbg(idev->dev, "Reading header\n"); ++ dev_dbg(&idev->dev, "Reading header\n"); + __issei_dma_read_hdr(idev, data); + -+ dev_dbg(idev->dev, "Reading data (size %u)\n", data->length); ++ dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length); + data->buf = kmalloc(data->length, GFP_KERNEL); + if (!data->buf) + return -ENOMEM; @@ -604,7 +675,7 @@ index 000000000000..8ad54f4636dc +} diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h new file mode 100644 -index 000000000000..8d92285f5da7 +index 0000000000000..8d92285f5da76 --- /dev/null +++ b/drivers/misc/issei/dma.h @@ -0,0 +1,58 @@ @@ -668,7 +739,7 @@ index 000000000000..8d92285f5da7 +#endif /*_ISSEI_DMA_H_*/ diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h new file mode 100644 -index 000000000000..75346d3ff986 +index 0000000000000..75346d3ff986b --- /dev/null +++ b/drivers/misc/issei/hw_msg.h @@ -0,0 +1,176 @@ @@ -850,10 +921,10 @@ index 000000000000..75346d3ff986 +#endif /* _ISSEI_HW_MSG_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h new file mode 100644 -index 000000000000..4e507ce4d10e +index 0000000000000..cfac43c14ba24 --- /dev/null +++ b/drivers/misc/issei/issei_dev.h -@@ -0,0 +1,153 @@ +@@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2025 Intel Corporation */ +#ifndef _ISSEI_DEV_H_ @@ -951,6 +1022,7 @@ index 000000000000..4e507ce4d10e + +/** + * struct issei_device - issei device ++ * @parent: parent device object + * @dev: associated device object + * @cdev: character device + * @minor: allocated minor number @@ -977,8 +1049,9 @@ index 000000000000..4e507ce4d10e + * @hw: hw-specific data + */ +struct issei_device { -+ struct device *dev; -+ struct cdev cdev; ++ struct device *parent; ++ struct device dev; ++ struct cdev *cdev; + int minor; + bool power_down; + wait_queue_head_t wait_rst_irq; @@ -1009,10 +1082,10 @@ index 000000000000..4e507ce4d10e +#endif /* _ISSEI_DEV_H_ */ diff --git a/include/uapi/linux/issei.h b/include/uapi/linux/issei.h new file mode 100644 -index 000000000000..234fbc0aeaaa +index 0000000000000..78a231775b521 --- /dev/null +++ b/include/uapi/linux/issei.h -@@ -0,0 +1,78 @@ +@@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2023-2025 Intel Corporation @@ -1079,17 +1152,6 @@ index 000000000000..234fbc0aeaaa +#define IOCTL_ISSEI_DISCONNECT_CLIENT \ + _IO('H', 0x02) + -+/* -+ * This IOCTL is used to obtain driver status -+ * -+ * The 32bit output parameter: -+ * Bit[0] - driver reset status (1 - in reset, 0 - out of reset) -+ * Bit[1-7] - Reserved -+ * Bit[8-15] - Number of resets performed after driver load -+ */ -+#define IOCTL_ISSEI_DRIVER_STATUS \ -+ _IOR('H', 0x03, __u32) -+ +#endif /* _LINUX_ISSEI_H */ -- 2.43.0 diff --git a/SPECS/kernel-rt/0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu b/SPECS/kernel-rt/0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu deleted file mode 100644 index afd8a13e4..000000000 --- a/SPECS/kernel-rt/0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu +++ /dev/null @@ -1,878 +0,0 @@ -From 34b0b6fcd8a20612950a7a8a7d1b252c43313119 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 21 Oct 2025 10:35:24 +0800 -Subject: [PATCH 1/2] media: i2c: max9x: fix S3/S4 error for max9x - -Tracked-On: #JILCNT-697 - Tracked-On: #H15018371204 - -Signed-off-by: hepengpx -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/isx031.c | 70 +++++++----- - drivers/media/i2c/max9x/Makefile | 2 +- - drivers/media/i2c/max9x/max9295.c | 39 ++++--- - drivers/media/i2c/max9x/max9296.h | 2 +- - drivers/media/i2c/max9x/max96724.c | 7 +- - drivers/media/i2c/max9x/regmap-retry.c | 52 +++++++++ - drivers/media/i2c/max9x/regmap-retry.h | 18 +++ - drivers/media/i2c/max9x/serdes.c | 148 ++++++++----------------- - 8 files changed, 188 insertions(+), 150 deletions(-) - create mode 100644 drivers/media/i2c/max9x/regmap-retry.c - create mode 100644 drivers/media/i2c/max9x/regmap-retry.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index ddc3e512efa4..e613cdb06b83 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -33,6 +33,8 @@ - #define ISX031_MODE_4LANES_30FPS 0x17 - #define ISX031_MODE_2LANES_30FPS 0x18 - -+static DEFINE_MUTEX(isx031_mutex); -+ - struct isx031_reg { - enum { - ISX031_REG_LEN_DELAY = 0, -@@ -95,7 +97,7 @@ struct isx031 { - u8 lanes; - - /* To serialize asynchronus callbacks */ -- struct mutex mutex; -+ struct mutex *mutex; - - /* i2c client */ - struct i2c_client *client; -@@ -306,6 +308,21 @@ static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) - return 0; - } - -+static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ int ret; -+ int retry = 100; -+ -+ while (retry--) { -+ ret = isx031_write_reg(isx031, reg, len, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ - static int isx031_write_reg_list(struct isx031 *isx031, - const struct isx031_reg_list *r_list) - { -@@ -318,7 +335,7 @@ static int isx031_write_reg_list(struct isx031 *isx031, - msleep(r_list->regs[i].val); - continue; - } -- ret = isx031_write_reg(isx031, r_list->regs[i].address, -+ ret = isx031_write_reg_retry(isx031, r_list->regs[i].address, - ISX031_REG_LEN_08BIT, - r_list->regs[i].val); - if (ret) { -@@ -353,7 +370,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); - return ret; - } - -@@ -506,13 +523,12 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - if (isx031->streaming == enable) - return 0; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - - ret = isx031_start_streaming(isx031); -@@ -528,7 +544,8 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - - isx031->streaming = enable; - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(isx031->mutex); - - return ret; - } -@@ -553,11 +570,11 @@ static int __maybe_unused isx031_suspend(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - if (isx031->streaming) - isx031_stop_streaming(isx031); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -568,7 +585,9 @@ static int __maybe_unused isx031_resume(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - const struct isx031_reg_list *reg_list; -- int ret; -+ int ret = 0; -+ -+ mutex_lock(isx031->mutex); - - if (isx031->reset_gpio != NULL) - isx031_reset(isx031->reset_gpio); -@@ -579,27 +598,26 @@ static int __maybe_unused isx031_resume(struct device *dev) - ret = isx031_write_reg_list(isx031, reg_list); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); -- return ret; -+ goto err_unlock; - } - } else { - dev_err(&client->dev, "isx031 resume failed"); -- return ret; -+ goto err_unlock; - } - -- mutex_lock(&isx031->mutex); - if (isx031->streaming) { - ret = isx031_start_streaming(isx031); - if (ret) { - isx031->streaming = false; - isx031_stop_streaming(isx031); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - } - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(isx031->mutex); - -- return 0; -+ return ret; - } - - static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -@@ -638,7 +656,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - if (i >= ARRAY_SIZE(supported_modes)) - mode = &supported_modes[0]; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -@@ -647,7 +665,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - isx031->cur_mode = mode; - } - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -658,14 +676,14 @@ static int isx031_get_format(struct v4l2_subdev *sd, - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - isx031_update_pad_format(isx031->cur_mode, &fmt->format); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -674,10 +692,10 @@ static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - isx031_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -710,12 +728,10 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { - static void isx031_remove(struct i2c_client *client) - { - struct v4l2_subdev *sd = i2c_get_clientdata(client); -- struct isx031 *isx031 = to_isx031(sd); - - v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); - - } - -@@ -776,8 +792,7 @@ static int isx031_probe(struct i2c_client *client) - if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - -- mutex_init(&isx031->mutex); -- -+ isx031->mutex = &isx031_mutex; - /* 1920x1536 default */ - isx031->cur_mode = NULL; - isx031->pre_mode = &supported_modes[0]; -@@ -808,7 +823,6 @@ static int isx031_probe(struct i2c_client *client) - probe_error_media_entity_cleanup: - media_entity_cleanup(&isx031->sd.entity); - pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); - - return ret; - } -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -index ab533b790f72..ec275fe74e94 100644 ---- a/drivers/media/i2c/max9x/Makefile -+++ b/drivers/media/i2c/max9x/Makefile -@@ -7,4 +7,4 @@ ccflags-y += -I$(src)/../../pci/intel/ipu6/ - endif - - obj-m += max9x.o --max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -+max9x-y += regmap-retry.o serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index cc1ee2f5ff92..a5bacc3684a9 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -30,6 +30,7 @@ - #include - - #include "max9295.h" -+#include "regmap-retry.h" - - static const char *const max9295_gpio_chip_names[] = { - "MFP1", -@@ -261,14 +262,14 @@ static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, - struct device *dev = common->dev; - struct regmap *map = common->map; - int data_type_slot, dt; -- int ret; -+ int ret = 0; - - for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { - dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; - dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", - pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); - -- TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), - MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -@@ -510,35 +511,35 @@ static int max9295_enable_rclk(struct max9x_common *common) - dev_info(dev, "Enable RCLK: 27MHz"); - - // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_PREDEF_FREQ_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) - ); - - // Enable reference generation -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) - ); - - // Configure reference generation output on MFP4 -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_GPIO_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) - ); - - // Enable output -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) - ); - -- TRY(ret, regmap_update_bits(map, MAX9295_REG3, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REG3, - MAX9295_RCLK_SEL_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) - ); - -- TRY(ret, regmap_update_bits(map, MAX9295_REG6, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REG6, - MAX9295_RCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) - ); -@@ -553,7 +554,7 @@ static int max9295_set_local_control_channel_enabled(struct max9x_common *common - - dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ return regmap_update_bits_retry(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, - MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); - } - -@@ -575,8 +576,8 @@ static int max9295_verify_devid(struct max9x_common *common) - int ret; - - // Fetch and output chip name + revision -- TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -- TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_REV, &dev_rev)); - - switch (dev_id) { - case MAX9295A: -@@ -617,10 +618,10 @@ static int max9295_enable(struct max9x_common *common) - TRY(ret, max9295_set_local_control_channel_enabled(common, false)); - - /* Clear the pipe maps */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_9, 0)); - - /* Clear the csi port selections */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); - - return 0; - } -@@ -685,6 +686,8 @@ static int max9295_remap_addr(struct max9x_common *common) - TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, - MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); - -+ msleep(20); -+ - return 0; - } - -@@ -717,18 +720,18 @@ static int max9295_add_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr || src == 0) { - dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", - MAX9295_I2C_SRC(i2c_id, alias), virt_addr, - MAX9295_I2C_DST(i2c_id, alias), phys_addr); -- TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) - ); - -- TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_SRC(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) - ); - } -@@ -746,10 +749,10 @@ static int max9295_remove_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr) { -- return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ return regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); - } - } -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -index 38c344669df4..acc6bb03405b 100644 ---- a/drivers/media/i2c/max9x/max9296.h -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -49,7 +49,7 @@ enum max9296_link_mode { - #define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ - /* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ - --#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 350 - - #define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 - -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index b214e1176963..37a29d2ec046 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -30,6 +30,7 @@ - #include - - #include "max96724.h" -+#include "regmap-retry.h" - - // Params - int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -@@ -337,7 +338,7 @@ static int max96724_set_all_reset(struct max9x_common *common, bool enable) - - dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX96724_RESET_ALL, -+ return regmap_update_bits_retry(map, MAX96724_RESET_ALL, - MAX96724_RESET_ALL_FIELD, - MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); - } -@@ -354,8 +355,8 @@ static int max96724_soft_reset(struct max9x_common *common) - return ret; - - /* Wait for hardware available after soft reset */ -- /* TODO: Optimize sleep time 45 ms */ -- msleep(45); -+ /* TODO: Optimize sleep time 20 ms */ -+ msleep(20); - - ret = max96724_set_all_reset(common, 0); - if (ret) -diff --git a/drivers/media/i2c/max9x/regmap-retry.c b/drivers/media/i2c/max9x/regmap-retry.c -new file mode 100644 -index 000000000000..48e5ec4bcb4f ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.c -@@ -0,0 +1,52 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013--2025 Intel Corporation -+ */ -+ -+#include "regmap-retry.h" -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_write(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_update_bits(map, reg, mask, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -diff --git a/drivers/media/i2c/max9x/regmap-retry.h b/drivers/media/i2c/max9x/regmap-retry.h -new file mode 100644 -index 000000000000..9e9ac63878a0 ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.h -@@ -0,0 +1,18 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013--2025 Intel Corporation -+ */ -+ -+#ifndef _REGMAP_RETRY_H -+#define _REGMAP_RETRY_H -+ -+#include -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val); -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val); -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val); -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index 633c55504273..fce930a1fdea 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -31,6 +31,7 @@ - #include - - #include "serdes.h" -+#include "regmap-retry.h" - - static const s64 max9x_op_sys_clock[] = { - MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -@@ -397,22 +398,6 @@ static void *parse_serdes_pdata(struct device *dev) - return des_pdata; - } - --static int regmap_read_retry(struct regmap *map, unsigned int reg, -- unsigned int *val) --{ -- int ret = 0; -- int i = 0; -- -- for (i = 0; i < 50; i++) { -- ret = regmap_read(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- - static int max9x_enable_resume(struct max9x_common *common) - { - struct device *dev = common->dev; -@@ -457,6 +442,7 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - unsigned int phys_addr, virt_addr; - struct i2c_client *phys_client; - struct regmap *phys_map, *virt_map; -+ struct device *dev = &serial_link->remote.client->dev; - unsigned int val; - const struct regmap_config regmap_config = { - .reg_bits = 16, -@@ -466,27 +452,23 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - if (!serial_link->remote.pdata) - return 0; - -- ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; -- - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; - if (phys_addr == virt_addr) - return 0; - -- dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ dev_info(dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); - - phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); - if (IS_ERR_OR_NULL(phys_client)) { -- dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_client); - return ret; - } - - phys_map = regmap_init_i2c(phys_client, ®map_config); - if (IS_ERR_OR_NULL(phys_map)) { -- dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_map); - goto err_client; - } -@@ -494,36 +476,36 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); - - virt_map = ser_common->map; -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { -- dev_info(common->dev, "Remap not necessary"); -+ dev_info(dev, "Remap not necessary"); - ret = 0; - goto err_regmap; - } - - ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ dev_err(dev, "Device not present at 0x%02x", phys_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ dev_info(dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { -- dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ dev_err(dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); - goto err_regmap; - } - - msleep(100); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ dev_err(dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ dev_info(dev, "DEV_ID after: 0x%02x", val); - } - - err_regmap: -@@ -531,8 +513,6 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - err_client: - i2c_unregister_device(phys_client); - -- max9x_des_deisolate_serial_link(common, link_id); -- - return ret; - } - -@@ -582,42 +562,31 @@ int max9x_common_resume(struct max9x_common *common) - struct max9x_common *des_common = NULL; - struct device *dev = common->dev; - u32 des_link; -- u32 phys_addr, virt_addr; - int ret = 0; -- int retry = 50; - -- if (dev->platform_data) { -+ if (dev->platform_data && common->type == MAX9X_SERIALIZER) { - struct max9x_pdata *pdata = dev->platform_data; -- -- virt_addr = common->client->addr; -- phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -- -- if (common->type == MAX9X_SERIALIZER) { -- WARN_ON(pdata->num_serial_links < 1); -- -- des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -- if (des_common) { -- des_link = pdata->serial_links[0].des_link_id; -- ret = max9x_remap_serializers_resume(des_common, des_link); -- if (ret) -- return ret; -- ret = max9x_des_isolate_serial_link(des_common, des_link); -- if (ret) -- goto err_reset_serializer; -- } -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common( -+ pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ } else { -+ return ret; - } - } - -- while (retry--) { -- ret = max9x_verify_devid(common); -- if (ret) -- msleep(100); -- else -- break; -- } -+ ret = max9x_verify_devid(common); - if (ret) { - dev_err(dev, "can't get devid after resume"); -- goto err_deisolate; -+ goto err_reset_serializer; - } - - ret = max9x_enable_resume(common); -@@ -641,11 +610,6 @@ int max9x_common_resume(struct max9x_common *common) - err_disable: - max9x_disable(common); - --err_deisolate: -- if (common->type == MAX9X_SERIALIZER && des_common) { -- max9x_des_deisolate_serial_link(des_common, des_link); -- } -- - err_reset_serializer: - if (common->type == MAX9X_SERIALIZER) { - if (common->common_ops && common->common_ops->remap_reset) { -@@ -655,6 +619,11 @@ int max9x_common_resume(struct max9x_common *common) - } - } - -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ - return ret; - } - -@@ -664,26 +633,12 @@ int max9x_common_suspend(struct max9x_common *common) - - dev_dbg(common->dev, "try to suspend"); - -- max9x_disable_translations(common); - - for (link_id = 0; link_id < common->num_serial_links; link_id++) - max9x_disable_serial_link(common, link_id); - - max9x_disable(common); - -- if (common->type == MAX9X_SERIALIZER) { -- struct device *dev = common->dev; -- int ret; -- -- if (dev->platform_data) { -- if (common->common_ops && common->common_ops->remap_reset) { -- ret = common->common_ops->remap_reset(common); -- if (ret) -- return ret; -- } -- } -- } -- - return 0; - } - -@@ -1099,7 +1054,7 @@ int max9x_verify_devid(struct max9x_common *common) - * Fetch and output chip name + revision - * try both virtual address and physical address - */ -- ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); -+ ret = regmap_read_retry(map, MAX9X_DEV_ID, &dev_id); - if (ret) { - dev_warn(dev, "Failed to read chip ID from virtual address"); - if (phys_map) { -@@ -1119,7 +1074,7 @@ int max9x_verify_devid(struct max9x_common *common) - } - common->des = &max9x_chips[chip_type]; - common->type = common->des->serdes_type; -- TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, common->des->rev_reg, &dev_rev)); - dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); - - dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -@@ -1176,14 +1131,14 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - if (IS_ERR_OR_NULL(virt_map)) - goto err_virt_client; - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { - dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_virt_regmap; - } - -- ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_virt_regmap; -@@ -1191,7 +1146,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { - dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); -@@ -1200,7 +1155,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - usleep_range(1000, 1050); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_virt_regmap; -@@ -1218,10 +1173,6 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - err_client: - i2c_unregister_device(phys_client); - -- max9x_deselect_i2c_chan(common->muxc, link_id); -- -- max9x_des_deisolate_serial_link(common, link_id); -- - return ret; - } - -@@ -1786,6 +1737,7 @@ static int max9x_registered(struct v4l2_subdev *sd) - if (subdev_pdata) { - struct max9x_pdata *ser_pdata = - subdev_pdata->board_info.platform_data; -+ struct v4l2_subdev *subdev = NULL; - - WARN_ON(ser_pdata->num_serial_links < 1); - -@@ -1797,13 +1749,13 @@ static int max9x_registered(struct v4l2_subdev *sd) - * physical i2c at the same time - */ - ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; - -- struct v4l2_subdev *subdev = -- v4l2_i2c_new_subdev_board(sd->v4l2_dev, -- common->muxc->adapter[link_id], -- &subdev_pdata->board_info, NULL); -+ if (!ret) -+ subdev = v4l2_i2c_new_subdev_board( -+ sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, -+ NULL); - - ret = max9x_des_deisolate_serial_link(common, link_id); - if (ret) -@@ -2633,8 +2585,6 @@ int max9x_setup_translations(struct max9x_common *common) - break; - } - -- msleep(10); -- - return err; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu b/SPECS/kernel-rt/0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu deleted file mode 100644 index f0134876e..000000000 --- a/SPECS/kernel-rt/0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu +++ /dev/null @@ -1,32439 +0,0 @@ -From f4927c135b40fcf235caf994b82a1444ca1fcca2 Mon Sep 17 00:00:00 2001 -From: Hao Yao -Date: Tue, 30 Sep 2025 15:52:51 +0800 -Subject: [PATCH 01/11] media: ipu7: IPU7 driver release for PTL Beta v6.17 iot - -Signed-off-by: Hao Yao ---- - drivers/media/i2c/isx031.c | 840 +++++ - drivers/media/i2c/lt6911gxd.c | 644 ++++ - drivers/media/i2c/max9x/Makefile | 10 + - drivers/media/i2c/max9x/max9295.c | 790 +++++ - drivers/media/i2c/max9x/max9295.h | 143 + - drivers/media/i2c/max9x/max9296.c | 881 +++++ - drivers/media/i2c/max9x/max9296.h | 144 + - drivers/media/i2c/max9x/max96717.c | 589 ++++ - drivers/media/i2c/max9x/max96717.h | 90 + - drivers/media/i2c/max9x/max96724.c | 1169 +++++++ - drivers/media/i2c/max9x/max96724.h | 241 ++ - drivers/media/i2c/max9x/max9x_pdata.h | 103 + - drivers/media/i2c/max9x/serdes.c | 2750 ++++++++++++++++ - drivers/media/i2c/max9x/serdes.h | 460 +++ - drivers/media/pci/intel/ipu7/Kconfig | 45 + - drivers/media/pci/intel/ipu7/Makefile | 37 + - .../pci/intel/ipu7/abi/ipu7_fw_boot_abi.h | 163 + - .../pci/intel/ipu7/abi/ipu7_fw_common_abi.h | 175 + - .../pci/intel/ipu7/abi/ipu7_fw_config_abi.h | 19 + - .../intel/ipu7/abi/ipu7_fw_insys_config_abi.h | 19 + - .../pci/intel/ipu7/abi/ipu7_fw_isys_abi.h | 459 +++ - .../pci/intel/ipu7/abi/ipu7_fw_msg_abi.h | 465 +++ - .../intel/ipu7/abi/ipu7_fw_psys_config_abi.h | 24 + - .../pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h | 49 + - drivers/media/pci/intel/ipu7/ipu7-boot.c | 431 +++ - drivers/media/pci/intel/ipu7/ipu7-boot.h | 25 + - drivers/media/pci/intel/ipu7/ipu7-bus.c | 158 + - drivers/media/pci/intel/ipu7/ipu7-bus.h | 69 + - .../media/pci/intel/ipu7/ipu7-buttress-regs.h | 461 +++ - drivers/media/pci/intel/ipu7/ipu7-buttress.c | 1193 +++++++ - drivers/media/pci/intel/ipu7/ipu7-buttress.h | 77 + - drivers/media/pci/intel/ipu7/ipu7-cpd.c | 277 ++ - drivers/media/pci/intel/ipu7/ipu7-cpd.h | 16 + - drivers/media/pci/intel/ipu7/ipu7-dma.c | 478 +++ - drivers/media/pci/intel/ipu7/ipu7-dma.h | 46 + - drivers/media/pci/intel/ipu7/ipu7-fw-isys.c | 389 +++ - drivers/media/pci/intel/ipu7/ipu7-fw-isys.h | 42 + - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 1034 ++++++ - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.h | 16 + - .../pci/intel/ipu7/ipu7-isys-csi2-regs.h | 1197 +++++++ - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c | 544 ++++ - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h | 64 + - .../media/pci/intel/ipu7/ipu7-isys-queue.c | 1193 +++++++ - .../media/pci/intel/ipu7/ipu7-isys-queue.h | 75 + - .../media/pci/intel/ipu7/ipu7-isys-subdev.c | 348 ++ - .../media/pci/intel/ipu7/ipu7-isys-subdev.h | 56 + - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c | 693 ++++ - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h | 70 + - .../media/pci/intel/ipu7/ipu7-isys-video.c | 1311 ++++++++ - .../media/pci/intel/ipu7/ipu7-isys-video.h | 125 + - drivers/media/pci/intel/ipu7/ipu7-isys.c | 1624 ++++++++++ - drivers/media/pci/intel/ipu7/ipu7-isys.h | 187 ++ - drivers/media/pci/intel/ipu7/ipu7-mmu.c | 854 +++++ - drivers/media/pci/intel/ipu7/ipu7-mmu.h | 414 +++ - .../media/pci/intel/ipu7/ipu7-platform-regs.h | 82 + - drivers/media/pci/intel/ipu7/ipu7-syscom.c | 79 + - drivers/media/pci/intel/ipu7/ipu7-syscom.h | 35 + - drivers/media/pci/intel/ipu7/ipu7.c | 2864 +++++++++++++++++ - drivers/media/pci/intel/ipu7/ipu7.h | 259 ++ - drivers/media/pci/intel/ipu7/psys/Makefile | 18 + - drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 1545 +++++++++ - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.c | 603 ++++ - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.h | 42 + - drivers/media/pci/intel/ipu7/psys/ipu7-psys.c | 398 +++ - drivers/media/pci/intel/ipu7/psys/ipu7-psys.h | 184 ++ - drivers/media/platform/intel/Kconfig | 27 +- - drivers/media/platform/intel/Makefile | 21 +- - .../media/platform/intel/ipu-acpi-common.c | 441 +++ - drivers/media/platform/intel/ipu-acpi-pdata.c | 594 ++++ - drivers/media/platform/intel/ipu-acpi.c | 224 ++ - .../media/platform/intel/ipu7-fpga-pdata.c | 60 + - include/media/i2c/isx031.h | 24 + - include/media/ipu-acpi-pdata.h | 120 + - include/media/ipu-acpi.h | 186 ++ - include/uapi/linux/ipu7-psys.h | 246 ++ - 75 files changed, 31815 insertions(+), 13 deletions(-) - create mode 100644 drivers/media/i2c/isx031.c - create mode 100644 drivers/media/i2c/lt6911gxd.c - create mode 100644 drivers/media/i2c/max9x/Makefile - create mode 100644 drivers/media/i2c/max9x/max9295.c - create mode 100644 drivers/media/i2c/max9x/max9295.h - create mode 100644 drivers/media/i2c/max9x/max9296.c - create mode 100644 drivers/media/i2c/max9x/max9296.h - create mode 100644 drivers/media/i2c/max9x/max96717.c - create mode 100644 drivers/media/i2c/max9x/max96717.h - create mode 100644 drivers/media/i2c/max9x/max96724.c - create mode 100644 drivers/media/i2c/max9x/max96724.h - create mode 100644 drivers/media/i2c/max9x/max9x_pdata.h - create mode 100644 drivers/media/i2c/max9x/serdes.c - create mode 100644 drivers/media/i2c/max9x/serdes.h - create mode 100644 drivers/media/pci/intel/ipu7/Kconfig - create mode 100644 drivers/media/pci/intel/ipu7/Makefile - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-platform-regs.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7.h - create mode 100644 drivers/media/pci/intel/ipu7/psys/Makefile - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu-psys.c - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.c - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.h - create mode 100644 drivers/media/platform/intel/ipu-acpi-common.c - create mode 100644 drivers/media/platform/intel/ipu-acpi-pdata.c - create mode 100644 drivers/media/platform/intel/ipu-acpi.c - create mode 100644 drivers/media/platform/intel/ipu7-fpga-pdata.c - create mode 100644 include/media/i2c/isx031.h - create mode 100644 include/media/ipu-acpi-pdata.h - create mode 100644 include/media/ipu-acpi.h - create mode 100644 include/uapi/linux/ipu7-psys.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -new file mode 100644 -index 000000000000..e7d9e417745a ---- /dev/null -+++ b/drivers/media/i2c/isx031.c -@@ -0,0 +1,840 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2022-2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#define to_isx031(_sd) container_of(_sd, struct isx031, sd) -+ -+#define ISX031_REG_MODE_SET_F 0x8A01 -+#define ISX031_MODE_STANDBY 0x00 -+#define ISX031_MODE_STREAMING 0x80 -+ -+#define ISX031_REG_SENSOR_STATE 0x6005 -+#define ISX031_STATE_STREAMING 0x05 -+#define ISX031_STATE_STARTUP 0x02 -+ -+#define ISX031_REG_MODE_SET_F_LOCK 0xBEF0 -+#define ISX031_MODE_UNLOCK 0x53 -+ -+#define ISX031_REG_MODE_SELECT 0x8A00 -+#define ISX031_MODE_4LANES_60FPS 0x01 -+#define ISX031_MODE_4LANES_30FPS 0x17 -+#define ISX031_MODE_2LANES_30FPS 0x18 -+ -+struct isx031_reg { -+ enum { -+ ISX031_REG_LEN_DELAY = 0, -+ ISX031_REG_LEN_08BIT = 1, -+ ISX031_REG_LEN_16BIT = 2, -+ } mode; -+ u16 address; -+ u16 val; -+}; -+ -+struct isx031_reg_list { -+ u32 num_of_regs; -+ const struct isx031_reg *regs; -+}; -+ -+struct isx031_link_freq_config { -+ const struct isx031_reg_list reg_list; -+}; -+ -+struct isx031_driver_mode { -+ int lanes; -+ int fps; -+ int mode; -+}; -+ -+static const struct isx031_driver_mode isx031_driver_modes[] = { -+ { 4, 60, ISX031_MODE_4LANES_60FPS }, -+ { 4, 30, ISX031_MODE_4LANES_30FPS }, -+ { 2, 30, ISX031_MODE_2LANES_30FPS }, -+}; -+ -+struct isx031_mode { -+ /* Frame width in pixels */ -+ u32 width; -+ -+ /* Frame height in pixels */ -+ u32 height; -+ -+ /* MEDIA_BUS_FMT */ -+ u32 code; -+ -+ /* CSI-2 data type ID */ -+ u8 datatype; -+ -+ /* MODE_FPS*/ -+ u32 fps; -+ -+ /* Sensor register settings for this resolution */ -+ const struct isx031_reg_list reg_list; -+}; -+ -+struct isx031 { -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ -+ /* Current mode */ -+ const struct isx031_mode *cur_mode; -+ /* Previous mode */ -+ const struct isx031_mode *pre_mode; -+ u8 lanes; -+ -+ /* To serialize asynchronus callbacks */ -+ struct mutex mutex; -+ -+ /* i2c client */ -+ struct i2c_client *client; -+ -+ struct isx031_platform_data *platform_data; -+ struct gpio_desc *reset_gpio; -+ -+ /* Streaming on/off */ -+ bool streaming; -+}; -+ -+static const struct isx031_reg isx031_init_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0xFFFF, 0x00}, // select mode -+ {ISX031_REG_LEN_08BIT, 0x0171, 0x00}, // close F_EBD -+ {ISX031_REG_LEN_08BIT, 0x0172, 0x00}, // close R_EBD -+}; -+ -+static const struct isx031_reg isx031_framesync_reg[] = { -+ /* External sync */ -+ {ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */ -+ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ -+ {ISX031_REG_LEN_08BIT, 0x0153, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */ -+ {ISX031_REG_LEN_08BIT, 0x0144, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AF1, 0x00}, -+}; -+ -+static const struct isx031_reg isx031_1920_1536_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, // H size = 1920 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, // H croped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0x00}, // V size 1536 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x06}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0x00}, // V cropped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x80}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x06}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, -+}; -+ -+static const struct isx031_reg isx031_1920_1080_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, // H size = 1920 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, // H croped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0x38}, // V size 1080 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x04}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0xE4}, // V cropped 228*2 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x80}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0x38}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x04}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0xE4}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, -+ -+}; -+ -+static const struct isx031_reg isx031_1280_720_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x00}, // H size = 1280 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x05}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x40}, // H croped 320*2 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0xD0}, // V size 720 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x02}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0x98}, // V cropped 408*2 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x05}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x40}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0xD0}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x02}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0x98}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x01}, -+}; -+ -+static const struct isx031_reg_list isx031_init_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_init_reg), -+ .regs = isx031_init_reg, -+}; -+ -+static const struct isx031_reg_list isx031_framesync_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_framesync_reg), -+ .regs = isx031_framesync_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1920_1536_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1920_1536_30fps_reg), -+ .regs = isx031_1920_1536_30fps_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1920_1080_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1920_1080_30fps_reg), -+ .regs = isx031_1920_1080_30fps_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1280_720_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1280_720_30fps_reg), -+ .regs = isx031_1280_720_30fps_reg, -+}; -+ -+static const struct isx031_mode supported_modes[] = { -+ { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .datatype = MIPI_CSI2_DT_YUV422_8B, -+ .fps = 30, -+ .reg_list = isx031_1920_1080_30fps_reg_list, -+ }, -+ { -+ .width = 1280, -+ .height = 720, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .datatype = MIPI_CSI2_DT_YUV422_8B, -+ .fps = 30, -+ .reg_list = isx031_1280_720_30fps_reg_list, -+ }, -+ { -+ .width = 1920, -+ .height = 1536, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .datatype = MIPI_CSI2_DT_YUV422_8B, -+ .fps = 30, -+ .reg_list = isx031_1920_1536_30fps_reg_list, -+ }, -+}; -+ -+static int isx031_reset(struct gpio_desc *reset_gpio) -+{ -+ if (!IS_ERR_OR_NULL(reset_gpio)) { -+ gpiod_set_value_cansleep(reset_gpio, 0); -+ usleep_range(500, 1000); -+ gpiod_set_value_cansleep(reset_gpio, 1); -+ /*Needs to sleep for quite a while before register writes*/ -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int isx031_read_reg(struct isx031 *isx031, u16 reg, u16 len, u32 *val) -+{ -+ struct i2c_client *client = isx031->client; -+ struct i2c_msg msgs[2]; -+ u8 addr_buf[2]; -+ u8 data_buf[4] = {0}; -+ int ret; -+ -+ if (len > 4) -+ return -EINVAL; -+ -+ put_unaligned_be16(reg, addr_buf); -+ msgs[0].addr = client->addr; -+ msgs[0].flags = 0; -+ msgs[0].len = sizeof(addr_buf); -+ msgs[0].buf = addr_buf; -+ msgs[1].addr = client->addr; -+ msgs[1].flags = I2C_M_RD; -+ msgs[1].len = len; -+ msgs[1].buf = &data_buf[4 - len]; -+ -+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); -+ if (ret != ARRAY_SIZE(msgs)) -+ return -EIO; -+ -+ *val = get_unaligned_be32(data_buf); -+ -+ return 0; -+} -+ -+static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ struct i2c_client *client = isx031->client; -+ u8 buf[6]; -+ -+ if (len > 4) -+ return -EINVAL; -+ -+ dev_dbg(&client->dev, "%s, reg %x len %x, val %x\n", __func__, reg, len, val); -+ put_unaligned_be16(reg, buf); -+ put_unaligned_be32(val << 8 * (4 - len), buf + 2); -+ if (i2c_master_send(client, buf, len + 2) != len + 2) { -+ dev_err(&client->dev, "%s:failed: reg=%2x\n", __func__, reg); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int isx031_write_reg_list(struct isx031 *isx031, -+ const struct isx031_reg_list *r_list) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(&isx031->sd); -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < r_list->num_of_regs; i++) { -+ if (r_list->regs[i].mode == ISX031_REG_LEN_DELAY) { -+ msleep(r_list->regs[i].val); -+ continue; -+ } -+ ret = isx031_write_reg(isx031, r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val); -+ if (ret) { -+ dev_err_ratelimited(&client->dev, -+ "failed to write reg 0x%4.4x. error = %d", -+ r_list->regs[i].address, ret); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static int isx031_find_driver_mode(int lanes, int fps) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(isx031_driver_modes); i++) { -+ if (isx031_driver_modes[i].lanes == lanes && isx031_driver_modes[i].fps == fps) -+ return isx031_driver_modes[i].mode; -+ } -+ -+ return -EINVAL; -+} -+ -+static int isx031_set_driver_mode(struct isx031 *isx031) -+{ -+ int ret; -+ int mode; -+ -+ mode = isx031_find_driver_mode(isx031->lanes, isx031->cur_mode->fps); -+ if (mode < 0) -+ return mode; -+ -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ return ret; -+} -+ -+static int isx031_mode_transit(struct isx031 *isx031, int state) -+{ -+ struct i2c_client *client = isx031->client; -+ int ret; -+ int cur_mode, mode; -+ u32 val; -+ int retry = 50; -+ -+ if (state == ISX031_STATE_STARTUP) -+ mode = ISX031_MODE_STANDBY; -+ else if (state == ISX031_STATE_STREAMING) -+ mode = ISX031_MODE_STREAMING; -+ -+ retry = 50; -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0) -+ break; -+ usleep_range(10000, 10500); -+ } -+ cur_mode = val; -+ -+ //TODO: only set if isx031->lanes != 0, means get lanes from pdata -+ ret = isx031_set_driver_mode(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to set driver mode"); -+ return ret; -+ } -+ -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F_LOCK, 1, -+ ISX031_MODE_UNLOCK); -+ if (ret) { -+ dev_err(&client->dev, "failed to unlock mode"); -+ return ret; -+ } -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -+ mode); -+ if (ret) { -+ dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", -+ cur_mode, mode); -+ return ret; -+ } -+ -+ /*streaming transit to standby need 1 frame+5ms*/ -+ retry = 50; -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0 && val == state) -+ break; -+ usleep_range(10000, 10500); -+ } -+ -+ return 0; -+} -+ -+static int isx031_identify_module(struct isx031 *isx031) -+{ -+ struct i2c_client *client = isx031->client; -+ int ret; -+ int retry = 50; -+ u32 val; -+ -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0) -+ break; -+ usleep_range(100000, 100500); -+ } -+ -+ if (ret) -+ return ret; -+ -+ dev_dbg(&client->dev, "sensor in mode 0x%x", val); -+ -+ /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly*/ -+ if (val == ISX031_STATE_STREAMING) { -+ if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) -+ return ret; -+ } -+ -+ ret = isx031_write_reg_list(isx031, &isx031_init_reg_list); -+ if (ret) -+ return ret; -+ if (isx031->platform_data != NULL && !isx031->platform_data->irq_pin_flags) { -+ ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed in set framesync."); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void isx031_update_pad_format(const struct isx031_mode *mode, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ fmt->width = mode->width; -+ fmt->height = mode->height; -+ fmt->code = mode->code; -+ fmt->field = V4L2_FIELD_NONE; -+} -+ -+static int isx031_start_streaming(struct isx031 *isx031) -+{ -+ int ret; -+ struct i2c_client *client = isx031->client; -+ const struct isx031_reg_list *reg_list; -+ -+ if (isx031->cur_mode != isx031->pre_mode) { -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed to set stream mode"); -+ return ret; -+ } -+ isx031->pre_mode = isx031->cur_mode; -+ } else { -+ dev_dbg(&client->dev, "same mode, skip write reg list"); -+ } -+ -+ ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); -+ if (ret) { -+ dev_err(&client->dev, "failed to start streaming"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void isx031_stop_streaming(struct isx031 *isx031) -+{ -+ struct i2c_client *client = isx031->client; -+ if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) -+ dev_err(&client->dev, "failed to stop streaming"); -+} -+ -+static int isx031_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ struct i2c_client *client = isx031->client; -+ int ret = 0; -+ -+ if (isx031->streaming == enable) -+ return 0; -+ -+ mutex_lock(&isx031->mutex); -+ if (enable) { -+ ret = pm_runtime_get_sync(&client->dev); -+ if (ret < 0) { -+ pm_runtime_put_noidle(&client->dev); -+ mutex_unlock(&isx031->mutex); -+ return ret; -+ } -+ -+ ret = isx031_start_streaming(isx031); -+ if (ret) { -+ enable = 0; -+ isx031_stop_streaming(isx031); -+ pm_runtime_put(&client->dev); -+ } -+ } else { -+ isx031_stop_streaming(isx031); -+ pm_runtime_put(&client->dev); -+ } -+ -+ isx031->streaming = enable; -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return ret; -+} -+ -+static int isx031_enable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return isx031_set_stream(subdev, true); -+} -+ -+static int isx031_disable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return isx031_set_stream(subdev, false); -+} -+ -+static int __maybe_unused isx031_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ if (isx031->streaming) -+ isx031_stop_streaming(isx031); -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int __maybe_unused isx031_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ const struct isx031_reg_list *reg_list; -+ int ret; -+ -+ if (isx031->reset_gpio != NULL) -+ isx031_reset(isx031->reset_gpio); -+ -+ ret = isx031_identify_module(isx031); -+ if (ret == 0) { -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "resume: failed to apply cur mode"); -+ return ret; -+ } -+ } else { -+ dev_err(&client->dev, "isx031 resume failed"); -+ return ret; -+ } -+ -+ mutex_lock(&isx031->mutex); -+ if (isx031->streaming) { -+ ret = isx031_start_streaming(isx031); -+ if (ret) { -+ isx031->streaming = false; -+ isx031_stop_streaming(isx031); -+ mutex_unlock(&isx031->mutex); -+ return ret; -+ } -+ } -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -+ struct v4l2_mbus_frame_desc *desc) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -+ desc->num_entries = 0; -+ desc->entry[desc->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; -+ desc->entry[desc->num_entries].stream = 0; -+ desc->entry[desc->num_entries].pixelcode = isx031->cur_mode->code; -+ desc->entry[desc->num_entries].length = 0; -+ desc->entry[desc->num_entries].bus.csi2.vc = 0; -+ desc->entry[desc->num_entries].bus.csi2.dt = isx031->cur_mode->datatype; -+ desc->num_entries++; -+ return 0; -+} -+ -+static int isx031_set_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ const struct isx031_mode *mode; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) -+ if (supported_modes[i].code == fmt->format.code && -+ supported_modes[i].width == fmt->format.width && -+ supported_modes[i].height == fmt->format.height) { -+ mode = &supported_modes[i]; -+ break; -+ } -+ -+ if (i >= ARRAY_SIZE(supported_modes)) -+ mode = &supported_modes[0]; -+ -+ mutex_lock(&isx031->mutex); -+ -+ isx031_update_pad_format(mode, &fmt->format); -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; -+ } else { -+ isx031->cur_mode = mode; -+ } -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_get_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ fmt->format = *v4l2_subdev_state_get_format(sd_state, -+ fmt->pad); -+ else -+ isx031_update_pad_format(isx031->cur_mode, &fmt->format); -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ isx031_update_pad_format(&supported_modes[0], -+ v4l2_subdev_state_get_format(fh->state, 0)); -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static const struct v4l2_subdev_video_ops isx031_video_ops = { -+ .s_stream = isx031_set_stream, -+}; -+ -+static const struct v4l2_subdev_pad_ops isx031_pad_ops = { -+ .set_fmt = isx031_set_format, -+ .get_fmt = isx031_get_format, -+ .get_frame_desc = isx031_get_frame_desc, -+ .enable_streams = isx031_enable_streams, -+ .disable_streams = isx031_disable_streams, -+}; -+ -+static const struct v4l2_subdev_ops isx031_subdev_ops = { -+ .video = &isx031_video_ops, -+ .pad = &isx031_pad_ops, -+}; -+ -+static const struct media_entity_operations isx031_subdev_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+static const struct v4l2_subdev_internal_ops isx031_internal_ops = { -+ .open = isx031_open, -+}; -+ -+static void isx031_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ v4l2_async_unregister_subdev(sd); -+ media_entity_cleanup(&sd->entity); -+ pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); -+ -+} -+ -+static int isx031_probe(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd; -+ struct isx031 *isx031; -+ const struct isx031_reg_list *reg_list; -+ int ret; -+ -+ isx031 = devm_kzalloc(&client->dev, sizeof(*isx031), GFP_KERNEL); -+ if (!isx031) -+ return -ENOMEM; -+ -+ isx031->client = client; -+ isx031->platform_data = client->dev.platform_data; -+ if (isx031->platform_data == NULL) -+ dev_warn(&client->dev, "no platform data provided\n"); -+ -+ isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(isx031->reset_gpio)) -+ return -EPROBE_DEFER; -+ else if (isx031->reset_gpio == NULL) -+ dev_warn(&client->dev, "Reset GPIO not found"); -+ else { -+ dev_dbg(&client->dev, "Found reset GPIO"); -+ isx031_reset(isx031->reset_gpio); -+ } -+ -+ /* initialize subdevice */ -+ sd = &isx031->sd; -+ v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops); -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -+ sd->internal_ops = &isx031_internal_ops; -+ sd->entity.ops = &isx031_subdev_entity_ops; -+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ -+ /* initialize subdev media pad */ -+ isx031->pad.flags = MEDIA_PAD_FL_SOURCE; -+ ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad); -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "%s : media entity init Failed %d\n", __func__, ret); -+ return ret; -+ } -+ -+ ret = isx031_identify_module(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to find sensor: %d", ret); -+ return ret; -+ } -+ -+ if (isx031->platform_data && isx031->platform_data->suffix) -+ snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %s", -+ isx031->platform_data->suffix); -+ -+ if (isx031->platform_data && isx031->platform_data->lanes) -+ isx031->lanes = isx031->platform_data->lanes; -+ -+ mutex_init(&isx031->mutex); -+ -+ /* 1920x1536 default */ -+ isx031->cur_mode = NULL; -+ isx031->pre_mode = &supported_modes[0]; -+ reg_list = &isx031->pre_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed to apply preset mode"); -+ goto probe_error_media_entity_cleanup; -+ } -+ isx031->cur_mode = isx031->pre_mode; -+ ret = v4l2_async_register_subdev_sensor(&isx031->sd); -+ if (ret < 0) { -+ dev_err(&client->dev, "failed to register V4L2 subdev: %d", -+ ret); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ /* -+ * Device is already turned on by i2c-core with ACPI domain PM. -+ * Enable runtime PM and turn off the device. -+ */ -+ pm_runtime_set_active(&client->dev); -+ pm_runtime_enable(&client->dev); -+ pm_runtime_idle(&client->dev); -+ -+ return 0; -+ -+probe_error_media_entity_cleanup: -+ media_entity_cleanup(&isx031->sd.entity); -+ pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops isx031_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(isx031_suspend, isx031_resume) -+}; -+ -+static const struct i2c_device_id isx031_id_table[] = { -+ { "isx031", 0 }, -+ { /* sentinel */ }, -+}; -+MODULE_DEVICE_TABLE(i2c, isx031_id_table); -+ -+static struct i2c_driver isx031_i2c_driver = { -+ .driver = { -+ .name = "isx031", -+ .pm = &isx031_pm_ops, -+ }, -+ .probe = isx031_probe, -+ .remove = isx031_remove, -+ .id_table = isx031_id_table, -+}; -+ -+module_i2c_driver(isx031_i2c_driver); -+ -+MODULE_AUTHOR("Hao Yao "); -+MODULE_DESCRIPTION("isx031 sensor driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/media/i2c/lt6911gxd.c b/drivers/media/i2c/lt6911gxd.c -new file mode 100644 -index 000000000000..d791560ce1a3 ---- /dev/null -+++ b/drivers/media/i2c/lt6911gxd.c -@@ -0,0 +1,644 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2023 - 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define LT6911GXD_CHIP_ID 0x2204 -+#define REG_CHIP_ID CCI_REG16(0xe100) -+ -+#define REG_ENABLE_I2C CCI_REG8(0xe0ee) -+ -+#define REG_PIX_CLK CCI_REG24(0xe085) -+#define REG_BYTE_CLK CCI_REG24(0xe092) -+#define REG_H_TOTAL CCI_REG16(0xe088) -+#define REG_V_TOTAL CCI_REG16(0xe08a) -+#define REG_HALF_H_ACTIVE CCI_REG16(0xe08c) -+#define REG_V_ACTIVE CCI_REG16(0xe08e) -+#define REG_MIPI_FORMAT CCI_REG8(0xf988) -+#define REG_MIPI_TX_CTRL CCI_REG8(0xe0b0) -+ -+/* Interrupts */ -+#define REG_INT_HDMI CCI_REG8(0xe084) -+#define INT_VIDEO_DISAPPEAR 0x0 -+#define INT_VIDEO_READY 0x1 -+ -+#define LT6911GXD_DEFAULT_WIDTH 3840 -+#define LT6911GXD_DEFAULT_HEIGHT 2160 -+#define LT6911GXD_DEFAULT_LANES 2 -+#define LT6911GXD_DEFAULT_FPS 30 -+#define LT6911GXD_MAX_LINK_FREQ 297000000 -+#define LT6911GXD_PAGE_CONTROL 0xff -+#define BPP_MODE_BIT 4 -+#define YUV16_BIT 2 -+ -+static const struct regmap_range_cfg lt6911gxd_ranges[] = { -+ { -+ .name = "register_range", -+ .range_min = 0, -+ .range_max = 0xffff, -+ .selector_reg = LT6911GXD_PAGE_CONTROL, -+ .selector_mask = 0xff, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 0x100, -+ }, -+}; -+ -+static const struct regmap_config lt6911gxd_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = 0xffff, -+ .ranges = lt6911gxd_ranges, -+ .num_ranges = ARRAY_SIZE(lt6911gxd_ranges), -+}; -+ -+struct lt6911gxd_mode { -+ u32 width; -+ u32 height; -+ u32 code; -+ u32 fps; -+ u32 lanes; -+ s64 link_freq; -+}; -+ -+struct lt6911gxd { -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct v4l2_ctrl *pixel_rate; -+ struct v4l2_ctrl *link_freq; -+ -+ struct lt6911gxd_mode cur_mode; -+ struct regmap *regmap; -+ struct gpio_desc *reset_gpio; -+ struct gpio_desc *irq_gpio; -+}; -+ -+static const struct v4l2_event lt6911gxd_ev_source_change = { -+ .type = V4L2_EVENT_SOURCE_CHANGE, -+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, -+}; -+ -+static const struct v4l2_event lt6911gxd_ev_stream_end = { -+ .type = V4L2_EVENT_EOS, -+}; -+ -+static inline struct lt6911gxd *to_lt6911gxd(struct v4l2_subdev *sd) -+{ -+ return container_of(sd, struct lt6911gxd, sd); -+} -+ -+static const struct lt6911gxd_mode default_mode = { -+ .width = LT6911GXD_DEFAULT_WIDTH, -+ .height = LT6911GXD_DEFAULT_HEIGHT, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .fps = LT6911GXD_DEFAULT_FPS, -+ .lanes = LT6911GXD_DEFAULT_LANES, -+ .link_freq = LT6911GXD_MAX_LINK_FREQ, -+}; -+ -+static s64 get_pixel_rate(struct lt6911gxd *lt6911gxd) -+{ -+ s64 pixel_rate; -+ -+ pixel_rate = (s64)lt6911gxd->cur_mode.width * -+ lt6911gxd->cur_mode.height * -+ lt6911gxd->cur_mode.fps * 16; -+ do_div(pixel_rate, lt6911gxd->cur_mode.lanes); -+ -+ return pixel_rate; -+} -+ -+static int lt6911gxd_status_update(struct lt6911gxd *lt6911gxd) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(<6911gxd->sd); -+ u64 int_event; -+ u64 byte_clk, pixel_clk, fps; -+ u64 htotal, vtotal, width, height; -+ int timeout_cnt = 3; -+ int ret = 0; -+ -+ /* Read interrupt event */ -+ cci_read(lt6911gxd->regmap, REG_INT_HDMI, &int_event, &ret); -+ while (ret && timeout_cnt--) { -+ ret = cci_read(lt6911gxd->regmap, REG_INT_HDMI, -+ &int_event, NULL); -+ } -+ -+ if (ret) -+ return dev_err_probe(&client->dev, ret, -+ "failed to read interrupt event\n"); -+ -+ /* TODO: add audio ready and disappear type */ -+ switch (int_event) { -+ case INT_VIDEO_READY: -+ cci_read(lt6911gxd->regmap, REG_BYTE_CLK, &byte_clk, &ret); -+ byte_clk *= 1000; -+ cci_read(lt6911gxd->regmap, REG_PIX_CLK, &pixel_clk, &ret); -+ pixel_clk *= 1000; -+ -+ if (ret || byte_clk == 0 || pixel_clk == 0) { -+ dev_err(&client->dev, -+ "invalid ByteClock or PixelClock\n"); -+ return -EINVAL; -+ } -+ -+ cci_read(lt6911gxd->regmap, REG_H_TOTAL, &htotal, &ret); -+ cci_read(lt6911gxd->regmap, REG_V_TOTAL, &vtotal, &ret); -+ if (ret || htotal == 0 || vtotal == 0) { -+ dev_err(&client->dev, "invalid htotal or vtotal\n"); -+ return -EINVAL; -+ } -+ -+ fps = div_u64(pixel_clk, htotal * vtotal); -+ if (fps > 60) { -+ dev_err(&client->dev, -+ "max fps is 60, current fps: %llu\n", fps); -+ return -EINVAL; -+ } -+ -+ cci_read(lt6911gxd->regmap, REG_HALF_H_ACTIVE, -+ &width, &ret); -+ cci_read(lt6911gxd->regmap, REG_V_ACTIVE, &height, &ret); -+ if (ret || width == 0 || width > 3840 || -+ height == 0 || height > 2160) { -+ dev_err(&client->dev, "invalid width or height\n"); -+ return -EINVAL; -+ } -+ -+ lt6911gxd->cur_mode.height = height; -+ lt6911gxd->cur_mode.width = width; -+ lt6911gxd->cur_mode.fps = fps; -+ /* MIPI Clock Rate = ByteClock × 4, defined in lt6911gxd spec */ -+ lt6911gxd->cur_mode.link_freq = byte_clk * 4; -+ v4l2_subdev_notify_event(<6911gxd->sd, -+ <6911gxd_ev_source_change); -+ break; -+ -+ case INT_VIDEO_DISAPPEAR: -+ cci_write(lt6911gxd->regmap, REG_MIPI_TX_CTRL, 0x0, NULL); -+ lt6911gxd->cur_mode.height = 0; -+ lt6911gxd->cur_mode.width = 0; -+ lt6911gxd->cur_mode.fps = 0; -+ lt6911gxd->cur_mode.link_freq = 0; -+ v4l2_subdev_notify_event(<6911gxd->sd, -+ <6911gxd_ev_stream_end); -+ break; -+ -+ default: -+ return -ENOLINK; -+ } -+ -+ return ret; -+} -+ -+/* TODO: add audio sampling rate and present control */ -+static int lt6911gxd_init_controls(struct lt6911gxd *lt6911gxd) -+{ -+ struct v4l2_ctrl_handler *ctrl_hdlr; -+ s64 pixel_rate; -+ int ret; -+ -+ ctrl_hdlr = <6911gxd->ctrl_handler; -+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); -+ if (ret) -+ return ret; -+ -+ lt6911gxd->link_freq = -+ v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, -+ sizeof(lt6911gxd->cur_mode.link_freq), -+ 0, <6911gxd->cur_mode.link_freq); -+ -+ pixel_rate = get_pixel_rate(lt6911gxd); -+ lt6911gxd->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL, -+ V4L2_CID_PIXEL_RATE, -+ pixel_rate, pixel_rate, 1, -+ pixel_rate); -+ -+ if (ctrl_hdlr->error) { -+ ret = ctrl_hdlr->error; -+ goto hdlr_free; -+ } -+ lt6911gxd->sd.ctrl_handler = ctrl_hdlr; -+ -+ return 0; -+ -+hdlr_free: -+ v4l2_ctrl_handler_free(ctrl_hdlr); -+ return ret; -+} -+ -+static void lt6911gxd_update_pad_format(const struct lt6911gxd_mode *mode, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ fmt->width = mode->width; -+ fmt->height = mode->height; -+ fmt->code = mode->code; -+ fmt->field = V4L2_FIELD_NONE; -+} -+ -+static int lt6911gxd_enable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ int ret; -+ -+ ret = pm_runtime_resume_and_get(&client->dev); -+ if (ret < 0) -+ return ret; -+ -+ cci_write(lt6911gxd->regmap, REG_MIPI_TX_CTRL, 0x1, &ret); -+ if (ret) { -+ dev_err(&client->dev, "failed to start stream: %d\n", ret); -+ goto err_rpm_put; -+ } -+ -+ return 0; -+ -+err_rpm_put: -+ pm_runtime_put(&client->dev); -+ return ret; -+} -+ -+static int lt6911gxd_disable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(<6911gxd->sd); -+ int ret; -+ -+ ret = cci_write(lt6911gxd->regmap, REG_MIPI_TX_CTRL, 0x0, NULL); -+ if (ret) -+ dev_err(&client->dev, "failed to stop stream: %d\n", ret); -+ -+ pm_runtime_put(&client->dev); -+ return 0; -+} -+ -+static int lt6911gxd_set_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ u64 pixel_rate, link_freq; -+ -+ lt6911gxd_update_pad_format(<6911gxd->cur_mode, &fmt->format); -+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ return 0; -+ -+ pixel_rate = get_pixel_rate(lt6911gxd); -+ __v4l2_ctrl_modify_range(lt6911gxd->pixel_rate, pixel_rate, -+ pixel_rate, 1, pixel_rate); -+ -+ link_freq = lt6911gxd->cur_mode.link_freq; -+ __v4l2_ctrl_modify_range(lt6911gxd->link_freq, link_freq, -+ link_freq, 1, link_freq); -+ -+ return 0; -+} -+ -+static int lt6911gxd_get_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); -+ else -+ lt6911gxd_update_pad_format(<6911gxd->cur_mode, &fmt->format); -+ -+ return 0; -+} -+ -+static int lt6911gxd_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (code->index) -+ return -EINVAL; -+ -+ code->code = lt6911gxd->cur_mode.code; -+ -+ return 0; -+} -+ -+static int lt6911gxd_enum_frame_size(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_frame_size_enum *fse) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (fse->index != 0) -+ return -EINVAL; -+ -+ if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16) -+ return -EINVAL; -+ -+ fse->min_width = lt6911gxd->cur_mode.width; -+ fse->max_width = fse->min_width; -+ fse->min_height = lt6911gxd->cur_mode.height; -+ fse->max_height = fse->min_height; -+ -+ return 0; -+} -+ -+static int lt6911gxd_enum_frame_interval(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_frame_interval_enum *fie) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (fie->index != 0) -+ return -EINVAL; -+ -+ fie->interval.numerator = 1; -+ fie->interval.denominator = lt6911gxd->cur_mode.fps; -+ -+ return 0; -+} -+ -+static int lt6911gxd_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state) -+{ -+ struct v4l2_subdev_format fmt = { -+ .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY -+ : V4L2_SUBDEV_FORMAT_ACTIVE, -+ }; -+ -+ return lt6911gxd_set_format(sd, sd_state, &fmt); -+} -+ -+static const struct v4l2_subdev_video_ops lt6911gxd_video_ops = { -+ .s_stream = v4l2_subdev_s_stream_helper, -+}; -+ -+static const struct v4l2_subdev_pad_ops lt6911gxd_pad_ops = { -+ .set_fmt = lt6911gxd_set_format, -+ .get_fmt = lt6911gxd_get_format, -+ .enable_streams = lt6911gxd_enable_streams, -+ .disable_streams = lt6911gxd_disable_streams, -+ .enum_mbus_code = lt6911gxd_enum_mbus_code, -+ .enum_frame_size = lt6911gxd_enum_frame_size, -+ .enum_frame_interval = lt6911gxd_enum_frame_interval, -+ .get_frame_interval = v4l2_subdev_get_frame_interval, -+}; -+ -+static const struct v4l2_subdev_core_ops lt6911gxd_subdev_core_ops = { -+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static const struct v4l2_subdev_ops lt6911gxd_subdev_ops = { -+ .core = <6911gxd_subdev_core_ops, -+ .video = <6911gxd_video_ops, -+ .pad = <6911gxd_pad_ops, -+}; -+ -+static const struct media_entity_operations lt6911gxd_subdev_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+static const struct v4l2_subdev_internal_ops lt6911gxd_internal_ops = { -+ .init_state = lt6911gxd_init_state, -+}; -+ -+static int lt6911gxd_fwnode_parse(struct lt6911gxd *lt6911gxd, -+ struct device *dev) -+{ -+ struct fwnode_handle *endpoint; -+ struct v4l2_fwnode_endpoint bus_cfg = { -+ .bus_type = V4L2_MBUS_CSI2_CPHY, -+ }; -+ int ret; -+ -+ endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!endpoint) -+ return dev_err_probe(dev, -EPROBE_DEFER, -+ "endpoint node not found\n"); -+ -+ ret = v4l2_fwnode_endpoint_parse(endpoint, &bus_cfg); -+ fwnode_handle_put(endpoint); -+ if (ret) { -+ dev_err(dev, "failed to parse endpoint node: %d\n", ret); -+ goto out_err; -+ } -+ -+ /* -+ * Check the number of MIPI CSI2 data lanes, -+ * lt6911gxd only support 4 lanes. -+ */ -+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != LT6911GXD_DEFAULT_LANES) { -+ dev_err(dev, "only 2 data lanes are currently supported\n"); -+ goto out_err; -+ } -+ -+ return 0; -+ -+out_err: -+ v4l2_fwnode_endpoint_free(&bus_cfg); -+ return ret; -+} -+ -+static int lt6911gxd_identify_module(struct lt6911gxd *lt6911gxd, -+ struct device *dev) -+{ -+ u64 val; -+ int ret = 0; -+ -+ /* Chip ID should be confirmed when the I2C slave is active */ -+ cci_write(lt6911gxd->regmap, REG_ENABLE_I2C, 0x1, &ret); -+ cci_read(lt6911gxd->regmap, REG_CHIP_ID, &val, &ret); -+ cci_write(lt6911gxd->regmap, REG_ENABLE_I2C, 0x0, &ret); -+ if (ret) -+ return dev_err_probe(dev, ret, "fail to read chip id\n"); -+ -+ if (val != LT6911GXD_CHIP_ID) { -+ return dev_err_probe(dev, -ENXIO, "chip id mismatch: %x!=%x\n", -+ LT6911GXD_CHIP_ID, (u16)val); -+ } -+ -+ return 0; -+} -+ -+static irqreturn_t lt6911gxd_threaded_irq_fn(int irq, void *dev_id) -+{ -+ struct v4l2_subdev *sd = dev_id; -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev_format fmt = { -+ .which = V4L2_SUBDEV_FORMAT_ACTIVE -+ }; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ lt6911gxd_status_update(lt6911gxd); -+ lt6911gxd_set_format(sd, state, &fmt); -+ v4l2_subdev_unlock_state(state); -+ -+ return IRQ_HANDLED; -+} -+ -+static void lt6911gxd_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ free_irq(gpiod_to_irq(lt6911gxd->irq_gpio), lt6911gxd); -+ v4l2_async_unregister_subdev(sd); -+ v4l2_subdev_cleanup(sd); -+ media_entity_cleanup(&sd->entity); -+ v4l2_ctrl_handler_free(<6911gxd->ctrl_handler); -+ pm_runtime_disable(&client->dev); -+ pm_runtime_set_suspended(&client->dev); -+} -+ -+static int lt6911gxd_probe(struct i2c_client *client) -+{ -+ struct lt6911gxd *lt6911gxd; -+ struct device *dev = &client->dev; -+ u64 irq_pin_flags; -+ int ret; -+ -+ lt6911gxd = devm_kzalloc(dev, sizeof(*lt6911gxd), GFP_KERNEL); -+ if (!lt6911gxd) -+ return -ENOMEM; -+ -+ /* define default mode: 4k@60fps, changed when interrupt occurs. */ -+ lt6911gxd->cur_mode = default_mode; -+ -+ lt6911gxd->regmap = devm_regmap_init_i2c(client, -+ <6911gxd_regmap_config); -+ if (IS_ERR(lt6911gxd->regmap)) -+ return dev_err_probe(dev, PTR_ERR(lt6911gxd->regmap), -+ "failed to init CCI\n"); -+ -+ v4l2_i2c_subdev_init(<6911gxd->sd, client, <6911gxd_subdev_ops); -+ -+ lt6911gxd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); -+ if (IS_ERR(lt6911gxd->reset_gpio)) -+ return dev_err_probe(dev, PTR_ERR(lt6911gxd->reset_gpio), -+ "failed to get reset gpio\n"); -+ -+ ret = lt6911gxd_fwnode_parse(lt6911gxd, dev); -+ if (ret) -+ return ret; -+ -+ usleep_range(10000, 10500); -+ -+ ret = lt6911gxd_identify_module(lt6911gxd, dev); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to find chip\n"); -+ -+ ret = lt6911gxd_init_controls(lt6911gxd); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to init control\n"); -+ -+ lt6911gxd->sd.dev = dev; -+ lt6911gxd->sd.internal_ops = <6911gxd_internal_ops; -+ lt6911gxd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -+ V4L2_SUBDEV_FL_HAS_EVENTS; -+ lt6911gxd->sd.entity.ops = <6911gxd_subdev_entity_ops; -+ lt6911gxd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -+ lt6911gxd->pad.flags = MEDIA_PAD_FL_SOURCE; -+ ret = media_entity_pads_init(<6911gxd->sd.entity, 1, <6911gxd->pad); -+ if (ret) { -+ dev_err(dev, "failed to init entity pads: %d\n", ret); -+ goto err_v4l2_ctrl_handler_free; -+ } -+ -+ /* -+ * Device is already turned on by i2c-core with ACPI domain PM. -+ * Enable runtime PM and turn off the device. -+ */ -+ pm_runtime_set_active(dev); -+ pm_runtime_enable(dev); -+ pm_runtime_idle(dev); -+ -+ ret = v4l2_subdev_init_finalize(<6911gxd->sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); -+ goto err_media_entity_cleanup; -+ } -+ -+ lt6911gxd->irq_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN); -+ if (IS_ERR(lt6911gxd->irq_gpio)) -+ return dev_err_probe(dev, PTR_ERR(lt6911gxd->irq_gpio), -+ "failed to get hpd gpio\n"); -+ dev_dbg(dev, "%s: use hpd irq_gpio!\n", __func__); -+ -+ /* Setting irq */ -+ irq_pin_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | -+ IRQF_ONESHOT; -+ -+ ret = request_threaded_irq(gpiod_to_irq(lt6911gxd->irq_gpio), NULL, -+ lt6911gxd_threaded_irq_fn, -+ irq_pin_flags, NULL, lt6911gxd); -+ if (ret) { -+ dev_err(dev, "failed to request IRQ: %d\n", ret); -+ goto err_subdev_cleanup; -+ } -+ -+ ret = v4l2_async_register_subdev_sensor(<6911gxd->sd); -+ if (ret) { -+ dev_err(dev, "failed to register V4L2 subdev: %d\n", ret); -+ goto err_free_irq; -+ } -+ -+ return 0; -+ -+err_free_irq: -+ free_irq(gpiod_to_irq(lt6911gxd->irq_gpio), lt6911gxd); -+ -+err_subdev_cleanup: -+ v4l2_subdev_cleanup(<6911gxd->sd); -+ -+err_media_entity_cleanup: -+ pm_runtime_disable(dev); -+ pm_runtime_set_suspended(dev); -+ media_entity_cleanup(<6911gxd->sd.entity); -+ -+err_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(lt6911gxd->sd.ctrl_handler); -+ -+ return ret; -+} -+ -+static const struct acpi_device_id lt6911gxd_acpi_ids[] = { -+ { "INTC1124" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(acpi, lt6911gxd_acpi_ids); -+ -+static struct i2c_driver lt6911gxd_i2c_driver = { -+ .driver = { -+ .name = "lt6911gxd", -+ .acpi_match_table = ACPI_PTR(lt6911gxd_acpi_ids), -+ }, -+ .probe = lt6911gxd_probe, -+ .remove = lt6911gxd_remove, -+}; -+ -+module_i2c_driver(lt6911gxd_i2c_driver); -+ -+MODULE_AUTHOR("Zou, Xiaohong xiaohong.zou@intel.com"); -+MODULE_DESCRIPTION("Lontium lt6911gxd HDMI to MIPI Bridge Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -new file mode 100644 -index 000000000000..ab533b790f72 ---- /dev/null -+++ b/drivers/media/i2c/max9x/Makefile -@@ -0,0 +1,10 @@ -+ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU7)),) -+ccflags-y += -I$(src)/../../pci/intel/ipu7/ -+endif -+ -+ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU6)),) -+ccflags-y += -I$(src)/../../pci/intel/ipu6/ -+endif -+ -+obj-m += max9x.o -+max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -new file mode 100644 -index 000000000000..cc1ee2f5ff92 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -0,0 +1,790 @@ -+/* -+ * max9295_main.c - Maxim MAX9295 CSI-2 to GMSL2/GMSL1 Serializer -+ * -+ * Copyright (c) 2020, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max9295.h" -+ -+static const char *const max9295_gpio_chip_names[] = { -+ "MFP1", -+ "MFP2", -+ "MFP3", -+ "MFP4", -+ "MFP5", -+ "MFP6", -+ "MFP7", -+ "MFP8", -+ "MFP9", -+ "MFP10", -+ "MFP11", -+}; -+ -+/* Declarations */ -+static int max9295_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int csi_id, bool enable); -+static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max9295_set_video_pipe_double_loading(struct max9x_common *common, unsigned int pipe_id, unsigned int bpp); -+static int max9295_set_video_pipe_pixel_padding(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int min_bpp, unsigned int max_bpp); -+static int max9295_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max9295_enable_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_disable_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_set_local_control_channel_enabled(struct max9x_common *common, bool enabled); -+static int max9295_select_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_deselect_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_verify_devid(struct max9x_common *common); -+static int max9295_enable(struct max9x_common *common); -+static int max9295_disable(struct max9x_common *common); -+static int max9295_remap_addr(struct max9x_common *common); -+static int max9295_remap_reset(struct max9x_common *common); -+static int max9295_add_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr); -+static int max9295_remove_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr); -+static int max9295_reset(struct max9x_common *common); -+ -+/* max9295 gpio */ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip); -+static int max9295_gpio_get_direction(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_direction_input(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value); -+static int max9295_gpio_get(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); -+static int max9295_setup_gpio(struct max9x_common *common); -+/* max9295 gpio */ -+ -+static struct max9x_common_ops max9295_common_ops = { -+ .max_elements = max9295_max_elements, -+ .soft_reset = max9295_reset, -+ .enable = max9295_enable, -+ .disable = max9295_disable, -+ .verify_devid = max9295_verify_devid, -+ .remap_addr = max9295_remap_addr, -+ .remap_reset = max9295_remap_reset, -+ .setup_gpio = max9295_setup_gpio, -+}; -+ -+static struct max9x_serial_link_ops max9295_serial_link_ops = { -+ .enable = max9295_enable_serial_link, -+ .disable = max9295_disable_serial_link, -+ .select = max9295_select_serial_link, -+ .deselect = max9295_deselect_serial_link, -+}; -+ -+static struct max9x_translation_ops max9295_translation_ops = { -+ .add = max9295_add_translate_addr, -+ .remove = max9295_remove_translate_addr, -+}; -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip) -+{ -+ return container_of(chip, struct max9x_common, gpio_chip); -+} -+ -+static int max9295_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ TRY(ret, regmap_read(map, MAX9295_GPIO_A(offset), &val)); -+ -+ return (FIELD_GET(MAX9295_GPIO_A_OUT_DIS_FIELD, val) == 0U ? -+ GPIOD_OUT_LOW : GPIOD_IN); -+} -+ -+static int max9295_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), -+ MAX9295_GPIO_A_OUT_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_DIS_FIELD, 1U)); -+} -+ -+static int max9295_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int mask = 0; -+ unsigned int val; -+ -+ mask = MAX9295_GPIO_A_OUT_DIS_FIELD | MAX9295_GPIO_A_OUT_FIELD | -+ MAX9295_GPIO_A_RX_EN_FIELD; -+ -+ // Enable the GPIO as an output -+ val = MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_DIS_FIELD, 0U); -+ // Write out the initial value to the GPIO -+ val |= MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U)); -+ // Disable remote control over SerDes link -+ val |= MAX9X_FIELD_PREP(MAX9295_GPIO_A_RX_EN_FIELD, 0U); -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), mask, val); -+} -+ -+static int max9295_gpio_get(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ TRY(ret, regmap_read(map, MAX9295_GPIO_A(offset), &val)); -+ -+ return FIELD_GET(MAX9295_GPIO_A_IN_FIELD, val); -+} -+ -+static int max9295_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), -+ MAX9295_GPIO_A_OUT_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U))); -+} -+ -+static int max9295_setup_gpio(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ struct max9x_gpio_pdata *gpio_pdata; -+ -+ if (dev->platform_data) { -+ struct max9x_pdata *pdata = dev->platform_data; -+ gpio_pdata = &pdata->gpio; -+ } -+ -+ // Functions -+ if (gpio_pdata && gpio_pdata->label) -+ common->gpio_chip.label = gpio_pdata->label; -+ else -+ common->gpio_chip.label = dev_name(dev); -+ -+ dev_dbg(dev, "gpio_chip label is %s, dev_name is %s", -+ common->gpio_chip.label, dev_name(dev)); -+ -+ common->gpio_chip.parent = dev; -+ common->gpio_chip.get_direction = max9295_gpio_get_direction; -+ common->gpio_chip.direction_input = max9295_gpio_direction_input; -+ common->gpio_chip.direction_output = max9295_gpio_direction_output; -+ common->gpio_chip.get = max9295_gpio_get; -+ common->gpio_chip.set = max9295_gpio_set; -+ common->gpio_chip.ngpio = MAX9295_NUM_GPIO; -+ common->gpio_chip.can_sleep = 1; -+ common->gpio_chip.base = -1; -+ if (gpio_pdata && gpio_pdata->names) -+ common->gpio_chip.names = gpio_pdata->names; -+ else -+ common->gpio_chip.names = max9295_gpio_chip_names; -+ -+ ret = devm_gpiochip_add_data(dev, &common->gpio_chip, common); -+ if (ret < 0) { -+ dev_err(dev, "gpio_init: Failed to add max9295_gpio\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max9295_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, csi %d: %s, %d lanes", pipe_id, csi_id, -+ (enable ? "enable" : "disable"), common->csi_link[csi_id].config.num_lanes); -+ -+ // Select number of lanes for CSI port csi_id -+ TRY(ret, regmap_update_bits(map, MAX9295_MIPI_RX_1, -+ MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ common->csi_link[csi_id].config.num_lanes - 1)) -+ ); -+ -+ // Select CSI port csi_id for video pipe pipe_id -+ // Enable CSI port csi_id (9295A only has port B, 9295B has both ports) -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_0, -+ MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) | MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id), csi_id) | -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id), enable ? 1U : 0U)) -+ ); -+ -+ // Start video pipe pipe_id from CSI port csi_id -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_9, -+ MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), enable ? 1U : 0U)) -+ ); -+ -+ // Enable video transmit for pipe -+ TRY(ret, regmap_update_bits(map, MAX9295_REG2, -+ MAX9295_REG2_VID_TX_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX9295_REG2_VID_TX_EN_FIELD(pipe_id), enable ? 1U : 0U)) -+ ); -+ -+ return 0; -+} -+ -+static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, -+ unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int data_type_slot, dt; -+ int ret; -+ -+ for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { -+ dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; -+ dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", -+ pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | -+ MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -+ ); -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9295_set_video_pipe_double_loading() - Configure Double Loading Mode on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.1 "Double Mode (Serializer)" -+ */ -+static int max9295_set_video_pipe_double_loading(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_pipe_config *config = &common->video_pipe[pipe_id].config; -+ unsigned int reg; -+ unsigned int fields; -+ int ret; -+ -+ if (pipe_id >= MAX9295_NUM_VIDEO_PIPES) -+ return -EINVAL; -+ -+ // Reset double loading registers to defaults -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_10, -+ MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id), -+ 0x0)); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_11, -+ MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id) | -+ MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id), -+ 0x0)); -+ -+ // Enable double pixel mode for pipe -+ switch (bpp) { -+ case 0: -+ //bpp not used on this pipe, but still valid input -+ break; -+ case 8: -+ reg = MAX9295_FRONTTOP_10; -+ fields = MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id); -+ break; -+ case 10: -+ reg = MAX9295_FRONTTOP_11; -+ fields = MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id); -+ break; -+ case 12: -+ reg = MAX9295_FRONTTOP_11; -+ fields = MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable pixel doubling for specified pipe -+ if (bpp != 0) { -+ dev_info(dev, "Configuring double loading mode for pipe %u (%ubpp -> %ubpp)", -+ pipe_id, bpp, (bpp * 2)); -+ TRY(ret, regmap_update_bits(map, reg, fields, 0xFF)); -+ } -+ -+ // We share MAX9295_SOFT_BPP_FIELD/MAX9295_SOFT_BPP_EN_FIELD with -+ // max9295_set_video_pipe_pixel_padding(), only write to it if zero -+ // padding is disabled and double loading mode is enabled. Zero padding -+ // takes precedence and handles the 'both are disabled' case. -+ if (config->soft_min_pixel_bpp == 0 && config->soft_max_pixel_bpp == 0) { -+ // Override output bpp -+ TRY(ret, regmap_update_bits(map, MAX9295_SOFT_BPP(pipe_id), -+ MAX9295_SOFT_BPP_EN_FIELD | MAX9295_SOFT_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_EN_FIELD, bpp == 0 ? 0U : 1U) | -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_FIELD, bpp * 2)) -+ ); -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9295_set_video_pipe_pixel_padding() - Configure zero padding on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @min_bpp: Smallest BPP value of data in the pipe. Must be >= 8. -+ * @max_bpp: Largest BPP value of data in the pipe. Must be <= 16. -+ * -+ * Normally, video pipes can only transmit data with the same BPP value. Zero -+ * padding is a method to allow data with multiple BPP values to be transmitted -+ * over the same video pipe by padding the smaller BPP data to be the same BPP -+ * as the largest BPP data. The deserializer will automatically recover the -+ * data's original BPP based on datatype information transmitted alongside the -+ * padded data. -+ * -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.2 "Zero Padding" -+ */ -+static int max9295_set_video_pipe_pixel_padding(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int min_bpp, unsigned int max_bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_pipe_config *config = &common->video_pipe[pipe_id].config; -+ int ret; -+ bool enable = (min_bpp != 0) && (max_bpp != 0); -+ -+ if (enable) -+ dev_dbg(dev, "Configuring zero padding for pipe %u (%u <= bpp <= %u)", pipe_id, min_bpp, max_bpp); -+ else -+ dev_dbg(dev, "%s: pipe %u, min_bpp: %u, max_bpp: %u not enabled", __func__, pipe_id, min_bpp, max_bpp); -+ -+ if (pipe_id >= MAX9295_NUM_VIDEO_PIPES) -+ return -EINVAL; -+ -+ /* Auto bpp should be disabled to override bpp */ -+ TRY(ret, regmap_update_bits(map, MAX9295_VIDEO_TX0(pipe_id), -+ MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD, enable ? 0U : 1U)) -+ ); -+ -+ if (enable) -+ TRY(ret, regmap_update_bits(map, MAX9295_VIDEO_TX1(pipe_id), -+ MAX9295_VIDEO_TX1_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_VIDEO_TX1_BPP_FIELD, max_bpp)) -+ ); -+ -+ // We share MAX9295_SOFT_BPP_FIELD/MAX9295_SOFT_BPP_EN_FIELD with -+ // max9295_set_video_pipe_double_loading(), only write to it if zero -+ // padding is enabled (this function takes precedence) or if both zero -+ // pading is disabled and double loading is disabled. -+ if (enable || (!enable && config->dbl_pixel_bpp == 0)) { -+ TRY(ret, regmap_update_bits(map, MAX9295_SOFT_BPP(pipe_id), -+ MAX9295_SOFT_BPP_EN_FIELD | MAX9295_SOFT_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_EN_FIELD, enable ? 1U : 0U) | -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_FIELD, min_bpp)) -+ ); -+ } -+ -+ return 0; -+} -+ -+static int max9295_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX9295_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX9295_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX9295_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX9295_NUM_CSI_LINKS; -+ case MAX9X_DATA_TYPES: -+ return MAX9295_NUM_DATA_TYPES; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static int max9295_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_dbg(dev, "Link %d: Enable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ TRY(ret, max9295_set_pipe_data_types_enabled(common, pipe_id, true)); -+ -+ TRY(ret, max9295_set_video_pipe_double_loading(common, pipe_id, config->dbl_pixel_bpp)); -+ -+ TRY(ret, max9295_set_video_pipe_pixel_padding(common, pipe_id, -+ config->soft_min_pixel_bpp, config->soft_max_pixel_bpp)); -+ -+ TRY(ret, max9295_set_pipe_csi_enabled(common, pipe_id, config->src_csi, true)); -+ } -+ -+ return 0; -+} -+ -+static int max9295_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_dbg(dev, "Link %d: Disable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ TRY(ret, max9295_set_pipe_data_types_enabled(common, pipe_id, false)); -+ -+ TRY(ret, max9295_set_pipe_csi_enabled(common, pipe_id, config->src_csi, false)); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Enable RCLK (27MHz) output on MFP4 pin. This pin is routed on some imager boards -+ * to the imager instead of an on-board oscillator. -+ */ -+static int max9295_enable_rclk(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ if (!common->external_refclk_enable) { -+ dev_dbg(dev, "Enable RCLK: external-refclk-enable not present, not enabling external refclk"); -+ return 0; -+ } -+ -+ dev_info(dev, "Enable RCLK: 27MHz"); -+ -+ // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ MAX9295_REFGEN_PREDEF_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) -+ ); -+ -+ // Enable reference generation -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ MAX9295_REFGEN_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) -+ ); -+ -+ // Configure reference generation output on MFP4 -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ MAX9295_PCLK_GPIO_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) -+ ); -+ -+ // Enable output -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ MAX9295_PCLK_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_REG3, -+ MAX9295_RCLK_SEL_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_REG6, -+ MAX9295_RCLK_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) -+ ); -+ -+ return 0; -+} -+ -+static int max9295_set_local_control_channel_enabled(struct max9x_common *common, bool enabled) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); -+} -+ -+static int max9295_select_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max9295_set_local_control_channel_enabled(common, true); -+} -+ -+static int max9295_deselect_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max9295_set_local_control_channel_enabled(common, false); -+} -+ -+static int max9295_verify_devid(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int dev_id, dev_rev; -+ int ret; -+ -+ // Fetch and output chip name + revision -+ TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ -+ switch (dev_id) { -+ case MAX9295A: -+ dev_info(dev, "Detected MAX9295A revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ case MAX9295B: -+ dev_info(dev, "Detected MAX9295B revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ case MAX9295E: -+ dev_info(dev, "Detected MAX9295E revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ default: -+ dev_err(dev, "Unknown device ID %d revision %ld", dev_id, -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int max9295_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ TRY(ret, max9295_verify_devid(common)); -+ -+ // Turn on RCLK/PCLK -+ ret = max9295_enable_rclk(common); -+ if (ret) -+ dev_warn(dev, "Failed to enable RCLK output"); -+ -+ // Initialize local CC to off -+ TRY(ret, max9295_set_local_control_channel_enabled(common, false)); -+ -+ /* Clear the pipe maps */ -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ -+ /* Clear the csi port selections */ -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ -+ return 0; -+} -+ -+static int max9295_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "Disable"); -+ -+ return 0; -+} -+ -+static int max9295_remap_addr(struct max9x_common *common) -+{ -+ int ret; -+ struct device *dev = common->dev; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ -+ if (!common->phys_client) -+ return 0; -+ -+ if (!common->phys_map) -+ return 0; -+ -+ dev_info(dev, "Remap address from 0x%02x to 0x%02x", -+ common->phys_client->addr, common->client->addr); -+ -+ TRY(ret, regmap_update_bits(common->phys_map, MAX9295_REG0, -+ MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, common->client->addr)) -+ ); -+ -+ /* -+ * Use lower bits of I2C address as unique TX_SRC_ID to prevent -+ * conflicts for info frames, SPI, etc. (Leave video pipes alone) -+ */ -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGI_INFOFR_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_SPI_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ /* -+ * This exponential retry works around for MAX96724X. -+ */ -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ ret = regmap_update_bits(common->map, MAX9295_CFGC_CC_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ if (ret) { -+ dev_warn(dev, -+ "configure MAX9295_CFGC_CC_TR3 failed, trying again (waiting %d ms)", ms); -+ msleep(ms); -+ } else -+ break; -+ } -+ -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_GPIO_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_X, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ -+ return 0; -+} -+ -+static int max9295_remap_reset(struct max9x_common *common) -+{ -+ int ret; -+ struct device *dev = common->dev; -+ struct max9x_pdata *pdata = dev->platform_data; -+ u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -+ common->client->addr; -+ u32 virt_addr = common->client->addr; -+ -+ dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, -+ phys_addr); -+ -+ TRY(ret, regmap_update_bits( -+ common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ -+ return 0; -+} -+ -+static int max9295_add_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int alias; -+ unsigned int src; -+ int ret; -+ -+ for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ -+ src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); -+ if (src == virt_addr || src == 0) { -+ dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", -+ MAX9295_I2C_SRC(i2c_id, alias), virt_addr, -+ MAX9295_I2C_DST(i2c_id, alias), phys_addr); -+ TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) -+ ); -+ } -+ } -+ -+ return 0; -+} -+ -+static int max9295_remove_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr) -+{ -+ struct regmap *map = common->map; -+ unsigned int alias; -+ unsigned int src; -+ int ret; -+ -+ for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); -+ if (src == virt_addr) { -+ return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); -+ } -+ } -+ -+ return 0; -+} -+ -+static int max9295_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Reset"); -+ -+ /* Reset entire chip by CTRL0_RST_ALL: 0x10[7]*/ -+ TRY(ret, regmap_write(map, MAX9295_CTRL0, MAX9295_CTRL0_RST_ALL)); -+ usleep_range(45000, 45050); -+ -+ return 0; -+} -+ -+int max9295_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ (*common_ops) = &max9295_common_ops; -+ (*serial_ops) = &max9295_serial_link_ops; -+ (*csi_ops) = NULL; -+ (*lf_ops) = NULL; -+ (*trans_ops) = &max9295_translation_ops; -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Yan, Dongcheng "); -+MODULE_DESCRIPTION("Maxim MAX9295 CSI-2/parallel to GMSL2 Serializer driver"); -diff --git a/drivers/media/i2c/max9x/max9295.h b/drivers/media/i2c/max9x/max9295.h -new file mode 100644 -index 000000000000..de4505639dec ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9295.h -@@ -0,0 +1,143 @@ -+/* -+ * max9295.h - Maxim MAX9295 registers and constants. -+ * -+ * Copyright (c) 2020, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9295_H_ -+#define _MAX9295_H_ -+ -+#include -+#include "serdes.h" -+ -+enum max9295_dev_id { -+ MAX9295A = 0x91, -+ MAX9295B = 0x93, -+ MAX9295E = 0x9B -+}; -+ -+enum max9295_gpio_out_type { -+ MAX9295_GPIO_OUT_TYPE_OPEN_DRAIN = 0, -+ MAX9295_GPIO_OUT_TYPE_PUSH_PULL = 1, -+}; -+ -+enum max9295_gpio_pull_updn_sel { -+ MAX9295_GPIO_PULL_UPDN_SEL_NONE = 0, -+ MAX9295_GPIO_PULL_UPDN_SEL_UP = 1, -+ MAX9295_GPIO_PULL_UPDN_SEL_DOWN = 2, -+}; -+ -+#define MAX9295_NUM_ALIASES 2 /* 2 per i2c bus */ -+#define MAX9295_NUM_SERIAL_LINKS 1 -+#define MAX9295_NUM_VIDEO_PIPES 4 -+#define MAX9295_NUM_MIPI_MAPS 16 -+#define MAX9295_NUM_CSI_LINKS 2 // Only 1 port, but it is technically Port B -+#define MAX9295_NUM_GPIO 11 -+#define MAX9295_NUM_DATA_TYPES 4 -+ -+#define MAX9295_REG0 (0x0) -+#define MAX9295_REG0_DEV_ADDR_FIELD GENMASK(7, 1) -+ -+#define MAX9295_PHY_REM_CTRL (0x1) -+#define MAX9295_PHY_REM_CTRL_DIS_FIELD BIT(4) -+#define MAX9295_PHY_LOCAL_CTRL_DIS_FIELD BIT(5) -+ -+#define MAX9295_REG2 (0x2) -+#define MAX9295_REG2_VID_TX_EN_FIELD(pipe_id) BIT((pipe_id) + 4) -+ -+#define MAX9295_DEV_ID (0xD) -+#define MAX9295_DEV_REV (0xE) -+#define MAX9295_DEV_REV_FIELD GENMASK(3, 0) -+#define MAX9295_CTRL0 (0x10) -+#define MAX9295_CTRL0_RST_ALL BIT(7) -+ -+#define MAX9295_CFGI_INFOFR_TR3 (0x7B) -+#define MAX9295_CFGL_SPI_TR3 (0x83) -+#define MAX9295_CFGC_CC_TR3 (0x8B) -+#define MAX9295_CFGL_GPIO_TR3 (0x93) -+#define MAX9295_CFGL_IIC_X (0xA3) -+#define MAX9295_CFGL_IIC_Y (0xAB) -+#define MAX9295_TR3_TX_SRC_ID GENMASK(2, 0) -+ -+#define MAX9295_VIDEO_TX0(pipe_id) (0x100 + (pipe_id) * 8) -+#define MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD BIT(3) -+#define MAX9295_VIDEO_TX1(pipe_id) (0x101 + (pipe_id) * 8) -+#define MAX9295_VIDEO_TX1_BPP_FIELD GENMASK(5, 0) -+ -+#define MAX9295_GPIO(gpio) (0x2BE + ((gpio) * 3)) -+#define MAX9295_GPIO_A(gpio) (MAX9295_GPIO(gpio) + 0) -+#define MAX9295_GPIO_A_OUT_DIS_FIELD BIT(0) -+#define MAX9295_GPIO_A_TX_EN_FIELD BIT(1) -+#define MAX9295_GPIO_A_RX_EN_FIELD BIT(2) -+#define MAX9295_GPIO_A_IN_FIELD BIT(3) -+#define MAX9295_GPIO_A_OUT_FIELD BIT(4) -+#define MAX9295_GPIO_A_RES_CFG_FIELD BIT(7) -+#define MAX9295_GPIO_B(gpio) (MAX9295_GPIO(gpio) + 1) -+#define MAX9295_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX9295_GPIO_B_OUT_TYPE_FIELD BIT(5) -+#define MAX9295_GPIO_B_PULL_UPDN_SEL_FIELD GENMASK(7, 6) -+#define MAX9295_GPIO_C(gpio) (MAX9295_GPIO(gpio) + 2) -+#define MAX9295_GPIO_C_RX_ID GENMASK(4, 0) -+ -+#define MAX9295_FRONTTOP_0 (0x308) -+#define MAX9295_FRONTTOP_0_LINE_INFO BIT(6) -+#define MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id) BIT((csi_id) + 4) -+#define MAX9295_FRONTTOP_9 (0x311) -+#define MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id) BIT((pipe_id) + 4 * (csi_id)) -+ -+// Double loading mode -+#define MAX9295_FRONTTOP_10 (0x312) -+#define MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_11 (0x313) -+#define MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id) BIT((pipe_id) + 4) -+ -+#define MAX9295_MEM_DT_SEL(pipe_id, dt_slot) (0x314 + (dt_slot) / 2 * 0xC2 + 2 * (pipe_id) + (dt_slot)) -+#define MAX9295_MEM_DT_SEL_DT_FIELD GENMASK(5, 0) -+#define MAX9295_MEM_DT_SEL_EN_FIELD BIT(6) -+ -+// Software BPP override (used for double loading mode and zero padding) -+#define MAX9295_SOFT_BPP(pipe_id) (0x31C + (pipe_id)) -+#define MAX9295_SOFT_BPP_EN_FIELD BIT(5) -+#define MAX9295_SOFT_BPP_FIELD GENMASK(4, 0) -+ -+#define MAX9295_MIPI_RX (0x330) -+#define MAX9295_MIPI_RX_1 (MAX9295_MIPI_RX + 1) -+#define MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id) (GENMASK(1, 0) << (csi_id * 4)) -+ -+// I2C SRC/DST -+#define MAX9295_I2C_SRC(i2c_id, n) ((i2c_id == 0 ? 0x42 : (0x550 + (4 * ((i2c_id) - 1)))) + (2 * (n)) + 0) -+#define MAX9295_I2C_SRC_FIELD GENMASK(7, 1) -+#define MAX9295_I2C_DST(i2c_id, n) ((i2c_id == 0 ? 0x42 : (0x550 + (4 * ((i2c_id) - 1)))) + (2 * (n)) + 1) -+#define MAX9295_I2C_DST_FIELD GENMASK(7, 1) -+ -+// RCLK registers -+#define MAX9295_REG6 (0x6) -+#define MAX9295_RCLK_EN_FIELD BIT(5) -+#define MAX9295_REG3 (0x3) -+#define MAX9295_RCLK_SEL_FIELD GENMASK(1, 0) -+#define MAX9295_REF_VTG0 (0x3F0) -+#define MAX9295_REFGEN_PREDEF_FREQ_FIELD GENMASK(5, 4) -+#define MAX9295_REFGEN_PREDEF_ALT_FIELD BIT(3) -+#define MAX9295_REFGEN_EN_FIELD BIT(0) -+#define MAX9295_REF_VTG1 (0x3F1) -+#define MAX9295_PCLK_GPIO_FIELD GENMASK(5, 1) -+#define MAX9295_PCLK_EN_FIELD BIT(0) -+ -+#endif /* _MAX9295_H_ */ -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -new file mode 100644 -index 000000000000..41074d60cc01 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -0,0 +1,881 @@ -+/* -+ * max9296.c - Maxim MAX9296 GMSL2/GMSL1 to CSI-2 Deserializer -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max9296.h" -+ -+// Params -+int max9296_serial_link_timeout_ms = MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -+module_param(max9296_serial_link_timeout_ms, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max9296_serial_link_timeout_ms, "Timeout for serial link in milliseconds"); -+ -+// Declarations -+static int max9296_set_phy_mode(struct max9x_common *common, unsigned int phy_mode); -+static int max9296_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map); -+static int max9296_set_phy_dpll_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz); -+static int max9296_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes); -+static int max9296_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, -+ bool enable, unsigned int width); -+static int max9296_configure_csi_dphy(struct max9x_common *common); -+static int max9296_enable(struct max9x_common *common); -+static int max9296_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max9296_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked); -+static int max9296_serial_link_reset(struct max9x_common *common, unsigned int link_id); -+static int max9296_set_serial_link_rate(struct max9x_common *common, unsigned int link_id); -+static int max9296_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe); -+static int max9296_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps); -+static int max9296_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int map_id, struct max9x_serdes_mipi_map *mipi_map); -+static int max9296_set_csi_double_loading_mode(struct max9x_common *common, unsigned int csi_id, unsigned int bpp); -+static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned int link_id); -+static int max9296_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9296_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9296_isolate_serial_link(struct max9x_common *common, unsigned int link); -+static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned int link); -+static int max9296_wait_link_lock(struct max9x_common *common, int link); -+static int max9296_enable_csi_link(struct max9x_common *common, unsigned int csi_link_id); -+static int max9296_disable_csi_link(struct max9x_common *common, unsigned int csi_link_id); -+ -+/* Currently unused */ -+static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_id, unsigned int *phy_lane_map); -+ -+// Functions -+static int max9296_set_phy_mode(struct max9x_common *common, unsigned int phy_mode) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI: phy_mode=%d", phy_mode); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY0, -+ MAX9296_MIPI_PHY0_MODE_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_MIPI_PHY0_MODE_FIELD, phy_mode)); -+} -+ -+static int max9296_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: %s", csi_id, (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY_ENABLE, -+ MAX9296_MIPI_PHY_ENABLE_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9296_MIPI_PHY_ENABLE_FIELD(csi_id), enable ? 1U : 0U)); -+} -+ -+static int max9296_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: phy_lane_map=0x%04x", csi_id, phy_lane_map); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY_LANE_MAP(csi_id), -+ MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi_id, 0) -+ | MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi_id, 1), -+ phy_lane_map); -+} -+ -+static int max9296_set_phy_dpll_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: DPLL %s", csi_id, (enable ? "on" : "off")); -+ -+ return regmap_update_bits(map, MAX9296_DPLL_RESET(csi_id), -+ MAX9296_DPLL_RESET_SOFT_RST_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_DPLL_RESET_SOFT_RST_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max9296_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ if (WARN_ONCE(freq_mhz > 0 && freq_mhz < MAX9296_DPLL_FREQ_MHZ_MULTIPLE, -+ "CSI frequency must be greater than %d MHz", MAX9296_DPLL_FREQ_MHZ_MULTIPLE)) -+ return -EINVAL; -+ -+ dev_dbg(dev, "CSI link %d: freq %u MHz, %u mult", csi_id, freq_mhz, freq_mhz / MAX9296_DPLL_FREQ_MHZ_MULTIPLE); -+ -+ return regmap_update_bits(map, MAX9296_DPLL_FREQ(csi_id), -+ MAX9296_DPLL_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_DPLL_FREQ_FIELD, freq_mhz / MAX9296_DPLL_FREQ_MHZ_MULTIPLE)); -+} -+ -+static int max9296_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: %d lanes", csi_id, num_lanes); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_TX_LANE_CNT(csi_id), -+ MAX9296_MIPI_TX_LANE_CNT_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_LANE_CNT_FIELD, -+ (common->csi_link[csi_id].config.num_lanes - 1))); -+} -+ -+static int max9296_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, -+ bool enable, unsigned int width) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: Initial deskew %s", csi_id, enable ? "enabled" : "disabled"); -+ -+ /* clamp initial deskew width to 7 which is 8*32k UI*/ -+ if (width > 7) -+ width = 7; -+ -+ return regmap_write(map, MAX9296_MIPI_TX_DESKEW_INIT(csi_id), -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_DESKEW_INIT_AUTO_EN, enable) | -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_DESKEW_INIT_WIDTH, width)); -+} -+ -+static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_id, -+ unsigned int *phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ int i; -+ const unsigned int phy_count = 2; -+ const unsigned int controller = csi_id / phy_count; -+ -+ if (common->csi_link[csi_id].config.num_lanes != common->csi_link[csi_id].config.num_maps) { -+ dev_err(dev, "CSI%u number of maps %u must match number of lanes %u.", csi_id, -+ common->csi_link[csi_id].config.num_maps, -+ common->csi_link[csi_id].config.num_lanes); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < common->csi_link[csi_id].config.num_maps; i++) { -+ const struct max9x_serdes_phy_map *map = &common->csi_link[csi_id].config.map[i]; -+ -+ if (map->int_csi >= 4) { -+ dev_err(dev, "CSI%u does not have %u Lanes can not map.", csi_id, -+ map->int_csi + 1); -+ return -EINVAL; -+ } -+ if (map->phy_lane >= 2) { -+ dev_err(dev, "Each PHY has 2 lanes can not map %u.", map->phy_ind); -+ return -EINVAL; -+ } -+ if (map->phy_ind < (controller * phy_count) || -+ map->phy_ind >= ((controller + 1) * phy_count)) { -+ dev_err(dev, "CSI%u does not have PHYs %u can not map.", csi_id, -+ map->phy_ind); -+ return -EINVAL; -+ } -+ phy_lane_map[map->phy_ind] |= MAX9X_FIELD_PREP( -+ MAX9296_MIPI_PHY_LANE_MAP_FIELD(map->phy_ind, map->phy_lane), map->int_csi); -+ } -+ return 0; -+} -+ -+/* -+ * MAX9296A has four PHYs, but does not support single-PHY configurations, -+ * only double-PHY configurations, even when only using two lanes. -+ * For PHY 0 + PHY 1, PHY 1 is the master PHY. -+ * For PHY 2 + PHY 3, PHY 2 is the master PHY. -+ * Clock is always on the master PHY. -+ * For first pair of PHYs, first lanes are on the master PHY. -+ * For second pair of PHYs, first lanes are on the master PHY too. -+ * -+ * PHY 0 + 1 -+ * CLK = PHY 1 -+ * PHY1 Lane 0 = D0 -+ * PHY1 Lane 1 = D1 -+ * PHY0 Lane 0 = D2 -+ * PHY0 Lane 1 = D3 -+ * -+ * PHY 2 + 3 -+ * CLK = PHY 2 -+ * PHY2 Lane 0 = D0 -+ * PHY2 Lane 1 = D1 -+ * PHY3 Lane 0 = D2 -+ * PHY3 Lane 1 = D3 -+ */ -+static int max9296_configure_csi_dphy(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phy_mode; -+ unsigned int phy_lane_map[MAX9296_NUM_CSI_LINKS] = {0}; -+ unsigned int csi_id; -+ unsigned int PHY1, PHY2; -+ int ret; -+ -+ for (csi_id = 0; csi_id < MAX9296_NUM_CSI_LINKS; csi_id++) { -+ dev_dbg(dev, "CSI link %d: enabled=%d, num_lanes=%d, freq_mhz=%d init_deskew=%d", -+ csi_id, common->csi_link[csi_id].enabled, -+ common->csi_link[csi_id].config.num_lanes, -+ common->csi_link[csi_id].config.freq_mhz, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ } -+ -+ PHY1 = common->csi_link[0].enabled ? common->csi_link[0].config.num_lanes : 0; -+ PHY2 = common->csi_link[1].enabled ? common->csi_link[1].config.num_lanes : 0; -+ /* Each csi controller has 4 lanes. each phy has 2 lanes*/ -+ if ((PHY1 + PHY2) > 4) { -+ dev_err(dev, "CSI controller 1 has more than %u lanes. PHY0: %u, PHY1: %u", 4, PHY1, -+ PHY1); -+ return -EINVAL; -+ } -+ -+ PHY1 = common->csi_link[2].enabled ? common->csi_link[2].config.num_lanes : 0; -+ PHY2 = common->csi_link[3].enabled ? common->csi_link[3].config.num_lanes : 0; -+ /* Each csi controller has 4 lanes. each phy has 2 lanes */ -+ if ((PHY1 + PHY2) > 4) { -+ dev_err(dev, "CSI controller 2 has more than %u lanes. PHY2: %u, PHY3: %u", 4, PHY1, -+ PHY1); -+ return -EINVAL; -+ } -+ -+ if (common->csi_link[1].enabled && common->csi_link[1].config.num_lanes == 4) { -+ if (common->csi_link[0].enabled) -+ dev_warn(dev, "CSI link 0 enabled, but CSI link 1 is 4 lanes."); -+ -+ /* lane 1 is master for CSI controller 1*/ -+ max9296_conf_phy_maps(common, 1, phy_lane_map); -+ if (common->csi_link[2].enabled && common->csi_link[2].config.num_lanes == 4) { -+ if (common->csi_link[3].enabled) -+ dev_warn(dev, "CSI link 3 enabled, but CSI link 2 is 4 lanes."); -+ dev_dbg(dev, "CSI phy mode is set to 2X4Lanes"); -+ /* lane 2 is master for CSI controller 2*/ -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_2X4; // 2x 4lane -+ } else { -+ dev_dbg(dev, "CSI phy mode is set to A: 4Lanes B: 2X2Lanes"); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ max9296_conf_phy_maps(common, 3, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_1X4A_22; // A: 1x 4lane B: 2x 2lane -+ } -+ } else { -+ max9296_conf_phy_maps(common, 1, phy_lane_map); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ if (common->csi_link[2].enabled && common->csi_link[2].config.num_lanes == 4) { -+ if (common->csi_link[3].enabled) -+ dev_warn(dev, "CSI link 3 enabled, but CSI link 2 is 4 lanes."); -+ dev_dbg(dev, "CSI phy mode is set to A: 2X2Lanes B: 4Lanes"); -+ /* lane 2 is master for CSI controller 2*/ -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_1X4B_22; // B: 1x 4lane A: 2x 2lane -+ } else { -+ dev_dbg(dev, "CSI phy mode is set to 4X2Lanes"); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ max9296_conf_phy_maps(common, 3, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_4X2; // 4x 2lane -+ } -+ } -+ -+ TRY(ret, max9296_set_phy_mode(common, phy_mode)); -+ -+ for (csi_id = 0; csi_id < MAX9296_NUM_CSI_LINKS; csi_id++) { -+ struct max9x_serdes_csi_config *config = -+ &common->csi_link[csi_id].config; -+ -+ TRY(ret, max9296_set_phy_enabled(common, csi_id, false)); -+ -+ TRY(ret, max9296_set_phy_dpll_enabled(common, csi_id, false)); -+ -+ TRY(ret, max9296_set_phy_lane_map(common, csi_id, -+ phy_lane_map[csi_id]) -+ ); -+ -+ TRY(ret, max9296_set_mipi_lane_cnt(common, csi_id, -+ config->num_lanes) -+ ); -+ } -+ -+ return 0; -+} -+ -+static int max9296_set_all_reset(struct max9x_common *common, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Reset ALL %s", (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_CTRL0, -+ MAX9296_CTRL0_RESET_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_CTRL0_RESET_ALL_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max9296_soft_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "Soft reset"); -+ -+ TRY(ret, max9296_set_all_reset(common, 1)); -+ /* lock time for reset_all / PWDNB in spec, I2C Wake time is 2.25ms */ -+ usleep_range(45000, 45050); -+ TRY(ret, max9296_set_all_reset(common, 0)); -+ -+ return 0; -+} -+ -+static int max9296_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX9296_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX9296_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX9296_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX9296_NUM_CSI_LINKS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static struct max9x_common_ops max9296_common_ops = { -+ .enable = max9296_enable, -+ .soft_reset = max9296_soft_reset, -+ .max_elements = max9296_max_elements, -+}; -+ -+static int max9296_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ /* -+ * link_id ignored for max9296, pipe routing done through stream-ids, -+ * which must be unique across links -+ */ -+ dev_dbg(dev, "Video-pipe %d: src_pipe=%u", pipe_id, src_pipe); -+ -+ return regmap_update_bits(map, MAX9296_VIDEO_PIPE_SEL(pipe_id), -+ MAX9296_VIDEO_PIPE_STR_SEL_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_VIDEO_PIPE_STR_SEL_FIELD, src_pipe)); -+} -+ -+static int max9296_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val = 0; -+ int ret; -+ -+ if (num_maps > 0) -+ val = GENMASK(num_maps - 1, 0); -+ -+ dev_dbg(dev, "Video-pipe %d: num_maps=%u", pipe_id, num_maps); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_EN_L(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_EN_FIELD, val)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_EN_H(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_EN_FIELD, val >> 8)) -+ ); -+ -+ return 0; -+} -+ -+static int max9296_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int map_id, struct max9x_serdes_mipi_map *mipi_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, map %d: VC%d:DT%02x->VC%d:DT%02x, dst_csi=%d ", -+ pipe_id, map_id, mipi_map->src_vc, mipi_map->src_dt, -+ mipi_map->dst_vc, mipi_map->dst_dt, mipi_map->dst_csi); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_SRC_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRC_L_VC_FIELD, mipi_map->src_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRC_L_DT_FIELD, mipi_map->src_dt)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_DST_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_DST_L_VC_FIELD, mipi_map->dst_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_DST_L_DT_FIELD, mipi_map->dst_dt)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_SRCDST_H(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRCDST_H_SRC_VC_FIELD, mipi_map->src_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRCDST_H_DST_VC_FIELD, mipi_map->dst_vc)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9296_MAP_DPHY_DEST(pipe_id, map_id), -+ MAX9296_MAP_DPHY_DEST_FIELD(map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_DPHY_DEST_FIELD(map_id), mipi_map->dst_csi)) -+ ); -+ -+ return 0; -+} -+ -+/** -+ * max9296_set_csi_double_loading_mode() - Configure Double Loading Mode on a CSI controller -+ * @common: max9x_common -+ * @csi_id: Target CSI controller's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.4.2.3 "Double Mode (Deserializer)" -+ */ -+static int max9296_set_csi_double_loading_mode(struct max9x_common *common, unsigned int csi_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int value; -+ -+ switch (bpp) { -+ case 0: -+ value = 0; -+ break; -+ case 8: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_8BPP, 1U); -+ // To fully support 8bpp, additional register writes are -+ // needed for 'bpp8dbl' and 'bpp8dbl_mode' fields on each pipe. -+ dev_err(dev, "8 BPP currently unsupported for pixel doubling"); -+ return -EINVAL; -+ case 10: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_10BPP, 1U); -+ break; -+ case 12: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_12BPP, 1U); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ if (bpp > 0) -+ dev_info(dev, "Configuring double loading mode on CSI %d: %u bpp -> %u bpp", -+ csi_id, bpp, (bpp * 2)); -+ -+ // Enable alt mem mapping -+ return regmap_update_bits(map, MAX9296_MIPI_TX_ALT_MEM(csi_id), -+ MAX9296_MIPI_TX_ALT_MEM_FIELD, value); -+} -+ -+static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct max9x_serdes_csi_link *csi_link; -+ int ret; -+ -+ if (csi_id > common->num_csi_links) -+ return -EINVAL; -+ -+ csi_link = &common->csi_link[csi_id]; -+ -+ if (WARN_ONCE(enable && csi_link->enabled == false, "Tried to enable a disabled CSI port???")) -+ return -EINVAL; -+ -+ if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, "Tried to enable CSI port with no lanes???")) -+ return -EINVAL; -+ -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+ dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, (enable ? "enable" : "disable"), csi_link->usecount); -+ -+ if (enable && csi_link->usecount == 1) { -+ // Enable && first user -+ ret = max9296_set_initial_deskew(common, csi_id, csi_link->config.auto_init_deskew_enabled, -+ csi_link->config.initial_deskew_width); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_freq(common, csi_id, csi_link->config.freq_mhz); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ } else if (!enable && csi_link->usecount == 0) { -+ // Disable && no more users -+ ret = max9296_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ } -+ -+ return 0; -+} -+ -+static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Video-pipe %d: %s", pipe_id, (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_VIDEO_PIPE_EN(pipe_id), -+ MAX9296_VIDEO_PIPE_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_VIDEO_PIPE_EN_FIELD(pipe_id), enable ? 1U : 0U)); -+} -+ -+/***** max9296_serial_link_ops auxiliary functions *****/ -+static int max9296_set_serial_link_rate(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -+ int tx_rate, rx_rate; -+ -+ tx_rate = max9x_serdes_mhz_to_rate(max9296_tx_rates, ARRAY_SIZE(max9296_tx_rates), config->tx_freq_mhz); -+ if (tx_rate < 0) -+ return tx_rate; -+ -+ rx_rate = max9x_serdes_mhz_to_rate(max9296_rx_rates, ARRAY_SIZE(max9296_rx_rates), config->rx_freq_mhz); -+ if (rx_rate < 0) -+ return rx_rate; -+ -+ dev_dbg(dev, "Serial-link %d: TX=%d MHz RX=%d MHz", link_id, config->tx_freq_mhz, config->rx_freq_mhz); -+ -+ return regmap_update_bits(map, MAX9296_PHY_REM_CTRL, -+ MAX9296_PHY_REM_CTRL_TX_FIELD | MAX9296_PHY_REM_CTRL_RX_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_PHY_REM_CTRL_TX_FIELD, tx_rate) | -+ MAX9X_FIELD_PREP(MAX9296_PHY_REM_CTRL_RX_FIELD, rx_rate)); -+} -+ -+static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max9296_set_video_pipe_src(common, pipe_id, config->src_link, config->src_pipe); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_video_pipe_maps_enabled(common, pipe_id, config->num_maps); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max9296_set_video_pipe_map(common, pipe_id, map_id, &config->map[map_id]); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_csi_double_loading_mode(common, -+ config->map[map_id].dst_csi, config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ -+ if (common->csi_link[config->map[map_id].dst_csi].config.auto_start) { -+ ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, true); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ ret = max9296_set_video_pipe_enabled(common, pipe_id, true); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max9296_serial_link_reset(struct max9x_common *common, unsigned int link_id) -+{ -+ struct regmap *map = common->map; -+ -+ /* GMSL RX rate must be the same as the SER. This is set in REG1(0x1)[1:0] */ -+ return regmap_update_bits(map, MAX9296_CTRL0, MAX9296_CTRL0_RESET_ONESHOT_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_CTRL0_RESET_ONESHOT_FIELD, 1U)); -+} -+ -+static int max9296_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked) -+{ -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ /* Only looks at link A refer to header file */ -+ ret = regmap_read(map, MAX9296_PHY_LOCKED(link_id), &val); -+ if (ret) -+ return ret; -+ -+ if (FIELD_GET(MAX9296_PHY_LOCKED_FIELD, val) != 0) -+ *locked = true; -+ else -+ *locked = false; -+ -+ return 0; -+} -+ -+static int max9296_wait_link_lock(struct max9x_common *common, int link) -+{ -+ bool locked; -+ int ret; -+ ulong timeout = jiffies + msecs_to_jiffies(max9296_serial_link_timeout_ms); -+ -+ do { -+ ret = max9296_get_serial_link_lock(common, link, &locked); -+ if (ret == 0 && locked) -+ return 0; -+ -+ usleep_range(1000, 2000); -+ } while (time_is_after_jiffies(timeout)); -+ -+ return -ETIMEDOUT; -+} -+/***** max9296_serial_link_ops auxiliary functions *****/ -+ -+/***** max9296_serial_link_ops *****/ -+static int max9296_isolate_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int link_cfg; -+ unsigned int auto_link; -+ int ret; -+ -+ dev_dbg(dev, "Isolate link %d", link); -+ -+ auto_link = 0; -+ link_cfg = (link == 0) ? MAX9296_LINK_A : MAX9296_LINK_B; -+ -+ TRY_DEV_HERE(ret, regmap_update_bits(map, MAX9296_CTRL0, -+ MAX9296_CTRL0_AUTO_CFG_FIELD | MAX9296_CTRL0_LINK_CFG_FIELD, -+ FIELD_PREP(MAX9296_CTRL0_AUTO_CFG_FIELD, auto_link) -+ | FIELD_PREP(MAX9296_CTRL0_LINK_CFG_FIELD, link_cfg)), -+ dev); -+ -+ TRY_DEV_HERE(ret, max9296_serial_link_reset(common, link), dev); -+ -+ TRY_DEV_HERE(ret, max9296_wait_link_lock(common, link), dev); -+ -+ return 0; -+} -+ -+static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int link_cfg; -+ unsigned int auto_link = 0; -+ int ret; -+ bool link_a = common->serial_link[0].detected; -+ bool link_b = common->serial_link[1].detected; -+ -+ if (link_a && link_b) -+ link_cfg = MAX9296_LINK_SPLIT; -+ else if (link_a) -+ link_cfg = MAX9296_LINK_A; -+ else if (link_b) -+ link_cfg = MAX9296_LINK_B; -+ else { -+ dev_err(dev, "No link was detected"); -+ return -1; -+ } -+ -+ dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -+ -+ TRY_DEV_HERE(ret, regmap_update_bits( -+ map, -+ MAX9296_CTRL0, -+ MAX9296_CTRL0_AUTO_CFG_FIELD -+ |MAX9296_CTRL0_LINK_CFG_FIELD, -+ FIELD_PREP(MAX9296_CTRL0_AUTO_CFG_FIELD, auto_link) -+ |FIELD_PREP(MAX9296_CTRL0_LINK_CFG_FIELD, link_cfg)), -+ dev); -+ -+ TRY_DEV_HERE(ret, max9296_serial_link_reset(common, link), dev); -+ -+ TRY_DEV_HERE(ret, max9296_wait_link_lock(common, link), dev); -+ -+ return 0; -+} -+ -+static int max9296_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ -+ if (WARN_ON_ONCE(link_id >= common->num_serial_links)) -+ return -EINVAL; -+ -+ if (WARN_ONCE(common->serial_link[link_id].config.link_type -+ != MAX9X_LINK_TYPE_GMSL2, "Only GMSL2 is supported!")) -+ return -EINVAL; -+ -+ // GMSL2 -+ ret = max9296_set_serial_link_rate(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9296_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ common->serial_link[link_id].detected = true; -+ -+ ret = max9296_set_serial_link_routing(common, link_id); -+ if (ret) -+ return ret; -+ -+ max9296_deisolate_serial_link(common, link_id); -+ -+ return 0; -+} -+ -+static int max9296_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max9296_set_video_pipe_enabled(common, pipe_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_video_pipe_maps_enabled(common, pipe_id, 0); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static struct max9x_serial_link_ops max9296_serial_link_ops = { -+ .enable = max9296_enable_serial_link, -+ .disable = max9296_disable_serial_link, -+ .isolate = max9296_isolate_serial_link, -+ .deisolate = max9296_deisolate_serial_link, -+}; -+/***** max9296_serial_link_ops *****/ -+ -+static int max9296_enable_csi_link(struct max9x_common *common, unsigned int csi_link_id) -+{ -+ return max9296_set_csi_link_enabled(common, csi_link_id, true); -+} -+ -+static int max9296_disable_csi_link(struct max9x_common *common, unsigned int csi_link_id) -+{ -+ return max9296_set_csi_link_enabled(common, csi_link_id, false); -+} -+ -+static struct max9x_csi_link_ops max9296_csi_link_ops = { -+ .enable = max9296_enable_csi_link, -+ .disable = max9296_disable_csi_link, -+}; -+ -+static int max9296_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int link_id; -+ int ret; -+ -+ dev_dbg(dev, "Enable"); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ ret = max9296_disable_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ } -+ -+ ret = max9296_configure_csi_dphy(common); -+ -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+int max9296_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ *common_ops = &max9296_common_ops; -+ *serial_ops = &max9296_serial_link_ops; -+ *csi_ops = &max9296_csi_link_ops; -+ *lf_ops = NULL; -+ *trans_ops = NULL; -+ -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Jacob Kiggins "); -+MODULE_AUTHOR("Yan Dongcheng "); -+MODULE_DESCRIPTION("Maxim MAX9296 Dual GMSL2/GMSL1 to CSI-2 Deserializer driver"); -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -new file mode 100644 -index 000000000000..38c344669df4 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -0,0 +1,144 @@ -+/* -+ * max9296.h - Maxim 9296 registers and constants. -+ * -+ * Copyright (c) 2023-2024 Define Design Deploy Corp. All Rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9296_H_ -+#define _MAX9296_H_ -+ -+#include -+#include "serdes.h" -+ -+enum max9296_dev_id { -+ MAX9296A = 0x94 -+}; -+ -+enum max9296_phy_mode { -+ MAX9296_MIPI_PHY_4X2 = BIT(0), // four 2-lane ports -+ MAX9296_MIPI_PHY_1X4 = BIT(1), // one 4-lane (CSI2 is master (TODO: Which port???)) -+ MAX9296_MIPI_PHY_2X4 = BIT(2), // two 4-lane ports (CSI1 is master for port A, CSI2 for port B) -+ MAX9296_MIPI_PHY_1X4A_22 = BIT(3), // one 4-lane (PHY0+PHY1, CSI1 for port A) port and two 2-lane ports -+ MAX9296_MIPI_PHY_1X4B_22 = BIT(4), // one 4-lane (PHY2+PHY3, CSI2 for port B) port and two 2-lane ports -+}; -+ -+enum max9296_link_mode { -+ MAX9296_LINK_AB, -+ MAX9296_LINK_A, -+ MAX9296_LINK_B, -+ MAX9296_LINK_SPLIT -+}; -+ -+#define MAX9296_NUM_SERIAL_LINKS 2 -+#define MAX9296_NUM_VIDEO_PIPES 4 -+#define MAX9296_NUM_MIPI_MAPS 16 -+#define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ -+/* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ -+ -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+ -+#define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 -+ -+#define MAX9296_FLD_OFS(n, bits_per_field, count) (((n) % (count)) * (bits_per_field)) -+#define MAX9296_OFFSET_GENMASK(offset, h, l) GENMASK(offset + h, offset + l) -+ -+#define MAX9296_CTRL0 0x10 -+#define MAX9296_CTRL0_AUTO_CFG_FIELD BIT(4) -+#define MAX9296_CTRL0_LINK_CFG_FIELD GENMASK(1, 0) -+#define MAX9296_CTRL0_RESET_LINK_FIELD BIT(6) // One bit to reset whole link -+#define MAX9296_CTRL0_RESET_ALL_FIELD BIT(7) -+#define MAX9296_CTRL0_RESET_ONESHOT_FIELD BIT(5) -+ -+#define MAX9296_PHY_REM_CTRL (0x1) -+#define MAX9296_PHY_REM_CTRL_TX_FIELD (GENMASK(1, 0) << 2) -+#define MAX9296_PHY_REM_CTRL_RX_FIELD GENMASK(1, 0) -+#define MAX9296_PHY_REM_CTRL_DIS_FIELD BIT(4) -+ -+/* -+ *CTRL3(0x13) LINK_MODE is set to link A. -+ */ -+#define MAX9296_PHY_LOCKED(link) (0x13) /* Based on link mode */ -+#define MAX9296_PHY_LOCKED_FIELD BIT(3) -+ -+#define MAX9296_VIDEO_PIPE_SEL(pipe) (0x50 + pipe) -+#define MAX9296_VIDEO_PIPE_STR_SEL_FIELD GENMASK(1, 0) -+ -+#define MAX9296_VIDEO_PIPE_EN(pipe) (0x2) -+#define MAX9296_VIDEO_PIPE_EN_FIELD(pipe) (BIT(pipe) << 4) -+ -+#define MAX9296_DPLL_FREQ(phy) (0x31D + ((phy) * 3)) -+#define MAX9296_DPLL_FREQ_FIELD GENMASK(4, 0) -+ -+#define MAX9296_MIPI_TX_EXT(pipe) (0x500 + ((pipe) * 0x10)) -+ -+#define MAX9296_MIPI_PHY0 0x330 -+#define MAX9296_MIPI_PHY0_MODE_FIELD GENMASK(4, 0) -+#define MAX9296_MIPI_PHY_ENABLE 0x332 -+#define MAX9296_MIPI_PHY_ENABLE_FIELD(csi) BIT((csi) + 4) -+#define MAX9296_MIPI_PHY_LANE_MAP(csi) (0x333 + (csi) / 2) -+#define MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ -+ (GENMASK(1, 0) << (MAX9296_FLD_OFS(csi, 4, 2) + MAX9296_FLD_OFS(lane, 2, 2))) -+ -+// Note that CSIs and pipes overlap: -+#define MAX9296_MIPI_TX(pipe) (0x400 + ((pipe) * 0x40)) -+ -+/* Offsets for MAX9296_MIPI_TX under 0x0B are only for lanes 1 and 3 */ -+#define MAX9296_MIPI_TX_LANE_CNT(csi) (MAX9296_MIPI_TX(csi) + 0x0A) -+#define MAX9296_MIPI_TX_LANE_CNT_FIELD GENMASK(7, 6) -+#define MAX9296_MIPI_TX_DESKEW_INIT(csi) (MAX9296_MIPI_TX(csi) + 0x03) -+#define MAX9296_MIPI_TX_DESKEW_INIT_AUTO_EN BIT(7) -+#define MAX9296_MIPI_TX_DESKEW_INIT_WIDTH GENMASK(2, 0) -+#define MAX9296_MAP_EN_L(pipe) (MAX9296_MIPI_TX(pipe) + 0x0B) -+#define MAX9296_MAP_EN_H(pipe) (MAX9296_MIPI_TX(pipe) + 0x0C) -+#define MAX9296_MAP_EN_FIELD GENMASK(7, 0) -+#define MAX9296_MAP_SRC_L(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x0D + ((map) * 2)) -+#define MAX9296_MAP_SRC_L_VC_FIELD GENMASK(7, 6) -+#define MAX9296_MAP_SRC_L_DT_FIELD GENMASK(5, 0) -+#define MAX9296_MAP_DST_L(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x0D + ((map) * 2) + 1) -+#define MAX9296_MAP_DST_L_VC_FIELD GENMASK(7, 6) -+#define MAX9296_MAP_DST_L_DT_FIELD GENMASK(5, 0) -+#define MAX9296_MAP_SRCDST_H(pipe, map) (MAX9296_MIPI_TX_EXT(pipe) + (map)) -+#define MAX9296_MAP_SRCDST_H_SRC_VC_FIELD GENMASK(7, 5) -+#define MAX9296_MAP_SRCDST_H_DST_VC_FIELD GENMASK(4, 2) -+#define MAX9296_MAP_DPHY_DEST(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x2D + ((map) / 4)) -+#define MAX9296_MAP_DPHY_DEST_FIELD(map) (GENMASK(1, 0) << MAX9296_FLD_OFS(map, 2, 4)) -+ -+#define MAX9296_MIPI_TX_ALT_MEM(csi) (MAX9296_MIPI_TX(csi) + 0x33) -+#define MAX9296_MIPI_TX_ALT_MEM_FIELD GENMASK(2, 0) -+#define MAX9296_MIPI_TX_ALT_MEM_8BPP BIT(1) -+#define MAX9296_MIPI_TX_ALT_MEM_10BPP BIT(2) -+#define MAX9296_MIPI_TX_ALT_MEM_12BPP BIT(0) -+ -+#define MAX9296_GPIO_A(gpio_num) (0x2B0 + 3 * gpio_num) -+#define MAX9296_GPIO_B(gpio_num) (MAX9296_GPIO_A(gpio_num) + 1) -+#define MAX9296_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX9296_GPIO_C(gpio_num) (MAX9296_GPIO_A(gpio_num) + 2) -+#define MAX9296_GPIO_C_RX_ID GENMASK(4, 0) -+ -+#define MAX9296_DPLL_RESET(phy) (0x1C00 + ((phy) * 0x100)) -+#define MAX9296_DPLL_RESET_SOFT_RST_FIELD BIT(0) -+ -+static struct max9x_serdes_rate_table max9296_rx_rates[] = { -+ { .val = 1, .freq_mhz = 3000}, // 3 GHz -+ { .val = 2, .freq_mhz = 6000}, // 6 GHz -+}; -+ -+static struct max9x_serdes_rate_table max9296_tx_rates[] = { -+ { .val = 0, .freq_mhz = 187}, // 187.5 MHz -+}; -+ -+#endif /* _MAX9296_H_ */ -diff --git a/drivers/media/i2c/max9x/max96717.c b/drivers/media/i2c/max9x/max96717.c -new file mode 100644 -index 000000000000..4e79ae63a6ed ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96717.c -@@ -0,0 +1,589 @@ -+/* -+ * max96717_main.c - Maxim MAX96717 CSI-2 to GMSL2/GMSL1 Serializer -+ * -+ * Copyright (c) 2022, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max96717.h" -+ -+static const struct regmap_config max96717_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+}; -+ -+// Declarations -+static int max96717_set_pipe_csi_enabled(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int csi_id, bool enable); -+static int max96717_video_pipe_double_pixel(struct max9x_common *common, unsigned int pipe_id, unsigned int bpp); -+static int max96717_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max96717_enable_serial_link(struct max9x_common *common, unsigned int link); -+static int max96717_disable_serial_link(struct max9x_common *common, unsigned int link); -+static int max96717_enable(struct max9x_common *common); -+static int max96717_disable(struct max9x_common *common); -+static int max96717_pixel_mode(struct max9x_common *common, bool pixel); -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip); -+static int max96717_gpio_get_direction(struct gpio_chip *chip, unsigned int offset); -+static int max96717_gpio_direction_input(struct gpio_chip *chip, unsigned int offset); -+static int max96717_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value); -+static int max96717_gpio_get(struct gpio_chip *chip, unsigned int offset); -+static int max96717_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); -+static int max96717_setup_gpio(struct max9x_common *common); -+ -+static struct max9x_common_ops max96717_common_ops = { -+ .max_elements = max96717_max_elements, -+ .enable = max96717_enable, -+ .disable = max96717_disable, -+}; -+ -+static struct max9x_serial_link_ops max96717_serial_link_ops = { -+ .enable = max96717_enable_serial_link, -+ .disable = max96717_disable_serial_link, -+}; -+ -+static struct max9x_translation_ops max96717_translation_ops; -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip) -+{ -+ return container_of(chip, struct max9x_common, gpio_chip); -+} -+ -+static int max96717_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(map, MAX96717_GPIO_A(offset), &val); -+ if (ret) -+ return ret; -+ -+ return (FIELD_GET(MAX96717_GPIO_A_OUT_DIS_FIELD, val) == 0U ? -+ GPIOD_OUT_LOW : GPIOD_IN); -+} -+ -+static int max96717_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX96717_GPIO_A(offset), -+ MAX96717_GPIO_A_OUT_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_DIS_FIELD, 1U)); -+} -+ -+static int max96717_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int mask = 0; -+ unsigned int val; -+ -+ mask = MAX96717_GPIO_A_OUT_DIS_FIELD | MAX96717_GPIO_A_OUT_FIELD | -+ MAX96717_GPIO_A_RX_EN_FIELD; -+ -+ // Enable the GPIO as an output -+ val = MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_DIS_FIELD, 0U); -+ // Write out the initial value to the GPIO -+ val |= MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U)); -+ // Disable remote control over SerDes link -+ val |= MAX9X_FIELD_PREP(MAX96717_GPIO_A_RX_EN_FIELD, 0U); -+ -+ return regmap_update_bits(map, MAX96717_GPIO_A(offset), mask, val); -+} -+ -+static int max96717_gpio_get(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(map, MAX96717_GPIO_A(offset), &val); -+ if (ret) -+ return ret; -+ -+ return FIELD_GET(MAX96717_GPIO_A_IN_FIELD, val); -+} -+ -+static int max96717_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX96717_GPIO_A(offset), -+ MAX96717_GPIO_A_OUT_FIELD, -+ MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U))); -+} -+ -+static int max96717_setup_gpio(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ struct max9x_gpio_pdata *gpio_pdata; -+ -+ if (common->dev->platform_data) { -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ gpio_pdata = &pdata->gpio; -+ } -+ -+ // Functions -+ if (gpio_pdata && gpio_pdata->label) -+ common->gpio_chip.label = gpio_pdata->label; -+ else -+ common->gpio_chip.label = dev_name(dev); -+ -+ dev_dbg(dev, "gpio_chip label is %s, dev_name is %s", -+ common->gpio_chip.label, dev_name(dev)); -+ -+ // Functions -+ common->gpio_chip.label = MAX96717_NAME; -+ common->gpio_chip.parent = dev; -+ common->gpio_chip.get_direction = max96717_gpio_get_direction; -+ common->gpio_chip.direction_input = max96717_gpio_direction_input; -+ common->gpio_chip.direction_output = max96717_gpio_direction_output; -+ common->gpio_chip.get = max96717_gpio_get; -+ common->gpio_chip.set = max96717_gpio_set; -+ common->gpio_chip.ngpio = MAX96717_NUM_GPIO; -+ common->gpio_chip.can_sleep = 1; -+ common->gpio_chip.base = -1; -+ if (gpio_pdata && gpio_pdata->names) -+ common->gpio_chip.names = gpio_pdata->names; -+ -+ ret = devm_gpiochip_add_data(dev, &common->gpio_chip, common); -+ if (ret < 0) { -+ dev_err(dev, "gpio_init: Failed to add max96717_gpio\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, -+ unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, csi %d: %s, %d lanes", \ -+ pipe_id, csi_id, (enable ? "enable" : "disable"), \ -+ common->csi_link[csi_id].config.num_lanes); -+ -+ // Select number of lanes for CSI port csi_id -+ ret = regmap_update_bits(map, MAX96717_MIPI_RX_1, -+ MAX96717_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX96717_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ common->csi_link[csi_id].config.num_lanes - 1)); -+ if (ret) -+ return ret; -+ -+ // Select CSI port csi_id for video pipe pipe_id -+ ret = regmap_update_bits(map, MAX96717_FRONTTOP_0, -+ MAX96717_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) -+ | MAX96717_FRONTTOP_0_START_CSI_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX96717_FRONTTOP_0_SEL_CSI_FIELD(pipe_id), csi_id) -+ | MAX9X_FIELD_PREP(MAX96717_FRONTTOP_0_START_CSI_FIELD(csi_id), enable ? 1U : 0U)); -+ if (ret) -+ return ret; -+ -+ // Start video pipe pipe_id from CSI port csi_id -+ ret = regmap_update_bits(map, MAX96717_FRONTTOP_9, -+ MAX96717_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), -+ MAX9X_FIELD_PREP(MAX96717_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), enable ? 1U : 0U)); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+/** -+ * max96717_video_pipe_double_pixel() - Configure Double Loading Mode on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.1 "Double Mode (Serializer)" -+ */ -+static int max96717_video_pipe_double_pixel(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ unsigned int reg; -+ unsigned int fields; -+ unsigned int vals; -+ unsigned int dbl_bpp; -+ -+ // Clear all the double pixel mode fields -+ ret = regmap_update_bits(map, -+ MAX96717_FRONTTOP_10, -+ MAX96717_FRONTTOP_10_DBL8_FIELD(pipe_id), -+ 0x0); -+ if (ret) -+ return ret; -+ -+ ret = regmap_update_bits(map, -+ MAX96717_FRONTTOP_11, -+ MAX96717_FRONTTOP_11_DBL10_FIELD(pipe_id) | -+ MAX96717_FRONTTOP_11_DBL12_FIELD(pipe_id), -+ 0x0); -+ if (ret) -+ return ret; -+ -+ // Enable/disable double pixel mode for pipe -+ switch (bpp) { -+ case 0: -+ //bpp not used on this pipe, but still valid input -+ break; -+ case 8: -+ reg = MAX96717_FRONTTOP_10; -+ fields = MAX96717_FRONTTOP_10_DBL8_FIELD(pipe_id); -+ break; -+ case 10: -+ reg = MAX96717_FRONTTOP_11; -+ fields = MAX96717_FRONTTOP_11_DBL10_FIELD(pipe_id); -+ break; -+ case 12: -+ reg = MAX96717_FRONTTOP_11; -+ fields = MAX96717_FRONTTOP_11_DBL12_FIELD(pipe_id); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable pixel doubling for specified pipe -+ if (bpp != 0) { -+ dev_info(dev, "Configure double loading mode for pipe %u (%ubpp -> %ubpp)", -+ pipe_id, bpp, (bpp * 2)); -+ ret = regmap_update_bits(map, reg, fields, 0xFF); -+ if (ret) -+ return ret; -+ } -+ -+ // Enable software BPP override and set BPP value -+ dbl_bpp = bpp * 2; -+ -+ // Override output bpp -+ reg = MAX96717_FRONTTOP_2X(pipe_id); -+ -+ fields = MAX96717_FRONTTOP_2X_BPP_EN_FIELD; -+ vals = MAX9X_FIELD_PREP(MAX96717_FRONTTOP_2X_BPP_EN_FIELD, bpp == 0 ? 0U : 1U); -+ -+ fields |= MAX96717_FRONTTOP_2X_BPP_FIELD; -+ vals |= MAX9X_FIELD_PREP(MAX96717_FRONTTOP_2X_BPP_FIELD, dbl_bpp); -+ -+ return regmap_update_bits(map, reg, fields, vals); -+} -+ -+static int max96717_max_elements(struct max9x_common *common, -+ enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX96717_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX96717_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX96717_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX96717_NUM_CSI_LINKS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static int max96717_enable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_info(dev, "Link %d: Enable", link_id); -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ ret = max96717_set_pipe_csi_enabled(common, pipe_id, -+ config->src_csi, true); -+ if (ret) -+ return ret; -+ ret = max96717_video_pipe_double_pixel(common, pipe_id, -+ config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_disable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_info(dev, "Link %d: Disable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ ret = max96717_set_pipe_csi_enabled(common, pipe_id, -+ config->src_csi, false); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "Disable"); -+ -+ return 0; -+} -+ -+static int max96717_pixel_mode(struct max9x_common *common, bool pixel) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Pixel mode: %d", pixel); -+ -+ // Register actually enables tunnel mode. Clearing the bit "enables" pixel mode. -+ return regmap_write(map, MAX96717_EXT11, -+ MAX9X_FIELD_PREP(MAX96717_TUNNEL_MODE, !pixel)); -+} -+ -+/** -+ * Enable the MAX96717 to replicate a frame sync signal from the deserializer. -+ * NOTE: Currently the MAX96717 driver supports frame sync across its -+ * GPIO8. -+ */ -+static int max96717_enable_frame_sync(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ int ret; -+ int deserializer_tx_id; -+ -+ ret = of_property_read_u32(node, "fsync-tx-id", &deserializer_tx_id); -+ // Not necessarily problematic, no frame sync tx found -+ if (ret == -ENODATA || ret == -EINVAL) { -+ dev_info(dev, "Frame sync GPIO tx id not found"); -+ return 0; -+ } -+ // Other errors are problematic -+ else if (ret < 0) { -+ dev_err(dev, "Failed to read frame sync tx id with err %d", -+ ret); -+ return ret; -+ } -+ -+ ret = regmap_write(map, MAX96717_GPIO_C(8), deserializer_tx_id); -+ if (ret) { -+ dev_err(dev, "Failed to write des frame sync id with err: %d", -+ ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_get_datatype(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret, datatype; -+ -+ ret = of_property_read_u32(node, "data-type", &datatype); -+ if (ret == -ENODATA || ret == -EINVAL) { -+ dev_dbg(dev, "Data-type not found not filtering"); -+ return regmap_write(map, MAX96717_FRONTTOP_16, 0); -+ } -+ // Other errors are problematic -+ else if (ret < 0) { -+ dev_err(dev, "Problem reading in data-type with err %d", ret); -+ return ret; -+ } -+ -+ dev_dbg(dev, "Setting image data type to %x", datatype); -+ /* Filter out metadata, only use the image datatype. -+ * MAX96717_FRONTTOP_16: mem_dt1_selz. -+ * Bit 6 is enable -+ */ -+ return regmap_write(map, MAX96717_FRONTTOP_16, -+ (datatype & MAX96717_FRONTTOP_16_FIELD) | MAX96717_FRONTTOP_16_ENABLE); -+} -+ -+static int max96717_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "setup gpio"); -+ ret = max96717_setup_gpio(common); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "setup datatype"); -+ ret = max96717_get_datatype(common); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "setup pixel mode"); -+ // this driver MUST be in pixel mode as that is what our driver architecture supports -+ ret = max96717_pixel_mode(common, true); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "setup frame sync"); -+ ret = max96717_enable_frame_sync(common); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+int max96717_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ *common_ops = &max96717_common_ops; -+ *serial_ops = &max96717_serial_link_ops; -+ *csi_ops = NULL; -+ *lf_ops = NULL; -+ *trans_ops = &max96717_translation_ops; -+ return 0; -+} -+ -+#if 0 -+static int max96717_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *ser = NULL; -+ int ret = 0; -+ -+ dev_info(dev, "Probing"); -+ -+ ser = devm_kzalloc(dev, sizeof(*ser), GFP_KERNEL); -+ if (!ser) { -+ dev_err(dev, "Failed to allocate memory."); -+ return -ENOMEM; -+ } -+ -+ ret = max9x_common_init_i2c_client( -+ ser, -+ client, -+ &max96717_regmap_config, -+ &max96717_common_ops, -+ &max96717_serial_link_ops, -+ NULL, /* csi_link_os */ -+ NULL); -+ if (ret) -+ return ret; -+ -+ return 0; -+ -+err: -+ max9x_destroy(ser); -+ return ret; -+} -+ -+static void max96717_remove(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *ser = NULL; -+ -+ dev_info(dev, "Removing"); -+ -+ ser = max9x_client_to_common(client); -+ -+ max9x_destroy(ser); -+ -+} -+ -+static struct i2c_device_id max96717_idtable[] = { -+ {MAX96717_NAME, 0}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(i2c, max96717_idtable); -+ -+static struct of_device_id max96717_of_match[] = { -+ { .compatible = MAX96717_NAME}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, max96717_of_match); -+ -+static struct i2c_driver max96717_driver = { -+ .driver = { -+ .name = MAX96717_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(max96717_of_match), -+ }, -+ .probe = max96717_probe, -+ .remove = max96717_remove, -+ .id_table = max96717_idtable, -+}; -+ -+module_i2c_driver(max96717_driver); -+#endif -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Cody Burrows "); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("He Jiabin "); -+MODULE_DESCRIPTION("Maxim MAX96717 CSI-2 to GMSL2 Serializer driver"); -diff --git a/drivers/media/i2c/max9x/max96717.h b/drivers/media/i2c/max9x/max96717.h -new file mode 100644 -index 000000000000..022340a7eba9 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96717.h -@@ -0,0 +1,90 @@ -+/* -+ * max96717.h - Maxim MAX96717 registers and constants. -+ * -+ * Copyright (c) 2022, D3 Engineering. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX96717_H_ -+#define _MAX96717_H_ -+ -+#include -+#include "serdes.h" -+ -+#define MAX96717_NAME "max96717" -+ -+enum max96717_gpio_out_type { -+ MAX96717_GPIO_OUT_TYPE_OPEN_DRAIN = 0, -+ MAX96717_GPIO_OUT_TYPE_PUSH_PULL = 1, -+}; -+ -+enum max96717_gpio_pull_updn_sel { -+ MAX96717_GPIO_PULL_UPDN_SEL_NONE = 0, -+ MAX96717_GPIO_PULL_UPDN_SEL_UP = 1, -+ MAX96717_GPIO_PULL_UPDN_SEL_DOWN = 2, -+}; -+ -+#define MAX96717_NUM_SERIAL_LINKS 1 -+#define MAX96717_NUM_VIDEO_PIPES 1 -+#define MAX96717_NUM_MIPI_MAPS 1 -+#define MAX96717_NUM_CSI_LINKS 1 -+#define MAX96717_NUM_GPIO 11 -+ -+#define MAX96717_GPIO(gpio) (0x2BE + ((gpio) * 3)) -+#define MAX96717_GPIO_A(gpio) (MAX96717_GPIO(gpio) + 0) -+#define MAX96717_GPIO_A_OUT_DIS_FIELD BIT(0) -+#define MAX96717_GPIO_A_TX_EN_FIELD BIT(1) -+#define MAX96717_GPIO_A_RX_EN_FIELD BIT(2) -+#define MAX96717_GPIO_A_IN_FIELD BIT(3) -+#define MAX96717_GPIO_A_OUT_FIELD BIT(4) -+#define MAX96717_GPIO_A_RES_CFG_FIELD BIT(7) -+#define MAX96717_GPIO_B(gpio) (MAX96717_GPIO(gpio) + 1) -+#define MAX96717_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX96717_GPIO_B_OUT_TYPE_FIELD BIT(5) -+#define MAX96717_GPIO_B_PULL_UPDN_SEL_FIELD GENMASK(7, 6) -+#define MAX96717_GPIO_C(gpio) (MAX96717_GPIO(gpio) + 2) -+#define MAX96717_GPIO_C_RX_ID GENMASK(4, 0) -+ -+/* -+ * Some macros refer to a pipe despite not using it -+ * this is to make code easier to port between drivers -+ * in spite of the max96717 only having 1 video pipe -+ */ -+#define MAX96717_FRONTTOP_0 (0x308) -+#define MAX96717_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) BIT(pipe_id + 2) -+#define MAX96717_FRONTTOP_0_START_CSI_FIELD(csi_id) BIT((csi_id) + 5) -+#define MAX96717_FRONTTOP_9 (0x311) -+#define MAX96717_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id) BIT((pipe_id + 2) + 4 * (csi_id + 1)) -+#define MAX96717_FRONTTOP_10 (0x312) -+#define MAX96717_FRONTTOP_10_DBL8_FIELD(pipe_id) BIT(2) -+#define MAX96717_FRONTTOP_11 (0x313) -+#define MAX96717_FRONTTOP_11_DBL10_FIELD(pipe_id) BIT(2) -+#define MAX96717_FRONTTOP_11_DBL12_FIELD(pipe_id) BIT(6) -+#define MAX96717_FRONTTOP_16 (0x318) -+#define MAX96717_FRONTTOP_16_FIELD GENMASK(5, 0) -+#define MAX96717_FRONTTOP_16_ENABLE BIT(6) -+#define MAX96717_FRONTTOP_2X(pipe_id) (0x31E) -+#define MAX96717_FRONTTOP_2X_BPP_EN_FIELD BIT(5) -+#define MAX96717_FRONTTOP_2X_BPP_FIELD GENMASK(4, 0) -+ -+#define MAX96717_MIPI_RX (0x330) -+#define MAX96717_MIPI_RX_1 (MAX96717_MIPI_RX + 1) -+#define MAX96717_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id) (GENMASK(1, 0) << ((csi_id + 1) * 4)) -+ -+#define MAX96717_EXT11 (0x383) -+#define MAX96717_TUNNEL_MODE BIT(7) -+ -+#endif /* _MAX96717_H_ */ -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -new file mode 100644 -index 000000000000..1c1059c0c9de ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -0,0 +1,1169 @@ -+/* -+ * max96724.c - Maxim MAX96724 GMSL2/GMSL1 to CSI-2 Deserializer -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max96724.h" -+ -+// Params -+int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -+module_param(max96724_serial_link_timeout_ms, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max96724_serial_link_timeout_ms, "Timeout for serial link in milliseconds"); -+ -+// Declarations -+static int max96724_set_phy_mode(struct max9x_common *common, unsigned int phy_mode); -+static int max96724_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max96724_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map); -+static int max96724_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz); -+static int max96724_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes); -+static int max96724_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max96724_configure_csi_dphy(struct max9x_common *common); -+static int max96724_enable(struct max9x_common *common); -+static int max96724_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max96724_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked); -+static int max96724_set_serial_link_state(struct max9x_common *common, unsigned int link_id, bool enable); -+static int max96724_serial_link_reset(struct max9x_common *common, unsigned int link_id); -+static int max96724_set_serial_link_rate(struct max9x_common *common, unsigned int link_id); -+static int max96724_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe); -+static int max96724_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps); -+static int max96724_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, unsigned int map_id, -+ struct max9x_serdes_mipi_map *mipi_map); -+static int max96724_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max96724_csi_double_pixel(struct max9x_common *common, unsigned int csi_id, unsigned int bpp); -+static int max96724_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max96724_set_serial_link_routing(struct max9x_common *common, unsigned int link_id); -+static int max96724_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max96724_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max96724_set_remote_control_channel_enabled(struct max9x_common *common, unsigned int link_id, bool enabled); -+static int max96724_select_serial_link(struct max9x_common *common, unsigned int link); -+static int max96724_deselect_serial_link(struct max9x_common *common, unsigned int link); -+static int max96724_enable_native_frame_sync(struct max9x_common *common); -+static int max96724_enable_gpio_frame_sync(struct max9x_common *common); -+static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line); -+static int max96724_enable_line_fault(struct max9x_common *common, unsigned int line); -+static int max96724_set_line_fault(struct max9x_common *common, unsigned int line, bool enable); -+static int max96724_get_line_fault(struct max9x_common *common, unsigned int line); -+ -+// Functions -+static int max96724_set_phy_mode(struct max9x_common *common, unsigned int phy_mode) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI: phy_mode=%d", phy_mode); -+ -+ return regmap_update_bits(map, MAX96724_MIPI_PHY0, -+ MAX96724_MIPI_PHY0_MODE_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, phy_mode)); -+} -+ -+static int max96724_set_phy_enabled(struct max9x_common *common, -+ unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "CSI link %d: %s", csi_id, (enable ? "enable" : "disable")); -+ -+ ret = regmap_update_bits(map, MAX96724_MIPI_PHY_ENABLE, -+ MAX96724_MIPI_PHY_ENABLE_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(csi_id), enable ? 1U : 0U)); -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_phy_lane_map(struct max9x_common *common, -+ unsigned int csi_id, unsigned int phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: phy_lane_map=0x%04x", csi_id, phy_lane_map); -+ -+ return regmap_update_bits(map, MAX96724_MIPI_PHY_LANE_MAP(csi_id), -+ MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi_id, 0) -+ | MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi_id, 1), -+ phy_lane_map); -+} -+ -+static int max96724_set_phy_dpll_freq(struct max9x_common *common, -+ unsigned int csi_id, unsigned int freq_mhz) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: freq %u MHz, %u mult", csi_id, freq_mhz, -+ freq_mhz / MAX96724_DPLL_FREQ_MHZ_MULTIPLE); -+ -+ return regmap_update_bits(map, MAX96724_DPLL_FREQ(csi_id), -+ MAX96724_DPLL_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, freq_mhz / MAX96724_DPLL_FREQ_MHZ_MULTIPLE)); -+} -+ -+static int max96724_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val; -+ unsigned int width; -+ -+ dev_dbg(dev, "CSI link %d: Initial deskew %s", csi_id, enable ? "enabled" : "disabled"); -+ -+ val = MAX9X_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_EN, enable); -+ -+ if (enable) { -+ width = common->csi_link[csi_id].config.initial_deskew_width; -+ dev_dbg(dev, "Initial deskew width: 0x%03x", width); -+ -+ if (width > MAX96724_INIT_DESKEW_WIDTH_MAX) { -+ dev_err(dev, "Unsupported initial deskew width!"); -+ return -EINVAL; -+ } -+ -+ val |= MAX9X_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD, width); -+ } -+ -+ return regmap_write(map, MAX96724_MIPI_TX_DESKEW_INIT(csi_id), val); -+} -+ -+static int max96724_set_mipi_lane_cnt(struct max9x_common *common, -+ unsigned int csi_id, int num_lanes) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "CSI link %d: %d lanes", csi_id, num_lanes); -+ -+ ret = regmap_update_bits(map, MAX96724_MIPI_TX_LANE_CNT(csi_id), -+ MAX96724_MIPI_TX_VCX_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS)); -+ -+ if (ret) { -+ dev_err(dev, "Failed to configure virtual channel extension!"); -+ return ret; -+ } -+ -+ ret = regmap_update_bits( -+ map, MAX96724_MIPI_TX_LANE_CNT(csi_id), -+ MAX96724_MIPI_TX_CPHY_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, -+ common->csi_link[csi_id].config.bus_type == -+ V4L2_MBUS_CSI2_CPHY ? -+ 1 : -+ 0)); -+ -+ if (ret) { -+ dev_err(dev, "Failed to enable CSI2 %d to CPHY mode!", csi_id); -+ return ret; -+ } -+ -+ if (num_lanes > 0) { -+ ret = regmap_update_bits( -+ map, MAX96724_MIPI_TX_LANE_CNT(csi_id), -+ MAX96724_MIPI_TX_LANE_CNT_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_TX_LANE_CNT_FIELD, -+ (num_lanes - 1))); -+ -+ if (ret) { -+ dev_err(dev, -+ " Failed to set CSI2 controller %d with %d lanes !", -+ csi_id, num_lanes); -+ return ret; -+ } -+ } -+ -+ return ret; -+} -+ -+static int max96724_configure_csi_dphy(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phy_mode; -+ unsigned int phy_lane_map[MAX96724_NUM_CSI_LINKS]; -+ unsigned int csi_id; -+ int ret; -+ -+ for (csi_id = 0; csi_id < MAX96724_NUM_CSI_LINKS; csi_id++) { -+ dev_dbg(dev, "CSI link %d: enabled=%d, num_lanes=%d, freq_mhz=%d, init_deskew=%d", -+ csi_id, -+ common->csi_link[csi_id].enabled, -+ common->csi_link[csi_id].config.num_lanes, -+ common->csi_link[csi_id].config.freq_mhz, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ } -+ -+ //TODO: Allow DT to override lane mapping? -+ -+ // Determine correct phy_mode and associate lane mapping -+ if (common->csi_link[0].config.num_lanes <= 2 -+ && common->csi_link[1].config.num_lanes <= 2 -+ && common->csi_link[2].config.num_lanes <= 2 -+ && common->csi_link[3].config.num_lanes <= 2) { -+ -+ phy_mode = MAX96724_MIPI_PHY_4X2; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 1); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 1); -+ -+ } else if (common->csi_link[0].config.num_lanes == 0 -+ && common->csi_link[1].config.num_lanes >= 3 -+ && common->csi_link[2].config.num_lanes >= 3 -+ && common->csi_link[3].config.num_lanes == 0) { -+ -+ phy_mode = MAX96724_MIPI_PHY_2X4; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 3); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 3); -+ -+ } else if (common->csi_link[0].config.num_lanes == 0 -+ && common->csi_link[1].config.num_lanes >= 3 -+ && common->csi_link[2].config.num_lanes <= 2 -+ && common->csi_link[3].config.num_lanes <= 2) { -+ -+ phy_mode = MAX96724_MIPI_PHY_1X4A_22; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 3); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 1); -+ -+ } else if (common->csi_link[0].config.num_lanes <= 2 -+ && common->csi_link[1].config.num_lanes <= 2 -+ && common->csi_link[2].config.num_lanes >= 3 -+ && common->csi_link[3].config.num_lanes == 0) { -+ -+ phy_mode = MAX96724_MIPI_PHY_1X4B_22; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 1); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 3); -+ -+ } else { -+ dev_err(dev, "Invalid CSI configuration! Supported modes: 4x2, 2x4, 1x4+2x2, 2x2+1x4"); -+ return -EINVAL; -+ } -+ -+ ret = max96724_set_phy_mode(common, phy_mode); -+ if (ret) -+ return ret; -+ -+ for (csi_id = 0; csi_id < MAX96724_NUM_CSI_LINKS; csi_id++) { -+ struct max9x_serdes_csi_config *config = &common->csi_link[csi_id].config; -+ -+ ret = max96724_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_phy_lane_map(common, csi_id, phy_lane_map[csi_id]); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_mipi_lane_cnt(common, csi_id, -+ common->csi_link[csi_id].config.num_lanes); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_initial_deskew(common, csi_id, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ if (ret) -+ return ret; -+ -+ if (WARN_ONCE(config->freq_mhz > 0 && config->freq_mhz < MAX96724_DPLL_FREQ_MHZ_MULTIPLE, -+ "CSI frequency must be greater than %d MHz", MAX96724_DPLL_FREQ_MHZ_MULTIPLE)) -+ return -EINVAL; -+ -+ ret = max96724_set_phy_dpll_freq(common, csi_id, config->freq_mhz); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96724_set_all_reset(struct max9x_common *common, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX96724_RESET_ALL, -+ MAX96724_RESET_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max96724_soft_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "Soft reset"); -+ -+ ret = max96724_set_all_reset(common, 1); -+ if (ret) -+ return ret; -+ -+ /* Wait for hardware available after soft reset */ -+ /* TODO: Optimize sleep time 45 ms */ -+ msleep(45); -+ -+ ret = max96724_set_all_reset(common, 0); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max96724_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX96724_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX96724_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX96724_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX96724_NUM_CSI_LINKS; -+ case MAX9X_LINE_FAULT: -+ return MAX96724_NUM_LINE_FAULTS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static struct max9x_common_ops max96724_common_ops = { -+ .enable = max96724_enable, -+ .soft_reset = max96724_soft_reset, -+ .max_elements = max96724_max_elements, -+}; -+ -+static int max96724_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(map, MAX96724_PHY_LOCKED(link_id), &val); -+ if (ret) { -+ dev_err(dev, "failed to read link lock with err %d", ret); -+ return ret; -+ } -+ -+ if (FIELD_GET(MAX96724_PHY_LOCKED_FIELD, val) != 0) -+ *locked = true; -+ else -+ *locked = false; -+ -+ return 0; -+} -+ -+static int max96724_set_serial_link_state(struct max9x_common *common, unsigned int link_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ enum max9x_serdes_link_type link_type = common->serial_link[link_id].config.link_type; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Serial-link %d: %s", link_id, (enable ? "up" : "down")); -+ -+ ret = regmap_update_bits(map, MAX96724_LINK_CTRL, -+ MAX96724_LINK_CTRL_EN_FIELD(link_id) -+ | MAX96724_LINK_CTRL_GMSL_FIELD(link_id), -+ MAX9X_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(link_id), enable ? 1U : 0U) -+ | MAX9X_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(link_id), link_type == MAX9X_LINK_TYPE_GMSL2 ? 1U : 0U)); -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_serial_link_reset(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Serial-link %d reset", link_id); -+ -+ ret = regmap_update_bits(map, MAX96724_RESET_CTRL, -+ MAX96724_RESET_CTRL_FIELD(link_id), -+ MAX9X_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(link_id), 1U)); -+ mutex_unlock(lock); -+ -+ return ret; -+} -+ -+static int max96724_set_serial_link_rate(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -+ unsigned int tx_rate, rx_rate; -+ -+ tx_rate = max9x_serdes_mhz_to_rate(max96724_tx_rates, ARRAY_SIZE(max96724_tx_rates), config->tx_freq_mhz); -+ if (tx_rate < 0) -+ return tx_rate; -+ -+ rx_rate = max9x_serdes_mhz_to_rate(max96724_rx_rates, ARRAY_SIZE(max96724_rx_rates), config->rx_freq_mhz); -+ if (rx_rate < 0) -+ return rx_rate; -+ -+ dev_dbg(dev, "Serial-link %d: TX=%d MHz RX=%d MHz", link_id, config->tx_freq_mhz, config->rx_freq_mhz); -+ -+ return regmap_update_bits(map, MAX96724_PHY_RATE_CTRL(link_id), -+ MAX96724_PHY_RATE_CTRL_TX_FIELD(link_id) -+ | MAX96724_PHY_RATE_CTRL_RX_FIELD(link_id), -+ MAX9X_FIELD_PREP(MAX96724_PHY_RATE_CTRL_TX_FIELD(link_id), tx_rate) -+ | MAX9X_FIELD_PREP(MAX96724_PHY_RATE_CTRL_RX_FIELD(link_id), rx_rate)); -+} -+ -+static int max96724_set_video_pipe_src(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int link_id, unsigned int src_pipe) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Video-pipe %d: src_link=%u, src_pipe=%u", pipe_id, link_id, src_pipe); -+ -+ return regmap_update_bits(map, MAX96724_VIDEO_PIPE_SEL(pipe_id), -+ MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id) -+ | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id), link_id) -+ | MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), src_pipe)); -+} -+ -+static int max96724_set_video_pipe_maps_enabled(struct max9x_common *common, -+ unsigned int pipe_id, int num_maps) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val = 0; -+ int ret = 0; -+ struct mutex *lock = &common->link_mutex; -+ -+ if (num_maps > 0) -+ val = GENMASK(num_maps - 1, 0); -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Video-pipe %d: num_maps=%u", pipe_id, num_maps); -+ -+ ret = regmap_write(map, MAX96724_MAP_EN_L(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_EN_FIELD, val)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_write(map, MAX96724_MAP_EN_H(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_EN_FIELD, val >> 8)); -+ if (ret) -+ goto unlock_exit; -+ -+unlock_exit: -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_video_pipe_map(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int map_id, -+ struct max9x_serdes_mipi_map *mipi_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret = 0; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Video-pipe %d, map %d: VC%d:DT%02x->VC%d:DT%02x, dst_csi=%d ", -+ pipe_id, map_id, -+ mipi_map->src_vc, -+ mipi_map->src_dt, -+ mipi_map->dst_vc, -+ mipi_map->dst_dt, -+ mipi_map->dst_csi); -+ -+ ret = regmap_write(map, MAX96724_MAP_SRC_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_SRC_L_VC_FIELD, mipi_map->src_vc) -+ | MAX9X_FIELD_PREP(MAX96724_MAP_SRC_L_DT_FIELD, mipi_map->src_dt)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_write(map, MAX96724_MAP_DST_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_DST_L_VC_FIELD, mipi_map->dst_vc) -+ | MAX9X_FIELD_PREP(MAX96724_MAP_DST_L_DT_FIELD, mipi_map->dst_dt)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_write(map, MAX96724_MAP_SRCDST_H(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_SRCDST_H_SRC_VC_FIELD, mipi_map->src_vc) -+ | MAX9X_FIELD_PREP(MAX96724_MAP_SRCDST_H_DST_VC_FIELD, mipi_map->dst_vc)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_update_bits(map, MAX96724_MAP_DPHY_DEST(pipe_id, map_id), -+ MAX96724_MAP_DPHY_DEST_FIELD(map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_DPHY_DEST_FIELD(map_id), mipi_map->dst_csi)); -+ if (ret) -+ goto unlock_exit; -+ -+unlock_exit: -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_csi_link_enabled(struct max9x_common *common, -+ unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct max9x_serdes_csi_link *csi_link; -+ int ret; -+ -+ if (csi_id > common->num_csi_links) -+ return -EINVAL; -+ -+ csi_link = &common->csi_link[csi_id]; -+ -+ if (WARN_ONCE(enable && csi_link->enabled == false, -+ "Tried to enable a disabled CSI port???")) -+ return -EINVAL; -+ -+ if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, -+ "Tried to enable CSI port with no lanes???")) -+ return -EINVAL; -+ -+ // Keep track of number of enabled maps using this CSI link -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+ dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, -+ (enable ? "enable" : "disable"), csi_link->usecount); -+ -+ if (enable && csi_link->usecount == 1) { -+ // Enable && first user -+ -+ ret = max96724_set_phy_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ } else if (!enable && csi_link->usecount == 0) { -+ // Disable && no more users -+ -+ ret = max96724_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ } -+ -+ return 0; -+} -+ -+static int max96724_csi_double_pixel(struct max9x_common *common, -+ unsigned int csi_id, -+ unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int value; -+ int ret; -+ -+ dev_info(dev, "CSI ALT Mem mapping for %u bpp on csi %i", bpp, csi_id); -+ -+ switch (bpp) { -+ case 0: -+ value = 0; -+ break; -+ case 8: -+ value = FIELD_PREP(MAX96724_MIPI_TX_ALT_MEM_8BPP, 1U); -+ dev_err(dev, "8 BPP currently unsupported for pixel doubling"); -+ return -EINVAL; -+ case 10: -+ value = FIELD_PREP(MAX96724_MIPI_TX_ALT_MEM_10BPP, 1U); -+ break; -+ case 12: -+ value = FIELD_PREP(MAX96724_MIPI_TX_ALT_MEM_12BPP, 1U); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable alt mem mapping -+ ret = regmap_update_bits( -+ map, MAX96724_MIPI_TX_ALT_MEM(csi_id), -+ MAX96724_MIPI_TX_ALT_MEM_FIELD, value); -+ -+ return ret; -+ -+} -+ -+static int max96724_set_video_pipe_enabled(struct max9x_common *common, -+ unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret = 0; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Video-pipe %d: %s", pipe_id, (enable ? "enable" : "disable")); -+ -+ // Enable max96712 "legacy" mode -+ // Non "legacy" mode ignores pipe mapping, and selects all streams for pipe -+ // 0. The Jetson doesn't know what to do with that and throws spurious data -+ // stream errors. -+ ret = regmap_update_bits(map, MAX96724_VIDEO_PIPE_EN, -+ MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, -+ MAX96724_VIDEO_PIPE_LEGACY_MODE)); -+ if (ret) { -+ dev_err(dev, "Failed to clear select all streams bit"); -+ goto unlock_exit; -+ } -+ -+ ret = regmap_update_bits(map, MAX96724_VIDEO_PIPE_EN, -+ MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), enable ? 1U : 0U)); -+ -+unlock_exit: -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_serial_link_routing(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max96724_set_video_pipe_src(common, pipe_id, -+ config->src_link, -+ config->src_pipe); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_video_pipe_maps_enabled(common, pipe_id, -+ config->num_maps); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max96724_set_video_pipe_map(common, pipe_id, map_id, -+ &config->map[map_id]); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_csi_link_enabled(common, -+ config->map[map_id].dst_csi, -+ true); -+ if (ret) -+ return ret; -+ -+ ret = max96724_csi_double_pixel(common, -+ config->map[map_id].dst_csi, -+ config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ } -+ -+ ret = max96724_set_video_pipe_enabled(common, pipe_id, true); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96724_disable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max96724_set_video_pipe_enabled(common, pipe_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_video_pipe_maps_enabled(common, pipe_id, 0); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max96724_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ /* TODO: if disabling serial link, serializer can't perform i2c communication. */ -+ // ret = max96724_set_serial_link_state(common, link_id, false); -+ // if (ret) -+ // return ret; -+ -+ return 0; -+} -+ -+static int max96724_enable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ bool locked; -+ unsigned long timeout; -+ -+ if (WARN_ON_ONCE(link_id >= common->num_serial_links)) -+ return -EINVAL; -+ -+ if (WARN_ONCE(common->serial_link[link_id].config.link_type != MAX9X_LINK_TYPE_GMSL2, -+ "Only GMSL2 is supported!")) -+ return -EINVAL; -+ -+ // GMSL2 -+ ret = max96724_set_remote_control_channel_enabled(common, link_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_serial_link_state(common, link_id, true); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_serial_link_rate(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max96724_serial_link_reset(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_serial_link_routing(common, link_id); -+ if (ret) -+ return ret; -+ -+ timeout = jiffies + msecs_to_jiffies(max96724_serial_link_timeout_ms); -+ do { -+ usleep_range(10 * 1000, 25 * 1000); // 10 to 25 ms -+ ret = max96724_get_serial_link_lock(common, link_id, &locked); -+ if (ret) -+ return ret; -+ } while (!locked && time_before(jiffies, timeout)); -+ -+ if (!locked) { -+ dev_err(dev, "Serial-link %d: Failed to lock!", link_id); -+ return -ETIMEDOUT; -+ } -+ -+ return 0; -+} -+ -+static int max96724_set_remote_control_channel_enabled(struct max9x_common *common, -+ unsigned int link_id, -+ bool enabled) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ //TODO: Allow DT to choose which port gets enabled: bit 0 disables port 0, bit 1 disables port 1 -+ //See also 0x0E REG14 DIS_REM_CC_P2[3:0] -+ -+ // Note that the register value 1 *disables* port-to-remote command & control -+ mutex_lock(lock); -+ dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); -+ if (enabled) { -+ ret = regmap_write(map, MAX96724_REM_CC, -+ ~(MAX9X_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(link_id, 0), enabled ? 1 : 0))); -+ } else { -+ ret = regmap_write(map, MAX96724_REM_CC, ~(0)); -+ } -+ -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_select_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max96724_set_remote_control_channel_enabled(common, link, true); -+} -+ -+static int max96724_deselect_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max96724_set_remote_control_channel_enabled(common, link, false); -+} -+ -+static struct max9x_serial_link_ops max96724_serial_link_ops = { -+ .enable = max96724_enable_serial_link, -+ .disable = max96724_disable_serial_link, -+ .select = max96724_select_serial_link, -+ .deselect = max96724_deselect_serial_link, -+ .get_locked = max96724_get_serial_link_lock, -+}; -+ -+static int max96724_enable_native_frame_sync(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret, i; -+ unsigned int val; -+ enum max96724_fsync_pin pin; -+ unsigned int fsync_freq; -+ unsigned int pclk_freq; -+ unsigned int fsync_period; -+ unsigned int fsync_tx_id; -+ bool fsync_master; -+ -+ if (!of_property_read_bool(node, "frame-sync-enable")) { -+ dev_info(dev, "Native frame sync not enabled"); -+ return regmap_write(map, MAX96724_FSYNC_0, -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -+ MAX96724_FSYNC_GEN_OFF_GPIO_OFF)); -+ } -+ -+ fsync_master = of_property_read_bool(node, "frame-sync-master"); -+ if (fsync_master) -+ dev_dbg(dev, "Frame sync master mode"); -+ else -+ dev_dbg(dev, "Frame sync slave mode"); -+ -+ ret = of_property_read_u32(node, "frame-sync-pin", &val); -+ if (ret) { -+ dev_err(dev, "Missing property: frame-sync-pin"); -+ return ret; -+ } -+ -+ // check value of pin -+ switch (val) { -+ case MAX96724_FSYNC_PIN_MFP0: -+ case MAX96724_FSYNC_PIN_MFP7: -+ pin = val; -+ break; -+ -+ default: -+ dev_err(dev, "Invalid frame-sync-pin"); -+ return -EINVAL; -+ }; -+ -+ ret = of_property_read_u32(node, "frame-sync-tx-id", &val); -+ if (ret) { -+ dev_err(dev, "Missing property: frame-sync-tx-id"); -+ return -EINVAL; -+ } -+ -+ // check value of frame-sync-tx-id -+ fsync_tx_id = val & 0x1F; -+ if (fsync_tx_id != val) -+ dev_warn(dev, "Truncated frame-sync-tx-id to 5 bits!"); -+ -+ ret = of_property_read_u32(node, "pclk-freq", &pclk_freq); -+ if (ret) { -+ dev_err(dev, "Missing property: pclk-freq"); -+ return -EINVAL; -+ } -+ -+ ret = of_property_read_u32(node, "frame-sync-freq", &fsync_freq); -+ if (ret) { -+ dev_err(dev, "Missing property: frame-sync-freq;"); -+ return -EINVAL; -+ } -+ -+ // Reset register to known state -+ ret = regmap_write(map, MAX96724_FSYNC_15, 0xDF); -+ if (ret) { -+ dev_dbg(dev, "Failed to reset FSYNC state"); -+ return ret; -+ } -+ -+ // Disable AUTO FS links -+ val = MAX9X_FIELD_PREP(MAX96724_FS_GPIO_TYPE_FIELD, MAX96724_FS_GPIO_TYPE_GMSL2) | -+ MAX9X_FIELD_PREP(MAX96724_FS_USE_XTAL_FIELD, true) | -+ MAX9X_FIELD_PREP(MAX96724_AUTO_FS_LINKS_FIELD, 0); -+ // Enable all FS links manually -+ for (i = 0; i < 4; ++i) -+ val |= MAX9X_FIELD_PREP(MAX96724_FS_LINK_FIELD(i), 1); -+ -+ ret = regmap_write(map, MAX96724_FSYNC_15, val); -+ if (ret) { -+ dev_dbg(dev, "Failed to write FSYNC_15"); -+ return ret; -+ } -+ -+ // Calculate value of FSYNC_PERIOD registers -+ // FSYNC_PERIOD = number of pclk cycles per fsync period -+ fsync_period = pclk_freq / fsync_freq; -+ dev_dbg(dev, "Calculated FSYNC_PERIOD: 0x%06x", fsync_period); -+ -+ for (val = MAX96724_FSYNC_5; val <= MAX96724_FSYNC_7; ++val) { -+ ret = regmap_write(map, val, (uint8_t) fsync_period); -+ if (ret) { -+ dev_err(dev, "Failed to write FSYNC_PERIOD registers to 0x%03x", val); -+ return ret; -+ } -+ -+ fsync_period = fsync_period >> 8; -+ } -+ -+ ret = regmap_write(map, MAX96724_FSYNC_17, -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_TX_ID_FIELD, fsync_tx_id) | -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_ERR_THR_FIELD, 0)); -+ if (ret) { -+ dev_err(dev, "Failed to set FSYNC_17"); -+ return ret; -+ } -+ -+ ret = regmap_write(map, MAX96724_FSYNC_0, -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_OUT_PIN_FIELD, pin) | -+ MAX9X_FIELD_PREP(MAX96724_EN_VS_GEN_FIELD, 0) | -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -+ (fsync_master ? MAX96724_FSYNC_GEN_ON_GPIO_OUTPUT : MAX96724_FSYNC_GEN_OFF_GPIO_INPUT)) | -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_METH_FIELD, MAX96724_FSYNC_METHOD_MANUAL)); -+ -+ return 0; -+} -+ -+static int max96724_enable_gpio_frame_sync(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ u32 fsync_gpios[MAX96724_NUM_GPIOS]; -+ int num_fsync_gpios; -+ int i, gpio, gpio_tx_val, ret; -+ -+ // Clean up any previous values in the event the chip was not reset -+ // or GPIO forwarding needs to be toggled off -+ dev_dbg(dev, "Setting GPIO registers to default value"); -+ for (i = 0; i < MAX96724_NUM_GPIOS; i++) { -+ // Default values per the datasheet -+ TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), (BIT(7) | BIT(0)))); -+ -+ // Link A register has different fields from Links B, C, D -+ TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), (BIT(7) | BIT(5) | i))); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), i)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), i)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), i)); -+ } -+ -+ // Read DT to find fsync GPIOs -+ ret = of_property_read_variable_u32_array(node, "frame-sync-ports", -+ fsync_gpios, 0, MAX96724_NUM_GPIOS); -+ -+ if (ret == -ENODATA || ret == -EINVAL) { -+ dev_dbg(dev, "No frame sync GPIOs specified in DT"); -+ return 0; -+ } -+ -+ if (ret < 0) { -+ dev_err(dev, "Failed to parse DT frame-sync-ports, error %d", ret); -+ return ret; -+ } -+ -+ num_fsync_gpios = ret; -+ dev_info(dev, "Enabling %d frame sync GPIOs", num_fsync_gpios); -+ -+ // Configure MAX96724 to forward specified GPIOs -+ for (i = 0; i < num_fsync_gpios; i++) { -+ gpio = fsync_gpios[i]; -+ -+ if (gpio >= MAX96724_NUM_GPIOS) { -+ dev_warn(dev, "Skipping invalid GPIO %d in DT", gpio); -+ continue; -+ } -+ -+ // See: MAX96724 Users Guide "Configuring GPIO forwarding" -+ -+ // Enable GPIO for transmission -+ TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), -+ MAX96724_GPIO_RES_CFG | MAX96724_GPIO_TX_ENABLE | MAX96724_GPIO_OUTDRV_DISABLE)); -+ -+ // Configure transmission registers on Links A-D. -+ gpio_tx_val = MAX96724_GPIO_PUSH_PULL | gpio; -+ -+ TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), gpio_tx_val)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), gpio_tx_val)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), gpio_tx_val)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), gpio_tx_val)); -+ } -+ -+ return 0; -+} -+ -+static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line) -+{ -+ return max96724_set_line_fault(common, line, false); -+} -+ -+static int max96724_enable_line_fault(struct max9x_common *common, unsigned int line) -+{ -+ return max96724_set_line_fault(common, line, true); -+} -+ -+static int max96724_set_line_fault(struct max9x_common *common, unsigned int line, bool enable) -+{ -+ int ret; -+ -+ ret = regmap_update_bits(common->map, -+ MAX96724_PU_LF, -+ MAX96724_PU_LF_FIELD(line), -+ enable ? 0xFF : 0x00); -+ -+ return ret; -+} -+ -+static int max96724_get_line_fault(struct max9x_common *common, unsigned int line) -+{ -+ unsigned int val = 0; -+ int ret = regmap_read(common->map, MAX96724_LF(line), &val); -+ -+ if (ret < 0) -+ return ret; -+ -+ return MAX96724_LF_VAL(val, line); -+} -+ -+static struct max9x_line_fault_ops max96724_line_fault_ops = { -+ .enable = max96724_enable_line_fault, -+ .disable = max96724_disable_line_fault, -+ .get_status = max96724_get_line_fault, -+}; -+ -+static int max96724_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int link_id; -+ int ret; -+ -+ dev_dbg(dev, "Enable"); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ ret = max96724_disable_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ } -+ -+ // Apply errata tuning -+ ret = regmap_multi_reg_write(map, max96724_errata_rev1, ARRAY_SIZE(max96724_errata_rev1)); -+ if (ret) -+ return ret; -+ -+ ret = max96724_configure_csi_dphy(common); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max96724_enable_csi_link(struct max9x_common *common, -+ unsigned int csi_link_id) -+{ -+ return max96724_set_csi_link_enabled(common, csi_link_id, true); -+} -+ -+static int max96724_disable_csi_link(struct max9x_common *common, -+ unsigned int csi_link_id) -+{ -+ return max96724_set_csi_link_enabled(common, csi_link_id, false); -+} -+ -+static struct max9x_csi_link_ops max96724_csi_link_ops = { -+ .enable = max96724_enable_csi_link, -+ .disable = max96724_disable_csi_link, -+}; -+ -+int max96724_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ (*common_ops) = &max96724_common_ops; -+ (*serial_ops) = &max96724_serial_link_ops; -+ (*csi_ops) = &max96724_csi_link_ops; -+ (*lf_ops) = &max96724_line_fault_ops; -+ (*trans_ops) = NULL; -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Cody Burrows "); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("He Jiabin "); -+MODULE_DESCRIPTION("Maxim MAX96724 Quad GMSL2/GMSL1 to CSI-2 Deserializer driver"); -diff --git a/drivers/media/i2c/max9x/max96724.h b/drivers/media/i2c/max9x/max96724.h -new file mode 100644 -index 000000000000..5cd1bd289b1f ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96724.h -@@ -0,0 +1,241 @@ -+/* -+ * max96724.h - Maxim MAX96724 registers and constants. -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX96724_H_ -+#define _MAX96724_H_ -+ -+#include -+#include -+#include "serdes.h" -+ -+enum max96724_phy_mode { -+ MAX96724_MIPI_PHY_4X2 = BIT(0), // four 2-lane ports -+ MAX96724_MIPI_PHY_2X4 = BIT(2), // two 4-lane ports (CSI1 is master for port A, CSI2 for port B) -+ MAX96724_MIPI_PHY_1X4A_22 = BIT(3), // one 4-lane (PHY0+PHY1, CSI1 for port A) port and two 2-lane ports -+ MAX96724_MIPI_PHY_1X4B_22 = BIT(4), // one 4-lane (PHY2+PHY3, CSI2 for port B) port and two 2-lane ports -+}; -+ -+// described in detail on page 340 of the datasheet & reg doc -+enum max96724_initial_deskew_width { -+ MAX96724_INIT_DESKEW_1x32UI = 0, -+ MAX96724_INIT_DESKEW_2x32UI, -+ MAX96724_INIT_DESKEW_3x32UI, -+ MAX96724_INIT_DESKEW_4x32UI, -+ MAX96724_INIT_DESKEW_5x32UI, -+ MAX96724_INIT_DESKEW_6x32UI, -+ MAX96724_INIT_DESKEW_7x32UI, -+ MAX96724_INIT_DESKEW_8x32UI, -+ MAX96724_INIT_DESKEW_WIDTH_MAX = MAX96724_INIT_DESKEW_8x32UI, -+}; -+ -+enum max96724_fsync_pin { -+ MAX96724_FSYNC_PIN_MFP0 = 0, -+ MAX96724_FSYNC_PIN_MFP7 = 1, -+}; -+ -+enum max96724_fsync_mode { -+ MAX96724_FSYNC_GEN_ON_GPIO_OFF = 0, -+ MAX96724_FSYNC_GEN_ON_GPIO_OUTPUT, -+ MAX96724_FSYNC_GEN_OFF_GPIO_INPUT, -+ MAX96724_FSYNC_GEN_OFF_GPIO_OFF, -+}; -+ -+enum max96724_fsync_method { -+ MAX96724_FSYNC_METHOD_MANUAL = 0, -+ MAX96724_FSYNC_METHOD_SEMI_AUTO, -+ MAX96724_FSYNC_METHOD_AUTO, -+}; -+ -+enum max96724_fsync_gpio_type { -+ MAX96724_FS_GPIO_TYPE_GMSL1 = 0, -+ MAX96724_FS_GPIO_TYPE_GMSL2, -+}; -+ -+enum max96724_virtual_channel_size { -+ MAX96724_VC_2_BITS = 0, -+ MAX96724_VC_4_BITS = 1, -+}; -+ -+#define MAX96724_NUM_SERIAL_LINKS 4 -+#define MAX96724_NUM_VIDEO_PIPES 4 -+#define MAX96724_NUM_MIPI_MAPS 16 -+#define MAX96724_NUM_CSI_LINKS 4 -+#define MAX96724_NUM_LINE_FAULTS 4 -+// There are 16 GPIO registers and 11 GPIO TX/RX forwarding registers, but only 9 pins exposed -+#define MAX96724_NUM_GPIOS 9 -+ -+#define MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS 200 -+ -+#define MAX96724_DPLL_FREQ_MHZ_MULTIPLE 100 -+ -+#define MAX96724_FLD_OFS(n, bits_per_field, count) (((n) % (count)) * (bits_per_field)) -+#define MAX96724_OFFSET_GENMASK(offset, h, l) GENMASK(offset + h, offset + l) -+ -+#define MAX96724_REM_CC 0x03 -+#define MAX96724_REM_CC_DIS_PORT_FIELD(link, port) BIT(MAX96724_FLD_OFS(link, 2, 4) + (port % 2)) -+#define MAX96724_LINK_CTRL 0x06 -+#define MAX96724_LINK_CTRL_GMSL_FIELD(link) (BIT(link) << 4) -+#define MAX96724_LINK_CTRL_EN_FIELD(link) BIT(link) -+#define MAX96724_PHY_RATE_CTRL(link) (0x10 + ((link) / 2)) -+#define MAX96724_PHY_RATE_CTRL_TX_FIELD(link) (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2)) -+#define MAX96724_PHY_RATE_CTRL_RX_FIELD(link) (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 0)) -+ -+// Link lock registers -+#define MAX96724_PHY_LOCKED(link) ((link) == 0 ? 0x1A : 0x0A + ((link) - 1)) -+#define MAX96724_PHY_LOCKED_FIELD BIT(3) -+#define MAX96724_RESET_ALL 0x13 -+#define MAX96724_RESET_ALL_FIELD BIT(6) -+#define MAX96724_RESET_CTRL 0x18 -+#define MAX96724_RESET_CTRL_FIELD(link) BIT(link) -+#define MAX96724_DEV_REV 0x4C -+#define MAX96724_DEV_REV_FIELD GENMASK(3, 0) -+ -+#define MAX96724_VIDEO_PIPE_SEL(pipe) (0xF0 + ((pipe) / 2)) -+#define MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(link) \ -+ (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2)) -+#define MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(link) \ -+ (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 0)) -+ -+#define MAX96724_VIDEO_PIPE_EN 0xF4 -+#define MAX96724_VIDEO_PIPE_EN_FIELD(pipe) BIT(pipe) -+#define MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD BIT(4) -+#define MAX96724_VIDEO_PIPE_LEGACY_MODE 0 -+ -+#define MAX96724_DPLL_FREQ(phy) (0x415 + ((phy) * 3)) -+#define MAX96724_DPLL_FREQ_FIELD GENMASK(4, 0) -+ -+#define MAX96724_MIPI_TX_EXT(pipe) (0x800 + ((pipe) * 0x10)) -+ -+#define MAX96724_MIPI_PHY0 0x8A0 -+#define MAX96724_MIPI_PHY0_MODE_FIELD GENMASK(4, 0) -+#define MAX96724_MIPI_PHY_ENABLE 0x8A2 -+#define MAX96724_MIPI_PHY_ENABLE_FIELD(csi) BIT((csi) + 4) -+#define MAX96724_MIPI_PHY_LANE_MAP(csi) (0x8A3 + (csi) / 2) -+#define MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ -+ (GENMASK(1, 0) << (MAX96724_FLD_OFS(csi, 4, 2) + MAX96724_FLD_OFS(lane, 2, 2))) -+ -+// Note that CSIs and pipes overlap: -+// 0x901 through 0x90A and 0x933 through 0x934 are CSIs, repeated every 0x40 up to 4 times -+// 0x90B through 0x932 are pipes, repeated every 0x40 up to 4 times -+#define MAX96724_MIPI_TX(pipe) (0x900 + ((pipe) * 0x40)) -+#define MAX96724_MIPI_TX_LANE_CNT(csi) (MAX96724_MIPI_TX(csi) + 0x0A) -+#define MAX96724_MIPI_TX_LANE_CNT_FIELD GENMASK(7, 6) -+#define MAX96724_MIPI_TX_CPHY_EN_FIELD BIT(5) -+#define MAX96724_MIPI_TX_VCX_EN_FIELD BIT(4) -+#define MAX96724_MIPI_TX_DESKEW_INIT(csi) (MAX96724_MIPI_TX(csi) + 0x03) -+#define MAX96724_MIPI_TX_DESKEW_INIT_AUTO_EN BIT(7) -+#define MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD GENMASK(2, 0) -+#define MAX96724_MAP_EN_L(pipe) (MAX96724_MIPI_TX(pipe) + 0x0B) -+#define MAX96724_MAP_EN_H(pipe) (MAX96724_MIPI_TX(pipe) + 0x0C) -+#define MAX96724_MAP_EN_FIELD GENMASK(7, 0) -+#define MAX96724_MAP_SRC_L(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x0D + ((map) * 2)) -+#define MAX96724_MAP_SRC_L_VC_FIELD GENMASK(7, 6) -+#define MAX96724_MAP_SRC_L_DT_FIELD GENMASK(5, 0) -+#define MAX96724_MAP_DST_L(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x0D + ((map) * 2) + 1) -+#define MAX96724_MAP_DST_L_VC_FIELD GENMASK(7, 6) -+#define MAX96724_MAP_DST_L_DT_FIELD GENMASK(5, 0) -+#define MAX96724_MAP_SRCDST_H(pipe, map) (MAX96724_MIPI_TX_EXT(pipe) + (map)) -+#define MAX96724_MAP_SRCDST_H_SRC_VC_FIELD GENMASK(7, 5) -+#define MAX96724_MAP_SRCDST_H_DST_VC_FIELD GENMASK(4, 2) -+#define MAX96724_MAP_DPHY_DEST(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x2D + ((map) / 4)) -+#define MAX96724_MAP_DPHY_DEST_FIELD(map) (GENMASK(1, 0) << MAX96724_FLD_OFS(map, 2, 4)) -+ -+#define MAX96724_MIPI_TX_ALT_MEM(csi) (MAX96724_MIPI_TX(csi) + 0x33) -+#define MAX96724_MIPI_TX_ALT_MEM_FIELD GENMASK(2, 0) -+#define MAX96724_MIPI_TX_ALT_MEM_8BPP BIT(1) -+#define MAX96724_MIPI_TX_ALT_MEM_10BPP BIT(2) -+#define MAX96724_MIPI_TX_ALT_MEM_12BPP BIT(0) -+ -+#define MAX96724_VID_TX(pipe) (0x100 + (0x12 * (pipe))) -+ -+// GPIO Registers -+#define MAX96724_GPIO_REG(n) (0x300 + 3 * n) -+#define MAX96724_GPIO_RES_CFG BIT(7) -+#define MAX96724_GPIO_TX_ENABLE BIT(1) -+#define MAX96724_GPIO_OUTDRV_DISABLE BIT(0) -+ -+#define MAX96724_GPIO_A_REG(n) (MAX96724_GPIO_REG(n) + 1) -+ -+#define MAX96724_GPIO_B_REG(n) \ -+ ((n <= 2) ? (0x337 + 3 * n) : (n <= 7) ? (0x341 + 3 * (n - 3)) \ -+ : (0x351 + 3 * (n - 8))) -+ -+#define MAX96724_GPIO_C_REG(n) \ -+ ((n == 0) ? (0x36D) : (n <= 5) ? (0x371 + 3 * (n - 1)) \ -+ : (0x381 + 3 * (n - 6))) -+ -+#define MAX96724_GPIO_D_REG(n) \ -+ ((n <= 3) ? (0x3A4 + 3 * n) : (n <= 8) ? (0x3B1 + 3 * (n - 4)) \ -+ : (0x3C1 + 3 * (n - 9))) -+ -+#define MAX96724_GPIO_PUSH_PULL BIT(5) -+ -+// Native frame sync registers -+#define MAX96724_FSYNC_0 (0x4A0) -+#define MAX96724_FSYNC_OUT_PIN_FIELD BIT(5) -+#define MAX96724_EN_VS_GEN_FIELD BIT(6) -+#define MAX96724_FSYNC_MODE_FIELD GENMASK(3, 2) -+#define MAX96724_FSYNC_METH_FIELD GENMASK(1, 0) -+ -+#define MAX96724_FSYNC_5 (0x4A5) -+#define MAX96724_FSYNC_PERIOD_L_FIELD GENMASK(7, 0) -+#define MAX96724_FSYNC_6 (0x4A6) -+#define MAX96724_FSYNC_PERIOD_M_FIELD GENMASK(7, 0) -+#define MAX96724_FSYNC_7 (0x4A7) -+#define MAX96724_FSYNC_PERIOD_H_FIELD GENMASK(7, 0) -+ -+#define MAX96724_FSYNC_15 (0x4AF) -+#define MAX96724_FS_GPIO_TYPE_FIELD BIT(7) -+#define MAX96724_FS_USE_XTAL_FIELD BIT(6) -+#define MAX96724_AUTO_FS_LINKS_FIELD BIT(4) -+#define MAX96724_FS_LINK_FIELD(link) BIT(link) -+ -+#define MAX96724_FSYNC_17 (0x4B1) -+#define MAX96724_FSYNC_TX_ID_FIELD GENMASK(7, 3) -+#define MAX96724_FSYNC_ERR_THR_FIELD GENMASK(2, 0) -+ -+// Line fault status registers -+#define MAX96724_LF(link) (0xE1 + ((link) / 2)) -+#define MAX96724_LF_VAL(val, link) ((val >> ((link % 2) * 4)) & GENMASK(2, 0)) -+ -+#define MAX96724_PU_LF (0xE0) -+#define MAX96724_PU_LF_FIELD(line) BIT(line) -+ -+static struct max9x_serdes_rate_table max96724_rx_rates[] = { -+ { .val = 1, .freq_mhz = 3000 }, // 3 Gbps -+ { .val = 2, .freq_mhz = 6000 }, // 6 Gbps -+}; -+ -+static struct max9x_serdes_rate_table max96724_tx_rates[] = { -+ { .val = 0, .freq_mhz = 187 }, // 187.5 Mbps -+}; -+ -+// See: Errata for MAX96724/MAX96724F/MAX96724R (Rev. 1) document -+// https://www.analog.com/media/en/technical-documentation/data-sheets/max96724-f-r-rev1-b-0a-errata.pdf -+static const struct reg_sequence max96724_errata_rev1[] = { -+ // Errata #5 - GMSL2 Link requires register writes for robust 6 Gbps operation -+ { 0x1449, 0x75, }, -+ { 0x1549, 0x75, }, -+ { 0x1649, 0x75, }, -+ { 0x1749, 0x75, }, -+}; -+ -+#endif /* _MAX96724_H_ */ -+ -diff --git a/drivers/media/i2c/max9x/max9x_pdata.h b/drivers/media/i2c/max9x/max9x_pdata.h -new file mode 100644 -index 000000000000..874e7412315f ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9x_pdata.h -@@ -0,0 +1,103 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9X_PDATA_H_ -+#define _MAX9X_PDATA_H_ -+ -+#include -+#include -+ -+struct max9x_common; -+ -+enum max9x_serdes_link_type { -+ MAX9X_LINK_TYPE_GMSL1 = 0, -+ MAX9X_LINK_TYPE_GMSL2, -+ MAX9X_LINK_TYPE_FPDLINK, -+}; -+ -+struct max9x_subdev_pdata { -+ unsigned int serial_link_id; // DES identify GMSL link/pipe id -+ struct i2c_board_info board_info; -+ unsigned short phys_addr; // Remap or translate subdev -+}; -+ -+struct max9x_serial_link_pdata { -+ unsigned int link_id; -+ enum max9x_serdes_link_type link_type; -+ unsigned int rx_freq_mhz; -+ unsigned int tx_freq_mhz; -+ char poc_regulator[32]; -+ -+ // SER -+ struct i2c_client *des_client; -+ unsigned int des_link_id; -+}; -+ -+struct max9x_serdes_mipi_map { -+ u16 src_vc; -+ u16 src_dt; -+ u16 dst_vc; -+ u16 dst_dt; -+ u16 dst_csi; -+}; -+ -+struct max9x_video_pipe_pdata { -+ unsigned int serial_link_id; // DES: src-link, SER: dst-link -+ unsigned int pipe_id; // X, Y, Z, U -+ -+ // DES -+ struct max9x_serdes_mipi_map *maps; -+ unsigned int num_maps; -+ unsigned int src_pipe_id; -+ -+ // SER -+ unsigned int src_csi_id; -+ unsigned int *data_types; -+ unsigned int num_data_types; -+}; -+ -+struct max9x_serdes_phy_map { -+ u8 int_csi; // Internal CSI lane -+ u8 phy_ind; // PHY index -+ u8 phy_lane; // PHY lane index -+}; -+ -+struct max9x_csi_link_pdata { -+ unsigned int link_id; -+ enum v4l2_mbus_type bus_type; -+ unsigned int num_lanes; -+ struct max9x_serdes_phy_map *maps; -+ unsigned int num_maps; -+ unsigned int tx_rate_mbps; -+ bool auto_initial_deskew; -+ unsigned int initial_deskew_width; -+ bool auto_start; -+}; -+ -+struct max9x_gpio_pdata { -+ const char *label; -+ const char *const *names; -+}; -+ -+struct max9x_pdata { -+ unsigned short phys_addr; // Remap self -+ char suffix[5]; -+ -+ struct max9x_subdev_pdata *subdevs; // DES: the serializers, 1 per link; SER: sensor, eeprom, etc -+ unsigned int num_subdevs; -+ -+ struct max9x_serial_link_pdata *serial_links; // DES only. SER is currently presumed to have only 1 active link -+ unsigned int num_serial_links; -+ -+ struct max9x_video_pipe_pdata *video_pipes; -+ unsigned int num_video_pipes; -+ -+ struct max9x_csi_link_pdata *csi_links; -+ unsigned int num_csi_links; -+ -+ struct max9x_gpio_pdata gpio; -+ -+ bool external_refclk_enable; -+}; -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -new file mode 100644 -index 000000000000..633c55504273 ---- /dev/null -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -0,0 +1,2750 @@ -+/* -+ * serdes.c -+ * -+ * Copyright (c) 2018-2020 D3 Engineering. All rights reserved. -+ * Copyright (c) 2023, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2025 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "serdes.h" -+ -+static const s64 max9x_op_sys_clock[] = { -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2000), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1900), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1800), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1700), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1600), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1000), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(900), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(800), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(700), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(600), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(80), -+}; -+ -+int max9x_get_ops(char dev_id, -+ struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ int rval = 0; -+ -+ switch (dev_id) { -+ case MAX9296: -+ rval = max9296_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ case MAX96724: -+ rval = max96724_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ case MAX9295: -+ rval = max9295_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ case MAX96717: -+ rval = max96717_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ default: -+ break; -+ } -+ -+ return rval; -+} -+ -+static struct max9x_desc max9x_chips[] = { -+ [MAX9296] = { -+ .dev_id = 0x94, -+ .rev_reg = 0xE, -+ .serdes_type = MAX9X_DESERIALIZER, -+ .chip_type = MAX9296, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+ [MAX96724] = { -+ .dev_id = 0xA2, -+ .rev_reg = 0x4C, -+ .serdes_type = MAX9X_DESERIALIZER, -+ .chip_type = MAX96724, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+ [MAX9295] = { -+ .dev_id = 0x91, -+ .rev_reg = 0xE, -+ .serdes_type = MAX9X_SERIALIZER, -+ .chip_type = MAX9295, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+ /*need to check dev_id and others when used*/ -+ [MAX96717] = { -+ .dev_id = 0x91, -+ .rev_reg = 0xE, -+ .serdes_type = MAX9X_SERIALIZER, -+ .chip_type = MAX96717, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+}; -+ -+static const struct of_device_id max9x_of_match[] = { -+ { .compatible = "max9x,max9296", .data = &max9x_chips[MAX9296] }, -+ { .compatible = "max9x,max96724", .data = &max9x_chips[MAX96724] }, -+ { .compatible = "max9x,max9295", .data = &max9x_chips[MAX9295] }, -+ { .compatible = "max9x,max96717", .data = &max9x_chips[MAX96717] }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, max9x_of_match); -+ -+static const struct i2c_device_id max9x_id[] = { -+ { "max9x", MAX9296 }, -+ { "max9296", MAX9296 }, -+ { "max96724", MAX96724 }, -+ { "max9295", MAX9295 }, -+ { "max96717", MAX96717 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, max9x_id); -+ -+typedef int (*max9x_serdes_parse_child_func)(struct max9x_common *common, struct device_node *node); -+ -+static int max9x_soft_reset(struct max9x_common *common); -+static int max9x_remap_addr(struct max9x_common *common); -+static int max9x_setup_gpio(struct max9x_common *common); -+static int max9x_enable(struct max9x_common *common); -+static int max9x_disable(struct max9x_common *common); -+static int max9x_verify_devid(struct max9x_common *common); -+ -+static int max9x_enable_resume(struct max9x_common *common); -+static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned int link_id); -+static int max9x_create_adapters_resume(struct max9x_common *common); -+ -+static int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id); -+static int max9x_create_adapters(struct max9x_common *common); -+static int max9x_csi_link_to_pad(struct max9x_common *common, int csi_id); -+static int max9x_serial_link_to_pad(struct max9x_common *common, int link_id); -+static int max9x_register_v4l_subdev(struct max9x_common *common); -+static int max9x_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_sysfs_create_get_link(struct max9x_common *common, unsigned int link_id); -+static void max9x_sysfs_destroy_get_link(struct max9x_common *common, unsigned int link_id); -+ -+static int max9x_enable_line_faults(struct max9x_common *common); -+static int max9x_disable_line_faults(struct max9x_common *common); -+static void max9x_sysfs_destroy_line_fault_status(struct max9x_common *common, unsigned int line); -+ -+static int max9x_parse_pdata(struct max9x_common *common, struct max9x_pdata *pdata); -+static int max9x_parse_serial_link_pdata(struct max9x_common *common, -+ struct max9x_serial_link_pdata *max9x_serial_link_pdata); -+static int max9x_parse_video_pipe_pdata(struct max9x_common *common, -+ struct max9x_video_pipe_pdata *video_pipe_pdata); -+static int max9x_parse_csi_link_pdata(struct max9x_common *common, struct max9x_csi_link_pdata *csi_link_pdata); -+static int max9x_parse_subdev_pdata(struct max9x_common *common, struct max9x_subdev_pdata *subdev_pdata); -+ -+static int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id); -+static int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id); -+ -+static int max9x_des_isolate_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_des_deisolate_serial_link(struct max9x_common *common, unsigned int link_id); -+ -+static ssize_t max9x_link_status_show(struct device *dev, struct device_attribute *attr, char *buf); -+ -+static int max9x_setup_translations(struct max9x_common *common); -+static int max9x_disable_translations(struct max9x_common *common); -+ -+#define MAX9X_ALLOCATE_ELEMENTS(common, element_type, elements, num_elements) ({ \ -+ struct device *dev = common->dev; \ -+ int allocate_ret = 0; \ -+ (common)->num_elements = 0; \ -+ (common)->elements = NULL; \ -+ if ((common)->common_ops && (common)->common_ops->max_elements) { \ -+ (common)->num_elements = (common)->common_ops->max_elements((common), element_type); \ -+ if ((common)->num_elements > 0) { \ -+ (common)->elements = devm_kzalloc((common)->dev, \ -+ (common)->num_elements * sizeof(typeof(*((common)->elements))), GFP_KERNEL); \ -+ if (!(common)->elements) { \ -+ dev_err(dev, "Failed to allocated memory for " # element_type); \ -+ allocate_ret = -ENOMEM; \ -+ } \ -+ } \ -+ } \ -+ allocate_ret; \ -+}) -+ -+static struct max9x_pdata *pdata_ser(struct device *dev, struct max9x_subdev_pdata *sdinfo, const char *name, -+ unsigned int phys_addr, unsigned int virt_addr) -+{ -+ struct max9x_pdata *pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -+ -+ dev_info(dev, "ser %s phys %02x virt %02x\n", name, phys_addr, virt_addr); -+ -+ sdinfo->board_info.platform_data = pdata; -+ strscpy(sdinfo->board_info.type, name, I2C_NAME_SIZE); -+ sdinfo->board_info.addr = virt_addr; -+ sdinfo->phys_addr = pdata->phys_addr = phys_addr; -+ -+ return pdata; -+} -+ -+static struct max9x_pdata *pdata_sensor(struct device *dev, struct max9x_subdev_pdata *sdinfo, const char *name, -+ unsigned int phys_addr, unsigned int virt_addr) -+{ -+ dev_info(dev, "sen %s phys %02x virt %02x\n", name, phys_addr, virt_addr); -+ -+ strscpy(sdinfo->board_info.type, name, I2C_NAME_SIZE); -+ sdinfo->board_info.addr = virt_addr; -+ sdinfo->phys_addr = phys_addr; -+ -+ return NULL; -+} -+ -+static struct max9x_pdata *parse_ser_pdata(struct device *dev, const char *ser_name, char *suffix, -+ unsigned int ser_nlanes, unsigned int phys_addr, -+ unsigned int virt_addr, struct max9x_subdev_pdata *ser_sdinfo, -+ unsigned int sensor_dt) -+{ -+ struct max9x_pdata *ser_pdata = pdata_ser(dev, ser_sdinfo, ser_name, phys_addr, virt_addr); -+ struct max9x_serial_link_pdata *ser_serial_link; -+ struct max9x_video_pipe_pdata *ser_video_pipe; -+ -+ snprintf(ser_pdata->suffix, sizeof(ser_pdata->suffix), "%s", suffix); -+ -+ ser_pdata->num_serial_links = 1; -+ ser_pdata->serial_links = devm_kzalloc(dev, ser_pdata->num_serial_links * sizeof(*ser_pdata->serial_links), -+ GFP_KERNEL); -+ -+ ser_serial_link = &ser_pdata->serial_links[0]; -+ ser_serial_link->link_id = 0; -+ ser_serial_link->link_type = MAX9X_LINK_TYPE_GMSL2; -+ ser_serial_link->rx_freq_mhz = 6000; -+ ser_serial_link->tx_freq_mhz = 187; -+ -+ ser_pdata->num_video_pipes = 1; -+ ser_pdata->video_pipes = devm_kzalloc(dev, -+ ser_pdata->num_video_pipes * sizeof(*ser_pdata->video_pipes), GFP_KERNEL); -+ -+ ser_video_pipe = &ser_pdata->video_pipes[0]; -+ ser_video_pipe->serial_link_id = 0; -+ ser_video_pipe->pipe_id = ser_sdinfo->serial_link_id; -+ ser_video_pipe->src_csi_id = 1; /* PHY B typically */ -+ -+ ser_video_pipe->num_data_types = 1; -+ ser_video_pipe->data_types = devm_kzalloc(dev, -+ ser_video_pipe->num_data_types * sizeof(*ser_video_pipe->data_types), GFP_KERNEL); -+ ser_video_pipe->data_types[0] = sensor_dt; -+ -+ ser_pdata->num_csi_links = 1; -+ ser_pdata->csi_links = devm_kzalloc(dev, ser_pdata->num_csi_links * sizeof(*ser_pdata->csi_links), GFP_KERNEL); -+ -+ struct max9x_csi_link_pdata *csi_link = &ser_pdata->csi_links[0]; -+ -+ csi_link->link_id = 1; -+ csi_link->num_lanes = ser_nlanes; -+ -+ return ser_pdata; -+} -+ -+static void parse_sensor_pdata(struct device *dev, const char *sensor_name, char *suffix, unsigned int ser_nlanes, -+ unsigned int phys_addr, unsigned int virt_addr, struct max9x_subdev_pdata *ser_sdinfo, -+ struct max9x_pdata *ser_pdata) -+{ -+ ser_pdata->num_subdevs = 1; -+ ser_pdata->subdevs = devm_kzalloc(dev, ser_pdata->num_subdevs * sizeof(*ser_pdata->subdevs), GFP_KERNEL); -+ pdata_sensor(dev, &ser_pdata->subdevs[0], sensor_name, phys_addr, virt_addr); -+ -+ /* NOTE: i2c_dev_set_name() will prepend "i2c-" to this name */ -+ char *dev_name = devm_kzalloc(dev, I2C_NAME_SIZE, GFP_KERNEL); -+ -+ snprintf(dev_name, I2C_NAME_SIZE, "%s %s", sensor_name, suffix); -+ ser_pdata->subdevs[0].board_info.dev_name = dev_name; -+ -+ struct isx031_platform_data *sen_pdata = devm_kzalloc(dev, sizeof(*sen_pdata), GFP_KERNEL); -+ -+ if (!sen_pdata) -+ return; -+ -+ ser_pdata->subdevs[0].board_info.platform_data = sen_pdata; -+ sen_pdata->lanes = ser_nlanes; -+ sen_pdata->irq_pin_flags = 1; //workaround for identify D3. -+ snprintf(sen_pdata->suffix, sizeof(sen_pdata->suffix), "%s", suffix); -+} -+ -+static void *parse_serdes_pdata(struct device *dev) -+{ -+ /* -+ * Assumptions: -+ * - All ports have same model of sensor -+ * - Single-port usage will always be port 0, never port 1 -+ * - Serializer always uses serial link 0 -+ */ -+ struct serdes_platform_data *serdes_pdata = dev->platform_data; -+ unsigned int num_ports = serdes_pdata->subdev_num; -+ unsigned int csi_port = (serdes_pdata->des_port / 90); -+ struct max9x_pdata *des_pdata = devm_kzalloc(dev, sizeof(*des_pdata), GFP_KERNEL); -+ -+ snprintf(des_pdata->suffix, sizeof(des_pdata->suffix), "%c", serdes_pdata->suffix); -+ des_pdata->num_serial_links = num_ports; -+ des_pdata->serial_links = devm_kzalloc(dev, -+ des_pdata->num_serial_links * sizeof(*des_pdata->serial_links), GFP_KERNEL); -+ -+ des_pdata->num_subdevs = num_ports; -+ des_pdata->subdevs = devm_kzalloc(dev, des_pdata->num_subdevs * sizeof(*des_pdata->subdevs), GFP_KERNEL); -+ -+ des_pdata->num_video_pipes = num_ports; -+ des_pdata->video_pipes = devm_kzalloc(dev, -+ des_pdata->num_video_pipes * sizeof(*des_pdata->video_pipes), GFP_KERNEL); -+ -+ for (unsigned int serial_link_id = 0; serial_link_id < des_pdata->num_serial_links; serial_link_id++) { -+ struct max9x_serial_link_pdata *serial_link = &des_pdata->serial_links[serial_link_id]; -+ unsigned int video_pipe_id = serial_link_id; -+ struct serdes_subdev_info *serdes_sdinfo = &serdes_pdata->subdev_info[serial_link_id]; -+ struct max9x_video_pipe_pdata *des_video_pipe = &des_pdata->video_pipes[video_pipe_id]; -+ struct max9x_subdev_pdata *ser_sdinfo = &des_pdata->subdevs[serial_link_id]; -+ const char *ser_name = serdes_pdata->ser_name; -+ const char *sensor_name = serdes_sdinfo->board_info.type; -+ unsigned int ser_alias = serdes_sdinfo->ser_alias; -+ unsigned int sensor_alias = serdes_sdinfo->board_info.addr; -+ unsigned int ser_phys_addr = serdes_sdinfo->ser_phys_addr; -+ unsigned int sensor_phys_addr = serdes_sdinfo->phy_i2c_addr; -+ unsigned int lanes = serdes_pdata->ser_nlanes; -+ unsigned int dt = serdes_sdinfo->sensor_dt; -+ -+ serial_link->link_id = serial_link_id; -+ serial_link->link_type = MAX9X_LINK_TYPE_GMSL2; -+ serial_link->rx_freq_mhz = 6000; -+ serial_link->tx_freq_mhz = 187; -+ -+ des_video_pipe->serial_link_id = serial_link_id; -+ des_video_pipe->pipe_id = video_pipe_id; -+ des_video_pipe->src_pipe_id = video_pipe_id; -+ des_video_pipe->num_maps = 3; -+ des_video_pipe->maps = devm_kzalloc(dev, -+ des_video_pipe->num_maps * sizeof(*des_video_pipe->maps), GFP_KERNEL); -+ -+ ser_sdinfo->serial_link_id = serial_link_id; -+ -+ SET_CSI_MAP(des_video_pipe->maps, 0, 0, 0x00, video_pipe_id, 0x00, csi_port); -+ SET_CSI_MAP(des_video_pipe->maps, 1, 0, 0x01, video_pipe_id, 0x01, csi_port); -+ SET_CSI_MAP(des_video_pipe->maps, 2, 0, dt, video_pipe_id, dt, csi_port); /* YUV422 8-bit */ -+ -+ struct max9x_pdata *ser_pdata = parse_ser_pdata(dev, ser_name, serdes_sdinfo->suffix, lanes, -+ ser_phys_addr, ser_alias, ser_sdinfo, dt); -+ -+ parse_sensor_pdata(dev, sensor_name, serdes_sdinfo->suffix, lanes, sensor_phys_addr, sensor_alias, -+ ser_sdinfo, ser_pdata); -+ -+ } -+ -+ des_pdata->num_csi_links = 1; -+ des_pdata->csi_links = devm_kzalloc(dev, des_pdata->num_csi_links * sizeof(*des_pdata->csi_links), GFP_KERNEL); -+ -+ do { -+ struct max9x_csi_link_pdata *csi_link = &des_pdata->csi_links[0]; -+ -+ csi_link->link_id = csi_port; -+ csi_link->bus_type = serdes_pdata->bus_type; -+ csi_link->num_lanes = serdes_pdata->deser_nlanes; -+ csi_link->tx_rate_mbps = 2000; -+ csi_link->auto_initial_deskew = true; -+ csi_link->initial_deskew_width = 7; -+ csi_link->auto_start = false; -+ csi_link->num_maps = 2; -+ csi_link->maps = devm_kzalloc(dev, csi_link->num_maps * sizeof(*csi_link->maps), GFP_KERNEL); -+ if (csi_port == 1) { -+ SET_PHY_MAP(csi_link->maps, 0, 0, 1, 0); /* 0 (DA0) -> PHY1.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 1, 1, 1); /* 1 (DA1) -> PHY1.1 */ -+ } else if (csi_port == 2) { -+ SET_PHY_MAP(csi_link->maps, 0, 2, 2, 0); /* 0 (DA0) -> PHY2.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 2, 2, 1); /* 1 (DA1) -> PHY2.1 */ -+ } -+ } while (0); -+ -+ return des_pdata; -+} -+ -+static int regmap_read_retry(struct regmap *map, unsigned int reg, -+ unsigned int *val) -+{ -+ int ret = 0; -+ int i = 0; -+ -+ for (i = 0; i < 50; i++) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+static int max9x_enable_resume(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (common->regulator_enabled) -+ return 0; -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_enable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ common->regulator_enabled = true; -+ } -+ -+ if (common->common_ops && common->common_ops->enable) { -+ ret = common->common_ops->enable(common); -+ if (ret) -+ goto err; -+ } -+ -+ return 0; -+ -+err: -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (common->regulator_enabled && !IS_ERR_OR_NULL(common->vdd_regulator)) -+ regulator_disable(common->vdd_regulator); -+ -+ common->regulator_enabled = false; -+ -+ return ret; -+} -+ -+static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[link_id]; -+ unsigned int phys_addr, virt_addr; -+ struct i2c_client *phys_client; -+ struct regmap *phys_map, *virt_map; -+ unsigned int val; -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (!serial_link->remote.pdata) -+ return 0; -+ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ phys_addr = serial_link->remote.pdata->phys_addr; -+ virt_addr = serial_link->remote.pdata->board_info.addr; -+ if (phys_addr == virt_addr) -+ return 0; -+ -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ -+ phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(phys_client)) { -+ dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ ret = PTR_ERR(phys_client); -+ return ret; -+ } -+ -+ phys_map = regmap_init_i2c(phys_client, ®map_config); -+ if (IS_ERR_OR_NULL(phys_map)) { -+ dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ ret = PTR_ERR(phys_map); -+ goto err_client; -+ } -+ -+ struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); -+ -+ virt_map = ser_common->map; -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (!ret) { -+ dev_info(common->dev, "Remap not necessary"); -+ ret = 0; -+ goto err_regmap; -+ } -+ -+ ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ goto err_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ } -+ -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ if (ret) { -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ phys_addr, virt_addr, ret); -+ goto err_regmap; -+ } -+ -+ msleep(100); -+ -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ goto err_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ } -+ -+err_regmap: -+ regmap_exit(phys_map); -+err_client: -+ i2c_unregister_device(phys_client); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ -+ return ret; -+} -+ -+static int max9x_create_adapters_resume(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int link_id; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ int err = 0; -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ dev_info(dev, "RESUME: Serial-link %d: %senabled", link_id, -+ (common->serial_link[link_id].enabled ? "" : "not ")); -+ -+ if (!common->serial_link[link_id].enabled) -+ continue; -+ -+ /* This exponential retry works around a current problem in the locking code. */ -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ err = max9x_enable_serial_link(common, link_id); -+ if (!err) -+ break; -+ -+ dev_warn(dev, -+ "enable link %d failed, trying again (waiting %d ms)", -+ link_id, ms); -+ msleep(ms); -+ } -+ if (ms > RETRY_MS_MAX) { -+ dev_err(dev, "failed to enable link %d after multiple retries", -+ link_id); -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ } -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) -+ max9x_setup_translations(common); -+ -+ return 0; -+} -+ -+int max9x_common_resume(struct max9x_common *common) -+{ -+ struct max9x_common *des_common = NULL; -+ struct device *dev = common->dev; -+ u32 des_link; -+ u32 phys_addr, virt_addr; -+ int ret = 0; -+ int retry = 50; -+ -+ if (dev->platform_data) { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ return ret; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_reset_serializer; -+ } -+ } -+ } -+ -+ while (retry--) { -+ ret = max9x_verify_devid(common); -+ if (ret) -+ msleep(100); -+ else -+ break; -+ } -+ if (ret) { -+ dev_err(dev, "can't get devid after resume"); -+ goto err_deisolate; -+ } -+ -+ ret = max9x_enable_resume(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable"); -+ goto err_disable; -+ } -+ -+ ret = max9x_create_adapters_resume(common); -+ if (ret) { -+ dev_err(dev, "Failed to create adapters"); -+ goto err_disable; -+ } -+ -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ -+ return 0; -+ -+err_disable: -+ max9x_disable(common); -+ -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ -+err_reset_serializer: -+ if (common->type == MAX9X_SERIALIZER) { -+ if (common->common_ops && common->common_ops->remap_reset) { -+ ret = common->common_ops->remap_reset(common); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return ret; -+} -+ -+int max9x_common_suspend(struct max9x_common *common) -+{ -+ unsigned int link_id; -+ -+ dev_dbg(common->dev, "try to suspend"); -+ -+ max9x_disable_translations(common); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) -+ max9x_disable_serial_link(common, link_id); -+ -+ max9x_disable(common); -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (dev->platform_data) { -+ if (common->common_ops && common->common_ops->remap_reset) { -+ ret = common->common_ops->remap_reset(common); -+ if (ret) -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+int max9x_common_init_i2c_client(struct max9x_common *common, -+ struct i2c_client *client, -+ const struct regmap_config *regmap_config, -+ struct max9x_common_ops *common_ops, -+ struct max9x_serial_link_ops *serial_link_ops, -+ struct max9x_csi_link_ops *csi_link_ops, -+ struct max9x_line_fault_ops *lf_ops) -+{ -+ struct device *dev = &client->dev; -+ struct i2c_adapter *adap = to_i2c_adapter(dev->parent); -+ struct max9x_pdata *pdata = NULL; -+ u32 phys_addr, virt_addr; -+ int ret; -+ -+ common->dev = dev; -+ common->client = client; -+ -+ /* If no GPIO is found this will return NULL, and will not error */ -+ common->reset_gpio = devm_gpiod_get_optional(dev, MAX9X_RESET_GPIO_NAME, GPIOD_OUT_HIGH); -+ if (IS_ERR(common->reset_gpio)) { -+ dev_err(dev, "gpiod_get failed with error: %ld", PTR_ERR(common->reset_gpio)); -+ return PTR_ERR(common->reset_gpio); -+ } -+ -+ common->vdd_regulator = devm_regulator_get_optional(dev, MAX9X_VDD_REGULATOR_NAME); -+ if (IS_ERR_OR_NULL(common->vdd_regulator)) -+ dev_info(dev, "Missing VDD regulator"); -+ -+ common->map = devm_regmap_init_i2c(client, regmap_config); -+ if (IS_ERR_OR_NULL(common->map)) { -+ dev_err(dev, "Failed to create regmap."); -+ return PTR_ERR(common->map); -+ } -+ -+ if (dev->platform_data) { -+ pdata = dev->platform_data; -+ /* -+ * for serializer, the i2c phys_addr supposed to be updated as virt_addr -+ * when deserializer do max9x_remap_serializer, -+ * check here to avoid failure when directly read chipid from virt_addr -+ */ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (phys_addr != virt_addr) { -+ common->phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(common->phys_client)) { -+ dev_err(dev, "Failed to create dummy device for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ -+ common->phys_map = regmap_init_i2c(common->phys_client, regmap_config); -+ if (IS_ERR_OR_NULL(common->phys_map)) { -+ dev_err(dev, "Failed to create dummy device regmap for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ } -+ } -+ -+ ret = max9x_verify_devid(common); -+ if (ret) -+ return ret; -+ -+ ret = common->des->get_max9x_ops(common->des->chip_type, &(common->common_ops), -+ &(common->serial_link_ops), &(common->csi_link_ops), &(common->line_fault_ops), -+ &(common->translation_ops)); -+ if (ret) { -+ dev_err(dev, "Failed to get ops."); -+ return ret; -+ } -+ -+ mutex_init(&common->link_mutex); -+ mutex_init(&common->isolate_mutex); -+ common->isolated_link = -1; -+ common->selected_link = -1; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_CSI_LINK, csi_link, num_csi_links); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_VIDEO_PIPE, video_pipe, num_video_pipes); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_SERIAL_LINK, serial_link, num_serial_links); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_LINE_FAULT, line_fault, num_line_faults); -+ if (ret) -+ return ret; -+ -+ common->muxc = i2c_mux_alloc( -+ adap, dev, -+ common->num_serial_links, -+ 0, -+ I2C_MUX_LOCKED, -+ max9x_select_i2c_chan, -+ max9x_deselect_i2c_chan); -+ if (IS_ERR_OR_NULL(common->muxc)) { -+ dev_err(dev, "Failed to allocate mux core"); -+ return -ENOMEM; -+ } -+ common->muxc->priv = common; -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ dev->platform_data = parse_serdes_pdata(dev); -+ -+ if (dev->platform_data) { -+ pdata = dev->platform_data; -+ -+ dev_dbg(dev, "Parse pdata"); -+ -+ ret = max9x_parse_pdata(common, pdata); -+ if (ret) -+ return ret; -+ } -+ -+ dev_dbg(dev, "Enable"); -+ -+ ret = max9x_enable(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable"); -+ goto enable_err; -+ } -+ -+enable_err: -+ if (common->phys_map) { -+ regmap_exit(common->phys_map); -+ common->phys_map = NULL; -+ } -+ -+ if (common->phys_client) { -+ i2c_unregister_device(common->phys_client); -+ common->phys_client = NULL; -+ } -+ -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "Enable gpio"); -+ ret = max9x_setup_gpio(common); -+ if (ret) -+ goto err_enable; -+ -+ dev_dbg(dev, "Enable line faults"); -+ -+ ret = max9x_enable_line_faults(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable line faults"); -+ goto err_enable; -+ } -+ -+ dev_dbg(dev, "Enable links"); -+ -+ ret = max9x_create_adapters(common); -+ if (ret) -+ goto err_enable; -+ -+ ret = max9x_register_v4l_subdev(common); -+ if (ret) -+ goto err_adapters; -+ -+ return 0; -+ -+err_adapters: -+ i2c_mux_del_adapters(common->muxc); -+ -+err_enable: -+ max9x_disable(common); -+ -+ return ret; -+} -+EXPORT_SYMBOL(max9x_common_init_i2c_client); -+ -+void max9x_destroy(struct max9x_common *common) -+{ -+ unsigned int link_id; -+ unsigned int line; -+ -+ dev_dbg(common->dev, "Destroy"); -+ -+ max9x_disable_translations(common); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ max9x_disable_serial_link(common, link_id); -+ max9x_sysfs_destroy_get_link(common, link_id); -+ } -+ -+ for (line = 0; line < common->num_line_faults; line++) -+ max9x_sysfs_destroy_line_fault_status(common, line); -+ -+ max9x_disable(common); -+ -+ /* unregister devices? */ -+ -+ v4l2_async_unregister_subdev(&common->v4l.sd); -+ media_entity_cleanup(&common->v4l.sd.entity); -+ -+ i2c_mux_del_adapters(common->muxc); -+ mutex_destroy(&common->link_mutex); -+ mutex_destroy(&common->isolate_mutex); -+} -+EXPORT_SYMBOL(max9x_destroy); -+ -+/* -+ * max9x_serdes_mhz_to_rate()- Lookup register value for given frequency -+ * -+ * Return: Positive value on success, negative error on failure -+ */ -+int max9x_serdes_mhz_to_rate(struct max9x_serdes_rate_table *table, -+ int len, unsigned int freq_mhz) -+{ -+ int i; -+ -+ for (i = 0; i < len; i++) -+ if (table[i].freq_mhz == freq_mhz) -+ return table[i].val; -+ -+ WARN_ONCE(true, "Invalid GMSL frequency: %d MHz", freq_mhz); -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL(max9x_serdes_mhz_to_rate); -+ -+struct max9x_common *max9x_sd_to_common(struct v4l2_subdev *sd) -+{ -+ if (!sd) -+ return NULL; -+ -+ struct max9x_serdes_v4l *v4l = container_of(sd, struct max9x_serdes_v4l, sd); -+ -+ return container_of(v4l, struct max9x_common, v4l); -+} -+EXPORT_SYMBOL(max9x_sd_to_common); -+ -+struct max9x_common *max9x_client_to_common(struct i2c_client *client) -+{ -+ if (!client) -+ return NULL; -+ -+ return max9x_sd_to_common(i2c_get_clientdata(client)); -+} -+EXPORT_SYMBOL(max9x_client_to_common); -+ -+int max9x_soft_reset(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->soft_reset) -+ return common->common_ops->soft_reset(common); -+ -+ return 0; -+} -+ -+int max9x_remap_addr(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->remap_addr) -+ return common->common_ops->remap_addr(common); -+ -+ return 0; -+} -+ -+int max9x_setup_gpio(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->remap_addr) -+ return common->common_ops->setup_gpio(common); -+ -+ return 0; -+} -+ -+/* -+ * max9x_enable() - Enable reset and power to des. -+ */ -+int max9x_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phys_addr, virt_addr; -+ int ret; -+ -+ if (common->regulator_enabled) -+ return 0; -+ -+ virt_addr = common->client->addr; -+ phys_addr = (common->phys_client ? common->phys_client->addr : virt_addr); -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_enable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ common->regulator_enabled = true; -+ } -+ -+ /* Ensure device is reset */ -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) { -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ usleep_range(1000, 1050); -+ gpiod_set_value_cansleep(common->reset_gpio, 0); -+ } else { -+ /* No reset_gpio, device requires soft-reset */ -+ if (phys_addr == virt_addr) { -+ /* No remapping necessary */ -+ ret = max9x_soft_reset(common); -+ if (ret) -+ goto err; -+ } else if (phys_addr != virt_addr) { -+ /* Try virt address first */ -+ ret = max9x_soft_reset(common); -+ if (ret) { -+ /* Nope? Try phys address next */ -+ struct regmap *virt_map = common->map; -+ -+ common->map = common->phys_map; -+ ret = max9x_soft_reset(common); -+ common->map = virt_map; -+ if (ret) -+ goto err; -+ } -+ } -+ } -+ -+ dev_info(dev, "sleep for startup after soft reset"); -+ usleep_range(100000, 100050); -+ -+ if (phys_addr != virt_addr) { -+ /* Device is now reset, but requires remap */ -+ ret = max9x_remap_addr(common); -+ if (ret) -+ goto err; -+ } -+ -+ if (common->common_ops && common->common_ops->enable) { -+ ret = common->common_ops->enable(common); -+ if (ret) -+ goto err; -+ } -+ -+ return 0; -+ -+err: -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (common->regulator_enabled && !IS_ERR_OR_NULL(common->vdd_regulator)) -+ regulator_disable(common->vdd_regulator); -+ -+ common->regulator_enabled = false; -+ -+ return ret; -+} -+ -+/* -+ * max9x_disable() - Disable reset and power to des. -+ */ -+int max9x_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ ret = max9x_disable_line_faults(common); -+ if (ret) { -+ dev_err(dev, "Failed to disable line faults"); -+ return ret; -+ } -+ -+ if (common->regulator_enabled) { -+ if (common->common_ops && common->common_ops->disable) -+ return common->common_ops->disable(common); -+ -+ common->regulator_enabled = false; -+ -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_disable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to disable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ } -+ return 0; -+} -+ -+static int max9x_get_chip_type(unsigned int dev_id) -+{ -+ unsigned int des_num = sizeof(max9x_chips) / sizeof(struct max9x_desc); -+ unsigned int i = 0; -+ -+ for (i = 0; i < des_num; i++) { -+ if (dev_id == max9x_chips[i].dev_id) -+ return i; -+ } -+ -+ return -1; -+} -+ -+int max9x_verify_devid(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct regmap *phys_map = common->phys_map; -+ unsigned int dev_id, dev_rev, chip_type; -+ int ret; -+ -+ /* -+ * Fetch and output chip name + revision -+ * try both virtual address and physical address -+ */ -+ ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); -+ if (ret) { -+ dev_warn(dev, "Failed to read chip ID from virtual address"); -+ if (phys_map) { -+ ret = regmap_read(phys_map, MAX9X_DEV_ID, &dev_id); -+ if (ret) { -+ dev_err(dev, "Failed to read chip ID from phys address"); -+ return ret; -+ } -+ } else -+ return ret; -+ } -+ -+ chip_type = max9x_get_chip_type(dev_id); -+ if (chip_type == -1) { -+ dev_warn(dev, "Unknown chip ID 0x%x", dev_id); -+ return -1; -+ } -+ common->des = &max9x_chips[chip_type]; -+ common->type = common->des->serdes_type; -+ TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); -+ dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); -+ -+ dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -+ return 0; -+} -+ -+/* TODO: remap not hardcode according to pdata */ -+int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[link_id]; -+ unsigned int phys_addr, virt_addr; -+ struct i2c_client *phys_client, *virt_client; -+ struct regmap *phys_map, *virt_map; -+ unsigned int val; -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (!serial_link->remote.pdata) -+ return 0; -+ -+ ret = max9x_select_i2c_chan(common->muxc, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ phys_addr = serial_link->remote.pdata->phys_addr; -+ virt_addr = serial_link->remote.pdata->board_info.addr; -+ if (phys_addr == virt_addr) -+ return 0; -+ -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ -+ phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(phys_client)) { -+ dev_err(common->dev, "Failed to create dummy client for phys_addr"); -+ return PTR_ERR(phys_client); -+ } -+ -+ phys_map = regmap_init_i2c(phys_client, ®map_config); -+ if (IS_ERR_OR_NULL(phys_map)) -+ goto err_client; -+ -+ virt_client = i2c_new_dummy_device(common->client->adapter, virt_addr); -+ if (IS_ERR_OR_NULL(virt_client)) -+ goto err_regmap; -+ -+ virt_map = regmap_init_i2c(virt_client, ®map_config); -+ if (IS_ERR_OR_NULL(virt_map)) -+ goto err_virt_client; -+ -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (!ret) { -+ dev_info(common->dev, "Remap not necessary"); -+ ret = 0; -+ goto err_virt_regmap; -+ } -+ -+ ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ goto err_virt_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ } -+ -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ if (ret) { -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ phys_addr, virt_addr, ret); -+ goto err_virt_regmap; -+ } -+ -+ usleep_range(1000, 1050); -+ -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ goto err_virt_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ } -+ -+err_virt_regmap: -+ regmap_exit(virt_map); -+err_virt_client: -+ i2c_unregister_device(virt_client); -+ -+err_regmap: -+ regmap_exit(phys_map); -+err_client: -+ i2c_unregister_device(phys_client); -+ -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ -+ return ret; -+} -+ -+int max9x_create_adapters(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int link_id; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ int err = 0; -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ err = max9x_sysfs_create_get_link(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to create sysfs lock status file for link %d", -+ link_id); -+ continue; -+ } -+ -+ dev_info(dev, "Serial-link %d: %senabled", -+ link_id, (common->serial_link[link_id].enabled ? "" : "not ")); -+ -+ if (!common->serial_link[link_id].enabled) -+ continue; -+ -+ /* This exponential retry works around a current problem in the locking code. */ -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ err = max9x_enable_serial_link(common, link_id); -+ if (!err) -+ break; -+ -+ dev_warn(dev, -+ "enable link %d failed, trying again (waiting %d ms)", -+ link_id, ms); -+ msleep(ms); -+ } -+ if (ms > RETRY_MS_MAX) { -+ dev_err(dev, "failed to enable link %d after multiple retries", -+ link_id); -+ goto err_disable; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ err = max9x_remap_serializers(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to remap serializers on link %d", link_id); -+ goto err_disable; -+ } -+ } -+ -+ continue; -+err_disable: -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ max9x_setup_translations(common); -+ -+ err = i2c_mux_add_adapter(common->muxc, 0, link_id); -+ if (err) { -+ dev_err(dev, "failed to add adapter for link %d", -+ link_id); -+ max9x_disable_serial_link(common, link_id); -+ continue; -+ } -+ } -+ -+ return 0; -+} -+ -+static void max9x_des_s_csi_link(struct max9x_common *common, -+ unsigned int serial_link_id, int enable) -+{ -+ unsigned int video_pipe_id; -+ int err = 0; -+ -+ for (video_pipe_id = 0; video_pipe_id < common->num_video_pipes; -+ video_pipe_id++) { -+ struct max9x_serdes_video_pipe *video_pipe = -+ &common->video_pipe[video_pipe_id]; -+ unsigned int map_id; -+ -+ if (!video_pipe->enabled) -+ continue; -+ -+ if (video_pipe->config.src_link != serial_link_id) -+ continue; -+ -+ for (map_id = 0; map_id < video_pipe->config.num_maps; -+ map_id++) { -+ unsigned int csi_link_id = -+ video_pipe->config.map[map_id].dst_csi; -+ -+ if (common->csi_link[csi_link_id].config.auto_start) -+ continue; /* Already started at probe */ -+ -+ if (enable) { -+ if (common->csi_link_ops->enable) { -+ err = common->csi_link_ops->enable( -+ common, csi_link_id); -+ if (err) -+ dev_warn( -+ common->dev, -+ "csi_link_ops->enable CSI %d failed: %d", -+ csi_link_id, err); -+ } -+ } else { -+ if (common->csi_link_ops->disable) { -+ err = common->csi_link_ops->disable( -+ common, csi_link_id); -+ if (err) -+ dev_warn( -+ common->dev, -+ "csi_link_ops->disable CSI %d failed: %d", -+ csi_link_id, err); -+ } -+ } -+ } -+ } -+} -+ -+static int _max9x_s_remote_stream(struct max9x_common *common, u32 sink_pad, -+ u32 sink_stream, int enable) -+{ -+ struct media_pad *remote_pad; -+ struct v4l2_subdev *remote_sd; -+ int ret = 0; -+ -+ if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ remote_pad = media_pad_remote_pad_first(&common->v4l.pads[sink_pad]); -+ if (IS_ERR_OR_NULL(remote_pad)) { -+ dev_err(common->dev, "Failed to find remote pad for %s %u", -+ common->v4l.sd.entity.name, sink_pad); -+ return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -ENODEV; -+ } -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ if (!remote_sd) { -+ dev_err(common->dev, "Failed to resolve entity %s to subdev", -+ remote_pad->entity->name); -+ return -ENODEV; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ ret = enable ? v4l2_subdev_enable_streams(remote_sd, -+ remote_pad->index, -+ BIT(sink_stream)) : -+ v4l2_subdev_disable_streams(remote_sd, -+ remote_pad->index, -+ BIT(sink_stream)); -+ -+ } else { -+ ret = v4l2_subdev_call(remote_sd, video, s_stream, enable); -+ } -+ -+ if (ret) { -+ dev_err(common->dev, "Failed to %s stream %s %u:%u", -+ enable ? "enable" : "disable", remote_sd->entity.name, -+ remote_pad->index, sink_stream); -+ return ret; -+ } -+ -+ return ret; -+} -+ -+static int _max9x_des_set_stream(struct max9x_common *common, u32 sink_pad, -+ u32 sink_stream, int enable) -+{ -+ u32 rxport; -+ int ret = 0; -+ -+ if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ rxport = sink_pad - common->num_csi_links; -+ if (rxport < 0 || rxport >= common->num_serial_links) { -+ dev_err(common->dev, "Failed to get rxport for pad: %u!", -+ sink_pad); -+ return -EINVAL; -+ } -+ if (enable) -+ max9x_des_s_csi_link(common, rxport, enable); -+ -+ ret = _max9x_s_remote_stream(common, sink_pad, sink_stream, enable); -+ if (ret) { -+ dev_err(common->dev, -+ "Failed to %s remote stream for sink %s %u:%u", -+ enable ? "enable" : "disable", -+ common->v4l.sd.entity.name, sink_pad, sink_stream); -+ return ret; -+ } -+ if (!enable) -+ max9x_des_s_csi_link(common, rxport, enable); -+ -+ return 0; -+} -+ -+static int _max9x_ser_set_stream(struct max9x_common *common, u32 sink_pad, -+ u32 sink_stream, int enable) -+{ -+ return _max9x_s_remote_stream(common, sink_pad, sink_stream, enable); -+} -+ -+static int max9x_streams_mask_to_stream(u64 streams_mask, u32 *stream) -+{ -+ int ret = -EINVAL; -+ -+ for (int i = 0; i < 64; i++) { -+ if (streams_mask & BIT(i)) { -+ *stream = i; -+ ret = 0; -+ break; -+ } -+ } -+ return ret; -+} -+ -+static int max9x_get_corresponding_pad_and_stream(struct v4l2_subdev_state *state, u32 pad, -+ u32 stream, u32 *another_pad, -+ u32 *another_stream) -+{ -+ struct v4l2_subdev_route *route; -+ -+ for_each_active_route(&state->routing, route) { -+ if (route->sink_pad == pad && route->sink_stream == stream) { -+ *another_pad = route->source_pad; -+ *another_stream = route->source_stream; -+ return 0; -+ } else if (route->source_pad == pad && -+ route->source_stream == stream) { -+ *another_pad = route->sink_pad; -+ *another_stream = route->sink_stream; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int _max9x_set_stream(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, u32 pad, -+ u64 streams_mask, int enable) -+{ -+ struct max9x_common *common = max9x_sd_to_common(subdev); -+ u32 sink_pad; -+ u32 sink_stream; -+ u32 source_stream; -+ int ret = 0; -+ -+ ret = max9x_streams_mask_to_stream(streams_mask, &source_stream); -+ if (ret) { -+ dev_err(common->dev, "Failed to get source stream!"); -+ return ret; -+ } -+ -+ ret = max9x_get_corresponding_pad_and_stream(state, pad, source_stream, -+ &sink_pad, &sink_stream); -+ if (ret) { -+ dev_err(common->dev, -+ "Failed to get sink pad and sink stream for %s %u:%u", -+ common->v4l.sd.entity.name, pad, source_stream); -+ return -EINVAL; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ ret = _max9x_des_set_stream(common, sink_pad, sink_stream, -+ enable); -+ else -+ ret = _max9x_ser_set_stream(common, sink_pad, sink_stream, -+ enable); -+ -+ return ret; -+} -+ -+static int max9x_enable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return _max9x_set_stream(subdev, state, pad, streams_mask, true); -+} -+ -+static int max9x_disable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return _max9x_set_stream(subdev, state, pad, streams_mask, false); -+} -+ -+static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ if (IS_ERR_OR_NULL(fmt)) { -+ dev_err(common->dev, "Invalid fmt %p", fmt); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (fmt->pad < 0 || fmt->pad >= common->v4l.num_pads) { -+ dev_err(sd->dev, "%s invalid pad %d", __func__, fmt->pad); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); -+ -+ if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -+ return &common->v4l.ffmts[fmt->pad]; -+ -+ return ERR_PTR(-EINVAL); -+} -+ -+static int max9x_get_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_mbus_framefmt *ffmt; -+ -+ mutex_lock(&v4l->lock); -+ -+ ffmt = __max9x_get_ffmt(sd, v4l2_state, fmt); -+ if (IS_ERR_OR_NULL(ffmt)) { -+ mutex_unlock(&v4l->lock); -+ return -EINVAL; -+ } -+ -+ fmt->format = *ffmt; -+ mutex_unlock(&v4l->lock); -+ -+ dev_info(sd->dev, "framefmt: width: %d, height: %d, code: 0x%x.", -+ fmt->format.width, fmt->format.height, fmt->format.code); -+ -+ return 0; -+} -+ -+static int max9x_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_mbus_framefmt *ffmt; -+ -+ mutex_lock(&v4l->lock); -+ -+ ffmt = __max9x_get_ffmt(sd, v4l2_state, fmt); -+ if (IS_ERR_OR_NULL(ffmt)) { -+ mutex_unlock(&v4l->lock); -+ return -EINVAL; -+ } -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { -+ ffmt->width = fmt->format.width; -+ ffmt->height = fmt->format.height; -+ ffmt->code = fmt->format.code; -+ } -+ fmt->format = *ffmt; -+ -+ mutex_unlock(&v4l->lock); -+ -+ return 0; -+} -+ -+static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -+ struct v4l2_mbus_frame_desc *desc) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ int ret = -EINVAL; -+ -+ if (((common->type != MAX9X_DESERIALIZER) && -+ (common->type != MAX9X_SERIALIZER)) || -+ pad < 0 || pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -+ -+ struct v4l2_subdev_state *state = -+ v4l2_subdev_lock_and_get_active_state(sd); -+ struct v4l2_subdev_route *route; -+ -+ for_each_active_route(&state->routing, route) { -+ if (route->source_pad != pad) -+ continue; -+ if (route->sink_pad >= common->v4l.num_pads) { -+ ret = -EINVAL; -+ dev_err(common->dev, "Found invalid route sink_pad!"); -+ goto out_unlock; -+ } -+ -+ struct media_pad *remote_pad = media_pad_remote_pad_first( -+ &common->v4l.pads[route->sink_pad]); -+ struct v4l2_mbus_frame_desc source_desc; -+ -+ ret = v4l2_subdev_call( -+ media_entity_to_v4l2_subdev(remote_pad->entity), pad, -+ get_frame_desc, remote_pad->index, &source_desc); -+ if (ret) { -+ dev_err(common->dev, -+ "Failed to get sink pad %u remote frame desc!", -+ route->sink_pad); -+ goto out_unlock; -+ } -+ -+ struct v4l2_mbus_frame_desc_entry *source_desc_entry; -+ -+ for (int i = 0; i < source_desc.num_entries; i++) { -+ if (source_desc.entry[i].stream == route->sink_stream) { -+ source_desc_entry = &source_desc.entry[i]; -+ break; -+ } -+ } -+ if (!source_desc_entry) { -+ ret = -EPIPE; -+ dev_err(common->dev, -+ "Failed to get source desc entry for pad: %u, stream: %u", -+ route->sink_pad, route->sink_stream); -+ goto out_unlock; -+ } -+ desc->entry[desc->num_entries].flags = source_desc_entry->flags; -+ desc->entry[desc->num_entries].stream = route->source_stream; -+ desc->entry[desc->num_entries].pixelcode = -+ source_desc_entry->pixelcode; -+ desc->entry[desc->num_entries].length = -+ source_desc_entry->length; -+ if (common->type == MAX9X_DESERIALIZER) -+ desc->entry[desc->num_entries].bus.csi2.vc = -+ route->sink_pad - common->num_csi_links; -+ desc->entry[desc->num_entries].bus.csi2.dt = -+ source_desc_entry->bus.csi2.dt; -+ desc->num_entries++; -+ } -+ -+out_unlock: -+ v4l2_subdev_unlock_state(state); -+ return ret; -+} -+ -+static int max9x_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ if (mbus_code_to_csi_dt(code->code) < 0) -+ return -EINVAL; -+ return 0; -+} -+ -+static int _max9x_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_krouting *routing) -+{ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1536, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ }; -+ int ret; -+ -+ /* -+ * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until -+ * frame desc is made dynamically allocated. -+ */ -+ -+ if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) -+ return -E2BIG; -+ -+ ret = v4l2_subdev_routing_validate(sd, routing, -+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | -+ V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); -+ if (ret) -+ return ret; -+ -+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static int max9x_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing) -+{ -+ _max9x_set_routing(sd, state, routing); -+ -+ return 0; -+} -+ -+static int max9x_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ struct v4l2_subdev_route des_routes[] = { -+ { -+ .sink_pad = 5, -+ .sink_stream = 0, -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }, -+ }; -+ struct v4l2_subdev_route ser_routes[] = { -+ { -+ .sink_pad = 0, -+ .sink_stream = 0, -+ .source_pad = 2, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }, -+ }; -+ struct v4l2_subdev_krouting des_routing = { -+ .num_routes = ARRAY_SIZE(des_routes), -+ .routes = des_routes, -+ }; -+ struct v4l2_subdev_krouting ser_routing = { -+ .num_routes = ARRAY_SIZE(ser_routes), -+ .routes = ser_routes, -+ }; -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ return _max9x_set_routing(sd, state, &des_routing); -+ else -+ return _max9x_set_routing(sd, state, &ser_routing); -+} -+ -+static const struct v4l2_subdev_pad_ops max9x_sd_pad_ops = { -+ .get_fmt = max9x_get_fmt, -+ .set_fmt = max9x_set_fmt, -+ .get_frame_desc = max9x_get_frame_desc, -+ .enum_mbus_code = max9x_enum_mbus_code, -+ .set_routing = max9x_set_routing, -+ .enable_streams = max9x_enable_streams, -+ .disable_streams = max9x_disable_streams, -+}; -+ -+static struct v4l2_subdev_ops max9x_sd_ops = { -+ .pad = &max9x_sd_pad_ops, -+}; -+ -+static int max9x_registered(struct v4l2_subdev *sd) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct device *dev = common->dev; -+ int ret; -+ -+ for (unsigned int link_id = 0; link_id < common->num_serial_links; link_id++) { -+ if (!common->serial_link[link_id].enabled) { -+ dev_dbg(dev, "Serial-link %d not enabled, skipping subdevs", link_id); -+ continue; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ struct max9x_subdev_pdata *subdev_pdata = -+ common->serial_link[link_id].remote.pdata; -+ -+ if (subdev_pdata) { -+ struct max9x_pdata *ser_pdata = -+ subdev_pdata->board_info.platform_data; -+ -+ WARN_ON(ser_pdata->num_serial_links < 1); -+ -+ ser_pdata->serial_links[0].des_client = common->client; -+ ser_pdata->serial_links[0].des_link_id = link_id; -+ /* -+ * Isolate this link until after reset and potential address remapping, -+ * avoiding a race condition with two serializers resetting same -+ * physical i2c at the same time -+ */ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); -+ -+ ret = max9x_des_deisolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ if (IS_ERR_OR_NULL(subdev)) { -+ dev_err(dev, "Failure registering serializer %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ return PTR_ERR(subdev); -+ } -+ -+ dev_dbg(dev, "Registered serializer %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ -+ struct max9x_common *ser_common = max9x_sd_to_common(subdev); -+ -+ int remote_pad = max9x_serial_link_to_pad(ser_common, 0); -+ int local_pad = max9x_serial_link_to_pad(common, link_id); -+ -+ dev_dbg(dev, "Create link from ser link 0 (pad %d) -> des link %d (pad %d)", -+ remote_pad, link_id, local_pad); -+ -+ ret = media_create_pad_link(&subdev->entity, remote_pad, -+ &sd->entity, local_pad, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) { -+ dev_err(dev, "Failed creating pad link to serializer"); -+ return ret; -+ } -+ -+ common->serial_link[link_id].remote.client = ser_common->client; -+ } -+ } else { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ -+ if (subdev_pdata->serial_link_id == link_id) { -+ char dev_id[I2C_NAME_SIZE]; -+ -+ snprintf(dev_id, sizeof(dev_id), "i2c-%s", -+ subdev_pdata->board_info.dev_name); -+ -+ dev_dbg(dev, "Registering sensor %s (%s)...", -+ subdev_pdata->board_info.type, dev_id); -+ -+ static struct gpiod_lookup_table sensor_gpios = { -+ .dev_id = "", -+ .table = { -+ GPIO_LOOKUP("", 0, "reset", -+ GPIO_ACTIVE_HIGH), -+ {} -+ }, -+ }; -+ -+ sensor_gpios.dev_id = dev_id; -+ sensor_gpios.table[0].key = common->gpio_chip.label; -+ -+ gpiod_add_lookup_table(&sensor_gpios); -+ -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); -+ -+ gpiod_remove_lookup_table(&sensor_gpios); -+ -+ if (IS_ERR_OR_NULL(subdev)) { -+ dev_err(dev, -+ "Failure registering sensor %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ return PTR_ERR(subdev); -+ } -+ -+ dev_dbg(dev, "Registered sensor %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ -+ int remote_pad = media_get_pad_index(&subdev->entity, -+ MEDIA_PAD_FL_SOURCE, -+ PAD_SIGNAL_DEFAULT); -+ int local_pad = max9x_csi_link_to_pad(common, 0); -+ -+ dev_dbg(dev, "Create link from sen pad %d -> ser link %d (pad %d)", -+ remote_pad, link_id, -+ local_pad); -+ -+ ret = media_create_pad_link(&subdev->entity, remote_pad, -+ &sd->entity, local_pad, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) { -+ dev_err(dev, "Failed creating pad link to serializer"); -+ return ret; -+ } -+ -+ } -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static struct v4l2_subdev_internal_ops max9x_sd_internal_ops = { -+ .registered = max9x_registered, -+ .init_state = max9x_init_state, -+}; -+ -+static int max9x_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct max9x_serdes_v4l *v4l = container_of(ctrl->handler, struct max9x_serdes_v4l, ctrl_handler); -+ struct max9x_common *common = container_of(v4l, struct max9x_common, v4l); -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "s_ctrl"); -+ -+ switch (ctrl->id) { -+ case V4L2_CID_LINK_FREQ: { -+ if (ctrl->p_new.p_u8) { -+ if (*ctrl->p_new.p_u8 <= (ARRAY_SIZE(max9x_op_sys_clock) - 1)) { -+ unsigned int csi_link_id; -+ struct v4l2_ctrl *link_freq = v4l->link_freq; -+ -+ dev_info(dev, "user-modified %s index val=%d to user-val=%d", -+ ctrl->name, (unsigned int)link_freq->val, -+ (unsigned int)*ctrl->p_new.p_u8); -+ -+ link_freq->val = (s32) *ctrl->p_new.p_u8; -+ -+ for (csi_link_id = 0; csi_link_id < common->num_csi_links; csi_link_id++) { -+ if (!common->csi_link[csi_link_id].enabled) -+ continue; -+ -+ common->csi_link[csi_link_id].config.freq_mhz = -+ MAX9X_LINK_FREQ_HZ_TO_MBPS(max9x_op_sys_clock[link_freq->val]); -+ -+ if (common->csi_link[csi_link_id].config.freq_mhz > 1500) { -+ common->csi_link[csi_link_id].config.auto_init_deskew_enabled = true; -+ common->csi_link[csi_link_id].config.initial_deskew_width = 7; -+ } -+ } -+ } -+ } -+ } -+ break; -+ -+ default: -+ dev_info(dev, "unknown control id: 0x%X", ctrl->id); -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops max9x_ctrl_ops = { -+ .s_ctrl = max9x_s_ctrl, -+}; -+ -+static struct v4l2_ctrl_config max9x_v4l2_controls[] = { -+ { -+ .ops = &max9x_ctrl_ops, -+ .id = V4L2_CID_LINK_FREQ, -+ .name = "V4L2_CID_LINK_FREQ", -+ .type = V4L2_CTRL_TYPE_INTEGER_MENU, -+ .min = 0, -+ .max = ARRAY_SIZE(max9x_op_sys_clock) - 1, -+ .def = 2, -+ .menu_skip_mask = 0, -+ .qmenu_int = max9x_op_sys_clock, -+ }, -+}; -+ -+static int max9x_csi_link_to_pad(struct max9x_common *common, int csi_id) -+{ -+ return (csi_id < common->num_csi_links) ? csi_id : -EINVAL; -+} -+ -+static int max9x_serial_link_to_pad(struct max9x_common *common, int link_id) -+{ -+ dev_dbg(common->dev, "link_id %d < num_serial_links %d ? num_csi_links %d + link_id %d : -EINVAL", -+ link_id, common->num_serial_links, common->num_csi_links, link_id); -+ return (link_id < common->num_serial_links) ? common->num_csi_links + link_id : -EINVAL; -+} -+ -+static int max9x_register_v4l_subdev(struct max9x_common *common) -+{ -+ struct i2c_client *client = common->client; -+ struct device *dev = common->dev; -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_subdev *sd = &v4l->sd; -+ struct v4l2_ctrl_handler *ctrl_handler = &v4l->ctrl_handler; -+ struct max9x_pdata *pdata = dev->platform_data; -+ int ret; -+ -+ mutex_init(&v4l->lock); -+ -+ v4l2_i2c_subdev_init(sd, client, &max9x_sd_ops); -+ snprintf(sd->name, sizeof(sd->name), "%s %s", client->name, pdata->suffix); -+ -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; -+ sd->internal_ops = &max9x_sd_internal_ops; -+ sd->entity.function = MEDIA_ENT_F_VID_MUX; -+ -+ v4l->num_pads = common->num_csi_links + common->num_serial_links; -+ v4l->pads = devm_kzalloc(dev, v4l->num_pads * sizeof(*v4l->pads), GFP_KERNEL); -+ v4l->ffmts = devm_kzalloc(dev, v4l->num_pads * sizeof(*v4l->ffmts), GFP_KERNEL); -+ -+ /* change sink/source turn */ -+ for (unsigned int p = 0; p < v4l->num_pads; p++) { -+ struct media_pad *pad = &v4l->pads[p]; -+ -+ if (p < common->num_csi_links) { -+ unsigned int c = p; -+ -+ dev_dbg(dev, "pad %d/%d -> csi %d", p, v4l->num_pads, c); -+ -+ pad->flags = (common->type == MAX9X_SERIALIZER) ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; -+ } else if (p >= common->num_csi_links) { -+ unsigned int s = p - common->num_csi_links; -+ -+ dev_dbg(dev, "pad %d/%d -> serial %d", p, v4l->num_pads, s); -+ -+ pad->flags = (common->type == MAX9X_SERIALIZER) ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; -+ } -+ } -+ -+ ret = v4l2_ctrl_handler_init(ctrl_handler, ARRAY_SIZE(max9x_v4l2_controls)); -+ if (ret) { -+ dev_err(dev, "Failed to init V4L2 controls: %d", ret); -+ return ret; -+ } -+ -+ for (int i = 0; i < ARRAY_SIZE(max9x_v4l2_controls); i++) { -+ struct v4l2_ctrl_config *ctrl_config = &max9x_v4l2_controls[i]; -+ struct v4l2_ctrl *ctrl; -+ -+ if (ctrl_config->id == V4L2_CID_LINK_FREQ) { -+ unsigned int link_freq_n; -+ unsigned int csi_link_id; -+ -+ for (link_freq_n = 0; link_freq_n < ARRAY_SIZE(max9x_op_sys_clock); link_freq_n++) { -+ unsigned int link_freq = MAX9X_LINK_FREQ_HZ_TO_MBPS(max9x_op_sys_clock[link_freq_n]); -+ -+ for (csi_link_id = 0; csi_link_id < common->num_csi_links; csi_link_id++) { -+ if (!common->csi_link[csi_link_id].enabled) -+ continue; -+ if (common->csi_link[csi_link_id].config.freq_mhz == link_freq) { -+ ctrl_config->def = link_freq_n; -+ break; -+ } -+ } -+ } -+ } -+ -+ ctrl = v4l2_ctrl_new_custom(ctrl_handler, ctrl_config, common); -+ if (!ctrl) { -+ ret = ctrl_handler->error; -+ dev_err(dev, "Failed to create V4L2 control %s: %d", ctrl_config->name, ret); -+ goto probe_error_v4l2_ctrl_handler_free; -+ } -+ } -+ -+ v4l->link_freq = v4l2_ctrl_find(ctrl_handler, V4L2_CID_LINK_FREQ); -+ v4l->sd.ctrl_handler = ctrl_handler; -+ -+ ret = media_entity_pads_init(&v4l->sd.entity, v4l->num_pads, v4l->pads); -+ if (ret) { -+ dev_err(dev, "Failed to init media entity: %d", ret); -+ goto probe_error_v4l2_ctrl_handler_free; -+ } -+ -+ ret = v4l2_subdev_init_finalize(&v4l->sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); -+ media_entity_cleanup(&v4l->sd.entity); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ ret = v4l2_async_register_subdev(&v4l->sd); -+ if (ret) { -+ dev_err(dev, "v4l register failed: %d", ret); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ return 0; -+ -+probe_error_media_entity_cleanup: -+ media_entity_cleanup(&v4l->sd.entity); -+probe_error_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(ctrl_handler); -+ return ret; -+} -+ -+/* -+ * max9x_enable_serial_link() - Enable power and logic to link. -+ */ -+int max9x_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct max9x_serdes_serial_link *serial_link; -+ struct device *dev = common->dev; -+ int ret; -+ -+ serial_link = &common->serial_link[link_id]; -+ -+ if (!serial_link->regulator_enabled) { -+ if (!IS_ERR_OR_NULL(serial_link->poc_regulator)) { -+ ret = regulator_enable(serial_link->poc_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_POC_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ -+ serial_link->regulator_enabled = true; -+ } -+ -+ if (common->serial_link_ops && common->serial_link_ops->enable) -+ return common->serial_link_ops->enable(common, link_id); -+ -+ return 0; -+} -+ -+/* -+ * max9x_disable_serial_link() - Disable power and logic to link. -+ */ -+int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct max9x_serdes_serial_link *serial_link; -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (link_id >= common->num_serial_links) -+ return 0; -+ -+ serial_link = &common->serial_link[link_id]; -+ -+ if (serial_link->regulator_enabled) { -+ if (common->serial_link_ops && common->serial_link_ops->disable) -+ common->serial_link_ops->disable(common, link_id); -+ -+ serial_link->regulator_enabled = false; -+ -+ if (!IS_ERR_OR_NULL(serial_link->poc_regulator)) { -+ ret = regulator_disable(serial_link->poc_regulator); -+ -+ if (ret) { -+ dev_err(dev, "Failed to disable %s", MAX9X_POC_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * max9x_sysfs_create_get_link() - Creates a sysfs virtual file to check link lock status -+ */ -+int max9x_sysfs_create_get_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ char *attr_name; -+ -+ if (common->serial_link_ops && common->serial_link_ops->get_locked) { -+ struct device_attribute *link_lock_status = -+ devm_kzalloc(dev, sizeof(struct device_attribute), GFP_KERNEL); -+ -+ if (!link_lock_status) { -+ dev_err(dev, "Failed to allocate memory for link lock status"); -+ return -ENOMEM; -+ } -+ -+ attr_name = (char *)devm_kzalloc(dev, sizeof(char) * ATTR_NAME_LEN, GFP_KERNEL); -+ if (!attr_name) { -+ dev_err(dev, "Failed to allocate memory link lock attribute name"); -+ return -ENOMEM; -+ } -+ -+ ret = snprintf(attr_name, ATTR_NAME_LEN, "link-lock_%d", link_id); -+ if (ret < 0) -+ return ret; -+ -+ link_lock_status->attr.name = attr_name; -+ link_lock_status->attr.mode = ATTR_READ_ONLY; -+ link_lock_status->show = max9x_link_status_show; -+ -+ ret = device_create_file(dev, link_lock_status); -+ if (ret < 0) -+ return ret; -+ -+ common->serial_link[link_id].link_lock_status = link_lock_status; -+ } -+ -+ return 0; -+} -+ -+/* -+ * max9x_sysfs_destroy_get_link() - Destroys the sysfs device attribute for link lock status -+ */ -+static void max9x_sysfs_destroy_get_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ -+ if (common->serial_link[link_id].link_lock_status) -+ device_remove_file(dev, common->serial_link[link_id].link_lock_status); -+} -+ -+/* -+ * max9x_enable_line_faults() - Enables all the line fault monitors using the device tree -+ */ -+int max9x_enable_line_faults(struct max9x_common *common) -+{ -+ return 0; -+} -+ -+/* -+ * max9x_disable_line_faults() - Disables all currently stored line fault monitors -+ */ -+int max9x_disable_line_faults(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ int line; -+ int final_ret = 0; -+ -+ if (common->line_fault && -+ common->line_fault_ops && -+ common->line_fault_ops->disable) { -+ -+ for (line = 0; line < common->num_line_faults; line++) { -+ ret = common->line_fault_ops->disable(common, line); -+ -+ if (ret) { -+ dev_err(dev, "Failed to disable line fault %d", line); -+ final_ret = ret; -+ } -+ -+ common->line_fault[line].enabled = false; -+ } -+ } -+ -+ return final_ret; -+} -+ -+static void max9x_sysfs_destroy_line_fault_status(struct max9x_common *common, unsigned int line) -+{ -+ struct device *dev = common->dev; -+ -+ if (common->line_fault[line].line_fault_status) -+ device_remove_file(dev, common->line_fault[line].line_fault_status); -+} -+ -+static int max9x_parse_pdata(struct max9x_common *common, struct max9x_pdata *pdata) -+{ -+ int ret; -+ -+ for (int i = 0; i < pdata->num_serial_links; i++) { -+ ret = max9x_parse_serial_link_pdata(common, &pdata->serial_links[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_video_pipes; i++) { -+ ret = max9x_parse_video_pipe_pdata(common, &pdata->video_pipes[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_csi_links; i++) { -+ ret = max9x_parse_csi_link_pdata(common, &pdata->csi_links[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_subdevs; i++) { -+ ret = max9x_parse_subdev_pdata(common, &pdata->subdevs[i]); -+ if (ret) -+ goto err; -+ } -+ -+ common->external_refclk_enable = pdata->external_refclk_enable; -+ -+err: -+ return ret; -+} -+ -+static int max9x_parse_serial_link_pdata(struct max9x_common *common, -+ struct max9x_serial_link_pdata *serial_link_pdata) -+{ -+ struct device *dev = common->dev; -+ unsigned int serial_link_id = serial_link_pdata->link_id; -+ -+ if (serial_link_id > common->num_serial_links) { -+ dev_err(dev, "Serial link pdata: Invalid link id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[serial_link_id]; -+ -+ serial_link->enabled = true; -+ -+ serial_link->config.link_type = serial_link_pdata->link_type; -+ serial_link->config.rx_freq_mhz = serial_link_pdata->rx_freq_mhz; -+ serial_link->config.tx_freq_mhz = serial_link_pdata->tx_freq_mhz; -+ -+ if (serial_link_pdata->poc_regulator[0] != 0) { -+ serial_link->poc_regulator = devm_regulator_get_optional(dev, serial_link_pdata->poc_regulator); -+ -+ if (PTR_ERR(serial_link->poc_regulator) == -EPROBE_DEFER) { -+ dev_dbg(dev, "POC regulator not ready deferring..."); -+ return -EPROBE_DEFER; -+ } -+ if (IS_ERR_OR_NULL(serial_link->poc_regulator)) -+ dev_dbg(dev, "Missing POC regulator"); -+ } -+ -+ return 0; -+} -+ -+static int max9x_parse_video_pipe_pdata(struct max9x_common *common, -+ struct max9x_video_pipe_pdata *video_pipe_pdata) -+{ -+ struct device *dev = common->dev; -+ unsigned int serial_link_id = video_pipe_pdata->serial_link_id; -+ unsigned int pipe_id = video_pipe_pdata->pipe_id; -+ unsigned int max_maps; -+ unsigned int max_data_types; -+ -+ if (serial_link_id > common->num_serial_links) { -+ dev_err(dev, "Video pdata: Invalid serial link id"); -+ return -EINVAL; -+ } -+ if (pipe_id > common->num_video_pipes) { -+ dev_err(dev, "Video pdata: Invalid video pipe id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_video_pipe *pipe = &common->video_pipe[pipe_id]; -+ -+ pipe->enabled = true; -+ -+ max_maps = 0; -+ max_data_types = 0; -+ if (common->common_ops && common->common_ops->max_elements) { -+ max_maps = common->common_ops->max_elements(common, MAX9X_MIPI_MAP); -+ max_data_types = common->common_ops->max_elements(common, MAX9X_DATA_TYPES); -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ if (video_pipe_pdata->num_maps > max_maps) { -+ dev_err(dev, "Video pdata: Too many maps"); -+ return -EINVAL; -+ } -+ -+ pipe->config.map = devm_kzalloc(dev, -+ video_pipe_pdata->num_maps * sizeof(*pipe->config.map), -+ GFP_KERNEL); -+ if (!pipe->config.map) { -+ dev_err(dev, "Video pdata: Failed t oallocate mmeory for maps"); -+ return -ENOMEM; -+ } -+ -+ pipe->config.src_link = video_pipe_pdata->serial_link_id; -+ pipe->config.src_pipe = video_pipe_pdata->src_pipe_id; -+ -+ for (unsigned int i = 0; i < video_pipe_pdata->num_maps; i++) { -+ struct max9x_serdes_mipi_map *map = &pipe->config.map[i]; -+ struct max9x_serdes_mipi_map *map_pdata = &video_pipe_pdata->maps[i]; -+ -+ map->src_vc = map_pdata->src_vc; -+ map->src_dt = map_pdata->src_dt; -+ map->dst_vc = map_pdata->dst_vc; -+ map->dst_dt = map_pdata->dst_dt; -+ map->dst_csi = map_pdata->dst_csi; -+ } -+ pipe->config.num_maps = video_pipe_pdata->num_maps; -+ } else if (common->type == MAX9X_SERIALIZER) { -+ if (video_pipe_pdata->num_data_types > max_data_types) { -+ dev_err(dev, "Video pdata: Too many maps"); -+ return -EINVAL; -+ } -+ -+ pipe->config.data_type = devm_kzalloc(dev, -+ video_pipe_pdata->num_data_types * sizeof(*pipe->config.map), -+ GFP_KERNEL); -+ if (!pipe->config.data_type) { -+ dev_err(dev, "Video pdata: Failed t oallocate mmeory for data types"); -+ return -ENOMEM; -+ } -+ -+ pipe->config.src_csi = video_pipe_pdata->src_csi_id; -+ -+ for (unsigned int i = 0; i < video_pipe_pdata->num_data_types; i++) { -+ pipe->config.data_type[i] = video_pipe_pdata->data_types[i]; -+ } -+ pipe->config.num_data_types = video_pipe_pdata->num_data_types; -+ } -+ -+ return 0; -+} -+ -+static int max9x_parse_csi_link_pdata(struct max9x_common *common, -+ struct max9x_csi_link_pdata *csi_link_pdata) -+{ -+ unsigned int csi_link_id = csi_link_pdata->link_id; -+ -+ if (csi_link_id > common->num_csi_links) { -+ dev_err(common->dev, "CSI link pdata: Invalid link id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_csi_link *csi_link = &common->csi_link[csi_link_id]; -+ -+ csi_link->enabled = true; -+ -+ csi_link->config.num_maps = csi_link_pdata->num_maps; -+ csi_link->config.map = -+ devm_kzalloc(common->dev, -+ csi_link->config.num_maps * sizeof(*csi_link->config.map), -+ GFP_KERNEL); -+ memcpy(csi_link->config.map, csi_link_pdata->maps, -+ csi_link->config.num_maps * sizeof(*csi_link->config.map)); -+ csi_link->config.bus_type = csi_link_pdata->bus_type; -+ csi_link->config.num_lanes = csi_link_pdata->num_lanes; -+ csi_link->config.freq_mhz = csi_link_pdata->tx_rate_mbps; -+ csi_link->config.auto_init_deskew_enabled = csi_link_pdata->auto_initial_deskew; -+ -+ if (csi_link->config.auto_init_deskew_enabled) -+ csi_link->config.initial_deskew_width = csi_link_pdata->initial_deskew_width; -+ csi_link->config.auto_start = csi_link_pdata->auto_start; -+ -+ return 0; -+} -+ -+static int max9x_parse_subdev_pdata(struct max9x_common *common, -+ struct max9x_subdev_pdata *subdev_pdata) -+{ -+ unsigned int serial_link_id = subdev_pdata->serial_link_id; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[serial_link_id]; -+ -+ if (!serial_link->enabled) -+ return 0; -+ -+ serial_link->remote.pdata = subdev_pdata; -+ -+ return 0; -+} -+ -+int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) -+{ -+ struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; -+ int ret = 0; -+ unsigned long timeout = jiffies + msecs_to_jiffies(10000); -+ -+ dev_dbg(common->dev, "try to select %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ -+ if (unlikely(chan_id > common->num_serial_links)) -+ return -EINVAL; -+ -+ do { -+ mutex_lock(&common->isolate_mutex); -+ if (common->selected_link < 0 || chan_id == common->selected_link) -+ break; -+ -+ mutex_unlock(&common->isolate_mutex); -+ -+ usleep_range(1000, 1050); -+ -+ if (time_is_before_jiffies(timeout)) { -+ dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ return -ETIMEDOUT; -+ } -+ } while (1); -+ -+ common->selected_link = chan_id; -+ -+ if (common->serial_link_ops && common->serial_link_ops->select) -+ ret = common->serial_link_ops->select(common, chan_id); -+ -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) -+{ -+ struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; -+ int ret = 0; -+ -+ dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ -+ if (unlikely(chan_id > common->num_serial_links)) -+ return -EINVAL; -+ -+ mutex_lock(&common->isolate_mutex); -+ if (common->serial_link_ops && common->serial_link_ops->deselect) -+ ret = common->serial_link_ops->deselect(common, chan_id); -+ -+ common->selected_link = -1; -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+int max9x_des_isolate_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret = 0; -+ unsigned long timeout = jiffies + msecs_to_jiffies(10000); -+ -+ if (link_id >= common->num_serial_links) { -+ dev_err(common->dev, "link_id %d outside of num_serial_links %d", link_id, common->num_serial_links); -+ return -EINVAL; -+ } -+ -+ dev_info(common->dev, "Isolate %d", link_id); -+ -+ do { -+ mutex_lock(&common->isolate_mutex); -+ if ((common->isolated_link < 0) && (common->selected_link < 0 || link_id == common->selected_link)) -+ break; -+ -+ if (common->isolated_link == link_id) { -+ dev_warn(common->dev, "Link %d is already isolated", link_id); -+ mutex_unlock(&common->isolate_mutex); -+ return -EINVAL; -+ } -+ mutex_unlock(&common->isolate_mutex); -+ -+ usleep_range(1000, 1050); -+ -+ if (time_is_before_jiffies(timeout)) -+ return -ETIMEDOUT; -+ } while (1); -+ -+ common->isolated_link = link_id; -+ if (common->serial_link_ops && common->serial_link_ops->isolate) -+ ret = common->serial_link_ops->isolate(common, link_id); -+ -+ mutex_unlock(&common->isolate_mutex); -+ dev_info(common->dev, "Isolate %d complete", link_id); -+ -+ return ret; -+} -+ -+int max9x_des_deisolate_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret = 0; -+ -+ if (link_id >= common->num_serial_links) -+ return -EINVAL; -+ -+ dev_info(common->dev, "Deisolate %d", link_id); -+ -+ mutex_lock(&common->isolate_mutex); -+ if (common->serial_link_ops && common->serial_link_ops->deisolate) -+ ret = common->serial_link_ops->deisolate(common, link_id); -+ -+ common->isolated_link = -1; -+ dev_info(common->dev, "Deisolate %d complete", link_id); -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+ssize_t max9x_link_status_show(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct max9x_common *common = dev_get_drvdata(dev); -+ int link; -+ int ret; -+ bool locked; -+ -+ ret = sscanf(attr->attr.name, "link-lock_%d", &link); -+ if (ret < 0) -+ return ret; -+ -+ if (common->serial_link_ops && common->serial_link_ops->get_locked) { -+ ret = common->serial_link_ops->get_locked(common, link, &locked); -+ if (ret < 0) -+ return ret; -+ -+ return sysfs_emit(buf, "%d", !!locked); -+ } -+ -+ dev_err(dev, "get_locked not defined"); -+ return -EINVAL; -+} -+ -+int max9x_setup_translations(struct max9x_common *common) -+{ -+ int err = 0; -+ -+ if (!common->translation_ops) -+ return 0; -+ -+ /* -+ * Translation is currently only supported on serializer side -+ * (translating requests from SOC to remote sensor module) -+ */ -+ if (common->type != MAX9X_SERIALIZER) -+ return 0; -+ -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ unsigned int virt_addr = subdev_pdata->board_info.addr; -+ unsigned int phys_addr = subdev_pdata->phys_addr ? subdev_pdata->phys_addr : virt_addr; -+ -+ if (virt_addr == phys_addr || common->translation_ops->add == NULL) -+ continue; -+ -+ /* Only I2C 1 is supported at this time */ -+ err = common->translation_ops->add(common, 0, virt_addr, phys_addr); -+ if (err) -+ dev_warn(common->dev, "Failed to add translation for i2c address 0x%02x -> 0x%02x: %d", -+ virt_addr, phys_addr, err); -+ break; -+ } -+ -+ msleep(10); -+ -+ return err; -+} -+ -+int max9x_disable_translations(struct max9x_common *common) -+{ -+ int err = 0; -+ -+ if (!common->translation_ops) -+ return 0; -+ -+ /* -+ * Translation is currently only supported on serializer side -+ * (translating requests from SOC to remote sensor module) -+ */ -+ if (common->type != MAX9X_SERIALIZER) -+ return 0; -+ -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ unsigned int virt_addr = subdev_pdata->board_info.addr; -+ unsigned int phys_addr = subdev_pdata->phys_addr ? subdev_pdata->phys_addr : virt_addr; -+ -+ if (virt_addr != phys_addr) { -+ if (common->translation_ops->remove) { -+ /* Only I2C 1 is supported at this time */ -+ err = common->translation_ops->remove(common, 0, virt_addr, phys_addr); -+ if (err) -+ dev_err(common->dev, "Failed to remove translation for i2c address 0x%02x -> 0x%02x: %d", -+ virt_addr, phys_addr, err); -+ break; -+ } -+ } -+ } -+ -+ return err; -+} -+ -+static int max9x_des_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *des = NULL; -+ int ret = 0; -+ -+ dev_info(dev, "Probing"); -+ -+ des = devm_kzalloc(dev, sizeof(*des), GFP_KERNEL); -+ if (!des) { -+ dev_err(dev, "Failed to allocate memory."); -+ return -ENOMEM; -+ } -+ -+ TRY(ret, max9x_common_init_i2c_client( -+ des, -+ client, -+ &max9x_regmap_config, -+ NULL, NULL, NULL, NULL)); -+ -+ dev_info(dev, "probe successful"); -+ return 0; -+} -+ -+static void max9x_des_remove(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *des = NULL; -+ -+ dev_info(dev, "Removing"); -+ -+ des = max9x_client_to_common(client); -+ -+ max9x_destroy(des); -+ -+} -+ -+static int max9x_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_resume(common); -+} -+ -+static int max9x_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_suspend(common); -+} -+ -+static const struct dev_pm_ops max9x_des_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(max9x_suspend, max9x_resume) -+}; -+ -+static struct i2c_driver max9x_driver = { -+ .driver = { -+ .name = "max9x", -+ .of_match_table = max9x_of_match, -+ .pm = &max9x_des_pm_ops, -+ }, -+ .probe = max9x_des_probe, -+ .remove = max9x_des_remove, -+ .id_table = max9x_id, -+}; -+ -+module_i2c_driver(max9x_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Yan, Dongcheng "); -+MODULE_DESCRIPTION("Common logic for Maxim GMSL serializers & deserializers"); -diff --git a/drivers/media/i2c/max9x/serdes.h b/drivers/media/i2c/max9x/serdes.h -new file mode 100644 -index 000000000000..227f61e03229 ---- /dev/null -+++ b/drivers/media/i2c/max9x/serdes.h -@@ -0,0 +1,460 @@ -+/* -+ * serdes.h -+ * -+ * Copyright (c) 2018-2020 D3 Engineering. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _SERDES_H_ -+#define _SERDES_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) -+#include "ipu6-isys.h" -+#endif -+#include -+#include "max9x_pdata.h" -+ -+#define MAX9X_VDD_REGULATOR_NAME "vdd" -+#define MAX9X_POC_REGULATOR_NAME "poc" -+#define MAX9X_RESET_GPIO_NAME "reset" -+#define MAX9X_DEV_ID 0xD -+#define MAX9X_DEV_REV_FIELD GENMASK(3, 0) -+ -+/*Used for device attributes*/ -+#define ATTR_NAME_LEN (30) /* arbitrary number used to allocate an attribute */ -+#define ATTR_READ_ONLY (0444) -+ -+#define MAX9X_LINK_FREQ_MBPS_TO_HZ(mbps) (((unsigned long long)(mbps)*1000000ULL)/2ULL) -+#define MAX9X_LINK_FREQ_HZ_TO_MBPS(hz) (((unsigned long long)(hz)*2ULL)/1000000ULL) -+#define MAX9X_LINK_FREQ_MBPS_TO_REG(mbps) ((mbps)/100U) -+ -+#define MAX9X_FIELD_PREP(_mask, _val) \ -+ ({ \ -+ ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ -+ }) -+ -+#define TRY(err, expr) \ -+ do {\ -+ err = expr; \ -+ if (err) { \ -+ return err; \ -+ } \ -+ } while (0) -+ -+#define TRY_DEV_HERE(err, expr, dev) \ -+ do { \ -+ err = expr; \ -+ if (err) { \ -+ dev_err((dev), \ -+ "%s failed (%d) in %s() at %s:%d", #expr, \ -+ (err), __func__, __FILE__, __LINE__); \ -+ return err; \ -+ } \ -+ } while (0) -+ -+/* dt is defined in soft_dt_x, such as BACKTOP15(0x316).[5:0] */ -+#define MIPI_CSI2_TYPE_YUV422_8 0x1e -+#define MIPI_CSI2_TYPE_YUV422_10 0x1f -+#define MIPI_CSI2_TYPE_RGB565 0x22 -+#define MIPI_CSI2_TYPE_RGB888 0x24 -+#define MIPI_CSI2_TYPE_RAW8 0x2a -+#define MIPI_CSI2_TYPE_RAW10 0x2b -+#define MIPI_CSI2_TYPE_RAW12 0x2c -+ -+static inline int mbus_code_to_csi_dt(int code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return MIPI_CSI2_TYPE_RGB565; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return MIPI_CSI2_TYPE_RGB888; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return MIPI_CSI2_TYPE_YUV422_10; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ case MEDIA_BUS_FMT_VYUY8_1X16: -+ return MIPI_CSI2_TYPE_YUV422_8; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return MIPI_CSI2_TYPE_RAW12; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return MIPI_CSI2_TYPE_RAW10; -+ case MEDIA_BUS_FMT_Y8_1X8: -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return MIPI_CSI2_TYPE_RAW8; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct regmap_config max9x_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+}; -+ -+enum max9x_chip_type { -+ MAX9296 = 0, -+ MAX96724 = 1, -+ MAX9295, -+ MAX96717, -+ MAX96724F, -+ MAX96724R, -+}; -+ -+enum max9x_serdes_type { -+ MAX9X_DESERIALIZER = 0, -+ MAX9X_SERIALIZER = 1, -+}; -+ -+struct max9x_serdes_rate_table { -+ unsigned int val; -+ unsigned int freq_mhz; -+}; -+ -+struct max9x_serdes_csi_config { -+ struct max9x_serdes_phy_map *map; -+ unsigned int num_maps; -+ enum v4l2_mbus_type bus_type; -+ unsigned int num_lanes; -+ unsigned int freq_mhz; -+ unsigned int initial_deskew_width; -+ bool auto_init_deskew_enabled; -+ bool auto_start; -+}; -+ -+struct max9x_serdes_pipe_config { -+ // Deserializer -+ unsigned int src_link; -+ unsigned int src_pipe; -+ struct max9x_serdes_mipi_map *map; -+ unsigned int num_maps; -+ -+ // Serializer -+ //TODO: dst_link? -+ unsigned int src_csi; -+ unsigned int *data_type; -+ unsigned int num_data_types; -+ //TODO: MIPI VC filter mask -+ -+ /* -+ * Which bpp value to double i.e. dbl_pixel_bpp = 10 will -+ * cause 10 bit data to be transmitted together. -+ */ -+ //TODO: Multiple dbl bpp values -+ unsigned int dbl_pixel_bpp; -+ -+ /* -+ * Software override for bits per pixel. This is compatible with -+ * double bpp. These are the min/max values before padding -+ * and after doubling. Leave either 0 to disable. -+ */ -+ unsigned int soft_min_pixel_bpp; -+ unsigned int soft_max_pixel_bpp; -+ -+ //TODO: MIPI DT override -+}; -+ -+struct max9x_serdes_serial_config { -+ enum max9x_serdes_link_type link_type; -+ unsigned int rx_freq_mhz; // Previously forward_freq_mhz -+ unsigned int tx_freq_mhz; // Previously back_freq_mhz -+}; -+ -+struct max9x_serdes_v4l { -+ struct mutex lock; -+ struct max9x_common *common; -+ struct v4l2_subdev sd; -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct media_pad *pads; -+ int num_pads; -+ -+ struct v4l2_ctrl *link_freq; // CSI link frequency, used to determine ISP clock -+ struct v4l2_mbus_framefmt *ffmts; -+ int ref_count; -+}; -+ -+struct max9x_serdes_csi_link { -+ bool enabled; -+ unsigned int usecount; -+ struct max9x_serdes_csi_config config; -+}; -+ -+struct max9x_serdes_video_pipe { -+ bool enabled; -+ //TODO: Anything else need to be tracked? -+ struct max9x_serdes_pipe_config config; -+}; -+ -+struct max9x_serdes_serial_link { -+ bool enabled; -+ bool detected; -+ struct regulator *poc_regulator; /* feeds the serializer, imager */ -+ bool regulator_enabled; -+ struct { -+ struct i2c_client *client; -+ struct max9x_subdev_pdata *pdata; -+ } remote; -+ struct regmap *map; -+ struct max9x_serdes_serial_config config; -+ struct device_attribute *link_lock_status; -+}; -+ -+struct max9x_serdes_line_fault { -+ bool enabled; -+ struct device_attribute *line_fault_status; -+}; -+ -+struct max9x_common { -+ struct max9x_desc *des; -+ struct device *dev; -+ struct i2c_client *client; -+ struct regmap *map; -+ struct i2c_client *phys_client; -+ struct regmap *phys_map; -+ struct i2c_mux_core *muxc; -+ struct gpio_chip gpio_chip; -+ enum max9x_serdes_type type; -+ -+ struct gpio_desc *reset_gpio; -+ struct regulator *vdd_regulator; -+ bool regulator_enabled; -+ -+ struct max9x_common_ops *common_ops; -+ struct max9x_serial_link_ops *serial_link_ops; -+ struct max9x_csi_link_ops *csi_link_ops; -+ struct max9x_line_fault_ops *line_fault_ops; -+ struct max9x_translation_ops *translation_ops; -+ -+ struct max9x_serdes_csi_link *csi_link; -+ int num_csi_links; -+ -+ struct max9x_serdes_video_pipe *video_pipe; -+ int num_video_pipes; -+ -+ struct max9x_serdes_serial_link *serial_link; -+ int num_serial_links; -+ -+ struct max9x_serdes_line_fault *line_fault; -+ int num_line_faults; -+ -+ struct max9x_serdes_v4l v4l; -+ -+ struct mutex link_mutex; -+ struct mutex isolate_mutex; -+ int isolated_link; -+ int selected_link; -+ bool external_refclk_enable; -+}; -+ -+int max9x_serdes_mhz_to_rate(struct max9x_serdes_rate_table *table, int len, unsigned int freq_mhz); -+struct max9x_common *max9x_sd_to_common(struct v4l2_subdev *sd); -+struct max9x_common *max9x_client_to_common(struct i2c_client *client); -+ -+enum max9x_element_type { -+ MAX9X_SERIAL_LINK = 0, -+ MAX9X_VIDEO_PIPE, -+ MAX9X_MIPI_MAP, -+ MAX9X_CSI_LINK, -+ MAX9X_LINE_FAULT, -+ MAX9X_DATA_TYPES -+}; -+ -+struct max9x_common_ops { -+ int (*soft_reset)(struct max9x_common *common); -+ int (*enable)(struct max9x_common *common); -+ int (*disable)(struct max9x_common *common); -+ int (*max_elements)(struct max9x_common *common, enum max9x_element_type element); -+ int (*verify_devid)(struct max9x_common *common); -+ int (*remap_addr)(struct max9x_common *common); -+ int (*remap_reset)(struct max9x_common *common); -+ int (*setup_gpio)(struct max9x_common *common); -+}; -+ -+struct max9x_serial_link_ops { -+ int (*enable)(struct max9x_common *common, unsigned int link); -+ int (*disable)(struct max9x_common *common, unsigned int link); -+ int (*select)(struct max9x_common *common, unsigned int link); -+ int (*deselect)(struct max9x_common *common, unsigned int link); -+ int (*get_locked)(struct max9x_common *common, unsigned int link, bool *locked); -+ int (*isolate)(struct max9x_common *common, unsigned int link); -+ int (*deisolate)(struct max9x_common *common, unsigned int link); -+}; -+ -+struct max9x_csi_link_ops { -+ int (*enable)(struct max9x_common *common, unsigned int link); -+ int (*disable)(struct max9x_common *common, unsigned int link); -+}; -+ -+struct max9x_translation_ops { -+ int (*add)(struct max9x_common *common, unsigned int i2c_id, unsigned int src, unsigned int dst); -+ int (*remove)(struct max9x_common *common, unsigned int i2c_id, unsigned int src, unsigned int dst); -+}; -+ -+struct max9x_line_fault_ops { -+ int (*enable)(struct max9x_common *common, unsigned int line); -+ int (*disable)(struct max9x_common *common, unsigned int line); -+ int (*get_status)(struct max9x_common *common, unsigned int line); -+}; -+ -+struct max9x_desc { -+ char dev_id; -+ char rev_reg; -+ enum max9x_serdes_type serdes_type; -+ enum max9x_chip_type chip_type; -+ int (*get_max9x_ops)(char dev_id, -+ struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+}; -+ -+int max9x_common_init_i2c_client(struct max9x_common *common, -+ struct i2c_client *client, -+ const struct regmap_config *regmap_config, -+ struct max9x_common_ops *common_ops, -+ struct max9x_serial_link_ops *serial_link_ops, -+ struct max9x_csi_link_ops *csi_link_ops, -+ struct max9x_line_fault_ops *lf_ops); -+void max9x_destroy(struct max9x_common *common); -+int max9x_common_resume(struct max9x_common *common); -+int max9x_common_suspend(struct max9x_common *common); -+int max9x_get_ops(char dev_id, struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max96724_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max96717_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max9296_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max9295_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+/* -+ * Both DES and SER have their own i2c_mux_core: -+ * 1. SER's muxc does not provide select/deselect and has at most one link -+ * (which itself has all children of the SER/DES link) -+ * 2. DER's muxc has driver specific select/deselect, one or more links, -+ * each of which must have exactly one SER -+ * 3. SER's probe() performs re-numbering and address translation (if needed: -+ * i2c-mux means this is no longer strictly required) -+ * -+ * des -> i2c-mux -> link@0 -> ser -> sensor, eeprom, etc -+ * link@n -> ser -> sensor, eeprom, etc -+ * -+ * +-----+ -+ * | des | -+ * +--+--+ -+ * | -+ * | +--------+ -+ * +--+ link@0 | -+ * | +------+-+ -+ * | | +-----+ -+ * | +----+ ser | -+ * | +--+--+ -+ * | | -+ * | | +--------+ -+ * | +--+ link@0 | -+ * | +-----+--+ -+ * | | -+ * | | +--------+ -+ * | +----+ sensor | -+ * | | +--------+ -+ * | | -+ * | | +--------+ -+ * | +----+ eeprom | -+ * | +--------+ -+ * | -+ * | +--------+ -+ * +--+ link@n | -+ * +------+-+ -+ * | +-----+ -+ * +----+ ser | -+ * +--+--+ -+ * | -+ * | +--------+ -+ * +--+ link@0 | -+ * +-----+--+ -+ * | -+ * | +--------+ -+ * +----+ sensor | -+ * | +--------+ -+ * | -+ * | +--------+ -+ * +----+ eeprom | -+ * +--------+ -+ */ -+ -+// for pdata parse -+ -+#define SET_CSI_MAP(maps, i, _src_vc, _src_dt, _dst_vc, _dst_dt, _dst_csi) do { \ -+ (maps)[i].src_vc = _src_vc; \ -+ (maps)[i].src_dt = _src_dt; \ -+ (maps)[i].dst_vc = _dst_vc; \ -+ (maps)[i].dst_dt = _dst_dt; \ -+ (maps)[i].dst_csi = _dst_csi; \ -+} while (0) -+ -+#define SET_PHY_MAP(maps, i, _int_csi, _phy_ind, _phy_lane) do { \ -+ (maps)[i].int_csi = _int_csi; \ -+ (maps)[i].phy_ind = _phy_ind; \ -+ (maps)[i].phy_lane = _phy_lane; \ -+} while (0) -+ -+#endif // _SERDES_H_ -diff --git a/drivers/media/pci/intel/ipu7/Kconfig b/drivers/media/pci/intel/ipu7/Kconfig -new file mode 100644 -index 000000000000..91954fdadc8b ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/Kconfig -@@ -0,0 +1,45 @@ -+config VIDEO_INTEL_IPU7 -+ tristate "Intel IPU7 driver" -+ depends on ACPI || COMPILE_TEST -+ depends on VIDEO_DEV -+ depends on X86 && HAS_DMA -+ depends on IPU_BRIDGE || !IPU_BRIDGE -+ # -+ # This driver incorrectly tries to override the dma_ops. It should -+ # never have done that, but for now keep it working on architectures -+ # that use dma ops -+ # -+ depends on ARCH_HAS_DMA_OPS -+ select AUXILIARY_BUS -+ select IOMMU_IOVA -+ select VIDEO_V4L2_SUBDEV_API -+ select MEDIA_CONTROLLER -+ select VIDEOBUF2_DMA_SG -+ select V4L2_FWNODE -+ help -+ This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs -+ and used for capturing images and video from camera sensors. -+ -+ To compile this driver, say Y here! It contains 3 modules - -+ intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. -+ -+config VIDEO_INTEL_IPU7_MGC -+ bool "Compile for IPU7 MGC driver" -+ depends on VIDEO_INTEL_IPU7 -+ help -+ If selected, MGC device nodes would be created. -+ -+ Recommended for driver developers only. -+ -+ If you want to the MGC devices exposed to user as media entity, -+ you must select this option, otherwise no. -+ -+config VIDEO_INTEL_IPU7_ISYS_RESET -+ bool "IPU7 ISYS RESET" -+ depends on VIDEO_INTEL_IPU7 -+ default n -+ help -+ This option enables IPU7 ISYS reset feature to support -+ HDMI-MIPI converter hot-plugging. -+ -+ If doubt, say N here. -diff --git a/drivers/media/pci/intel/ipu7/Makefile b/drivers/media/pci/intel/ipu7/Makefile -new file mode 100644 -index 000000000000..50417017da3a ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/Makefile -@@ -0,0 +1,37 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2017 - 2025 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+intel-ipu7-objs += ipu7.o \ -+ ipu7-bus.o \ -+ ipu7-dma.o \ -+ ipu7-mmu.o \ -+ ipu7-buttress.o \ -+ ipu7-cpd.o \ -+ ipu7-syscom.o \ -+ ipu7-boot.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7.o -+ -+intel-ipu7-isys-objs += ipu7-isys.o \ -+ ipu7-isys-csi2.o \ -+ ipu7-isys-csi-phy.o \ -+ ipu7-fw-isys.o \ -+ ipu7-isys-video.o \ -+ ipu7-isys-queue.o \ -+ ipu7-isys-subdev.o -+ -+ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+intel-ipu7-isys-objs += ipu7-isys-tpg.o -+endif -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ -+ -+ccflags-y += -I$(src)/ -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -new file mode 100644 -index 000000000000..a1519c4fe661 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -@@ -0,0 +1,163 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_BOOT_ABI_H -+#define IPU7_FW_BOOT_ABI_H -+ -+#include "ipu7_fw_common_abi.h" -+#include "ipu7_fw_syscom_abi.h" -+ -+#define IA_GOFO_FWLOG_SEVERITY_CRIT (0U) -+#define IA_GOFO_FWLOG_SEVERITY_ERROR (1U) -+#define IA_GOFO_FWLOG_SEVERITY_WARNING (2U) -+#define IA_GOFO_FWLOG_SEVERITY_INFO (3U) -+#define IA_GOFO_FWLOG_SEVERITY_DEBUG (4U) -+#define IA_GOFO_FWLOG_SEVERITY_VERBOSE (5U) -+#define IA_GOFO_FWLOG_MAX_LOGGER_SOURCES (64U) -+ -+#define LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK BIT(0) -+#define LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK BIT(1) -+#define LOGGER_CONFIG_CHANNEL_ENABLE_ALL_BITMASK \ -+ (LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK | \ -+ LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK) -+ -+struct ia_gofo_logger_config { -+ u8 use_source_severity; -+ u8 source_severity[IA_GOFO_FWLOG_MAX_LOGGER_SOURCES]; -+ u8 use_channels_enable_bitmask; -+ u8 channels_enable_bitmask; -+ u8 padding[1]; -+ ia_gofo_addr_t hw_printf_buffer_base_addr; -+ u32 hw_printf_buffer_size_bytes; -+}; -+ -+#pragma pack(push, 1) -+ -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP \ -+ ((u32)IA_GOFO_FW_BOOT_ID_MAX) -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET (0U) -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PS_OFFSET \ -+ ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ -+ (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) -+#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) -+#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) -+#define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP) -+#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) -+#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) -+ -+#define IA_GOFO_BOOT_RESERVED_SIZE (58U) -+#define IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE (IA_GOFO_BOOT_RESERVED_SIZE) -+#define IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS \ -+ (sizeof(ia_gofo_addr_t) + sizeof(ia_gofo_addr_t) + sizeof(u32)) -+ -+enum ia_gofo_buttress_reg_id { -+ IA_GOFO_FW_BOOT_CONFIG_ID = 0, -+ IA_GOFO_FW_BOOT_STATE_ID = 1, -+ IA_GOFO_FW_BOOT_RESERVED1_ID = IA_GOFO_FW_BOOT_STATE_ID, -+ IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2, -+ IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3, -+ IA_GOFO_FW_BOOT_RESERVED0_ID = IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID, -+ IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID = 4, -+ IA_GOFO_FW_BOOT_ID_MAX -+}; -+ -+enum ia_gofo_boot_uc_tile_frequency_units { -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ = 0, -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_HZ = 1, -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_N -+}; -+ -+#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ -+ (0xdead0000U == ((boot_state) & 0xffff0000U)) -+ -+struct ia_gofo_boot_config { -+ u32 length; -+ struct ia_gofo_version_s config_version; -+ struct ia_gofo_msg_version_list client_version_support; -+ ia_gofo_addr_t pkg_dir; -+ ia_gofo_addr_t subsys_config; -+ u32 uc_tile_frequency; -+ u16 checksum; -+ u8 uc_tile_frequency_units; -+ u8 padding[1]; -+ u32 reserved[IA_GOFO_BOOT_RESERVED_SIZE]; -+ struct syscom_config_s syscom_context_config; -+}; -+ -+struct ia_gofo_secondary_boot_config { -+ u32 length; -+ struct ia_gofo_version_s config_version; -+ struct ia_gofo_msg_version_list client_version_support; -+ u8 reserved1[IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS]; -+ u16 checksum; -+ u8 padding[2]; -+ u32 reserved2[IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE]; -+ struct syscom_config_s syscom_context_config; -+}; -+ -+#pragma pack(pop) -+ -+#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U -+#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U -+#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U -+#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U -+#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U -+#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U -+#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U -+#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U -+#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U -+ -+enum ia_gofo_boot_state { -+ IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, -+ IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, -+ IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, -+ IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, -+ IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, -+ IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, -+ IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, -+ IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, -+ IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, -+ IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, -+ IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, -+ IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, -+ IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = -+ IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DIRTY = -+ IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DTAG = -+ IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_CACHE = -+ IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_DOUBLE_EXCEPTION = -+ IA_GOFO_DOUBLE_EXCEPTION_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = -+ IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = -+ 0xdead1012U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, -+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, -+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, -+ IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, -+ IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, -+ IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -new file mode 100644 -index 000000000000..7bb6fac585a3 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -@@ -0,0 +1,175 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_COMMOM_ABI_H -+#define IPU7_FW_COMMOM_ABI_H -+ -+#include -+ -+#pragma pack(push, 1) -+typedef u32 ia_gofo_addr_t; -+ -+#define IA_GOFO_ADDR_NULL (0U) -+ -+struct ia_gofo_version_s { -+ u8 patch; -+ u8 subminor; -+ u8 minor; -+ u8 major; -+}; -+ -+#define IA_GOFO_MSG_VERSION_INIT(major_val, minor_val, subminor_val, patch_val)\ -+ {.major = (major_val), .minor = (minor_val), .subminor = \ -+ (subminor_val), .patch = (patch_val)} -+ -+#define IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES (3U) -+#define IA_GOFO_MSG_RESERVED_SIZE (3U) -+ -+struct ia_gofo_msg_version_list { -+ u8 num_versions; -+ u8 reserved[IA_GOFO_MSG_RESERVED_SIZE]; -+ struct ia_gofo_version_s versions[IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES]; -+}; -+ -+#pragma pack(pop) -+ -+#define TLV_TYPE_PADDING (0U) -+ -+#pragma pack(push, 1) -+ -+#define IA_GOFO_ABI_BITS_PER_BYTE (8U) -+ -+struct ia_gofo_tlv_header { -+ u16 tlv_type; -+ u16 tlv_len32; -+}; -+ -+struct ia_gofo_tlv_list { -+ u16 num_elems; -+ u16 head_offset; -+}; -+ -+#define TLV_ITEM_ALIGNMENT ((u32)sizeof(u32)) -+#define TLV_MSG_ALIGNMENT ((u32)sizeof(u64)) -+#define TLV_LIST_ALIGNMENT TLV_ITEM_ALIGNMENT -+#pragma pack(pop) -+ -+#define IA_GOFO_MODULO(dividend, divisor) ((dividend) % (divisor)) -+ -+#define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) -+#define IA_GOFO_MSG_ERR_OK (0U) -+#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) -+#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) -+#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) -+ -+#pragma pack(push, 1) -+struct ia_gofo_msg_err { -+ u32 err_group; -+ u32 err_code; -+ u32 err_detail[IA_GOFO_MSG_ERR_MAX_DETAILS]; -+}; -+ -+#pragma pack(pop) -+ -+#define IA_GOFO_MSG_ERR_GROUP_APP_EXT_START (16U) -+#define IA_GOFO_MSG_ERR_GROUP_MAX (31U) -+#define IA_GOFO_MSG_ERR_GROUP_INTERNAL_START (IA_GOFO_MSG_ERR_GROUP_MAX + 1U) -+#define IA_GOFO_MSG_ERR_GROUP_RESERVED IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED -+#define IA_GOFO_MSG_ERR_GROUP_GENERAL 1 -+ -+enum ia_gofo_msg_err_general { -+ IA_GOFO_MSG_ERR_GENERAL_OK = IA_GOFO_MSG_ERR_OK, -+ IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_SMALL = 1, -+ IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_LARGE = 2, -+ IA_GOFO_MSG_ERR_GENERAL_DEVICE_STATE = 3, -+ IA_GOFO_MSG_ERR_GENERAL_ALIGNMENT = 4, -+ IA_GOFO_MSG_ERR_GENERAL_INDIRECT_REF_PTR_INVALID = 5, -+ IA_GOFO_MSG_ERR_GENERAL_INVALID_MSG_TYPE = 6, -+ IA_GOFO_MSG_ERR_GENERAL_SYSCOM_FAIL = 7, -+ IA_GOFO_MSG_ERR_GENERAL_N -+}; -+ -+#pragma pack(push, 1) -+#define IA_GOFO_MSG_TYPE_RESERVED 0 -+#define IA_GOFO_MSG_TYPE_INDIRECT 1 -+#define IA_GOFO_MSG_TYPE_LOG 2 -+#define IA_GOFO_MSG_TYPE_GENERAL_ERR 3 -+ -+struct ia_gofo_msg_header { -+ struct ia_gofo_tlv_header tlv_header; -+ struct ia_gofo_tlv_list msg_options; -+ u64 user_token; -+}; -+ -+struct ia_gofo_msg_header_ack { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_msg_err err; -+ -+}; -+ -+struct ia_gofo_msg_general_err { -+ struct ia_gofo_msg_header_ack header; -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+enum ia_gofo_msg_link_streaming_mode { -+ IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF = 0, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_DOFF = 1, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_BCLM = 2, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_BCSM_FIX = 3, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_N -+}; -+ -+enum ia_gofo_soc_pbk_instance_id { -+ IA_GOFO_SOC_PBK_ID0 = 0, -+ IA_GOFO_SOC_PBK_ID1 = 1, -+ IA_GOFO_SOC_PBK_ID_N -+}; -+ -+#define IA_GOFO_MSG_LINK_PBK_MAX_SLOTS (2U) -+ -+struct ia_gofo_msg_indirect { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_tlv_header ref_header; -+ ia_gofo_addr_t ref_msg_ptr; -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+#define IA_GOFO_MSG_LOG_MAX_PARAMS (4U) -+#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) -+ -+#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) -+#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) -+ -+struct ia_gofo_msg_log_info { -+ u16 log_counter; -+ u8 msg_parameter_types; -+ /* [0:0] is_out_of_order, [1:3] logger_channel, [4:7] reserved */ -+ u8 logger_opts; -+ u32 fmt_id; -+ u32 params[IA_GOFO_MSG_LOG_MAX_PARAMS]; -+}; -+ -+struct ia_gofo_msg_log_info_ts { -+ u64 msg_ts; -+ struct ia_gofo_msg_log_info log_info; -+}; -+ -+struct ia_gofo_msg_log { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_msg_log_info_ts log_info_ts; -+}; -+ -+#pragma pack(pop) -+ -+#define IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID (0U) -+#define IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID (1U) -+#define IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID (2U) -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -new file mode 100644 -index 000000000000..c3f62aaedd86 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -@@ -0,0 +1,19 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2021 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_CONFIG_ABI_H -+#define IPU7_FW_CONFIG_ABI_H -+ -+#include -+ -+#define IPU_CONFIG_ABI_WDT_TIMER_DISABLED 0U -+#define IPU_CONFIG_ABI_CMD_TIMER_DISABLED 0U -+ -+struct ipu7_wdt_abi { -+ u32 wdt_timer1_us; -+ u32 wdt_timer2_us; -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -new file mode 100644 -index 000000000000..f161a605c500 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -@@ -0,0 +1,19 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2021 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_INSYS_CONFIG_ABI_H -+#define IPU7_FW_INSYS_CONFIG_ABI_H -+ -+#include "ipu7_fw_boot_abi.h" -+#include "ipu7_fw_config_abi.h" -+#include "ipu7_fw_isys_abi.h" -+ -+struct ipu7_insys_config { -+ u32 timeout_val_ms; -+ struct ia_gofo_logger_config logger_config; -+ struct ipu7_wdt_abi wdt_config; -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -new file mode 100644 -index 000000000000..45db85eb13ec ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -@@ -0,0 +1,459 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_ISYS_ABI_H -+#define IPU7_FW_ISYS_ABI_H -+ -+#include "ipu7_fw_common_abi.h" -+#include "ipu7_fw_isys_abi.h" -+ -+#define IPU_INSYS_MAX_OUTPUT_QUEUES (3U) -+#define IPU_INSYS_STREAM_ID_MAX (16U) -+ -+#define IPU_INSYS_MAX_INPUT_QUEUES (IPU_INSYS_STREAM_ID_MAX + 1U) -+#define IPU_INSYS_OUTPUT_FIRST_QUEUE (0U) -+#define IPU_INSYS_OUTPUT_LAST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES - 1U) -+#define IPU_INSYS_OUTPUT_MSG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE) -+#define IPU_INSYS_OUTPUT_LOG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE + 1U) -+#define IPU_INSYS_OUTPUT_RESERVED_QUEUE (IPU_INSYS_OUTPUT_LAST_QUEUE) -+#define IPU_INSYS_INPUT_FIRST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES) -+#define IPU_INSYS_INPUT_LAST_QUEUE \ -+ (IPU_INSYS_INPUT_FIRST_QUEUE + IPU_INSYS_MAX_INPUT_QUEUES - 1U) -+#define IPU_INSYS_INPUT_DEV_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE) -+#define IPU_INSYS_INPUT_MSG_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE + 1U) -+#define IPU_INSYS_INPUT_MSG_MAX_QUEUE (IPU_INSYS_MAX_INPUT_QUEUES - 1U) -+ -+#define MAX_OPINS_FOR_SINGLE_IPINS (3U) -+#define DEV_SEND_QUEUE_SIZE (IPU_INSYS_STREAM_ID_MAX) -+ -+#define PIN_PLANES_MAX (4U) -+ -+#define INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_INPUT \ -+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES -+ -+typedef u64 ipu7_insys_return_token; -+ -+enum ipu7_insys_resp_type { -+ IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0, -+ IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1, -+ IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2, -+ IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3, -+ IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4, -+ IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5, -+ IPU_INSYS_RESP_TYPE_PIN_DATA_READY = 6, -+ IPU_INSYS_RESP_TYPE_FRAME_SOF = 7, -+ IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, -+ IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, -+ IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, -+ IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, -+ N_IPU_INSYS_RESP_TYPE -+}; -+ -+enum ipu7_insys_send_type { -+ IPU_INSYS_SEND_TYPE_STREAM_OPEN = 0, -+ IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1, -+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE = 2, -+ IPU_INSYS_SEND_TYPE_STREAM_ABORT = 3, -+ IPU_INSYS_SEND_TYPE_STREAM_FLUSH = 4, -+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE = 5, -+ N_IPU_INSYS_SEND_TYPE -+}; -+ -+enum ipu7_insys_mipi_vc { -+ IPU_INSYS_MIPI_VC_0 = 0, -+ IPU_INSYS_MIPI_VC_1 = 1, -+ IPU_INSYS_MIPI_VC_2 = 2, -+ IPU_INSYS_MIPI_VC_3 = 3, -+ IPU_INSYS_MIPI_VC_4 = 4, -+ IPU_INSYS_MIPI_VC_5 = 5, -+ IPU_INSYS_MIPI_VC_6 = 6, -+ IPU_INSYS_MIPI_VC_7 = 7, -+ IPU_INSYS_MIPI_VC_8 = 8, -+ IPU_INSYS_MIPI_VC_9 = 9, -+ IPU_INSYS_MIPI_VC_10 = 10, -+ IPU_INSYS_MIPI_VC_11 = 11, -+ IPU_INSYS_MIPI_VC_12 = 12, -+ IPU_INSYS_MIPI_VC_13 = 13, -+ IPU_INSYS_MIPI_VC_14 = 14, -+ IPU_INSYS_MIPI_VC_15 = 15, -+ N_IPU_INSYS_MIPI_VC -+}; -+ -+enum ipu7_insys_mipi_port { -+ IPU_INSYS_MIPI_PORT_0 = 0, -+ IPU_INSYS_MIPI_PORT_1 = 1, -+ IPU_INSYS_MIPI_PORT_2 = 2, -+ IPU_INSYS_MIPI_PORT_3 = 3, -+ IPU_INSYS_MIPI_PORT_4 = 4, -+ IPU_INSYS_MIPI_PORT_5 = 5, -+ NA_IPU_INSYS_MIPI_PORT -+}; -+ -+enum ipu7_insys_frame_format_type { -+ IPU_INSYS_FRAME_FORMAT_NV11 = 0, -+ IPU_INSYS_FRAME_FORMAT_NV12 = 1, -+ IPU_INSYS_FRAME_FORMAT_NV12_16 = 2, -+ IPU_INSYS_FRAME_FORMAT_NV12_TILEY = 3, -+ IPU_INSYS_FRAME_FORMAT_NV16 = 4, -+ IPU_INSYS_FRAME_FORMAT_NV21 = 5, -+ IPU_INSYS_FRAME_FORMAT_NV61 = 6, -+ IPU_INSYS_FRAME_FORMAT_YV12 = 7, -+ IPU_INSYS_FRAME_FORMAT_YV16 = 8, -+ IPU_INSYS_FRAME_FORMAT_YUV420 = 9, -+ IPU_INSYS_FRAME_FORMAT_YUV420_10 = 10, -+ IPU_INSYS_FRAME_FORMAT_YUV420_12 = 11, -+ IPU_INSYS_FRAME_FORMAT_YUV420_14 = 12, -+ IPU_INSYS_FRAME_FORMAT_YUV420_16 = 13, -+ IPU_INSYS_FRAME_FORMAT_YUV422 = 14, -+ IPU_INSYS_FRAME_FORMAT_YUV422_16 = 15, -+ IPU_INSYS_FRAME_FORMAT_UYVY = 16, -+ IPU_INSYS_FRAME_FORMAT_YUYV = 17, -+ IPU_INSYS_FRAME_FORMAT_YUV444 = 18, -+ IPU_INSYS_FRAME_FORMAT_YUV_LINE = 19, -+ IPU_INSYS_FRAME_FORMAT_RAW8 = 20, -+ IPU_INSYS_FRAME_FORMAT_RAW10 = 21, -+ IPU_INSYS_FRAME_FORMAT_RAW12 = 22, -+ IPU_INSYS_FRAME_FORMAT_RAW14 = 23, -+ IPU_INSYS_FRAME_FORMAT_RAW16 = 24, -+ IPU_INSYS_FRAME_FORMAT_RGB565 = 25, -+ IPU_INSYS_FRAME_FORMAT_PLANAR_RGB888 = 26, -+ IPU_INSYS_FRAME_FORMAT_RGBA888 = 27, -+ IPU_INSYS_FRAME_FORMAT_QPLANE6 = 28, -+ IPU_INSYS_FRAME_FORMAT_BINARY_8 = 29, -+ IPU_INSYS_FRAME_FORMAT_Y_8 = 30, -+ IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, -+ IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, -+ IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, -+ N_IPU_INSYS_FRAME_FORMAT -+}; -+ -+#define IPU_INSYS_FRAME_FORMAT_RAW (IPU_INSYS_FRAME_FORMAT_RAW16) -+#define N_IPU_INSYS_MIPI_DATA_TYPE 0x40 -+ -+enum ipu7_insys_mipi_dt_rename_mode { -+ IPU_INSYS_MIPI_DT_NO_RENAME = 0, -+ IPU_INSYS_MIPI_DT_RENAMED_MODE = 1, -+ N_IPU_INSYS_MIPI_DT_MODE -+}; -+ -+#define IPU_INSYS_SEND_MSG_ENABLED 1U -+#define IPU_INSYS_SEND_MSG_DISABLED 0U -+ -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7) -+#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_RESP ( \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED) -+#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_IRQ ( \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED) -+ -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9) -+#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK) -+#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK) -+ -+#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0) -+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1) -+#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2) -+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3) -+#define IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4) -+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5) -+#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \ -+ IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \ -+ IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \ -+ IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY) -+#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \ -+ IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \ -+ IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \ -+ IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY) -+ -+enum ipu7_insys_output_link_dest { -+ IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, -+ IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, -+ IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 -+}; -+ -+enum ipu7_insys_dpcm_type { -+ IPU_INSYS_DPCM_TYPE_DISABLED = 0, -+ IPU_INSYS_DPCM_TYPE_10_8_10 = 1, -+ IPU_INSYS_DPCM_TYPE_12_8_12 = 2, -+ IPU_INSYS_DPCM_TYPE_12_10_12 = 3, -+ N_IPU_INSYS_DPCM_TYPE -+}; -+ -+enum ipu7_insys_dpcm_predictor { -+ IPU_INSYS_DPCM_PREDICTOR_1 = 0, -+ IPU_INSYS_DPCM_PREDICTOR_2 = 1, -+ N_IPU_INSYS_DPCM_PREDICTOR -+}; -+ -+enum ipu7_insys_send_queue_token_flag { -+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, -+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 -+}; -+ -+#pragma pack(push, 1) -+struct ipu7_insys_resolution { -+ u32 width; -+ u32 height; -+}; -+ -+struct ipu7_insys_capture_output_pin_payload { -+ u64 user_token; -+ ia_gofo_addr_t addr; -+ u8 pad[4]; -+}; -+ -+struct ipu7_insys_output_link { -+ u32 buffer_lines; -+ u16 foreign_key; -+ u16 granularity_pointer_update; -+ u8 msg_link_streaming_mode; -+ u8 pbk_id; -+ u8 pbk_slot_id; -+ u8 dest; -+ u8 use_sw_managed; -+ u8 is_snoop; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_output_cropping { -+ u16 line_top; -+ u16 line_bottom; -+#ifdef IPU8_INSYS_NEW_ABI -+ u16 column_left; -+ u16 column_right; -+#endif -+}; -+ -+struct ipu7_insys_output_dpcm { -+ u8 enable; -+ u8 type; -+ u8 predictor; -+ u8 pad; -+}; -+ -+#ifdef IPU8_INSYS_NEW_ABI -+enum ipu_insys_cfa_dim { -+ IPU_INSYS_CFA_DIM_2x2 = 0, -+ IPU_INSYS_CFA_DIM_4x4 = 1, -+ N_IPU_INSYS_CFA_DIM -+}; -+ -+#define IPU_INSYS_MAX_BINNING_FACTOR (4U) -+#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) -+#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) -+#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) -+#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) -+ -+struct ipu7_insys_upipe_output_pin { -+ ia_gofo_addr_t opaque_pin_cfg; -+ u16 plane_offset_1; -+ u16 plane_offset_2; -+ u8 single_uob_fifo; -+ u8 shared_uob_fifo; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_capture_output_pin_cfg { -+ struct ipu7_insys_capture_output_pin_payload pin_payload; -+ ia_gofo_addr_t upipe_capture_cfg; -+}; -+ -+#endif -+struct ipu7_insys_output_pin { -+ struct ipu7_insys_output_link link; -+ struct ipu7_insys_output_cropping crop; -+ struct ipu7_insys_output_dpcm dpcm; -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_upipe_output_pin upipe_pin_cfg; -+#endif -+ u32 stride; -+ u16 ft; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 upipe_enable; -+#endif -+ u8 send_irq; -+ u8 input_pin_id; -+ u8 early_ack_en; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 cfa_dim; -+ u8 binning_factor; -+#else -+ u8 pad[3]; -+#endif -+}; -+ -+struct ipu7_insys_input_pin { -+ struct ipu7_insys_resolution input_res; -+ u16 sync_msg_map; -+ u8 dt; -+ u8 disable_mipi_unpacking; -+ u8 dt_rename_mode; -+ u8 mapped_dt; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_stream_cfg { -+ struct ipu7_insys_input_pin input_pins[4]; -+ struct ipu7_insys_output_pin output_pins[4]; -+ u16 stream_msg_map; -+ u8 port_id; -+ u8 vc; -+ u8 nof_input_pins; -+ u8 nof_output_pins; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_buffset { -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_capture_output_pin_cfg output_pins[4]; -+#else -+ struct ipu7_insys_capture_output_pin_payload output_pins[4]; -+#endif -+ u8 capture_msg_map; -+ u8 frame_id; -+ u8 skip_frame; -+ u8 pad[5]; -+}; -+ -+struct ipu7_insys_resp { -+ u64 buf_id; -+ struct ipu7_insys_capture_output_pin_payload pin; -+ struct ia_gofo_msg_err error_info; -+ u32 timestamp[2]; -+ u8 type; -+ u8 msg_link_streaming_mode; -+ u8 stream_id; -+ u8 pin_id; -+ u8 frame_id; -+ u8 skip_frame; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_resp_queue_token { -+ struct ipu7_insys_resp resp_info; -+}; -+ -+struct ipu7_insys_send_queue_token { -+ u64 buf_handle; -+ ia_gofo_addr_t addr; -+ u16 stream_id; -+ u8 send_type; -+ u8 flag; -+}; -+ -+#pragma pack(pop) -+ -+enum insys_msg_err_stream { -+ INSYS_MSG_ERR_STREAM_OK = IA_GOFO_MSG_ERR_OK, -+ INSYS_MSG_ERR_STREAM_STREAM_ID = 1, -+ INSYS_MSG_ERR_STREAM_MAX_OPINS = 2, -+ INSYS_MSG_ERR_STREAM_MAX_IPINS = 3, -+ INSYS_MSG_ERR_STREAM_STREAM_MESSAGES_MAP = 4, -+ INSYS_MSG_ERR_STREAM_SYNC_MESSAGES_MAP = 5, -+ INSYS_MSG_ERR_STREAM_SENSOR_TYPE = 6, -+ INSYS_MSG_ERR_STREAM_FOREIGN_KEY = 7, -+ INSYS_MSG_ERR_STREAM_STREAMING_MODE = 8, -+ INSYS_MSG_ERR_STREAM_DPCM_EN = 9, -+ INSYS_MSG_ERR_STREAM_DPCM_TYPE = 10, -+ INSYS_MSG_ERR_STREAM_DPCM_PREDICTOR = 11, -+ INSYS_MSG_ERR_STREAM_GRANULARITY_POINTER_UPDATE = 12, -+ INSYS_MSG_ERR_STREAM_MPF_LUT_ENTRY_RESOURCES_BUSY = 13, -+ INSYS_MSG_ERR_STREAM_MPF_DEV_ID = 14, -+ INSYS_MSG_ERR_STREAM_BUFFER_LINES = 15, -+ INSYS_MSG_ERR_STREAM_IPIN_ID = 16, -+ INSYS_MSG_ERR_STREAM_DATA_TYPE = 17, -+ INSYS_MSG_ERR_STREAM_STREAMING_PROTOCOL_STATE = 18, -+ INSYS_MSG_ERR_STREAM_SYSCOM_FLUSH = 19, -+ INSYS_MSG_ERR_STREAM_MIPI_VC = 20, -+ INSYS_MSG_ERR_STREAM_STREAM_SRC = 21, -+ INSYS_MSG_ERR_STREAM_PBK_ID = 22, -+ INSYS_MSG_ERR_STREAM_CMD_QUEUE_DEALLOCATE = 23, -+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES = 24, -+ INSYS_MSG_ERR_STREAM_IPIN_CONFIGURATION = 25, -+ INSYS_MSG_ERR_STREAM_INVALID_STATE = 26, -+ INSYS_MSG_ERR_STREAM_SW_MANAGED = 27, -+ INSYS_MSG_ERR_STREAM_PBK_SLOT_ID = 28, -+ INSYS_MSG_ERR_STREAM_FLUSH_TIMEOUT = 29, -+ INSYS_MSG_ERR_STREAM_IPIN_WIDTH = 30, -+ INSYS_MSG_ERR_STREAM_IPIN_HEIGHT = 31, -+ INSYS_MSG_ERR_STREAM_OUTPUT_PIN_EARLY_ACK_EN = 32, -+ INSYS_MSG_ERR_STREAM_INCONSISTENT_PARAMS = 33, -+ INSYS_MSG_ERR_STREAM_PLANE_COUNT = 34, -+ INSYS_MSG_ERR_STREAM_FRAME_FORMAT_TYPE = 35, -+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36, -+ INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37, -+ INSYS_MSG_ERR_STREAM_CLOSED = 38, -+ INSYS_MSG_ERR_STREAM_N -+}; -+ -+enum insys_msg_err_capture { -+ INSYS_MSG_ERR_CAPTURE_OK = IA_GOFO_MSG_ERR_OK, -+ INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1, -+ INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2, -+ INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3, -+ INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4, -+ INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5, -+ INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6, -+ INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7, -+ INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8, -+ INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9, -+ INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10, -+ INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11, -+ INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12, -+ INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21, -+ INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22, -+ INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23, -+ INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24, -+ INSYS_MSG_ERR_CAPTURE_N -+}; -+ -+enum insys_msg_err_groups { -+ INSYS_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -+ INSYS_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -+ INSYS_MSG_ERR_GROUP_STREAM = 2, -+ INSYS_MSG_ERR_GROUP_CAPTURE = 3, -+ INSYS_MSG_ERR_GROUP_N, -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -new file mode 100644 -index 000000000000..8a78dd0936df ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -@@ -0,0 +1,465 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_MSG_ABI_H -+#define IPU7_FW_MSG_ABI_H -+ -+#include "ipu7_fw_common_abi.h" -+ -+#pragma pack(push, 1) -+enum ipu7_msg_type { -+ IPU_MSG_TYPE_RESERVED = IA_GOFO_MSG_TYPE_RESERVED, -+ IPU_MSG_TYPE_INDIRECT = IA_GOFO_MSG_TYPE_INDIRECT, -+ IPU_MSG_TYPE_DEV_LOG = IA_GOFO_MSG_TYPE_LOG, -+ IPU_MSG_TYPE_GENERAL_ERR = IA_GOFO_MSG_TYPE_GENERAL_ERR, -+ IPU_MSG_TYPE_DEV_OPEN = 4, -+ IPU_MSG_TYPE_DEV_OPEN_ACK = 5, -+ IPU_MSG_TYPE_GRAPH_OPEN = 6, -+ IPU_MSG_TYPE_GRAPH_OPEN_ACK = 7, -+ IPU_MSG_TYPE_TASK_REQ = 8, -+ IPU_MSG_TYPE_TASK_DONE = 9, -+ IPU_MSG_TYPE_GRAPH_CLOSE = 10, -+ IPU_MSG_TYPE_GRAPH_CLOSE_ACK = 11, -+ IPU_MSG_TYPE_DEV_CLOSE = 12, -+ IPU_MSG_TYPE_DEV_CLOSE_ACK = 13, -+ IPU_MSG_TYPE_TERM_EVENT = 14, -+ IPU_MSG_TYPE_N, -+}; -+ -+#define IPU_MSG_MAX_NODE_TERMS (64U) -+#define IPU_MSG_MAX_FRAGS (7U) -+ -+enum ipu7_msg_node_type { -+ IPU_MSG_NODE_TYPE_PAD = 0, -+ IPU_MSG_NODE_TYPE_BASE, -+ IPU_MSG_NODE_TYPE_N -+}; -+ -+#define IPU_MSG_NODE_MAX_DEVICES (128U) -+#define DEB_NUM_UINT32 (IPU_MSG_NODE_MAX_DEVICES / (sizeof(u32) * 8U)) -+ -+typedef u32 ipu7_msg_teb_t[2]; -+typedef u32 ipu7_msg_deb_t[DEB_NUM_UINT32]; -+ -+#define IPU_MSG_NODE_MAX_ROUTE_ENABLES (128U) -+#define RBM_NUM_UINT32 (IPU_MSG_NODE_MAX_ROUTE_ENABLES / (sizeof(u32) * 8U)) -+ -+typedef u32 ipu7_msg_rbm_t[RBM_NUM_UINT32]; -+ -+enum ipu7_msg_node_profile_type { -+ IPU_MSG_NODE_PROFILE_TYPE_PAD = 0, -+ IPU_MSG_NODE_PROFILE_TYPE_BASE, -+ IPU_MSG_NODE_PROFILE_TYPE_CB, -+ IPU_MSG_NODE_PROFILE_TYPE_N -+}; -+ -+struct ipu7_msg_node_profile { -+ struct ia_gofo_tlv_header tlv_header; -+ ipu7_msg_teb_t teb; -+}; -+ -+struct ipu7_msg_cb_profile { -+ struct ipu7_msg_node_profile profile_base; -+ ipu7_msg_deb_t deb; -+ ipu7_msg_rbm_t rbm; -+ ipu7_msg_rbm_t reb; -+}; -+ -+#define IPU_MSG_NODE_MAX_PROFILES (2U) -+#define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) -+#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) -+ -+#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) -+#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) -+#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) -+ -+struct ipu7_msg_node { -+ struct ia_gofo_tlv_header tlv_header; -+ u8 node_rsrc_id; -+ u8 node_ctx_id; -+ u8 num_frags; -+ u8 reserved[1]; -+ struct ia_gofo_tlv_list profiles_list; -+ struct ia_gofo_tlv_list terms_list; -+ struct ia_gofo_tlv_list node_options; -+}; -+ -+enum ipu7_msg_node_option_types { -+ IPU_MSG_NODE_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_NODE_OPTION_TYPES_N -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+ -+enum ipu7_msg_link_type { -+ IPU_MSG_LINK_TYPE_PAD = 0, -+ IPU_MSG_LINK_TYPE_GENERIC = 1, -+ IPU_MSG_LINK_TYPE_N -+}; -+ -+enum ipu7_msg_link_option_types { -+ IPU_MSG_LINK_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_LINK_OPTION_TYPES_CMPRS = 1, -+ IPU_MSG_LINK_OPTION_TYPES_N -+}; -+ -+enum ipu7_msg_link_cmprs_option_bit_depth { -+ IPU_MSG_LINK_CMPRS_OPTION_8BPP = 0, -+ IPU_MSG_LINK_CMPRS_OPTION_10BPP = 1, -+ IPU_MSG_LINK_CMPRS_OPTION_12BPP = 2, -+}; -+ -+#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM (128U) -+#define IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE (5U) -+#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_NUM_MAX \ -+ (IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM - 1U) -+ -+struct ipu7_msg_link_cmprs_plane_desc { -+ u8 plane_enable; -+ u8 cmprs_enable; -+ u8 encoder_plane_id; -+ u8 decoder_plane_id; -+ u8 cmprs_is_lossy; -+ u8 cmprs_is_footprint; -+ u8 bit_depth; -+ u8 space_saving_numerator; -+ u32 pixels_offset; -+ u32 ts_offset; -+ u32 tile_row_to_tile_row_stride; -+ u32 rows_of_tiles; -+ u32 lossy_cfg[IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE]; -+}; -+ -+#define IPU_MSG_LINK_CMPRS_MAX_PLANES (2U) -+#define IPU_MSG_LINK_CMPRS_NO_ALIGN_INTERVAL (0U) -+#define IPU_MSG_LINK_CMPRS_MIN_ALIGN_INTERVAL (16U) -+#define IPU_MSG_LINK_CMPRS_MAX_ALIGN_INTERVAL (1024U) -+struct ipu7_msg_link_cmprs_option { -+ struct ia_gofo_tlv_header header; -+ u32 cmprs_buf_size; -+ u16 align_interval; -+ u8 reserved[2]; -+ struct ipu7_msg_link_cmprs_plane_desc plane_descs[2]; -+}; -+ -+struct ipu7_msg_link_ep { -+ u8 node_ctx_id; -+ u8 term_id; -+}; -+ -+struct ipu7_msg_link_ep_pair { -+ struct ipu7_msg_link_ep ep_src; -+ struct ipu7_msg_link_ep ep_dst; -+}; -+ -+#define IPU_MSG_LINK_FOREIGN_KEY_NONE (65535U) -+#define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) -+#define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) -+#define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) -+#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) -+ -+struct ipu7_msg_link { -+ struct ia_gofo_tlv_header tlv_header; -+ struct ipu7_msg_link_ep_pair endpoints; -+ u16 foreign_key; -+ u8 streaming_mode; -+ u8 pbk_id; -+ u8 pbk_slot_id; -+ u8 delayed_link; -+ u8 reserved[2]; -+ struct ia_gofo_tlv_list link_options; -+}; -+ -+#pragma pack(pop) -+ -+enum ipu7_msg_dev_state { -+ IPU_MSG_DEV_STATE_CLOSED = 0, -+ IPU_MSG_DEV_STATE_OPEN_WAIT = 1, -+ IPU_MSG_DEV_STATE_OPEN = 2, -+ IPU_MSG_DEV_STATE_CLOSE_WAIT = 3, -+ IPU_MSG_DEV_STATE_N -+}; -+ -+enum ipu7_msg_graph_state { -+ IPU_MSG_GRAPH_STATE_CLOSED = 0, -+ IPU_MSG_GRAPH_STATE_OPEN_WAIT = 1, -+ IPU_MSG_GRAPH_STATE_OPEN = 2, -+ IPU_MSG_GRAPH_STATE_CLOSE_WAIT = 3, -+ IPU_MSG_GRAPH_STATE_N -+}; -+ -+enum ipu7_msg_task_state { -+ IPU_MSG_TASK_STATE_DONE = 0, -+ IPU_MSG_TASK_STATE_WAIT_DONE = 1, -+ IPU_MSG_TASK_STATE_N -+}; -+ -+enum ipu7_msg_err_groups { -+ IPU_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -+ IPU_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -+ IPU_MSG_ERR_GROUP_DEVICE = 2, -+ IPU_MSG_ERR_GROUP_GRAPH = 3, -+ IPU_MSG_ERR_GROUP_TASK = 4, -+ IPU_MSG_ERR_GROUP_N, -+}; -+ -+#pragma pack(push, 1) -+struct ipu7_msg_task { -+ struct ia_gofo_msg_header header; -+ u8 graph_id; -+ u8 profile_idx; -+ u8 node_ctx_id; -+ u8 frame_id; -+ u8 frag_id; -+ u8 req_done_msg; -+ u8 req_done_irq; -+ u8 reserved[1]; -+ ipu7_msg_teb_t payload_reuse_bm; -+ ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; -+}; -+ -+struct ipu7_msg_task_done { -+ struct ia_gofo_msg_header_ack header; -+ u8 graph_id; -+ u8 frame_id; -+ u8 node_ctx_id; -+ u8 profile_idx; -+ u8 frag_id; -+ u8 reserved[3]; -+}; -+ -+enum ipu7_msg_err_task { -+ IPU_MSG_ERR_TASK_OK = IA_GOFO_MSG_ERR_OK, -+ IPU_MSG_ERR_TASK_GRAPH_ID = 1, -+ IPU_MSG_ERR_TASK_NODE_CTX_ID = 2, -+ IPU_MSG_ERR_TASK_PROFILE_IDX = 3, -+ IPU_MSG_ERR_TASK_CTX_MEMORY_TASK = 4, -+ IPU_MSG_ERR_TASK_TERM_PAYLOAD_PTR = 5, -+ IPU_MSG_ERR_TASK_FRAME_ID = 6, -+ IPU_MSG_ERR_TASK_FRAG_ID = 7, -+ IPU_MSG_ERR_TASK_EXEC_EXT = 8, -+ IPU_MSG_ERR_TASK_EXEC_SBX = 9, -+ IPU_MSG_ERR_TASK_EXEC_INT = 10, -+ IPU_MSG_ERR_TASK_EXEC_UNKNOWN = 11, -+ IPU_MSG_ERR_TASK_PRE_EXEC = 12, -+ IPU_MSG_ERR_TASK_N -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+enum ipu7_msg_term_type { -+ IPU_MSG_TERM_TYPE_PAD = 0, -+ IPU_MSG_TERM_TYPE_BASE, -+ IPU_MSG_TERM_TYPE_N, -+}; -+ -+#define IPU_MSG_TERM_EVENT_TYPE_NONE 0U -+#define IPU_MSG_TERM_EVENT_TYPE_PROGRESS 1U -+#define IPU_MSG_TERM_EVENT_TYPE_N (IPU_MSG_TERM_EVENT_TYPE_PROGRESS + 1U) -+ -+struct ipu7_msg_term { -+ struct ia_gofo_tlv_header tlv_header; -+ u8 term_id; -+ u8 event_req_bm; -+ u8 reserved[2]; -+ u32 payload_size; -+ struct ia_gofo_tlv_list term_options; -+}; -+ -+enum ipu7_msg_term_option_types { -+ IPU_MSG_TERM_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_TERM_OPTION_TYPES_N -+}; -+ -+struct ipu7_msg_term_event { -+ struct ia_gofo_msg_header header; -+ u8 graph_id; -+ u8 frame_id; -+ u8 node_ctx_id; -+ u8 profile_idx; -+ u8 frag_id; -+ u8 term_id; -+ u8 event_type; -+ u8 reserved[1]; -+ u64 event_ts; -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+#define IPU_MSG_DEVICE_SEND_MSG_ENABLED 1U -+#define IPU_MSG_DEVICE_SEND_MSG_DISABLED 0U -+ -+#define IPU_MSG_DEVICE_OPEN_SEND_RESP BIT(0) -+#define IPU_MSG_DEVICE_OPEN_SEND_IRQ BIT(1) -+ -+#define IPU_MSG_DEVICE_CLOSE_SEND_RESP BIT(0) -+#define IPU_MSG_DEVICE_CLOSE_SEND_IRQ BIT(1) -+ -+struct ipu7_msg_dev_open { -+ struct ia_gofo_msg_header header; -+ u32 max_graphs; -+ u8 dev_msg_map; -+ u8 enable_power_gating; -+ u8 reserved[2]; -+}; -+ -+struct ipu7_msg_dev_open_ack { -+ struct ia_gofo_msg_header_ack header; -+}; -+ -+struct ipu7_msg_dev_close { -+ struct ia_gofo_msg_header header; -+ u8 dev_msg_map; -+ u8 reserved[7]; -+}; -+ -+struct ipu7_msg_dev_close_ack { -+ struct ia_gofo_msg_header_ack header; -+}; -+ -+enum ipu7_msg_err_device { -+ IPU_MSG_ERR_DEVICE_OK = IA_GOFO_MSG_ERR_OK, -+ IPU_MSG_ERR_DEVICE_MAX_GRAPHS = 1, -+ IPU_MSG_ERR_DEVICE_MSG_MAP = 2, -+ IPU_MSG_ERR_DEVICE_N -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) -+#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U -+#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U -+ -+#define IPU_MSG_GRAPH_OPEN_SEND_RESP BIT(0) -+#define IPU_MSG_GRAPH_OPEN_SEND_IRQ BIT(1) -+ -+#define IPU_MSG_GRAPH_CLOSE_SEND_RESP BIT(0) -+#define IPU_MSG_GRAPH_CLOSE_SEND_IRQ BIT(1) -+ -+struct ipu7_msg_graph_open { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_tlv_list nodes; -+ struct ia_gofo_tlv_list links; -+ u8 graph_id; -+ u8 graph_msg_map; -+ u8 reserved[6]; -+}; -+ -+enum ipu7_msg_graph_ack_option_types { -+ IPU_MSG_GRAPH_ACK_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_GRAPH_ACK_TASK_Q_INFO, -+ IPU_MSG_GRAPH_ACK_OPTION_TYPES_N -+}; -+ -+struct ipu7_msg_graph_open_ack_task_q_info { -+ struct ia_gofo_tlv_header header; -+ u8 node_ctx_id; -+ u8 q_id; -+ u8 reserved[2]; -+}; -+ -+struct ipu7_msg_graph_open_ack { -+ struct ia_gofo_msg_header_ack header; -+ u8 graph_id; -+ u8 reserved[7]; -+}; -+ -+struct ipu7_msg_graph_close { -+ struct ia_gofo_msg_header header; -+ u8 graph_id; -+ u8 graph_msg_map; -+ u8 reserved[6]; -+}; -+ -+struct ipu7_msg_graph_close_ack { -+ struct ia_gofo_msg_header_ack header; -+ u8 graph_id; -+ u8 reserved[7]; -+}; -+ -+enum ipu7_msg_err_graph { -+ IPU_MSG_ERR_GRAPH_OK = IA_GOFO_MSG_ERR_OK, -+ IPU_MSG_ERR_GRAPH_GRAPH_STATE = 1, -+ IPU_MSG_ERR_GRAPH_MAX_GRAPHS = 2, -+ IPU_MSG_ERR_GRAPH_GRAPH_ID = 3, -+ IPU_MSG_ERR_GRAPH_NODE_CTX_ID = 4, -+ IPU_MSG_ERR_GRAPH_NODE_RSRC_ID = 5, -+ IPU_MSG_ERR_GRAPH_PROFILE_IDX = 6, -+ IPU_MSG_ERR_GRAPH_TERM_ID = 7, -+ IPU_MSG_ERR_GRAPH_TERM_PAYLOAD_SIZE = 8, -+ IPU_MSG_ERR_GRAPH_LINK_NODE_CTX_ID = 9, -+ IPU_MSG_ERR_GRAPH_LINK_TERM_ID = 10, -+ IPU_MSG_ERR_GRAPH_PROFILE_TYPE = 11, -+ IPU_MSG_ERR_GRAPH_NUM_FRAGS = 12, -+ IPU_MSG_ERR_GRAPH_QUEUE_ID_USAGE = 13, -+ IPU_MSG_ERR_GRAPH_QUEUE_OPEN = 14, -+ IPU_MSG_ERR_GRAPH_QUEUE_CLOSE = 15, -+ IPU_MSG_ERR_GRAPH_QUEUE_ID_TASK_REQ_MISMATCH = 16, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_FGRAPH = 17, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE = 18, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE_PROFILE = 19, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_TERM = 20, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_LINK = 21, -+ IPU_MSG_ERR_GRAPH_CTX_MSG_MAP = 22, -+ IPU_MSG_ERR_GRAPH_CTX_FOREIGN_KEY = 23, -+ IPU_MSG_ERR_GRAPH_CTX_STREAMING_MODE = 24, -+ IPU_MSG_ERR_GRAPH_CTX_PBK_RSRC = 25, -+ IPU_MSG_ERR_GRAPH_UNSUPPORTED_EVENT_TYPE = 26, -+ IPU_MSG_ERR_GRAPH_TOO_MANY_EVENTS = 27, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_CMPRS = 28, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_ALIGN_INTERVAL = 29, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_PLANE_ID = 30, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_UNSUPPORTED_MODE = 31, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_BIT_DEPTH = 32, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_STRIDE_ALIGNMENT = 33, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_SUB_BUFFER_ALIGNMENT = 34, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_ORDER = 35, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_OVERLAP = 36, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_BUFFER_TOO_SMALL = 37, -+ IPU_MSG_ERR_GRAPH_CTX_DELAYED_LINK = 38, -+ IPU_MSG_ERR_GRAPH_N -+}; -+ -+#pragma pack(pop) -+ -+#define FWPS_MSG_ABI_MAX_INPUT_QUEUES (60U) -+#define FWPS_MSG_ABI_MAX_OUTPUT_QUEUES (2U) -+#define FWPS_MSG_ABI_MAX_QUEUES \ -+ (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES + FWPS_MSG_ABI_MAX_INPUT_QUEUES) -+ -+#define FWPS_MSG_ABI_OUT_ACK_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID) -+#define FWPS_MSG_ABI_OUT_LOG_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID) -+#if (FWPS_MSG_ABI_OUT_LOG_QUEUE_ID >= FWPS_MSG_ABI_MAX_OUTPUT_QUEUES) -+#error "Maximum output queues configuration is too small to fit ACK and LOG \ -+queues" -+#endif -+#define FWPS_MSG_ABI_IN_DEV_QUEUE_ID (IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID) -+#define FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID (3U) -+#define FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID \ -+ (FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID + 1U) -+ -+#if (FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID >= FWPS_MSG_ABI_MAX_INPUT_QUEUES) -+#error "Maximum queues configuration is too small to fit minimum number of \ -+useful queues" -+#endif -+ -+#define FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID (FWPS_MSG_ABI_MAX_QUEUES - 1U) -+#define FWPS_MSG_ABI_IN_MAX_TASK_QUEUES \ -+ (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID - \ -+ FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID + 1U) -+#define FWPS_MSG_ABI_OUT_FIRST_QUEUE_ID (FWPS_MSG_ABI_OUT_ACK_QUEUE_ID) -+#define FWPS_MSG_ABI_OUT_LAST_QUEUE_ID (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES - 1U) -+#define FWPS_MSG_ABI_IN_FIRST_QUEUE_ID (FWPS_MSG_ABI_IN_DEV_QUEUE_ID) -+#define FWPS_MSG_ABI_IN_LAST_QUEUE_ID (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID) -+ -+#define FWPS_MSG_HOST2FW_MAX_SIZE (2U * 1024U) -+#define FWPS_MSG_FW2HOST_MAX_SIZE (256U) -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -new file mode 100644 -index 000000000000..0af04c8c6a88 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -@@ -0,0 +1,24 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ -+#define IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ -+ -+#include -+ -+#include "ipu7_fw_boot_abi.h" -+#include "ipu7_fw_config_abi.h" -+ -+struct ipu7_psys_config { -+ u32 use_debug_manifest; -+ u32 timeout_val_ms; -+ u32 compression_support_enabled; -+ struct ia_gofo_logger_config logger_config; -+ struct ipu7_wdt_abi wdt_config; -+ u8 ipu_psys_debug_bitmask; -+ u8 padding[3]; -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -new file mode 100644 -index 000000000000..bfa5258d5b97 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -@@ -0,0 +1,49 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_SYSCOM_ABI_H -+#define IPU7_FW_SYSCOM_ABI_H -+ -+#include -+ -+#include "ipu7_fw_common_abi.h" -+ -+#pragma pack(push, 1) -+#define SYSCOM_QUEUE_MIN_CAPACITY 2U -+ -+struct syscom_queue_params_config { -+ ia_gofo_addr_t token_array_mem; -+ u16 token_size_in_bytes; -+ u16 max_capacity; -+}; -+ -+struct syscom_config_s { -+ u16 max_output_queues; -+ u16 max_input_queues; -+}; -+ -+#pragma pack(pop) -+ -+static inline struct syscom_queue_params_config * -+syscom_config_get_queue_configs(struct syscom_config_s *config) -+{ -+ return (struct syscom_queue_params_config *)(&config[1]); -+} -+ -+static inline const struct syscom_queue_params_config * -+syscom_config_get_queue_configs_const(const struct syscom_config_s *config) -+{ -+ return (const struct syscom_queue_params_config *)(&config[1]); -+} -+ -+#pragma pack(push, 1) -+struct syscom_queue_indices_s { -+ u32 read_index; -+ u32 write_index; -+}; -+ -+#pragma pack(pop) -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.c b/drivers/media/pci/intel/ipu7/ipu7-boot.c -new file mode 100644 -index 000000000000..a88182d70173 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-boot.c -@@ -0,0 +1,431 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2022 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_boot_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-dma.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+ -+#define IPU_FW_START_STOP_TIMEOUT 2000 -+#define IPU_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC) -+#define BOOT_STATE_IS_CRITICAL(s) IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s) -+#define BOOT_STATE_IS_READY(s) ((s) == IA_GOFO_FW_BOOT_STATE_READY) -+#define BOOT_STATE_IS_INACTIVE(s) ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE) -+ -+struct ipu7_boot_context { -+ u32 base; -+ u32 dmem_address; -+ u32 status_ctrl_reg; -+ u32 fw_start_address_reg; -+ u32 fw_code_base_reg; -+}; -+ -+static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = { -+ { -+ /* ISYS */ -+ .dmem_address = IPU_ISYS_DMEM_OFFSET, -+ .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS, -+ .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR, -+ .fw_code_base_reg = IS_UC_CTRL_BASE -+ }, -+ { -+ /* PSYS */ -+ .dmem_address = IPU_PSYS_DMEM_OFFSET, -+ .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS, -+ .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR, -+ .fw_code_base_reg = PS_UC_CTRL_BASE -+ } -+}; -+ -+static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev, -+ enum ia_gofo_buttress_reg_id reg) -+{ -+ u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX; -+ -+ return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg); -+} -+ -+static void write_fw_boot_param(const struct ipu7_bus_device *adev, -+ enum ia_gofo_buttress_reg_id reg, -+ u32 val) -+{ -+ void __iomem *base = adev->isp->base; -+ -+ dev_dbg(&adev->auxdev.dev, -+ "write boot param reg: %d addr: %x val: 0x%x\n", -+ reg, get_fw_boot_reg_addr(adev, reg), val); -+ writel(val, base + get_fw_boot_reg_addr(adev, reg)); -+} -+ -+static u32 read_fw_boot_param(const struct ipu7_bus_device *adev, -+ enum ia_gofo_buttress_reg_id reg) -+{ -+ void __iomem *base = adev->isp->base; -+ -+ return readl(base + get_fw_boot_reg_addr(adev, reg)); -+} -+ -+static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ const struct device *dev = &adev->auxdev.dev; -+ u32 ucx_ctrl_status = ctx->status_ctrl_reg; -+ u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT; -+ void __iomem *base = adev->isp->base; -+ u32 val, val2; -+ int ret; -+ -+ dev_dbg(dev, "cell enter reset...\n"); -+ val = readl(base + ucx_ctrl_status); -+ dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n", -+ ucx_ctrl_status, val); -+ -+ dev_dbg(dev, "force cell reset...\n"); -+ val |= UCX_CTL_RESET; -+ val &= ~UCX_CTL_RUN; -+ -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ucx_ctrl_status, val); -+ writel(val, base + ucx_ctrl_status); -+ -+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -+ (val2 & 0x3U) == (val & 0x3U), 100, timeout); -+ if (ret) { -+ dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2); -+ return -ETIMEDOUT; -+ } -+ -+ dev_dbg(dev, "cell exit reset...\n"); -+ val = readl(base + ucx_ctrl_status); -+ WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN), -+ "cell status 0x%x", val); -+ -+ val &= ~(UCX_CTL_RESET | UCX_CTL_RUN); -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ucx_ctrl_status, val); -+ writel(val, base + ucx_ctrl_status); -+ -+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -+ (val2 & 0x3U) == (val & 0x3U), 100, timeout); -+ if (ret) { -+ dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2); -+ return -ETIMEDOUT; -+ } -+ -+ return 0; -+} -+ -+static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ void __iomem *base = adev->isp->base; -+ const struct device *dev = &adev->auxdev.dev; -+ u32 val; -+ -+ dev_dbg(dev, "starting cell...\n"); -+ val = readl(base + ctx->status_ctrl_reg); -+ WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN)); -+ -+ val &= ~UCX_CTL_RESET; -+ val |= UCX_CTL_RUN; -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ctx->status_ctrl_reg, val); -+ writel(val, base + ctx->status_ctrl_reg); -+} -+ -+static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ void __iomem *base = adev->isp->base; -+ const struct device *dev = &adev->auxdev.dev; -+ u32 val; -+ -+ dev_dbg(dev, "stopping cell...\n"); -+ -+ val = readl(base + ctx->status_ctrl_reg); -+ val &= ~UCX_CTL_RUN; -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ctx->status_ctrl_reg, val); -+ writel(val, base + ctx->status_ctrl_reg); -+ -+ /* Wait for uC transactions complete */ -+ usleep_range(10, 20); -+ -+ val = readl(base + ctx->status_ctrl_reg); -+ val |= UCX_CTL_RESET; -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ctx->status_ctrl_reg, val); -+ writel(val, base + ctx->status_ctrl_reg); -+} -+ -+static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ void __iomem *base = adev->isp->base; -+ -+ dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n", -+ ctx->fw_start_address_reg, adev->fw_entry); -+ writel(adev->fw_entry, base + ctx->fw_start_address_reg); -+ -+ return ipu7_boot_cell_reset(adev); -+} -+ -+static void init_boot_config(struct ia_gofo_boot_config *boot_config, -+ u32 length, u8 major) -+{ -+ /* syscom version, new syscom2 version */ -+ boot_config->length = length; -+ boot_config->config_version.major = 1U; -+ boot_config->config_version.minor = 0U; -+ boot_config->config_version.subminor = 0U; -+ boot_config->config_version.patch = 0U; -+ -+ /* msg version for task interface */ -+ boot_config->client_version_support.num_versions = 1U; -+ boot_config->client_version_support.versions[0].major = major; -+ boot_config->client_version_support.versions[0].minor = 0U; -+ boot_config->client_version_support.versions[0].subminor = 0U; -+ boot_config->client_version_support.versions[0].patch = 0U; -+} -+ -+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -+ struct syscom_queue_config *qconfigs, -+ int num_queues, u32 uc_freq, -+ dma_addr_t subsys_config, u8 major) -+{ -+ u32 total_queue_size_aligned = 0; -+ struct ipu7_syscom_context *syscom = adev->syscom; -+ struct ia_gofo_boot_config *boot_config; -+ struct syscom_queue_params_config *cfgs; -+ struct device *dev = &adev->auxdev.dev; -+ struct syscom_config_s *syscfg; -+ dma_addr_t queue_mem_dma_ptr; -+ void *queue_mem_ptr; -+ unsigned int i; -+ -+ dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n", -+ num_queues, uc_freq, &subsys_config); -+ /* Allocate boot config. */ -+ adev->boot_config_size = -+ sizeof(*cfgs) * num_queues + sizeof(*boot_config); -+ adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size, -+ &adev->boot_config_dma_addr, -+ GFP_KERNEL, 0); -+ if (!adev->boot_config) { -+ dev_err(dev, "Failed to allocate boot config.\n"); -+ return -ENOMEM; -+ } -+ -+ boot_config = adev->boot_config; -+ memset(boot_config, 0, sizeof(struct ia_gofo_boot_config)); -+ init_boot_config(boot_config, adev->boot_config_size, major); -+ boot_config->subsys_config = subsys_config; -+ -+ boot_config->uc_tile_frequency = uc_freq; -+ boot_config->uc_tile_frequency_units = -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ; -+ boot_config->syscom_context_config.max_output_queues = -+ syscom->num_output_queues; -+ boot_config->syscom_context_config.max_input_queues = -+ syscom->num_input_queues; -+ -+ ipu7_dma_sync_single(adev, adev->boot_config_dma_addr, -+ adev->boot_config_size); -+ -+ for (i = 0; i < num_queues; i++) { -+ u32 queue_size = qconfigs[i].max_capacity * -+ qconfigs[i].token_size_in_bytes; -+ -+ queue_size = ALIGN(queue_size, 64U); -+ total_queue_size_aligned += queue_size; -+ qconfigs[i].queue_size = queue_size; -+ } -+ -+ /* Allocate queue memory */ -+ syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned, -+ &syscom->queue_mem_dma_addr, -+ GFP_KERNEL, 0); -+ if (!syscom->queue_mem) { -+ dev_err(dev, "Failed to allocate queue memory.\n"); -+ return -ENOMEM; -+ } -+ syscom->queue_mem_size = total_queue_size_aligned; -+ -+ syscfg = &boot_config->syscom_context_config; -+ cfgs = ipu7_syscom_get_queue_config(syscfg); -+ queue_mem_ptr = syscom->queue_mem; -+ queue_mem_dma_ptr = syscom->queue_mem_dma_addr; -+ for (i = 0; i < num_queues; i++) { -+ cfgs[i].token_array_mem = queue_mem_dma_ptr; -+ cfgs[i].max_capacity = qconfigs[i].max_capacity; -+ cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes; -+ qconfigs[i].token_array_mem = queue_mem_ptr; -+ queue_mem_dma_ptr += qconfigs[i].queue_size; -+ queue_mem_ptr += qconfigs[i].queue_size; -+ } -+ -+ ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr, -+ total_queue_size_aligned); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7"); -+ -+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_syscom_context *syscom = adev->syscom; -+ -+ if (syscom->queue_mem) { -+ ipu7_dma_free(adev, syscom->queue_mem_size, -+ syscom->queue_mem, -+ syscom->queue_mem_dma_addr, 0); -+ syscom->queue_mem = NULL; -+ syscom->queue_mem_dma_addr = 0; -+ } -+ -+ if (adev->boot_config) { -+ ipu7_dma_free(adev, adev->boot_config_size, -+ adev->boot_config, -+ adev->boot_config_dma_addr, 0); -+ adev->boot_config = NULL; -+ adev->boot_config_dma_addr = 0; -+ } -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7"); -+ -+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev) -+{ -+ const struct device *dev = &adev->auxdev.dev; -+ u32 timeout = IPU_FW_START_STOP_TIMEOUT; -+ void __iomem *base = adev->isp->base; -+ u32 boot_state, last_boot_state; -+ u32 indices_addr, msg_ver, id; -+ int ret; -+ -+ ret = ipu7_boot_cell_init(adev); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "start booting fw...\n"); -+ /* store "uninit" state to syscom/boot state reg */ -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -+ IA_GOFO_FW_BOOT_STATE_UNINIT); -+ /* -+ * Set registers to zero -+ * (not strictly required, but recommended for diagnostics) -+ */ -+ write_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0); -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0); -+ /* store firmware configuration address */ -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID, -+ adev->boot_config_dma_addr); -+ -+ /* Kick uC, then wait for boot complete */ -+ ipu7_boot_cell_start(adev); -+ -+ last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT; -+ while (timeout--) { -+ boot_state = read_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_STATE_ID); -+ if (boot_state != last_boot_state) { -+ dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n", -+ last_boot_state, boot_state); -+ last_boot_state = boot_state; -+ } -+ if (BOOT_STATE_IS_CRITICAL(boot_state) || -+ BOOT_STATE_IS_READY(boot_state)) -+ break; -+ usleep_range(1000, 1200); -+ } -+ -+ if (BOOT_STATE_IS_CRITICAL(boot_state)) { -+ ipu7_dump_fw_error_log(adev); -+ dev_err(dev, "critical boot state error 0x%x\n", boot_state); -+ return -EINVAL; -+ } else if (!BOOT_STATE_IS_READY(boot_state)) { -+ dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state); -+ return -ETIMEDOUT; -+ } -+ dev_dbg(dev, "fw boot done.\n"); -+ -+ /* Get FW syscom queue indices addr */ -+ id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID; -+ indices_addr = read_fw_boot_param(adev, id); -+ adev->syscom->queue_indices = base + indices_addr; -+ dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr); -+ -+ /* Get message version. */ -+ msg_ver = read_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID); -+ dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7"); -+ -+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev) -+{ -+ const struct device *dev = &adev->auxdev.dev; -+ u32 timeout = IPU_FW_START_STOP_TIMEOUT; -+ u32 boot_state; -+ -+ boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); -+ if (BOOT_STATE_IS_CRITICAL(boot_state) || -+ !BOOT_STATE_IS_READY(boot_state)) { -+ dev_err(dev, "fw not ready for shutdown, state 0x%x\n", -+ boot_state); -+ return -EBUSY; -+ } -+ -+ /* Issue shutdown to start shutdown process */ -+ dev_dbg(dev, "stopping fw...\n"); -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD); -+ while (timeout--) { -+ boot_state = read_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_STATE_ID); -+ if (BOOT_STATE_IS_CRITICAL(boot_state) || -+ BOOT_STATE_IS_INACTIVE(boot_state)) -+ break; -+ usleep_range(1000, 1200); -+ } -+ -+ if (BOOT_STATE_IS_CRITICAL(boot_state)) { -+ ipu7_dump_fw_error_log(adev); -+ dev_err(dev, "critical boot state error 0x%x\n", boot_state); -+ return -EINVAL; -+ } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) { -+ dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state); -+ return -ETIMEDOUT; -+ } -+ -+ ipu7_boot_cell_stop(adev); -+ dev_dbg(dev, "stop fw done.\n"); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7"); -+ -+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev) -+{ -+ return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.h b/drivers/media/pci/intel/ipu7/ipu7-boot.h -new file mode 100644 -index 000000000000..5600be849931 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-boot.h -@@ -0,0 +1,25 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2022 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BOOT_H -+#define IPU7_BOOT_H -+ -+#include -+ -+struct ipu7_bus_device; -+struct syscom_queue_config; -+ -+#define FW_QUEUE_CONFIG_SIZE(num_queues) \ -+ (sizeof(struct syscom_queue_config) * (num_queues)) -+ -+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -+ struct syscom_queue_config *qconfigs, -+ int num_queues, u32 uc_freq, -+ dma_addr_t subsys_config, u8 major); -+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev); -+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev); -+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev); -+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.c b/drivers/media/pci/intel/ipu7/ipu7-bus.c -new file mode 100644 -index 000000000000..7da44fde002a ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-bus.c -@@ -0,0 +1,158 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-boot.h" -+#include "ipu7-dma.h" -+ -+static int bus_pm_runtime_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ int ret; -+ -+ ret = pm_generic_runtime_suspend(dev); -+ if (ret) -+ return ret; -+ -+ ret = ipu_buttress_powerdown(dev, adev->ctrl); -+ if (!ret) -+ return 0; -+ -+ dev_err(dev, "power down failed!\n"); -+ -+ /* Powering down failed, attempt to resume device now */ -+ ret = pm_generic_runtime_resume(dev); -+ if (!ret) -+ return -EBUSY; -+ -+ return -EIO; -+} -+ -+static int bus_pm_runtime_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ int ret; -+ -+ ret = ipu_buttress_powerup(dev, adev->ctrl); -+ if (ret) -+ return ret; -+ -+ ret = pm_generic_runtime_resume(dev); -+ if (ret) -+ goto out_err; -+ -+ return 0; -+ -+out_err: -+ ipu_buttress_powerdown(dev, adev->ctrl); -+ -+ return -EBUSY; -+} -+ -+static struct dev_pm_domain ipu7_bus_pm_domain = { -+ .ops = { -+ .runtime_suspend = bus_pm_runtime_suspend, -+ .runtime_resume = bus_pm_runtime_resume, -+ }, -+}; -+ -+static DEFINE_MUTEX(ipu7_bus_mutex); -+static void ipu7_bus_release(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ -+ kfree(adev->pdata); -+ kfree(adev); -+} -+ -+struct ipu7_bus_device * -+ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -+ void *pdata, const struct ipu_buttress_ctrl *ctrl, -+ const char *name) -+{ -+ struct auxiliary_device *auxdev; -+ struct ipu7_bus_device *adev; -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ int ret; -+ -+ adev = kzalloc(sizeof(*adev), GFP_KERNEL); -+ if (!adev) -+ return ERR_PTR(-ENOMEM); -+ -+ adev->isp = isp; -+ adev->ctrl = ctrl; -+ adev->pdata = pdata; -+ auxdev = &adev->auxdev; -+ auxdev->name = name; -+ auxdev->id = (pci_domain_nr(pdev->bus) << 16) | -+ PCI_DEVID(pdev->bus->number, pdev->devfn); -+ -+ auxdev->dev.parent = parent; -+ auxdev->dev.release = ipu7_bus_release; -+ -+ ret = auxiliary_device_init(auxdev); -+ if (ret < 0) { -+ dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", -+ ret); -+ kfree(adev); -+ return ERR_PTR(ret); -+ } -+ -+ dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain); -+ -+ pm_runtime_forbid(&adev->auxdev.dev); -+ pm_runtime_enable(&adev->auxdev.dev); -+ -+ return adev; -+} -+ -+int ipu7_bus_add_device(struct ipu7_bus_device *adev) -+{ -+ struct auxiliary_device *auxdev = &adev->auxdev; -+ int ret; -+ -+ ret = auxiliary_device_add(auxdev); -+ if (ret) { -+ auxiliary_device_uninit(auxdev); -+ return ret; -+ } -+ -+ mutex_lock(&ipu7_bus_mutex); -+ list_add(&adev->list, &adev->isp->devices); -+ mutex_unlock(&ipu7_bus_mutex); -+ -+ pm_runtime_allow(&auxdev->dev); -+ -+ return 0; -+} -+ -+void ipu7_bus_del_devices(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev, *save; -+ -+ mutex_lock(&ipu7_bus_mutex); -+ -+ list_for_each_entry_safe(adev, save, &isp->devices, list) { -+ pm_runtime_disable(&adev->auxdev.dev); -+ list_del(&adev->list); -+ auxiliary_device_delete(&adev->auxdev); -+ auxiliary_device_uninit(&adev->auxdev); -+ } -+ -+ mutex_unlock(&ipu7_bus_mutex); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.h b/drivers/media/pci/intel/ipu7/ipu7-bus.h -new file mode 100644 -index 000000000000..45157df16e90 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-bus.h -@@ -0,0 +1,69 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BUS_H -+#define IPU7_BUS_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_boot_abi.h" -+ -+#include "ipu7-syscom.h" -+ -+struct pci_dev; -+struct ipu_buttress_ctrl; -+struct ipu7_mmu; -+struct ipu7_device; -+ -+enum ipu7_subsys { -+ IPU_IS = 0, -+ IPU_PS = 1, -+ IPU_SUBSYS_NUM = 2, -+}; -+ -+struct ipu7_bus_device { -+ struct auxiliary_device auxdev; -+ const struct auxiliary_driver *auxdrv; -+ const struct ipu7_auxdrv_data *auxdrv_data; -+ struct list_head list; -+ enum ipu7_subsys subsys; -+ void *pdata; -+ struct ipu7_mmu *mmu; -+ struct ipu7_device *isp; -+ const struct ipu_buttress_ctrl *ctrl; -+ u64 dma_mask; -+ struct sg_table fw_sgt; -+ u32 fw_entry; -+ struct ipu7_syscom_context *syscom; -+ struct ia_gofo_boot_config *boot_config; -+ dma_addr_t boot_config_dma_addr; -+ u32 boot_config_size; -+}; -+ -+struct ipu7_auxdrv_data { -+ irqreturn_t (*isr)(struct ipu7_bus_device *adev); -+ irqreturn_t (*isr_threaded)(struct ipu7_bus_device *adev); -+ bool wake_isr_thread; -+}; -+ -+#define to_ipu7_bus_device(_dev) \ -+ container_of(to_auxiliary_dev(_dev), struct ipu7_bus_device, auxdev) -+#define auxdev_to_adev(_auxdev) \ -+ container_of(_auxdev, struct ipu7_bus_device, auxdev) -+#define ipu7_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) -+ -+struct ipu7_bus_device * -+ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -+ void *pdata, const struct ipu_buttress_ctrl *ctrl, -+ const char *name); -+int ipu7_bus_add_device(struct ipu7_bus_device *adev); -+void ipu7_bus_del_devices(struct pci_dev *pdev); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h b/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -new file mode 100644 -index 000000000000..3eafd6a3813d ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -@@ -0,0 +1,461 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BUTTRESS_REGS_H -+#define IPU7_BUTTRESS_REGS_H -+ -+#define BUTTRESS_REG_IRQ_STATUS 0x2000 -+#define BUTTRESS_REG_IRQ_STATUS_UNMASKED 0x2004 -+#define BUTTRESS_REG_IRQ_ENABLE 0x2008 -+#define BUTTRESS_REG_IRQ_CLEAR 0x200c -+#define BUTTRESS_REG_IRQ_MASK 0x2010 -+#define BUTTRESS_REG_TSC_CMD 0x2014 -+#define BUTTRESS_REG_TSC_CTL 0x2018 -+#define BUTTRESS_REG_TSC_LO 0x201c -+#define BUTTRESS_REG_TSC_HI 0x2020 -+ -+/* valid for PTL */ -+#define BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030 -+#define BUTTRESS_REG_PB_TIMESTAMP_HI 0x2034 -+#define BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038 -+ -+#define BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100 -+#define BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104 -+#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ 0x2108 -+#define BUTTRESS_REG_PS_DOMAINS_STATUS 0x2110 -+#define BUTTRESS_REG_PWR_STATUS 0x2114 -+#define BUTTRESS_REG_PS_WORKPOINT_REQ_SHADOW 0x2120 -+#define BUTTRESS_REG_IS_WORKPOINT_REQ_SHADOW 0x2124 -+#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ_SHADOW 0x2128 -+#define BUTTRESS_REG_ISPS_WORKPOINT_DOWNLOAD 0x212c -+#define BUTTRESS_REG_PG_FLOW_OVERRIDE 0x2180 -+#define BUTTRESS_REG_GLOBAL_OVERRIDE_UNGATE_CTL 0x2184 -+#define BUTTRESS_REG_PWR_FSM_CTL 0x2188 -+#define BUTTRESS_REG_IDLE_WDT 0x218c -+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_EN 0x2190 -+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_ADDR 0x2194 -+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_DATA 0x2198 -+#define BUTTRESS_REG_POWER_EN_DELAY 0x219c -+#define IPU7_BUTTRESS_REG_LTR_CONTROL 0x21a0 -+#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4 -+#define IPU7_BUTTRESS_REG_INT_FRM_PUNIT 0x21a8 -+#define IPU8_BUTTRESS_REG_LTR_CONTROL 0x21a4 -+#define IPU8_BUTTRESS_REG_NDE_CONTROL 0x21a8 -+#define IPU8_BUTTRESS_REG_INT_FRM_PUNIT 0x21ac -+#define BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0 -+#define BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4 -+#define BUTTRESS_REG_DVFS_FSM_STATUS 0x21b8 -+#define BUTTRESS_REG_PS_PLL_ENABLE 0x21bc -+#define BUTTRESS_REG_D2D_CTL 0x21d4 -+#define BUTTRESS_REG_IB_CLK_CTL 0x21d8 -+#define BUTTRESS_REG_IB_CRO_CLK_CTL 0x21dc -+#define BUTTRESS_REG_FUNC_FUSES 0x21e0 -+#define BUTTRESS_REG_ISOCH_CTL 0x21e4 -+#define BUTTRESS_REG_WORKPOINT_CTL 0x21f0 -+#define BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS 0x2200 -+#define BUTTRESS_REG_DRV_IS_UCX_START_ADDR 0x2204 -+#define BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS 0x2208 -+#define BUTTRESS_REG_DRV_PS_UCX_START_ADDR 0x220c -+#define BUTTRESS_REG_DRV_UCX_RESET_CFG 0x2210 -+ -+/* configured by CSE */ -+#define BUTTRESS_REG_CSE_IS_UCX_CONTROL_STATUS 0x2300 -+#define BUTTRESS_REG_CSE_IS_UCX_START_ADDR 0x2304 -+#define BUTTRESS_REG_CSE_PS_UCX_CONTROL_STATUS 0x2308 -+#define BUTTRESS_REG_CSE_PS_UCX_START_ADDR 0x230c -+ -+#define BUTTRESS_REG_CAMERA_MASK 0x2310 -+#define BUTTRESS_REG_FW_CTL 0x2314 -+#define BUTTRESS_REG_SECURITY_CTL 0x2318 -+#define BUTTRESS_REG_FUNCTIONAL_FW_SETUP 0x231c -+#define BUTTRESS_REG_FW_BASE 0x2320 -+#define BUTTRESS_REG_FW_BASE_LIMIT 0x2324 -+#define BUTTRESS_REG_FW_SCRATCH_BASE 0x2328 -+#define BUTTRESS_REG_FW_SCRATCH_LIMIT 0x232c -+#define BUTTRESS_REG_CSE_ACTION 0x2330 -+ -+/* configured by SW */ -+#define BUTTRESS_REG_FW_RESET_CTL 0x2334 -+#define BUTTRESS_REG_FW_SOURCE_SIZE 0x2338 -+#define BUTTRESS_REG_FW_SOURCE_BASE 0x233c -+ -+#define BUTTRESS_REG_IPU_SEC_CP_LSB 0x2400 -+#define BUTTRESS_REG_IPU_SEC_CP_MSB 0x2404 -+#define BUTTRESS_REG_IPU_SEC_WAC_LSB 0x2408 -+#define BUTTRESS_REG_IPU_SEC_WAC_MSB 0x240c -+#define BUTTRESS_REG_IPU_SEC_RAC_LSB 0x2410 -+#define BUTTRESS_REG_IPU_SEC_RAC_MSB 0x2414 -+#define BUTTRESS_REG_IPU_DRV_CP_LSB 0x2418 -+#define BUTTRESS_REG_IPU_DRV_CP_MSB 0x241c -+#define BUTTRESS_REG_IPU_DRV_WAC_LSB 0x2420 -+#define BUTTRESS_REG_IPU_DRV_WAC_MSB 0x2424 -+#define BUTTRESS_REG_IPU_DRV_RAC_LSB 0x2428 -+#define BUTTRESS_REG_IPU_DRV_RAC_MSB 0x242c -+#define BUTTRESS_REG_IPU_FW_CP_LSB 0x2430 -+#define BUTTRESS_REG_IPU_FW_CP_MSB 0x2434 -+#define BUTTRESS_REG_IPU_FW_WAC_LSB 0x2438 -+#define BUTTRESS_REG_IPU_FW_WAC_MSB 0x243c -+#define BUTTRESS_REG_IPU_FW_RAC_LSB 0x2440 -+#define BUTTRESS_REG_IPU_FW_RAC_MSB 0x2444 -+#define BUTTRESS_REG_IPU_BIOS_SEC_CP_LSB 0x2448 -+#define BUTTRESS_REG_IPU_BIOS_SEC_CP_MSB 0x244c -+#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_LSB 0x2450 -+#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_MSB 0x2454 -+#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_LSB 0x2458 -+#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_MSB 0x245c -+#define BUTTRESS_REG_IPU_DFD_CP_LSB 0x2460 -+#define BUTTRESS_REG_IPU_DFD_CP_MSB 0x2464 -+#define BUTTRESS_REG_IPU_DFD_WAC_LSB 0x2468 -+#define BUTTRESS_REG_IPU_DFD_WAC_MSB 0x246c -+#define BUTTRESS_REG_IPU_DFD_RAC_LSB 0x2470 -+#define BUTTRESS_REG_IPU_DFD_RAC_MSB 0x2474 -+#define BUTTRESS_REG_CSE2IUDB0 0x2500 -+#define BUTTRESS_REG_CSE2IUDATA0 0x2504 -+#define BUTTRESS_REG_CSE2IUCSR 0x2508 -+#define BUTTRESS_REG_IU2CSEDB0 0x250c -+#define BUTTRESS_REG_IU2CSEDATA0 0x2510 -+#define BUTTRESS_REG_IU2CSECSR 0x2514 -+#define BUTTRESS_REG_CSE2IUDB0_CR_SHADOW 0x2520 -+#define BUTTRESS_REG_CSE2IUDATA0_CR_SHADOW 0x2524 -+#define BUTTRESS_REG_CSE2IUCSR_CR_SHADOW 0x2528 -+#define BUTTRESS_REG_IU2CSEDB0_CR_SHADOW 0x252c -+#define BUTTRESS_REG_DVFS_FSM_SURVIVABILITY 0x2900 -+#define BUTTRESS_REG_FLOWS_FSM_SURVIVABILITY 0x2904 -+#define BUTTRESS_REG_FABRICS_FSM_SURVIVABILITY 0x2908 -+#define BUTTRESS_REG_PS_SUB1_PM_FSM_SURVIVABILITY 0x290c -+#define BUTTRESS_REG_PS_SUB0_PM_FSM_SURVIVABILITY 0x2910 -+#define BUTTRESS_REG_PS_PM_FSM_SURVIVABILITY 0x2914 -+#define BUTTRESS_REG_IS_PM_FSM_SURVIVABILITY 0x2918 -+#define BUTTRESS_REG_FLR_RST_FSM_SURVIVABILITY 0x291c -+#define BUTTRESS_REG_FW_RST_FSM_SURVIVABILITY 0x2920 -+#define BUTTRESS_REG_RESETPREP_FSM_SURVIVABILITY 0x2924 -+#define BUTTRESS_REG_POWER_FSM_DOMAIN_STATUS 0x3000 -+#define BUTTRESS_REG_IDLEREQ_STATUS1 0x3004 -+#define BUTTRESS_REG_POWER_FSM_STATUS_IS_PS 0x3008 -+#define BUTTRESS_REG_POWER_ACK_B_STATUS 0x300c -+#define BUTTRESS_REG_DOMAIN_RETENTION_CTL 0x3010 -+#define BUTTRESS_REG_CG_CTRL_BITS 0x3014 -+#define BUTTRESS_REG_IS_IFC_STATUS0 0x3018 -+#define BUTTRESS_REG_IS_IFC_STATUS1 0x301c -+#define BUTTRESS_REG_PS_IFC_STATUS0 0x3020 -+#define BUTTRESS_REG_PS_IFC_STATUS1 0x3024 -+#define BUTTRESS_REG_BTRS_IFC_STATUS0 0x3028 -+#define BUTTRESS_REG_BTRS_IFC_STATUS1 0x302c -+#define BUTTRESS_REG_IPU_SKU 0x3030 -+#define BUTTRESS_REG_PS_IDLEACK 0x3034 -+#define BUTTRESS_REG_IS_IDLEACK 0x3038 -+#define BUTTRESS_REG_SPARE_REGS_0 0x303c -+#define BUTTRESS_REG_SPARE_REGS_1 0x3040 -+#define BUTTRESS_REG_SPARE_REGS_2 0x3044 -+#define BUTTRESS_REG_SPARE_REGS_3 0x3048 -+#define BUTTRESS_REG_IUNIT_ACV 0x304c -+#define BUTTRESS_REG_CHICKEN_BITS 0x3050 -+#define BUTTRESS_REG_SBENDPOINT_CFG 0x3054 -+#define BUTTRESS_REG_ECC_ERR_LOG 0x3058 -+#define BUTTRESS_REG_POWER_FSM_STATUS 0x3070 -+#define BUTTRESS_REG_RESET_FSM_STATUS 0x3074 -+#define BUTTRESS_REG_IDLE_STATUS 0x3078 -+#define BUTTRESS_REG_IDLEACK_STATUS 0x307c -+#define BUTTRESS_REG_IPU_DEBUG 0x3080 -+ -+#define BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 -+#define BUTTRESS_REG_FW_BOOT_PARAMS1 0x4004 -+#define BUTTRESS_REG_FW_BOOT_PARAMS2 0x4008 -+#define BUTTRESS_REG_FW_BOOT_PARAMS3 0x400c -+#define BUTTRESS_REG_FW_BOOT_PARAMS4 0x4010 -+#define BUTTRESS_REG_FW_BOOT_PARAMS5 0x4014 -+#define BUTTRESS_REG_FW_BOOT_PARAMS6 0x4018 -+#define BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c -+#define BUTTRESS_REG_FW_BOOT_PARAMS8 0x4020 -+#define BUTTRESS_REG_FW_BOOT_PARAMS9 0x4024 -+#define BUTTRESS_REG_FW_BOOT_PARAMS10 0x4028 -+#define BUTTRESS_REG_FW_BOOT_PARAMS11 0x402c -+#define BUTTRESS_REG_FW_BOOT_PARAMS12 0x4030 -+#define BUTTRESS_REG_FW_BOOT_PARAMS13 0x4034 -+#define BUTTRESS_REG_FW_BOOT_PARAMS14 0x4038 -+#define BUTTRESS_REG_FW_BOOT_PARAMS15 0x403c -+ -+#define BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \ -+ (BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U)) -+#define BUTTRESS_REG_FW_GP(i) (0x4040 + 0x4 * (i)) -+#define BUTTRESS_REG_FPGA_SUPPORT(i) (0x40c0 + 0x4 * (i)) -+ -+#define BUTTRESS_REG_FW_GP8 0x4060 -+#define BUTTRESS_REG_FW_GP24 0x40a0 -+ -+#define BUTTRESS_REG_GPIO_0_PADCFG_ADDR_CR 0x4100 -+#define BUTTRESS_REG_GPIO_1_PADCFG_ADDR_CR 0x4104 -+#define BUTTRESS_REG_GPIO_2_PADCFG_ADDR_CR 0x4108 -+#define BUTTRESS_REG_GPIO_3_PADCFG_ADDR_CR 0x410c -+#define BUTTRESS_REG_GPIO_4_PADCFG_ADDR_CR 0x4110 -+#define BUTTRESS_REG_GPIO_5_PADCFG_ADDR_CR 0x4114 -+#define BUTTRESS_REG_GPIO_6_PADCFG_ADDR_CR 0x4118 -+#define BUTTRESS_REG_GPIO_7_PADCFG_ADDR_CR 0x411c -+#define BUTTRESS_REG_GPIO_ENABLE 0x4140 -+#define BUTTRESS_REG_GPIO_VALUE_CR 0x4144 -+ -+#define BUTTRESS_REG_IS_MEM_CORRECTABLE_ERROR_STATUS 0x5000 -+#define BUTTRESS_REG_IS_MEM_FATAL_ERROR_STATUS 0x5004 -+#define BUTTRESS_REG_IS_MEM_NON_FATAL_ERROR_STATUS 0x5008 -+#define BUTTRESS_REG_IS_MEM_CHECK_PASSED 0x500c -+#define BUTTRESS_REG_IS_MEM_ERROR_INJECT 0x5010 -+#define BUTTRESS_REG_IS_MEM_ERROR_CLEAR 0x5014 -+#define BUTTRESS_REG_PS_MEM_CORRECTABLE_ERROR_STATUS 0x5040 -+#define BUTTRESS_REG_PS_MEM_FATAL_ERROR_STATUS 0x5044 -+#define BUTTRESS_REG_PS_MEM_NON_FATAL_ERROR_STATUS 0x5048 -+#define BUTTRESS_REG_PS_MEM_CHECK_PASSED 0x504c -+#define BUTTRESS_REG_PS_MEM_ERROR_INJECT 0x5050 -+#define BUTTRESS_REG_PS_MEM_ERROR_CLEAR 0x5054 -+ -+#define BUTTRESS_REG_IS_AB_REGION_MIN_ADDRESS(i) (0x6000 + 0x8 * (i)) -+#define BUTTRESS_REG_IS_AB_REGION_MAX_ADDRESS(i) (0x6004 + 0x8 * (i)) -+#define BUTTRESS_REG_IS_AB_VIOLATION_LOG0 0x6080 -+#define BUTTRESS_REG_IS_AB_VIOLATION_LOG1 0x6084 -+#define BUTTRESS_REG_PS_AB_REGION_MIN_ADDRESS(i) (0x6100 + 0x8 * (i)) -+#define BUTTRESS_REG_PS_AB_REGION_MAX_ADDRESS0 (0x6104 + 0x8 * (i)) -+#define BUTTRESS_REG_PS_AB_VIOLATION_LOG0 0x6180 -+#define BUTTRESS_REG_PS_AB_VIOLATION_LOG1 0x6184 -+#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG0 0x6200 -+#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG1 0x6204 -+#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG0 0x6208 -+#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG1 0x620c -+#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG0 0x6210 -+#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG1 0x6214 -+#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG0 0x6218 -+#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG1 0x621c -+#define BUTTRESS_REG_AB_ENABLE 0x6220 -+#define BUTTRESS_REG_AB_DEFAULT_ACCESS 0x6230 -+ -+/* Indicates CSE has received an IPU driver IPC transaction */ -+#define BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0) -+/* Indicates an IPC transaction from CSE has arrived */ -+#define BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1) -+/* Indicates a CSR update from CSE has arrived */ -+#define BUTTRESS_IRQ_CSE_CSR_SET BIT(2) -+/* Indicates an interrupt set by Punit (not in use at this time) */ -+#define BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ BIT(3) -+/* Indicates an SAI violation was detected on access to IB registers */ -+#define BUTTRESS_IRQ_SAI_VIOLATION BIT(4) -+/* Indicates a transaction to IS was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_IS_AB_VIOLATION BIT(5) -+/* Indicates a transaction to PS was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_PS_AB_VIOLATION BIT(6) -+/* Indicates an error response was detected by the IB config NoC */ -+#define BUTTRESS_IRQ_IB_CFG_NOC_ERR_IRQ BIT(7) -+/* Indicates an error response was detected by the IB data NoC */ -+#define BUTTRESS_IRQ_IB_DATA_NOC_ERR_IRQ BIT(8) -+/* Transaction to DVP regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_IB_DVP_AB_VIOLATION BIT(9) -+/* Transaction to ATB2DTF regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_ATB2DTF_AB_VIOLATION BIT(10) -+/* Transaction to IS debug regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_IS_DEBUG_AB_VIOLATION BIT(11) -+/* Transaction to PS debug regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_PS_DEBUG_AB_VIOLATION BIT(12) -+/* Indicates timeout occurred waiting for a response from a target */ -+#define BUTTRESS_IRQ_IB_CFG_NOC_TIMEOUT_IRQ BIT(13) -+/* Set when any correctable ECC error input wire to buttress is set */ -+#define BUTTRESS_IRQ_ECC_CORRECTABLE BIT(14) -+/* Any noncorrectable-nonfatal ECC error input wire to buttress is set */ -+#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_NONFATAL BIT(15) -+/* Set when any noncorrectable-fatal ECC error input wire to buttress is set */ -+#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_FATAL BIT(16) -+/* Set when timeout occurred waiting for a response from a target */ -+#define BUTTRESS_IRQ_IS_CFG_NOC_TIMEOUT_IRQ BIT(17) -+#define BUTTRESS_IRQ_PS_CFG_NOC_TIMEOUT_IRQ BIT(18) -+#define BUTTRESS_IRQ_LB_CFG_NOC_TIMEOUT_IRQ BIT(19) -+/* IS FW double exception event */ -+#define BUTTRESS_IRQ_IS_UC_PFATAL_ERROR BIT(26) -+/* PS FW double exception event */ -+#define BUTTRESS_IRQ_PS_UC_PFATAL_ERROR BIT(27) -+/* IS FW watchdog event */ -+#define BUTTRESS_IRQ_IS_WATCHDOG BIT(28) -+/* PS FW watchdog event */ -+#define BUTTRESS_IRQ_PS_WATCHDOG BIT(29) -+/* IS IRC irq out */ -+#define BUTTRESS_IRQ_IS_IRQ BIT(30) -+/* PS IRC irq out */ -+#define BUTTRESS_IRQ_PS_IRQ BIT(31) -+ -+/* buttress irq */ -+#define BUTTRESS_PWR_STATUS_HH_STATE_IDLE 0U -+#define BUTTRESS_PWR_STATUS_HH_STATE_IN_PRGS 1U -+#define BUTTRESS_PWR_STATUS_HH_STATE_DONE 2U -+#define BUTTRESS_PWR_STATUS_HH_STATE_ERR 3U -+ -+#define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) -+#define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 -+#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) -+#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) -+/* new for PTL */ -+#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) -+#define BUTTRESS_IRQS (BUTTRESS_IRQ_IS_IRQ | \ -+ BUTTRESS_IRQ_PS_IRQ | \ -+ BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \ -+ BUTTRESS_IRQ_CSE_CSR_SET | \ -+ BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE | \ -+ BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -+ -+/* Iunit to CSE regs */ -+#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) -+#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27 -+#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10 -+#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 -+ -+#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 -+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 -+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 -+#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 -+ -+#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) -+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) -+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) -+#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) -+ -+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) -+ -+/* 0x20 == NACK, 0xf == unknown command */ -+#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 -+#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff -+ -+/* IS/PS freq control */ -+#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU -+#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU -+ -+#define IPU7_IS_FREQ_MAX 450 -+#define IPU7_IS_FREQ_MIN 50 -+#define IPU7_PS_FREQ_MAX 750 -+#define BUTTRESS_PS_FREQ_RATIO_STEP 25U -+/* valid for IPU8 */ -+#define BUTTRESS_IS_FREQ_RATIO_STEP 25U -+ -+/* IS: 400mhz, PS: 500mhz */ -+#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b -+#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14 -+/* IS: 400mhz, PS: 400mhz */ -+#define IPU8_IS_FREQ_CTL_DEFAULT_RATIO 0x10 -+#define IPU8_PS_FREQ_CTL_DEFAULT_RATIO 0x10 -+ -+#define IPU_FREQ_CTL_CDYN 0x80 -+#define IPU_FREQ_CTL_RATIO_SHIFT 0x0 -+#define IPU_FREQ_CTL_CDYN_SHIFT 0x8 -+ -+/* buttree power status */ -+#define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 -+#define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ -+ (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) -+ -+#define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 -+#define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ -+ (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) -+ -+#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 -+#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 -+#define IPU_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 -+#define IPU_BUTTRESS_PWR_STATE_UP_DONE 0x3 -+ -+#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 -+#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3 << 3) -+ -+#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 -+#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3 << 6) -+ -+#define PS_FSM_CG BIT(3) -+ -+#define BUTTRESS_OVERRIDE_IS_CLK BIT(1) -+#define BUTTRESS_OVERRIDE_PS_CLK BIT(2) -+/* ps_pll only valid for ipu8 */ -+#define BUTTRESS_OWN_ACK_PS_PLL BIT(8) -+#define BUTTRESS_OWN_ACK_IS_CLK BIT(9) -+#define BUTTRESS_OWN_ACK_PS_CLK BIT(10) -+ -+/* FW reset ctrl */ -+#define BUTTRESS_FW_RESET_CTL_START BIT(0) -+#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) -+ -+/* security */ -+#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) -+#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) -+ -+#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) -+#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) -+#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) -+ -+/* D2D */ -+#define BUTTRESS_D2D_PWR_EN BIT(0) -+#define BUTTRESS_D2D_PWR_ACK BIT(4) -+ -+/* NDE */ -+#define NDE_VAL_MASK GENMASK(9, 0) -+#define NDE_SCALE_MASK GENMASK(12, 10) -+#define NDE_VALID_MASK BIT(13) -+#define NDE_RESVEC_MASK GENMASK(19, 16) -+#define NDE_IN_VBLANK_DIS_MASK BIT(31) -+ -+#define BUTTRESS_NDE_VAL_ACTIVE 48 -+#define BUTTRESS_NDE_SCALE_ACTIVE 2 -+#define BUTTRESS_NDE_VALID_ACTIVE 1 -+ -+#define BUTTRESS_NDE_VAL_DEFAULT 1023 -+#define BUTTRESS_NDE_SCALE_DEFAULT 2 -+#define BUTTRESS_NDE_VALID_DEFAULT 0 -+ -+/* IS and PS UCX control */ -+#define UCX_CTL_RESET BIT(0) -+#define UCX_CTL_RUN BIT(1) -+#define UCX_CTL_WAKEUP BIT(2) -+#define UCX_CTL_SPARE GENMASK(7, 3) -+#define UCX_STS_PWR GENMASK(17, 16) -+#define UCX_STS_SLEEPING BIT(18) -+ -+/* offset from PHY base */ -+#define PHY_CSI_CFG 0xc0 -+#define PHY_CSI_RCOMP_CONTROL 0xc8 -+#define PHY_CSI_BSCAN_EXCLUDE 0xd8 -+ -+#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) -+#define PHY_DPHY_DLL_OVRD(x) (0x14c + 0x100 * (x)) -+#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) -+#define PHY_CPHY_RX_CONTROL2(x) (0x114 + 0x100 * (x)) -+#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) -+#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) -+ -+/* PB registers */ -+#define INTERRUPT_STATUS 0x0 -+#define BTRS_LOCAL_INTERRUPT_MASK 0x4 -+#define GLOBAL_INTERRUPT_MASK 0x8 -+#define HM_ATS 0xc -+#define ATS_ERROR_LOG1 0x10 -+#define ATS_ERROR_LOG2 0x14 -+#define ATS_ERROR_CLEAR 0x18 -+#define CFI_0_ERROR_LOG 0x1c -+#define CFI_0_ERROR_CLEAR 0x20 -+#define HASH_CONFIG 0x2c -+#define TLBID_HASH_ENABLE_31_0 0x30 -+#define TLBID_HASH_ENABLE_63_32 0x34 -+#define TLBID_HASH_ENABLE_95_64 0x38 -+#define TLBID_HASH_ENABLE_127_96 0x3c -+#define CFI_1_ERROR_LOGGING 0x40 -+#define CFI_1_ERROR_CLEAR 0x44 -+#define IMR_ERROR_LOGGING_LOW 0x48 -+#define IMR_ERROR_LOGGING_HIGH 0x4c -+#define IMR_ERROR_CLEAR 0x50 -+#define PORT_ARBITRATION_WEIGHTS 0x54 -+#define IMR_ERROR_LOGGING_CFI_1_LOW 0x58 -+#define IMR_ERROR_LOGGING_CFI_1_HIGH 0x5c -+#define IMR_ERROR_CLEAR_CFI_1 0x60 -+#define BAR2_MISC_CONFIG 0x64 -+#define RSP_ID_CONFIG_AXI2CFI_0 0x68 -+#define RSP_ID_CONFIG_AXI2CFI_1 0x6c -+#define PB_DRIVER_PCODE_MAILBOX_STATUS 0x70 -+#define PB_DRIVER_PCODE_MAILBOX_INTERFACE 0x74 -+#define PORT_ARBITRATION_WEIGHTS_ATS 0x78 -+ -+#endif /* IPU7_BUTTRESS_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.c b/drivers/media/pci/intel/ipu7/ipu7-buttress.c -new file mode 100644 -index 000000000000..b350ca5678d0 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-buttress.c -@@ -0,0 +1,1193 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-buttress-regs.h" -+ -+#define BOOTLOADER_STATUS_OFFSET BUTTRESS_REG_FW_BOOT_PARAMS7 -+ -+#define BOOTLOADER_MAGIC_KEY 0xb00710adU -+ -+#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 -+#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 -+#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE -+ -+#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10U -+ -+#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) -+#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) -+#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) -+ -+#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC -+#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC -+#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) -+#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) -+ -+#define BUTTRESS_IPC_RESET_RETRY 2000U -+#define BUTTRESS_CSE_IPC_RESET_RETRY 4U -+#define BUTTRESS_IPC_CMD_SEND_RETRY 1U -+ -+struct ipu7_ipc_buttress_msg { -+ u32 cmd; -+ u32 expected_resp; -+ bool require_resp; -+ u8 cmd_size; -+}; -+ -+static const u32 ipu7_adev_irq_mask[2] = { -+ BUTTRESS_IRQ_IS_IRQ, -+ BUTTRESS_IRQ_PS_IRQ -+}; -+ -+int ipu_buttress_ipc_reset(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc) -+{ -+ unsigned int retries = BUTTRESS_IPC_RESET_RETRY; -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ u32 val = 0, csr_in_clr; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(dev, "Skip IPC reset for non-secure mode\n"); -+ return 0; -+ } -+ -+ mutex_lock(&b->ipc_mutex); -+ -+ /* Clear-by-1 CSR (all bits), corresponding internal states. */ -+ val = readl(isp->base + ipc->csr_in); -+ writel(val, isp->base + ipc->csr_in); -+ -+ /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ -+ writel(ENTRY, isp->base + ipc->csr_out); -+ /* -+ * Clear-by-1 all CSR bits EXCEPT following -+ * bits: -+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * C. Possibly custom bits, depending on -+ * their role. -+ */ -+ csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | -+ BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | -+ BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; -+ -+ do { -+ usleep_range(400, 500); -+ val = readl(isp->base + ipc->csr_in); -+ switch (val) { -+ case ENTRY | EXIT: -+ case ENTRY | EXIT | QUERY: -+ /* -+ * 1) Clear-by-1 CSR bits -+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2). -+ * 2) Set peer CSR bit -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -+ */ -+ writel(ENTRY | EXIT, isp->base + ipc->csr_in); -+ writel(QUERY, isp->base + ipc->csr_out); -+ break; -+ case ENTRY: -+ case ENTRY | QUERY: -+ /* -+ * 1) Clear-by-1 CSR bits -+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). -+ * 2) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ */ -+ writel(ENTRY | QUERY, isp->base + ipc->csr_in); -+ writel(ENTRY, isp->base + ipc->csr_out); -+ break; -+ case EXIT: -+ case EXIT | QUERY: -+ /* -+ * Clear-by-1 CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * 1) Clear incoming doorbell. -+ * 2) Clear-by-1 all CSR bits EXCEPT following -+ * bits: -+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * C. Possibly custom bits, depending on -+ * their role. -+ * 3) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ */ -+ writel(EXIT, isp->base + ipc->csr_in); -+ writel(0, isp->base + ipc->db0_in); -+ writel(csr_in_clr, isp->base + ipc->csr_in); -+ writel(EXIT, isp->base + ipc->csr_out); -+ -+ /* -+ * Read csr_in again to make sure if RST_PHASE2 is done. -+ * If csr_in is QUERY, it should be handled again. -+ */ -+ usleep_range(200, 300); -+ val = readl(isp->base + ipc->csr_in); -+ if (val & QUERY) { -+ dev_dbg(dev, -+ "RST_PHASE2 retry csr_in = %x\n", val); -+ break; -+ } -+ mutex_unlock(&b->ipc_mutex); -+ return 0; -+ case QUERY: -+ /* -+ * 1) Clear-by-1 CSR bit -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -+ * 2) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1 -+ */ -+ writel(QUERY, isp->base + ipc->csr_in); -+ writel(ENTRY, isp->base + ipc->csr_out); -+ break; -+ default: -+ dev_dbg_ratelimited(dev, "Unexpected CSR 0x%x\n", val); -+ break; -+ } -+ } while (retries--); -+ -+ mutex_unlock(&b->ipc_mutex); -+ dev_err(dev, "Timed out while waiting for CSE\n"); -+ -+ return -ETIMEDOUT; -+} -+ -+static void ipu_buttress_ipc_validity_close(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc) -+{ -+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, -+ isp->base + ipc->csr_out); -+} -+ -+static int -+ipu_buttress_ipc_validity_open(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc) -+{ -+ unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; -+ void __iomem *addr; -+ int ret; -+ u32 val; -+ -+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, -+ isp->base + ipc->csr_out); -+ -+ addr = isp->base + ipc->csr_in; -+ ret = readl_poll_timeout(addr, val, val & mask, 200, -+ BUTTRESS_IPC_VALIDITY_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); -+ ipu_buttress_ipc_validity_close(isp, ipc); -+ } -+ -+ return ret; -+} -+ -+static void ipu_buttress_ipc_recv(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc, u32 *ipc_msg) -+{ -+ if (ipc_msg) -+ *ipc_msg = readl(isp->base + ipc->data0_in); -+ writel(0, isp->base + ipc->db0_in); -+} -+ -+static int ipu_buttress_ipc_send_msg(struct ipu7_device *isp, -+ struct ipu7_ipc_buttress_msg *msg) -+{ -+ unsigned long tx_timeout_jiffies, rx_timeout_jiffies; -+ unsigned int retry = BUTTRESS_IPC_CMD_SEND_RETRY; -+ struct ipu_buttress *b = &isp->buttress; -+ struct ipu_buttress_ipc *ipc = &b->cse; -+ struct device *dev = &isp->pdev->dev; -+ int tout; -+ u32 val; -+ int ret; -+ -+ mutex_lock(&b->ipc_mutex); -+ -+ ret = ipu_buttress_ipc_validity_open(isp, ipc); -+ if (ret) { -+ dev_err(dev, "IPC validity open failed\n"); -+ goto out; -+ } -+ -+ tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); -+ rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); -+ -+try: -+ reinit_completion(&ipc->send_complete); -+ if (msg->require_resp) -+ reinit_completion(&ipc->recv_complete); -+ -+ dev_dbg(dev, "IPC command: 0x%x\n", msg->cmd); -+ writel(msg->cmd, isp->base + ipc->data0_out); -+ val = BUTTRESS_IU2CSEDB0_BUSY | msg->cmd_size; -+ writel(val, isp->base + ipc->db0_out); -+ -+ tout = wait_for_completion_timeout(&ipc->send_complete, -+ tx_timeout_jiffies); -+ if (!tout) { -+ dev_err(dev, "send IPC response timeout\n"); -+ if (!retry--) { -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ /* Try again if CSE is not responding on first try */ -+ writel(0, isp->base + ipc->db0_out); -+ goto try; -+ } -+ -+ if (!msg->require_resp) { -+ ret = -EIO; -+ goto out; -+ } -+ -+ tout = wait_for_completion_timeout(&ipc->recv_complete, -+ rx_timeout_jiffies); -+ if (!tout) { -+ dev_err(dev, "recv IPC response timeout\n"); -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ if (ipc->nack_mask && -+ (ipc->recv_data & ipc->nack_mask) == ipc->nack) { -+ dev_err(dev, "IPC NACK for cmd 0x%x\n", msg->cmd); -+ ret = -EIO; -+ goto out; -+ } -+ -+ if (ipc->recv_data != msg->expected_resp) { -+ dev_err(dev, -+ "expected resp: 0x%x, IPC response: 0x%x\n", -+ msg->expected_resp, ipc->recv_data); -+ ret = -EIO; -+ goto out; -+ } -+ -+ dev_dbg(dev, "IPC commands done\n"); -+ -+out: -+ ipu_buttress_ipc_validity_close(isp, ipc); -+ mutex_unlock(&b->ipc_mutex); -+ -+ return ret; -+} -+ -+static int ipu_buttress_ipc_send(struct ipu7_device *isp, -+ u32 ipc_msg, u32 size, bool require_resp, -+ u32 expected_resp) -+{ -+ struct ipu7_ipc_buttress_msg msg = { -+ .cmd = ipc_msg, -+ .cmd_size = size, -+ .require_resp = require_resp, -+ .expected_resp = expected_resp, -+ }; -+ -+ return ipu_buttress_ipc_send_msg(isp, &msg); -+} -+ -+static irqreturn_t ipu_buttress_call_isr(struct ipu7_bus_device *adev) -+{ -+ irqreturn_t ret = IRQ_WAKE_THREAD; -+ -+ if (!adev || !adev->auxdrv || !adev->auxdrv_data) -+ return IRQ_NONE; -+ -+ if (adev->auxdrv_data->isr) -+ ret = adev->auxdrv_data->isr(adev); -+ -+ if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) -+ ret = IRQ_NONE; -+ -+ return ret; -+} -+ -+irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) -+{ -+ struct ipu7_device *isp = isp_ptr; -+ struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ irqreturn_t ret = IRQ_NONE; -+ u32 pb_irq, pb_local_irq; -+ u32 disable_irqs = 0; -+ u32 irq_status; -+ unsigned int i; -+ -+ pm_runtime_get_noresume(dev); -+ -+ pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); -+ writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); -+ -+ /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ -+ pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); -+ if (pb_local_irq & ~BIT(0)) { -+ dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, -+ pb_local_irq); -+ dev_warn(dev, "Details: %x %x %x %x %x %x %x %x\n", -+ readl(isp->pb_base + ATS_ERROR_LOG1), -+ readl(isp->pb_base + ATS_ERROR_LOG2), -+ readl(isp->pb_base + CFI_0_ERROR_LOG), -+ readl(isp->pb_base + CFI_1_ERROR_LOGGING), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_LOW), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_HIGH), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_LOW), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_HIGH)); -+ } -+ -+ irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -+ if (!irq_status) { -+ pm_runtime_put_noidle(dev); -+ return IRQ_NONE; -+ } -+ -+ do { -+ writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR); -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask); i++) { -+ irqreturn_t r = ipu_buttress_call_isr(adev[i]); -+ -+ if (!(irq_status & ipu7_adev_irq_mask[i])) -+ continue; -+ -+ if (r == IRQ_WAKE_THREAD) { -+ ret = IRQ_WAKE_THREAD; -+ disable_irqs |= ipu7_adev_irq_mask[i]; -+ } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { -+ ret = IRQ_HANDLED; -+ } -+ } -+ -+ if (irq_status & (BUTTRESS_IRQS | BUTTRESS_IRQ_SAI_VIOLATION) && -+ ret == IRQ_NONE) -+ ret = IRQ_HANDLED; -+ -+ if (irq_status & BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING) { -+ dev_dbg(dev, "BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING\n"); -+ ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); -+ complete(&b->cse.recv_complete); -+ } -+ -+ if (irq_status & BUTTRESS_IRQ_CSE_CSR_SET) -+ dev_dbg(dev, "BUTTRESS_IRQ_CSE_CSR_SET\n"); -+ -+ if (irq_status & BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) { -+ dev_dbg(dev, "BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE\n"); -+ complete(&b->cse.send_complete); -+ } -+ -+ if (irq_status & BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -+ dev_dbg(dev, "BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ\n"); -+ -+ if (irq_status & BUTTRESS_IRQ_SAI_VIOLATION && -+ ipu_buttress_get_secure_mode(isp)) -+ dev_err(dev, "BUTTRESS_IRQ_SAI_VIOLATION\n"); -+ -+ irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -+ } while (irq_status); -+ -+ if (disable_irqs) -+ writel(BUTTRESS_IRQS & ~disable_irqs, -+ isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ -+ pm_runtime_put(dev); -+ -+ return ret; -+} -+ -+irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) -+{ -+ struct ipu7_device *isp = isp_ptr; -+ struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -+ const struct ipu7_auxdrv_data *drv_data = NULL; -+ irqreturn_t ret = IRQ_NONE; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask) && adev[i]; i++) { -+ drv_data = adev[i]->auxdrv_data; -+ if (!drv_data) -+ continue; -+ -+ if (drv_data->wake_isr_thread && -+ drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) -+ ret = IRQ_HANDLED; -+ } -+ -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ -+ return ret; -+} -+ -+static int isys_d2d_power(struct device *dev, bool on) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ int ret = 0; -+ u32 target = on ? BUTTRESS_D2D_PWR_ACK : 0U; -+ u32 val; -+ -+ dev_dbg(dev, "power %s isys d2d.\n", on ? "UP" : "DOWN"); -+ val = readl(isp->base + BUTTRESS_REG_D2D_CTL); -+ if ((val & BUTTRESS_D2D_PWR_ACK) == target) { -+ dev_info(dev, "d2d already in %s state.\n", -+ on ? "UP" : "DOWN"); -+ return 0; -+ } -+ -+ val = on ? val | BUTTRESS_D2D_PWR_EN : val & (~BUTTRESS_D2D_PWR_EN); -+ writel(val, isp->base + BUTTRESS_REG_D2D_CTL); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_D2D_CTL, -+ val, (val & BUTTRESS_D2D_PWR_ACK) == target, -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_err(dev, "power %s d2d timeout. status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+ -+ return ret; -+} -+ -+static void isys_nde_control(struct device *dev, bool on) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, value, scale, valid, resvec; -+ u32 nde_reg; -+ -+ if (on) { -+ value = BUTTRESS_NDE_VAL_ACTIVE; -+ scale = BUTTRESS_NDE_SCALE_ACTIVE; -+ valid = BUTTRESS_NDE_VALID_ACTIVE; -+ } else { -+ value = BUTTRESS_NDE_VAL_DEFAULT; -+ scale = BUTTRESS_NDE_SCALE_DEFAULT; -+ valid = BUTTRESS_NDE_VALID_DEFAULT; -+ } -+ -+ /* only set the fabrics resource ownership for ipu8 */ -+ nde_reg = is_ipu8(isp->hw_ver) ? IPU8_BUTTRESS_REG_NDE_CONTROL : -+ IPU7_BUTTRESS_REG_NDE_CONTROL; -+ resvec = is_ipu8(isp->hw_ver) ? 0x2 : 0xe; -+ val = FIELD_PREP(NDE_VAL_MASK, value) | -+ FIELD_PREP(NDE_SCALE_MASK, scale) | -+ FIELD_PREP(NDE_VALID_MASK, valid) | -+ FIELD_PREP(NDE_RESVEC_MASK, resvec); -+ -+ writel(val, isp->base + nde_reg); -+} -+ -+static int ipu7_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ -+ exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -+ if (ctrl->subsys_id == IPU_IS) { -+ ret = isys_d2d_power(dev, true); -+ if (ret) -+ goto out_power; -+ isys_nde_control(dev, true); -+ } -+ -+ /* request clock resource ownership */ -+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ val |= ctrl->ovrd_clk; -+ writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS, -+ val, (val & ctrl->own_clk_ack), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_warn(dev, "request clk ownership timeout. status 0x%x\n", -+ val); -+ -+ val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -+ -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power up timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ -+ /* release clock resource ownership */ -+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ val &= ~ctrl->ovrd_clk; -+ writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ -+out_power: -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+static int ipu7_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ -+ exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -+ val = 0x8U << ctrl->ratio_shift; -+ -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power down timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+out_power: -+ if (ctrl->subsys_id == IPU_IS && !ret) { -+ isys_d2d_power(dev, false); -+ isys_nde_control(dev, false); -+ } -+ -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+static int ipu8_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 sleep_level_reg = BUTTRESS_REG_SLEEP_LEVEL_STS; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -+ if (ctrl->subsys_id == IPU_IS) { -+ ret = isys_d2d_power(dev, true); -+ if (ret) -+ goto out_power; -+ isys_nde_control(dev, true); -+ } -+ -+ /* request ps_pll when psys freq > 400Mhz */ -+ if (ctrl->subsys_id == IPU_PS && ctrl->ratio > 0x10) { -+ writel(1, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -+ ret = readl_poll_timeout(isp->base + sleep_level_reg, -+ val, (val & ctrl->own_clk_ack), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_warn(dev, "ps_pll req ack timeout. status 0x%x\n", -+ val); -+ } -+ -+ val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power up timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+out_power: -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+static int ipu8_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -+ -+ if (ctrl->subsys_id == IPU_PS) -+ val = 0x10U << ctrl->ratio_shift; -+ else -+ val = 0x8U << ctrl->ratio_shift; -+ -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power down timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+out_power: -+ if (ctrl->subsys_id == IPU_IS && !ret) { -+ isys_d2d_power(dev, false); -+ isys_nde_control(dev, false); -+ } -+ -+ if (ctrl->subsys_id == IPU_PS) { -+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS); -+ if (val & ctrl->own_clk_ack) -+ writel(0, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -+ } -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+int ipu_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ -+ if (is_ipu8(isp->hw_ver)) -+ return ipu8_buttress_powerup(dev, ctrl); -+ -+ return ipu7_buttress_powerup(dev, ctrl); -+} -+ -+int ipu_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ -+ if (is_ipu8(isp->hw_ver)) -+ return ipu8_buttress_powerdown(dev, ctrl); -+ -+ return ipu7_buttress_powerdown(dev, ctrl); -+} -+ -+bool ipu_buttress_get_secure_mode(struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -+ -+ return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; -+} -+ -+bool ipu_buttress_auth_done(struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ if (!isp->secure_mode) -+ return true; -+ -+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -+ val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); -+ -+ return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_auth_done, "INTEL_IPU7"); -+ -+int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq) -+{ -+ u32 reg_val; -+ int ret; -+ -+ ret = pm_runtime_get_sync(&isp->isys->auxdev.dev); -+ if (ret < 0) { -+ pm_runtime_put(&isp->isys->auxdev.dev); -+ dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -+ return ret; -+ } -+ -+ reg_val = readl(isp->base + BUTTRESS_REG_IS_WORKPOINT_REQ); -+ -+ pm_runtime_put(&isp->isys->auxdev.dev); -+ -+ if (is_ipu8(isp->hw_ver)) -+ *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 25; -+ else -+ *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 50 / 3; -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_isys_freq, "INTEL_IPU7"); -+ -+int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq) -+{ -+ u32 reg_val; -+ int ret; -+ -+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -+ return ret; -+ } -+ -+ reg_val = readl(isp->base + BUTTRESS_REG_PS_WORKPOINT_REQ); -+ -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ -+ reg_val &= BUTTRESS_PS_FREQ_CTL_RATIO_MASK; -+ *freq = BUTTRESS_PS_FREQ_RATIO_STEP * reg_val; -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_psys_freq, "INTEL_IPU7"); -+ -+int ipu_buttress_reset_authentication(struct ipu7_device *isp) -+{ -+ struct device *dev = &isp->pdev->dev; -+ int ret; -+ u32 val; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(dev, "Skip auth for non-secure mode\n"); -+ return 0; -+ } -+ -+ writel(BUTTRESS_FW_RESET_CTL_START, isp->base + -+ BUTTRESS_REG_FW_RESET_CTL); -+ -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, -+ val & BUTTRESS_FW_RESET_CTL_DONE, 500, -+ BUTTRESS_CSE_FWRESET_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "Time out while resetting authentication state\n"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "FW reset for authentication done\n"); -+ writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); -+ /* leave some time for HW restore */ -+ usleep_range(800, 1000); -+ -+ return 0; -+} -+ -+int ipu_buttress_authenticate(struct ipu7_device *isp) -+{ -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ u32 data, mask, done, fail; -+ int ret; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(dev, "Skip auth for non-secure mode\n"); -+ return 0; -+ } -+ -+ mutex_lock(&b->auth_mutex); -+ -+ if (ipu_buttress_auth_done(isp)) { -+ ret = 0; -+ goto out_unlock; -+ } -+ -+ /* -+ * BUTTRESS_REG_FW_SOURCE_BASE needs to be set with FW CPD -+ * package address for secure mode. -+ */ -+ -+ writel(isp->cpd_fw->size, isp->base + BUTTRESS_REG_FW_SOURCE_SIZE); -+ writel(sg_dma_address(isp->psys->fw_sgt.sgl), -+ isp->base + BUTTRESS_REG_FW_SOURCE_BASE); -+ -+ /* -+ * Write boot_load into IU2CSEDATA0 -+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -+ */ -+ dev_info(dev, "Sending BOOT_LOAD to CSE\n"); -+ ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, -+ 1, true, -+ BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); -+ if (ret) { -+ dev_err(dev, "CSE boot_load failed\n"); -+ goto out_unlock; -+ } -+ -+ mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; -+ done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; -+ fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -+ ((data & mask) == done || -+ (data & mask) == fail), 500, -+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "CSE boot_load timeout\n"); -+ goto out_unlock; -+ } -+ -+ if ((data & mask) == fail) { -+ dev_err(dev, "CSE auth failed\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ ret = readl_poll_timeout(isp->base + BOOTLOADER_STATUS_OFFSET, -+ data, data == BOOTLOADER_MAGIC_KEY, 500, -+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "Unexpected magic number 0x%x\n", data); -+ goto out_unlock; -+ } -+ -+ /* -+ * Write authenticate_run into IU2CSEDATA0 -+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -+ */ -+ dev_info(dev, "Sending AUTHENTICATE_RUN to CSE\n"); -+ ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, -+ 1, true, -+ BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); -+ if (ret) { -+ dev_err(dev, "CSE authenticate_run failed\n"); -+ goto out_unlock; -+ } -+ -+ done = BUTTRESS_SECURITY_CTL_AUTH_DONE; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -+ ((data & mask) == done || -+ (data & mask) == fail), 500, -+ BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "CSE authenticate timeout\n"); -+ goto out_unlock; -+ } -+ -+ if ((data & mask) == fail) { -+ dev_err(dev, "CSE boot_load failed\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ dev_info(dev, "CSE authenticate_run done\n"); -+ -+out_unlock: -+ mutex_unlock(&b->auth_mutex); -+ -+ return ret; -+} -+ -+static int ipu_buttress_send_tsc_request(struct ipu7_device *isp) -+{ -+ u32 val, mask, done; -+ int ret; -+ -+ mask = BUTTRESS_PWR_STATUS_HH_STATUS_MASK; -+ -+ writel(BUTTRESS_TSC_CMD_START_TSC_SYNC, -+ isp->base + BUTTRESS_REG_TSC_CMD); -+ -+ val = readl(isp->base + BUTTRESS_REG_PWR_STATUS); -+ val = FIELD_GET(mask, val); -+ if (val == BUTTRESS_PWR_STATUS_HH_STATE_ERR) { -+ dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); -+ return -EINVAL; -+ } -+ -+ done = BUTTRESS_PWR_STATUS_HH_STATE_DONE; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, val, -+ FIELD_GET(mask, val) == done, 500, -+ BUTTRESS_TSC_SYNC_TIMEOUT_US); -+ if (ret) -+ dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); -+ -+ return ret; -+} -+ -+int ipu_buttress_start_tsc_sync(struct ipu7_device *isp) -+{ -+ void __iomem *base = isp->base; -+ unsigned int i; -+ u32 val; -+ -+ if (is_ipu8(isp->hw_ver)) { -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -+ if (val == 1) -+ return 0; -+ usleep_range(40, 50); -+ } -+ -+ dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -+ return -ETIMEDOUT; -+ } -+ -+ if (is_ipu7p5(isp->hw_ver)) { -+ val = readl(base + BUTTRESS_REG_TSC_CTL); -+ val |= BUTTRESS_SEL_PB_TIMESTAMP; -+ writel(val, base + BUTTRESS_REG_TSC_CTL); -+ -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -+ if (val == 1) -+ return 0; -+ usleep_range(40, 50); -+ } -+ -+ dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -+ -+ return -ETIMEDOUT; -+ } -+ -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ int ret; -+ -+ ret = ipu_buttress_send_tsc_request(isp); -+ if (ret != -ETIMEDOUT) -+ return ret; -+ -+ val = readl(base + BUTTRESS_REG_TSC_CTL); -+ val = val | BUTTRESS_TSW_WA_SOFT_RESET; -+ writel(val, base + BUTTRESS_REG_TSC_CTL); -+ val = val & (~BUTTRESS_TSW_WA_SOFT_RESET); -+ writel(val, base + BUTTRESS_REG_TSC_CTL); -+ } -+ -+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); -+ -+ return -ETIMEDOUT; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_start_tsc_sync, "INTEL_IPU7"); -+ -+void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val) -+{ -+ unsigned long flags; -+ u32 tsc_hi, tsc_lo; -+ -+ local_irq_save(flags); -+ if (is_ipu7(isp->hw_ver)) { -+ tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); -+ tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI); -+ } else { -+ tsc_lo = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_LO); -+ tsc_hi = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_HI); -+ } -+ *val = (u64)tsc_hi << 32 | tsc_lo; -+ local_irq_restore(flags); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_read, "INTEL_IPU7"); -+ -+u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp) -+{ -+ u64 ns = ticks * 10000; -+ -+ /* -+ * converting TSC tick count to ns is calculated by: -+ * Example (TSC clock frequency is 19.2MHz): -+ * ns = ticks * 1000 000 000 / 19.2Mhz -+ * = ticks * 1000 000 000 / 19200000Hz -+ * = ticks * 10000 / 192 ns -+ */ -+ return div_u64(ns, isp->buttress.ref_clk); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_ticks_to_ns, "INTEL_IPU7"); -+ -+/* trigger uc control to wakeup fw */ -+void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); -+ val |= UCX_CTL_WAKEUP; -+ writel(val, isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_is_uc, "INTEL_IPU7"); -+ -+void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); -+ val |= UCX_CTL_WAKEUP; -+ writel(val, isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_ps_uc, "INTEL_IPU7"); -+ -+static const struct x86_cpu_id ipu_misc_cfg_exclusion[] = { -+ X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0), -+ {}, -+}; -+ -+static void ipu_buttress_setup(struct ipu7_device *isp) -+{ -+ struct device *dev = &isp->pdev->dev; -+ u32 val; -+ -+ /* program PB BAR */ -+#define WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19) -+ writel(0, isp->pb_base + GLOBAL_INTERRUPT_MASK); -+ val = readl(isp->pb_base + BAR2_MISC_CONFIG); -+ if (is_ipu7(isp->hw_ver) || x86_match_cpu(ipu_misc_cfg_exclusion)) -+ val |= 0x100U; -+ else -+ val |= FIELD_PREP(WRXREQOP_OVRD_VAL_MASK, 0xf) | -+ BIT(18) | 0x100U; -+ -+ writel(val, isp->pb_base + BAR2_MISC_CONFIG); -+ val = readl(isp->pb_base + BAR2_MISC_CONFIG); -+ -+ if (is_ipu8(isp->hw_ver)) { -+ writel(BIT(13), isp->pb_base + TLBID_HASH_ENABLE_63_32); -+ writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -+ dev_dbg(dev, "IPU8 TLBID_HASH %x %x\n", -+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -+ readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -+ } else if (is_ipu7p5(isp->hw_ver)) { -+ writel(BIT(14), isp->pb_base + TLBID_HASH_ENABLE_63_32); -+ writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -+ dev_dbg(dev, "IPU7P5 TLBID_HASH %x %x\n", -+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -+ readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -+ } else { -+ writel(BIT(22), isp->pb_base + TLBID_HASH_ENABLE_63_32); -+ writel(BIT(1), isp->pb_base + TLBID_HASH_ENABLE_127_96); -+ dev_dbg(dev, "TLBID_HASH %x %x\n", -+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -+ readl(isp->pb_base + TLBID_HASH_ENABLE_127_96)); -+ } -+ -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_CLEAR); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_MASK); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ /* LNL SW workaround for PS PD hang when PS sub-domain during PD */ -+ writel(PS_FSM_CG, isp->base + BUTTRESS_REG_CG_CTRL_BITS); -+} -+ -+void ipu_buttress_restore(struct ipu7_device *isp) -+{ -+ struct ipu_buttress *b = &isp->buttress; -+ -+ ipu_buttress_setup(isp); -+ -+ writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_IDLE_WDT); -+} -+ -+int ipu_buttress_init(struct ipu7_device *isp) -+{ -+ int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ u32 val; -+ -+ mutex_init(&b->power_mutex); -+ mutex_init(&b->auth_mutex); -+ mutex_init(&b->cons_mutex); -+ mutex_init(&b->ipc_mutex); -+ init_completion(&b->cse.send_complete); -+ init_completion(&b->cse.recv_complete); -+ -+ b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; -+ b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; -+ b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; -+ b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; -+ b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; -+ b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; -+ b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; -+ b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; -+ -+ isp->secure_mode = ipu_buttress_get_secure_mode(isp); -+ val = readl(isp->base + BUTTRESS_REG_IPU_SKU); -+ dev_info(dev, "IPU%u SKU %u in %s mode mask 0x%x\n", val & 0xfU, -+ (val >> 4) & 0x7U, isp->secure_mode ? "secure" : "non-secure", -+ readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); -+ b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_IDLE_WDT); -+ b->ref_clk = 384; -+ -+ ipu_buttress_setup(isp); -+ -+ /* Retry couple of times in case of CSE initialization is delayed */ -+ do { -+ ret = ipu_buttress_ipc_reset(isp, &b->cse); -+ if (ret) { -+ dev_warn(dev, "IPC reset protocol failed, retrying\n"); -+ } else { -+ dev_dbg(dev, "IPC reset done\n"); -+ return 0; -+ } -+ } while (ipc_reset_retry--); -+ -+ dev_err(dev, "IPC reset protocol failed\n"); -+ -+ mutex_destroy(&b->power_mutex); -+ mutex_destroy(&b->auth_mutex); -+ mutex_destroy(&b->cons_mutex); -+ mutex_destroy(&b->ipc_mutex); -+ -+ return ret; -+} -+ -+void ipu_buttress_exit(struct ipu7_device *isp) -+{ -+ struct ipu_buttress *b = &isp->buttress; -+ -+ writel(0, isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ mutex_destroy(&b->power_mutex); -+ mutex_destroy(&b->auth_mutex); -+ mutex_destroy(&b->cons_mutex); -+ mutex_destroy(&b->ipc_mutex); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.h b/drivers/media/pci/intel/ipu7/ipu7-buttress.h -new file mode 100644 -index 000000000000..8da7dd612575 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-buttress.h -@@ -0,0 +1,77 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BUTTRESS_H -+#define IPU7_BUTTRESS_H -+ -+#include -+#include -+#include -+#include -+ -+struct device; -+struct ipu7_device; -+ -+struct ipu_buttress_ctrl { -+ u32 subsys_id; -+ u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; -+ u32 ratio; -+ u32 ratio_shift; -+ u32 cdyn; -+ u32 cdyn_shift; -+ u32 ovrd_clk; -+ u32 own_clk_ack; -+}; -+ -+struct ipu_buttress_ipc { -+ struct completion send_complete; -+ struct completion recv_complete; -+ u32 nack; -+ u32 nack_mask; -+ u32 recv_data; -+ u32 csr_out; -+ u32 csr_in; -+ u32 db0_in; -+ u32 db0_out; -+ u32 data0_out; -+ u32 data0_in; -+}; -+ -+struct ipu_buttress { -+ struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; -+ struct ipu_buttress_ipc cse; -+ u32 psys_min_freq; -+ u32 wdt_cached_value; -+ u8 psys_force_ratio; -+ bool force_suspend; -+ u32 ref_clk; -+}; -+ -+int ipu_buttress_ipc_reset(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc); -+int ipu_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl); -+int ipu_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl); -+bool ipu_buttress_get_secure_mode(struct ipu7_device *isp); -+int ipu_buttress_authenticate(struct ipu7_device *isp); -+int ipu_buttress_reset_authentication(struct ipu7_device *isp); -+bool ipu_buttress_auth_done(struct ipu7_device *isp); -+int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq); -+int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq); -+int ipu_buttress_start_tsc_sync(struct ipu7_device *isp); -+void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val); -+u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp); -+ -+irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr); -+irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr); -+int ipu_buttress_init(struct ipu7_device *isp); -+void ipu_buttress_exit(struct ipu7_device *isp); -+void ipu_buttress_csi_port_config(struct ipu7_device *isp, -+ u32 legacy, u32 combo); -+void ipu_buttress_restore(struct ipu7_device *isp); -+void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp); -+void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp); -+#endif /* IPU7_BUTTRESS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.c b/drivers/media/pci/intel/ipu7/ipu7-cpd.c -new file mode 100644 -index 000000000000..25dd71e1809e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-cpd.c -@@ -0,0 +1,277 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2015 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-cpd.h" -+ -+/* $CPD */ -+#define CPD_HDR_MARK 0x44504324 -+ -+/* Maximum size is 4K DWORDs or 16KB */ -+#define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) -+ -+#define CPD_MANIFEST_IDX 0 -+#define CPD_BINARY_START_IDX 1U -+#define CPD_METADATA_START_IDX 2U -+#define CPD_BINARY_NUM 2U /* ISYS + PSYS */ -+/* -+ * Entries include: -+ * 1 manifest entry. -+ * 1 metadata entry for each sub system(ISYS and PSYS). -+ * 1 binary entry for each sub system(ISYS and PSYS). -+ */ -+#define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U) -+ -+#define CPD_METADATA_ATTR 0xa -+#define CPD_METADATA_IPL 0x1c -+#define ONLINE_METADATA_SIZE 128U -+#define ONLINE_METADATA_LINES 6U -+ -+struct ipu7_cpd_hdr { -+ u32 hdr_mark; -+ u32 ent_cnt; -+ u8 hdr_ver; -+ u8 ent_ver; -+ u8 hdr_len; -+ u8 rsvd; -+ u8 partition_name[4]; -+ u32 crc32; -+} __packed; -+ -+struct ipu7_cpd_ent { -+ u8 name[12]; -+ u32 offset; -+ u32 len; -+ u8 rsvd[4]; -+} __packed; -+ -+struct ipu7_cpd_metadata_hdr { -+ u32 type; -+ u32 len; -+} __packed; -+ -+struct ipu7_cpd_metadata_attr { -+ struct ipu7_cpd_metadata_hdr hdr; -+ u8 compression_type; -+ u8 encryption_type; -+ u8 rsvd[2]; -+ u32 uncompressed_size; -+ u32 compressed_size; -+ u32 module_id; -+ u8 hash[48]; -+} __packed; -+ -+struct ipu7_cpd_metadata_ipl { -+ struct ipu7_cpd_metadata_hdr hdr; -+ u32 param[4]; -+ u8 rsvd[8]; -+} __packed; -+ -+struct ipu7_cpd_metadata { -+ struct ipu7_cpd_metadata_attr attr; -+ struct ipu7_cpd_metadata_ipl ipl; -+} __packed; -+ -+static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx) -+{ -+ const struct ipu7_cpd_hdr *cpd_hdr = cpd; -+ -+ return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx; -+} -+ -+#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0) -+ -+static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) -+{ -+ struct ipu7_cpd_ent *cpd_ent = -+ ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -+ -+ return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); -+} -+ -+static int ipu7_cpd_validate_cpd(struct ipu7_device *isp, -+ const void *cpd, unsigned long data_size) -+{ -+ const struct ipu7_cpd_hdr *cpd_hdr = cpd; -+ struct device *dev = &isp->pdev->dev; -+ struct ipu7_cpd_ent *ent; -+ unsigned int i; -+ u8 len; -+ -+ len = cpd_hdr->hdr_len; -+ -+ /* Ensure cpd hdr is within moduledata */ -+ if (data_size < len) { -+ dev_err(dev, "Invalid CPD moduledata size\n"); -+ return -EINVAL; -+ } -+ -+ /* Check for CPD file marker */ -+ if (cpd_hdr->hdr_mark != CPD_HDR_MARK) { -+ dev_err(dev, "Invalid CPD header marker\n"); -+ return -EINVAL; -+ } -+ -+ /* Sanity check for CPD entry header */ -+ if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) { -+ dev_err(dev, "Invalid CPD entry number %d\n", -+ cpd_hdr->ent_cnt); -+ return -EINVAL; -+ } -+ if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { -+ dev_err(dev, "Invalid CPD entry headers\n"); -+ return -EINVAL; -+ } -+ -+ /* Ensure that all entries are within moduledata */ -+ ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len); -+ for (i = 0; i < cpd_hdr->ent_cnt; i++) { -+ if (data_size < ent->offset || -+ data_size - ent->offset < ent->len) { -+ dev_err(dev, "Invalid CPD entry %d\n", i); -+ return -EINVAL; -+ } -+ ent++; -+ } -+ -+ return 0; -+} -+ -+static int ipu7_cpd_validate_metadata(struct ipu7_device *isp, -+ const void *cpd, int idx) -+{ -+ const struct ipu7_cpd_ent *cpd_ent = -+ ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -+ const struct ipu7_cpd_metadata *metadata = -+ ipu7_cpd_get_metadata(cpd, idx); -+ struct device *dev = &isp->pdev->dev; -+ -+ /* Sanity check for metadata size */ -+ if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { -+ dev_err(dev, "Invalid metadata size\n"); -+ return -EINVAL; -+ } -+ -+ /* Validate type and length of metadata sections */ -+ if (metadata->attr.hdr.type != CPD_METADATA_ATTR) { -+ dev_err(dev, "Invalid metadata attr type (%d)\n", -+ metadata->attr.hdr.type); -+ return -EINVAL; -+ } -+ if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { -+ dev_err(dev, "Invalid metadata attr size (%d)\n", -+ metadata->attr.hdr.len); -+ return -EINVAL; -+ } -+ if (metadata->ipl.hdr.type != CPD_METADATA_IPL) { -+ dev_err(dev, "Invalid metadata ipl type (%d)\n", -+ metadata->ipl.hdr.type); -+ return -EINVAL; -+ } -+ if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { -+ dev_err(dev, "Invalid metadata ipl size (%d)\n", -+ metadata->ipl.hdr.len); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file, -+ unsigned long cpd_file_size) -+{ -+ struct device *dev = &isp->pdev->dev; -+ struct ipu7_cpd_ent *ent; -+ unsigned int i; -+ int ret; -+ char *buf; -+ -+ ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size); -+ if (ret) { -+ dev_err(dev, "Invalid CPD in file\n"); -+ return -EINVAL; -+ } -+ -+ /* Sanity check for manifest size */ -+ ent = ipu7_cpd_get_manifest(cpd_file); -+ if (ent->len > MAX_MANIFEST_SIZE) { -+ dev_err(dev, "Invalid manifest size\n"); -+ return -EINVAL; -+ } -+ -+ /* Validate metadata */ -+ for (i = 0; i < CPD_BINARY_NUM; i++) { -+ ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); -+ if (ret) { -+ dev_err(dev, "Invalid metadata%d\n", i); -+ return ret; -+ } -+ } -+ -+ /* Get fw binary version. */ -+ buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ for (i = 0; i < CPD_BINARY_NUM; i++) { -+ char *lines[ONLINE_METADATA_LINES]; -+ char *info = buf; -+ unsigned int l; -+ -+ ent = ipu7_cpd_get_entry(cpd_file, -+ CPD_BINARY_START_IDX + i * 2U); -+ memcpy(info, (u8 *)cpd_file + ent->offset + ent->len - -+ ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE); -+ for (l = 0; l < ONLINE_METADATA_LINES; l++) { -+ lines[l] = strsep((char **)&info, "\n"); -+ if (!lines[l]) -+ break; -+ } -+ if (l < ONLINE_METADATA_LINES) { -+ dev_err(dev, "Failed to parse fw binary%d info.\n", i); -+ continue; -+ } -+ dev_info(dev, "FW binary%d info:\n", i); -+ dev_info(dev, "Name: %s\n", lines[1]); -+ dev_info(dev, "Version: %s\n", lines[2]); -+ dev_info(dev, "Timestamp: %s\n", lines[3]); -+ dev_info(dev, "Commit: %s\n", lines[4]); -+ } -+ kfree(buf); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7"); -+ -+int ipu7_cpd_copy_binary(const void *cpd, const char *name, -+ void *code_region, u32 *entry) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < CPD_BINARY_NUM; i++) { -+ const struct ipu7_cpd_ent *binary = -+ ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U); -+ const struct ipu7_cpd_metadata *metadata = -+ ipu7_cpd_get_metadata(cpd, i); -+ -+ if (!strncmp(binary->name, name, sizeof(binary->name))) { -+ memcpy(code_region + metadata->ipl.param[0], -+ cpd + binary->offset, binary->len); -+ *entry = metadata->ipl.param[2]; -+ return 0; -+ } -+ } -+ -+ return -ENOENT; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.h b/drivers/media/pci/intel/ipu7/ipu7-cpd.h -new file mode 100644 -index 000000000000..b4178848c6b9 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-cpd.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2015 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_CPD_H -+#define IPU7_CPD_H -+ -+struct ipu7_device; -+ -+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, -+ const void *cpd_file, -+ unsigned long cpd_file_size); -+int ipu7_cpd_copy_binary(const void *cpd, const char *name, -+ void *code_region, u32 *entry); -+#endif /* IPU7_CPD_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.c b/drivers/media/pci/intel/ipu7/ipu7-dma.c -new file mode 100644 -index 000000000000..d974e13e7933 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-dma.c -@@ -0,0 +1,478 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-mmu.h" -+ -+struct vm_info { -+ struct list_head list; -+ struct page **pages; -+ dma_addr_t ipu7_iova; -+ void *vaddr; -+ unsigned long size; -+}; -+ -+static struct vm_info *get_vm_info(struct ipu7_mmu *mmu, dma_addr_t iova) -+{ -+ struct vm_info *info, *save; -+ -+ list_for_each_entry_safe(info, save, &mmu->vma_list, list) { -+ if (iova >= info->ipu7_iova && -+ iova < (info->ipu7_iova + info->size)) -+ return info; -+ } -+ -+ return NULL; -+} -+ -+static void __clear_buffer(struct page *page, size_t size, unsigned long attrs) -+{ -+ void *ptr; -+ -+ if (!page) -+ return; -+ /* -+ * Ensure that the allocated pages are zeroed, and that any data -+ * lurking in the kernel direct-mapped region is invalidated. -+ */ -+ ptr = page_address(page); -+ memset(ptr, 0, size); -+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -+ clflush_cache_range(ptr, size); -+} -+ -+static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs) -+{ -+ unsigned int count = PHYS_PFN(size); -+ unsigned int array_size = count * sizeof(struct page *); -+ struct page **pages; -+ int i = 0; -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ return NULL; -+ -+ gfp |= __GFP_NOWARN; -+ -+ while (count) { -+ int j, order = __fls(count); -+ -+ pages[i] = alloc_pages(gfp, order); -+ while (!pages[i] && order) -+ pages[i] = alloc_pages(gfp, --order); -+ if (!pages[i]) -+ goto error; -+ -+ if (order) { -+ split_page(pages[i], order); -+ j = 1U << order; -+ while (j--) -+ pages[i + j] = pages[i] + j; -+ } -+ -+ __clear_buffer(pages[i], PAGE_SIZE << order, attrs); -+ i += 1U << order; -+ count -= 1U << order; -+ } -+ -+ return pages; -+error: -+ while (i--) -+ if (pages[i]) -+ __free_pages(pages[i], 0); -+ kvfree(pages); -+ return NULL; -+} -+ -+static void __free_buffer(struct page **pages, size_t size, unsigned long attrs) -+{ -+ unsigned int count = PHYS_PFN(size); -+ unsigned int i; -+ -+ for (i = 0; i < count && pages[i]; i++) { -+ __clear_buffer(pages[i], PAGE_SIZE, attrs); -+ __free_pages(pages[i], 0); -+ } -+ -+ kvfree(pages); -+} -+ -+void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -+ size_t size) -+{ -+ void *vaddr; -+ u32 offset; -+ struct vm_info *info; -+ struct ipu7_mmu *mmu = sys->mmu; -+ -+ info = get_vm_info(mmu, dma_handle); -+ if (WARN_ON(!info)) -+ return; -+ -+ offset = dma_handle - info->ipu7_iova; -+ if (WARN_ON(size > (info->size - offset))) -+ return; -+ -+ vaddr = info->vaddr + offset; -+ clflush_cache_range(vaddr, size); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_single, "INTEL_IPU7"); -+ -+void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ unsigned int nents) -+{ -+ struct scatterlist *sg; -+ int i; -+ -+ for_each_sg(sglist, sg, nents, i) -+ clflush_cache_range(sg_virt(sg), sg->length); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sg, "INTEL_IPU7"); -+ -+void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt) -+{ -+ ipu7_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sgtable, "INTEL_IPU7"); -+ -+void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -+ dma_addr_t *dma_handle, gfp_t gfp, -+ unsigned long attrs) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct pci_dev *pdev = sys->isp->pdev; -+ dma_addr_t pci_dma_addr, ipu7_iova; -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct vm_info *info; -+ unsigned long count; -+ struct page **pages; -+ struct iova *iova; -+ unsigned int i; -+ int ret; -+ -+ info = kzalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ -+ size = PAGE_ALIGN(size); -+ count = PHYS_PFN(size); -+ -+ iova = alloc_iova(&mmu->dmap->iovad, count, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -+ if (!iova) -+ goto out_kfree; -+ -+ pages = __alloc_buffer(size, gfp, attrs); -+ if (!pages) -+ goto out_free_iova; -+ -+ dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", -+ size, iova->pfn_lo, iova->pfn_hi); -+ for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { -+ pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, -+ PAGE_SIZE, DMA_BIDIRECTIONAL, -+ attrs); -+ dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", -+ &pci_dma_addr); -+ if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { -+ dev_err(dev, "pci_dma_mapping for page[%d] failed", i); -+ goto out_unmap; -+ } -+ -+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, -+ PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, -+ PAGE_SIZE); -+ if (ret) { -+ dev_err(dev, "ipu7_mmu_map for pci_dma[%d] %pad failed", -+ i, &pci_dma_addr); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, -+ PAGE_SIZE, DMA_BIDIRECTIONAL, -+ attrs); -+ goto out_unmap; -+ } -+ } -+ -+ info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); -+ if (!info->vaddr) -+ goto out_unmap; -+ -+ *dma_handle = PFN_PHYS(iova->pfn_lo); -+ -+ info->pages = pages; -+ info->ipu7_iova = *dma_handle; -+ info->size = size; -+ list_add(&info->list, &mmu->vma_list); -+ -+ return info->vaddr; -+ -+out_unmap: -+ while (i--) { -+ ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ ipu7_iova); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -+ DMA_BIDIRECTIONAL, attrs); -+ -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, ipu7_iova, PAGE_SIZE); -+ } -+ -+ __free_buffer(pages, size, attrs); -+ -+out_free_iova: -+ __free_iova(&mmu->dmap->iovad, iova); -+out_kfree: -+ kfree(info); -+ -+ return NULL; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_alloc, "INTEL_IPU7"); -+ -+void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -+ dma_addr_t dma_handle, unsigned long attrs) -+{ -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct pci_dev *pdev = sys->isp->pdev; -+ struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); -+ dma_addr_t pci_dma_addr, ipu7_iova; -+ struct vm_info *info; -+ struct page **pages; -+ unsigned int i; -+ -+ if (WARN_ON(!iova)) -+ return; -+ -+ info = get_vm_info(mmu, dma_handle); -+ if (WARN_ON(!info)) -+ return; -+ -+ if (WARN_ON(!info->vaddr)) -+ return; -+ -+ if (WARN_ON(!info->pages)) -+ return; -+ -+ list_del(&info->list); -+ -+ size = PAGE_ALIGN(size); -+ -+ pages = info->pages; -+ -+ vunmap(vaddr); -+ -+ for (i = 0; i < PHYS_PFN(size); i++) { -+ ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ ipu7_iova); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -+ DMA_BIDIRECTIONAL, attrs); -+ } -+ -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ -+ __free_buffer(pages, size, attrs); -+ -+ mmu->tlb_invalidate(mmu); -+ -+ __free_iova(&mmu->dmap->iovad, iova); -+ -+ kfree(info); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_free, "INTEL_IPU7"); -+ -+int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -+ void *addr, dma_addr_t iova, size_t size, -+ unsigned long attrs) -+{ -+ struct ipu7_mmu *mmu = sys->mmu; -+ size_t count = PFN_UP(size); -+ struct vm_info *info; -+ size_t i; -+ int ret; -+ -+ info = get_vm_info(mmu, iova); -+ if (!info) -+ return -EFAULT; -+ -+ if (!info->vaddr) -+ return -EFAULT; -+ -+ if (vma->vm_start & ~PAGE_MASK) -+ return -EINVAL; -+ -+ if (size > info->size) -+ return -EFAULT; -+ -+ for (i = 0; i < count; i++) { -+ ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), -+ info->pages[i]); -+ if (ret < 0) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct iova *iova = find_iova(&mmu->dmap->iovad, -+ PHYS_PFN(sg_dma_address(sglist))); -+ struct scatterlist *sg; -+ dma_addr_t pci_dma_addr; -+ unsigned int i; -+ -+ if (!nents) -+ return; -+ -+ if (WARN_ON(!iova)) -+ return; -+ -+ /* -+ * Before IPU7 mmu unmap, return the pci dma address back to sg -+ * assume the nents is less than orig_nents as the least granule -+ * is 1 SZ_4K page -+ */ -+ dev_dbg(dev, "trying to unmap concatenated %u ents\n", nents); -+ for_each_sg(sglist, sg, nents, i) { -+ dev_dbg(dev, "unmap sg[%d] %pad size %u\n", i, -+ &sg_dma_address(sg), sg_dma_len(sg)); -+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ sg_dma_address(sg)); -+ dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", -+ &pci_dma_addr, i); -+ sg_dma_address(sg) = pci_dma_addr; -+ } -+ -+ dev_dbg(dev, "ipu7_mmu_unmap low pfn %lu high pfn %lu\n", -+ iova->pfn_lo, iova->pfn_hi); -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ -+ mmu->tlb_invalidate(mmu); -+ __free_iova(&mmu->dmap->iovad, iova); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7"); -+ -+int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct scatterlist *sg; -+ struct iova *iova; -+ size_t npages = 0; -+ unsigned long iova_addr; -+ int i; -+ -+ for_each_sg(sglist, sg, nents, i) { -+ if (sg->offset) { -+ dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", -+ i, sg->offset); -+ return -EFAULT; -+ } -+ } -+ -+ for_each_sg(sglist, sg, nents, i) -+ npages += PFN_UP(sg_dma_len(sg)); -+ -+ dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n", -+ nents, npages); -+ -+ if (attrs & DMA_ATTR_RESERVE_REGION) { -+ /* -+ * Reserve iova with size aligned to IPU_FW_CODE_REGION_SIZE. -+ * Only apply for non-secure mode. -+ */ -+ unsigned long lo, hi; -+ -+ lo = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_START); -+ hi = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_END) - 1U; -+ iova = reserve_iova(&mmu->dmap->iovad, lo, hi); -+ if (!iova) { -+ dev_err(dev, "Reserve iova[%lx:%lx] failed.\n", lo, hi); -+ return -ENOMEM; -+ } -+ dev_dbg(dev, "iova[%lx:%lx] reserved for FW code.\n", lo, hi); -+ } else { -+ iova = alloc_iova(&mmu->dmap->iovad, npages, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), -+ 0); -+ if (!iova) -+ return 0; -+ } -+ -+ dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, -+ iova->pfn_hi); -+ -+ iova_addr = iova->pfn_lo; -+ for_each_sg(sglist, sg, nents, i) { -+ phys_addr_t iova_pa; -+ int ret; -+ -+ iova_pa = PFN_PHYS(iova_addr); -+ dev_dbg(dev, "mapping entry %d: iova %pap phy %pap size %d\n", -+ i, &iova_pa, &sg_dma_address(sg), sg_dma_len(sg)); -+ -+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -+ sg_dma_address(sg), -+ PAGE_ALIGN(sg_dma_len(sg))); -+ if (ret) -+ goto out_fail; -+ -+ sg_dma_address(sg) = PFN_PHYS(iova_addr); -+ -+ iova_addr += PFN_UP(sg_dma_len(sg)); -+ } -+ -+ dev_dbg(dev, "dmamap %d ents %zu pages mapped\n", nents, npages); -+ -+ return nents; -+ -+out_fail: -+ ipu7_dma_unmap_sg(sys, sglist, i, dir, attrs); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sg, "INTEL_IPU7"); -+ -+int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs) -+{ -+ int nents; -+ -+ nents = ipu7_dma_map_sg(sys, sgt->sgl, sgt->nents, dir, attrs); -+ if (nents < 0) -+ return nents; -+ -+ sgt->nents = nents; -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sgtable, "INTEL_IPU7"); -+ -+void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs) -+{ -+ ipu7_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sgtable, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.h b/drivers/media/pci/intel/ipu7/ipu7-dma.h -new file mode 100644 -index 000000000000..fe789af5e664 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-dma.h -@@ -0,0 +1,46 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013--2025 Intel Corporation */ -+ -+#ifndef IPU7_DMA_H -+#define IPU7_DMA_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7-bus.h" -+ -+#define DMA_ATTR_RESERVE_REGION BIT(31) -+struct ipu7_mmu_info; -+ -+struct ipu7_dma_mapping { -+ struct ipu7_mmu_info *mmu_info; -+ struct iova_domain iovad; -+}; -+ -+void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -+ size_t size); -+void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ unsigned int nents); -+void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt); -+void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -+ dma_addr_t *dma_handle, gfp_t gfp, -+ unsigned long attrs); -+void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -+ dma_addr_t dma_handle, unsigned long attrs); -+int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -+ void *addr, dma_addr_t iova, size_t size, -+ unsigned long attrs); -+int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs); -+void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs); -+int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs); -+void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs); -+#endif /* IPU7_DMA_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -new file mode 100644 -index 000000000000..2958e39a359e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -@@ -0,0 +1,389 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_insys_config_abi.h" -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-isys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+ -+static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = { -+ "STREAM_OPEN", -+ "STREAM_START_AND_CAPTURE", -+ "STREAM_CAPTURE", -+ "STREAM_ABORT", -+ "STREAM_FLUSH", -+ "STREAM_CLOSE" -+}; -+ -+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, -+ void *cpu_mapped_buf, -+ dma_addr_t dma_mapped_buf, -+ size_t size, u16 send_type) -+{ -+ struct ipu7_syscom_context *ctx = isys->adev->syscom; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu7_insys_send_queue_token *token; -+ -+ if (send_type >= N_IPU_INSYS_SEND_TYPE) -+ return -EINVAL; -+ -+ dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); -+ -+ /* -+ * Time to flush cache in case we have some payload. Not all messages -+ * have that -+ */ -+ if (cpu_mapped_buf) -+ clflush_cache_range(cpu_mapped_buf, size); -+ -+ token = ipu7_syscom_get_token(ctx, stream_handle + -+ IPU_INSYS_INPUT_MSG_QUEUE); -+ if (!token) -+ return -EBUSY; -+ -+ token->addr = dma_mapped_buf; -+ token->buf_handle = (unsigned long)cpu_mapped_buf; -+ token->send_type = send_type; -+ token->stream_id = stream_handle; -+ token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE; -+ -+ ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE); -+ /* now wakeup FW */ -+ ipu_buttress_wakeup_is_uc(isys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, u16 send_type) -+{ -+ return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, -+ send_type); -+} -+ -+int ipu7_fw_isys_init(struct ipu7_isys *isys) -+{ -+ struct syscom_queue_config *queue_configs; -+ struct ipu7_bus_device *adev = isys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_insys_config *isys_config; -+ struct ipu7_syscom_context *syscom; -+ dma_addr_t isys_config_dma_addr; -+ unsigned int i, num_queues; -+ u32 freq; -+ u8 major; -+ int ret; -+ -+ /* Allocate and init syscom context. */ -+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -+ GFP_KERNEL); -+ if (!syscom) -+ return -ENOMEM; -+ -+ adev->syscom = syscom; -+ syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; -+ syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; -+ num_queues = syscom->num_input_queues + syscom->num_output_queues; -+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -+ GFP_KERNEL); -+ if (!queue_configs) { -+ ipu7_fw_isys_release(isys); -+ return -ENOMEM; -+ } -+ syscom->queue_configs = queue_configs; -+ queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity = -+ IPU_ISYS_SIZE_RECV_QUEUE; -+ queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = -+ sizeof(struct ipu7_insys_resp); -+ queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity = -+ IPU_ISYS_SIZE_LOG_QUEUE; -+ queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = -+ sizeof(struct ipu7_insys_resp); -+ queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; -+ queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; -+ -+ queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity = -+ IPU_ISYS_MAX_STREAMS; -+ queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes = -+ sizeof(struct ipu7_insys_send_queue_token); -+ -+ for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) { -+ queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE; -+ queue_configs[i].token_size_in_bytes = -+ sizeof(struct ipu7_insys_send_queue_token); -+ } -+ -+ /* Allocate ISYS subsys config. */ -+ isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config), -+ &isys_config_dma_addr, GFP_KERNEL, 0); -+ if (!isys_config) { -+ dev_err(dev, "Failed to allocate isys subsys config.\n"); -+ ipu7_fw_isys_release(isys); -+ return -ENOMEM; -+ } -+ isys->subsys_config = isys_config; -+ isys->subsys_config_dma_addr = isys_config_dma_addr; -+ memset(isys_config, 0, sizeof(struct ipu7_insys_config)); -+ isys_config->logger_config.use_source_severity = 0; -+ isys_config->logger_config.use_channels_enable_bitmask = 1; -+ isys_config->logger_config.channels_enable_bitmask = -+ LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK; -+ isys_config->logger_config.hw_printf_buffer_base_addr = 0U; -+ isys_config->logger_config.hw_printf_buffer_size_bytes = 0U; -+ isys_config->wdt_config.wdt_timer1_us = 0; -+ isys_config->wdt_config.wdt_timer2_us = 0; -+ ret = ipu_buttress_get_isys_freq(adev->isp, &freq); -+ if (ret) { -+ dev_err(dev, "Failed to get ISYS frequency.\n"); -+ ipu7_fw_isys_release(isys); -+ return ret; -+ } -+ -+ ipu7_dma_sync_single(adev, isys_config_dma_addr, -+ sizeof(struct ipu7_insys_config)); -+ -+ major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U; -+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -+ freq, isys_config_dma_addr, major); -+ if (ret) -+ ipu7_fw_isys_release(isys); -+ -+ return ret; -+} -+ -+void ipu7_fw_isys_release(struct ipu7_isys *isys) -+{ -+ struct ipu7_bus_device *adev = isys->adev; -+ -+ ipu7_boot_release_boot_config(adev); -+ if (isys->subsys_config) { -+ ipu7_dma_free(adev, -+ sizeof(struct ipu7_insys_config), -+ isys->subsys_config, -+ isys->subsys_config_dma_addr, 0); -+ isys->subsys_config = NULL; -+ isys->subsys_config_dma_addr = 0; -+ } -+} -+ -+int ipu7_fw_isys_open(struct ipu7_isys *isys) -+{ -+ return ipu7_boot_start_fw(isys->adev); -+} -+ -+int ipu7_fw_isys_close(struct ipu7_isys *isys) -+{ -+ return ipu7_boot_stop_fw(isys->adev); -+} -+ -+struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys) -+{ -+ return (struct ipu7_insys_resp *) -+ ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_MSG_QUEUE); -+} -+ -+void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) -+{ -+ ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); -+} -+ -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys) -+{ -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ void *token; -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ }; -+ -+ return 0; -+} -+ -+#endif -+void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -+ struct ipu7_insys_stream_cfg *cfg) -+{ -+ unsigned int i; -+ -+ dev_dbg(dev, "---------------------------\n"); -+ dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); -+ -+ dev_dbg(dev, ".port id %d\n", cfg->port_id); -+ dev_dbg(dev, ".vc %d\n", cfg->vc); -+ dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins); -+ dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins); -+ dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map); -+ -+ for (i = 0; i < cfg->nof_input_pins; i++) { -+ dev_dbg(dev, ".input_pin[%d]:\n", i); -+ dev_dbg(dev, "\t.dt = 0x%0x\n", -+ cfg->input_pins[i].dt); -+ dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n", -+ cfg->input_pins[i].disable_mipi_unpacking); -+ dev_dbg(dev, "\t.dt_rename_mode = %d\n", -+ cfg->input_pins[i].dt_rename_mode); -+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", -+ cfg->input_pins[i].mapped_dt); -+ dev_dbg(dev, "\t.input_res = %d x %d\n", -+ cfg->input_pins[i].input_res.width, -+ cfg->input_pins[i].input_res.height); -+ dev_dbg(dev, "\t.sync_msg_map = 0x%x\n", -+ cfg->input_pins[i].sync_msg_map); -+ } -+ -+ for (i = 0; i < cfg->nof_output_pins; i++) { -+ dev_dbg(dev, ".output_pin[%d]:\n", i); -+ dev_dbg(dev, "\t.input_pin_id = %d\n", -+ cfg->output_pins[i].input_pin_id); -+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); -+ dev_dbg(dev, "\t.send_irq = %d\n", -+ cfg->output_pins[i].send_irq); -+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); -+ -+ dev_dbg(dev, "\t.link.buffer_lines = %d\n", -+ cfg->output_pins[i].link.buffer_lines); -+ dev_dbg(dev, "\t.link.foreign_key = %d\n", -+ cfg->output_pins[i].link.foreign_key); -+ dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n", -+ cfg->output_pins[i].link.granularity_pointer_update); -+ dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n", -+ cfg->output_pins[i].link.msg_link_streaming_mode); -+ dev_dbg(dev, "\t.link.pbk_id = %d\n", -+ cfg->output_pins[i].link.pbk_id); -+ dev_dbg(dev, "\t.link.pbk_slot_id = %d\n", -+ cfg->output_pins[i].link.pbk_slot_id); -+ dev_dbg(dev, "\t.link.dest = %d\n", -+ cfg->output_pins[i].link.dest); -+ dev_dbg(dev, "\t.link.use_sw_managed = %d\n", -+ cfg->output_pins[i].link.use_sw_managed); -+ dev_dbg(dev, "\t.link.is_snoop = %d\n", -+ cfg->output_pins[i].link.is_snoop); -+ -+ dev_dbg(dev, "\t.crop.line_top = %d\n", -+ cfg->output_pins[i].crop.line_top); -+ dev_dbg(dev, "\t.crop.line_bottom = %d\n", -+ cfg->output_pins[i].crop.line_bottom); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.crop.column_left = %d\n", -+ cfg->output_pins[i].crop.column_left); -+ dev_dbg(dev, "\t.crop.colunm_right = %d\n", -+ cfg->output_pins[i].crop.column_right); -+#endif -+ -+ dev_dbg(dev, "\t.dpcm_enable = %d\n", -+ cfg->output_pins[i].dpcm.enable); -+ dev_dbg(dev, "\t.dpcm.type = %d\n", -+ cfg->output_pins[i].dpcm.type); -+ dev_dbg(dev, "\t.dpcm.predictor = %d\n", -+ cfg->output_pins[i].dpcm.predictor); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.upipe_enable = %d\n", -+ cfg->output_pins[i].upipe_enable); -+ dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); -+ dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); -+ dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); -+#endif -+ } -+ dev_dbg(dev, "---------------------------\n"); -+} -+ -+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -+ struct ipu7_insys_buffset *buf, -+ unsigned int outputs) -+{ -+ unsigned int i; -+ -+ dev_dbg(dev, "--------------------------\n"); -+ dev_dbg(dev, "IPU_ISYS_BUFF_SET\n"); -+ dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map); -+ dev_dbg(dev, ".frame_id = %d\n", buf->frame_id); -+ dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame); -+ -+ for (i = 0; i < outputs; i++) { -+ dev_dbg(dev, ".output_pin[%d]:\n", i); -+#ifndef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.user_token = %llx\n", -+ buf->output_pins[i].user_token); -+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); -+#else -+ dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", -+ buf->output_pins[i].pin_payload.user_token); -+ dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", -+ buf->output_pins[i].pin_payload.addr); -+ dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", -+ buf->output_pins[i].upipe_capture_cfg); -+#endif -+ } -+ dev_dbg(dev, "---------------------------\n"); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -new file mode 100644 -index 000000000000..1235adc9694e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_ISYS_H -+#define IPU7_FW_ISYS_H -+ -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+struct device; -+struct ipu7_insys_buffset; -+struct ipu7_insys_stream_cfg; -+struct ipu7_isys; -+ -+/* From here on type defines not coming from the ISYSAPI interface */ -+ -+int ipu7_fw_isys_init(struct ipu7_isys *isys); -+void ipu7_fw_isys_release(struct ipu7_isys *isys); -+int ipu7_fw_isys_open(struct ipu7_isys *isys); -+int ipu7_fw_isys_close(struct ipu7_isys *isys); -+ -+void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -+ struct ipu7_insys_stream_cfg *cfg); -+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -+ struct ipu7_insys_buffset *buf, -+ unsigned int outputs); -+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, u16 send_type); -+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, -+ void *cpu_mapped_buf, -+ dma_addr_t dma_mapped_buf, -+ size_t size, u16 send_type); -+struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); -+void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys); -+#endif -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -new file mode 100644 -index 000000000000..b8c5db7ae300 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -@@ -0,0 +1,1034 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-isys-csi-phy.h" -+ -+#define PORT_A 0U -+#define PORT_B 1U -+#define PORT_C 2U -+#define PORT_D 3U -+ -+#define N_DATA_IDS 8U -+static DECLARE_BITMAP(data_ids, N_DATA_IDS); -+ -+struct ddlcal_counter_ref_s { -+ u16 min_mbps; -+ u16 max_mbps; -+ -+ u16 ddlcal_counter_ref; -+}; -+ -+struct ddlcal_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 oa_lanex_hsrx_cdphy_sel_fast; -+ u16 ddlcal_max_phase; -+ u16 phase_bound; -+ u16 ddlcal_dll_fbk; -+ u16 ddlcal_ddl_coarse_bank; -+ u16 fjump_deskew; -+ u16 min_eye_opening_deskew; -+}; -+ -+struct i_thssettle_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 i_thssettle; -+}; -+ -+ /* lane2 for 4l3t, lane1 for 2l2t */ -+struct oa_lane_clk_div_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 oa_lane_hsrx_hs_clk_div; -+}; -+ -+struct cdr_fbk_cap_prog_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 val; -+}; -+ -+static const struct ddlcal_counter_ref_s table0[] = { -+ { 1500, 1999, 118 }, -+ { 2000, 2499, 157 }, -+ { 2500, 3499, 196 }, -+ { 3500, 4499, 274 }, -+ { 4500, 4500, 352 }, -+ { } -+}; -+ -+static const struct ddlcal_params table1[] = { -+ { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 }, -+ { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 }, -+ { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 }, -+ { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 }, -+ { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 }, -+ { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 }, -+ { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 }, -+ { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 }, -+ { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 }, -+ { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 }, -+ { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 }, -+ { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 }, -+ { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 }, -+ { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 }, -+ { } -+}; -+ -+static const struct i_thssettle_params table2[] = { -+ { 80, 124, 24 }, -+ { 125, 249, 20 }, -+ { 250, 499, 16 }, -+ { 500, 749, 14 }, -+ { 750, 1499, 13 }, -+ { 1500, 4500, 12 }, -+ { } -+}; -+ -+static const struct oa_lane_clk_div_params table6[] = { -+ { 80, 159, 0x1 }, -+ { 160, 319, 0x2 }, -+ { 320, 639, 0x3 }, -+ { 640, 1279, 0x4 }, -+ { 1280, 2560, 0x5 }, -+ { 2561, 4500, 0x6 }, -+ { } -+}; -+ -+static const struct cdr_fbk_cap_prog_params table7[] = { -+ { 80, 919, 0 }, -+ { 920, 1029, 1 }, -+ { 1030, 1169, 2 }, -+ { 1170, 1349, 3 }, -+ { 1350, 1589, 4 }, -+ { 1590, 1949, 5 }, -+ { 1950, 2499, 6 }, -+ { } -+}; -+ -+static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -+ -+ dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x", -+ base + addr - isys_base, data); -+ writew(data, base + addr); -+} -+ -+static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -+ u16 data; -+ -+ data = readw(base + addr); -+ dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x", -+ base + addr - isys_base, data); -+ -+ return data; -+} -+ -+static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -+ struct device *dev = &isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, data); -+ writel(data, base + addr); -+ dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, readl(base + addr)); -+} -+ -+static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ u32 gpreg = isys->pdata->ipdata->csi2.gpreg; -+ void __iomem *base = isys_base + gpreg + 0x1000 * id; -+ struct device *dev = &isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, data); -+ writel(data, base + addr); -+ dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, readl(base + addr)); -+} -+ -+static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -+ u32 data; -+ -+ data = readl(base + addr); -+ dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x", -+ base + addr - isys_base, data); -+ -+ return data; -+} -+ -+static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -+ u16 val, u8 lo, u8 hi) -+{ -+ u32 temp, mask; -+ -+ WARN_ON(lo > hi); -+ WARN_ON(hi > 15); -+ -+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -+ temp = dwc_phy_read(isys, id, addr); -+ temp &= ~mask; -+ temp |= (val << lo) & mask; -+ dwc_phy_write(isys, id, addr, temp); -+} -+ -+static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -+ u32 val, u8 hi, u8 lo) -+{ -+ u32 temp, mask; -+ -+ WARN_ON(lo > hi); -+ -+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -+ temp = dwc_csi_read(isys, id, addr); -+ temp &= ~mask; -+ temp |= (val << lo) & mask; -+ dwc_csi_write(isys, id, addr, temp); -+} -+ -+static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 id, lanes, phy_mode; -+ u32 val; -+ -+ id = csi2->port; -+ lanes = csi2->nlanes; -+ phy_mode = csi2->phy_mode; -+ dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u", -+ id, lanes, phy_mode); -+ -+ val = dwc_csi_read(isys, id, VERSION); -+ dev_dbg(dev, "csi-%d controller version = 0x%x", id, val); -+ -+ /* num of active data lanes */ -+ dwc_csi_write(isys, id, N_LANES, lanes - 1); -+ dwc_csi_write(isys, id, CDPHY_MODE, phy_mode); -+ dwc_csi_write(isys, id, VC_EXTENSION, 0); -+ -+ /* only mask PHY_FATAL and PKT_FATAL interrupts */ -+ dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff); -+ dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3); -+ dwc_csi_write(isys, id, INT_MSK_PHY, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_LINE, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0); -+} -+ -+static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id) -+{ -+ dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0); -+ dwc_csi_write(isys, id, DPHY_RSTZ, 0); -+ dwc_csi_write(isys, id, CSI2_RESETN, 0); -+ gpreg_write(isys, id, PHY_RESET, 0); -+ gpreg_write(isys, id, PHY_SHUTDOWN, 0); -+} -+ -+/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */ -+static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ u32 reg, n; -+ u8 lo, hi; -+ int ret; -+ -+ dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", -+ id, vc, dt); -+ -+ dwc_csi_write(isys, id, VC_EXTENSION, 0x0); -+ n = find_first_zero_bit(data_ids, N_DATA_IDS); -+ if (n == N_DATA_IDS) -+ return -ENOSPC; -+ -+ ret = test_and_set_bit(n, data_ids); -+ if (ret) -+ return -EBUSY; -+ -+ reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2; -+ lo = (n % 4) * 8; -+ hi = lo + 4; -+ dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo); -+ -+ reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2; -+ lo = (n % 4) * 8; -+ hi = lo + 5; -+ dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo); -+ -+ return 0; -+} -+ -+static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) -+{ -+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct v4l2_mbus_frame_desc desc; -+ struct v4l2_subdev *ext_sd; -+ struct media_pad *pad; -+ unsigned int i; -+ int ret; -+ -+ pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -+ if (IS_ERR(pad)) { -+ dev_warn(dev, "can't get remote source pad of %s (%ld)\n", -+ csi2->asd.sd.name, PTR_ERR(pad)); -+ return PTR_ERR(pad); -+ } -+ -+ ext_sd = media_entity_to_v4l2_subdev(pad->entity); -+ if (WARN(!ext_sd, "Failed to get subdev for entity %s\n", -+ pad->entity->name)) -+ return -ENODEV; -+ -+ ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc); -+ if (ret) -+ return ret; -+ -+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -+ dev_warn(dev, "Unsupported frame descriptor type\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ desc_entry = &desc.entry[i]; -+ if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { -+ ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, -+ desc_entry->bus.csi2.dt); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+#define CDPHY_TIMEOUT 5000000U -+static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; -+ void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ u32 phy_ready; -+ u32 reg, rext; -+ int ret; -+ -+ dev_dbg(dev, "waiting phy ready...\n"); -+ ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready, -+ phy_ready & BIT(0) && phy_ready != ~0U, -+ 100, CDPHY_TIMEOUT); -+ dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY)); -+ dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id, -+ dwc_csi_read(isys, id, PHY_RX)); -+ dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id, -+ dwc_csi_read(isys, id, PHY_STOPSTATE)); -+ dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id, -+ dwc_csi_read(isys, id, PHY_CAL)); -+ for (i = 0; i < 4U; i++) { -+ reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U); -+ dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n", -+ id, i, dwc_phy_read(isys, id, reg)); -+ } -+ dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5)); -+ dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0)); -+ dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB)); -+ dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB)); -+ dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT)); -+ -+ if (!ret) { -+ if (id) { -+ dev_dbg(dev, "ignore phy %u rext\n", id); -+ return 0; -+ } -+ -+ rext = dwc_phy_read(isys, id, -+ CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU; -+ dev_dbg(dev, "phy %u rext value = %u\n", id, rext); -+ isys->phy_rext_cal = (rext ? rext : 5); -+ -+ return 0; -+ } -+ -+ dev_err(dev, "wait phy ready timeout!\n"); -+ -+ return ret; -+} -+ -+static int lookup_table1(u64 mbps) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(table1); i++) { -+ if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps) -+ return i; -+ } -+ -+ return -ENXIO; -+} -+ -+static const u16 deskew_fine_mem[] = { -+ 0x0404, 0x040c, 0x0414, 0x041c, -+ 0x0423, 0x0429, 0x0430, 0x043a, -+ 0x0445, 0x044a, 0x0450, 0x045a, -+ 0x0465, 0x0469, 0x0472, 0x047a, -+ 0x0485, 0x0489, 0x0490, 0x049a, -+ 0x04a4, 0x04ac, 0x04b4, 0x04bc, -+ 0x04c4, 0x04cc, 0x04d4, 0x04dc, -+ 0x04e4, 0x04ec, 0x04f4, 0x04fc, -+ 0x0504, 0x050c, 0x0514, 0x051c, -+ 0x0523, 0x0529, 0x0530, 0x053a, -+ 0x0545, 0x054a, 0x0550, 0x055a, -+ 0x0565, 0x0569, 0x0572, 0x057a, -+ 0x0585, 0x0589, 0x0590, 0x059a, -+ 0x05a4, 0x05ac, 0x05b4, 0x05bc, -+ 0x05c4, 0x05cc, 0x05d4, 0x05dc, -+ 0x05e4, 0x05ec, 0x05f4, 0x05fc, -+ 0x0604, 0x060c, 0x0614, 0x061c, -+ 0x0623, 0x0629, 0x0632, 0x063a, -+ 0x0645, 0x064a, 0x0650, 0x065a, -+ 0x0665, 0x0669, 0x0672, 0x067a, -+ 0x0685, 0x0689, 0x0690, 0x069a, -+ 0x06a4, 0x06ac, 0x06b4, 0x06bc, -+ 0x06c4, 0x06cc, 0x06d4, 0x06dc, -+ 0x06e4, 0x06ec, 0x06f4, 0x06fc, -+ 0x0704, 0x070c, 0x0714, 0x071c, -+ 0x0723, 0x072a, 0x0730, 0x073a, -+ 0x0745, 0x074a, 0x0750, 0x075a, -+ 0x0765, 0x0769, 0x0772, 0x077a, -+ 0x0785, 0x0789, 0x0790, 0x079a, -+ 0x07a4, 0x07ac, 0x07b4, 0x07bc, -+ 0x07c4, 0x07cc, 0x07d4, 0x07dc, -+ 0x07e4, 0x07ec, 0x07f4, 0x07fc, -+}; -+ -+static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -+ bool aggregation, u64 mbps) -+{ -+ u16 hsrxval0 = 0; -+ u16 hsrxval1 = 0; -+ u16 hsrxval2 = 0; -+ int index; -+ u16 reg; -+ u16 val; -+ u32 i; -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9); -+ if (mbps > 1500) -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -+ 40, 0, 7); -+ else -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -+ 104, 0, 7); -+ -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6); -+ -+ for (i = 0; i < ARRAY_SIZE(table0); i++) { -+ if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) { -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3, -+ table0[i].ddlcal_counter_ref, -+ 0, 9); -+ break; -+ } -+ } -+ -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, -+ table1[index].phase_bound, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -+ table1[index].ddlcal_dll_fbk, 4, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -+ table1[index].ddlcal_ddl_coarse_bank, 0, 3); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8; -+ val = table1[index].oa_lanex_hsrx_cdphy_sel_fast; -+ for (i = 0; i < lanes + 1; i++) -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -+ 12, 12); -+ } -+ -+ reg = CORE_DIG_DLANE_0_RW_LP_0; -+ for (i = 0; i < lanes; i++) -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, -+ 0, 0, 0); -+ if (!is_ipu7(isys->adev->isp->hw_ver) || -+ id == PORT_B || id == PORT_C) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -+ 1, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -+ 0, 0, 0); -+ } else { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -+ 0, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -+ 1, 0, 0); -+ } -+ -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -+ 0, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -+ 0, 0, 0); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12; -+ val = (mbps > 1500) ? 0 : 1; -+ for (i = 0; i < lanes + 1; i++) { -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3); -+ } -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13; -+ val = (mbps > 1500) ? 0 : 1; -+ for (i = 0; i < lanes + 1; i++) { -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3); -+ } -+ -+ if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C) -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9; -+ else -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9; -+ -+ for (i = 0; i < ARRAY_SIZE(table6); i++) { -+ if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) { -+ dwc_phy_write_mask(isys, id, reg, -+ table6[i].oa_lane_hsrx_hs_clk_div, -+ 5, 7); -+ break; -+ } -+ } -+ -+ if (aggregation) { -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, -+ 1, 1); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15; -+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -+ -+ val = (id == PORT_A) ? 3 : 0; -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15; -+ dwc_phy_write_mask(isys, id, reg, val, 3, 4); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15; -+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7); -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_0; -+ for (i = 0; i < ARRAY_SIZE(table2); i++) { -+ if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) { -+ u8 j; -+ -+ for (j = 0; j < lanes; j++) -+ dwc_phy_write_mask(isys, id, reg + (j * 0x400), -+ table2[i].i_thssettle, -+ 8, 15); -+ break; -+ } -+ } -+ -+ /* deskew */ -+ for (i = 0; i < lanes; i++) { -+ reg = CORE_DIG_DLANE_0_RW_CFG_1; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), -+ ((mbps > 1500) ? 0x1 : 0x2), 2, 3); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_2; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), -+ ((mbps > 2500) ? 0 : 1), 15, 15); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12); -+ -+ reg = CORE_DIG_DLANE_0_RW_LP_0; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_LP_2; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_1; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_3; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2); -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ val = table1[index].fjump_deskew; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -+ 3, 8); -+ } -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_4; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_5; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_6; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7); -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ val = table1[index].min_eye_opening_deskew; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -+ 8, 15); -+ } -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_7; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_9; -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ val = table1[index].ddlcal_max_phase; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), -+ val, 0, 7); -+ } -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15); -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0); -+ -+ for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++) -+ dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM, -+ deskew_fine_mem[i], 0, 15); -+ -+ if (mbps > 1500) { -+ hsrxval0 = 4; -+ hsrxval2 = 3; -+ } -+ -+ if (mbps > 2500) -+ hsrxval1 = 2; -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -+ hsrxval0, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -+ hsrxval0, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -+ hsrxval0, 0, 2); -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -+ hsrxval0, 0, 2); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -+ hsrxval0, 0, 2); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -+ hsrxval1, 3, 4); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -+ hsrxval1, 3, 4); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -+ hsrxval1, 3, 4); -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -+ hsrxval1, 3, 4); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -+ hsrxval1, 3, 4); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15, -+ hsrxval2, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15, -+ hsrxval2, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15, -+ hsrxval2, 0, 2); -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15, -+ hsrxval2, 0, 2); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15, -+ hsrxval2, 0, 2); -+ } -+ -+ /* force and override rext */ -+ if (isys->phy_rext_cal && id) { -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, -+ isys->phy_rext_cal, 0, 3); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -+ 1, 11, 11); -+ } -+} -+ -+static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -+ bool aggregation, u64 mbps) -+{ -+ u8 trios = 2; -+ u16 coarse_target; -+ u16 deass_thresh; -+ u16 delay_thresh; -+ u16 reset_thresh; -+ u16 cap_prog = 6U; -+ u16 reg; -+ u16 val; -+ u32 i; -+ u64 r64; -+ u32 r; -+ -+ if (is_ipu7p5(isys->adev->isp->hw_ver)) -+ val = 0x15; -+ else -+ val = 0x155; -+ -+ if (is_ipu7(isys->adev->isp->hw_ver)) -+ trios = 3; -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); -+ -+ reg = CORE_DIG_CLANE_0_RW_LP_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -+ -+ val = (mbps > 900U) ? 1U : 0U; -+ for (i = 0; i < trios; i++) { -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_1; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_5; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_6; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15); -+ } -+ -+ /* -+ * Below 900Msps, always use the same value. -+ * The formula is suitable for data rate 80-3500Msps. -+ * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5 -+ */ -+ if (mbps >= 80U) -+ coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1; -+ else -+ coarse_target = 56; -+ -+ for (i = 0; i < trios; i++) { -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; -+ dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); -+ } -+ -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); -+ -+ if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -+ 1, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -+ 0, 0, 0); -+ } -+ -+ for (i = 0; i < trios; i++) { -+ reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; -+ dwc_phy_write_mask(isys, id, reg, 1, 6, 8); -+ dwc_phy_write_mask(isys, id, reg, 1, 3, 5); -+ dwc_phy_write_mask(isys, id, reg, 2, 0, 2); -+ } -+ -+ deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1; -+ if (r64 != 0) -+ deass_thresh++; -+ -+ reg = CORE_DIG_RW_TRIO0_2; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, -+ deass_thresh, 0, 7); -+ -+ delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u; -+ -+ if (delay_thresh < 1) -+ delay_thresh = 1; -+ -+ reg = CORE_DIG_RW_TRIO0_1; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, -+ delay_thresh, 0, 15); -+ -+ reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r); -+ if (!r) -+ reset_thresh--; -+ -+ if (reset_thresh < 1) -+ reset_thresh = 1; -+ -+ reg = CORE_DIG_RW_TRIO0_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, -+ reset_thresh, 9, 11); -+ -+ reg = CORE_DIG_CLANE_0_RW_LP_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); -+ -+ reg = CORE_DIG_CLANE_0_RW_LP_2; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); -+ -+ for (i = 0; i < ARRAY_SIZE(table7); i++) { -+ if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) { -+ cap_prog = table7[i].val; -+ break; -+ } -+ } -+ -+ for (i = 0; i < (lanes + 1); i++) { -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; -+ dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); -+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; -+ dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); -+ } -+} -+ -+static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -+ bool aggregation) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 phy_mode; -+ s64 link_freq; -+ u64 mbps; -+ -+ if (aggregation) -+ link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]); -+ else -+ link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]); -+ -+ if (link_freq < 0) { -+ dev_err(dev, "get link freq failed (%lld)\n", link_freq); -+ return link_freq; -+ } -+ -+ mbps = div_u64(link_freq, 500000); -+ dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n", -+ id, lanes, aggregation, mbps); -+ -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7); -+ dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2, -+ 1, 12, 13); -+ dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0, -+ 63, 2, 7); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1, -+ 563, 0, 11); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7); -+ /* bypass the RCAL state (bit6) */ -+ if (aggregation && id != PORT_A) -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45, -+ 0, 7); -+ -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8); -+ dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6); -+ dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1); -+ dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -+ 0, 10, 10); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -+ 1, 10, 10); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -+ 0, 15, 15); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3, -+ 3, 8, 9); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -+ 0, 15, 15); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6, -+ 7, 12, 14); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -+ 0, 8, 10); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, -+ 0, 8, 8); -+ -+ if (aggregation) -+ phy_mode = isys->csi2[0].phy_mode; -+ else -+ phy_mode = isys->csi2[id].phy_mode; -+ -+ if (phy_mode == PHY_MODE_DPHY) { -+ ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps); -+ } else if (phy_mode == PHY_MODE_CPHY) { -+ ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps); -+ } else { -+ dev_err(dev, "unsupported phy mode %d!\n", -+ isys->csi2[id].phy_mode); -+ } -+ -+ return 0; -+} -+ -+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ u32 lanes = csi2->nlanes; -+ bool aggregation = false; -+ u32 id = csi2->port; -+ int ret; -+ -+ /* lanes remapping for aggregation (port AB) mode */ -+ if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { -+ aggregation = true; -+ lanes = 2; -+ } -+ -+ ipu7_isys_csi_phy_reset(isys, id); -+ gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); -+ gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -+ gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); -+ gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); -+ gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); -+ -+ /* config PORT_B if aggregation mode */ -+ if (aggregation) { -+ ipu7_isys_csi_phy_reset(isys, PORT_B); -+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0); -+ gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3); -+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -+ gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf); -+ gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode); -+ } -+ -+ ipu7_isys_csi_ctrl_cfg(csi2); -+ ipu7_isys_csi_ctrl_dids_config(csi2, id); -+ -+ ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, id, PHY_RESET, 1); -+ gpreg_write(isys, id, PHY_SHUTDOWN, 1); -+ dwc_csi_write(isys, id, DPHY_RSTZ, 1); -+ dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1); -+ dwc_csi_write(isys, id, CSI2_RESETN, 1); -+ -+ ret = ipu7_isys_phy_ready(isys, id); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0); -+ gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0); -+ -+ /* config PORT_B if aggregation mode */ -+ if (aggregation) { -+ ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, PORT_B, PHY_RESET, 1); -+ gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1); -+ dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1); -+ dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1); -+ dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1); -+ ret = ipu7_isys_phy_ready(isys, PORT_B); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0); -+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0); -+ } -+ -+ return 0; -+} -+ -+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ -+ ipu7_isys_csi_phy_reset(isys, csi2->port); -+ if (!is_ipu7(isys->adev->isp->hw_ver) && -+ csi2->nlanes > 2U && csi2->port == PORT_A) -+ ipu7_isys_csi_phy_reset(isys, PORT_B); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -new file mode 100644 -index 000000000000..dfdcb61540c4 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_CSI_PHY_H -+#define IPU7_ISYS_CSI_PHY_H -+ -+struct ipu7_isys; -+ -+#define PHY_MODE_DPHY 0U -+#define PHY_MODE_CPHY 1U -+ -+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2); -+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -new file mode 100644 -index 000000000000..aad52c44a005 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -@@ -0,0 +1,1197 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_CSI2_REG_H -+#define IPU7_ISYS_CSI2_REG_H -+ -+/* IS main regs base */ -+#define IS_MAIN_BASE 0x240000 -+#define IS_MAIN_S2B_BASE (IS_MAIN_BASE + 0x22000) -+#define IS_MAIN_B2O_BASE (IS_MAIN_BASE + 0x26000) -+#define IS_MAIN_ISD_M0_BASE (IS_MAIN_BASE + 0x2b000) -+#define IS_MAIN_ISD_M1_BASE (IS_MAIN_BASE + 0x2b100) -+#define IS_MAIN_ISD_INT_BASE (IS_MAIN_BASE + 0x2b200) -+#define IS_MAIN_GDA_BASE (IS_MAIN_BASE + 0x32000) -+#define IS_MAIN_GPREGS_MAIN_BASE (IS_MAIN_BASE + 0x32500) -+#define IS_MAIN_IRQ_CTRL_BASE (IS_MAIN_BASE + 0x32700) -+#define IS_MAIN_PWM_CTRL_BASE (IS_MAIN_BASE + 0x32b00) -+ -+#define S2B_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_S2B_BASE + 0x1c) -+#define S2B_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_S2B_BASE + 0x20) -+#define S2B_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_S2B_BASE + 0x24) -+#define S2B_IID_IRQ_CTL_STATUS(iid) (IS_MAIN_S2B_BASE + 0x94 + \ -+ 0x100 * (iid)) -+ -+#define B2O_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_B2O_BASE + 0x30) -+#define B2O_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_B2O_BASE + 0x34) -+#define B2O_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_B2O_BASE + 0x38) -+#define B2O_IID_IRQ_CTL_STATUS(oid) (IS_MAIN_B2O_BASE + 0x3dc + \ -+ 0x200 * (oid)) -+ -+#define ISD_M0_IRQ_CTL_STATUS (IS_MAIN_ISD_M0_BASE + 0x1c) -+#define ISD_M0_IRQ_CTL_CLEAR (IS_MAIN_ISD_M0_BASE + 0x20) -+#define ISD_M0_IRQ_CTL_ENABLE (IS_MAIN_ISD_M0_BASE + 0x24) -+ -+#define ISD_M1_IRQ_CTL_STATUS (IS_MAIN_ISD_M1_BASE + 0x1c) -+#define ISD_M1_IRQ_CTL_CLEAR (IS_MAIN_ISD_M1_BASE + 0x20) -+#define ISD_M1_IRQ_CTL_ENABLE (IS_MAIN_ISD_M1_BASE + 0x24) -+ -+#define ISD_INT_IRQ_CTL_STATUS (IS_MAIN_ISD_INT_BASE + 0x1c) -+#define ISD_INT_IRQ_CTL_CLEAR (IS_MAIN_ISD_INT_BASE + 0x20) -+#define ISD_INT_IRQ_CTL_ENABLE (IS_MAIN_ISD_INT_BASE + 0x24) -+ -+#define GDA_IRQ_CTL_STATUS (IS_MAIN_GDA_BASE + 0x1c) -+#define GDA_IRQ_CTL_CLEAR (IS_MAIN_GDA_BASE + 0x20) -+#define GDA_IRQ_CTL_ENABLE (IS_MAIN_GDA_BASE + 0x24) -+ -+#define IS_MAIN_IRQ_CTL_EDGE IS_MAIN_IRQ_CTRL_BASE -+#define IS_MAIN_IRQ_CTL_MASK (IS_MAIN_IRQ_CTRL_BASE + 0x4) -+#define IS_MAIN_IRQ_CTL_STATUS (IS_MAIN_IRQ_CTRL_BASE + 0x8) -+#define IS_MAIN_IRQ_CTL_CLEAR (IS_MAIN_IRQ_CTRL_BASE + 0xc) -+#define IS_MAIN_IRQ_CTL_ENABLE (IS_MAIN_IRQ_CTRL_BASE + 0x10) -+#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IS_MAIN_IRQ_CTRL_BASE + 0x14) -+ -+/* IS IO regs base */ -+#define IS_PHY_NUM 4U -+#define IS_IO_BASE 0x280000 -+ -+/* dwc csi cdphy registers */ -+#define IS_IO_CDPHY_BASE(i) (IS_IO_BASE + 0x10000 * (i)) -+#define PPI_STARTUP_RW_COMMON_DPHY_0 0x1800 -+#define PPI_STARTUP_RW_COMMON_DPHY_1 0x1802 -+#define PPI_STARTUP_RW_COMMON_DPHY_2 0x1804 -+#define PPI_STARTUP_RW_COMMON_DPHY_3 0x1806 -+#define PPI_STARTUP_RW_COMMON_DPHY_4 0x1808 -+#define PPI_STARTUP_RW_COMMON_DPHY_5 0x180a -+#define PPI_STARTUP_RW_COMMON_DPHY_6 0x180c -+#define PPI_STARTUP_RW_COMMON_DPHY_7 0x180e -+#define PPI_STARTUP_RW_COMMON_DPHY_8 0x1810 -+#define PPI_STARTUP_RW_COMMON_DPHY_9 0x1812 -+#define PPI_STARTUP_RW_COMMON_DPHY_A 0x1814 -+#define PPI_STARTUP_RW_COMMON_DPHY_10 0x1820 -+#define PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822 -+#define PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824 -+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a -+#define PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c -+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e -+#define PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850 -+#define PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852 -+#define PPI_CALIBCTRL_R_ADC_DEBUG 0x1854 -+#define PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00 -+#define PPI_RW_LPDCOCAL_TIMEBASE 0x1c02 -+#define PPI_RW_LPDCOCAL_NREF 0x1c04 -+#define PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06 -+#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08 -+#define PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a -+#define PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c -+#define PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e -+#define PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18 -+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a -+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c -+#define PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e -+#define PPI_RW_LB_TIMEBASE_CONFIG 0x1c20 -+#define PPI_RW_LB_STARTCMU_CONFIG 0x1c22 -+#define PPI_R_LBPULSE_COUNTER_RB 0x1c24 -+#define PPI_R_LB_START_CMU_RB 0x1c26 -+#define PPI_RW_LB_DPHY_BURST_START 0x1c28 -+#define PPI_RW_LB_CPHY_BURST_START 0x1c2a -+#define PPI_RW_DDLCAL_CFG_0 0x1c40 -+#define PPI_RW_DDLCAL_CFG_1 0x1c42 -+#define PPI_RW_DDLCAL_CFG_2 0x1c44 -+#define PPI_RW_DDLCAL_CFG_3 0x1c46 -+#define PPI_RW_DDLCAL_CFG_4 0x1c48 -+#define PPI_RW_DDLCAL_CFG_5 0x1c4a -+#define PPI_RW_DDLCAL_CFG_6 0x1c4c -+#define PPI_RW_DDLCAL_CFG_7 0x1c4e -+#define PPI_R_DDLCAL_DEBUG_0 0x1c50 -+#define PPI_R_DDLCAL_DEBUG_1 0x1c52 -+#define PPI_RW_PARITY_TEST 0x1c60 -+#define PPI_RW_STARTUP_OVR_0 0x1c62 -+#define PPI_RW_STARTUP_STATE_OVR_1 0x1c64 -+#define PPI_RW_DTB_SELECTOR 0x1c66 -+#define PPI_RW_DPHY_CLK_SPARE 0x1c6a -+#define PPI_RW_COMMON_CFG 0x1c6c -+#define PPI_RW_TERMCAL_CFG_0 0x1c80 -+#define PPI_R_TERMCAL_DEBUG_0 0x1c82 -+#define PPI_RW_TERMCAL_CTRL_0 0x1c84 -+#define PPI_RW_OFFSETCAL_CFG_0 0x1ca0 -+#define PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2 -+#define PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4 -+#define PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6 -+#define PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8 -+#define PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa -+#define PPI_RW_HSDCOCAL_CFG_O 0x1d00 -+#define PPI_RW_HSDCOCAL_CFG_1 0x1d02 -+#define PPI_RW_HSDCOCAL_CFG_2 0x1d04 -+#define PPI_RW_HSDCOCAL_CFG_3 0x1d06 -+#define PPI_RW_HSDCOCAL_CFG_4 0x1d08 -+#define PPI_RW_HSDCOCAL_CFG_5 0x1d0a -+#define PPI_RW_HSDCOCAL_CFG_6 0x1d0c -+#define PPI_RW_HSDCOCAL_CFG_7 0x1d0e -+#define PPI_RW_HSDCOCAL_CFG_8 0x1d10 -+#define PPI_R_HSDCOCAL_DEBUG_RB 0x1d12 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8 -+#define CORE_DIG_RW_TRIO0_0 0x2100 -+#define CORE_DIG_RW_TRIO0_1 0x2102 -+#define CORE_DIG_RW_TRIO0_2 0x2104 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8 -+#define CORE_DIG_RW_TRIO1_0 0x2500 -+#define CORE_DIG_RW_TRIO1_1 0x2502 -+#define CORE_DIG_RW_TRIO1_2 0x2504 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8 -+#define CORE_DIG_RW_TRIO2_0 0x2900 -+#define CORE_DIG_RW_TRIO2_1 0x2902 -+#define CORE_DIG_RW_TRIO2_2 0x2904 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856 -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858 -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860 -+#define CORE_DIG_RW_COMMON_0 0x3880 -+#define CORE_DIG_RW_COMMON_1 0x3882 -+#define CORE_DIG_RW_COMMON_2 0x3884 -+#define CORE_DIG_RW_COMMON_3 0x3886 -+#define CORE_DIG_RW_COMMON_4 0x3888 -+#define CORE_DIG_RW_COMMON_5 0x388a -+#define CORE_DIG_RW_COMMON_6 0x388c -+#define CORE_DIG_RW_COMMON_7 0x388e -+#define CORE_DIG_RW_COMMON_8 0x3890 -+#define CORE_DIG_RW_COMMON_9 0x3892 -+#define CORE_DIG_RW_COMMON_10 0x3894 -+#define CORE_DIG_RW_COMMON_11 0x3896 -+#define CORE_DIG_RW_COMMON_12 0x3898 -+#define CORE_DIG_RW_COMMON_13 0x389a -+#define CORE_DIG_RW_COMMON_14 0x389c -+#define CORE_DIG_RW_COMMON_15 0x389e -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0 -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2 -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4 -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6 -+#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0 -+#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2 -+#define PPI_RW_DPHY_LANE0_LBERT_0 0x4000 -+#define PPI_RW_DPHY_LANE0_LBERT_1 0x4002 -+#define PPI_R_DPHY_LANE0_LBERT_0 0x4004 -+#define PPI_R_DPHY_LANE0_LBERT_1 0x4006 -+#define PPI_RW_DPHY_LANE0_SPARE 0x4008 -+#define PPI_RW_DPHY_LANE1_LBERT_0 0x4400 -+#define PPI_RW_DPHY_LANE1_LBERT_1 0x4402 -+#define PPI_R_DPHY_LANE1_LBERT_0 0x4404 -+#define PPI_R_DPHY_LANE1_LBERT_1 0x4406 -+#define PPI_RW_DPHY_LANE1_SPARE 0x4408 -+#define PPI_RW_DPHY_LANE2_LBERT_0 0x4800 -+#define PPI_RW_DPHY_LANE2_LBERT_1 0x4802 -+#define PPI_R_DPHY_LANE2_LBERT_0 0x4804 -+#define PPI_R_DPHY_LANE2_LBERT_1 0x4806 -+#define PPI_RW_DPHY_LANE2_SPARE 0x4808 -+#define PPI_RW_DPHY_LANE3_LBERT_0 0x4c00 -+#define PPI_RW_DPHY_LANE3_LBERT_1 0x4c02 -+#define PPI_R_DPHY_LANE3_LBERT_0 0x4c04 -+#define PPI_R_DPHY_LANE3_LBERT_1 0x4c06 -+#define PPI_RW_DPHY_LANE3_SPARE 0x4c08 -+#define CORE_DIG_DLANE_0_RW_CFG_0 0x6000 -+#define CORE_DIG_DLANE_0_RW_CFG_1 0x6002 -+#define CORE_DIG_DLANE_0_RW_CFG_2 0x6004 -+#define CORE_DIG_DLANE_0_RW_LP_0 0x6080 -+#define CORE_DIG_DLANE_0_RW_LP_1 0x6082 -+#define CORE_DIG_DLANE_0_RW_LP_2 0x6084 -+#define CORE_DIG_DLANE_0_R_LP_0 0x60a0 -+#define CORE_DIG_DLANE_0_R_LP_1 0x60a2 -+#define CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0 -+#define CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100 -+#define CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102 -+#define CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104 -+#define CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106 -+#define CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108 -+#define CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a -+#define CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c -+#define CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e -+#define CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110 -+#define CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112 -+#define CORE_DIG_DLANE_0_R_HS_RX_0 0x6120 -+#define CORE_DIG_DLANE_0_R_HS_RX_1 0x6122 -+#define CORE_DIG_DLANE_0_R_HS_RX_2 0x6124 -+#define CORE_DIG_DLANE_0_R_HS_RX_3 0x6126 -+#define CORE_DIG_DLANE_0_R_HS_RX_4 0x6128 -+#define CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200 -+#define CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202 -+#define CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204 -+#define CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206 -+#define CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208 -+#define CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a -+#define CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c -+#define CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e -+#define CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210 -+#define CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212 -+#define CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214 -+#define CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216 -+#define CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218 -+#define CORE_DIG_DLANE_1_RW_CFG_0 0x6400 -+#define CORE_DIG_DLANE_1_RW_CFG_1 0x6402 -+#define CORE_DIG_DLANE_1_RW_CFG_2 0x6404 -+#define CORE_DIG_DLANE_1_RW_LP_0 0x6480 -+#define CORE_DIG_DLANE_1_RW_LP_1 0x6482 -+#define CORE_DIG_DLANE_1_RW_LP_2 0x6484 -+#define CORE_DIG_DLANE_1_R_LP_0 0x64a0 -+#define CORE_DIG_DLANE_1_R_LP_1 0x64a2 -+#define CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0 -+#define CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500 -+#define CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502 -+#define CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504 -+#define CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506 -+#define CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508 -+#define CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a -+#define CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c -+#define CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e -+#define CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510 -+#define CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512 -+#define CORE_DIG_DLANE_1_R_HS_RX_0 0x6520 -+#define CORE_DIG_DLANE_1_R_HS_RX_1 0x6522 -+#define CORE_DIG_DLANE_1_R_HS_RX_2 0x6524 -+#define CORE_DIG_DLANE_1_R_HS_RX_3 0x6526 -+#define CORE_DIG_DLANE_1_R_HS_RX_4 0x6528 -+#define CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600 -+#define CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602 -+#define CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604 -+#define CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606 -+#define CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608 -+#define CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a -+#define CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c -+#define CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e -+#define CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610 -+#define CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612 -+#define CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614 -+#define CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616 -+#define CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618 -+#define CORE_DIG_DLANE_2_RW_CFG_0 0x6800 -+#define CORE_DIG_DLANE_2_RW_CFG_1 0x6802 -+#define CORE_DIG_DLANE_2_RW_CFG_2 0x6804 -+#define CORE_DIG_DLANE_2_RW_LP_0 0x6880 -+#define CORE_DIG_DLANE_2_RW_LP_1 0x6882 -+#define CORE_DIG_DLANE_2_RW_LP_2 0x6884 -+#define CORE_DIG_DLANE_2_R_LP_0 0x68a0 -+#define CORE_DIG_DLANE_2_R_LP_1 0x68a2 -+#define CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0 -+#define CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900 -+#define CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902 -+#define CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904 -+#define CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906 -+#define CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908 -+#define CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a -+#define CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c -+#define CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e -+#define CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910 -+#define CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912 -+#define CORE_DIG_DLANE_2_R_HS_RX_0 0x6920 -+#define CORE_DIG_DLANE_2_R_HS_RX_1 0x6922 -+#define CORE_DIG_DLANE_2_R_HS_RX_2 0x6924 -+#define CORE_DIG_DLANE_2_R_HS_RX_3 0x6926 -+#define CORE_DIG_DLANE_2_R_HS_RX_4 0x6928 -+#define CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00 -+#define CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02 -+#define CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04 -+#define CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06 -+#define CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08 -+#define CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a -+#define CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c -+#define CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e -+#define CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10 -+#define CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12 -+#define CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14 -+#define CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16 -+#define CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18 -+#define CORE_DIG_DLANE_3_RW_CFG_0 0x6c00 -+#define CORE_DIG_DLANE_3_RW_CFG_1 0x6c02 -+#define CORE_DIG_DLANE_3_RW_CFG_2 0x6c04 -+#define CORE_DIG_DLANE_3_RW_LP_0 0x6c80 -+#define CORE_DIG_DLANE_3_RW_LP_1 0x6c82 -+#define CORE_DIG_DLANE_3_RW_LP_2 0x6c84 -+#define CORE_DIG_DLANE_3_R_LP_0 0x6ca0 -+#define CORE_DIG_DLANE_3_R_LP_1 0x6ca2 -+#define CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0 -+#define CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00 -+#define CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02 -+#define CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04 -+#define CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06 -+#define CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08 -+#define CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a -+#define CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c -+#define CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e -+#define CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10 -+#define CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12 -+#define CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20 -+#define CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22 -+#define CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24 -+#define CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26 -+#define CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28 -+#define CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00 -+#define CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02 -+#define CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04 -+#define CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06 -+#define CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08 -+#define CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a -+#define CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c -+#define CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e -+#define CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10 -+#define CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12 -+#define CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14 -+#define CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16 -+#define CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18 -+#define CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000 -+#define CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002 -+#define CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004 -+#define CORE_DIG_DLANE_CLK_RW_LP_0 0x7080 -+#define CORE_DIG_DLANE_CLK_RW_LP_1 0x7082 -+#define CORE_DIG_DLANE_CLK_RW_LP_2 0x7084 -+#define CORE_DIG_DLANE_CLK_R_LP_0 0x70a0 -+#define CORE_DIG_DLANE_CLK_R_LP_1 0x70a2 -+#define CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218 -+#define PPI_RW_CPHY_TRIO0_LBERT_0 0x8000 -+#define PPI_RW_CPHY_TRIO0_LBERT_1 0x8002 -+#define PPI_R_CPHY_TRIO0_LBERT_0 0x8004 -+#define PPI_R_CPHY_TRIO0_LBERT_1 0x8006 -+#define PPI_RW_CPHY_TRIO0_SPARE 0x8008 -+#define PPI_RW_CPHY_TRIO1_LBERT_0 0x8400 -+#define PPI_RW_CPHY_TRIO1_LBERT_1 0x8402 -+#define PPI_R_CPHY_TRIO1_LBERT_0 0x8404 -+#define PPI_R_CPHY_TRIO1_LBERT_1 0x8406 -+#define PPI_RW_CPHY_TRIO1_SPARE 0x8408 -+#define PPI_RW_CPHY_TRIO2_LBERT_0 0x8800 -+#define PPI_RW_CPHY_TRIO2_LBERT_1 0x8802 -+#define PPI_R_CPHY_TRIO2_LBERT_0 0x8804 -+#define PPI_R_CPHY_TRIO2_LBERT_1 0x8806 -+#define PPI_RW_CPHY_TRIO2_SPARE 0x8808 -+#define CORE_DIG_CLANE_0_RW_CFG_0 0xa000 -+#define CORE_DIG_CLANE_0_RW_CFG_2 0xa004 -+#define CORE_DIG_CLANE_0_RW_LP_0 0xa080 -+#define CORE_DIG_CLANE_0_RW_LP_1 0xa082 -+#define CORE_DIG_CLANE_0_RW_LP_2 0xa084 -+#define CORE_DIG_CLANE_0_R_LP_0 0xa0a0 -+#define CORE_DIG_CLANE_0_R_LP_1 0xa0a2 -+#define CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100 -+#define CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102 -+#define CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104 -+#define CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106 -+#define CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108 -+#define CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a -+#define CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c -+#define CORE_DIG_CLANE_0_R_RX_0 0xa120 -+#define CORE_DIG_CLANE_0_R_RX_1 0xa122 -+#define CORE_DIG_CLANE_0_R_TX_0 0xa124 -+#define CORE_DIG_CLANE_0_R_RX_2 0xa126 -+#define CORE_DIG_CLANE_0_R_RX_3 0xa128 -+#define CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200 -+#define CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202 -+#define CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204 -+#define CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206 -+#define CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208 -+#define CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a -+#define CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c -+#define CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e -+#define CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210 -+#define CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212 -+#define CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214 -+#define CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216 -+#define CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218 -+#define CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a -+#define CORE_DIG_CLANE_1_RW_CFG_0 0xa400 -+#define CORE_DIG_CLANE_1_RW_CFG_2 0xa404 -+#define CORE_DIG_CLANE_1_RW_LP_0 0xa480 -+#define CORE_DIG_CLANE_1_RW_LP_1 0xa482 -+#define CORE_DIG_CLANE_1_RW_LP_2 0xa484 -+#define CORE_DIG_CLANE_1_R_LP_0 0xa4a0 -+#define CORE_DIG_CLANE_1_R_LP_1 0xa4a2 -+#define CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500 -+#define CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502 -+#define CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504 -+#define CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506 -+#define CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508 -+#define CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a -+#define CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c -+#define CORE_DIG_CLANE_1_R_RX_0 0xa520 -+#define CORE_DIG_CLANE_1_R_RX_1 0xa522 -+#define CORE_DIG_CLANE_1_R_TX_0 0xa524 -+#define CORE_DIG_CLANE_1_R_RX_2 0xa526 -+#define CORE_DIG_CLANE_1_R_RX_3 0xa528 -+#define CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600 -+#define CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602 -+#define CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604 -+#define CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606 -+#define CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608 -+#define CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a -+#define CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c -+#define CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e -+#define CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610 -+#define CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612 -+#define CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614 -+#define CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616 -+#define CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618 -+#define CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a -+#define CORE_DIG_CLANE_2_RW_CFG_0 0xa800 -+#define CORE_DIG_CLANE_2_RW_CFG_2 0xa804 -+#define CORE_DIG_CLANE_2_RW_LP_0 0xa880 -+#define CORE_DIG_CLANE_2_RW_LP_1 0xa882 -+#define CORE_DIG_CLANE_2_RW_LP_2 0xa884 -+#define CORE_DIG_CLANE_2_R_LP_0 0xa8a0 -+#define CORE_DIG_CLANE_2_R_LP_1 0xa8a2 -+#define CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900 -+#define CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902 -+#define CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904 -+#define CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906 -+#define CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908 -+#define CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a -+#define CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c -+#define CORE_DIG_CLANE_2_R_RX_0 0xa920 -+#define CORE_DIG_CLANE_2_R_RX_1 0xa922 -+#define CORE_DIG_CLANE_2_R_TX_0 0xa924 -+#define CORE_DIG_CLANE_2_R_RX_2 0xa926 -+#define CORE_DIG_CLANE_2_R_RX_3 0xa928 -+#define CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00 -+#define CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02 -+#define CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04 -+#define CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06 -+#define CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08 -+#define CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a -+#define CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c -+#define CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e -+#define CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10 -+#define CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12 -+#define CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14 -+#define CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16 -+#define CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18 -+#define CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a -+ -+/* dwc csi host controller registers */ -+#define IS_IO_CSI2_HOST_BASE(i) (IS_IO_BASE + 0x40000 + \ -+ 0x2000 * (i)) -+#define VERSION 0x0 -+#define N_LANES 0x4 -+#define CSI2_RESETN 0x8 -+#define INT_ST_MAIN 0xc -+#define DATA_IDS_1 0x10 -+#define DATA_IDS_2 0x14 -+#define CDPHY_MODE 0x1c -+#define DATA_IDS_VC_1 0x30 -+#define DATA_IDS_VC_2 0x34 -+#define PHY_SHUTDOWNZ 0x40 -+#define DPHY_RSTZ 0x44 -+#define PHY_RX 0x48 -+#define PHY_STOPSTATE 0x4c -+#define PHY_TEST_CTRL0 0x50 -+#define PHY_TEST_CTRL1 0x54 -+#define PPI_PG_PATTERN_VRES 0x60 -+#define PPI_PG_PATTERN_HRES 0x64 -+#define PPI_PG_CONFIG 0x68 -+#define PPI_PG_ENABLE 0x6c -+#define PPI_PG_STATUS 0x70 -+#define VC_EXTENSION 0xc8 -+#define PHY_CAL 0xcc -+#define INT_ST_PHY_FATAL 0xe0 -+#define INT_MSK_PHY_FATAL 0xe4 -+#define INT_FORCE_PHY_FATAL 0xe8 -+#define INT_ST_PKT_FATAL 0xf0 -+#define INT_MSK_PKT_FATAL 0xf4 -+#define INT_FORCE_PKT_FATAL 0xf8 -+#define INT_ST_PHY 0x110 -+#define INT_MSK_PHY 0x114 -+#define INT_FORCE_PHY 0x118 -+#define INT_ST_LINE 0x130 -+#define INT_MSK_LINE 0x134 -+#define INT_FORCE_LINE 0x138 -+#define INT_ST_BNDRY_FRAME_FATAL 0x280 -+#define INT_MSK_BNDRY_FRAME_FATAL 0x284 -+#define INT_FORCE_BNDRY_FRAME_FATAL 0x288 -+#define INT_ST_SEQ_FRAME_FATAL 0x290 -+#define INT_MSK_SEQ_FRAME_FATAL 0x294 -+#define INT_FORCE_SEQ_FRAME_FATAL 0x298 -+#define INT_ST_CRC_FRAME_FATAL 0x2a0 -+#define INT_MSK_CRC_FRAME_FATAL 0x2a4 -+#define INT_FORCE_CRC_FRAME_FATAL 0x2a8 -+#define INT_ST_PLD_CRC_FATAL 0x2b0 -+#define INT_MSK_PLD_CRC_FATAL 0x2b4 -+#define INT_FORCE_PLD_CRC_FATAL 0x2b8 -+#define INT_ST_DATA_ID 0x2c0 -+#define INT_MSK_DATA_ID 0x2c4 -+#define INT_FORCE_DATA_ID 0x2c8 -+#define INT_ST_ECC_CORRECTED 0x2d0 -+#define INT_MSK_ECC_CORRECTED 0x2d4 -+#define INT_FORCE_ECC_CORRECTED 0x2d8 -+#define SCRAMBLING 0x300 -+#define SCRAMBLING_SEED1 0x304 -+#define SCRAMBLING_SEED2 0x308 -+#define SCRAMBLING_SEED3 0x30c -+#define SCRAMBLING_SEED4 0x310 -+#define SCRAMBLING 0x300 -+ -+#define IS_IO_CSI2_ADPL_PORT_BASE(i) (IS_IO_BASE + 0x40800 + \ -+ 0x2000 * (i)) -+#define CSI2_ADPL_INPUT_MODE 0x0 -+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4 -+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8 -+#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc -+#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4 -+#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8 -+#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac -+#define CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc -+#define CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0 -+#define CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4 -+#define CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8 -+#define CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc -+#define CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0 -+ -+/* software control the legacy csi irq */ -+#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40c00 + \ -+ 0x2000 * (i)) -+#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \ -+ 0x2000 * (i)) -+#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IS_IO_BASE + 0x49000) -+#define IS_IO_CSI2_IRQ_CTRL_BASE (IS_IO_BASE + 0x4e100) -+ -+#define IRQ_CTL_EDGE 0x0 -+#define IRQ_CTL_MASK 0x4 -+#define IRQ_CTL_STATUS 0x8 -+#define IRQ_CTL_CLEAR 0xc -+#define IRQ_CTL_ENABLE 0x10 -+/* FE irq for PTL */ -+#define IRQ1_CTL_MASK 0x14 -+#define IRQ1_CTL_STATUS 0x18 -+#define IRQ1_CTL_CLEAR 0x1c -+#define IRQ1_CTL_ENABLE 0x20 -+ -+/* software to set the clock gate to use the port or mgc */ -+#define IS_IO_GPREGS_BASE (IS_IO_BASE + 0x49200) -+#define SRST_PORT_ARB 0x0 -+#define SRST_MGC 0x4 -+#define SRST_WIDTH_CONV 0x8 -+#define SRST_CSI_IRQ 0xc -+#define SRST_CSI_LEGACY_IRQ 0x10 -+#define CLK_EN_TXCLKESC 0x14 -+#define CLK_DIV_FACTOR_IS_CLK 0x18 -+#define CLK_DIV_FACTOR_APB_CLK 0x1c -+#define CSI_PORT_CLK_GATE 0x20 -+#define CSI_PORTAB_AGGREGATION 0x24 -+#define MGC_CLK_GATE 0x2c -+#define CG_CTRL_BITS 0x3c -+#define SPARE_RW 0xf8 -+#define SPARE_RO 0xfc -+ -+#define IS_IO_CSI2_MPF_PORT_BASE(i) (IS_IO_BASE + 0x53000 + \ -+ 0x1000 * (i)) -+#define MPF_16_IRQ_CNTRL_STATUS 0x238 -+#define MPF_16_IRQ_CNTRL_CLEAR 0x23c -+#define MPF_16_IRQ_CNTRL_ENABLE 0x240 -+ -+/* software config the phy */ -+#define IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x53400) -+#define IPU8_IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x40e00) -+#define CSI_ADAPT_LAYER_SRST 0x0 -+#define MPF_SRST_RST 0x4 -+#define CSI_ERR_IRQ_CTRL_SRST 0x8 -+#define CSI_SYNC_RC_SRST 0xc -+#define CSI_CG_CTRL_BITS 0x10 -+#define SOC_CSI2HOST_SELECT 0x14 -+#define PHY_RESET 0x18 -+#define PHY_SHUTDOWN 0x1c -+#define PHY_MODE 0x20 -+#define PHY_READY 0x24 -+#define PHY_CLK_LANE_FORCE_CONTROL 0x28 -+#define PHY_CLK_LANE_CONTROL 0x2c -+#define PHY_CLK_LANE_STATUS 0x30 -+#define PHY_LANE_RX_ESC_REQ 0x34 -+#define PHY_LANE_RX_ESC_DATA 0x38 -+#define PHY_LANE_TURNDISABLE 0x3c -+#define PHY_LANE_DIRECTION 0x40 -+#define PHY_LANE_FORCE_CONTROL 0x44 -+#define PHY_LANE_CONTROL_EN 0x48 -+#define PHY_LANE_CONTROL_DATAWIDTH 0x4c -+#define PHY_LANE_STATUS 0x50 -+#define PHY_LANE_ERR 0x54 -+#define PHY_LANE_RXALP 0x58 -+#define PHY_LANE_RXALP_NIBBLE 0x5c -+#define PHY_PARITY_ERROR 0x60 -+#define PHY_DEBUG_REGS_CLK_GATE_EN 0x64 -+#define SPARE_RW 0xf8 -+#define SPARE_RO 0xfc -+ -+/* software not touch */ -+#define PORT_ARB_BASE (IS_IO_BASE + 0x4e000) -+#define PORT_ARB_IRQ_CTL_STATUS 0x4 -+#define PORT_ARB_IRQ_CTL_CLEAR 0x8 -+#define PORT_ARB_IRQ_CTL_ENABLE 0xc -+ -+#define MGC_PPC 4U -+#define MGC_DTYPE_RAW(i) (((i) - 8) / 2) -+#define IS_IO_MGC_BASE (IS_IO_BASE + 0x48000) -+#define MGC_KICK 0x0 -+#define MGC_ASYNC_STOP 0x4 -+#define MGC_PORT_OFFSET 0x100 -+#define MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4) -+#define MGC_MG_PORT(i) (IS_IO_MGC_BASE + \ -+ (i) * MGC_PORT_OFFSET) -+/* per mgc instance */ -+#define MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28 -+#define MGC_MG_MODE 0x2c -+#define MGC_MG_INIT_COUNTER 0x30 -+#define MGC_MG_MIPI_VC 0x34 -+#define MGC_MG_MIPI_DTYPES 0x38 -+#define MGC_MG_MULTI_DTYPES_MODE 0x3c -+#define MGC_MG_NOF_FRAMES 0x40 -+#define MGC_MG_FRAME_DIM 0x44 -+#define MGC_MG_HBLANK 0x48 -+#define MGC_MG_VBLANK 0x4c -+#define MGC_MG_TPG_MODE 0x50 -+#define MGC_MG_TPG_R0 0x54 -+#define MGC_MG_TPG_G0 0x58 -+#define MGC_MG_TPG_B0 0x5c -+#define MGC_MG_TPG_R1 0x60 -+#define MGC_MG_TPG_G1 0x64 -+#define MGC_MG_TPG_B1 0x68 -+#define MGC_MG_TPG_FACTORS 0x6c -+#define MGC_MG_TPG_MASKS 0x70 -+#define MGC_MG_TPG_XY_MASK 0x74 -+#define MGC_MG_TPG_TILE_DIM 0x78 -+#define MGC_MG_PRBS_LFSR_INIT_0 0x7c -+#define MGC_MG_PRBS_LFSR_INIT_1 0x80 -+#define MGC_MG_SYNC_STOP_POINT 0x84 -+#define MGC_MG_SYNC_STOP_POINT_LOC 0x88 -+#define MGC_MG_ERR_INJECT 0x8c -+#define MGC_MG_ERR_LOCATION 0x90 -+#define MGC_MG_DTO_SPEED_CTRL_EN 0x94 -+#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98 -+#define MGC_MG_HOR_LOC_STTS 0x9c -+#define MGC_MG_VER_LOC_STTS 0xa0 -+#define MGC_MG_FRAME_NUM_STTS 0xa4 -+#define MGC_MG_BUSY_STTS 0xa8 -+#define MGC_MG_STOPPED_STTS 0xac -+/* tile width and height in pixels for Chess board and Color palette */ -+#define MGC_TPG_TILE_WIDTH 64U -+#define MGC_TPG_TILE_HEIGHT 64U -+ -+#define IPU_CSI_PORT_A_ADDR_OFFSET 0x0 -+#define IPU_CSI_PORT_B_ADDR_OFFSET 0x0 -+#define IPU_CSI_PORT_C_ADDR_OFFSET 0x0 -+#define IPU_CSI_PORT_D_ADDR_OFFSET 0x0 -+ -+/* -+ * 0 - CSI RX Port 0 interrupt; -+ * 1 - MPF Port 0 interrupt; -+ * 2 - CSI RX Port 1 interrupt; -+ * 3 - MPF Port 1 interrupt; -+ * 4 - CSI RX Port 2 interrupt; -+ * 5 - MPF Port 2 interrupt; -+ * 6 - CSI RX Port 3 interrupt; -+ * 7 - MPF Port 3 interrupt; -+ * 8 - Port ARB FIFO 0 overflow; -+ * 9 - Port ARB FIFO 1 overflow; -+ * 10 - Port ARB FIFO 2 overflow; -+ * 11 - Port ARB FIFO 3 overflow; -+ * 12 - isys_cfgnoc_err_probe_intl; -+ * 13-15 - reserved -+ */ -+#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff -+ -+/* Adapter layer irq */ -+#define IPU7_CSI_ADPL_IRQ_MASK 0xffff -+ -+/* sw irq from legacy irq control -+ * legacy irq status -+ * IPU7 -+ * 0 - CSI Port 0 error interrupt -+ * 1 - CSI Port 0 sync interrupt -+ * 2 - CSI Port 1 error interrupt -+ * 3 - CSI Port 1 sync interrupt -+ * 4 - CSI Port 2 error interrupt -+ * 5 - CSI Port 2 sync interrupt -+ * 6 - CSI Port 3 error interrupt -+ * 7 - CSI Port 3 sync interrupt -+ * IPU7P5 -+ * 0 - CSI Port 0 error interrupt -+ * 1 - CSI Port 0 fs interrupt -+ * 2 - CSI Port 0 fe interrupt -+ * 3 - CSI Port 1 error interrupt -+ * 4 - CSI Port 1 fs interrupt -+ * 5 - CSI Port 1 fe interrupt -+ * 6 - CSI Port 2 error interrupt -+ * 7 - CSI Port 2 fs interrupt -+ * 8 - CSI Port 2 fe interrupt -+ */ -+#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff -+ -+/* legacy error status per port -+ * 0 - Error handler FIFO full; -+ * 1 - Reserved Short Packet encoding detected; -+ * 2 - Reserved Long Packet encoding detected; -+ * 3 - Received packet is too short (fewer data words than specified in header); -+ * 4 - Received packet is too long (more data words than specified in header); -+ * 5 - Short packet discarded due to errors; -+ * 6 - Long packet discarded due to errors; -+ * 7 - CSI Combo Rx interrupt; -+ * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0; -+ */ -+#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff -+ -+/* -+ * 0 - VC0 frame start received -+ * 1 - VC0 frame end received -+ * 2 - VC1 frame start received -+ * 3 - VC1 frame end received -+ * 4 - VC2 frame start received -+ * 5 - VC2 frame end received -+ * 6 - VC3 frame start received -+ * 7 - VC3 frame end received -+ * 8 - VC4 frame start received -+ * 9 - VC4 frame end received -+ * 10 - VC5 frame start received -+ * 11 - VC5 frame end received -+ * 12 - VC6 frame start received -+ * 13 - VC6 frame end received -+ * 14 - VC7 frame start received -+ * 15 - VC7 frame end received -+ * 16 - VC8 frame start received -+ * 17 - VC8 frame end received -+ * 18 - VC9 frame start received -+ * 19 - VC9 frame end received -+ * 20 - VC10 frame start received -+ * 21 - VC10 frame end received -+ * 22 - VC11 frame start received -+ * 23 - VC11 frame end received -+ * 24 - VC12 frame start received -+ * 25 - VC12 frame end received -+ * 26 - VC13 frame start received -+ * 27 - VC13 frame end received -+ * 28 - VC14 frame start received -+ * 29 - VC14 frame end received -+ * 30 - VC15 frame start received -+ * 31 - VC15 frame end received -+ */ -+ -+#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0 -+#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0 -+ -+#define CSI_RX_NUM_ERRORS_IN_IRQ 12U -+#define CSI_RX_NUM_SYNC_IN_IRQ 32U -+ -+enum CSI_FE_MODE_TYPE { -+ CSI_FE_DPHY_MODE = 0, -+ CSI_FE_CPHY_MODE = 1, -+}; -+ -+enum CSI_FE_INPUT_MODE { -+ CSI_SENSOR_INPUT = 0, -+ CSI_MIPIGEN_INPUT = 1, -+}; -+ -+enum MGC_CSI_ADPL_TYPE { -+ MGC_MAPPED_2_LANES = 0, -+ MGC_MAPPED_4_LANES = 1, -+}; -+ -+enum CSI2HOST_SELECTION { -+ CSI2HOST_SEL_SOC = 0, -+ CSI2HOST_SEL_CSI2HOST = 1, -+}; -+ -+#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port)) -+#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port)) -+ -+/* ---------------------------------------------------------------- */ -+#define CSI_REG_BASE 0x220000 -+#define CSI_REG_BASE_PORT(id) ((id) * 0x1000) -+ -+/* CSI Port General Purpose Registers */ -+#define CSI_REG_PORT_GPREG_SRST 0x0 -+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 -+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 -+ -+#define CSI_RX_NUM_IRQ 32U -+ -+#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555 -+#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa -+#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff -+#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff -+ -+#endif /* IPU7_ISYS_CSI2_REG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -new file mode 100644 -index 000000000000..0fe4299222e8 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -@@ -0,0 +1,544 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-isys-csi-phy.h" -+ -+static const u32 csi2_supported_codes[] = { -+ MEDIA_BUS_FMT_Y10_1X10, -+ MEDIA_BUS_FMT_RGB565_1X16, -+ MEDIA_BUS_FMT_RGB888_1X24, -+ MEDIA_BUS_FMT_UYVY8_1X16, -+ MEDIA_BUS_FMT_YUYV8_1X16, -+ MEDIA_BUS_FMT_YUYV10_1X20, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ 0, -+}; -+ -+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) -+{ -+ struct media_pad *src_pad; -+ -+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -+ if (IS_ERR(src_pad)) { -+ dev_err(&csi2->isys->adev->auxdev.dev, -+ "can't get source pad of %s (%ld)\n", -+ csi2->asd.sd.name, PTR_ERR(src_pad)); -+ return PTR_ERR(src_pad); -+ } -+ -+ return v4l2_get_link_freq(src_pad, 0, 0); -+} -+ -+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", -+ sub->type, sub->id); -+ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { -+ .subscribe_event = csi2_subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_device *isp = csi2->isys->adev->isp; -+ unsigned int offset, mask; -+ -+ /* enable CSI2 legacy error irq */ -+ offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(mask, csi2->base + offset + IRQ_CTL_MASK); -+ writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ /* enable CSI2 legacy sync irq */ -+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(mask, csi2->base + offset + IRQ_CTL_MASK); -+ writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK; -+ if (!is_ipu7(isp->hw_ver)) { -+ writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -+ writel(mask, csi2->base + offset + IRQ1_CTL_MASK); -+ writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE); -+ } -+} -+ -+static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_device *isp = csi2->isys->adev->isp; -+ unsigned int offset, mask; -+ -+ /* disable CSI2 legacy error irq */ -+ offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(0, csi2->base + offset + IRQ_CTL_MASK); -+ writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ /* disable CSI2 legacy sync irq */ -+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(0, csi2->base + offset + IRQ_CTL_MASK); -+ writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ if (!is_ipu7(isp->hw_ver)) { -+ writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -+ writel(0, csi2->base + offset + IRQ1_CTL_MASK); -+ writel(0, csi2->base + offset + IRQ1_CTL_ENABLE); -+ } -+} -+ -+static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ void __iomem *isys_base = isys->pdata->base; -+ -+ ipu7_isys_csi_phy_powerdown(csi2); -+ -+ writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); -+ csi2_irq_disable(csi2); -+} -+ -+static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ unsigned int port, nlanes, offset; -+ int ret; -+ -+ port = csi2->port; -+ nlanes = csi2->nlanes; -+ -+ offset = IS_IO_GPREGS_BASE; -+ writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK); -+ dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", -+ port, readl(isys_base + offset + CSI_PORT_CLK_GATE), -+ readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); -+ if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { -+ dev_dbg(dev, "CSI port %u in aggregation mode\n", port); -+ writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); -+ } -+ -+ /* input is coming from CSI receiver (sensor) */ -+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -+ writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE); -+ writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); -+ -+ ret = ipu7_isys_csi_phy_powerup(csi2); -+ if (ret) { -+ dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret); -+ return ret; -+ } -+ -+ csi2_irq_enable(csi2); -+ -+ return 0; -+} -+ -+static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct device *dev = &asd->isys->adev->auxdev.dev; -+ struct v4l2_mbus_framefmt *sink_ffmt; -+ struct v4l2_mbus_framefmt *src_ffmt; -+ struct v4l2_rect *crop; -+ -+ if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; -+ -+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -+ sel->pad, -+ sel->stream); -+ if (!sink_ffmt) -+ return -EINVAL; -+ -+ src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); -+ if (!src_ffmt) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -+ if (!crop) -+ return -EINVAL; -+ -+ /* Only vertical cropping is supported */ -+ sel->r.left = 0; -+ sel->r.width = sink_ffmt->width; -+ /* Non-bayer formats can't be single line cropped */ -+ if (!ipu7_isys_is_bayer_format(sink_ffmt->code)) -+ sel->r.top &= ~1U; -+ sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT, -+ sink_ffmt->height - sel->r.top); -+ *crop = sel->r; -+ -+ /* update source pad format */ -+ src_ffmt->width = sel->r.width; -+ src_ffmt->height = sel->r.height; -+ if (ipu7_isys_is_bayer_format(sink_ffmt->code)) -+ src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code, -+ sel->r.left, -+ sel->r.top); -+ dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", -+ sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, -+ src_ffmt->code); -+ -+ return 0; -+} -+ -+static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct v4l2_mbus_framefmt *sink_ffmt; -+ struct v4l2_rect *crop; -+ int ret = 0; -+ -+ if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) -+ return -EINVAL; -+ -+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -+ sel->pad, -+ sel->stream); -+ if (!sink_ffmt) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -+ if (!crop) -+ return -EINVAL; -+ -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = sink_ffmt->width; -+ sel->r.height = sink_ffmt->height; -+ break; -+ case V4L2_SEL_TGT_CROP: -+ sel->r = *crop; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+/* -+ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set -+ * of streams. -+ */ -+#define CSI2_SUBDEV_MAX_STREAM_ID 63 -+ -+static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -+ struct v4l2_subdev *r_sd; -+ struct media_pad *rp; -+ u32 sink_pad, sink_stream; -+ int ret, i; -+ -+ if (!csi2->stream_count) { -+ dev_dbg(&csi2->isys->adev->auxdev.dev, -+ "stream on CSI2-%u with %u lanes\n", csi2->port, -+ csi2->nlanes); -+ ret = ipu7_isys_csi2_enable_stream(csi2); -+ if (ret) -+ return ret; -+ } -+ -+ for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -+ if (streams_mask & BIT_ULL(i)) -+ break; -+ } -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -+ &sink_pad, &sink_stream); -+ if (ret) -+ return ret; -+ -+ rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -+ r_sd = media_entity_to_v4l2_subdev(rp->entity); -+ -+ ret = v4l2_subdev_enable_streams(r_sd, rp->index, -+ BIT_ULL(sink_stream)); -+ if (!ret) { -+ csi2->stream_count++; -+ return 0; -+ } -+ -+ if (!csi2->stream_count) -+ ipu7_isys_csi2_disable_stream(csi2); -+ -+ return ret; -+} -+ -+static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -+ struct v4l2_subdev *r_sd; -+ struct media_pad *rp; -+ u32 sink_pad, sink_stream; -+ int ret, i; -+ -+ for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -+ if (streams_mask & BIT_ULL(i)) -+ break; -+ } -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -+ &sink_pad, &sink_stream); -+ if (ret) -+ return ret; -+ -+ rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -+ r_sd = media_entity_to_v4l2_subdev(rp->entity); -+ -+ v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream)); -+ -+ if (--csi2->stream_count) -+ return 0; -+ -+ dev_dbg(&csi2->isys->adev->auxdev.dev, -+ "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); -+ -+ ipu7_isys_csi2_disable_stream(csi2); -+ -+ return 0; -+} -+ -+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu7_isys_subdev_set_fmt, -+ .get_selection = ipu7_isys_csi2_get_sel, -+ .set_selection = ipu7_isys_csi2_set_sel, -+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -+ .enable_streams = ipu7_isys_csi2_enable_streams, -+ .disable_streams = ipu7_isys_csi2_disable_streams, -+ .set_routing = ipu7_isys_subdev_set_routing, -+}; -+ -+static const struct v4l2_subdev_ops csi2_sd_ops = { -+ .core = &csi2_sd_core_ops, -+ .pad = &csi2_sd_pad_ops, -+}; -+ -+static const struct media_entity_operations csi2_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+ .has_pad_interdep = v4l2_subdev_has_pad_interdep, -+}; -+ -+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2) -+{ -+ if (!csi2->isys) -+ return; -+ -+ v4l2_device_unregister_subdev(&csi2->asd.sd); -+ v4l2_subdev_cleanup(&csi2->asd.sd); -+ ipu7_isys_subdev_cleanup(&csi2->asd); -+ csi2->isys = NULL; -+} -+ -+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, -+ struct ipu7_isys *isys, -+ void __iomem *base, unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ csi2->isys = isys; -+ csi2->base = base; -+ csi2->port = index; -+ -+ if (!is_ipu7(isys->adev->isp->hw_ver)) -+ csi2->legacy_irq_mask = 0x7U << (index * 3U); -+ else -+ csi2->legacy_irq_mask = 0x3U << (index * 2U); -+ -+ dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index, -+ csi2->legacy_irq_mask); -+ -+ csi2->asd.sd.entity.ops = &csi2_entity_ops; -+ csi2->asd.isys = isys; -+ -+ ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, -+ IPU7_NR_OF_CSI2_SINK_PADS, -+ IPU7_NR_OF_CSI2_SRC_PADS); -+ if (ret) -+ return ret; -+ -+ csi2->asd.source = (int)index; -+ csi2->asd.supported_codes = csi2_supported_codes; -+ snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), -+ IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index); -+ v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); -+ -+ ret = v4l2_subdev_init_finalize(&csi2->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret); -+ goto isys_subdev_cleanup; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret); -+ goto v4l2_subdev_cleanup; -+ } -+ -+ return 0; -+ -+v4l2_subdev_cleanup: -+ v4l2_subdev_cleanup(&csi2->asd.sd); -+isys_subdev_cleanup: -+ ipu7_isys_subdev_cleanup(&csi2->asd); -+ -+ return ret; -+} -+ -+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct video_device *vdev = csi2->asd.sd.devnode; -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.id = stream->vc; -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", -+ csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); -+} -+ -+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", -+ csi2->port, frame_sequence); -+} -+ -+int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -+ struct ipu7_isys_csi2 *csi2, -+ struct media_entity *source_entity, -+ struct v4l2_mbus_frame_desc_entry *entry, -+ int *nr_queues) -+{ -+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct v4l2_mbus_frame_desc desc; -+ struct v4l2_subdev *source; -+ struct media_pad *pad; -+ unsigned int i; -+ int ret; -+ -+ source = media_entity_to_v4l2_subdev(source_entity); -+ if (!source) -+ return -EPIPE; -+ -+ pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); -+ if (!pad) -+ return -EPIPE; -+ -+ ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); -+ if (ret) -+ return ret; -+ -+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -+ dev_err(dev, "Unsupported frame descriptor type\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ if (source_stream == desc.entry[i].stream) { -+ desc_entry = &desc.entry[i]; -+ break; -+ } -+ } -+ -+ if (!desc_entry) { -+ dev_err(dev, "Failed to find stream %u from remote subdev\n", -+ source_stream); -+ return -EINVAL; -+ } -+ -+ if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) { -+ dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); -+ return -EINVAL; -+ } -+ -+ *entry = *desc_entry; -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) -+ (*nr_queues)++; -+ } -+ -+ return 0; -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -new file mode 100644 -index 000000000000..6c23b80f92a2 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -@@ -0,0 +1,64 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_CSI2_H -+#define IPU7_ISYS_CSI2_H -+ -+#include -+#include -+ -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-video.h" -+ -+struct ipu7_isys; -+struct ipu7_isys_csi2_pdata; -+struct ipu7_isys_stream; -+ -+#define IPU7_NR_OF_CSI2_VC 16U -+#define INVALID_VC_ID -1 -+#define IPU7_NR_OF_CSI2_SINK_PADS 1U -+#define IPU7_CSI2_PAD_SINK 0U -+#define IPU7_NR_OF_CSI2_SRC_PADS 8U -+#define IPU7_CSI2_PAD_SRC 1U -+#define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \ -+ IPU7_NR_OF_CSI2_SRC_PADS) -+ -+/* -+ * struct ipu7_isys_csi2 -+ * -+ * @nlanes: number of lanes in the receiver -+ */ -+struct ipu7_isys_csi2 { -+ struct ipu7_isys_subdev asd; -+ struct ipu7_isys_csi2_pdata *pdata; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS]; -+ -+ void __iomem *base; -+ u32 receiver_errors; -+ u32 legacy_irq_mask; -+ unsigned int nlanes; -+ unsigned int port; -+ unsigned int phy_mode; -+ unsigned int stream_count; -+}; -+ -+#define ipu7_isys_subdev_to_csi2(__sd) \ -+ container_of(__sd, struct ipu7_isys_csi2, asd) -+ -+#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd) -+ -+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2); -+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys, -+ void __iomem *base, unsigned int index); -+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2); -+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream); -+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream); -+int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -+ struct ipu7_isys_csi2 *csi2, -+ struct media_entity *source_entity, -+ struct v4l2_mbus_frame_desc_entry *entry, -+ int *nr_queues); -+#endif /* IPU7_ISYS_CSI2_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -new file mode 100644 -index 000000000000..9cee0fb4440c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -@@ -0,0 +1,1193 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-platform-regs.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include "ipu7-cpd.h" -+#endif -+ -+#define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) -+ -+static int ipu7_isys_buf_init(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ int ret; -+ -+ ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); -+ if (ret) -+ return ret; -+ -+ ivb->dma_addr = sg_dma_address(sg->sgl); -+ -+ return 0; -+} -+ -+static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ -+ ivb->dma_addr = 0; -+ ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); -+} -+ -+static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, -+ unsigned int *num_planes, unsigned int sizes[], -+ struct device *alloc_devs[]) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ u32 size = av->pix_fmt.sizeimage; -+ -+ /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ -+ if (!*num_planes) { -+ sizes[0] = size; -+ } else if (sizes[0] < size) { -+ dev_dbg(dev, "%s: queue setup: size %u < %u\n", -+ av->vdev.name, sizes[0], size); -+ return -EINVAL; -+ } -+ -+ *num_planes = 1; -+ -+ return 0; -+} -+ -+static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ u32 bytesperline = av->pix_fmt.bytesperline; -+ u32 height = av->pix_fmt.height; -+ -+ dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", -+ av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0)); -+ -+ if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0)) -+ return -EINVAL; -+ -+ dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n", -+ av->vdev.name, bytesperline, height); -+ vb2_set_plane_payload(vb, 0, bytesperline * height); -+ -+ return 0; -+} -+ -+/* -+ * Queue a buffer list back to incoming or active queues. The buffers -+ * are removed from the buffer list. -+ */ -+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -+ unsigned long op_flags, -+ enum vb2_buffer_state state) -+{ -+ struct ipu7_isys_buffer *ib, *ib_safe; -+ unsigned long flags; -+ bool first = true; -+ -+ if (!bl) -+ return; -+ -+ WARN_ON_ONCE(!bl->nbufs); -+ WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE && -+ op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING); -+ -+ list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { -+ struct ipu7_isys_video *av; -+ -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ struct ipu7_isys_queue *aq = -+ vb2_queue_to_isys_queue(vb->vb2_queue); -+ -+ av = ipu7_isys_queue_to_video(aq); -+ spin_lock_irqsave(&aq->lock, flags); -+ list_del(&ib->head); -+ if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) -+ list_add(&ib->head, &aq->active); -+ else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) -+ list_add_tail(&ib->head, &aq->incoming); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE) -+ vb2_buffer_done(vb, state); -+ -+ if (first) { -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "queue buf list %p flags %lx, s %d, %d bufs\n", -+ bl, op_flags, state, bl->nbufs); -+ first = false; -+ } -+ -+ bl->nbufs--; -+ } -+ -+ WARN_ON(bl->nbufs); -+} -+ -+/* -+ * flush_firmware_streamon_fail() - Flush in cases where requests may -+ * have been queued to firmware and the *firmware streamon fails for a -+ * reason or another. -+ */ -+static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_queue *aq; -+ unsigned long flags; -+ -+ lockdep_assert_held(&stream->mutex); -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer *ib, *ib_safe; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { -+ struct vb2_buffer *vb = -+ ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ if (av->streaming) { -+ dev_dbg(dev, -+ "%s: queue buffer %u back to incoming\n", -+ av->vdev.name, vb->index); -+ /* Queue already streaming, return to driver. */ -+ list_add(&ib->head, &aq->incoming); -+ continue; -+ } -+ /* Queue not yet streaming, return to user. */ -+ dev_dbg(dev, "%s: return %u back to videobuf2\n", -+ av->vdev.name, vb->index); -+ vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib), -+ VB2_BUF_STATE_QUEUED); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ } -+} -+ -+/* -+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers -+ * that contains one entry from each video buffer queue. If a buffer can't be -+ * obtained from every queue, the buffers are returned back to the queue. -+ */ -+static int buffer_list_get(struct ipu7_isys_stream *stream, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING; -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu7_isys_queue *aq; -+ unsigned long flags; -+ -+ bl->nbufs = 0; -+ INIT_LIST_HEAD(&bl->head); -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu7_isys_buffer *ib; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ if (list_empty(&aq->incoming)) { -+ spin_unlock_irqrestore(&aq->lock, flags); -+ if (!list_empty(&bl->head)) -+ ipu7_isys_buffer_list_queue(bl, buf_flag, 0); -+ return -ENODATA; -+ } -+ -+ ib = list_last_entry(&aq->incoming, -+ struct ipu7_isys_buffer, head); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ -+ if (av->skipframe) { -+ atomic_set(&ib->skipframe_flag, 1); -+ av->skipframe--; -+ } else { -+ atomic_set(&ib->skipframe_flag, 0); -+ } -+#endif -+ dev_dbg(dev, "buffer: %s: buffer %u\n", -+ ipu7_isys_queue_to_video(aq)->vdev.name, -+ ipu7_isys_buffer_to_vb2_buffer(ib)->index); -+ list_del(&ib->head); -+ list_add(&ib->head, &bl->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ bl->nbufs++; -+ } -+ -+ dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); -+ -+ return 0; -+} -+ -+static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, -+ struct ipu7_insys_buffset *set) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ -+#ifndef IPU8_INSYS_NEW_ABI -+ set->output_pins[aq->fw_output].addr = ivb->dma_addr; -+ set->output_pins[aq->fw_output].user_token = (uintptr_t)set; -+#else -+ set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; -+ set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; -+ set->output_pins[aq->fw_output].upipe_capture_cfg = 0; -+#endif -+} -+ -+/* -+ * Convert a buffer list to a isys fw ABI framebuffer set. The -+ * buffer list is not modified. -+ */ -+#define IPU_ISYS_FRAME_NUM_THRESHOLD (30) -+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -+ struct ipu7_isys_stream *stream, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ struct ipu7_isys_buffer *ib; -+ u32 buf_id; -+ -+ WARN_ON(!bl->nbufs); -+ -+ set->skip_frame = 0; -+ set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP | -+ IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ; -+ -+ buf_id = atomic_fetch_inc(&stream->buf_id); -+ set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER; -+ -+ list_for_each_entry(ib, &bl->head, head) { -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ ipu7_isys_buf_to_fw_frame_buf_pin(vb, set); -+ } -+} -+ -+/* Start streaming for real. The buffer list must be available. */ -+static int ipu7_isys_stream_start(struct ipu7_isys_video *av, -+ struct ipu7_isys_buffer_list *bl, bool error) -+{ -+ struct ipu7_isys_stream *stream = av->stream; -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer_list __bl; -+ int ret; -+ -+ mutex_lock(&stream->isys->stream_mutex); -+ -+ ret = ipu7_isys_video_set_streaming(av, 1, bl); -+ mutex_unlock(&stream->isys->stream_mutex); -+ if (ret) -+ goto out_requeue; -+ -+ stream->streaming = 1; -+ -+ bl = &__bl; -+ -+ do { -+ struct ipu7_insys_buffset *buf = NULL; -+ struct isys_fw_msgs *msg; -+ enum ipu7_insys_send_type send_type = -+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE; -+ -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) -+ break; -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) -+ return -ENOMEM; -+ -+ buf = &msg->fw_msg.frame; -+ -+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -+ -+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, -+ stream->nr_output_pins); -+ -+ ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, -+ 0); -+ -+ ret = ipu7_fw_isys_complex_cmd(stream->isys, -+ stream->stream_handle, buf, -+ msg->dma_addr, sizeof(*buf), -+ send_type); -+ } while (!WARN_ON(ret)); -+ -+ return 0; -+ -+out_requeue: -+ if (bl && bl->nbufs) -+ ipu7_isys_buffer_list_queue(bl, -+ IPU_ISYS_BUFFER_LIST_FL_INCOMING | -+ (error ? -+ IPU_ISYS_BUFFER_LIST_FL_SET_STATE : -+ 0), error ? VB2_BUF_STATE_ERROR : -+ VB2_BUF_STATE_QUEUED); -+ flush_firmware_streamon_fail(stream); -+ -+ return ret; -+} -+ -+static void buf_queue(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ struct media_pipeline *media_pipe = -+ media_entity_pipeline(&av->vdev.entity); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_buffer *ib = &ivb->ib; -+ struct ipu7_insys_buffset *buf = NULL; -+ struct ipu7_isys_buffer_list bl; -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ dma_addr_t dma; -+ int ret; -+ -+ dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); -+ -+ dma = ivb->dma_addr; -+ dev_dbg(dev, "iova: iova %pad\n", &dma); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ list_add(&ib->head, &aq->incoming); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (!media_pipe || !vb->vb2_queue->start_streaming_called) { -+ dev_dbg(dev, "media pipeline is not ready for %s\n", -+ av->vdev.name); -+ return; -+ } -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&av->isys->reset_mutex); -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ dev_dbg(dev, "in reset, adding to incoming\n"); -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ /* ip may be cleared in ipu reset */ -+ stream = av->stream; -+#endif -+ mutex_lock(&stream->mutex); -+ -+ if (stream->nr_streaming != stream->nr_queues) { -+ dev_dbg(dev, "not streaming yet, adding to incoming\n"); -+ goto out; -+ } -+ -+ /* -+ * We just put one buffer to the incoming list of this queue -+ * (above). Let's see whether all queues in the pipeline would -+ * have a buffer. -+ */ -+ ret = buffer_list_get(stream, &bl); -+ if (ret < 0) { -+ dev_dbg(dev, "No buffers available\n"); -+ goto out; -+ } -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ buf = &msg->fw_msg.frame; -+ -+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl); -+ -+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); -+ -+ if (!stream->streaming) { -+ ret = ipu7_isys_stream_start(av, &bl, true); -+ if (ret) -+ dev_err(dev, "stream start failed.\n"); -+ goto out; -+ } -+ -+ /* -+ * We must queue the buffers in the buffer list to the -+ * appropriate video buffer queues BEFORE passing them to the -+ * firmware since we could get a buffer event back before we -+ * have queued them ourselves to the active queue. -+ */ -+ ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -+ -+ ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle, -+ buf, msg->dma_addr, sizeof(*buf), -+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE); -+ if (ret < 0) -+ dev_err(dev, "send stream capture failed\n"); -+ -+out: -+ mutex_unlock(&stream->mutex); -+} -+ -+static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) -+{ -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *remote_pad = -+ media_pad_remote_pad_first(av->vdev.entity.pads); -+ struct v4l2_mbus_framefmt format; -+ struct v4l2_subdev *sd; -+ u32 r_stream, code; -+ int ret; -+ -+ if (!remote_pad) -+ return -ENOTCONN; -+ -+ sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index); -+ -+ ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, -+ &format); -+ if (ret) { -+ dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", -+ sd->entity.name, remote_pad->index, r_stream); -+ return ret; -+ } -+ -+ if (format.width != av->pix_fmt.width || -+ format.height != av->pix_fmt.height) { -+ dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", -+ av->pix_fmt.width, av->pix_fmt.height, format.width, -+ format.height); -+ return -EINVAL; -+ } -+ -+ code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -+ if (format.code != code) { -+ dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", -+ code, format.code); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void return_buffers(struct ipu7_isys_queue *aq, -+ enum vb2_buffer_state state) -+{ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ bool need_reset = false; -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+#endif -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ /* -+ * Something went wrong (FW crash / HW hang / not all buffers -+ * returned from isys) if there are still buffers queued in active -+ * queue. We have to clean up places a bit. -+ */ -+ while (!list_empty(&aq->active)) { -+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, state); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ need_reset = true; -+#endif -+ } -+ -+ while (!list_empty(&aq->incoming)) { -+ ib = list_last_entry(&aq->incoming, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, state); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ -+ spin_unlock_irqrestore(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ -+ if (need_reset) { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = true; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+#endif -+} -+ -+static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) -+{ -+ video_device_pipeline_stop(&av->vdev); -+ ipu7_isys_put_stream(av->stream); -+ av->stream = NULL; -+} -+ -+static int start_streaming(struct vb2_queue *q, unsigned int count) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ struct ipu7_isys_buffer_list __bl, *bl = NULL; -+ struct ipu7_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues, ret; -+ -+ dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", -+ av->vdev.name, av->pix_fmt.width, av->pix_fmt.height, -+ pfmt->css_pixelformat); -+ -+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_dbg(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu7_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_dbg(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu7_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) { -+ mutex_unlock(&stream->mutex); -+ goto out_pipeline_stop; -+ } -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) { -+ dev_warn(dev, "no buffer available, DRIVER BUG?\n"); -+ goto out; -+ } -+ -+ ret = ipu7_isys_fw_open(av->isys); -+ if (ret) -+ goto out_stream_start; -+ -+ ipu7_isys_setup_hw(av->isys); -+ -+ ret = ipu7_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_isys_fw_close; -+ -+out: -+ mutex_unlock(&stream->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->start_streaming = 1; -+#endif -+ -+ return 0; -+ -+out_isys_fw_close: -+ ipu7_isys_fw_close(av->isys); -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ mutex_unlock(&stream->mutex); -+ -+out_pipeline_stop: -+ ipu7_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static void reset_stop_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ mutex_lock(&stream->mutex); -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ while (!list_empty(&aq->active)) { -+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+ -+static int reset_start_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer_list __bl, *bl = NULL; -+ struct ipu7_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues; -+ int ret; -+ -+ dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); -+ -+ av->skipframe = 1; -+ -+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_dbg(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu7_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_dbg(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu7_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) { -+ mutex_unlock(&stream->mutex); -+ goto out_pipeline_stop; -+ } -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ ret = buffer_list_get(stream, bl); -+ /* -+ * In reset start streaming and no buffer available, -+ * it is considered that gstreamer has been closed, -+ * and reset start is no needed, not driver bug. -+ */ -+ if (ret) { -+ dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ goto out_stream_start; -+ } -+ -+ ret = ipu7_isys_fw_open(av->isys); -+ if (ret) -+ goto out_stream_start; -+ -+ ipu7_isys_setup_hw(av->isys); -+ -+ ret = ipu7_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_isys_fw_close; -+ -+out: -+ mutex_unlock(&stream->mutex); -+ av->start_streaming = 1; -+ return 0; -+ -+out_isys_fw_close: -+ ipu7_isys_fw_close(av->isys); -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ mutex_unlock(&stream->mutex); -+ -+out_pipeline_stop: -+ ipu7_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ av->start_streaming = 0; -+ dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); -+ return ret; -+} -+ -+static int ipu_isys_reset(struct ipu7_isys_video *self_av, -+ struct ipu7_isys_stream *self_stream) -+{ -+ struct ipu7_isys *isys = self_av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ struct ipu7_isys_video *av = NULL; -+ struct ipu7_isys_stream *stream = NULL; -+ struct device *dev = &adev->auxdev.dev; -+ int i, j; -+ int has_streaming = 0; -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&isys->reset_mutex); -+ return 0; -+ } -+ isys->state |= RESET_STATE_IN_RESET; -+ dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); -+ -+ while (isys->state & RESET_STATE_IN_STOP_STREAMING) { -+ dev_dbg(dev, "isys reset: %s: wait for stop\n", -+ self_av->vdev.name); -+ mutex_unlock(&isys->reset_mutex); -+ usleep_range(10000, 11000); -+ mutex_lock(&isys->reset_mutex); -+ } -+ -+ mutex_unlock(&isys->reset_mutex); -+ -+ dev_dbg(dev, "reset stop streams\n"); -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ av = &isys->csi2[i].av[j]; -+ if (av == self_av) -+ continue; -+ -+ stream = av->stream; -+ if (!stream || stream == self_stream) -+ continue; -+ -+ if (!stream->streaming && !stream->nr_streaming) -+ continue; -+ -+ av->reset = true; -+ has_streaming = true; -+ reset_stop_streaming(av); -+ } -+ } -+ -+ if (!has_streaming) -+ goto end_of_reset; -+ -+ ipu7_cleanup_fw_msg_bufs(isys); -+ -+ dev_dbg(dev, "reset start streams\n"); -+ -+ for (j = 0; j < csi2_pdata->nports; j++) { -+ for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { -+ av = &isys->csi2[j].av[i]; -+ if (!av->reset) -+ continue; -+ -+ av->reset = false; -+ reset_start_streaming(av); -+ } -+ } -+ -+end_of_reset: -+ mutex_lock(&isys->reset_mutex); -+ isys->state &= ~RESET_STATE_IN_RESET; -+ mutex_unlock(&isys->reset_mutex); -+ dev_dbg(dev, "reset done\n"); -+ -+ return 0; -+} -+ -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ bool need_reset; -+ -+ dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); -+ -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(dev, "stop: %s: wait for rest or stop, isys->state = %d\n", -+ av->vdev.name, av->isys->state); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ -+ if (!av->start_streaming) { -+ mutex_unlock(&av->isys->reset_mutex); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ return; -+ } -+ -+ av->isys->state |= RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ stream = av->stream; -+ if (!stream) { -+ dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } -+ -+ mutex_lock(&stream->mutex); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(dev, "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu7_isys_fw_close(av->isys); -+ -+ av->start_streaming = 0; -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ need_reset = av->isys->need_reset; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ if (need_reset) { -+ if (!stream->nr_streaming) { -+ ipu_isys_reset(av, stream); -+ } else { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = false; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+ } -+ -+ dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); -+} -+#else -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ -+ mutex_lock(&stream->mutex); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(&av->isys->adev->auxdev.dev, -+ "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+#endif -+ -+static unsigned int -+get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) -+{ -+ struct ipu7_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ -+ /* -+ * The timestamp is invalid as no TSC in some FPGA platform, -+ * so get the sequence from pipeline directly in this case. -+ */ -+ if (time == 0) -+ return atomic_read(&stream->sequence) - 1; -+ -+ for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -+ if (time == stream->seq[i].timestamp) { -+ dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n", -+ stream->seq[i].sequence, time); -+ return stream->seq[i].sequence; -+ } -+ -+ dev_dbg(dev, "SOF: looking for %llu\n", time); -+ for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -+ dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n", -+ stream->seq[i].sequence, stream->seq[i].timestamp); -+ dev_dbg(dev, "SOF sequence number not found\n"); -+ -+ return atomic_read(&stream->sequence) - 1; -+} -+ -+static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time) -+{ -+ struct ipu7_bus_device *adev = av->isys->adev; -+ struct ipu7_device *isp = adev->isp; -+ u64 delta, tsc_now; -+ -+ ipu_buttress_tsc_read(isp, &tsc_now); -+ if (!tsc_now) -+ return 0; -+ -+ delta = tsc_now - time; -+ -+ return ipu_buttress_tsc_ticks_to_ns(delta, isp); -+} -+ -+static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib, -+ u64 time) -+{ -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ u64 ns; -+ u32 sequence; -+ -+ ns = ktime_get_ns() - get_sof_ns_delta(av, time); -+ sequence = get_sof_sequence_by_timestamp(stream, time); -+ -+ vbuf->vb2_buf.timestamp = ns; -+ vbuf->sequence = sequence; -+ -+ dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", -+ av->vdev.name, ktime_get_ns(), sequence); -+ dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, -+ vbuf->vb2_buf.timestamp); -+} -+ -+static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) -+{ -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ if (atomic_read(&ib->str2mmio_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ /* -+ * Operation on buffer is ended with error and will be reported -+ * to the userspace when it is de-queued -+ */ -+ atomic_set(&ib->str2mmio_flag, 0); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ } else if (atomic_read(&ib->skipframe_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ atomic_set(&ib->skipframe_flag, 0); -+#endif -+ } else { -+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE); -+ } -+} -+ -+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -+ struct ipu7_insys_resp *info) -+{ -+ struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq; -+ u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]); -+ struct ipu7_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ bool first = true; -+ struct vb2_v4l2_buffer *buf; -+ -+ dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n", -+ ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr, -+ info->frame_id); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ if (list_empty(&aq->active)) { -+ spin_unlock_irqrestore(&aq->lock, flags); -+ dev_err(dev, "active queue empty\n"); -+ return; -+ } -+ -+ list_for_each_entry_reverse(ib, &aq->active, head) { -+ struct ipu7_isys_video_buffer *ivb; -+ struct vb2_v4l2_buffer *vvb; -+ dma_addr_t addr; -+ -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ vvb = to_vb2_v4l2_buffer(vb); -+ ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ addr = ivb->dma_addr; -+ -+ if (info->pin.addr != addr) { -+ if (first) -+ dev_err(dev, "Unexpected buffer address %pad\n", -+ &addr); -+ -+ first = false; -+ continue; -+ } -+ -+ dev_dbg(dev, "buffer: found buffer %pad\n", &addr); -+ -+ buf = to_vb2_v4l2_buffer(vb); -+ buf->field = V4L2_FIELD_NONE; -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu7_isys_buf_calc_sequence_time(ib, time); -+ -+ ipu7_isys_queue_buf_done(ib); -+ -+ return; -+ } -+ -+ dev_err(dev, "Failed to find a matching video buffer\n"); -+ -+ spin_unlock_irqrestore(&aq->lock, flags); -+} -+ -+static const struct vb2_ops ipu7_isys_queue_ops = { -+ .queue_setup = ipu7_isys_queue_setup, -+ .buf_init = ipu7_isys_buf_init, -+ .buf_prepare = ipu7_isys_buf_prepare, -+ .buf_cleanup = ipu7_isys_buf_cleanup, -+ .start_streaming = start_streaming, -+ .stop_streaming = stop_streaming, -+ .buf_queue = buf_queue, -+}; -+ -+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq) -+{ -+ struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys; -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_bus_device *adev = isys->adev; -+ int ret; -+ -+ if (!aq->vbq.io_modes) -+ aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; -+ -+ aq->vbq.drv_priv = isys; -+ aq->vbq.ops = &ipu7_isys_queue_ops; -+ aq->vbq.lock = &av->mutex; -+ aq->vbq.mem_ops = &vb2_dma_sg_memops; -+ aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ aq->vbq.min_queued_buffers = 1; -+ aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -+ -+ ret = vb2_queue_init(&aq->vbq); -+ if (ret) -+ return ret; -+ -+ aq->dev = &adev->auxdev.dev; -+ aq->vbq.dev = &adev->isp->pdev->dev; -+ spin_lock_init(&aq->lock); -+ INIT_LIST_HEAD(&aq->active); -+ INIT_LIST_HEAD(&aq->incoming); -+ -+ return 0; -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -new file mode 100644 -index 000000000000..5a909c3a78d2 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -@@ -0,0 +1,75 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_QUEUE_H -+#define IPU7_ISYS_QUEUE_H -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+struct device; -+struct ipu7_isys_stream; -+struct ipu7_insys_resp; -+struct ipu7_insys_buffset; -+ -+struct ipu7_isys_queue { -+ struct vb2_queue vbq; -+ struct list_head node; -+ struct device *dev; -+ spinlock_t lock; -+ struct list_head active; -+ struct list_head incoming; -+ unsigned int fw_output; -+}; -+ -+struct ipu7_isys_buffer { -+ struct list_head head; -+ atomic_t str2mmio_flag; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ atomic_t skipframe_flag; -+#endif -+}; -+ -+struct ipu7_isys_video_buffer { -+ struct vb2_v4l2_buffer vb_v4l2; -+ struct ipu7_isys_buffer ib; -+ dma_addr_t dma_addr; -+}; -+ -+#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) -+#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) -+#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) -+ -+struct ipu7_isys_buffer_list { -+ struct list_head head; -+ unsigned int nbufs; -+}; -+ -+#define vb2_queue_to_isys_queue(__vb2) \ -+ container_of(__vb2, struct ipu7_isys_queue, vbq) -+ -+#define ipu7_isys_to_isys_video_buffer(__ib) \ -+ container_of(__ib, struct ipu7_isys_video_buffer, ib) -+ -+#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb) \ -+ container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2) -+ -+#define ipu7_isys_buffer_to_vb2_buffer(__ib) \ -+ (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) -+ -+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -+ unsigned long op_flags, -+ enum vb2_buffer_state state); -+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -+ struct ipu7_isys_stream *stream, -+ struct ipu7_isys_buffer_list *bl); -+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -+ struct ipu7_insys_resp *info); -+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq); -+#endif /* IPU7_ISYS_QUEUE_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -new file mode 100644 -index 000000000000..98b6ef6a2f21 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -@@ -0,0 +1,348 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "ipu7-bus.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-subdev.h" -+ -+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return MIPI_CSI2_DT_RGB565; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return MIPI_CSI2_DT_RGB888; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return MIPI_CSI2_DT_YUV422_10B; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return MIPI_CSI2_DT_YUV422_8B; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return MIPI_CSI2_DT_RAW12; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return MIPI_CSI2_DT_RAW10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return MIPI_CSI2_DT_RAW8; -+ default: -+ WARN_ON(1); -+ return 0xff; -+ } -+} -+ -+bool ipu7_isys_is_bayer_format(u32 code) -+{ -+ switch (ipu7_isys_mbus_code_to_mipi(code)) { -+ case MIPI_CSI2_DT_RAW8: -+ case MIPI_CSI2_DT_RAW10: -+ case MIPI_CSI2_DT_RAW12: -+ case MIPI_CSI2_DT_RAW14: -+ case MIPI_CSI2_DT_RAW16: -+ case MIPI_CSI2_DT_RAW20: -+ case MIPI_CSI2_DT_RAW24: -+ case MIPI_CSI2_DT_RAW28: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y) -+{ -+ static const u32 code_map[] = { -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ }; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(code_map); i++) -+ if (code_map[i] == code) -+ break; -+ -+ if (WARN_ON(i == ARRAY_SIZE(code_map))) -+ return code; -+ -+ return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))]; -+} -+ -+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ u32 code = asd->supported_codes[0]; -+ struct v4l2_mbus_framefmt *fmt; -+ u32 other_pad, other_stream; -+ struct v4l2_rect *crop; -+ unsigned int i; -+ int ret; -+ -+ /* No transcoding, source and sink formats must match. */ -+ if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && -+ sd->entity.num_pads > 1) -+ return v4l2_subdev_get_fmt(sd, state, format); -+ -+ format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH, -+ IPU_ISYS_MAX_WIDTH); -+ format->format.height = clamp(format->format.height, -+ IPU_ISYS_MIN_HEIGHT, -+ IPU_ISYS_MAX_HEIGHT); -+ -+ for (i = 0; asd->supported_codes[i]; i++) { -+ if (asd->supported_codes[i] == format->format.code) { -+ code = asd->supported_codes[i]; -+ break; -+ } -+ } -+ format->format.code = code; -+ format->format.field = V4L2_FIELD_NONE; -+ -+ /* Store the format and propagate it to the source pad. */ -+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); -+ if (!fmt) -+ return -EINVAL; -+ -+ *fmt = format->format; -+ -+ if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) -+ return 0; -+ -+ /* propagate format to following source pad */ -+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, -+ format->stream); -+ if (!fmt) -+ return -EINVAL; -+ -+ *fmt = format->format; -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, -+ format->pad, -+ format->stream, -+ &other_pad, -+ &other_stream); -+ if (ret) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); -+ /* reset crop */ -+ crop->left = 0; -+ crop->top = 0; -+ crop->width = fmt->width; -+ crop->height = fmt->height; -+ -+ return 0; -+} -+ -+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ const u32 *supported_codes = asd->supported_codes; -+ u32 index; -+ -+ for (index = 0; supported_codes[index]; index++) { -+ if (index == code->index) { -+ code->code = supported_codes[index]; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_krouting *routing) -+{ -+ static const struct v4l2_mbus_framefmt fmt = { -+ .width = 4096, -+ .height = 3072, -+ .code = MEDIA_BUS_FMT_SGRBG10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ int ret; -+ -+ ret = v4l2_subdev_routing_validate(sd, routing, -+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); -+ if (ret) -+ return ret; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); -+} -+ -+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_mbus_framefmt *format) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_mbus_framefmt *fmt; -+ -+ if (!sd || !format) -+ return -EINVAL; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ fmt = v4l2_subdev_state_get_format(state, pad, stream); -+ if (fmt) -+ *format = *fmt; -+ v4l2_subdev_unlock_state(state); -+ -+ return fmt ? 0 : -EINVAL; -+} -+ -+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev_route *routes; -+ u32 source_stream = 0; -+ unsigned int i; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ if (!state) -+ return 0; -+ -+ routes = state->routing.routes; -+ for (i = 0; i < state->routing.num_routes; i++) { -+ if (routes[i].source_pad == pad) { -+ source_stream = routes[i].source_stream; -+ break; -+ } -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ return source_stream; -+} -+ -+static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route route = { -+ .sink_pad = 0, -+ .sink_stream = 0, -+ .source_pad = 1, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }; -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = &route, -+ }; -+ -+ return subdev_set_routing(sd, state, &routing); -+} -+ -+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing) -+{ -+ return subdev_set_routing(sd, state, routing); -+} -+ -+static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { -+ .init_state = ipu7_isys_subdev_init_state, -+}; -+ -+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -+ const struct v4l2_subdev_ops *ops, -+ unsigned int nr_ctrls, -+ unsigned int num_sink_pads, -+ unsigned int num_source_pads) -+{ -+ unsigned int num_pads = num_sink_pads + num_source_pads; -+ unsigned int i; -+ int ret; -+ -+ v4l2_subdev_init(&asd->sd, ops); -+ -+ asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -+ V4L2_SUBDEV_FL_HAS_EVENTS | -+ V4L2_SUBDEV_FL_STREAMS; -+ asd->sd.owner = THIS_MODULE; -+ asd->sd.dev = &asd->isys->adev->auxdev.dev; -+ asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -+ asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; -+ -+ asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, -+ sizeof(*asd->pad), GFP_KERNEL); -+ if (!asd->pad) -+ return -ENOMEM; -+ -+ for (i = 0; i < num_sink_pads; i++) -+ asd->pad[i].flags = MEDIA_PAD_FL_SINK | -+ MEDIA_PAD_FL_MUST_CONNECT; -+ -+ for (i = num_sink_pads; i < num_pads; i++) -+ asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; -+ -+ ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); -+ if (ret) { -+ pr_err("isys subdev init failed %d.\n", ret); -+ return ret; -+ } -+ -+ if (asd->ctrl_init) { -+ ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); -+ if (ret) -+ goto out_media_entity_cleanup; -+ -+ asd->ctrl_init(&asd->sd); -+ if (asd->ctrl_handler.error) { -+ ret = asd->ctrl_handler.error; -+ goto out_v4l2_ctrl_handler_free; -+ } -+ -+ asd->sd.ctrl_handler = &asd->ctrl_handler; -+ } -+ -+ asd->source = -1; -+ -+ return 0; -+ -+out_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(&asd->ctrl_handler); -+ -+out_media_entity_cleanup: -+ media_entity_cleanup(&asd->sd.entity); -+ -+ return ret; -+} -+ -+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) -+{ -+ media_entity_cleanup(&asd->sd.entity); -+ v4l2_ctrl_handler_free(&asd->ctrl_handler); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -new file mode 100644 -index 000000000000..aeefbb515807 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -@@ -0,0 +1,56 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_SUBDEV_H -+#define IPU7_ISYS_SUBDEV_H -+ -+#include -+ -+#include -+#include -+#include -+ -+struct ipu7_isys; -+ -+struct ipu7_isys_subdev { -+ struct v4l2_subdev sd; -+ struct ipu7_isys *isys; -+ u32 const *supported_codes; -+ struct media_pad *pad; -+ struct v4l2_ctrl_handler ctrl_handler; -+ void (*ctrl_init)(struct v4l2_subdev *sd); -+ int source; /* SSI stream source; -1 if unset */ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ bool is_tpg; -+#endif -+}; -+ -+#define to_ipu7_isys_subdev(__sd) \ -+ container_of(__sd, struct ipu7_isys_subdev, sd) -+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code); -+bool ipu7_isys_is_bayer_format(u32 code); -+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y); -+ -+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format); -+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum -+ *code); -+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); -+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_mbus_framefmt *format); -+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing); -+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -+ const struct v4l2_subdev_ops *ops, -+ unsigned int nr_ctrls, -+ unsigned int num_sink_pads, -+ unsigned int num_source_pads); -+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd); -+#endif /* IPU7_ISYS_SUBDEV_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -new file mode 100644 -index 000000000000..35b6298e4f8c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -@@ -0,0 +1,693 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-tpg.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-platform-regs.h" -+ -+static const u32 tpg_supported_codes[] = { -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ 0, -+}; -+ -+#define IPU_ISYS_FREQ 533000000UL -+ -+static u32 isys_mbus_code_to_bpp(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return 24; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return 20; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return 16; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return 12; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return 10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return 8; -+ default: -+ WARN_ON(1); -+ return 0; -+ } -+} -+ -+static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { -+ .s_stream = tpg_set_stream, -+}; -+ -+static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, -+ struct -+ ipu7_isys_subdev, -+ ctrl_handler), -+ struct ipu7_isys_tpg, asd); -+ switch (ctrl->id) { -+ case V4L2_CID_HBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_HBLANK); -+ break; -+ case V4L2_CID_VBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_VBLANK); -+ break; -+ case V4L2_CID_TEST_PATTERN: -+ writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); -+ break; -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { -+ .s_ctrl = ipu7_isys_tpg_s_ctrl, -+}; -+ -+static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) -+{ -+ return MGC_PPC * IPU_ISYS_FREQ / bpp; -+} -+ -+static const char *const tpg_mode_items[] = { -+ "Ramp", -+ "Checkerboard", -+ "Monochrome per frame", -+ "Color palette", -+}; -+ -+static struct v4l2_ctrl_config tpg_mode = { -+ .ops = &ipu7_isys_tpg_ctrl_ops, -+ .id = V4L2_CID_TEST_PATTERN, -+ .name = "Test Pattern", -+ .type = V4L2_CTRL_TYPE_MENU, -+ .min = TPG_MODE_RAMP, -+ .max = ARRAY_SIZE(tpg_mode_items) - 1, -+ .def = TPG_MODE_COLOR_PALETTE, -+ .menu_skip_mask = 0x2, -+ .qmenu = tpg_mode_items, -+}; -+ -+static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ int hblank; -+ u64 default_pixel_rate; -+ -+ hblank = 1024; -+ -+ tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_HBLANK, 8, 65535, 1, hblank); -+ -+ tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_VBLANK, 8, 65535, 1, 1024); -+ -+ default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); -+ tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_PIXEL_RATE, -+ default_pixel_rate, -+ default_pixel_rate, -+ 1, default_pixel_rate); -+ if (tpg->pixel_rate) { -+ tpg->pixel_rate->cur.val = default_pixel_rate; -+ tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ } -+ -+ v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); -+} -+ -+static int tpg_sd_init_cfg(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route routes[] = { -+ { -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ } -+ }; -+ -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = routes, -+ }; -+ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_SBGGR10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); -+} -+ -+static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { -+ .init_state = tpg_sd_init_cfg, -+}; -+ -+static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu7_isys_subdev_set_fmt, -+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -+}; -+ -+static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+}; -+ -+/* V4L2 subdev core operations */ -+static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { -+ .subscribe_event = subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static const struct v4l2_subdev_ops tpg_sd_ops = { -+ .core = &tpg_sd_core_ops, -+ .video = &tpg_sd_video_ops, -+ .pad = &tpg_sd_pad_ops, -+}; -+ -+static struct media_entity_operations tpg_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ struct video_device *vdev = tpg->asd.sd.devnode; -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "sof_event::tpg-%i sequence: %i\n", tpg->index, -+ ev.u.frame_sync.frame_sequence); -+} -+ -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "eof_event::tpg-%i sequence: %i\n", -+ tpg->index, frame_sequence); -+} -+ -+#define DEFAULT_VC_ID 0 -+static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) -+{ -+ return false; -+} -+ -+static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, -+ void __iomem *mg_base) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "---------MGC REG DUMP START----------"); -+ -+ dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", -+ MGC_MG_CSI_ADAPT_LAYER_TYPE, -+ readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); -+ dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); -+ dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); -+ dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); -+ dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MULTI_DTYPES_MODE, -+ readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); -+ dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", -+ MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); -+ dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", -+ MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); -+ dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", -+ MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); -+ dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", -+ MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); -+ dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); -+ dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", -+ readl(mg_base + MGC_MG_TPG_R0), -+ readl(mg_base + MGC_MG_TPG_G0), -+ readl(mg_base + MGC_MG_TPG_B0)); -+ dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", -+ readl(mg_base + MGC_MG_TPG_R1), -+ readl(mg_base + MGC_MG_TPG_G1), -+ readl(mg_base + MGC_MG_TPG_B1)); -+ dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); -+ dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", -+ MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); -+ dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", -+ MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_EN, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_INCR_VAL, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); -+ dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", -+ MGC_MG_FRAME_NUM_STTS, -+ readl(mg_base + MGC_MG_FRAME_NUM_STTS)); -+ -+ dev_dbg(dev, "---------MGC REG DUMP END----------"); -+} -+ -+#define TPG_STOP_TIMEOUT 500000 -+static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ unsigned int port; -+ u32 status; -+ void __iomem *reg; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ -+ port = 1 << tpg->index; -+ -+ dev_dbg(dev, "MG%d generated %u frames", tpg->index, -+ readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); -+ writel(port, mgc_base + MGC_ASYNC_STOP); -+ -+ dev_dbg(dev, "wait for MG%d stop", tpg->index); -+ -+ reg = mg_base + MGC_MG_STOPPED_STTS; -+ ret = readl_poll_timeout(reg, status, status & 0x1, 200, -+ TPG_STOP_TIMEOUT); -+ if (ret < 0) { -+ dev_err(dev, "mgc stop timeout"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "MG%d STOPPED", tpg->index); -+ -+ return 0; -+} -+ -+#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) -+#define TPG_FRAME_RATE 30 -+#define TPG_BLANK_RATIO (4 / 3) -+static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, -+ u32 *hblank_cycles, u32 *vblank_cycles) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 width, height; -+ u32 code; -+ u32 bpp; -+ u32 bits_per_line; -+ u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; -+ u64 vblank_us, hblank_us; -+ u32 ref_clk; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ u32 dto_incr_val = 0x100; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return; -+ -+ dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); -+ bits_per_line = width * bpp * TPG_BLANK_RATIO; -+ -+ cycles = div_u64(bits_per_line, 64); -+ dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, -+ bits_per_line, cycles); -+ -+ do { -+ dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, -+ dto_incr_val); -+ rate = div_u64(1 << 16, dto_incr_val); -+ ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); -+ dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, -+ ns_per_cycle); -+ -+ line_time_ns = cycles * ns_per_cycle; -+ frame_time_us = line_time_ns * height / 1000; -+ dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", -+ tpg->index, line_time_ns, frame_time_us); -+ -+ if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) -+ break; -+ -+ /* dto incr val step 0x100 */ -+ dto_incr_val += 0x100; -+ } while (dto_incr_val < (1 << 16)); -+ -+ if (dto_incr_val >= (1 << 16)) { -+ dev_warn(dev, "No DTO_INCR_VAL found\n"); -+ hblank_us = 10; /* 10us */ -+ vblank_us = 10000; /* 10ms */ -+ dto_incr_val = 0x1000; -+ } else { -+ hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; -+ vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; -+ } -+ -+ dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", -+ hblank_us, vblank_us, dto_incr_val); -+ -+ ref_clk = tpg->isys->adev->isp->buttress.ref_clk; -+ -+ *dto = dto_incr_val; -+ *hblank_cycles = hblank_us * ref_clk / 10; -+ *vblank_cycles = vblank_us * ref_clk / 10; -+ dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", -+ *hblank_cycles, *vblank_cycles); -+} -+ -+static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 port_map; -+ u32 csi_port; -+ u32 code, bpp; -+ u32 width, height; -+ u32 dto, hblank, vblank; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return ret; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", -+ tpg->index, code, width, height); -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return -EINVAL; -+ -+ csi_port = tpg->index; -+ if (csi_port >= 4) -+ dev_err(dev, "invalid tpg index %u\n", tpg->index); -+ -+ dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", -+ DEFAULT_VC_ID, csi_port); -+ -+ /* config port map -+ * TODO: add VC support and TPG with multiple -+ * source pads. Currently, for simplicity, only map 1 mg to 1 csi port -+ */ -+ port_map = 1 << tpg->index; -+ writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); -+ -+ /* configure adapt layer type */ -+ writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); -+ -+ /* configure MGC mode -+ * 0 - Disable MGC -+ * 1 - Enable PRBS -+ * 2 - Enable TPG -+ * 3 - Reserved [Write phase: SW/FW debug] -+ */ -+ writel(2, mg_base + MGC_MG_MODE); -+ -+ /* config mg init counter */ -+ writel(0, mg_base + MGC_MG_INIT_COUNTER); -+ -+ /* -+ * configure virtual channel -+ * TODO: VC support if need -+ * currently each MGC just uses 1 virtual channel -+ */ -+ writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); -+ -+ /* -+ * configure data type and multi dtypes mode -+ * TODO: it needs to add the metedata flow. -+ */ -+ if (is_metadata_enabled(tpg)) { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } else { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } -+ -+ /* -+ * configure frame information -+ */ -+ writel(0, mg_base + MGC_MG_NOF_FRAMES); -+ writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); -+ -+ tpg_get_timing(tpg, &dto, &hblank, &vblank); -+ writel(hblank, mg_base + MGC_MG_HBLANK); -+ writel(vblank, mg_base + MGC_MG_VBLANK); -+ -+ /* -+ * configure tpg mode, colors, mask, tile dimension -+ * Mode was set by user configuration -+ * 0 - Ramp mode -+ * 1 - Checkerboard -+ * 2 - Monochrome per frame -+ * 3 - Color palette -+ */ -+ writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); -+ -+ /* red and green for checkerboard, n/a for other modes */ -+ writel(58, mg_base + MGC_MG_TPG_R0); -+ writel(122, mg_base + MGC_MG_TPG_G0); -+ writel(46, mg_base + MGC_MG_TPG_B0); -+ writel(123, mg_base + MGC_MG_TPG_R1); -+ writel(85, mg_base + MGC_MG_TPG_G1); -+ writel(67, mg_base + MGC_MG_TPG_B1); -+ -+ writel(0x0, mg_base + MGC_MG_TPG_FACTORS); -+ -+ /* hor_mask [15:0] ver_mask [31:16] */ -+ writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); -+ /* xy_mask [11:0] */ -+ writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); -+ writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), -+ mg_base + MGC_MG_TPG_TILE_DIM); -+ -+ writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); -+ writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); -+ -+ /* disable err_injection */ -+ writel(0, mg_base + MGC_MG_ERR_INJECT); -+ writel(0, mg_base + MGC_MG_ERR_LOCATION); -+ -+ ipu7_mipigen_regdump(tpg, mg_base); -+ -+ dev_dbg(dev, "starting MG%d streaming...\n", csi_port); -+ -+ /* kick and start */ -+ writel(port_map, mgc_base + MGC_KICK); -+ -+ return 0; -+} -+ -+static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct ipu7_isys_csi2 *csi2; -+ u32 offset; -+ struct ipu7_isys *isys = tpg->isys; -+ -+ csi2 = &isys->csi2[tpg->index]; -+ offset = IS_IO_GPREGS_BASE; -+ -+ /* MGC is in use by SW or not */ -+ if (enable) -+ writel(1, csi2->base + offset + MGC_CLK_GATE); -+ else -+ writel(0, csi2->base + offset + MGC_CLK_GATE); -+} -+ -+static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ struct ipu7_isys *isys = tpg->isys; -+ struct ipu7_isys_csi2 *csi2; -+ u32 port, offset, val; -+ void __iomem *isys_base = isys->pdata->base; -+ -+ port = tpg->index; -+ csi2 = &isys->csi2[port]; -+ -+ offset = IS_IO_GPREGS_BASE; -+ val = readl(isys_base + offset + CSI_PORT_CLK_GATE); -+ dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); -+ -+ if (!enable) { -+ writel(~(1 << port) & val, -+ isys_base + offset + CSI_PORT_CLK_GATE); -+ return; -+ } -+ -+ /* set csi port is using by SW */ -+ writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); -+ /* input is coming from MGC */ -+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -+ writel(CSI_MIPIGEN_INPUT, -+ csi2->base + offset + CSI2_ADPL_INPUT_MODE); -+} -+ -+/* TODO: add the processing of vc */ -+int tpg_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ struct ipu7_isys_stream *stream = tpg->av->stream; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ -+ if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { -+ dev_err(dev, "invalid MGC index %d\n", tpg->index); -+ return -EINVAL; -+ } -+ -+ if (!enable) { -+ /* Stop MGC */ -+ stream->asd->is_tpg = false; -+ stream->asd = NULL; -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ ret = tpg_stop_stream(tpg); -+ ipu7_isys_ungate_mgc(tpg, enable); -+ -+ return ret; -+ } -+ -+ stream->asd = &tpg->asd; -+ /* ungate the MGC clock to program */ -+ ipu7_isys_ungate_mgc(tpg, enable); -+ /* Start MGC */ -+ ret = tpg_start_stream(tpg); -+ v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ -+ return ret; -+} -+ -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) -+{ -+ v4l2_device_unregister_subdev(&tpg->asd.sd); -+ ipu7_isys_subdev_cleanup(&tpg->asd); -+} -+ -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ tpg->isys = isys; -+ tpg->base = base; -+ tpg->sel = sel; -+ tpg->index = index; -+ -+ tpg->asd.sd.entity.ops = &tpg_entity_ops; -+ tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; -+ tpg->asd.isys = isys; -+ -+ ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, -+ NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); -+ if (ret) -+ return ret; -+ -+ tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; -+ tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -+ -+ tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; -+ tpg->asd.supported_codes = tpg_supported_codes; -+ tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; -+ -+ snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), -+ IPU_ISYS_ENTITY_PREFIX " TPG %u", index); -+ v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); -+ -+ ret = v4l2_subdev_init_finalize(&tpg->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to finalize subdev (%d)\n", ret); -+ goto fail; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); -+ if (ret) { -+ dev_info(dev, "can't register v4l2 subdev\n"); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ ipu7_isys_tpg_cleanup(tpg); -+ -+ return ret; -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -new file mode 100644 -index 000000000000..e2542a6472e3 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -@@ -0,0 +1,70 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_TPG_H -+#define IPU7_ISYS_TPG_H -+ -+#include -+#include -+#include -+ -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-queue.h" -+ -+struct ipu7_isys_tpg_pdata; -+struct ipu7_isys; -+ -+#define TPG_PAD_SOURCE 0 -+#define NR_OF_TPG_PADS 1 -+#define NR_OF_TPG_SOURCE_PADS 1 -+#define NR_OF_TPG_SINK_PADS 0 -+#define NR_OF_TPG_STREAMS 1 -+ -+enum isys_tpg_mode { -+ TPG_MODE_RAMP = 0, -+ TPG_MODE_CHECKERBOARD = 1, -+ TPG_MODE_MONO = 2, -+ TPG_MODE_COLOR_PALETTE = 3, -+}; -+ -+/* -+ * struct ipu7_isys_tpg -+ * -+ * @nlanes: number of lanes in the receiver -+ */ -+struct ipu7_isys_tpg { -+ struct ipu7_isys_subdev asd; -+ struct ipu7_isys_tpg_pdata *pdata; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_video *av; -+ -+ /* MG base not MGC */ -+ void __iomem *base; -+ void __iomem *sel; -+ unsigned int index; -+ -+ struct v4l2_ctrl *hblank; -+ struct v4l2_ctrl *vblank; -+ struct v4l2_ctrl *pixel_rate; -+}; -+ -+#define ipu7_isys_subdev_to_tpg(__sd) \ -+ container_of(__sd, struct ipu7_isys_tpg, asd) -+ -+#define to_ipu7_isys_tpg(sd) \ -+ container_of(to_ipu7_isys_subdev(sd), \ -+ struct ipu7_isys_tpg, asd) -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, -+ struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index); -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); -+int tpg_set_stream(struct v4l2_subdev *sd, int enable); -+ -+#endif /* IPU7_ISYS_TPG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -new file mode 100644 -index 000000000000..12fdf556c656 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -@@ -0,0 +1,1311 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-platform-regs.h" -+ -+const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { -+ {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -+ IPU_INSYS_FRAME_FORMAT_UYVY}, -+ {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -+ IPU_INSYS_FRAME_FORMAT_YUYV}, -+ {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -+ IPU_INSYS_FRAME_FORMAT_RGB565}, -+ {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -+ IPU_INSYS_FRAME_FORMAT_RGBA888}, -+}; -+ -+static int video_open(struct file *file) -+{ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = video_drvdata(file); -+ struct ipu7_isys *isys = av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -+ return -EIO; -+ } -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif -+ return v4l2_fh_open(file); -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static int video_release(struct file *file) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: enter\n", av->vdev.name); -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: wait for reset\n", av->vdev.name); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ return vb2_fop_release(file); -+} -+ -+#endif -+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -+ const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i]; -+ -+ if (pfmt->pixelformat == pixelformat) -+ return pfmt; -+ } -+ -+ return &ipu7_isys_pfmts[0]; -+} -+ -+static int ipu7_isys_vidioc_querycap(struct file *file, void *fh, -+ struct v4l2_capability *cap) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); -+ strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); -+ -+ return 0; -+} -+ -+static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, -+ struct v4l2_fmtdesc *f) -+{ -+ unsigned int i, num_found; -+ -+ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ continue; -+ -+ if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code) -+ continue; -+ -+ if (num_found < f->index) { -+ num_found++; -+ continue; -+ } -+ -+ f->flags = 0; -+ f->pixelformat = ipu7_isys_pfmts[i].pixelformat; -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ unsigned int i; -+ -+ if (fsize->index > 0) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -+ if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) -+ continue; -+ -+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; -+ fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH; -+ fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH; -+ fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT; -+ fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT; -+ fsize->stepwise.step_width = 2; -+ fsize->stepwise.step_height = 2; -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ f->fmt.pix = av->pix_fmt; -+ -+ return 0; -+} -+ -+static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type, -+ u32 *format, u32 *width, u32 *height, -+ u32 *bytesperline, u32 *sizeimage) -+{ -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(*format); -+ -+ *format = pfmt->pixelformat; -+ *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH); -+ *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); -+ -+ if (pfmt->bpp != pfmt->bpp_packed) -+ *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); -+ else -+ *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); -+ -+ *bytesperline = ALIGN(*bytesperline, 64U); -+ -+ /* -+ * (height + 1) * bytesperline due to a hardware issue: the DMA unit -+ * is a power of two, and a line should be transferred as few units -+ * as possible. The result is that up to line length more data than -+ * the image size may be transferred to memory after the image. -+ * Another limitation is the GDA allocation unit size. For low -+ * resolution it gives a bigger number. Use larger one to avoid -+ * memory corruption. -+ */ -+ *sizeimage = *bytesperline * *height + -+ max(*bytesperline, -+ av->isys->pdata->ipdata->isys_dma_overshoot); -+} -+ -+static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, -+ struct v4l2_format *f) -+{ -+ ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, -+ &f->fmt.pix.width, &f->fmt.pix.height, -+ &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); -+ -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; -+ f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -+ f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; -+ f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; -+} -+ -+static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ if (vb2_is_busy(&av->aq.vbq)) -+ return -EBUSY; -+ -+ __ipu_isys_vidioc_try_fmt_vid_cap(av, f); -+ -+ return 0; -+} -+ -+static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); -+ av->pix_fmt = f->fmt.pix; -+ -+ return 0; -+} -+ -+static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv, -+ struct v4l2_requestbuffers *p) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ int ret; -+ -+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); -+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); -+ -+ ret = vb2_queue_change_type(&av->aq.vbq, p->type); -+ if (ret) -+ return ret; -+ -+ return vb2_ioctl_reqbufs(file, priv, p); -+} -+ -+static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv, -+ struct v4l2_create_buffers *p) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ int ret; -+ -+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); -+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); -+ -+ ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); -+ if (ret) -+ return ret; -+ -+ return vb2_ioctl_create_bufs(file, priv, p); -+} -+ -+static int link_validate(struct media_link *link) -+{ -+ struct ipu7_isys_video *av = -+ container_of(link->sink, struct ipu7_isys_video, pad); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct v4l2_subdev_state *s_state; -+ struct v4l2_mbus_framefmt *s_fmt; -+ struct v4l2_subdev *s_sd; -+ struct media_pad *s_pad; -+ u32 s_stream, code; -+ int ret = -EPIPE; -+ -+ if (!link->source->entity) -+ return ret; -+ -+ s_sd = media_entity_to_v4l2_subdev(link->source->entity); -+ s_state = v4l2_subdev_get_unlocked_active_state(s_sd); -+ if (!s_state) -+ return ret; -+ -+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", -+ link->source->entity->name, link->source->index, -+ link->sink->entity->name); -+ -+ s_pad = media_pad_remote_pad_first(&av->pad); -+ s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); -+ -+ v4l2_subdev_lock_state(s_state); -+ -+ s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); -+ if (!s_fmt) { -+ dev_err(dev, "failed to get source pad format\n"); -+ goto unlock; -+ } -+ -+ code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -+ -+ if (s_fmt->width != av->pix_fmt.width || -+ s_fmt->height != av->pix_fmt.height || s_fmt->code != code) { -+ dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", -+ s_fmt->width, s_fmt->height, s_fmt->code, -+ av->pix_fmt.width, av->pix_fmt.height, code); -+ goto unlock; -+ } -+ -+ v4l2_subdev_unlock_state(s_state); -+ -+ return 0; -+unlock: -+ v4l2_subdev_unlock_state(s_state); -+ -+ return ret; -+} -+ -+static void get_stream_opened(struct ipu7_isys_video *av) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&av->isys->streams_lock, flags); -+ av->isys->stream_opened++; -+ spin_unlock_irqrestore(&av->isys->streams_lock, flags); -+} -+ -+static void put_stream_opened(struct ipu7_isys_video *av) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&av->isys->streams_lock, flags); -+ av->isys->stream_opened--; -+ spin_unlock_irqrestore(&av->isys->streams_lock, flags); -+} -+ -+static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, -+ struct ipu7_insys_stream_cfg *cfg) -+{ -+ struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); -+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); -+ struct ipu7_isys_stream *stream = av->stream; -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ struct ipu7_insys_output_pin *output_pin; -+ struct ipu7_insys_input_pin *input_pin; -+ int input_pins = cfg->nof_input_pins++; -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct ipu7_isys *isys = av->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct v4l2_mbus_framefmt fmt; -+ int output_pins; -+ u32 src_stream; -+ int ret; -+ -+ src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index); -+ ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, -+ &fmt); -+ if (ret < 0) { -+ dev_err(dev, "can't get stream format (%d)\n", ret); -+ return ret; -+ } -+ -+ input_pin = &cfg->input_pins[input_pins]; -+ input_pin->input_res.width = fmt.width; -+ input_pin->input_res.height = fmt.height; -+ input_pin->dt = av->dt; -+ input_pin->disable_mipi_unpacking = 0; -+ pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) -+ input_pin->disable_mipi_unpacking = 1; -+ input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE; -+ input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME; -+ /* if enable polling isys interrupt, the follow values maybe set */ -+ input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; -+ -+ output_pins = cfg->nof_output_pins++; -+ aq->fw_output = output_pins; -+ stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready; -+ stream->output_pins[output_pins].aq = aq; -+ -+ output_pin = &cfg->output_pins[output_pins]; -+ /* output pin msg link */ -+ output_pin->link.buffer_lines = 0; -+ output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE; -+ output_pin->link.granularity_pointer_update = 0; -+ output_pin->link.msg_link_streaming_mode = -+ IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF; -+ -+ output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE; -+ output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE; -+ output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM; -+ output_pin->link.use_sw_managed = 1; -+ /* TODO: set the snoop bit for metadata capture */ -+ output_pin->link.is_snoop = 0; -+ -+ /* output pin crop */ -+ output_pin->crop.line_top = 0; -+ output_pin->crop.line_bottom = 0; -+#ifdef IPU8_INSYS_NEW_ABI -+ output_pin->crop.column_left = 0; -+ output_pin->crop.column_right = 0; -+#endif -+ -+ /* output de-compression */ -+ output_pin->dpcm.enable = 0; -+ -+#ifdef IPU8_INSYS_NEW_ABI -+ /* upipe_cfg */ -+ output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; -+ output_pin->upipe_pin_cfg.plane_offset_1 = 0; -+ output_pin->upipe_pin_cfg.plane_offset_2 = 0; -+ output_pin->upipe_pin_cfg.single_uob_fifo = 0; -+ output_pin->upipe_pin_cfg.shared_uob_fifo = 0; -+ output_pin->upipe_enable = 0; -+ output_pin->binning_factor = 0; -+ /* stupid setting, even unused, SW still need to set a valid value */ -+ output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; -+#endif -+ -+ /* frame format type */ -+ pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ output_pin->ft = (u16)pfmt->css_pixelformat; -+ -+ /* stride in bytes */ -+ output_pin->stride = av->pix_fmt.bytesperline; -+ output_pin->send_irq = 1; -+ output_pin->early_ack_en = 0; -+ -+ /* input pin id */ -+ output_pin->input_pin_id = input_pins; -+ -+ return 0; -+} -+ -+/* Create stream and start it using the CSS FW ABI. */ -+static int start_stream_firmware(struct ipu7_isys_video *av, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_insys_stream_cfg *stream_cfg; -+ struct ipu7_insys_buffset *buf = NULL; -+ struct isys_fw_msgs *msg = NULL; -+ struct ipu7_isys_queue *aq; -+ int ret, retout, tout; -+ u16 send_type; -+ -+ if (WARN_ON(!bl)) -+ return -EIO; -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) -+ return -ENOMEM; -+ -+ stream_cfg = &msg->fw_msg.stream; -+ stream_cfg->port_id = stream->stream_source; -+ stream_cfg->vc = stream->vc; -+ stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP | -+ IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq); -+ -+ ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg); -+ if (ret < 0) { -+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -+ return ret; -+ } -+ } -+ -+ ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg); -+ -+ stream->nr_output_pins = stream_cfg->nof_output_pins; -+ -+ reinit_completion(&stream->stream_open_completion); -+ -+ ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, -+ stream_cfg, msg->dma_addr, -+ sizeof(*stream_cfg), -+ IPU_INSYS_SEND_TYPE_STREAM_OPEN); -+ if (ret < 0) { -+ dev_err(dev, "can't open stream (%d)\n", ret); -+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -+ return ret; -+ } -+ -+ get_stream_opened(av); -+ -+ tout = wait_for_completion_timeout(&stream->stream_open_completion, -+ FW_CALL_TIMEOUT_JIFFIES); -+ -+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -+ -+ if (!tout) { -+ dev_err(dev, "stream open time out\n"); -+ ret = -ETIMEDOUT; -+ goto out_put_stream_opened; -+ } -+ if (stream->error) { -+ dev_err(dev, "stream open error: %d\n", stream->error); -+ ret = -EIO; -+ goto out_put_stream_opened; -+ } -+ dev_dbg(dev, "start stream: open complete\n"); -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) { -+ ret = -ENOMEM; -+ goto out_put_stream_opened; -+ } -+ buf = &msg->fw_msg.frame; -+ -+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -+ ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -+ -+ reinit_completion(&stream->stream_start_completion); -+ -+ send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE; -+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, -+ stream_cfg->nof_output_pins); -+ ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf, -+ msg->dma_addr, sizeof(*buf), -+ send_type); -+ if (ret < 0) { -+ dev_err(dev, "can't start streaming (%d)\n", ret); -+ goto out_stream_close; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_start_completion, -+ FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) { -+ dev_err(dev, "stream start time out\n"); -+ ret = -ETIMEDOUT; -+ goto out_stream_close; -+ } -+ if (stream->error) { -+ dev_err(dev, "stream start error: %d\n", stream->error); -+ ret = -EIO; -+ goto out_stream_close; -+ } -+ dev_dbg(dev, "start stream: complete\n"); -+ -+ return 0; -+ -+out_stream_close: -+ reinit_completion(&stream->stream_close_completion); -+ -+ retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -+ if (retout < 0) { -+ dev_dbg(dev, "can't close stream (%d)\n", retout); -+ goto out_put_stream_opened; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_close_completion, -+ FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) -+ dev_err(dev, "stream close time out with error %d\n", -+ stream->error); -+ else -+ dev_dbg(dev, "stream close complete\n"); -+ -+out_put_stream_opened: -+ put_stream_opened(av); -+ -+ return ret; -+} -+ -+static void stop_streaming_firmware(struct ipu7_isys_video *av) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret, tout; -+ -+ reinit_completion(&stream->stream_stop_completion); -+ -+ ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU_INSYS_SEND_TYPE_STREAM_FLUSH); -+ if (ret < 0) { -+ dev_err(dev, "can't stop stream (%d)\n", ret); -+ return; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_stop_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else -+ FW_CALL_TIMEOUT_JIFFIES); -+#endif -+ if (!tout) -+ dev_warn(dev, "stream stop time out\n"); -+ else if (stream->error) -+ dev_warn(dev, "stream stop error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "stop stream: complete\n"); -+} -+ -+static void close_streaming_firmware(struct ipu7_isys_video *av) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret, tout; -+ -+ reinit_completion(&stream->stream_close_completion); -+ -+ ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -+ if (ret < 0) { -+ dev_err(dev, "can't close stream (%d)\n", ret); -+ return; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_close_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else -+ FW_CALL_TIMEOUT_JIFFIES); -+#endif -+ if (!tout) -+ dev_warn(dev, "stream close time out\n"); -+ else if (stream->error) -+ dev_warn(dev, "stream close error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "close stream: complete\n"); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ stream->last_sequence = atomic_read(&stream->sequence); -+ dev_dbg(dev, "ip->last_sequence = %d\n", -+ stream->last_sequence); -+ -+#endif -+ put_stream_opened(av); -+} -+ -+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -+ struct media_entity *source_entity, -+ int nr_queues) -+{ -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_csi2 *csi2; -+ -+ if (WARN_ON(stream->nr_streaming)) -+ return -EINVAL; -+ -+ stream->nr_queues = nr_queues; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ atomic_set(&stream->sequence, stream->last_sequence); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "atomic_set : stream->last_sequence = %d\n", -+ stream->last_sequence); -+ } else { -+ atomic_set(&stream->sequence, 0); -+ } -+#else -+ atomic_set(&stream->sequence, 0); -+#endif -+ atomic_set(&stream->buf_id, 0); -+ -+ stream->seq_index = 0; -+ memset(stream->seq, 0, sizeof(stream->seq)); -+ -+ if (WARN_ON(!list_empty(&stream->queues))) -+ return -EINVAL; -+ -+ stream->stream_source = stream->asd->source; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (!stream->asd->is_tpg) { -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ csi2->receiver_errors = 0; -+ } -+#else -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ csi2->receiver_errors = 0; -+#endif -+ stream->source_entity = source_entity; -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "prepare stream: external entity %s\n", -+ stream->source_entity->name); -+ -+ return 0; -+} -+ -+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream) -+{ -+ unsigned long flags; -+ struct device *dev; -+ unsigned int i; -+ -+ if (!stream) { -+ pr_err("ipu7-isys: no available stream\n"); -+ return; -+ } -+ -+ dev = &stream->isys->adev->auxdev.dev; -+ -+ spin_lock_irqsave(&stream->isys->streams_lock, flags); -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (&stream->isys->streams[i] == stream) { -+ if (stream->isys->streams_ref_count[i] > 0) -+ stream->isys->streams_ref_count[i]--; -+ else -+ dev_warn(dev, "invalid stream %d\n", i); -+ -+ break; -+ } -+ } -+ spin_unlock_irqrestore(&stream->isys->streams_lock, flags); -+} -+ -+static struct ipu7_isys_stream * -+ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd) -+{ -+ struct ipu7_isys_stream *stream = NULL; -+ struct ipu7_isys *isys = av->isys; -+ unsigned long flags; -+ unsigned int i; -+ u8 vc = av->vc; -+ -+ if (!isys) -+ return NULL; -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && -+ isys->streams[i].asd == asd) { -+ isys->streams_ref_count[i]++; -+ stream = &isys->streams[i]; -+ break; -+ } -+ } -+ -+ if (!stream) { -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (!isys->streams_ref_count[i]) { -+ isys->streams_ref_count[i]++; -+ stream = &isys->streams[i]; -+ stream->vc = vc; -+ stream->asd = asd; -+ break; -+ } -+ } -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle) -+{ -+ unsigned long flags; -+ struct ipu7_isys_stream *stream = NULL; -+ -+ if (!isys) -+ return NULL; -+ -+ if (stream_handle >= IPU_ISYS_MAX_STREAMS) { -+ dev_err(&isys->adev->auxdev.dev, -+ "stream_handle %d is invalid\n", stream_handle); -+ return NULL; -+ } -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ if (isys->streams_ref_count[stream_handle] > 0) { -+ isys->streams_ref_count[stream_handle]++; -+ stream = &isys->streams[stream_handle]; -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc) -+{ -+ struct ipu7_isys_stream *stream = NULL; -+ unsigned long flags; -+ unsigned int i; -+ -+ if (!isys) -+ return NULL; -+ -+ if (source < 0) { -+ dev_err(&isys->adev->auxdev.dev, -+ "query stream with invalid port number\n"); -+ return NULL; -+ } -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (!isys->streams_ref_count[i]) -+ continue; -+ -+ if (isys->streams[i].stream_source == source && -+ isys->streams[i].vc == vc) { -+ stream = &isys->streams[i]; -+ isys->streams_ref_count[i]++; -+ break; -+ } -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+static u32 get_remote_pad_stream(struct media_pad *r_pad) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev *sd; -+ u32 stream_id = 0; -+ unsigned int i; -+ -+ sd = media_entity_to_v4l2_subdev(r_pad->entity); -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ if (!state) -+ return 0; -+ -+ for (i = 0; i < state->stream_configs.num_configs; i++) { -+ struct v4l2_subdev_stream_config *cfg = -+ &state->stream_configs.configs[i]; -+ if (cfg->pad == r_pad->index) { -+ stream_id = cfg->stream; -+ break; -+ } -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ return stream_id; -+} -+ -+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ struct ipu7_isys_stream *stream = av->stream; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *r_pad; -+ struct v4l2_subdev *sd; -+ u32 r_stream; -+ int ret = 0; -+ -+ dev_dbg(dev, "set stream: %d\n", state); -+ -+ if (WARN(!stream->source_entity, "No source entity for stream\n")) -+ return -ENODEV; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (stream->asd->is_tpg) { -+ sd = &stream->asd->sd; -+ r_pad = media_pad_remote_pad_first(&av->pad); -+ r_stream = -+ ipu7_isys_get_src_stream_by_src_pad(sd, r_pad->index); -+ -+ if (!state) { -+ stop_streaming_firmware(av); -+ dev_dbg(dev, "disable streams 0x%lx of %s\n", -+ BIT(r_stream), sd->name); -+ ret = v4l2_subdev_disable_streams(sd, r_pad->index, -+ BIT(r_stream)); -+ if (ret) -+ dev_err(dev, "disable streams of %s failed\n", -+ sd->name); -+ -+ close_streaming_firmware(av); -+ } else { -+ ret = start_stream_firmware(av, bl); -+ if (ret) { -+ dev_err(dev, "start FW stream failed\n"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "set stream: source %d, handle %d\n", -+ stream->stream_source, stream->stream_handle); -+ -+ dev_dbg(dev, "enable streams 0x%lx of %s\n", -+ BIT(r_stream), sd->name); -+ /* start sub-device which connects with video */ -+ ret = v4l2_subdev_enable_streams(sd, r_pad->index, -+ BIT(r_stream)); -+ if (ret) { -+ dev_err(dev, "enable streams of %s failed\n", -+ sd->name); -+ goto out_media_entity_stop_streaming_firmware; -+ } -+ } -+ av->streaming = state; -+ -+ return 0; -+ } -+ -+#endif -+ sd = &stream->asd->sd; -+ r_pad = media_pad_remote_pad_first(&av->pad); -+ r_stream = get_remote_pad_stream(r_pad); -+ if (!state) { -+ stop_streaming_firmware(av); -+ -+ /* stop sub-device which connects with video */ -+ dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n", -+ sd->name, r_pad->index, BIT_ULL(r_stream)); -+ ret = v4l2_subdev_disable_streams(sd, r_pad->index, -+ BIT_ULL(r_stream)); -+ if (ret) { -+ dev_err(dev, "disable streams %s failed with %d\n", -+ sd->name, ret); -+ return ret; -+ } -+ -+ close_streaming_firmware(av); -+ } else { -+ ret = start_stream_firmware(av, bl); -+ if (ret) { -+ dev_err(dev, "start stream of firmware failed\n"); -+ return ret; -+ } -+ -+ /* start sub-device which connects with video */ -+ dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n", -+ sd->name, r_pad->index, BIT_ULL(r_stream)); -+ ret = v4l2_subdev_enable_streams(sd, r_pad->index, -+ BIT_ULL(r_stream)); -+ if (ret) { -+ dev_err(dev, "enable streams %s failed with %d\n", -+ sd->name, ret); -+ goto out_media_entity_stop_streaming_firmware; -+ } -+ } -+ -+ av->streaming = state; -+ -+ return 0; -+ -+out_media_entity_stop_streaming_firmware: -+ stop_streaming_firmware(av); -+ -+ return ret; -+} -+ -+static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { -+ .vidioc_querycap = ipu7_isys_vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, -+ .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes, -+ .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap, -+ .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap, -+ .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap, -+ .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs, -+ .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ .vidioc_expbuf = vb2_ioctl_expbuf, -+}; -+ -+static const struct media_entity_operations entity_ops = { -+ .link_validate = link_validate, -+}; -+ -+static const struct v4l2_file_operations isys_fops = { -+ .owner = THIS_MODULE, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, -+ .mmap = vb2_fop_mmap, -+ .open = video_open, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ .release = video_release, -+#else -+ .release = vb2_fop_release, -+#endif -+}; -+ -+int ipu7_isys_fw_open(struct ipu7_isys *isys) -+{ -+ struct ipu7_bus_device *adev = isys->adev; -+ int ret; -+ -+ ret = pm_runtime_resume_and_get(&adev->auxdev.dev); -+ if (ret < 0) -+ return ret; -+ -+ mutex_lock(&isys->mutex); -+ -+ if (isys->ref_count++) -+ goto unlock; -+ -+ /* -+ * Buffers could have been left to wrong queue at last closure. -+ * Move them now back to empty buffer queue. -+ */ -+ ipu7_cleanup_fw_msg_bufs(isys); -+ -+ ret = ipu7_fw_isys_open(isys); -+ if (ret < 0) -+ goto out; -+ -+unlock: -+ mutex_unlock(&isys->mutex); -+ -+ return 0; -+out: -+ isys->ref_count--; -+ mutex_unlock(&isys->mutex); -+ pm_runtime_put(&adev->auxdev.dev); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+void ipu7_isys_fw_close(struct ipu7_isys *isys) -+{ -+ int ret = 0; -+ -+ mutex_lock(&isys->mutex); -+ isys->ref_count--; -+ if (!isys->ref_count) { -+ /* need reset when fw close is abnormal */ -+ ret = ipu7_fw_isys_close(isys); -+ if (ret) { -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = true; -+ mutex_unlock(&isys->reset_mutex); -+ } -+ } -+ -+ mutex_unlock(&isys->mutex); -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put_sync(&isys->adev->auxdev.dev); -+ } else { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put(&isys->adev->auxdev.dev); -+ } -+} -+#else -+void ipu7_isys_fw_close(struct ipu7_isys *isys) -+{ -+ mutex_lock(&isys->mutex); -+ -+ isys->ref_count--; -+ -+ if (!isys->ref_count) -+ ipu7_fw_isys_close(isys); -+ -+ mutex_unlock(&isys->mutex); -+ pm_runtime_put(&isys->adev->auxdev.dev); -+} -+#endif -+ -+int ipu7_isys_setup_video(struct ipu7_isys_video *av, -+ struct media_entity **source_entity, int *nr_queues) -+{ -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *source_pad, *remote_pad; -+ struct v4l2_mbus_frame_desc_entry entry; -+ struct v4l2_subdev_route *route = NULL; -+ struct v4l2_subdev_route *r; -+ struct v4l2_subdev_state *state; -+ struct ipu7_isys_subdev *asd; -+ struct v4l2_subdev *remote_sd; -+ struct media_pipeline *pipeline; -+ int ret = -EINVAL; -+ -+ *nr_queues = 0; -+ -+ remote_pad = media_pad_remote_pad_unique(&av->pad); -+ if (IS_ERR(remote_pad)) { -+ dev_dbg(dev, "failed to get remote pad\n"); -+ return PTR_ERR(remote_pad); -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ asd = to_ipu7_isys_subdev(remote_sd); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ -+ if (strncmp(remote_pad->entity->name, "Intel IPU7 TPG", -+ strlen("Intel IPU7 TPG")) == 0) { -+ dev_dbg(dev, "Find TPG:%s stream\n", remote_sd->name); -+ -+ av->vc = 0; -+ av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -+ ret = video_device_pipeline_alloc_start(&av->vdev); -+ if (ret < 0) { -+ dev_dbg(dev, "media pipeline start failed\n"); -+ return ret; -+ } -+ -+ *source_entity = remote_pad->entity; -+ av->stream = ipu7_isys_get_stream(av, asd); -+ if (!av->stream) { -+ video_device_pipeline_stop(&av->vdev); -+ dev_err(dev, "no available stream for firmware\n"); -+ return -EINVAL; -+ } -+ -+ av->stream->asd->is_tpg = true; -+ *nr_queues = 1; -+ -+ return 0; -+ } -+#endif -+ -+ source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); -+ if (!source_pad) { -+ dev_dbg(dev, "No external source entity\n"); -+ return -ENODEV; -+ } -+ -+ *source_entity = source_pad->entity; -+ -+ state = v4l2_subdev_lock_and_get_active_state(remote_sd); -+ for_each_active_route(&state->routing, r) { -+ if (r->source_pad == remote_pad->index) -+ route = r; -+ } -+ -+ if (!route) { -+ v4l2_subdev_unlock_state(state); -+ dev_dbg(dev, "Failed to find route\n"); -+ return -ENODEV; -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, -+ to_ipu7_isys_csi2(asd), -+ *source_entity, &entry, -+ nr_queues); -+ if (ret == -ENOIOCTLCMD) { -+ av->vc = 0; -+ av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -+ if (av->dt == 0xff) -+ return -EINVAL; -+ *nr_queues = 1; -+ } else if (*nr_queues && !ret) { -+ dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", -+ entry.stream, entry.length, entry.bus.csi2.vc, -+ entry.bus.csi2.dt); -+ -+ av->vc = entry.bus.csi2.vc; -+ av->dt = entry.bus.csi2.dt; -+ } else { -+ dev_err(dev, "failed to get remote frame desc\n"); -+ return ret; -+ } -+ -+ pipeline = media_entity_pipeline(&av->vdev.entity); -+ if (!pipeline) -+ ret = video_device_pipeline_alloc_start(&av->vdev); -+ else -+ ret = video_device_pipeline_start(&av->vdev, pipeline); -+ if (ret < 0) { -+ dev_dbg(dev, "media pipeline start failed\n"); -+ return ret; -+ } -+ -+ av->stream = ipu7_isys_get_stream(av, asd); -+ if (!av->stream) { -+ video_device_pipeline_stop(&av->vdev); -+ dev_err(dev, "no available stream for firmware\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Do everything that's needed to initialise things related to video -+ * buffer queue, video node, and the related media entity. The caller -+ * is expected to assign isys field and set the name of the video -+ * device. -+ */ -+int ipu7_isys_video_init(struct ipu7_isys_video *av) -+{ -+ struct v4l2_format format = { -+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, -+ .fmt.pix = { -+ .width = 1920, -+ .height = 1080, -+ }, -+ }; -+ int ret; -+ -+ mutex_init(&av->mutex); -+ av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | -+ V4L2_CAP_VIDEO_CAPTURE; -+ av->vdev.vfl_dir = VFL_DIR_RX; -+ -+ ret = ipu7_isys_queue_init(&av->aq); -+ if (ret) -+ goto out_mutex_destroy; -+ -+ av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; -+ ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); -+ if (ret) -+ goto out_vb2_queue_cleanup; -+ -+ av->vdev.entity.ops = &entity_ops; -+ av->vdev.release = video_device_release_empty; -+ av->vdev.fops = &isys_fops; -+ av->vdev.v4l2_dev = &av->isys->v4l2_dev; -+ av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev; -+ av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops; -+ av->vdev.queue = &av->aq.vbq; -+ av->vdev.lock = &av->mutex; -+ -+ __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); -+ av->pix_fmt = format.fmt.pix; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->reset = false; -+ av->skipframe = 0; -+ av->start_streaming = 0; -+#endif -+ -+ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); -+ video_set_drvdata(&av->vdev, av); -+ -+ ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); -+ if (ret) -+ goto out_media_entity_cleanup; -+ -+ return ret; -+ -+out_media_entity_cleanup: -+ vb2_video_unregister_device(&av->vdev); -+ media_entity_cleanup(&av->vdev.entity); -+ -+out_vb2_queue_cleanup: -+ vb2_queue_release(&av->aq.vbq); -+ -+out_mutex_destroy: -+ mutex_destroy(&av->mutex); -+ -+ return ret; -+} -+ -+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) -+{ -+ vb2_video_unregister_device(&av->vdev); -+ media_entity_cleanup(&av->vdev.entity); -+ mutex_destroy(&av->mutex); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.h b/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -new file mode 100644 -index 000000000000..e6d1da2b7b47 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -@@ -0,0 +1,125 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_VIDEO_H -+#define IPU7_ISYS_VIDEO_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "ipu7-isys-queue.h" -+ -+#define IPU_INSYS_OUTPUT_PINS 11U -+#define IPU_ISYS_MAX_PARALLEL_SOF 2U -+ -+struct file; -+struct ipu7_isys; -+struct ipu7_isys_csi2; -+struct ipu7_insys_stream_cfg; -+struct ipu7_isys_subdev; -+ -+struct ipu7_isys_pixelformat { -+ u32 pixelformat; -+ u32 bpp; -+ u32 bpp_packed; -+ u32 code; -+ u32 css_pixelformat; -+}; -+ -+struct sequence_info { -+ unsigned int sequence; -+ u64 timestamp; -+}; -+ -+struct output_pin_data { -+ void (*pin_ready)(struct ipu7_isys_stream *stream, -+ struct ipu7_insys_resp *info); -+ struct ipu7_isys_queue *aq; -+}; -+ -+/* -+ * Align with firmware stream. Each stream represents a CSI virtual channel. -+ * May map to multiple video devices -+ */ -+struct ipu7_isys_stream { -+ struct mutex mutex; -+ struct media_entity *source_entity; -+ atomic_t sequence; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ int last_sequence; -+#endif -+ atomic_t buf_id; -+ unsigned int seq_index; -+ struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; -+ int stream_source; -+ int stream_handle; -+ unsigned int nr_output_pins; -+ struct ipu7_isys_subdev *asd; -+ -+ int nr_queues; /* Number of capture queues */ -+ int nr_streaming; -+ int streaming; -+ struct list_head queues; -+ struct completion stream_open_completion; -+ struct completion stream_close_completion; -+ struct completion stream_start_completion; -+ struct completion stream_stop_completion; -+ struct ipu7_isys *isys; -+ -+ struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS]; -+ int error; -+ u8 vc; -+}; -+ -+struct ipu7_isys_video { -+ struct ipu7_isys_queue aq; -+ /* Serialise access to other fields in the struct. */ -+ struct mutex mutex; -+ struct media_pad pad; -+ struct video_device vdev; -+ struct v4l2_pix_format pix_fmt; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_csi2 *csi2; -+ struct ipu7_isys_stream *stream; -+ unsigned int streaming; -+ u8 vc; -+ u8 dt; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ unsigned int reset; -+ unsigned int skipframe; -+ unsigned int start_streaming; -+#endif -+}; -+ -+#define ipu7_isys_queue_to_video(__aq) \ -+ container_of(__aq, struct ipu7_isys_video, aq) -+ -+extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[]; -+ -+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat); -+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -+ struct media_entity *source_entity, -+ int nr_queues); -+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -+ struct ipu7_isys_buffer_list *bl); -+int ipu7_isys_fw_open(struct ipu7_isys *isys); -+void ipu7_isys_fw_close(struct ipu7_isys *isys); -+int ipu7_isys_setup_video(struct ipu7_isys_video *av, -+ struct media_entity **source_entity, int *nr_queues); -+int ipu7_isys_video_init(struct ipu7_isys_video *av); -+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av); -+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream); -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, -+ u8 stream_handle); -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc); -+#endif /* IPU7_ISYS_VIDEO_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c -new file mode 100644 -index 000000000000..e5ba50142a1a ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys.c -@@ -0,0 +1,1624 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-mmu.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2.h" -+#include "ipu7-isys-csi-phy.h" -+#include "ipu7-isys-csi2-regs.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+#include "ipu7-isys-tpg.h" -+#endif -+#include "ipu7-isys-video.h" -+#include "ipu7-platform-regs.h" -+ -+#define ISYS_PM_QOS_VALUE 300 -+ -+static int -+isys_complete_ext_device_registration(struct ipu7_isys *isys, -+ struct v4l2_subdev *sd, -+ struct ipu7_isys_csi2_config *csi2) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ int ret; -+ -+ v4l2_set_subdev_hostdata(sd, csi2); -+ -+ for (i = 0; i < sd->entity.num_pads; i++) { -+ if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -+ break; -+ } -+ -+ if (i == sd->entity.num_pads) { -+ dev_warn(dev, "no source pad in external entity\n"); -+ ret = -ENOENT; -+ goto skip_unregister_subdev; -+ } -+ -+ ret = media_create_pad_link(&sd->entity, i, -+ &isys->csi2[csi2->port].asd.sd.entity, -+ 0, MEDIA_LNK_FL_ENABLED | -+ MEDIA_LNK_FL_IMMUTABLE); -+ if (ret) { -+ dev_warn(dev, "can't create link\n"); -+ goto skip_unregister_subdev; -+ } -+ -+ isys->csi2[csi2->port].nlanes = csi2->nlanes; -+ if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) -+ isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; -+ else -+ isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; -+ -+ return 0; -+ -+skip_unregister_subdev: -+ v4l2_device_unregister_subdev(sd); -+ return ret; -+} -+ -+struct isys_i2c_test { -+ u8 bus_nr; -+ u16 addr; -+ struct i2c_client *client; -+}; -+ -+static int isys_i2c_test(struct device *dev, void *priv) -+{ -+ struct i2c_client *client = i2c_verify_client(dev); -+ struct isys_i2c_test *test = priv; -+ -+ if (!client) -+ return 0; -+ -+ if (i2c_adapter_id(client->adapter) != test->bus_nr || -+ client->addr != test->addr) -+ return 0; -+ -+ test->client = client; -+ -+ return 0; -+} -+ -+static -+struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct i2c_board_info *info = &sd_info->i2c.board_info; -+ struct isys_i2c_test test = { -+ .bus_nr = i2c_adapter_id(adapter), -+ .addr = info->addr, -+ }; -+ int ret; -+ -+ ret = i2c_for_each_dev(&test, isys_i2c_test); -+ if (ret || !test.client) -+ return NULL; -+ return test.client; -+} -+ -+static int isys_register_ext_subdev(struct ipu7_isys *isys, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct i2c_adapter *adapter; -+ struct v4l2_subdev *sd; -+ struct i2c_client *client; -+ int ret; -+ int bus; -+ -+ bus = sd_info->i2c.i2c_adapter_id; -+ adapter = i2c_get_adapter(bus); -+ if (!adapter) { -+ dev_warn(dev, "can't find adapter\n"); -+ return -ENOENT; -+ } -+ -+ dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -+ sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -+ bus); -+ -+ if (sd_info->csi2) { -+ dev_info(dev, "sensor device on CSI port: %d\n", -+ sd_info->csi2->port); -+ if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -+ !isys->csi2[sd_info->csi2->port].isys) { -+ dev_warn(dev, "invalid csi2 port %u\n", -+ sd_info->csi2->port); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ } else { -+ dev_info(dev, "No camera subdevice\n"); -+ } -+ -+ client = isys_find_i2c_subdev(adapter, sd_info); -+ if (client) { -+ dev_warn(dev, "Device exists\n"); -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+ /* TODO: remove i2c_unregister_device() */ -+ i2c_unregister_device(client); -+#else -+ ret = 0; -+ goto skip_put_adapter; -+#endif -+ } -+ -+ sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -+ &sd_info->i2c.board_info, NULL); -+ if (!sd) { -+ dev_warn(dev, "can't create new i2c subdev\n"); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ -+ if (!sd_info->csi2) -+ return 0; -+ -+ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -+ -+skip_put_adapter: -+ i2c_put_adapter(adapter); -+ -+ return ret; -+} -+ -+static void isys_register_ext_subdevs(struct ipu7_isys *isys) -+{ -+ struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -+ struct ipu7_isys_subdev_info **sd_info; -+ -+ if (!spdata) { -+ dev_info(&isys->adev->auxdev.dev, -+ "no subdevice info provided\n"); -+ return; -+ } -+ for (sd_info = spdata->subdevs; *sd_info; sd_info++) -+ isys_register_ext_subdev(isys, *sd_info); -+} -+ -+static void isys_stream_init(struct ipu7_isys *isys) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ mutex_init(&isys->streams[i].mutex); -+ init_completion(&isys->streams[i].stream_open_completion); -+ init_completion(&isys->streams[i].stream_close_completion); -+ init_completion(&isys->streams[i].stream_start_completion); -+ init_completion(&isys->streams[i].stream_stop_completion); -+ INIT_LIST_HEAD(&isys->streams[i].queues); -+ isys->streams[i].isys = isys; -+ isys->streams[i].stream_handle = i; -+ isys->streams[i].vc = INVALID_VC_ID; -+ } -+} -+ -+static int isys_fw_log_init(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_log *fw_log; -+ void *log_buf; -+ -+ if (isys->fw_log) -+ return 0; -+ -+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -+ if (!fw_log) -+ return -ENOMEM; -+ -+ mutex_init(&fw_log->mutex); -+ -+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!log_buf) -+ return -ENOMEM; -+ -+ fw_log->head = log_buf; -+ fw_log->addr = log_buf; -+ fw_log->count = 0; -+ fw_log->size = 0; -+ -+ isys->fw_log = fw_log; -+ -+ return 0; -+} -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+/* The .bound() notifier callback when a match is found */ -+static int isys_notifier_bound(struct v4l2_async_notifier *notifier, -+ struct v4l2_subdev *sd, -+ struct v4l2_async_connection *asc) -+{ -+ struct ipu7_isys *isys = container_of(notifier, -+ struct ipu7_isys, notifier); -+ struct sensor_async_sd *s_asd = -+ container_of(asc, struct sensor_async_sd, asc); -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ ret = ipu_bridge_instantiate_vcm(sd->dev); -+ if (ret) { -+ dev_err(dev, "instantiate vcm failed\n"); -+ return ret; -+ } -+ -+ dev_info(dev, "bind %s nlanes is %d port is %d\n", -+ sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); -+ isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); -+ -+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+} -+ -+static int isys_notifier_complete(struct v4l2_async_notifier *notifier) -+{ -+ struct ipu7_isys *isys = container_of(notifier, -+ struct ipu7_isys, notifier); -+ -+ dev_info(&isys->adev->auxdev.dev, -+ "All sensor registration completed.\n"); -+ -+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+} -+ -+static const struct v4l2_async_notifier_operations isys_async_ops = { -+ .bound = isys_notifier_bound, -+ .complete = isys_notifier_complete, -+}; -+ -+static int isys_notifier_init(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2 = -+ &isys->pdata->ipdata->csi2; -+ struct ipu7_device *isp = isys->adev->isp; -+ struct device *dev = &isp->pdev->dev; -+ unsigned int i; -+ int ret; -+ -+ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); -+ -+ for (i = 0; i < csi2->nports; i++) { -+ struct v4l2_fwnode_endpoint vep = { -+ .bus_type = V4L2_MBUS_UNKNOWN -+ }; -+ struct sensor_async_sd *s_asd; -+ struct fwnode_handle *ep; -+ -+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!ep) -+ continue; -+ -+ ret = v4l2_fwnode_endpoint_parse(ep, &vep); -+ if (ret) -+ goto err_parse; -+ -+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && -+ vep.bus_type != V4L2_MBUS_CSI2_CPHY) { -+ ret = -EINVAL; -+ dev_err(dev, "unsupported bus type %d!\n", -+ vep.bus_type); -+ goto err_parse; -+ } -+ -+ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, -+ struct -+ sensor_async_sd); -+ if (IS_ERR(s_asd)) { -+ ret = PTR_ERR(s_asd); -+ goto err_parse; -+ } -+ -+ s_asd->csi2.port = vep.base.port; -+ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; -+ s_asd->csi2.bus_type = vep.bus_type; -+ -+ fwnode_handle_put(ep); -+ -+ continue; -+ -+err_parse: -+ fwnode_handle_put(ep); -+ return ret; -+ } -+ -+ if (list_empty(&isys->notifier.waiting_list)) { -+ /* isys probe could continue with async subdevs missing */ -+ dev_warn(dev, "no subdev found in graph\n"); -+ return 0; -+ } -+ -+ isys->notifier.ops = &isys_async_ops; -+ ret = v4l2_async_nf_register(&isys->notifier); -+ if (ret) { -+ dev_err(dev, "failed to register async notifier(%d)\n", ret); -+ v4l2_async_nf_cleanup(&isys->notifier); -+ } -+ -+ return ret; -+} -+ -+static void isys_notifier_cleanup(struct ipu7_isys *isys) -+{ -+ v4l2_async_nf_unregister(&isys->notifier); -+ v4l2_async_nf_cleanup(&isys->notifier); -+} -+#endif -+ -+static void isys_unregister_video_devices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i, j; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) -+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); -+} -+ -+static int isys_register_video_devices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i, j; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -+ -+ snprintf(av->vdev.name, sizeof(av->vdev.name), -+ IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u", -+ i * IPU7_NR_OF_CSI2_SRC_PADS + j); -+ av->isys = isys; -+ av->aq.vbq.buf_struct_size = -+ sizeof(struct ipu7_isys_video_buffer); -+ -+ ret = ipu7_isys_video_init(av); -+ if (ret) -+ goto fail; -+ } -+ } -+ -+ return 0; -+ -+fail: -+ i = i + 1U; -+ while (i--) { -+ while (j--) -+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); -+ j = IPU7_NR_OF_CSI2_SRC_PADS; -+ } -+ -+ return ret; -+} -+ -+static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2 = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i; -+ -+ for (i = 0; i < csi2->nports; i++) -+ ipu7_isys_csi2_cleanup(&isys->csi2[i]); -+} -+ -+static int isys_csi2_register_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ ret = ipu7_isys_csi2_init(&isys->csi2[i], isys, -+ isys->pdata->base + -+ csi2_pdata->offsets[i], i); -+ if (ret) -+ goto fail; -+ } -+ -+ isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK; -+ -+ return 0; -+ -+fail: -+ while (i--) -+ ipu7_isys_csi2_cleanup(&isys->csi2[i]); -+ -+ return ret; -+} -+ -+static int isys_csi2_create_media_links(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct media_entity *sd; -+ unsigned int i, j; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ sd = &isys->csi2[i].asd.sd.entity; -+ -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -+ -+ ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j, -+ &av->vdev.entity, 0, 0); -+ if (ret) { -+ dev_err(dev, "CSI2 can't create link\n"); -+ return ret; -+ } -+ -+ av->csi2 = &isys->csi2[i]; -+ } -+ } -+ -+ return 0; -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+static void isys_tpg_unregister_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -+ &isys->pdata->ipdata->tpg; -+ unsigned int i; -+ -+ if (!isys->tpg) -+ return; -+ -+ for (i = 0; i < tpg_pdata->ntpgs; i++) -+ ipu7_isys_tpg_cleanup(&isys->tpg[i]); -+ -+ kfree(isys->tpg); -+ isys->tpg = NULL; -+} -+ -+static int isys_tpg_register_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -+ &isys->pdata->ipdata->tpg; -+ unsigned int i; -+ int ret; -+ -+ isys->tpg = kcalloc(tpg_pdata->ntpgs, sizeof(*isys->tpg), GFP_KERNEL); -+ if (!isys->tpg) -+ return -ENOMEM; -+ -+ for (i = 0; i < tpg_pdata->ntpgs; i++) { -+ ret = ipu7_isys_tpg_init(&isys->tpg[i], isys, -+ isys->pdata->base + -+ tpg_pdata->offsets[i], -+ tpg_pdata->sels ? -+ (isys->pdata->base + -+ tpg_pdata->sels[i]) : NULL, i); -+ if (ret) -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ while (i--) -+ ipu7_isys_tpg_cleanup(&isys->tpg[i]); -+ -+ kfree(isys->tpg); -+ isys->tpg = NULL; -+ -+ return ret; -+} -+ -+static int isys_tpg_create_media_links(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -+ &isys->pdata->ipdata->tpg; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu7_isys_tpg *tpg; -+ struct media_entity *sd; -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < tpg_pdata->ntpgs; i++) { -+ tpg = &isys->tpg[i]; -+ sd = &tpg->asd.sd.entity; -+ tpg->av = &isys->csi2[tpg->index].av[0]; -+ -+ ret = media_create_pad_link(sd, TPG_PAD_SOURCE, -+ &tpg->av->vdev.entity, -+ TPG_PAD_SOURCE, 0); -+ if (ret) { -+ dev_err(dev, "TPG can't create link\n"); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+#endif -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+static int isys_register_devices(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_video_unregister_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ -+ if (!isys->pdata->spdata) { -+ ret = isys_notifier_init(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } else { -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } -+ -+ return 0; -+ -+out_csi2_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_video_unregister_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+#else -+static int isys_register_devices(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_video_unregister_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ ret = isys_tpg_register_subdevices(isys); -+ if (!ret) -+ ret = isys_tpg_create_media_links(isys); -+ -+ if (ret) -+ goto out_tpg_unregister_subdevices; -+ -+#endif -+ -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ if (ret) -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ goto out_tpg_unregister_subdevices; -+#else -+ goto out_csi2_unregister_subdevices; -+#endif -+ -+ return 0; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+out_tpg_unregister_subdevices: -+ isys_tpg_unregister_subdevices(isys); -+#endif -+out_csi2_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_video_unregister_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+#endif -+ -+static void isys_unregister_devices(struct ipu7_isys *isys) -+{ -+ isys_unregister_video_devices(isys); -+ isys_csi2_unregister_subdevices(isys); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ isys_tpg_unregister_subdevices(isys); -+#endif -+ v4l2_device_unregister(&isys->v4l2_dev); -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+} -+ -+static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable) -+{ -+ u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -+ void __iomem *base = isys->pdata->base; -+ u32 mask = isys->isr_csi2_mask; -+ -+ if (!enable) { -+ writel(mask, base + offset + IRQ_CTL_CLEAR); -+ writel(0, base + offset + IRQ_CTL_ENABLE); -+ return; -+ } -+ -+ writel(mask, base + offset + IRQ_CTL_EDGE); -+ writel(mask, base + offset + IRQ_CTL_CLEAR); -+ writel(mask, base + offset + IRQ_CTL_MASK); -+ writel(mask, base + offset + IRQ_CTL_ENABLE); -+} -+ -+static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable) -+{ -+ void __iomem *base = isys->pdata->base; -+ u32 mask = IS_UC_TO_SW_IRQ_MASK; -+ u32 offset = IS_UC_CTRL_BASE; -+ -+ if (!enable) { -+ writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE); -+ return; -+ } -+ -+ writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR); -+ writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N); -+ writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE); -+} -+ -+void ipu7_isys_setup_hw(struct ipu7_isys *isys) -+{ -+ u32 offset; -+ void __iomem *base = isys->pdata->base; -+ -+ /* soft reset */ -+ offset = IS_IO_GPREGS_BASE; -+ -+ writel(0x0, base + offset + CLK_EN_TXCLKESC); -+ /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */ -+ writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK); -+ /* correct the initial printf configuration */ -+ writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -+ -+ enable_to_sw_irq(isys, 1); -+ enable_csi2_legacy_irq(isys, 1); -+} -+ -+static void isys_cleanup_hw(struct ipu7_isys *isys) -+{ -+ enable_csi2_legacy_irq(isys, 0); -+ enable_to_sw_irq(isys, 0); -+} -+ -+static int isys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ struct ipu7_device *isp = adev->isp; -+ unsigned long flags; -+ int ret; -+ -+ if (!isys) -+ return 0; -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); -+ -+ ret = ipu_buttress_start_tsc_sync(isp); -+ if (ret) -+ return ret; -+ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->power = 1; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ -+ return 0; -+} -+ -+static int isys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ -+ if (!isys) -+ return 0; -+ -+ isys_cleanup_hw(isys); -+ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->power = 0; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = false; -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif -+ cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+static int isys_suspend(struct device *dev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(dev); -+ -+ /* If stream is open, refuse to suspend */ -+ if (isys->stream_opened) -+ return -EBUSY; -+ -+ return 0; -+} -+ -+static int isys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static const struct dev_pm_ops isys_pm_ops = { -+ .runtime_suspend = isys_runtime_pm_suspend, -+ .runtime_resume = isys_runtime_pm_resume, -+ .suspend = isys_suspend, -+ .resume = isys_resume, -+}; -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct isys_fw_msgs *fwmsg, *safe; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ if (!isys->pdata->spdata) -+ isys_notifier_cleanup(isys); -+ -+ isys_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#else -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct isys_fw_msgs *fwmsg, *safe; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ isys_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#endif -+ -+#ifdef CONFIG_DEBUG_FS -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_isys *isys = file->private_data; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 log_size; -+ int ret = 0; -+ void *buf; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_info(dev, "copy %d bytes fw log to user...\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations isys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, isys, &isys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ isys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ -+static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) -+{ -+ struct ipu7_bus_device *adev = isys->adev; -+ struct isys_fw_msgs *addr; -+ dma_addr_t dma_addr; -+ unsigned long flags; -+ unsigned int i; -+ -+ for (i = 0; i < amount; i++) { -+ addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs), -+ &dma_addr, GFP_KERNEL, 0); -+ if (!addr) -+ break; -+ addr->dma_addr = dma_addr; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ list_add(&addr->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ } -+ -+ if (i == amount) -+ return 0; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ while (!list_empty(&isys->framebuflist)) { -+ addr = list_first_entry(&isys->framebuflist, -+ struct isys_fw_msgs, head); -+ list_del(&addr->head); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ addr, addr->dma_addr, 0); -+ spin_lock_irqsave(&isys->listlock, flags); -+ } -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ -+ return -ENOMEM; -+} -+ -+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) -+{ -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu7_isys *isys = stream->isys; -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ int ret; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ if (list_empty(&isys->framebuflist)) { -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dev_dbg(dev, "Frame buffer list empty\n"); -+ -+ ret = alloc_fw_msg_bufs(isys, 5); -+ if (ret < 0) -+ return NULL; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ if (list_empty(&isys->framebuflist)) { -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dev_err(dev, "Frame list empty\n"); -+ return NULL; -+ } -+ } -+ msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); -+ list_move(&msg->head, &isys->framebuflist_fw); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); -+ -+ return msg; -+} -+ -+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys) -+{ -+ struct isys_fw_msgs *fwmsg, *fwmsg0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) -+ list_move(&fwmsg->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+} -+ -+void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data) -+{ -+ struct isys_fw_msgs *msg; -+ void *ptr = (void *)data; -+ unsigned long flags; -+ -+ if (WARN_ON_ONCE(!ptr)) -+ return; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); -+ list_move(&msg->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+} -+ -+static int isys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ struct ipu7_device *isp = adev->isp; -+ struct ipu7_isys *isys; -+ int ret = 0; -+ -+ if (!isp->ipu7_bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); -+ if (!isys) -+ return -ENOMEM; -+ -+ ret = pm_runtime_resume_and_get(&auxdev->dev); -+ if (ret < 0) -+ return ret; -+ -+ adev->auxdrv_data = -+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); -+ isys->adev = adev; -+ isys->pdata = adev->pdata; -+ -+ INIT_LIST_HEAD(&isys->requests); -+ csi2_pdata = &isys->pdata->ipdata->csi2; -+ -+ isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports, -+ sizeof(*isys->csi2), GFP_KERNEL); -+ if (!isys->csi2) { -+ ret = -ENOMEM; -+ goto out_runtime_put; -+ } -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ goto out_runtime_put; -+ -+ spin_lock_init(&isys->streams_lock); -+ spin_lock_init(&isys->power_lock); -+ isys->power = 0; -+ -+ mutex_init(&isys->mutex); -+ mutex_init(&isys->stream_mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_init(&isys->reset_mutex); -+ isys->state = 0; -+#endif -+ -+ spin_lock_init(&isys->listlock); -+ INIT_LIST_HEAD(&isys->framebuflist); -+ INIT_LIST_HEAD(&isys->framebuflist_fw); -+ -+ dev_set_drvdata(&auxdev->dev, isys); -+ -+ isys->icache_prefetch = 0; -+ isys->phy_rext_cal = 0; -+ -+ isys_stream_init(isys); -+ -+#ifdef CONFIG_DEBUG_FS -+ /* Debug fs failure is not fatal. */ -+ ipu7_isys_init_debugfs(isys); -+#endif -+ -+ cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -+ ret = alloc_fw_msg_bufs(isys, 40); -+ if (ret < 0) -+ goto out_cleanup_isys; -+ -+ ret = ipu7_fw_isys_init(isys); -+ if (ret) -+ goto out_cleanup_isys; -+ -+ ret = isys_register_devices(isys); -+ if (ret) -+ goto out_cleanup_fw; -+ -+ ret = isys_fw_log_init(isys); -+ if (ret) -+ goto out_cleanup; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ pm_runtime_put(&auxdev->dev); -+ -+ return 0; -+ -+out_cleanup: -+ isys_unregister_devices(isys); -+out_cleanup_fw: -+ ipu7_fw_isys_release(isys); -+out_cleanup_isys: -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ mutex_destroy(&isys->mutex); -+ mutex_destroy(&isys->stream_mutex); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+out_runtime_put: -+ pm_runtime_put(&auxdev->dev); -+ -+ return ret; -+} -+ -+struct ipu7_csi2_error { -+ const char *error_string; -+ bool is_info_only; -+}; -+ -+/* -+ * Strings corresponding to CSI-2 receiver errors are here. -+ * Corresponding macros are defined in the header file. -+ */ -+static const struct ipu7_csi2_error dphy_rx_errors[] = { -+ { "Error handler FIFO full", false }, -+ { "Reserved Short Packet encoding detected", true }, -+ { "Reserved Long Packet encoding detected", true }, -+ { "Received packet is too short", false}, -+ { "Received packet is too long", false}, -+ { "Short packet discarded due to errors", false }, -+ { "Long packet discarded due to errors", false }, -+ { "CSI Combo Rx interrupt", false }, -+ { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false }, -+ { "Received NULL packet", true }, -+ { "Received blanking packet", true }, -+ { "Tie to 0", true }, -+ { } -+}; -+ -+static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2) -+{ -+ u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -+ u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS); -+ u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -+ -+ if (!status) -+ return; -+ -+ dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n", -+ csi2->port, status); -+ -+ writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ csi2->receiver_errors |= status & mask; -+} -+ -+static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_csi2_error const *errors; -+ unsigned int i; -+ u32 status; -+ -+ /* Register errors once more in case of error interrupts are disabled */ -+ ipu7_isys_register_errors(csi2); -+ status = csi2->receiver_errors; -+ csi2->receiver_errors = 0; -+ errors = dphy_rx_errors; -+ -+ for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { -+ if (status & BIT(i)) -+ dev_err_ratelimited(&csi2->isys->adev->auxdev.dev, -+ "csi2-%i error: %s\n", -+ csi2->port, -+ errors[i].error_string); -+ } -+} -+ -+struct resp_to_msg { -+ enum ipu7_insys_resp_type type; -+ const char *msg; -+}; -+ -+static const struct resp_to_msg is_fw_msg[] = { -+ {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE, -+ "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"}, -+ {IPU_INSYS_RESP_TYPE_PIN_DATA_READY, -+ "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"}, -+ {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"}, -+ {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, -+ "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, -+ "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"}, -+ {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"}, -+}; -+ -+int isys_isr_one(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ struct ipu7_isys_stream *stream = NULL; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_isys_csi2 *csi2 = NULL; -+ struct ia_gofo_msg_err err_info; -+ struct ipu7_insys_resp *resp; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ struct ipu7_isys_tpg *tpg = NULL; -+#endif -+ u64 ts; -+ -+ if (!isys->adev->syscom) -+ return 1; -+ -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_isys_get_log(isys); -+#endif -+ -+ resp = ipu7_fw_isys_get_resp(isys); -+ if (!resp) -+ return 1; -+ if (resp->type >= N_IPU_INSYS_RESP_TYPE) { -+ dev_err(dev, "Unknown response type %u stream %u\n", -+ resp->type, resp->stream_id); -+ ipu7_fw_isys_put_resp(isys); -+ return 1; -+ } -+ -+ err_info = resp->error_info; -+ ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0]; -+ if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE && -+ err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) { -+ /* receive a sp w/o command, firmware drop it */ -+ dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n", -+ resp->type, is_fw_msg[resp->type].msg, -+ resp->stream_id); -+ dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n", -+ resp->pin_id, resp->buf_id, resp->frame_id); -+ dev_dbg(dev, "\terror group %u code %u details [%u %u]\n", -+ err_info.err_group, err_info.err_code, -+ err_info.err_detail[0], err_info.err_detail[1]); -+ } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) { -+ dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -+ resp->pin_id, resp->buf_id, resp->frame_id); -+ dev_err(dev, "\terror group %u code %u details [%u %u]\n", -+ err_info.err_group, err_info.err_code, -+ err_info.err_detail[0], err_info.err_detail[1]); -+ } else { -+ dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -+ resp->pin_id, resp->buf_id, resp->frame_id); -+ dev_dbg(dev, "\tts %llu\n", ts); -+ } -+ -+ if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) { -+ dev_err(dev, "bad stream handle %u\n", -+ resp->stream_id); -+ goto leave; -+ } -+ -+ stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id); -+ if (!stream) { -+ dev_err(dev, "stream of stream_handle %u is unused\n", -+ resp->stream_id); -+ goto leave; -+ } -+ -+ stream->error = err_info.err_code; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (stream->asd) { -+ if (stream->asd->is_tpg) -+ tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ else -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ } -+#else -+ if (stream->asd) -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+#endif -+ -+ switch (resp->type) { -+ case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE: -+ complete(&stream->stream_open_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK: -+ complete(&stream->stream_close_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: -+ complete(&stream->stream_start_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK: -+ complete(&stream->stream_stop_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK: -+ complete(&stream->stream_stop_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_PIN_DATA_READY: -+ /* -+ * firmware only release the capture msg until software -+ * get pin_data_ready event -+ */ -+ ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id); -+ if (resp->pin_id < IPU_INSYS_OUTPUT_PINS && -+ stream->output_pins[resp->pin_id].pin_ready) -+ stream->output_pins[resp->pin_id].pin_ready(stream, -+ resp); -+ else -+ dev_err(dev, "No handler for pin %u ready\n", -+ resp->pin_id); -+ if (csi2) -+ ipu7_isys_csi2_error(csi2); -+ -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK: -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: -+ case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE: -+ break; -+ case IPU_INSYS_RESP_TYPE_FRAME_SOF: -+ if (csi2) -+ ipu7_isys_csi2_sof_event_by_stream(stream); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (tpg) -+ ipu7_isys_tpg_sof_event_by_stream(stream); -+ -+#endif -+ stream->seq[stream->seq_index].sequence = -+ atomic_read(&stream->sequence) - 1U; -+ stream->seq[stream->seq_index].timestamp = ts; -+ dev_dbg(dev, -+ "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n", -+ resp->stream_id, resp->frame_id, -+ stream->seq[stream->seq_index].sequence, ts); -+ stream->seq_index = (stream->seq_index + 1U) -+ % IPU_ISYS_MAX_PARALLEL_SOF; -+ break; -+ case IPU_INSYS_RESP_TYPE_FRAME_EOF: -+ if (csi2) -+ ipu7_isys_csi2_eof_event_by_stream(stream); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (tpg) -+ ipu7_isys_tpg_eof_event_by_stream(stream); -+ -+#endif -+ dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n", -+ resp->stream_id, -+ stream->seq[stream->seq_index].sequence, ts); -+ break; -+ default: -+ dev_err(dev, "Unknown response type %u stream %u\n", -+ resp->type, resp->stream_id); -+ break; -+ } -+ -+ ipu7_isys_put_stream(stream); -+leave: -+ ipu7_fw_isys_put_resp(isys); -+ -+ return 0; -+} -+ -+static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2) -+{ -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct ipu7_device *isp = csi2->isys->adev->isp; -+ struct ipu7_isys_stream *s; -+ u32 sync, offset; -+ u32 fe = 0; -+ u8 vc; -+ -+ ipu7_isys_register_errors(csi2); -+ -+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -+ sync = readl(csi2->base + offset + IRQ_CTL_STATUS); -+ writel(sync, csi2->base + offset + IRQ_CTL_CLEAR); -+ dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync); -+ -+ if (!is_ipu7(isp->hw_ver)) { -+ fe = readl(csi2->base + offset + IRQ1_CTL_STATUS); -+ writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR); -+ dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe); -+ } -+ -+ for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) { -+ s = ipu7_isys_query_stream_by_source(csi2->isys, -+ csi2->asd.source, vc); -+ if (!s) -+ continue; -+ -+ if (!is_ipu7(isp->hw_ver)) { -+ if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc)) -+ ipu7_isys_csi2_sof_event_by_stream(s); -+ -+ if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc)) -+ ipu7_isys_csi2_eof_event_by_stream(s); -+ } else { -+ if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2))) -+ ipu7_isys_csi2_sof_event_by_stream(s); -+ -+ if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2))) -+ ipu7_isys_csi2_eof_event_by_stream(s); -+ } -+ } -+} -+ -+static irqreturn_t isys_isr(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ u32 status_csi, status_sw, csi_offset, sw_offset; -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *base = isys->pdata->base; -+ -+ spin_lock(&isys->power_lock); -+ if (!isys->power) { -+ spin_unlock(&isys->power_lock); -+ return IRQ_NONE; -+ } -+ -+ csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -+ sw_offset = IS_BASE; -+ -+ status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -+ status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -+ if (!status_csi && !status_sw) { -+ spin_unlock(&isys->power_lock); -+ return IRQ_NONE; -+ } -+ -+ if (status_csi) -+ dev_dbg(dev, "status csi 0x%08x\n", status_csi); -+ if (status_sw) -+ dev_dbg(dev, "status to_sw 0x%08x\n", status_sw); -+ -+ do { -+ writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR); -+ writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR); -+ -+ if (isys->isr_csi2_mask & status_csi) { -+ unsigned int i; -+ -+ for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { -+ /* irq from not enabled port */ -+ if (!isys->csi2[i].base) -+ continue; -+ if (status_csi & isys->csi2[i].legacy_irq_mask) -+ ipu7_isys_csi2_isr(&isys->csi2[i]); -+ } -+ } -+ -+ if (!isys_isr_one(adev)) -+ status_sw = TO_SW_IRQ_FW; -+ else -+ status_sw = 0; -+ -+ status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -+ status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -+ } while ((status_csi & isys->isr_csi2_mask) || -+ (status_sw & TO_SW_IRQ_FW)); -+ -+ writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N); -+ -+ spin_unlock(&isys->power_lock); -+ -+ return IRQ_HANDLED; -+} -+ -+static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = { -+ .isr = isys_isr, -+ .isr_threaded = NULL, -+ .wake_isr_thread = false, -+}; -+ -+static const struct auxiliary_device_id ipu7_isys_id_table[] = { -+ { -+ .name = "intel_ipu7.isys", -+ .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data, -+ }, -+ { } -+}; -+MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table); -+ -+static struct auxiliary_driver isys_driver = { -+ .name = IPU_ISYS_NAME, -+ .probe = isys_probe, -+ .remove = isys_remove, -+ .id_table = ipu7_isys_id_table, -+ .driver = { -+ .pm = &isys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(isys_driver); -+ -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Tianshu Qiu "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 input system driver"); -+MODULE_IMPORT_NS("INTEL_IPU7"); -+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.h b/drivers/media/pci/intel/ipu7/ipu7-isys.h -new file mode 100644 -index 000000000000..c9ca2fbb4d1e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys.h -@@ -0,0 +1,187 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_H -+#define IPU7_ISYS_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_msg_abi.h" -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-isys-csi2.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+#include "ipu7-isys-tpg.h" -+#endif -+#include "ipu7-isys-video.h" -+ -+#ifdef CONFIG_DEBUG_FS -+struct dentry; -+ -+#endif -+#define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" -+ -+/* FW support max 16 streams */ -+#define IPU_ISYS_MAX_STREAMS 16U -+ -+/* -+ * Current message queue configuration. These must be big enough -+ * so that they never gets full. Queues are located in system memory -+ */ -+#define IPU_ISYS_SIZE_RECV_QUEUE 40U -+#define IPU_ISYS_SIZE_LOG_QUEUE 256U -+#define IPU_ISYS_SIZE_SEND_QUEUE 40U -+#define IPU_ISYS_NUM_RECV_QUEUE 1U -+ -+#define IPU_ISYS_MIN_WIDTH 2U -+#define IPU_ISYS_MIN_HEIGHT 2U -+#define IPU_ISYS_MAX_WIDTH 8160U -+#define IPU_ISYS_MAX_HEIGHT 8190U -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define RESET_STATE_IN_RESET 1U -+#define RESET_STATE_IN_STOP_STREAMING 2U -+ -+#endif -+#define FW_CALL_TIMEOUT_JIFFIES \ -+ msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) -+#endif -+ -+struct isys_fw_log { -+ struct mutex mutex; /* protect whole struct */ -+ void *head; -+ void *addr; -+ u32 count; /* running counter of log */ -+ u32 size; /* actual size of log content, in bits */ -+}; -+ -+/* -+ * struct ipu7_isys -+ * -+ * @media_dev: Media device -+ * @v4l2_dev: V4L2 device -+ * @adev: ISYS bus device -+ * @power: Is ISYS powered on or not? -+ * @isr_bits: Which bits does the ISR handle? -+ * @power_lock: Serialise access to power (power state in general) -+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers -+ * @streams_lock: serialise access to streams -+ * @streams: streams per firmware stream ID -+ * @syscom: fw communication layer context -+ #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ * @need_reset: Isys requires d0i0->i3 transition -+ #endif -+ * @ref_count: total number of callers fw open -+ * @mutex: serialise access isys video open/release related operations -+ * @stream_mutex: serialise stream start and stop, queueing requests -+ * @pdata: platform data pointer -+ * @csi2: CSI-2 receivers -+ #ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ * @tpg: test pattern generators -+ #endif -+ */ -+struct ipu7_isys { -+ struct media_device media_dev; -+ struct v4l2_device v4l2_dev; -+ struct ipu7_bus_device *adev; -+ -+ int power; -+ spinlock_t power_lock; /* Serialise access to power */ -+ u32 isr_csi2_mask; -+ u32 csi2_rx_ctrl_cached; -+ spinlock_t streams_lock; -+ struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS]; -+ int streams_ref_count[IPU_ISYS_MAX_STREAMS]; -+ u32 phy_rext_cal; -+ bool icache_prefetch; -+ bool csi2_cse_ipc_not_supported; -+ unsigned int ref_count; -+ unsigned int stream_opened; -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif -+ struct mutex mutex; /* Serialise isys video open/release related */ -+ struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ -+ -+ struct ipu7_isys_pdata *pdata; -+ -+ struct ipu7_isys_csi2 *csi2; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ struct ipu7_isys_tpg *tpg; -+#endif -+ struct isys_fw_log *fw_log; -+ -+ struct list_head requests; -+ struct pm_qos_request pm_qos; -+ spinlock_t listlock; /* Protect framebuflist */ -+ struct list_head framebuflist; -+ struct list_head framebuflist_fw; -+ struct v4l2_async_notifier notifier; -+ -+ struct ipu7_insys_config *subsys_config; -+ dma_addr_t subsys_config_dma_addr; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct mutex reset_mutex; -+ bool need_reset; -+ int state; -+#endif -+}; -+ -+struct isys_fw_msgs { -+ union { -+ u64 dummy; -+ struct ipu7_insys_buffset frame; -+ struct ipu7_insys_stream_cfg stream; -+ } fw_msg; -+ struct list_head head; -+ dma_addr_t dma_addr; -+}; -+ -+struct ipu7_isys_csi2_config { -+ unsigned int nlanes; -+ unsigned int port; -+ enum v4l2_mbus_type bus_type; -+}; -+ -+struct ipu7_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu7_isys_subdev_info { -+ struct ipu7_isys_csi2_config *csi2; -+ struct ipu7_isys_subdev_i2c_info i2c; -+}; -+ -+struct ipu7_isys_subdev_pdata { -+ struct ipu7_isys_subdev_info **subdevs; -+}; -+ -+struct sensor_async_sd { -+ struct v4l2_async_connection asc; -+ struct ipu7_isys_csi2_config csi2; -+}; -+ -+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream); -+void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data); -+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys); -+int isys_isr_one(struct ipu7_bus_device *adev); -+void ipu7_isys_setup_hw(struct ipu7_isys *isys); -+#endif /* IPU7_ISYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.c b/drivers/media/pci/intel/ipu7/ipu7-mmu.c -new file mode 100644 -index 000000000000..e6989e3e59a1 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-mmu.c -@@ -0,0 +1,854 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-dma.h" -+#include "ipu7-mmu.h" -+#include "ipu7-platform-regs.h" -+ -+#define ISP_PAGE_SHIFT 12 -+#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) -+#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1U)) -+ -+#define ISP_L1PT_SHIFT 22 -+#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) -+ -+#define ISP_L2PT_SHIFT 12 -+#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) -+ -+#define ISP_L1PT_PTES 1024U -+#define ISP_L2PT_PTES 1024U -+ -+#define ISP_PADDR_SHIFT 12 -+ -+#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ -+#define REG_INFO 0x0008 -+ -+#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) -+ -+#define MMU_TLB_INVALIDATE_TIMEOUT 2000 -+ -+static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) -+{ -+ unsigned int i; -+ u32 irq_cause; -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ irq_cause = readl(mmu->mmu_hw[i].base + MMU_REG_IRQ_CAUSE); -+ pr_info("mmu %s irq_cause = 0x%x", mmu->mmu_hw[i].name, -+ irq_cause); -+ writel(0x1ffff, mmu->mmu_hw[i].base + MMU_REG_IRQ_CLEAR); -+ } -+} -+ -+static void tlb_invalidate(struct ipu7_mmu *mmu) -+{ -+ unsigned long flags; -+ unsigned int i; -+ int ret; -+ u32 val; -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ if (!mmu->ready) { -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+ return; -+ } -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ writel(0xffffffffU, mmu->mmu_hw[i].base + -+ MMU_REG_INVALIDATE_0); -+ -+ /* Need check with HW, use l1streams or l2streams */ -+ if (mmu->mmu_hw[i].nr_l2streams > 32) -+ writel(0xffffffffU, mmu->mmu_hw[i].base + -+ MMU_REG_INVALIDATE_1); -+ -+ /* -+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles) -+ * When the actual MMIO write reaches the IPU TLB Invalidate -+ * register, wmb() will force the TLB invalidate out if the CPU -+ * attempts to update the IOMMU page table (or sooner). -+ */ -+ wmb(); -+ -+ /* wait invalidation done */ -+ ret = readl_poll_timeout_atomic(mmu->mmu_hw[i].base + -+ MMU_REG_INVALIDATION_STATUS, -+ val, !(val & 0x1U), 500, -+ MMU_TLB_INVALIDATE_TIMEOUT); -+ if (ret) -+ dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i); -+ } -+ -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+} -+ -+static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr) -+{ -+ dma_addr_t dma; -+ -+ dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); -+ if (dma_mapping_error(mmu_info->dev, dma)) -+ return 0; -+ -+ return dma; -+} -+ -+static int get_dummy_page(struct ipu7_mmu_info *mmu_info) -+{ -+ void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ -+ if (!pt) -+ return -ENOMEM; -+ -+ dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map dummy page\n"); -+ goto err_free_page; -+ } -+ -+ mmu_info->dummy_page = pt; -+ mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; -+ -+ return 0; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return -ENOMEM; -+} -+ -+static void free_dummy_page(struct ipu7_mmu_info *mmu_info) -+{ -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_page); -+} -+ -+static int alloc_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ unsigned int i; -+ -+ if (!pt) -+ return -ENOMEM; -+ -+ dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l2pt page\n"); -+ goto err_free_page; -+ } -+ -+ for (i = 0; i < ISP_L2PT_PTES; i++) -+ pt[i] = mmu_info->dummy_page_pteval; -+ -+ mmu_info->dummy_l2_pt = pt; -+ mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; -+ -+ return 0; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return -ENOMEM; -+} -+ -+static void free_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_l2_pt); -+} -+ -+static u32 *alloc_l1_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ unsigned int i; -+ -+ if (!pt) -+ return NULL; -+ -+ dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); -+ -+ for (i = 0; i < ISP_L1PT_PTES; i++) -+ pt[i] = mmu_info->dummy_l2_pteval; -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l1pt page\n"); -+ goto err_free_page; -+ } -+ -+ mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; -+ dev_dbg(mmu_info->dev, "l1 pt %p mapped at %pad\n", pt, &dma); -+ -+ return pt; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return NULL; -+} -+ -+static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ unsigned int i; -+ -+ if (!pt) -+ return NULL; -+ -+ dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); -+ -+ for (i = 0; i < ISP_L1PT_PTES; i++) -+ pt[i] = mmu_info->dummy_page_pteval; -+ -+ return pt; -+} -+ -+static void l2_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t dummy, size_t size) -+{ -+ unsigned int l2_entries; -+ unsigned int l2_idx; -+ unsigned long flags; -+ u32 l1_idx; -+ u32 *l2_pt; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ for (l1_idx = iova >> ISP_L1PT_SHIFT; -+ size > 0U && l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ dev_dbg(mmu_info->dev, -+ "unmapping l2 pgtable (l1 index %u (iova 0x%8.8lx))\n", -+ l1_idx, iova); -+ -+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { -+ dev_err(mmu_info->dev, -+ "unmap not mapped iova 0x%8.8lx l1 index %u\n", -+ iova, l1_idx); -+ continue; -+ } -+ l2_pt = mmu_info->l2_pts[l1_idx]; -+ -+ l2_entries = 0; -+ for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -+ size > 0U && l2_idx < ISP_L2PT_PTES; l2_idx++) { -+ phys_addr_t pteval = TBL_PHYS_ADDR(l2_pt[l2_idx]); -+ -+ dev_dbg(mmu_info->dev, -+ "unmap l2 index %u with pteval 0x%p\n", -+ l2_idx, &pteval); -+ l2_pt[l2_idx] = mmu_info->dummy_page_pteval; -+ -+ iova += ISP_PAGE_SIZE; -+ size -= ISP_PAGE_SIZE; -+ -+ l2_entries++; -+ } -+ -+ WARN_ON_ONCE(!l2_entries); -+ clflush_cache_range(&l2_pt[l2_idx - l2_entries], -+ sizeof(l2_pt[0]) * l2_entries); -+ } -+ -+ WARN_ON_ONCE(size); -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+} -+ -+static int l2_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ struct device *dev = mmu_info->dev; -+ unsigned int l2_entries; -+ u32 *l2_pt, *l2_virt; -+ unsigned int l2_idx; -+ unsigned long flags; -+ size_t mapped = 0; -+ dma_addr_t dma; -+ u32 l1_entry; -+ u32 l1_idx; -+ int err = 0; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ -+ paddr = ALIGN(paddr, ISP_PAGE_SIZE); -+ for (l1_idx = iova >> ISP_L1PT_SHIFT; -+ size && l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ dev_dbg(dev, -+ "mapping l2 page table for l1 index %u (iova %8.8x)\n", -+ l1_idx, (u32)iova); -+ -+ l1_entry = mmu_info->l1_pt[l1_idx]; -+ if (l1_entry == mmu_info->dummy_l2_pteval) { -+ l2_virt = mmu_info->l2_pts[l1_idx]; -+ if (likely(!l2_virt)) { -+ l2_virt = alloc_l2_pt(mmu_info); -+ if (!l2_virt) { -+ err = -ENOMEM; -+ goto error; -+ } -+ } -+ -+ dma = map_single(mmu_info, l2_virt); -+ if (!dma) { -+ dev_err(dev, "Failed to map l2pt page\n"); -+ free_page((unsigned long)l2_virt); -+ err = -EINVAL; -+ goto error; -+ } -+ -+ l1_entry = dma >> ISP_PADDR_SHIFT; -+ -+ dev_dbg(dev, "page for l1_idx %u %p allocated\n", -+ l1_idx, l2_virt); -+ mmu_info->l1_pt[l1_idx] = l1_entry; -+ mmu_info->l2_pts[l1_idx] = l2_virt; -+ -+ clflush_cache_range(&mmu_info->l1_pt[l1_idx], -+ sizeof(mmu_info->l1_pt[l1_idx])); -+ } -+ -+ l2_pt = mmu_info->l2_pts[l1_idx]; -+ l2_entries = 0; -+ -+ for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -+ size && l2_idx < ISP_L2PT_PTES; l2_idx++) { -+ l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; -+ -+ dev_dbg(dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, -+ l2_pt[l2_idx]); -+ -+ iova += ISP_PAGE_SIZE; -+ paddr += ISP_PAGE_SIZE; -+ mapped += ISP_PAGE_SIZE; -+ size -= ISP_PAGE_SIZE; -+ -+ l2_entries++; -+ } -+ -+ WARN_ON_ONCE(!l2_entries); -+ clflush_cache_range(&l2_pt[l2_idx - l2_entries], -+ sizeof(l2_pt[0]) * l2_entries); -+ } -+ -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ return 0; -+ -+error: -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ /* unroll mapping in case something went wrong */ -+ if (mapped) -+ l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped); -+ -+ return err; -+} -+ -+static int __ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ u32 iova_start = round_down(iova, ISP_PAGE_SIZE); -+ u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); -+ -+ dev_dbg(mmu_info->dev, -+ "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr %pap\n", -+ iova_start, iova_end, size, &paddr); -+ -+ return l2_map(mmu_info, iova_start, paddr, size); -+} -+ -+static void __ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, -+ unsigned long iova, size_t size) -+{ -+ l2_unmap(mmu_info, iova, 0, size); -+} -+ -+static int allocate_trash_buffer(struct ipu7_mmu *mmu) -+{ -+ unsigned int n_pages = PFN_UP(IPU_MMUV2_TRASH_RANGE); -+ unsigned long iova_addr; -+ struct iova *iova; -+ unsigned int i; -+ dma_addr_t dma; -+ int ret; -+ -+ /* Allocate 8MB in iova range */ -+ iova = alloc_iova(&mmu->dmap->iovad, n_pages, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -+ if (!iova) { -+ dev_err(mmu->dev, "cannot allocate iova range for trash\n"); -+ return -ENOMEM; -+ } -+ -+ dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { -+ dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); -+ ret = -ENOMEM; -+ goto out_free_iova; -+ } -+ -+ mmu->pci_trash_page = dma; -+ -+ /* -+ * Map the 8MB iova address range to the same physical trash page -+ * mmu->trash_page which is already reserved at the probe -+ */ -+ iova_addr = iova->pfn_lo; -+ for (i = 0; i < n_pages; i++) { -+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -+ mmu->pci_trash_page, PAGE_SIZE); -+ if (ret) { -+ dev_err(mmu->dev, -+ "mapping trash buffer range failed\n"); -+ goto out_unmap; -+ } -+ -+ iova_addr++; -+ } -+ -+ mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); -+ dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", -+ mmu->mmid, (unsigned int)mmu->iova_trash_page); -+ return 0; -+ -+out_unmap: -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+out_free_iova: -+ __free_iova(&mmu->dmap->iovad, iova); -+ return ret; -+} -+ -+static void __mmu_at_init(struct ipu7_mmu *mmu) -+{ -+ struct ipu7_mmu_info *mmu_info; -+ unsigned int i; -+ -+ mmu_info = mmu->dmap->mmu_info; -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -+ unsigned int j; -+ -+ /* Write page table address per MMU */ -+ writel((phys_addr_t)mmu_info->l1_pt_dma, -+ mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR); -+ dev_dbg(mmu->dev, "mmu %s base was set as %x\n", mmu_hw->name, -+ readl(mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR)); -+ -+ /* Set info bits and axi_refill per MMU */ -+ writel(mmu_hw->info_bits, -+ mmu_hw->base + MMU_REG_USER_INFO_BITS); -+ writel(mmu_hw->refill, mmu_hw->base + MMU_REG_AXI_REFILL_IF_ID); -+ writel(mmu_hw->collapse_en_bitmap, -+ mmu_hw->base + MMU_REG_COLLAPSE_ENABLE_BITMAP); -+ -+ dev_dbg(mmu->dev, "mmu %s info_bits was set as %x\n", -+ mmu_hw->name, -+ readl(mmu_hw->base + MMU_REG_USER_INFO_BITS)); -+ -+ if (mmu_hw->at_sp_arb_cfg) -+ writel(mmu_hw->at_sp_arb_cfg, -+ mmu_hw->base + MMU_REG_AT_SP_ARB_CFG); -+ -+ /* default irq configuration */ -+ writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_MASK); -+ writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_ENABLE); -+ -+ /* Configure MMU TLB stream configuration for L1/L2 */ -+ for (j = 0; j < mmu_hw->nr_l1streams; j++) { -+ writel(mmu_hw->l1_block_sz[j], mmu_hw->base + -+ mmu_hw->l1_block + 4U * j); -+ } -+ -+ for (j = 0; j < mmu_hw->nr_l2streams; j++) { -+ writel(mmu_hw->l2_block_sz[j], mmu_hw->base + -+ mmu_hw->l2_block + 4U * j); -+ } -+ -+ for (j = 0; j < mmu_hw->uao_p_num; j++) { -+ if (!mmu_hw->uao_p2tlb[j]) -+ continue; -+ writel(mmu_hw->uao_p2tlb[j], mmu_hw->uao_base + 4U * j); -+ } -+ } -+} -+ -+static void __mmu_zlx_init(struct ipu7_mmu *mmu) -+{ -+ unsigned int i; -+ -+ dev_dbg(mmu->dev, "mmu zlx init\n"); -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -+ unsigned int j; -+ -+ dev_dbg(mmu->dev, "mmu %s zlx init\n", mmu_hw->name); -+ for (j = 0; j < IPU_ZLX_POOL_NUM; j++) { -+ if (!mmu_hw->zlx_axi_pool[j]) -+ continue; -+ writel(mmu_hw->zlx_axi_pool[j], -+ mmu_hw->zlx_base + ZLX_REG_AXI_POOL + j * 0x4U); -+ } -+ -+ for (j = 0; j < mmu_hw->zlx_nr; j++) { -+ if (!mmu_hw->zlx_conf[j]) -+ continue; -+ -+ writel(mmu_hw->zlx_conf[j], -+ mmu_hw->zlx_base + ZLX_REG_CONF + j * 0x8U); -+ } -+ -+ for (j = 0; j < mmu_hw->zlx_nr; j++) { -+ if (!mmu_hw->zlx_en[j]) -+ continue; -+ -+ writel(mmu_hw->zlx_en[j], -+ mmu_hw->zlx_base + ZLX_REG_EN + j * 0x8U); -+ } -+ } -+} -+ -+int ipu7_mmu_hw_init(struct ipu7_mmu *mmu) -+{ -+ unsigned long flags; -+ -+ dev_dbg(mmu->dev, "IPU mmu hardware init\n"); -+ -+ /* Initialise the each MMU and ZLX */ -+ __mmu_at_init(mmu); -+ __mmu_zlx_init(mmu); -+ -+ if (!mmu->trash_page) { -+ int ret; -+ -+ mmu->trash_page = alloc_page(GFP_KERNEL); -+ if (!mmu->trash_page) { -+ dev_err(mmu->dev, "insufficient memory for trash buffer\n"); -+ return -ENOMEM; -+ } -+ -+ ret = allocate_trash_buffer(mmu); -+ if (ret) { -+ __free_page(mmu->trash_page); -+ mmu->trash_page = NULL; -+ dev_err(mmu->dev, "trash buffer allocation failed\n"); -+ return ret; -+ } -+ } -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ mmu->ready = true; -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_init, "INTEL_IPU7"); -+ -+static struct ipu7_mmu_info *ipu7_mmu_alloc(struct ipu7_device *isp) -+{ -+ struct ipu7_mmu_info *mmu_info; -+ int ret; -+ -+ mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); -+ if (!mmu_info) -+ return NULL; -+ -+ if (isp->secure_mode) { -+ mmu_info->aperture_start = IPU_FW_CODE_REGION_END; -+ mmu_info->aperture_end = -+ (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS); -+ } else { -+ mmu_info->aperture_start = IPU_FW_CODE_REGION_START; -+ mmu_info->aperture_end = -+ (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS_NON_SECURE); -+ } -+ -+ mmu_info->pgsize_bitmap = SZ_4K; -+ mmu_info->dev = &isp->pdev->dev; -+ -+ ret = get_dummy_page(mmu_info); -+ if (ret) -+ goto err_free_info; -+ -+ ret = alloc_dummy_l2_pt(mmu_info); -+ if (ret) -+ goto err_free_dummy_page; -+ -+ mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); -+ if (!mmu_info->l2_pts) -+ goto err_free_dummy_l2_pt; -+ -+ /* -+ * We always map the L1 page table (a single page as well as -+ * the L2 page tables). -+ */ -+ mmu_info->l1_pt = alloc_l1_pt(mmu_info); -+ if (!mmu_info->l1_pt) -+ goto err_free_l2_pts; -+ -+ spin_lock_init(&mmu_info->lock); -+ -+ dev_dbg(mmu_info->dev, "domain initialised\n"); -+ -+ return mmu_info; -+ -+err_free_l2_pts: -+ vfree(mmu_info->l2_pts); -+err_free_dummy_l2_pt: -+ free_dummy_l2_pt(mmu_info); -+err_free_dummy_page: -+ free_dummy_page(mmu_info); -+err_free_info: -+ kfree(mmu_info); -+ -+ return NULL; -+} -+ -+void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ mmu->ready = false; -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_cleanup, "INTEL_IPU7"); -+ -+static struct ipu7_dma_mapping *alloc_dma_mapping(struct ipu7_device *isp) -+{ -+ struct ipu7_dma_mapping *dmap; -+ unsigned long base_pfn; -+ -+ dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); -+ if (!dmap) -+ return NULL; -+ -+ dmap->mmu_info = ipu7_mmu_alloc(isp); -+ if (!dmap->mmu_info) { -+ kfree(dmap); -+ return NULL; -+ } -+ -+ /* 0~64M is forbidden for uctile controller */ -+ base_pfn = max_t(unsigned long, 1, -+ PFN_DOWN(dmap->mmu_info->aperture_start)); -+ init_iova_domain(&dmap->iovad, SZ_4K, base_pfn); -+ dmap->mmu_info->dmap = dmap; -+ -+ dev_dbg(&isp->pdev->dev, "alloc mapping\n"); -+ -+ iova_cache_get(); -+ -+ return dmap; -+} -+ -+phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -+ dma_addr_t iova) -+{ -+ phys_addr_t phy_addr; -+ unsigned long flags; -+ u32 *l2_pt; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; -+ phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; -+ phy_addr <<= ISP_PAGE_SHIFT; -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ return phy_addr; -+} -+ -+void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ size_t size) -+{ -+ unsigned int min_pagesz; -+ -+ dev_dbg(mmu_info->dev, "unmapping iova 0x%lx size 0x%zx\n", iova, size); -+ -+ /* find out the minimum page size supported */ -+ min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -+ -+ /* -+ * The virtual address and the size of the mapping must be -+ * aligned (at least) to the size of the smallest page supported -+ * by the hardware -+ */ -+ if (!IS_ALIGNED(iova | size, min_pagesz)) { -+ dev_err(mmu_info->dev, -+ "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", -+ iova, size, min_pagesz); -+ return; -+ } -+ -+ __ipu7_mmu_unmap(mmu_info, iova, size); -+} -+ -+int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ unsigned int min_pagesz; -+ -+ if (mmu_info->pgsize_bitmap == 0UL) -+ return -ENODEV; -+ -+ /* find out the minimum page size supported */ -+ min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -+ -+ /* -+ * both the virtual address and the physical one, as well as -+ * the size of the mapping, must be aligned (at least) to the -+ * size of the smallest page supported by the hardware -+ */ -+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { -+ dev_err(mmu_info->dev, -+ "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", -+ iova, &paddr, size, min_pagesz); -+ return -EINVAL; -+ } -+ -+ dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", -+ iova, &paddr, size); -+ -+ return __ipu7_mmu_map(mmu_info, iova, paddr, size); -+} -+ -+static void ipu7_mmu_destroy(struct ipu7_mmu *mmu) -+{ -+ struct ipu7_dma_mapping *dmap = mmu->dmap; -+ struct ipu7_mmu_info *mmu_info = dmap->mmu_info; -+ struct iova *iova; -+ u32 l1_idx; -+ -+ if (mmu->iova_trash_page) { -+ iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); -+ if (iova) { -+ /* unmap and free the trash buffer iova */ -+ ipu7_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ __free_iova(&dmap->iovad, iova); -+ } else { -+ dev_err(mmu->dev, "trash buffer iova not found.\n"); -+ } -+ -+ mmu->iova_trash_page = 0; -+ dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ mmu->pci_trash_page = 0; -+ __free_page(mmu->trash_page); -+ } -+ -+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->l2_pts[l1_idx]); -+ } -+ } -+ -+ vfree(mmu_info->l2_pts); -+ free_dummy_page(mmu_info); -+ dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_l2_pt); -+ free_page((unsigned long)mmu_info->l1_pt); -+ kfree(mmu_info); -+} -+ -+struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -+ void __iomem *base, int mmid, -+ const struct ipu7_hw_variants *hw) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(to_pci_dev(dev)); -+ struct ipu7_mmu_pdata *pdata; -+ struct ipu7_mmu *mmu; -+ unsigned int i; -+ -+ if (hw->nr_mmus > IPU_MMU_MAX_NUM) -+ return ERR_PTR(-EINVAL); -+ -+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ for (i = 0; i < hw->nr_mmus; i++) { -+ struct ipu7_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; -+ const struct ipu7_mmu_hw *src_mmu = &hw->mmu_hw[i]; -+ -+ if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS || -+ src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS) -+ return ERR_PTR(-EINVAL); -+ -+ *pdata_mmu = *src_mmu; -+ pdata_mmu->base = base + src_mmu->offset; -+ pdata_mmu->zlx_base = base + src_mmu->zlx_offset; -+ pdata_mmu->uao_base = base + src_mmu->uao_offset; -+ } -+ -+ mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); -+ if (!mmu) -+ return ERR_PTR(-ENOMEM); -+ -+ mmu->mmid = mmid; -+ mmu->mmu_hw = pdata->mmu_hw; -+ mmu->nr_mmus = hw->nr_mmus; -+ mmu->tlb_invalidate = tlb_invalidate; -+ mmu->ready = false; -+ INIT_LIST_HEAD(&mmu->vma_list); -+ spin_lock_init(&mmu->ready_lock); -+ -+ mmu->dmap = alloc_dma_mapping(isp); -+ if (!mmu->dmap) { -+ dev_err(dev, "can't alloc dma mapping\n"); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ return mmu; -+} -+ -+void ipu7_mmu_cleanup(struct ipu7_mmu *mmu) -+{ -+ struct ipu7_dma_mapping *dmap = mmu->dmap; -+ -+ ipu7_mmu_destroy(mmu); -+ mmu->dmap = NULL; -+ iova_cache_put(); -+ put_iova_domain(&dmap->iovad); -+ kfree(dmap); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.h b/drivers/media/pci/intel/ipu7/ipu7-mmu.h -new file mode 100644 -index 000000000000..d85bb8ffc711 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-mmu.h -@@ -0,0 +1,414 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_MMU_H -+#define IPU7_MMU_H -+ -+#include -+#include -+#include -+#include -+ -+struct device; -+struct page; -+struct ipu7_hw_variants; -+struct ipu7_mmu; -+struct ipu7_mmu_info; -+ -+#define ISYS_MMID 0x1 -+#define PSYS_MMID 0x0 -+ -+/* IPU7 for LNL */ -+/* IS MMU Cmd RD */ -+#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 -+#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3 -+#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Cmd WR */ -+#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000 -+#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3 -+#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Data WR Snoop */ -+#define IPU7_IS_MMU_M0_OFFSET 0x276000 -+#define IPU7_IS_MMU_M0_STREAM_NUM 8 -+#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74 -+ -+/* IS MMU Data WR ISOC */ -+#define IPU7_IS_MMU_M1_OFFSET 0x277000 -+#define IPU7_IS_MMU_M1_STREAM_NUM 16 -+#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -+ -+/* PS MMU FW RD */ -+#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000 -+#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20 -+#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4 -+ -+/* PS MMU FW WR */ -+#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000 -+#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10 -+#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -+ -+/* PS MMU FW Data RD VC0 */ -+#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000 -+#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40 -+#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4 -+ -+/* PS MMU FW Data WR VC0 */ -+#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000 -+#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40 -+#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4 -+ -+/* IS UAO UC RD */ -+#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000 -+#define IPU7_IS_UAO_UC_RD_PLANENUM 4 -+ -+/* IS UAO UC WR */ -+#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000 -+#define IPU7_IS_UAO_UC_WR_PLANENUM 4 -+ -+/* IS UAO M0 WR */ -+#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000 -+#define IPU7_IS_UAO_M0_WR_PLANENUM 8 -+ -+/* IS UAO M1 WR */ -+#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000 -+#define IPU7_IS_UAO_M1_WR_PLANENUM 16 -+ -+/* PS UAO FW RD */ -+#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000 -+#define IPU7_PS_UAO_FW_RD_PLANENUM 20 -+ -+/* PS UAO FW WR */ -+#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000 -+#define IPU7_PS_UAO_FW_WR_PLANENUM 16 -+ -+/* PS UAO SRT RD */ -+#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000 -+#define IPU7_PS_UAO_SRT_RD_PLANENUM 40 -+ -+/* PS UAO SRT WR */ -+#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000 -+#define IPU7_PS_UAO_SRT_WR_PLANENUM 40 -+ -+#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000 -+#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000 -+#define IPU7_IS_ZLX_M0_OFFSET 0x27a000 -+#define IPU7_IS_ZLX_M1_OFFSET 0x27b000 -+#define IPU7_IS_ZLX_UC_RD_NUM 4 -+#define IPU7_IS_ZLX_UC_WR_NUM 4 -+#define IPU7_IS_ZLX_M0_NUM 8 -+#define IPU7_IS_ZLX_M1_NUM 16 -+ -+#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000 -+#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000 -+#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000 -+#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000 -+#define IPU7_PS_ZLX_DATA_RD_NUM 32 -+#define IPU7_PS_ZLX_DATA_WR_NUM 32 -+#define IPU7_PS_ZLX_FW_RD_NUM 16 -+#define IPU7_PS_ZLX_FW_WR_NUM 10 -+ -+/* IPU7P5 for PTL */ -+/* IS MMU Cmd RD */ -+#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000 -+#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3 -+#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Cmd WR */ -+#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000 -+#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3 -+#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Data WR Snoop */ -+#define IPU7P5_IS_MMU_M0_OFFSET 0x276000 -+#define IPU7P5_IS_MMU_M0_STREAM_NUM 16 -+#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -+ -+/* IS MMU Data WR ISOC */ -+#define IPU7P5_IS_MMU_M1_OFFSET 0x277000 -+#define IPU7P5_IS_MMU_M1_STREAM_NUM 16 -+#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -+ -+/* PS MMU FW RD */ -+#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000 -+#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16 -+#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94 -+ -+/* PS MMU FW WR */ -+#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000 -+#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10 -+#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -+ -+/* PS MMU FW Data RD VC0 */ -+#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000 -+#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22 -+#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac -+ -+/* PS MMU FW Data WR VC0 */ -+#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000 -+#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32 -+#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4 -+ -+/* IS UAO UC RD */ -+#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000 -+#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4 -+ -+/* IS UAO UC WR */ -+#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000 -+#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4 -+ -+/* IS UAO M0 WR */ -+#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000 -+#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16 -+ -+/* IS UAO M1 WR */ -+#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000 -+#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16 -+ -+/* PS UAO FW RD */ -+#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000 -+#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16 -+ -+/* PS UAO FW WR */ -+#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000 -+#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10 -+ -+/* PS UAO SRT RD */ -+#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000 -+#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22 -+ -+/* PS UAO SRT WR */ -+#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000 -+#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32 -+ -+#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000 -+#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000 -+#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000 -+#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000 -+#define IPU7P5_IS_ZLX_UC_RD_NUM 4 -+#define IPU7P5_IS_ZLX_UC_WR_NUM 4 -+#define IPU7P5_IS_ZLX_M0_NUM 16 -+#define IPU7P5_IS_ZLX_M1_NUM 16 -+ -+#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000 -+#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000 -+#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000 -+#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000 -+#define IPU7P5_PS_ZLX_DATA_RD_NUM 22 -+#define IPU7P5_PS_ZLX_DATA_WR_NUM 32 -+#define IPU7P5_PS_ZLX_FW_RD_NUM 16 -+#define IPU7P5_PS_ZLX_FW_WR_NUM 10 -+ -+/* IS MMU Cmd RD */ -+#define IPU8_IS_MMU_FW_RD_OFFSET 0x270000 -+#define IPU8_IS_MMU_FW_RD_STREAM_NUM 3 -+#define IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Cmd WR */ -+#define IPU8_IS_MMU_FW_WR_OFFSET 0x271000 -+#define IPU8_IS_MMU_FW_WR_STREAM_NUM 3 -+#define IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Data WR Snoop */ -+#define IPU8_IS_MMU_M0_OFFSET 0x272000 -+#define IPU8_IS_MMU_M0_STREAM_NUM 16 -+#define IPU8_IS_MMU_M0_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -+ -+/* IS MMU Data WR ISOC */ -+#define IPU8_IS_MMU_M1_OFFSET 0x273000 -+#define IPU8_IS_MMU_M1_STREAM_NUM 16 -+#define IPU8_IS_MMU_M1_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -+ -+/* IS MMU UPIPE ISOC */ -+#define IPU8_IS_MMU_UPIPE_OFFSET 0x274000 -+#define IPU8_IS_MMU_UPIPE_STREAM_NUM 6 -+#define IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG 0x6c -+ -+/* PS MMU FW RD */ -+#define IPU8_PS_MMU_FW_RD_OFFSET 0x148000 -+#define IPU8_PS_MMU_FW_RD_STREAM_NUM 12 -+#define IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x84 -+ -+/* PS MMU FW WR */ -+#define IPU8_PS_MMU_FW_WR_OFFSET 0x149000 -+#define IPU8_PS_MMU_FW_WR_STREAM_NUM 8 -+#define IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x74 -+ -+/* PS MMU FW Data RD VC0 */ -+#define IPU8_PS_MMU_SRT_RD_OFFSET 0x14a000 -+#define IPU8_PS_MMU_SRT_RD_STREAM_NUM 26 -+#define IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xbc -+ -+/* PS MMU FW Data WR VC0 */ -+#define IPU8_PS_MMU_SRT_WR_OFFSET 0x14b000 -+#define IPU8_PS_MMU_SRT_WR_STREAM_NUM 26 -+#define IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xbc -+ -+/* IS UAO UC RD */ -+#define IPU8_IS_UAO_UC_RD_OFFSET 0x27a000 -+#define IPU8_IS_UAO_UC_RD_PLANENUM 4 -+ -+/* IS UAO UC WR */ -+#define IPU8_IS_UAO_UC_WR_OFFSET 0x27b000 -+#define IPU8_IS_UAO_UC_WR_PLANENUM 4 -+ -+/* IS UAO M0 WR */ -+#define IPU8_IS_UAO_M0_WR_OFFSET 0x27c000 -+#define IPU8_IS_UAO_M0_WR_PLANENUM 16 -+ -+/* IS UAO M1 WR */ -+#define IPU8_IS_UAO_M1_WR_OFFSET 0x27d000 -+#define IPU8_IS_UAO_M1_WR_PLANENUM 16 -+ -+/* IS UAO UPIPE */ -+#define IPU8_IS_UAO_UPIPE_OFFSET 0x27e000 -+#define IPU8_IS_UAO_UPIPE_PLANENUM 6 -+ -+/* PS UAO FW RD */ -+#define IPU8_PS_UAO_FW_RD_OFFSET 0x156000 -+#define IPU8_PS_UAO_FW_RD_PLANENUM 12 -+ -+/* PS UAO FW WR */ -+#define IPU8_PS_UAO_FW_WR_OFFSET 0x157000 -+#define IPU8_PS_UAO_FW_WR_PLANENUM 8 -+ -+/* PS UAO SRT RD */ -+#define IPU8_PS_UAO_SRT_RD_OFFSET 0x154000 -+#define IPU8_PS_UAO_SRT_RD_PLANENUM 26 -+ -+/* PS UAO SRT WR */ -+#define IPU8_PS_UAO_SRT_WR_OFFSET 0x155000 -+#define IPU8_PS_UAO_SRT_WR_PLANENUM 26 -+ -+#define IPU8_IS_ZLX_UC_RD_OFFSET 0x275000 -+#define IPU8_IS_ZLX_UC_WR_OFFSET 0x276000 -+#define IPU8_IS_ZLX_M0_OFFSET 0x277000 -+#define IPU8_IS_ZLX_M1_OFFSET 0x278000 -+#define IPU8_IS_ZLX_UPIPE_OFFSET 0x279000 -+#define IPU8_IS_ZLX_UC_RD_NUM 4 -+#define IPU8_IS_ZLX_UC_WR_NUM 4 -+#define IPU8_IS_ZLX_M0_NUM 16 -+#define IPU8_IS_ZLX_M1_NUM 16 -+#define IPU8_IS_ZLX_UPIPE_NUM 6 -+ -+#define IPU8_PS_ZLX_DATA_RD_OFFSET 0x14e000 -+#define IPU8_PS_ZLX_DATA_WR_OFFSET 0x14f000 -+#define IPU8_PS_ZLX_FW_RD_OFFSET 0x150000 -+#define IPU8_PS_ZLX_FW_WR_OFFSET 0x151000 -+#define IPU8_PS_ZLX_DATA_RD_NUM 26 -+#define IPU8_PS_ZLX_DATA_WR_NUM 26 -+#define IPU8_PS_ZLX_FW_RD_NUM 12 -+#define IPU8_PS_ZLX_FW_WR_NUM 8 -+ -+#define MMU_REG_INVALIDATE_0 0x00 -+#define MMU_REG_INVALIDATE_1 0x04 -+#define MMU_REG_PAGE_TABLE_BASE_ADDR 0x08 -+#define MMU_REG_USER_INFO_BITS 0x0c -+#define MMU_REG_AXI_REFILL_IF_ID 0x10 -+#define MMU_REG_PW_EN_BITMAP 0x14 -+#define MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18 -+#define MMU_REG_GENERAL_REG 0x1c -+#define MMU_REG_AT_SP_ARB_CFG 0x20 -+#define MMU_REG_INVALIDATION_STATUS 0x24 -+#define MMU_REG_IRQ_LEVEL_NO_PULSE 0x28 -+#define MMU_REG_IRQ_MASK 0x2c -+#define MMU_REG_IRQ_ENABLE 0x30 -+#define MMU_REG_IRQ_EDGE 0x34 -+#define MMU_REG_IRQ_CLEAR 0x38 -+#define MMU_REG_IRQ_CAUSE 0x3c -+#define MMU_REG_CG_CTRL_BITS 0x40 -+#define MMU_REG_RD_FIFOS_STATUS 0x44 -+#define MMU_REG_WR_FIFOS_STATUS 0x48 -+#define MMU_REG_COMMON_FIFOS_STATUS 0x4c -+#define MMU_REG_FSM_STATUS 0x50 -+ -+#define ZLX_REG_AXI_POOL 0x0 -+#define ZLX_REG_EN 0x20 -+#define ZLX_REG_CONF 0x24 -+#define ZLX_REG_CG_CTRL 0x900 -+#define ZLX_REG_FORCE_BYPASS 0x904 -+ -+struct ipu7_mmu_info { -+ struct device *dev; -+ -+ u32 *l1_pt; -+ u32 l1_pt_dma; -+ u32 **l2_pts; -+ -+ u32 *dummy_l2_pt; -+ u32 dummy_l2_pteval; -+ void *dummy_page; -+ u32 dummy_page_pteval; -+ -+ dma_addr_t aperture_start; -+ dma_addr_t aperture_end; -+ unsigned long pgsize_bitmap; -+ -+ spinlock_t lock; /* Serialize access to users */ -+ struct ipu7_dma_mapping *dmap; -+}; -+ -+struct ipu7_mmu { -+ struct list_head node; -+ -+ struct ipu7_mmu_hw *mmu_hw; -+ unsigned int nr_mmus; -+ unsigned int mmid; -+ -+ phys_addr_t pgtbl; -+ struct device *dev; -+ -+ struct ipu7_dma_mapping *dmap; -+ struct list_head vma_list; -+ -+ struct page *trash_page; -+ dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ -+ dma_addr_t iova_trash_page; /* IOVA for IPU child nodes to use */ -+ -+ bool ready; -+ spinlock_t ready_lock; /* Serialize access to bool ready */ -+ -+ void (*tlb_invalidate)(struct ipu7_mmu *mmu); -+}; -+ -+struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -+ void __iomem *base, int mmid, -+ const struct ipu7_hw_variants *hw); -+void ipu7_mmu_cleanup(struct ipu7_mmu *mmu); -+int ipu7_mmu_hw_init(struct ipu7_mmu *mmu); -+void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu); -+int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size); -+void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ size_t size); -+phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -+ dma_addr_t iova); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h b/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -new file mode 100644 -index 000000000000..eeadc886a8cf ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -@@ -0,0 +1,82 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2018 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_PLATFORM_REGS_H -+#define IPU7_PLATFORM_REGS_H -+ -+#define IS_BASE 0x230000 -+#define IS_UC_CTRL_BASE (IS_BASE + 0x0) -+ -+#define PS_BASE 0x130000 -+#define PS_UC_CTRL_BASE (PS_BASE + 0x0) -+ -+/* -+ * bit 0: IRQ from FW, -+ * bit 1, 2 and 3: IRQ from HW -+ */ -+#define TO_SW_IRQ_MASK 0xf -+#define TO_SW_IRQ_FW BIT(0) -+ -+#define FW_CODE_BASE 0x0 -+#define FW_DATA_BASE 0x4 -+#define PRINTF_EN_THROUGH_TRACE 0x3004 -+#define PRINTF_EN_DIRECTLY_TO_DDR 0x3008 -+#define PRINTF_DDR_BASE_ADDR 0x300c -+#define PRINTF_DDR_SIZE 0x3010 -+#define PRINTF_DDR_NEXT_ADDR 0x3014 -+#define PRINTF_STATUS 0x3018 -+#define PRINTF_AXI_CNTL 0x301c -+#define PRINTF_MSG_LENGTH 0x3020 -+#define TO_SW_IRQ_CNTL_EDGE 0x4000 -+#define TO_SW_IRQ_CNTL_MASK_N 0x4004 -+#define TO_SW_IRQ_CNTL_STATUS 0x4008 -+#define TO_SW_IRQ_CNTL_CLEAR 0x400c -+#define TO_SW_IRQ_CNTL_ENABLE 0x4010 -+#define TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x4014 -+#define ERR_IRQ_CNTL_EDGE 0x4018 -+#define ERR_IRQ_CNTL_MASK_N 0x401c -+#define ERR_IRQ_CNTL_STATUS 0x4020 -+#define ERR_IRQ_CNTL_CLEAR 0x4024 -+#define ERR_IRQ_CNTL_ENABLE 0x4028 -+#define ERR_IRQ_CNTL_LEVEL_NOT_PULSE 0x402c -+#define LOCAL_DMEM_BASE_ADDR 0x1300000 -+ -+/* -+ * IS_UC_TO_SW irqs -+ * bit 0: IRQ from local FW -+ * bit 1~3: IRQ from HW -+ */ -+#define IS_UC_TO_SW_IRQ_MASK 0xf -+ -+#define IPU_ISYS_SPC_OFFSET 0x210000 -+#define IPU7_PSYS_SPC_OFFSET 0x118000 -+#define IPU_ISYS_DMEM_OFFSET 0x200000 -+#define IPU_PSYS_DMEM_OFFSET 0x100000 -+ -+#define IPU7_ISYS_CSI_PORT_NUM 4 -+ -+/* IRQ-related registers in PSYS */ -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_EDGE 0x134000 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK 0x134004 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS 0x134008 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR 0x13400c -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE 0x134010 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x134014 -+#define IRQ_FROM_LOCAL_FW BIT(0) -+ -+/* -+ * psys subdomains power request regs -+ */ -+enum ipu7_device_buttress_psys_domain_pos { -+ IPU_PSYS_SUBDOMAIN_LB = 0, -+ IPU_PSYS_SUBDOMAIN_BB = 1, -+}; -+ -+#define IPU7_PSYS_DOMAIN_POWER_MASK (BIT(IPU_PSYS_SUBDOMAIN_LB) | \ -+ BIT(IPU_PSYS_SUBDOMAIN_BB)) -+#define IPU8_PSYS_DOMAIN_POWER_MASK BIT(IPU_PSYS_SUBDOMAIN_LB) -+#define IPU_PSYS_DOMAIN_POWER_IN_PROGRESS BIT(31) -+ -+#endif /* IPU7_PLATFORM_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.c b/drivers/media/pci/intel/ipu7/ipu7-syscom.c -new file mode 100644 -index 000000000000..1a1c8cb02d83 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-syscom.c -@@ -0,0 +1,79 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_syscom_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-syscom.h" -+ -+static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx, -+ u32 q) -+{ -+ return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s)); -+} -+ -+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q) -+{ -+ struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -+ void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -+ u32 write_index = readl(queue_indices + -+ offsetof(struct syscom_queue_indices_s, -+ write_index)); -+ u32 read_index = readl(queue_indices + -+ offsetof(struct syscom_queue_indices_s, -+ read_index)); -+ void *token = NULL; -+ -+ if (q < ctx->num_output_queues) { -+ /* Output queue */ -+ bool empty = (write_index == read_index); -+ -+ if (!empty) -+ token = queue_params->token_array_mem + -+ read_index * -+ queue_params->token_size_in_bytes; -+ } else { -+ /* Input queue */ -+ bool full = (read_index == ((write_index + 1U) % -+ (u32)queue_params->max_capacity)); -+ -+ if (!full) -+ token = queue_params->token_array_mem + -+ write_index * queue_params->token_size_in_bytes; -+ } -+ return token; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7"); -+ -+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q) -+{ -+ struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -+ void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -+ u32 offset, index; -+ -+ if (q < ctx->num_output_queues) -+ /* Output queue */ -+ offset = offsetof(struct syscom_queue_indices_s, read_index); -+ -+ else -+ /* Input queue */ -+ offset = offsetof(struct syscom_queue_indices_s, write_index); -+ -+ index = readl(queue_indices + offset); -+ writel((index + 1U) % queue_params->max_capacity, -+ queue_indices + offset); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7"); -+ -+struct syscom_queue_params_config * -+ipu7_syscom_get_queue_config(struct syscom_config_s *config) -+{ -+ return (struct syscom_queue_params_config *)(&config[1]); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.h b/drivers/media/pci/intel/ipu7/ipu7-syscom.h -new file mode 100644 -index 000000000000..e1fbe3b7914e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-syscom.h -@@ -0,0 +1,35 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_SYSCOM_H -+#define IPU7_SYSCOM_H -+ -+#include -+ -+struct syscom_config_s; -+struct syscom_queue_params_config; -+ -+struct syscom_queue_config { -+ void *token_array_mem; -+ u32 queue_size; -+ u16 token_size_in_bytes; -+ u16 max_capacity; -+}; -+ -+struct ipu7_syscom_context { -+ u16 num_input_queues; -+ u16 num_output_queues; -+ struct syscom_queue_config *queue_configs; -+ void __iomem *queue_indices; -+ dma_addr_t queue_mem_dma_addr; -+ void *queue_mem; -+ u32 queue_mem_size; -+}; -+ -+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q); -+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q); -+struct syscom_queue_params_config * -+ipu7_syscom_get_queue_config(struct syscom_config_s *config); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7.c b/drivers/media/pci/intel/ipu7/ipu7.c -new file mode 100644 -index 000000000000..a76863eea356 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7.c -@@ -0,0 +1,2864 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "abi/ipu7_fw_common_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-mmu.h" -+#include "ipu7-platform-regs.h" -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+#include -+ -+#endif -+#define IPU_PCI_BAR 0 -+#define IPU_PCI_PBBAR 4 -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+static const unsigned int ipu7_tpg_offsets[] = { -+ MGC_MG_PORT(0), -+ MGC_MG_PORT(1), -+ MGC_MG_PORT(2), -+}; -+#endif -+ -+static const unsigned int ipu7_csi_offsets[] = { -+ IPU_CSI_PORT_A_ADDR_OFFSET, -+ IPU_CSI_PORT_B_ADDR_OFFSET, -+ IPU_CSI_PORT_C_ADDR_OFFSET, -+ IPU_CSI_PORT_D_ADDR_OFFSET, -+}; -+ -+static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = { -+ .csi2 = { -+ .gpreg = IS_IO_CSI2_GPREGS_BASE, -+ }, -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7P5_IS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "IS_FW_RD", -+ .offset = IPU7P5_IS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET, -+ .info_bits = 0x20005101, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0 -+ }, -+ .zlx_conf = { -+ 0x0, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004c, -+ 0x0000004d, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_FW_WR", -+ .offset = IPU7P5_IS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET, -+ .info_bits = 0x20005001, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x00010101, -+ 0x00010101, -+ 0x0, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004a, -+ 0x0000004b, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_ISOC", -+ .offset = IPU7P5_IS_MMU_M0_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET, -+ .info_bits = 0x20004e01, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_M0_NUM, -+ .zlx_axi_pool = { -+ 0x00000f10, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_SNOOP", -+ .offset = IPU7P5_IS_MMU_M1_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET, -+ .info_bits = 0x20004f01, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_M1_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ }, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7P5_PS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "PS_FW_RD", -+ .offset = IPU7P5_PS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET, -+ .info_bits = 0x20004001, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000f, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001a, -+ 0x0000001a, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0, 1, 1, 0, 0, -+ 0, 1, 1, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00000000, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00010101, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00010101, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002e, -+ 0x00000035, -+ 0x00000036, -+ 0x00000031, -+ 0x00000037, -+ 0x00000038, -+ 0x00000039, -+ 0x00000032, -+ 0x00000033, -+ 0x0000003a, -+ 0x0000003b, -+ 0x0000003c, -+ 0x00000034, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_FW_WR", -+ .offset = IPU7P5_PS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET, -+ .info_bits = 0x20003e01, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000010, -+ 0x00000010, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00000000, -+ 0x00010101, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002e, -+ 0x0000002f, -+ 0x00000030, -+ 0x00000031, -+ 0x00000032, -+ 0x00000033, -+ 0x00000034, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_DATA_RD", -+ .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET, -+ .info_bits = 0x20003f01, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000b, -+ 0x0000000d, -+ 0x0000000f, -+ 0x00000013, -+ 0x00000017, -+ 0x00000019, -+ 0x0000001b, -+ 0x0000001d, -+ 0x0000001f, -+ 0x0000002b, -+ 0x00000033, -+ 0x0000003f, -+ 0x00000047, -+ 0x00000049, -+ 0x0000004b, -+ 0x0000004c, -+ 0x0000004d, -+ 0x0000004e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00030303, -+ 0x00010101, -+ 0x00010101, -+ 0x00030202, -+ 0x00010101, -+ 0x00010101, -+ 0x00030303, -+ 0x00030303, -+ 0x00010101, -+ 0x00030800, -+ 0x00030500, -+ 0x00020101, -+ 0x00042000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00020400, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000001c, -+ 0x0000001d, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ 0x00000022, -+ 0x00000023, -+ 0x00000024, -+ 0x00000025, -+ 0x00000026, -+ 0x00000027, -+ 0x00000028, -+ 0x00000029, -+ 0x0000002a, -+ 0x0000002b, -+ 0x0000002c, -+ 0x0000002d, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "PS_DATA_WR", -+ .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET, -+ .info_bits = 0x20003d01, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000006, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000028, -+ 0x0000002a, -+ 0x00000036, -+ 0x0000003e, -+ 0x00000040, -+ 0x00000042, -+ 0x0000004e, -+ 0x00000056, -+ 0x0000005c, -+ 0x00000068, -+ 0x00000070, -+ 0x00000076, -+ 0x00000077, -+ 0x00000078, -+ 0x00000079, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000006, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000028, -+ 0x0000002a, -+ 0x00000036, -+ 0x0000003e, -+ 0x00000040, -+ 0x00000042, -+ 0x0000004e, -+ 0x00000056, -+ 0x0000005c, -+ 0x00000068, -+ 0x00000070, -+ 0x00000076, -+ 0x00000077, -+ 0x00000078, -+ 0x00000079, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f50, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 0, 0, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00010102, -+ 0x00030103, -+ 0x00030103, -+ 0x00010101, -+ 0x00010101, -+ 0x00030101, -+ 0x00010101, -+ 0x38010101, -+ 0x00000000, -+ 0x00000000, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x00030303, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00010101, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000000, -+ 0x00000001, -+ 0x00000002, -+ 0x00000003, -+ 0x00000004, -+ 0x00000005, -+ 0x00000006, -+ 0x00000007, -+ 0x00000008, -+ 0x00000009, -+ 0x0000000a, -+ 0x0000000b, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000015, -+ 0x00000016, -+ 0x00000017, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001b, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ }, -+ }, -+ .dmem_offset = IPU_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static struct ipu_isys_internal_pdata ipu7_isys_ipdata = { -+ .csi2 = { -+ .gpreg = IS_IO_CSI2_GPREGS_BASE, -+ }, -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7_IS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "IS_FW_RD", -+ .offset = IPU7_IS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET, -+ .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET, -+ .info_bits = 0x20006701, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 0, 0, 0 -+ }, -+ .zlx_conf = { -+ 0x0, 0x0, 0x0, 0x0, -+ }, -+ .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000061, -+ 0x00000064, -+ 0x00000065, -+ }, -+ }, -+ { -+ .name = "IS_FW_WR", -+ .offset = IPU7_IS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET, -+ .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET, -+ .info_bits = 0x20006801, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000061, -+ 0x00000062, -+ 0x00000063, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_ISOC", -+ .offset = IPU7_IS_MMU_M0_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_M0_OFFSET, -+ .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET, -+ .info_bits = 0x20006601, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_M0_NUM, -+ .zlx_axi_pool = { -+ 0x00000f10, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004a, -+ 0x0000004b, -+ 0x0000004c, -+ 0x0000004d, -+ 0x0000004e, -+ 0x0000004f, -+ 0x00000050, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_SNOOP", -+ .offset = IPU7_IS_MMU_M1_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_M1_OFFSET, -+ .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET, -+ .info_bits = 0x20006901, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x3, 0x6, 0x9, 0xc, -+ 0xe, 0x10, 0x12, 0x14, 0x16, -+ 0x18, 0x1a, 0x1c, 0x1e, 0x20, -+ 0x22, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_M1_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000051, -+ 0x00000052, -+ 0x00000053, -+ 0x00000054, -+ 0x00000055, -+ 0x00000056, -+ 0x00000057, -+ 0x00000058, -+ 0x00000059, -+ 0x0000005a, -+ 0x0000005b, -+ 0x0000005c, -+ 0x0000005d, -+ 0x0000005e, -+ 0x0000005f, -+ 0x00000060, -+ }, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu_psys_internal_pdata ipu7_psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7_PS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "PS_FW_RD", -+ .offset = IPU7_PS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET, -+ .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET, -+ .info_bits = 0x20004801, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0, 0x8, 0xa, 0xc, 0xd, -+ 0xf, 0x11, 0x12, 0x13, 0x14, -+ 0x16, 0x18, 0x19, 0x1a, 0x1a, -+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, 0x20, 0x22, 0x24, 0x26, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ }, -+ .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000036, -+ 0x0000003d, -+ 0x0000003e, -+ 0x00000039, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x0000003a, -+ 0x0000003b, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x0000003c, -+ }, -+ }, -+ { -+ .name = "PS_FW_WR", -+ .offset = IPU7_PS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET, -+ .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET, -+ .info_bits = 0x20004601, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0, 0x8, 0xa, 0xc, 0xd, -+ 0xe, 0xf, 0x10, 0x10, 0x10, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, 0, 0, 0, 0, -+ 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000036, -+ 0x00000037, -+ 0x00000038, -+ 0x00000039, -+ 0x0000003a, -+ 0x0000003b, -+ 0x0000003c, -+ }, -+ }, -+ { -+ .name = "PS_DATA_RD", -+ .offset = IPU7_PS_MMU_SRT_RD_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET, -+ .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET, -+ .info_bits = 0x20004701, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x4, 0x6, 0x8, 0xb, -+ 0xd, 0xf, 0x11, 0x13, 0x15, -+ 0x17, 0x23, 0x2b, 0x37, 0x3f, -+ 0x41, 0x43, 0x44, 0x45, 0x46, -+ 0x47, 0x48, 0x49, 0x4a, 0x4b, -+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, -+ 0x51, 0x52, 0x53, 0x55, 0x57, -+ 0x59, 0x5b, 0x5d, 0x5f, 0x61, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, 0x20, 0x22, 0x24, 0x26, -+ 0x28, 0x2a, 0x2c, 0x2e, 0x30, -+ 0x32, 0x34, 0x36, 0x38, 0x3a, -+ 0x3c, 0x3e, 0x40, 0x42, 0x44, -+ 0x46, 0x48, 0x4a, 0x4c, 0x4e, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00030303, -+ 0x00010101, -+ 0x00010101, -+ 0x00030202, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00030800, -+ 0x00030500, -+ 0x00020101, -+ 0x00042000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00020400, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000022, -+ 0x00000023, -+ 0x00000024, -+ 0x00000025, -+ 0x00000026, -+ 0x00000027, -+ 0x00000028, -+ 0x00000029, -+ 0x0000002a, -+ 0x0000002b, -+ 0x0000002c, -+ 0x0000002d, -+ 0x0000002e, -+ 0x0000002f, -+ 0x00000030, -+ 0x00000031, -+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ 0x00000032, -+ 0x00000033, -+ 0x00000034, -+ 0x00000035, -+ }, -+ }, -+ { -+ .name = "PS_DATA_WR", -+ .offset = IPU7_PS_MMU_SRT_WR_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET, -+ .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET, -+ .info_bits = 0x20004501, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x2, 0x6, 0xa, 0xc, -+ 0xe, 0x10, 0x12, 0x14, 0x16, -+ 0x18, 0x1a, 0x1c, 0x1e, 0x20, -+ 0x22, 0x24, 0x26, 0x32, 0x3a, -+ 0x3c, 0x3e, 0x4a, 0x52, 0x58, -+ 0x64, 0x6c, 0x72, 0x7e, 0x86, -+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90, -+ 0x91, 0x92, 0x94, 0x96, 0x98, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, 0x20, 0x22, 0x24, 0x26, -+ 0x28, 0x2a, 0x2c, 0x2e, 0x30, -+ 0x32, 0x34, 0x36, 0x38, 0x3a, -+ 0x3c, 0x3e, 0x40, 0x42, 0x44, -+ 0x46, 0x48, 0x4a, 0x4c, 0x4e, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f50, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 0, 0, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00010102, -+ 0x00030103, -+ 0x00030103, -+ 0x00010101, -+ 0x00010101, -+ 0x00030101, -+ 0x00010101, -+ 0x38010101, -+ 0x0, -+ 0x0, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00010101, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x0, -+ 0x0, -+ }, -+ .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000000, -+ 0x00000001, -+ 0x00000002, -+ 0x00000003, -+ 0x00000004, -+ 0x00000005, -+ 0x00000006, -+ 0x00000007, -+ 0x00000008, -+ 0x00000009, -+ 0x0000000a, -+ 0x0000000b, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000015, -+ 0x00000016, -+ 0x00000017, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001b, -+ 0x0000001c, -+ 0x0000001d, -+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ }, -+ }, -+ }, -+ .dmem_offset = IPU_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static struct ipu_isys_internal_pdata ipu8_isys_ipdata = { -+ .csi2 = { -+ .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE, -+ }, -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU8_IS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "IS_FW_RD", -+ .offset = IPU8_IS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_UC_RD_OFFSET, -+ .uao_offset = IPU8_IS_UAO_UC_RD_OFFSET, -+ .info_bits = 0x20005101, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_UC_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0 -+ }, -+ .zlx_conf = { -+ 0, 2, 0, 0 -+ }, -+ .uao_p_num = IPU8_IS_UAO_UC_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004c, -+ 0x0000004d, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_FW_WR", -+ .offset = IPU8_IS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_UC_WR_OFFSET, -+ .uao_offset = IPU8_IS_UAO_UC_WR_OFFSET, -+ .info_bits = 0x20005001, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_UC_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x2, -+ 0x2, -+ 0x0, -+ }, -+ .uao_p_num = IPU8_IS_UAO_UC_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004a, -+ 0x0000004b, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_ISOC", -+ .offset = IPU8_IS_MMU_M0_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_M0_OFFSET, -+ .uao_offset = IPU8_IS_UAO_M0_WR_OFFSET, -+ .info_bits = 0x20004e01, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_M0_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_M0_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_M0_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_M0_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_M0_NUM, -+ .zlx_axi_pool = { -+ 0x00000f10, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ }, -+ .uao_p_num = IPU8_IS_UAO_M0_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_SNOOP", -+ .offset = IPU8_IS_MMU_M1_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_M1_OFFSET, -+ .uao_offset = IPU8_IS_UAO_M1_WR_OFFSET, -+ .info_bits = 0x20004f01, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_M1_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_M1_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_M1_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_M1_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_M1_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ }, -+ .uao_p_num = IPU8_IS_UAO_M1_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ }, -+ }, -+ { -+ .name = "IS_UPIPE", -+ .offset = IPU8_IS_MMU_UPIPE_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_UPIPE_OFFSET, -+ .uao_offset = IPU8_IS_UAO_UPIPE_OFFSET, -+ .info_bits = 0x20005201, -+ .refill = 0x00002928, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_UPIPE_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ }, -+ .uao_p_num = IPU8_IS_UAO_UPIPE_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000043, -+ 0x00000044, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ }, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu_psys_internal_pdata ipu8_psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU8_PS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "PS_FW_RD", -+ .offset = IPU8_PS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_FW_RD_OFFSET, -+ .uao_offset = IPU8_PS_UAO_FW_RD_OFFSET, -+ .info_bits = 0x20003a01, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x00000018, -+ 0x00000018, -+ 0x00000018, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_FW_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0, 1, 1, 0, 0, -+ 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x2, -+ 0x0, -+ 0x0, -+ 0x2, -+ 0x2, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_FW_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002d, -+ 0x00000032, -+ 0x00000033, -+ 0x00000030, -+ 0x00000034, -+ 0x00000035, -+ 0x00000036, -+ 0x00000031, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_FW_WR", -+ .offset = IPU8_PS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_FW_WR_OFFSET, -+ .uao_offset = IPU8_PS_UAO_FW_WR_OFFSET, -+ .info_bits = 0x20003901, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000010, -+ 0x00000010, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_FW_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, 0x2, 0x2, 0x0, -+ 0x0, 0x0, 0x0, 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_FW_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002d, -+ 0x0000002e, -+ 0x0000002f, -+ 0x00000030, -+ 0x00000031, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_DATA_RD", -+ .offset = IPU8_PS_MMU_SRT_RD_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_DATA_RD_OFFSET, -+ .uao_offset = IPU8_PS_UAO_SRT_RD_OFFSET, -+ .info_bits = 0x20003801, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000014, -+ 0x00000018, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002c, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ 0x00000036, -+ 0x0000003a, -+ 0x0000003c, -+ 0x0000003c, -+ 0x0000003c, -+ 0x0000003c, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002c, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_DATA_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 0, 0, -+ 0, 0, -+ }, -+ .zlx_conf = { -+ 0x6, 0x3, 0x3, 0x6, -+ 0x2, 0x2, 0x6, 0x6, -+ 0x6, 0x3, 0x6, 0x3, -+ 0x3, 0x2, 0x2, 0x2, -+ 0x2, 0x2, 0x2, 0x6, -+ 0x6, 0x3, 0x0, 0x0, -+ 0x0, 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_SRT_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000017, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001b, -+ 0x0000001c, -+ 0x0000001d, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ 0x00000022, -+ 0x00000023, -+ 0x00000024, -+ 0x00000025, -+ 0x00000026, -+ 0x00000027, -+ 0x00000028, -+ 0x00000029, -+ 0x0000002a, -+ 0x0000002b, -+ 0x0000002c, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_DATA_WR", -+ .offset = IPU8_PS_MMU_SRT_WR_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_DATA_WR_OFFSET, -+ .uao_offset = IPU8_PS_UAO_SRT_WR_OFFSET, -+ .info_bits = 0x20003701, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000022, -+ 0x00000024, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ 0x00000036, -+ 0x00000038, -+ 0x0000003a, -+ 0x0000003a, -+ 0x0000003a, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002c, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_DATA_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f50, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 0, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 0, -+ 0, 0, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x6, -+ 0x38000002, -+ 0x38000000, -+ 0x3, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x6, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x3, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_SRT_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000000, -+ 0x00000001, -+ 0x00000002, -+ 0x00000003, -+ 0x00000004, -+ 0x00000005, -+ 0x00000006, -+ 0x00000007, -+ 0x00000008, -+ 0x00000009, -+ 0x0000000a, -+ 0x0000000b, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000015, -+ 0x00000016, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ }, -+ }, -+ .dmem_offset = IPU_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static const struct ipu_buttress_ctrl ipu7_isys_buttress_ctrl = { -+ .subsys_id = IPU_IS, -+ .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+ .ovrd_clk = BUTTRESS_OVERRIDE_IS_CLK, -+ .own_clk_ack = BUTTRESS_OWN_ACK_IS_CLK, -+}; -+ -+static const struct ipu_buttress_ctrl ipu7_psys_buttress_ctrl = { -+ .subsys_id = IPU_PS, -+ .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+ .ovrd_clk = BUTTRESS_OVERRIDE_PS_CLK, -+ .own_clk_ack = BUTTRESS_OWN_ACK_PS_CLK, -+}; -+ -+static const struct ipu_buttress_ctrl ipu8_isys_buttress_ctrl = { -+ .subsys_id = IPU_IS, -+ .ratio = IPU8_IS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+}; -+ -+static const struct ipu_buttress_ctrl ipu8_psys_buttress_ctrl = { -+ .subsys_id = IPU_PS, -+ .ratio = IPU8_PS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+ .own_clk_ack = BUTTRESS_OWN_ACK_PS_PLL, -+}; -+ -+void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -+ struct ipu_psys_internal_pdata *psys_ipdata) -+{ -+ isys_ipdata->csi2.nports = ARRAY_SIZE(ipu7_csi_offsets); -+ isys_ipdata->csi2.offsets = ipu7_csi_offsets; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ isys_ipdata->tpg.ntpgs = ARRAY_SIZE(ipu7_tpg_offsets); -+ isys_ipdata->tpg.offsets = ipu7_tpg_offsets; -+ isys_ipdata->tpg.sels = NULL; -+#endif -+ isys_ipdata->num_parallel_streams = IPU7_ISYS_NUM_STREAMS; -+ psys_ipdata->hw_variant.spc_offset = IPU7_PSYS_SPC_OFFSET; -+} -+ -+static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) -+{ -+ struct fwnode_handle *endpoint; -+ -+ if (IS_ERR_OR_NULL(fwnode)) -+ return -EINVAL; -+ -+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); -+ if (endpoint) { -+ fwnode_handle_put(endpoint); -+ return 0; -+ } -+ -+ return ipu7_isys_check_fwnode_graph(fwnode->secondary); -+} -+ -+static struct ipu7_bus_device * -+ipu7_isys_init(struct pci_dev *pdev, struct device *parent, -+ const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ struct ipu7_isys_subdev_pdata *spdata, -+ const struct ipu_isys_internal_pdata *ipdata, -+ unsigned int nr) -+{ -+ struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -+ struct ipu7_bus_device *isys_adev; -+ struct device *dev = &pdev->dev; -+ struct ipu7_isys_pdata *pdata; -+ int ret; -+ -+ ret = ipu7_isys_check_fwnode_graph(fwnode); -+ if (ret) { -+ if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { -+ dev_err(dev, -+ "fwnode graph has no endpoints connection\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); -+ if (ret) { -+ dev_err_probe(dev, ret, "IPU bridge init failed\n"); -+ return ERR_PTR(ret); -+ } -+ } -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ pdata->base = base; -+ pdata->ipdata = ipdata; -+ pdata->spdata = spdata; -+ -+ isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -+ IPU_ISYS_NAME); -+ if (IS_ERR(isys_adev)) { -+ dev_err_probe(dev, PTR_ERR(isys_adev), -+ "ipu7_bus_initialize_device isys failed\n"); -+ kfree(pdata); -+ return ERR_CAST(isys_adev); -+ } -+ -+ isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, -+ &ipdata->hw_variant); -+ if (IS_ERR(isys_adev->mmu)) { -+ dev_err_probe(dev, PTR_ERR(isys_adev->mmu), -+ "ipu7_mmu_init(isys_adev->mmu) failed\n"); -+ put_device(&isys_adev->auxdev.dev); -+ kfree(pdata); -+ return ERR_CAST(isys_adev->mmu); -+ } -+ -+ isys_adev->mmu->dev = &isys_adev->auxdev.dev; -+ isys_adev->subsys = IPU_IS; -+ -+ ret = ipu7_bus_add_device(isys_adev); -+ if (ret) { -+ kfree(pdata); -+ return ERR_PTR(ret); -+ } -+ -+ return isys_adev; -+} -+ -+static struct ipu7_bus_device * -+ipu7_psys_init(struct pci_dev *pdev, struct device *parent, -+ const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ const struct ipu_psys_internal_pdata *ipdata, unsigned int nr) -+{ -+ struct ipu7_bus_device *psys_adev; -+ struct ipu7_psys_pdata *pdata; -+ int ret; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ pdata->base = base; -+ pdata->ipdata = ipdata; -+ -+ psys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -+ IPU_PSYS_NAME); -+ if (IS_ERR(psys_adev)) { -+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), -+ "ipu7_bus_initialize_device psys failed\n"); -+ kfree(pdata); -+ return ERR_CAST(psys_adev); -+ } -+ -+ psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, -+ &ipdata->hw_variant); -+ if (IS_ERR(psys_adev->mmu)) { -+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), -+ "ipu7_mmu_init(psys_adev->mmu) failed\n"); -+ put_device(&psys_adev->auxdev.dev); -+ kfree(pdata); -+ return ERR_CAST(psys_adev->mmu); -+ } -+ -+ psys_adev->mmu->dev = &psys_adev->auxdev.dev; -+ psys_adev->subsys = IPU_PS; -+ -+ ret = ipu7_bus_add_device(psys_adev); -+ if (ret) { -+ kfree(pdata); -+ return ERR_PTR(ret); -+ } -+ -+ return psys_adev; -+} -+ -+static struct ia_gofo_msg_log_info_ts fw_error_log[IPU_SUBSYS_NUM]; -+void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) -+{ -+ void __iomem *reg = adev->isp->base + ((adev->subsys == IPU_IS) ? -+ BUTTRESS_REG_FW_GP24 : -+ BUTTRESS_REG_FW_GP8); -+ -+ memcpy_fromio(&fw_error_log[adev->subsys], reg, -+ sizeof(fw_error_log[adev->subsys])); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); -+ -+#ifdef CONFIG_DEBUG_FS -+static struct debugfs_blob_wrapper isys_fw_error; -+static struct debugfs_blob_wrapper psys_fw_error; -+ -+static int ipu7_init_debugfs(struct ipu7_device *isp) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -+ if (!dir) -+ return -ENOMEM; -+ -+ isys_fw_error.data = &fw_error_log[IPU_IS]; -+ isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); -+ file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); -+ if (!file) -+ goto err; -+ psys_fw_error.data = &fw_error_log[IPU_PS]; -+ psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); -+ file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); -+ if (!file) -+ goto err; -+ -+ isp->ipu7_dir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+ -+static void ipu7_remove_debugfs(struct ipu7_device *isp) -+{ -+ /* -+ * Since isys and psys debugfs dir will be created under ipu root dir, -+ * mark its dentry to NULL to avoid duplicate removal. -+ */ -+ debugfs_remove_recursive(isp->ipu7_dir); -+ isp->ipu7_dir = NULL; -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+static void ipu7_pci_config_setup(struct pci_dev *dev) -+{ -+ u16 pci_command; -+ -+ pci_read_config_word(dev, PCI_COMMAND, &pci_command); -+ pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; -+ pci_write_config_word(dev, PCI_COMMAND, pci_command); -+} -+ -+static int ipu7_map_fw_code_region(struct ipu7_bus_device *sys, -+ void *data, size_t size) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct sg_table *sgt = &sys->fw_sgt; -+ struct ipu7_device *isp = adev->isp; -+ struct pci_dev *pdev = isp->pdev; -+ unsigned long n_pages, i; -+ unsigned long attr = 0; -+ struct page **pages; -+ int ret; -+ -+ n_pages = PFN_UP(size); -+ -+ pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); -+ if (!pages) -+ return -ENOMEM; -+ -+ for (i = 0; i < n_pages; i++) { -+ struct page *p = vmalloc_to_page(data); -+ -+ if (!p) { -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ pages[i] = p; -+ data += PAGE_SIZE; -+ } -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size, -+ GFP_KERNEL); -+ if (ret) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ if (!isp->secure_mode) -+ attr |= DMA_ATTR_RESERVE_REGION; -+ -+ ret = dma_map_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+ if (ret < 0) { -+ dev_err(dev, "map fw code[%lu pages %u nents] failed\n", -+ n_pages, sgt->nents); -+ ret = -ENOMEM; -+ sg_free_table(sgt); -+ goto out; -+ } -+ -+ ret = ipu7_dma_map_sgtable(sys, sgt, DMA_BIDIRECTIONAL, attr); -+ if (ret) { -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+ sg_free_table(sgt); -+ goto out; -+ } -+ -+ ipu7_dma_sync_sgtable(sys, sgt); -+ -+ dev_dbg(dev, "fw code region mapped at 0x%pad entries %d\n", -+ &sgt->sgl->dma_address, sgt->nents); -+ -+out: -+ kfree(pages); -+ -+ return ret; -+} -+ -+static void ipu7_unmap_fw_code_region(struct ipu7_bus_device *sys) -+{ -+ struct pci_dev *pdev = sys->isp->pdev; -+ struct sg_table *sgt = &sys->fw_sgt; -+ -+ ipu7_dma_unmap_sgtable(sys, sgt, DMA_BIDIRECTIONAL, 0); -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+ sg_free_table(sgt); -+} -+ -+static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, -+ const char *sys_name) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_device *isp = sys->isp; -+ int ret; -+ -+ /* Copy FW binaries to specific location. */ -+ ret = ipu7_cpd_copy_binary(isp->cpd_fw->data, sys_name, -+ isp->fw_code_region, &sys->fw_entry); -+ if (ret) { -+ dev_err(dev, "%s binary not found.\n", sys_name); -+ return ret; -+ } -+ -+ ret = pm_runtime_get_sync(dev); -+ if (ret < 0) { -+ dev_err(dev, "Failed to get runtime PM\n"); -+ return ret; -+ } -+ -+ ret = ipu7_mmu_hw_init(sys->mmu); -+ if (ret) { -+ dev_err(dev, "Failed to set mmu hw\n"); -+ pm_runtime_put(dev); -+ return ret; -+ } -+ -+ /* Map code region. */ -+ ret = ipu7_map_fw_code_region(sys, isp->fw_code_region, -+ IPU_FW_CODE_REGION_SIZE); -+ if (ret) -+ dev_err(dev, "Failed to map fw code region for %s.\n", -+ sys_name); -+ -+ ipu7_mmu_hw_cleanup(sys->mmu); -+ pm_runtime_put(dev); -+ -+ return ret; -+} -+ -+static int ipu7_init_fw_code_region(struct ipu7_device *isp) -+{ -+ int ret; -+ -+ /* -+ * Allocate and map memory for FW execution. -+ * Not required in secure mode, in which FW runs in IMR. -+ */ -+ isp->fw_code_region = vmalloc(IPU_FW_CODE_REGION_SIZE); -+ if (!isp->fw_code_region) -+ return -ENOMEM; -+ -+ ret = ipu7_init_fw_code_region_by_sys(isp->isys, "isys"); -+ if (ret) -+ goto fail_init; -+ -+ ret = ipu7_init_fw_code_region_by_sys(isp->psys, "psys"); -+ if (ret) -+ goto fail_init; -+ -+ return 0; -+ -+fail_init: -+ vfree(isp->fw_code_region); -+ -+ return ret; -+} -+ -+static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -+{ -+ struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; -+ struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -+ const struct ipu_buttress_ctrl *isys_buttress_ctrl; -+ const struct ipu_buttress_ctrl *psys_buttress_ctrl; -+ struct ipu_isys_internal_pdata *isys_ipdata; -+ struct ipu_psys_internal_pdata *psys_ipdata; -+ unsigned int dma_mask = IPU_DMA_MASK; -+ struct device *dev = &pdev->dev; -+ void __iomem *isys_base = NULL; -+ void __iomem *psys_base = NULL; -+ phys_addr_t phys, pb_phys; -+ struct ipu7_device *isp; -+ u32 is_es; -+ int ret; -+ -+ if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es)) -+ is_es = 0; -+ -+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); -+ if (!isp) -+ return -ENOMEM; -+ -+ isp->pdev = pdev; -+ INIT_LIST_HEAD(&isp->devices); -+ -+ ret = pcim_enable_device(pdev); -+ if (ret) -+ return dev_err_probe(dev, ret, "Enable PCI device failed\n"); -+ -+ dev_info(dev, "Device 0x%x (rev: 0x%x)\n", -+ pdev->device, pdev->revision); -+ -+ phys = pci_resource_start(pdev, IPU_PCI_BAR); -+ pb_phys = pci_resource_start(pdev, IPU_PCI_PBBAR); -+ dev_info(dev, "IPU7 PCI BAR0 base %pap BAR2 base %pap\n", -+ &phys, &pb_phys); -+ -+ isp->base = pcim_iomap_region(pdev, IPU_PCI_BAR, IPU_NAME); -+ if (IS_ERR(isp->base)) -+ return dev_err_probe(dev, PTR_ERR(isp->base), -+ "Failed to I/O memory remapping bar %u\n", -+ IPU_PCI_BAR); -+ -+ isp->pb_base = pcim_iomap_region(pdev, IPU_PCI_PBBAR, IPU_NAME); -+ if (IS_ERR(isp->pb_base)) -+ return dev_err_probe(dev, PTR_ERR(isp->pb_base), -+ "Failed to I/O memory remapping bar %u\n", -+ IPU_PCI_PBBAR); -+ -+ dev_info(dev, "IPU7 PCI BAR0 mapped at %p\n BAR2 mapped at %p\n", -+ isp->base, isp->pb_base); -+ -+ pci_set_drvdata(pdev, isp); -+ pci_set_master(pdev); -+ -+ switch (id->device) { -+ case IPU7_PCI_ID: -+ isp->hw_ver = IPU_VER_7; -+ isp->cpd_fw_name = IPU7_FIRMWARE_NAME; -+ isys_ipdata = &ipu7_isys_ipdata; -+ psys_ipdata = &ipu7_psys_ipdata; -+ isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -+ psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -+ break; -+ case IPU7P5_PCI_ID: -+ isp->hw_ver = IPU_VER_7P5; -+ isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME; -+ isys_ipdata = &ipu7p5_isys_ipdata; -+ psys_ipdata = &ipu7p5_psys_ipdata; -+ isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -+ psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -+ break; -+ case IPU8_PCI_ID: -+ isp->hw_ver = IPU_VER_8; -+ isp->cpd_fw_name = IPU8_FIRMWARE_NAME; -+ isys_ipdata = &ipu8_isys_ipdata; -+ psys_ipdata = &ipu8_psys_ipdata; -+ isys_buttress_ctrl = &ipu8_isys_buttress_ctrl; -+ psys_buttress_ctrl = &ipu8_psys_buttress_ctrl; -+ break; -+ default: -+ WARN(1, "Unsupported IPU device"); -+ return -ENODEV; -+ } -+ -+ ipu_internal_pdata_init(isys_ipdata, psys_ipdata); -+ -+ isys_base = isp->base + isys_ipdata->hw_variant.offset; -+ psys_base = isp->base + psys_ipdata->hw_variant.offset; -+ -+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_mask)); -+ if (ret) -+ return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); -+ -+ dma_set_max_seg_size(dev, UINT_MAX); -+ -+ ipu7_pci_config_setup(pdev); -+ -+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "Failed to alloc irq vector\n"); -+ -+ ret = ipu_buttress_init(isp); -+ if (ret) -+ goto pci_irq_free; -+ -+ dev_info(dev, "firmware cpd file: %s\n", isp->cpd_fw_name); -+ -+ ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev); -+ if (ret) { -+ dev_err_probe(dev, ret, -+ "Requesting signed firmware %s failed\n", -+ isp->cpd_fw_name); -+ goto buttress_exit; -+ } -+ -+ ret = ipu7_cpd_validate_cpd_file(isp, isp->cpd_fw->data, -+ isp->cpd_fw->size); -+ if (ret) { -+ dev_err_probe(dev, ret, "Failed to validate cpd\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ isys_ctrl = devm_kmemdup(dev, isys_buttress_ctrl, -+ sizeof(*isys_buttress_ctrl), GFP_KERNEL); -+ if (!isys_ctrl) { -+ ret = -ENOMEM; -+ goto out_ipu_bus_del_devices; -+ } -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+ ipu_get_acpi_devices(&dev->platform_data); -+#endif -+ isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -+ dev->platform_data, -+ isys_ipdata, 0); -+ if (IS_ERR(isp->isys)) { -+ ret = PTR_ERR(isp->isys); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ psys_ctrl = devm_kmemdup(dev, psys_buttress_ctrl, -+ sizeof(*psys_buttress_ctrl), GFP_KERNEL); -+ if (!psys_ctrl) { -+ ret = -ENOMEM; -+ goto out_ipu_bus_del_devices; -+ } -+ -+ isp->psys = ipu7_psys_init(pdev, &isp->isys->auxdev.dev, -+ psys_ctrl, psys_base, -+ psys_ipdata, 0); -+ if (IS_ERR(isp->psys)) { -+ ret = PTR_ERR(isp->psys); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = devm_request_threaded_irq(dev, pdev->irq, -+ ipu_buttress_isr, -+ ipu_buttress_isr_threaded, -+ IRQF_SHARED, IPU_NAME, isp); -+ if (ret) -+ goto out_ipu_bus_del_devices; -+ -+ if (!isp->secure_mode) { -+ ret = ipu7_init_fw_code_region(isp); -+ if (ret) -+ goto out_ipu_bus_del_devices; -+ } else { -+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ dev_err(&isp->psys->auxdev.dev, -+ "Failed to get runtime PM\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = ipu7_mmu_hw_init(isp->psys->mmu); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "Failed to init MMU hardware\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = ipu7_map_fw_code_region(isp->psys, -+ (void *)isp->cpd_fw->data, -+ isp->cpd_fw->size); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "failed to map fw image\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = ipu_buttress_authenticate(isp); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "FW authentication failed\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ipu7_mmu_hw_cleanup(isp->psys->mmu); -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ } -+ -+#ifdef CONFIG_DEBUG_FS -+ ret = ipu7_init_debugfs(isp); -+ if (ret) { -+ dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); -+ goto out_ipu_bus_del_devices; -+ } -+#endif -+ pm_runtime_put_noidle(dev); -+ pm_runtime_allow(dev); -+ -+ isp->ipu7_bus_ready_to_probe = true; -+ -+ return 0; -+ -+out_ipu_bus_del_devices: -+ if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->isys); -+ if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->psys); -+#ifdef CONFIG_DEBUG_FS -+ if (!IS_ERR_OR_NULL(isp->fw_code_region)) -+ vfree(isp->fw_code_region); -+#endif -+ if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) -+ ipu7_mmu_cleanup(isp->psys->mmu); -+ if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -+ ipu7_mmu_cleanup(isp->isys->mmu); -+ if (!IS_ERR_OR_NULL(isp->psys)) -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ ipu7_bus_del_devices(pdev); -+ release_firmware(isp->cpd_fw); -+buttress_exit: -+ ipu_buttress_exit(isp); -+pci_irq_free: -+ pci_free_irq_vectors(pdev); -+ -+ return ret; -+} -+ -+static void ipu7_pci_remove(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ ipu7_remove_debugfs(isp); -+#endif -+ if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->isys); -+ if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->psys); -+ -+ if (!IS_ERR_OR_NULL(isp->fw_code_region)) -+ vfree(isp->fw_code_region); -+ -+ ipu7_mmu_cleanup(isp->isys->mmu); -+ ipu7_mmu_cleanup(isp->psys->mmu); -+ -+ ipu7_bus_del_devices(pdev); -+ -+ pm_runtime_forbid(&pdev->dev); -+ pm_runtime_get_noresume(&pdev->dev); -+ -+ ipu_buttress_exit(isp); -+ -+ release_firmware(isp->cpd_fw); -+ -+} -+ -+static void ipu7_pci_reset_prepare(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ -+ dev_warn(&pdev->dev, "FLR prepare\n"); -+ pm_runtime_forbid(&isp->pdev->dev); -+} -+ -+static void ipu7_pci_reset_done(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ -+ ipu_buttress_restore(isp); -+ if (isp->secure_mode) -+ ipu_buttress_reset_authentication(isp); -+ -+ isp->ipc_reinit = true; -+ pm_runtime_allow(&isp->pdev->dev); -+ -+ dev_warn(&pdev->dev, "FLR completed\n"); -+} -+ -+/* -+ * PCI base driver code requires driver to provide these to enable -+ * PCI device level PM state transitions (D0<->D3) -+ */ -+static int ipu7_suspend(struct device *dev) -+{ -+ return 0; -+} -+ -+static int ipu7_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu_buttress *b = &isp->buttress; -+ int ret; -+ -+ isp->secure_mode = ipu_buttress_get_secure_mode(isp); -+ dev_info(dev, "IPU7 in %s mode\n", -+ isp->secure_mode ? "secure" : "non-secure"); -+ -+ ipu_buttress_restore(isp); -+ -+ ret = ipu_buttress_ipc_reset(isp, &b->cse); -+ if (ret) -+ dev_err(dev, "IPC reset protocol failed!\n"); -+ -+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ dev_err(dev, "Failed to get runtime PM\n"); -+ return 0; -+ } -+ -+ ret = ipu_buttress_authenticate(isp); -+ if (ret) -+ dev_err(dev, "FW authentication failed(%d)\n", ret); -+ -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu7_runtime_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ int ret; -+ -+ ipu_buttress_restore(isp); -+ -+ if (isp->ipc_reinit) { -+ struct ipu_buttress *b = &isp->buttress; -+ -+ isp->ipc_reinit = false; -+ ret = ipu_buttress_ipc_reset(isp, &b->cse); -+ if (ret) -+ dev_err(dev, "IPC reset protocol failed!\n"); -+ } -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops ipu7_pm_ops = { -+ SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) -+ RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL) -+}; -+ -+static const struct pci_device_id ipu7_pci_tbl[] = { -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, -+ {0,} -+}; -+MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); -+ -+static const struct pci_error_handlers pci_err_handlers = { -+ .reset_prepare = ipu7_pci_reset_prepare, -+ .reset_done = ipu7_pci_reset_done, -+}; -+ -+static struct pci_driver ipu7_pci_driver = { -+ .name = IPU_NAME, -+ .id_table = ipu7_pci_tbl, -+ .probe = ipu7_pci_probe, -+ .remove = ipu7_pci_remove, -+ .driver = { -+ .pm = &ipu7_pm_ops, -+ }, -+ .err_handler = &pci_err_handlers, -+}; -+ -+module_pci_driver(ipu7_pci_driver); -+ -+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Tianshu Qiu "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_AUTHOR("Intel"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 pci driver"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7.h b/drivers/media/pci/intel/ipu7/ipu7.h -new file mode 100644 -index 000000000000..454d702ef3d4 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7.h -@@ -0,0 +1,259 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_H -+#define IPU7_H -+ -+#include -+#include -+#include -+ -+#include "ipu7-buttress.h" -+ -+struct ipu7_bus_device; -+struct pci_dev; -+struct firmware; -+ -+#define IPU_NAME "intel-ipu7" -+#define IPU_MEDIA_DEV_MODEL_NAME "ipu7" -+ -+#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin" -+#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin" -+#define IPU8_FIRMWARE_NAME "intel/ipu/ipu8_fw.bin" -+ -+#define IPU7_ISYS_NUM_STREAMS 12 -+ -+#define IPU7_PCI_ID 0x645d -+#define IPU7P5_PCI_ID 0xb05d -+#define IPU8_PCI_ID 0xd719 -+ -+#define FW_LOG_BUF_SIZE (2 * 1024 * 1024) -+ -+enum ipu_version { -+ IPU_VER_INVALID = 0, -+ IPU_VER_7 = 1, -+ IPU_VER_7P5 = 2, -+ IPU_VER_8 = 3, -+}; -+ -+static inline bool is_ipu7p5(u8 hw_ver) -+{ -+ return hw_ver == IPU_VER_7P5; -+} -+ -+static inline bool is_ipu7(u8 hw_ver) -+{ -+ return hw_ver == IPU_VER_7; -+} -+ -+static inline bool is_ipu8(u8 hw_ver) -+{ -+ return hw_ver == IPU_VER_8; -+} -+ -+#define IPU_UNIFIED_OFFSET 0 -+ -+/* -+ * ISYS DMA can overshoot. For higher resolutions over allocation is one line -+ * but it must be at minimum 1024 bytes. Value could be different in -+ * different versions / generations thus provide it via platform data. -+ */ -+#define IPU_ISYS_OVERALLOC_MIN 1024 -+ -+#define IPU_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */ -+#define IPU_FW_CODE_REGION_START 0x4000000 /* 64MB */ -+#define IPU_FW_CODE_REGION_END (IPU_FW_CODE_REGION_START + \ -+ IPU_FW_CODE_REGION_SIZE) /* 80MB */ -+ -+struct ipu7_device { -+ struct pci_dev *pdev; -+ struct list_head devices; -+ struct ipu7_bus_device *isys; -+ struct ipu7_bus_device *psys; -+ struct ipu_buttress buttress; -+ -+ const struct firmware *cpd_fw; -+ const char *cpd_fw_name; -+ /* Only for non-secure mode. */ -+ void *fw_code_region; -+ -+ void __iomem *base; -+ void __iomem *pb_base; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *ipu7_dir; -+#endif -+ u8 hw_ver; -+ bool ipc_reinit; -+ bool secure_mode; -+ bool ipu7_bus_ready_to_probe; -+}; -+ -+#define IPU_DMA_MASK 39 -+#define IPU_LIB_CALL_TIMEOUT_MS 2000 -+#define IPU_PSYS_CMD_TIMEOUT_MS 2000 -+#define IPU_PSYS_OPEN_CLOSE_TIMEOUT_US 50 -+#define IPU_PSYS_OPEN_CLOSE_RETRY (10000 / IPU_PSYS_OPEN_CLOSE_TIMEOUT_US) -+ -+#define IPU_ISYS_NAME "isys" -+#define IPU_PSYS_NAME "psys" -+ -+#define IPU_MMU_ADDR_BITS 32 -+/* FW is accessible within the first 2 GiB only in non-secure mode. */ -+#define IPU_MMU_ADDR_BITS_NON_SECURE 31 -+ -+#define IPU7_IS_MMU_NUM 4U -+#define IPU7_PS_MMU_NUM 4U -+#define IPU7P5_IS_MMU_NUM 4U -+#define IPU7P5_PS_MMU_NUM 4U -+#define IPU8_IS_MMU_NUM 5U -+#define IPU8_PS_MMU_NUM 4U -+#define IPU_MMU_MAX_NUM 5U /* max(IS, PS) */ -+#define IPU_MMU_MAX_TLB_L1_STREAMS 40U -+#define IPU_MMU_MAX_TLB_L2_STREAMS 40U -+#define IPU_ZLX_MAX_NUM 32U -+#define IPU_ZLX_POOL_NUM 8U -+#define IPU_UAO_PLANE_MAX_NUM 64U -+ -+/* -+ * To maximize the IOSF utlization, IPU need to send requests in bursts. -+ * At the DMA interface with the buttress, there are CDC FIFOs with burst -+ * collection capability. CDC FIFO burst collectors have a configurable -+ * threshold and is configured based on the outcome of performance measurements. -+ * -+ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 -+ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 -+ * -+ * Threshold values are pre-defined and are arrived at after performance -+ * evaluations on a type of IPU -+ */ -+#define IPU_MAX_VC_IOSF_PORTS 4 -+ -+/* -+ * IPU must configure correct arbitration mechanism related to the IOSF VC -+ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on -+ * stall and 1 means stall until the request is completed. -+ */ -+#define IPU_BTRS_ARB_MODE_TYPE_REARB 0 -+#define IPU_BTRS_ARB_MODE_TYPE_STALL 1 -+ -+/* Currently chosen arbitration mechanism for VC0 */ -+#define IPU_BTRS_ARB_STALL_MODE_VC0 IPU_BTRS_ARB_MODE_TYPE_REARB -+ -+/* Currently chosen arbitration mechanism for VC1 */ -+#define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB -+ -+struct ipu7_isys_subdev_pdata; -+ -+/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ -+#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) -+/* Max L2 blocks per stream */ -+#define IPU_MMUV2_MAX_L2_BLOCKS 2 -+/* Max L1 blocks per stream */ -+#define IPU_MMUV2_MAX_L1_BLOCKS 16 -+#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \ -+ IPU_MMUV2_MAX_L2_BLOCKS) -+/* Entries per L1 block */ -+#define MMUV2_ENTRIES_PER_L1_BLOCK 16 -+#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) -+#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE -+ -+struct ipu7_mmu_hw { -+ char name[32]; -+ -+ void __iomem *base; -+ void __iomem *zlx_base; -+ void __iomem *uao_base; -+ -+ u32 offset; -+ u32 zlx_offset; -+ u32 uao_offset; -+ -+ u32 info_bits; -+ u32 refill; -+ u32 collapse_en_bitmap; -+ u32 at_sp_arb_cfg; -+ -+ u32 l1_block; -+ u32 l2_block; -+ -+ u8 nr_l1streams; -+ u8 nr_l2streams; -+ u32 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS]; -+ u32 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS]; -+ -+ u8 zlx_nr; -+ u32 zlx_axi_pool[IPU_ZLX_POOL_NUM]; -+ u32 zlx_en[IPU_ZLX_MAX_NUM]; -+ u32 zlx_conf[IPU_ZLX_MAX_NUM]; -+ -+ u32 uao_p_num; -+ u32 uao_p2tlb[IPU_UAO_PLANE_MAX_NUM]; -+}; -+ -+struct ipu7_mmu_pdata { -+ u32 nr_mmus; -+ struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -+ int mmid; -+}; -+ -+struct ipu7_isys_csi2_pdata { -+ void __iomem *base; -+}; -+ -+struct ipu7_isys_internal_csi2_pdata { -+ u32 nports; -+ u32 const *offsets; -+ u32 gpreg; -+}; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+struct ipu7_isys_internal_tpg_pdata { -+ u32 ntpgs; -+ u32 const *offsets; -+ u32 *sels; -+}; -+#endif -+ -+struct ipu7_hw_variants { -+ unsigned long offset; -+ u32 nr_mmus; -+ struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -+ u8 cdc_fifos; -+ u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS]; -+ u32 dmem_offset; -+ u32 spc_offset; /* SPC offset from psys base */ -+}; -+ -+struct ipu_isys_internal_pdata { -+ struct ipu7_isys_internal_csi2_pdata csi2; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ struct ipu7_isys_internal_tpg_pdata tpg; -+#endif -+ struct ipu7_hw_variants hw_variant; -+ u32 num_parallel_streams; -+ u32 isys_dma_overshoot; -+}; -+ -+struct ipu7_isys_pdata { -+ void __iomem *base; -+ const struct ipu_isys_internal_pdata *ipdata; -+ struct ipu7_isys_subdev_pdata *spdata; -+}; -+ -+struct ipu_psys_internal_pdata { -+ struct ipu7_hw_variants hw_variant; -+}; -+ -+struct ipu7_psys_pdata { -+ void __iomem *base; -+ const struct ipu_psys_internal_pdata *ipdata; -+}; -+ -+int request_cpd_fw(const struct firmware **firmware_p, const char *name, -+ struct device *device); -+void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -+ struct ipu_psys_internal_pdata *psys_ipdata); -+void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev); -+#endif /* IPU7_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/Makefile b/drivers/media/pci/intel/ipu7/psys/Makefile -new file mode 100644 -index 000000000000..33eb383a14bc ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/Makefile -@@ -0,0 +1,18 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2017 - 2024 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+intel-ipu7-psys-objs += ipu-psys.o \ -+ ipu7-psys.o \ -+ ipu7-fw-psys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o -+ -+ccflags-y += -I$(src)/ -+ccflags-y += -I$(src)/../ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -new file mode 100644 -index 000000000000..582e59c89d7b ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -@@ -0,0 +1,1545 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2013 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "ipu7.h" -+#include "ipu7-mmu.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+#include "ipu7-boot.h" -+ -+#define IPU_PSYS_NUM_DEVICES 4U -+ -+static int psys_runtime_pm_resume(struct device *dev); -+static int psys_runtime_pm_suspend(struct device *dev); -+ -+#define IPU_FW_CALL_TIMEOUT_JIFFIES \ -+ msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) -+ -+static dev_t ipu7_psys_dev_t; -+static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); -+static DEFINE_MUTEX(ipu7_psys_mutex); -+ -+static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ struct vm_area_struct *vma; -+ unsigned long start, end; -+ int npages, array_size; -+ struct page **pages; -+ struct sg_table *sgt; -+ int ret = -ENOMEM; -+ int nr = 0; -+ u32 flags; -+ -+ start = (unsigned long)attach->userptr; -+ end = PAGE_ALIGN(start + attach->len); -+ npages = PHYS_PFN(end - (start & PAGE_MASK)); -+ array_size = npages * sizeof(struct page *); -+ -+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -+ if (!sgt) -+ return -ENOMEM; -+ -+ WARN_ON_ONCE(attach->npages); -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ goto free_sgt; -+ -+ mmap_read_lock(current->mm); -+ vma = vma_lookup(current->mm, start); -+ if (unlikely(!vma)) { -+ ret = -EFAULT; -+ goto error_up_read; -+ } -+ mmap_read_unlock(current->mm); -+ -+ flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; -+ nr = pin_user_pages_fast(start & PAGE_MASK, npages, -+ flags, pages); -+ if (nr < npages) -+ goto error; -+ -+ attach->pages = pages; -+ attach->npages = npages; -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, npages, -+ start & ~PAGE_MASK, attach->len, -+ GFP_KERNEL); -+ if (ret < 0) -+ goto error; -+ -+ attach->sgt = sgt; -+ -+ return 0; -+ -+error_up_read: -+ mmap_read_unlock(current->mm); -+error: -+ if (nr) -+ unpin_user_pages(pages, nr); -+ -+ kvfree(pages); -+free_sgt: -+ kfree(sgt); -+ -+ pr_err("failed to get userpages:%d\n", ret); -+ -+ return ret; -+} -+ -+static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ if (!attach || !attach->userptr || !attach->sgt) -+ return; -+ -+ unpin_user_pages(attach->pages, attach->npages); -+ -+ kvfree(attach->pages); -+ -+ sg_free_table(attach->sgt); -+ kfree(attach->sgt); -+ attach->sgt = NULL; -+} -+ -+static int ipu7_dma_buf_attach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_psys_kbuffer *kbuf = dbuf->priv; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ int ret; -+ -+ ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); -+ if (!ipu7_attach) -+ return -ENOMEM; -+ -+ ipu7_attach->len = kbuf->len; -+ ipu7_attach->userptr = kbuf->userptr; -+ -+ attach->priv = ipu7_attach; -+ -+ ret = ipu7_psys_get_userpages(ipu7_attach); -+ if (ret) { -+ kfree(ipu7_attach); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_detach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ -+ ipu7_psys_put_userpages(ipu7_attach); -+ kfree(ipu7_attach); -+ attach->priv = NULL; -+} -+ -+static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, -+ enum dma_data_direction dir) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ unsigned long attrs; -+ int ret; -+ -+ attrs = DMA_ATTR_SKIP_CPU_SYNC; -+ ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, -+ attrs); -+ if (ret) { -+ dev_err(attach->dev, "pci buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, -+ DMA_BIDIRECTIONAL); -+ -+ ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); -+ if (ret) { -+ dev_err(attach->dev, "ipu7 buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); -+ -+ return ipu7_attach->sgt; -+} -+ -+static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, -+ struct sg_table *sgt, -+ enum dma_data_direction dir) -+{ -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ -+ ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+} -+ -+static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) -+{ -+ return -ENOTTY; -+} -+ -+static void ipu7_dma_buf_release(struct dma_buf *buf) -+{ -+ struct ipu7_psys_kbuffer *kbuf = buf->priv; -+ -+ if (!kbuf) -+ return; -+ -+ if (kbuf->db_attach) -+ ipu7_psys_put_userpages(kbuf->db_attach->priv); -+ -+ kfree(kbuf); -+} -+ -+static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, -+ enum dma_data_direction dir) -+{ -+ return -ENOTTY; -+} -+ -+static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (list_empty(&dmabuf->attachments)) -+ return -EINVAL; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) -+ return -EINVAL; -+ -+ map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); -+ map->is_iomem = false; -+ if (!map->vaddr) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (WARN_ON(list_empty(&dmabuf->attachments))) -+ return; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || -+ !ipu7_attach->npages)) -+ return; -+ -+ vm_unmap_ram(map->vaddr, ipu7_attach->npages); -+} -+ -+static struct dma_buf_ops ipu7_dma_buf_ops = { -+ .attach = ipu7_dma_buf_attach, -+ .detach = ipu7_dma_buf_detach, -+ .map_dma_buf = ipu7_dma_buf_map, -+ .unmap_dma_buf = ipu7_dma_buf_unmap, -+ .release = ipu7_dma_buf_release, -+ .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, -+ .mmap = ipu7_dma_buf_mmap, -+ .vmap = ipu7_dma_buf_vmap, -+ .vunmap = ipu7_dma_buf_vunmap, -+}; -+ -+static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) -+{ -+ u8 graph_id = 0; -+ -+ for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { -+ if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) -+ break; -+ } -+ -+ if (graph_id == IPU_PSYS_NUM_STREAMS) -+ return -EBUSY; -+ -+ fh->psys->graph_id[graph_id] = graph_id; -+ return graph_id; -+} -+ -+static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) -+{ -+ fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; -+} -+ -+static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, -+ struct ipu7_bus_device *adev) -+{ -+ if (tq->msg_task) -+ ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), -+ tq->msg_task, tq->task_dma_addr, 0); -+ -+ list_del(&tq->list); -+ kfree(tq); -+} -+ -+static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct ipu_psys_task_ack *ack; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ -+ mutex_destroy(&ip->event_mutex); -+ mutex_destroy(&ip->task_mutex); -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { -+ list_del(&ack->list); -+ kfree(ack); -+ } -+} -+ -+static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ u8 i; -+ -+ INIT_LIST_HEAD(&ip->event_list); -+ INIT_LIST_HEAD(&ip->ack_list); -+ -+ INIT_LIST_HEAD(&ip->tq_running_list); -+ INIT_LIST_HEAD(&ip->tq_list); -+ -+ for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ if (!event) -+ goto event_cleanup; -+ -+ list_add(&event->list, &ip->event_list); -+ } -+ -+ for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { -+ tq = kzalloc(sizeof(*tq), GFP_KERNEL); -+ if (!tq) -+ goto tq_cleanup; -+ -+ list_add(&tq->list, &ip->tq_list); -+ -+ tq->msg_task = -+ ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), -+ &tq->task_dma_addr, GFP_KERNEL, 0); -+ -+ if (!tq->msg_task) { -+ dev_err(dev, "Failed to allocate msg task.\n"); -+ goto tq_cleanup; -+ } -+ } -+ -+ init_completion(&ip->graph_open); -+ init_completion(&ip->graph_close); -+ -+ return 0; -+ -+tq_cleanup: -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+event_cleanup: -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ return -ENOMEM; -+} -+ -+static int ipu7_psys_open(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_psys_fh *fh; -+ struct ipu7_psys_stream *ip; -+ int ret; -+ -+ fh = kzalloc(sizeof(*fh), GFP_KERNEL); -+ if (!fh) -+ return -ENOMEM; -+ -+ ip = kzalloc(sizeof(*ip), GFP_KERNEL); -+ if (!ip) { -+ ret = -ENOMEM; -+ goto alloc_failed; -+ } -+ -+ ret = ipu7_psys_stream_init(ip, psys->adev); -+ if (ret) -+ goto stream_init_failed; -+ -+ fh->ip = ip; -+ ip->fh = fh; -+ -+ fh->psys = psys; -+ -+ file->private_data = fh; -+ -+ mutex_init(&fh->mutex); -+ INIT_LIST_HEAD(&fh->bufmap); -+ init_waitqueue_head(&fh->wait); -+ -+ mutex_init(&ip->task_mutex); -+ mutex_init(&ip->event_mutex); -+ -+ mutex_lock(&psys->mutex); -+ -+ ret = ipu7_psys_get_graph_id(fh); -+ if (ret < 0) -+ goto open_failed; -+ -+ fh->ip->graph_id = ret; -+ -+ ret = pm_runtime_get_sync(dev); -+ if (ret < 0) { -+ dev_err(dev, "Runtime PM failed (%d)\n", ret); -+ goto rpm_put; -+ } -+ -+ list_add_tail(&fh->list, &psys->fhs); -+ -+ mutex_unlock(&psys->mutex); -+ -+ return 0; -+ -+rpm_put: -+ pm_runtime_put(dev); -+ ipu7_psys_put_graph_id(fh); -+ -+open_failed: -+ ipu7_psys_stream_deinit(ip, psys->adev); -+ -+ mutex_destroy(&fh->mutex); -+ -+ mutex_unlock(&psys->mutex); -+ -+stream_init_failed: -+ kfree(ip); -+ -+alloc_failed: -+ kfree(fh); -+ -+ return ret; -+} -+ -+static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ if (!kbuf) -+ return; -+ -+ kbuf->valid = false; -+ if (kbuf->kaddr) { -+ struct iosys_map dmap; -+ -+ iosys_map_set_vaddr(&dmap, kbuf->kaddr); -+ dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); -+ } -+ -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ -+ if (kbuf->sgt) -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ if (kbuf->db_attach) -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ dma_buf_put(kbuf->dbuf); -+ -+ kbuf->db_attach = NULL; -+ kbuf->dbuf = NULL; -+ kbuf->sgt = NULL; -+} -+ -+static int ipu7_psys_release(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct ipu7_psys_kbuffer *kbuf, *kbuf0; -+ struct dma_buf_attachment *dba; -+ -+ mutex_lock(&fh->mutex); -+ /* clean up buffers */ -+ if (!list_empty(&fh->bufmap)) { -+ list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { -+ list_del(&kbuf->list); -+ dba = kbuf->db_attach; -+ -+ /* Unmap and release buffers */ -+ if (kbuf->dbuf && dba) { -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ } else { -+ if (dba) -+ ipu7_psys_put_userpages(dba->priv); -+ kfree(kbuf); -+ } -+ } -+ } -+ mutex_unlock(&fh->mutex); -+ -+ ipu7_psys_stream_deinit(fh->ip, psys->adev); -+ -+ mutex_lock(&psys->mutex); -+ list_del(&fh->list); -+ -+ ipu7_psys_put_graph_id(fh); -+ kfree(fh->ip); -+ -+ mutex_unlock(&psys->mutex); -+ mutex_destroy(&fh->mutex); -+ kfree(fh); -+ -+ pm_runtime_put_sync(&psys->adev->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, -+ struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -+ struct dma_buf *dbuf; -+ int ret; -+ -+ if (!buf->base.userptr) { -+ dev_err(dev, "Buffer allocation not supported\n"); -+ return -EINVAL; -+ } -+ -+ if (!PAGE_ALIGNED(buf->base.userptr)) { -+ dev_err(dev, "Not page-aligned userptr is not supported\n"); -+ return -EINVAL; -+ } -+ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) -+ return -ENOMEM; -+ -+ kbuf->len = buf->len; -+ kbuf->userptr = buf->base.userptr; -+ kbuf->flags = buf->flags; -+ -+ exp_info.ops = &ipu7_dma_buf_ops; -+ exp_info.size = kbuf->len; -+ exp_info.flags = O_RDWR; -+ exp_info.priv = kbuf; -+ -+ dbuf = dma_buf_export(&exp_info); -+ if (IS_ERR(dbuf)) { -+ kfree(kbuf); -+ return PTR_ERR(dbuf); -+ } -+ -+ ret = dma_buf_fd(dbuf, 0); -+ if (ret < 0) { -+ dma_buf_put(dbuf); -+ return ret; -+ } -+ -+ kbuf->fd = ret; -+ buf->base.fd = ret; -+ buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; -+ buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; -+ kbuf->flags = buf->flags; -+ -+ mutex_lock(&fh->mutex); -+ list_add(&kbuf->list, &fh->bufmap); -+ mutex_unlock(&fh->mutex); -+ -+ dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", -+ buf->base.userptr, buf->len, buf->base.fd); -+ -+ return 0; -+} -+ -+static int -+ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) -+{ -+ return 0; -+} -+ -+static struct ipu7_psys_kbuffer * -+ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) -+{ -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ list_for_each_entry(kbuf, &fh->bufmap, list) { -+ if (kbuf->fd == fd) -+ return kbuf; -+ } -+ -+ return NULL; -+} -+ -+static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ -+ if (!kbuf || fd != kbuf->fd) { -+ dev_err(dev, "invalid kbuffer\n"); -+ return -EINVAL; -+ } -+ -+ /* From now on it is not safe to use this kbuffer */ -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ -+ list_del(&kbuf->list); -+ -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+ dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); -+ -+ return 0; -+} -+ -+static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct device *dev = &psys->adev->isp->pdev->dev; -+ struct dma_buf *dbuf; -+ struct iosys_map dmap; -+ int ret; -+ -+ dbuf = dma_buf_get(fd); -+ if (IS_ERR(dbuf)) -+ return -EINVAL; -+ -+ if (!kbuf) { -+ /* This fd isn't generated by ipu7_psys_getbuf, it -+ * is a new fd. Create a new kbuf item for this fd, and -+ * add this kbuf to bufmap list. -+ */ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ -+ /* fd valid and found, need remap */ -+ if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { -+ dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", -+ fd, kbuf); -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ if (ret) -+ goto mapbuf_fail; -+ -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ /* changed external dmabuf */ -+ if (!kbuf) { -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ } -+ -+ if (kbuf->sgt) { -+ dev_dbg(dev, "fd %d has been mapped!\n", fd); -+ dma_buf_put(dbuf); -+ goto mapbuf_end; -+ } -+ -+ kbuf->dbuf = dbuf; -+ -+ if (kbuf->len == 0) -+ kbuf->len = kbuf->dbuf->size; -+ -+ kbuf->fd = fd; -+ -+ kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); -+ if (IS_ERR(kbuf->db_attach)) { -+ ret = PTR_ERR(kbuf->db_attach); -+ dev_err(dev, "dma buf attach failed\n"); -+ goto attach_fail; -+ } -+ -+ kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, -+ DMA_BIDIRECTIONAL); -+ if (IS_ERR_OR_NULL(kbuf->sgt)) { -+ ret = -EINVAL; -+ kbuf->sgt = NULL; -+ dev_err(dev, "dma buf map attachment failed\n"); -+ goto kbuf_map_fail; -+ } -+ -+ if (!kbuf->userptr) { -+ ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ if (ret) { -+ dev_dbg(dev, "ipu7 buf map failed\n"); -+ goto kbuf_map_fail; -+ } -+ } -+ -+ kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); -+ -+ /* no need vmap for imported dmabufs */ -+ if (!kbuf->userptr) -+ goto mapbuf_end; -+ -+ dmap.is_iomem = false; -+ ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); -+ if (ret) { -+ dev_err(dev, "dma buf vmap failed\n"); -+ goto kbuf_map_fail; -+ } -+ kbuf->kaddr = dmap.vaddr; -+ -+mapbuf_end: -+ dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", -+ __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, -+ kbuf->len); -+ kbuf->valid = true; -+ -+ return 0; -+ -+kbuf_map_fail: -+ if (!IS_ERR_OR_NULL(kbuf->sgt)) { -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ } -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ -+attach_fail: -+ list_del(&kbuf->list); -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+mapbuf_fail: -+ dma_buf_put(dbuf); -+ -+ dev_err(dev, "%s failed for fd %d\n", __func__, fd); -+ return ret; -+} -+ -+static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ long ret; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ long ret; -+ -+ dev_dbg(dev, "IOC_UNMAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ if (!kbuf) { -+ dev_err(dev, -+ "buffer with fd %d not found\n", fd); -+ mutex_unlock(&fh->mutex); -+ return -EINVAL; -+ } -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { -+ dev_err(&psys->dev, "nodes is wrong\n"); -+ return -EINVAL; -+ } -+ -+ if (copy_from_user(fh->ip->nodes, graph->nodes, -+ graph->num_nodes * sizeof(*graph->nodes))) { -+ dev_err(&psys->dev, "Failed to copy nodes\n"); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_open); -+ -+ ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to open graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_open, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Open graph %d timeout\n", -+ fh->ip->graph_id); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Failed to set graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ graph->graph_id = fh->ip->graph_id; -+ -+ return 0; -+} -+ -+static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_close); -+ -+ ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to close graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_close, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Close graph %d timeout\n", -+ fh->ip->graph_id); -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Failed to close graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct ipu_psys_task_queue * -+ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_request *task) -+{ -+ struct device *dev = &ip->fh->psys->dev; -+ struct ipu7_psys_kbuffer *kbuf = NULL; -+ struct ipu_psys_task_queue *tq; -+ int fd, prevfd = -1; -+ u32 i; -+ -+ if (task->term_buf_count > MAX_GRAPH_TERMINALS) { -+ dev_err(dev, "num_teminal_buffer is too large\n"); -+ return NULL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (list_empty(&ip->tq_list)) { -+ dev_err(dev, "No available take queue for stream %p\n", ip); -+ goto unlock; -+ } -+ -+ tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, -+ list); -+ -+ if (copy_from_user(tq->task_buffers, -+ task->task_buffers, -+ task->term_buf_count * -+ sizeof(*task->task_buffers))) { -+ dev_err(dev, "failed to copy task buffers\n"); -+ goto unlock; -+ } -+ -+ for (i = 0; i < task->term_buf_count; i++) { -+ fd = tq->task_buffers[i].term_buf.base.fd; -+ kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); -+ if (!kbuf) { -+ dev_err(dev, "fd %d not found\n", fd); -+ goto unlock; -+ } -+ tq->ipu7_addr[i] = kbuf->dma_addr -+ + tq->task_buffers[i].term_buf.data_offset; -+ -+ if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & -+ IPU_BUFFER_FLAG_NO_FLUSH)) -+ continue; -+ -+ prevfd = fd; -+ -+ if (kbuf->kaddr) -+ clflush_cache_range(kbuf->kaddr, kbuf->len); -+ } -+ -+ dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_running_list); -+ -+ mutex_unlock(&ip->task_mutex); -+ return tq; -+ -+unlock: -+ mutex_unlock(&ip->task_mutex); -+ return NULL; -+} -+ -+static long ipu_psys_task_request(struct ipu_psys_task_request *task, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct ipu_psys_task_queue *tq; -+ int ret = 0; -+ -+ if (task->term_buf_count == 0 || !task->task_buffers) { -+ dev_err(&psys->dev, "task_buffer is NULL\n"); -+ return -EINVAL; -+ } -+ -+ tq = ipu7_psys_get_task_queue(fh->ip, task); -+ if (!tq) { -+ dev_err(&psys->dev, "Failed to get task queue\n"); -+ return -EINVAL; -+ } -+ -+ ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to request task %d\n", -+ fh->ip->graph_id); -+ mutex_lock(&fh->ip->task_mutex); -+ list_move_tail(&tq->list, &fh->ip->tq_list); -+ mutex_unlock(&fh->ip->task_mutex); -+ return ret; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; -+ -+ return 0; -+} -+ -+static unsigned int ipu7_psys_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_stream *ip = fh->ip; -+ unsigned int res = 0; -+ -+ dev_dbg(dev, "ipu psys poll\n"); -+ -+ poll_wait(file, &fh->wait, wait); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->ack_list)) -+ res = POLLIN; -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(dev, "ipu psys poll res %u\n", res); -+ -+ return res; -+} -+ -+static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ union { -+ struct ipu_psys_graph_info graph; -+ struct ipu_psys_task_request task; -+ struct ipu_psys_buffer buf; -+ struct ipu_psys_event ev; -+ } karg; -+ struct ipu7_psys_fh *fh = file->private_data; -+ long err = 0; -+ void __user *up = (void __user *)arg; -+ bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && -+ cmd != IPU_IOC_GRAPH_CLOSE); -+ -+ if (copy) { -+ if (_IOC_SIZE(cmd) > sizeof(karg)) -+ return -ENOTTY; -+ -+ if (_IOC_DIR(cmd) & _IOC_WRITE) { -+ err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); -+ if (err) -+ return -EFAULT; -+ } -+ } -+ -+ switch (cmd) { -+ case IPU_IOC_MAPBUF: -+ err = ipu7_psys_mapbuf(arg, fh); -+ break; -+ case IPU_IOC_UNMAPBUF: -+ err = ipu7_psys_unmapbuf(arg, fh); -+ break; -+ case IPU_IOC_GETBUF: -+ err = ipu7_psys_getbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_PUTBUF: -+ err = ipu7_psys_putbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_GRAPH_OPEN: -+ err = ipu_psys_graph_open(&karg.graph, fh); -+ break; -+ case IPU_IOC_GRAPH_CLOSE: -+ err = ipu_psys_graph_close(arg, fh); -+ break; -+ case IPU_IOC_TASK_REQUEST: -+ err = ipu_psys_task_request(&karg.task, fh); -+ break; -+ case IPU_IOC_DQEVENT: -+ err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); -+ break; -+ default: -+ err = -ENOTTY; -+ break; -+ } -+ -+ if (err) -+ return err; -+ -+ if (copy && _IOC_DIR(cmd) & _IOC_READ) -+ if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static const struct file_operations ipu7_psys_fops = { -+ .open = ipu7_psys_open, -+ .release = ipu7_psys_release, -+ .unlocked_ioctl = ipu7_psys_ioctl, -+ .poll = ipu7_psys_poll, -+ .owner = THIS_MODULE, -+}; -+ -+static void ipu7_psys_dev_release(struct device *dev) -+{ -+} -+ -+static int psys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ int ret; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ if (!ipu_buttress_auth_done(adev->isp)) { -+ dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); -+ return 0; -+ } -+ -+ ipu7_psys_setup_hw(psys); -+ -+ ipu7_psys_subdomains_power(psys, 1); -+ -+ ret = ipu7_boot_start_fw(psys->adev); -+ if (ret) { -+ dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); -+ return ret; -+ } -+ -+ ret = ipu7_fw_psys_open(psys); -+ if (ret) { -+ dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); -+ return ret; -+ } -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ psys->ready = 1; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return 0; -+} -+ -+static int psys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (!psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ psys->ready = 0; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ipu7_fw_psys_close(psys); -+ -+ ipu7_boot_stop_fw(psys->adev); -+ -+ ipu7_psys_subdomains_power(psys, 0); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+/* The following PM callbacks are needed to enable runtime PM in IPU PCI -+ * device resume, otherwise, runtime PM can't work in PCI resume from -+ * S3 state. -+ */ -+static int psys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static int psys_suspend(struct device *dev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(dev); -+ unsigned long flags; -+ int ret = 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) -+ ret = -EBUSY; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops psys_pm_ops = { -+ .runtime_suspend = psys_runtime_pm_suspend, -+ .runtime_resume = psys_runtime_pm_resume, -+ .suspend = psys_suspend, -+ .resume = psys_resume, -+}; -+ -+#ifdef CONFIG_DEBUG_FS -+static int psys_fw_log_init(struct ipu7_psys *psys) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log; -+ void *log_buf; -+ -+ if (psys->fw_log) -+ return 0; -+ -+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -+ if (!fw_log) -+ return -ENOMEM; -+ -+ mutex_init(&fw_log->mutex); -+ -+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!log_buf) -+ return -ENOMEM; -+ -+ fw_log->head = log_buf; -+ fw_log->addr = log_buf; -+ fw_log->count = 0; -+ fw_log->size = 0; -+ -+ psys->fw_log = fw_log; -+ -+ return 0; -+} -+ -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_psys *psys = file->private_data; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ struct device *dev = &psys->adev->auxdev.dev; -+ u32 log_size; -+ void *buf; -+ int ret = 0; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations psys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, psys, &psys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ psys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ -+static const struct bus_type ipu7_psys_bus = { -+ .name = "intel-ipu7-psys", -+}; -+ -+static int ipu7_psys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ struct device *dev = &auxdev->dev; -+ struct ipu7_psys *psys; -+ unsigned int minor; -+ unsigned int i; -+ int ret; -+ -+ if (!adev->isp->ipu7_bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, -+ IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); -+ if (ret) { -+ dev_err(dev, "can't alloc psys chrdev region (%d)\n", -+ ret); -+ return ret; -+ } -+ -+ ret = pm_runtime_resume_and_get(&auxdev->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ goto out_unregister_chr_region; -+ -+ mutex_lock(&ipu7_psys_mutex); -+ -+ minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); -+ if (minor == IPU_PSYS_NUM_DEVICES) { -+ dev_err(dev, "too many devices\n"); -+ goto out_unlock; -+ } -+ -+ psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); -+ if (!psys) { -+ ret = -ENOMEM; -+ goto out_unlock; -+ } -+ -+ for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) -+ psys->graph_id[i] = INVALID_STREAM_ID; -+ -+ adev->auxdrv_data = -+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(dev->driver); -+ -+ psys->adev = adev; -+ psys->pdata = adev->pdata; -+ -+ cdev_init(&psys->cdev, &ipu7_psys_fops); -+ psys->cdev.owner = ipu7_psys_fops.owner; -+ -+ ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); -+ if (ret) { -+ dev_err(dev, "cdev_add failed (%d)\n", ret); -+ goto out_unlock; -+ } -+ -+ set_bit(minor, ipu7_psys_devices); -+ -+ spin_lock_init(&psys->ready_lock); -+ -+ psys->ready = 0; -+ psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; -+ -+ mutex_init(&psys->mutex); -+ INIT_LIST_HEAD(&psys->fhs); -+ -+ ret = ipu7_fw_psys_init(psys); -+ if (ret) { -+ dev_err(dev, "FW init failed(%d)\n", ret); -+ goto out_mutex_destroy; -+ } -+ -+ psys->dev.bus = &ipu7_psys_bus; -+ psys->dev.parent = dev; -+ psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); -+ psys->dev.release = ipu7_psys_dev_release; -+ dev_set_name(&psys->dev, "ipu7-psys%d", minor); -+ ret = device_register(&psys->dev); -+ if (ret < 0) { -+ dev_err(&psys->dev, "psys device_register failed\n"); -+ goto out_fw_release; -+ } -+ -+ dev_set_drvdata(dev, psys); -+#ifdef CONFIG_DEBUG_FS -+ psys_fw_log_init(psys); -+ ipu7_psys_init_debugfs(psys); -+#endif -+ dev_info(dev, "IPU psys probe done.\n"); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ pm_runtime_put(&auxdev->dev); -+ -+ return 0; -+ -+out_fw_release: -+ ipu7_fw_psys_release(psys); -+out_mutex_destroy: -+ mutex_destroy(&psys->mutex); -+ cdev_del(&psys->cdev); -+out_unlock: -+ /* Safe to call even if the init is not called */ -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+out_unregister_chr_region: -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ pm_runtime_put(&auxdev->dev); -+ -+ return ret; -+} -+ -+static void ipu7_psys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); -+ struct device *dev = &auxdev->dev; -+#ifdef CONFIG_DEBUG_FS -+ struct ipu7_device *isp = psys->adev->isp; -+ -+ if (isp->ipu7_dir) -+ debugfs_remove_recursive(psys->debugfsdir); -+#endif -+ -+ mutex_lock(&ipu7_psys_mutex); -+ ipu7_fw_psys_release(psys); -+ device_unregister(&psys->dev); -+ clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); -+ cdev_del(&psys->cdev); -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ mutex_destroy(&psys->mutex); -+ -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ -+ dev_info(dev, "removed\n"); -+} -+ -+static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ struct device *dev = &psys->adev->auxdev.dev; -+ void __iomem *base = psys->pdata->base; -+ u32 status, state; -+ int r; -+ -+ mutex_lock(&psys->mutex); -+ r = pm_runtime_get_if_in_use(dev); -+ if (!r || WARN_ON_ONCE(r < 0)) { -+ mutex_unlock(&psys->mutex); -+ return IRQ_NONE; -+ } -+ -+ state = ipu7_boot_get_boot_state(adev); -+ if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { -+ dev_warn(&psys->dev, "error state %u\n", state); -+ } else { -+ status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); -+ writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ -+ if (status & IRQ_FROM_LOCAL_FW) -+ ipu7_psys_handle_events(psys); -+ } -+ -+ pm_runtime_put(dev); -+ mutex_unlock(&psys->mutex); -+ -+ return IRQ_HANDLED; -+} -+ -+static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { -+ .isr_threaded = psys_isr_threaded, -+ .wake_isr_thread = true, -+}; -+ -+static const struct auxiliary_device_id ipu7_psys_id_table[] = { -+ { -+ .name = "intel_ipu7.psys", -+ .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, -+ }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); -+ -+static struct auxiliary_driver ipu7_psys_driver = { -+ .name = IPU_PSYS_NAME, -+ .probe = ipu7_psys_probe, -+ .remove = ipu7_psys_remove, -+ .id_table = ipu7_psys_id_table, -+ .driver = { -+ .pm = &psys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(ipu7_psys_driver); -+ -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_AUTHOR("Tianshu Qiu "); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 processing system driver"); -+MODULE_IMPORT_NS("INTEL_IPU7"); -+MODULE_IMPORT_NS("DMA_BUF"); -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -new file mode 100644 -index 000000000000..15ba548ecd83 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -@@ -0,0 +1,603 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2016 - 2024 Intel Corporation -+ -+#include -+#include -+ -+#include -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+#include "abi/ipu7_fw_psys_config_abi.h" -+ -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-syscom.h" -+#include "ipu7-psys.h" -+ -+#define TLV_TYPE(type) ((u32)(type) & 0x3FU) -+#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) -+ -+/* -+ * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) -+ -+/* -+ * Special node resource ID to identify a generic external node. -+ * Required when there is a link to/from IPU and that node. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_syscom_context *syscom; -+ struct ipu7_psys_config *psys_config; -+ struct syscom_queue_config *queue_configs; -+ dma_addr_t psys_config_dma_addr; -+ u32 freq; -+ int i, num_queues, ret; -+ -+ /* Allocate and init syscom context. */ -+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -+ GFP_KERNEL); -+ if (!syscom) -+ return -ENOMEM; -+ -+ adev->syscom = syscom; -+ syscom->num_input_queues = FWPS_MSG_ABI_MAX_INPUT_QUEUES; -+ syscom->num_output_queues = FWPS_MSG_ABI_MAX_OUTPUT_QUEUES; -+ num_queues = syscom->num_input_queues + syscom->num_output_queues; -+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -+ GFP_KERNEL); -+ if (!queue_configs) { -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ syscom->queue_configs = queue_configs; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = -+ IPU_PSYS_ACK_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = -+ IPU_PSYS_LOG_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = -+ IPU_PSYS_CMD_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = -+ FWPS_MSG_HOST2FW_MAX_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = -+ 0; -+ -+ for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { -+ queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; -+ queue_configs[i].token_size_in_bytes = -+ sizeof(struct ia_gofo_msg_indirect); -+ } -+ -+ /* Allocate PSYS subsys config. */ -+ psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), -+ &psys_config_dma_addr, GFP_KERNEL, 0); -+ if (!psys_config) { -+ dev_err(dev, "Failed to allocate psys subsys config.\n"); -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ psys->subsys_config = psys_config; -+ psys->subsys_config_dma_addr = psys_config_dma_addr; -+ memset(psys_config, 0, sizeof(struct ipu7_psys_config)); -+ ret = ipu_buttress_get_psys_freq(adev->isp, &freq); -+ if (ret) { -+ dev_err(dev, "Failed to get PSYS frequency.\n"); -+ ipu7_fw_psys_release(psys); -+ return ret; -+ } -+ -+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -+ freq, psys_config_dma_addr, 1U); -+ if (ret) -+ ipu7_fw_psys_release(psys); -+ return ret; -+} -+ -+void ipu7_fw_psys_release(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ -+ ipu7_boot_release_boot_config(adev); -+ if (psys->subsys_config) { -+ ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), -+ psys->subsys_config, -+ psys->subsys_config_dma_addr, 0); -+ psys->subsys_config = NULL; -+ psys->subsys_config_dma_addr = 0; -+ } -+} -+ -+static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ int ret; -+ -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ return ret; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ if (ack_header->header.tlv_header.tlv_type == type) -+ return 0; -+ -+ return -EAGAIN; -+} -+ -+static int ipu7_fw_dev_open(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_open *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys open\n"); -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | -+ IPU_MSG_DEVICE_OPEN_SEND_IRQ); -+ token->enable_power_gating = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_open(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_open(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to open PSYS dev.\n"); -+ return ret; -+ } -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev open done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN; -+ return 0; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev open timeout!\n"); -+ -+ return ret; -+} -+ -+static int ipu7_fw_dev_close(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | -+ IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+void ipu7_fw_psys_close(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_close(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to close PSYS dev.\n"); -+ return; -+ } -+ -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev close done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; -+ return; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev close timeout!\n"); -+} -+ -+static void -+ipu7_fw_psys_build_node_profile(const struct node_profile *profile, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_cb_profile *cb_profile = -+ (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*cb_profile); -+ -+ memcpy(cb_profile->profile_base.teb, profile->teb, -+ sizeof(cb_profile->profile_base.teb)); -+ -+ memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); -+ memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); -+ memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); -+ -+ cb_profile->profile_base.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); -+ cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+} -+ -+/* skip term, return false */ -+static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_term); -+ -+ if (!term->term_id && !term->buf_size) -+ return false; -+ -+ memset(msg_term, 0, sizeof(*msg_term)); -+ msg_term->term_id = term->term_id; -+ /* Disable progress message on connect terminals */ -+ msg_term->event_req_bm = 0U; -+ msg_term->payload_size = term->buf_size; -+ -+ msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); -+ msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+ return true; -+} -+ -+/* When skip processing node, just return false */ -+static bool ipu7_fw_psys_build_node(const struct graph_node *node, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_node); -+ bool ret = false; -+ u8 i = 0; -+ u8 max_terms = 0; -+ -+ memset(msg_node, 0, sizeof(*msg_node)); -+ /** -+ * Pass node info to FW, do not check for external IP and ISYS -+ * As FW expects a external node -+ */ -+ if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && -+ node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] == 0U) -+ return false; -+ } -+ -+ /** -+ * Sanity check for dummy node, TEB should set to required one -+ */ -+ if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || -+ node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || -+ node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) -+ return false; -+ } -+ -+ msg_node->node_rsrc_id = node->node_rsrc_id; -+ msg_node->node_ctx_id = node->node_ctx_id; -+ msg_node->num_frags = 1; -+ -+ *buf_ptr_ptr += buf_size; -+ -+ msg_node->profiles_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr -+ - (uintptr_t)&msg_node->profiles_list); -+ for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { -+ ipu7_fw_psys_build_node_profile(&node->profiles[i], -+ buf_ptr_ptr); -+ msg_node->profiles_list.num_elems++; -+ } -+ -+ msg_node->terms_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_node->terms_list); -+ max_terms = ARRAY_SIZE(node->terminals); -+ for (i = 0; i < max_terms && i < node->num_terms; i++) { -+ ret = ipu7_fw_psys_build_node_term(&node->terminals[i], -+ buf_ptr_ptr); -+ if (ret) -+ msg_node->terms_list.num_elems++; -+ } -+ -+ buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; -+ msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); -+ msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ return true; -+} -+ -+static bool ipu7_fw_psys_build_link(const struct graph_link *link, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; -+ -+ if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && -+ !link->ep_src.term_id && !link->ep_dst.term_id) -+ return false; -+ -+ msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; -+ msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; -+ -+ msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; -+ msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; -+ -+ msg_link->foreign_key = link->foreign_key; -+ msg_link->streaming_mode = link->streaming_mode; -+ msg_link->pbk_id = link->pbk_id; -+ msg_link->pbk_slot_id = link->pbk_slot_id; -+ msg_link->delayed_link = link->delayed_link; -+ -+ *buf_ptr_ptr += sizeof(*msg_link); -+ -+ msg_link->link_options.num_elems = 0; -+ msg_link->link_options.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_link->link_options); -+ msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); -+ msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); -+ -+ return true; -+} -+ -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *buf_ptr; -+ struct ipu7_msg_graph_open *graph_open; -+ u32 buf_size = 0; -+ bool ret = false; -+ u8 i = 0; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); -+ buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!buf_ptr) -+ return -ENODATA; -+ -+ graph_open = (struct ipu7_msg_graph_open *)buf_ptr; -+ -+ memset(graph_open, 0, sizeof(*graph_open)); -+ graph_open->graph_id = ip->graph_id; -+ graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP -+ | IPU_MSG_GRAPH_OPEN_SEND_IRQ); -+ -+ buf_ptr += sizeof(*graph_open); -+ graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->nodes); -+ for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { -+ ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); -+ if (ret) -+ graph_open->nodes.num_elems++; -+ } -+ -+ graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->links); -+ for (i = 0; i < ARRAY_SIZE(graph->links); i++) { -+ ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); -+ if (ret) -+ graph_open->links.num_elems++; -+ } -+ -+ buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); -+ graph_open->header.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); -+ graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ graph_open->header.user_token = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_graph_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->graph_id = graph_id; -+ token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP -+ | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_task *msg = tq->msg_task; -+ struct ia_gofo_msg_indirect *ind; -+ u32 node_q_id = ip->q_id[task->node_ctx_id]; -+ u32 teb_hi, teb_lo; -+ u64 teb; -+ u8 i, term_id; -+ u8 num_terms; -+ -+ ind = ipu7_syscom_get_token(ctx, node_q_id); -+ if (!ind) -+ return -ENODATA; -+ -+ memset(msg, 0, sizeof(*msg)); -+ msg->graph_id = task->graph_id; -+ msg->node_ctx_id = task->node_ctx_id; -+ msg->profile_idx = 0U; /* Only one profile on HKR */ -+ msg->frame_id = task->frame_id; -+ msg->frag_id = 0U; /* No frag, set to 0 */ -+ /* -+ * Each task has a flag indicating if ack needed, it may be used to -+ * reduce interrupts if multiple CBs supported. -+ */ -+ msg->req_done_msg = 1; -+ msg->req_done_irq = 1; -+ -+ memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, -+ sizeof(task->payload_reuse_bm)); -+ -+ teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; -+ teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; -+ teb = (teb_lo | (((u64)teb_hi) << 32)); -+ -+ num_terms = ip->nodes[msg->node_ctx_id].num_terms; -+ for (i = 0U; i < num_terms; i++) { -+ term_id = tq->task_buffers[i].term_id; -+ if ((1U << term_id) & teb) -+ msg->term_buffers[term_id] = tq->ipu7_addr[i]; -+ } -+ -+ msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); -+ msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); -+ msg->header.user_token = (uintptr_t)tq; -+ -+ ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); -+ ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); -+ ind->header.msg_options.num_elems = 0; -+ ind->header.msg_options.head_offset = 0; -+ ind->ref_header = msg->header.tlv_header; -+ ind->ref_msg_ptr = tq->task_dma_addr; -+ -+ ipu7_syscom_put_token(ctx, node_q_id); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *token; -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ return 0; -+} -+ -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys) -+{ -+ void *token; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ }; -+ -+ return 0; -+} -+ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -new file mode 100644 -index 000000000000..c43f235386d1 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2016 - 2024 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_PSYS_H -+#define IPU7_FW_PSYS_H -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+ -+#include "ipu7-syscom.h" -+ -+#include -+ -+#define IPU_PSYS_MAX_GRAPH_NUMS (8U) -+#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS -+ -+struct ipu7_msg_to_str { -+ const enum ipu7_msg_type type; -+ const char *msg; -+}; -+ -+struct ipu7_psys; -+struct ipu7_psys_stream; -+struct ipu_psys_task_queue; -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys); -+void ipu7_fw_psys_release(struct ipu7_psys *psys); -+int ipu7_fw_psys_open(struct ipu7_psys *psys); -+void ipu7_fw_psys_close(struct ipu7_psys *psys); -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip); -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys); -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys); -+#endif /* IPU7_FW_PSYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -new file mode 100644 -index 000000000000..1ce52ae75a4c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -@@ -0,0 +1,398 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2020 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-fw-psys.h" -+ -+#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+static const struct ipu7_msg_to_str ps_fw_msg[] = { -+ {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, -+ {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, -+ {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, -+ {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, -+ {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, -+ {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, -+ {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, -+ {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, -+ {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, -+}; -+ -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_device *isp = psys->adev->isp; -+ void __iomem *base = isp->base; -+ u32 mask; -+ u32 val; -+ u32 req; -+ int ret; -+ -+ /* power domain req */ -+ mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : -+ IPU7_PSYS_DOMAIN_POWER_MASK; -+ req = on ? mask : 0; -+ val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); -+ -+ dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+ if ((val & mask) == req) { -+ dev_warn(dev, -+ "psys sub-domains power already in request state.\n"); -+ return; -+ } -+ writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); -+ ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, -+ val, -+ !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && -+ ((val & mask) == req), -+ 100, DOMAIN_POWE_TIMEOUT_US); -+ if (ret) -+ dev_err(dev, -+ "Psys sub-domains power %s timeout! status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+} -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys) -+{ -+ void __iomem *base = psys->pdata->base; -+ u32 val = IRQ_FROM_LOCAL_FW; -+ -+ /* Enable TO_SW IRQ from FW */ -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ -+ /* correct the initial printf configuration */ -+ writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -+} -+ -+static struct ipu7_psys_stream* -+ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) -+{ -+ struct ipu7_psys_fh *fh; -+ -+ list_for_each_entry(fh, &psys->fhs, list) { -+ if (fh->ip->graph_id == graph_id) -+ return fh->ip; -+ } -+ -+ return NULL; -+} -+ -+static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, -+ const void *buffer) -+{ -+ const struct ipu7_msg_graph_open_ack *ack_msg = -+ (const struct ipu7_msg_graph_open_ack *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ struct ipu7_psys_stream *ip; -+ struct device *dev = &psys->dev; -+ const struct ia_gofo_tlv_header *msg_tlv_item; -+ u16 num_items; -+ u16 head_offset; -+ u32 i; -+ -+ dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto open_graph_exit; -+ } -+ -+ num_items = ack_header->header.msg_options.num_elems; -+ if (!num_items) { -+ dev_err(dev, "%s, num_items is 0\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ head_offset = ack_header->header.msg_options.head_offset; -+ msg_tlv_item = (const struct ia_gofo_tlv_header *) -+ ((u8 *)&ack_header->header.msg_options + head_offset); -+ -+ if (!msg_tlv_item) { -+ dev_err(dev, "%s: failed to get tlv item\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ for (i = 0U; i < num_items; i++) { -+ u32 option_type = msg_tlv_item->tlv_type; -+ u32 option_bytes = msg_tlv_item->tlv_len32 * -+ TLV_ITEM_ALIGNMENT; -+ struct ipu7_msg_graph_open_ack_task_q_info *msg_option = -+ (void *)msg_tlv_item; -+ -+ switch (option_type) { -+ case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: -+ /* -+ * Should do check that: -+ * - Each managed node has a queue ID -+ * - Queue ID's are sane -+ */ -+ dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", -+ msg_option->node_ctx_id, msg_option->q_id); -+ ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; -+ break; -+ default: -+ /* -+ * Only one option supported -+ */ -+ dev_err(dev, "not supported %u\n", option_type); -+ break; -+ } -+ -+ msg_tlv_item = (struct ia_gofo_tlv_header *) -+ (((u8 *)msg_tlv_item) + option_bytes); -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; -+ -+open_graph_exit: -+ complete(&ip->graph_open); -+} -+ -+static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, -+ void *buffer, u32 error) -+{ -+ const struct ipu7_msg_task_done *ack_msg = -+ (const struct ipu7_msg_task_done *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ const struct ia_gofo_msg_header *msg_header = &ack_header->header; -+ struct ipu_psys_task_queue *tq; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ struct ipu_psys_task_ack *event; -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ tq = (void *)(uintptr_t)msg_header->user_token; -+ if (!tq) { -+ dev_err(dev, "%s: task_token is NULL\n", __func__); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { -+ dev_err(dev, "%s: graph %d node %d error %d\n", __func__, -+ ack_msg->graph_id, ack_msg->node_ctx_id, -+ tq->task_state); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ return -ENOENT; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_DONE; -+ dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->event_list)) { -+ event = list_first_entry(&ip->event_list, -+ struct ipu_psys_task_ack, list); -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_move_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_dbg(dev, "event queue is full, add new one\n"); -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ -+ if (event) { -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_add_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_err(dev, "failed to alloc event buf\n"); -+ } -+ } -+ mutex_unlock(&ip->event_mutex); -+ -+ wake_up_interruptible(&ip->fh->wait); -+ -+ return 0; -+} -+ -+static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, -+ void *buffer) -+{ -+ struct ipu7_msg_graph_close_ack *ack_msg = -+ (struct ipu7_msg_graph_close_ack *)buffer; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ -+ dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto graph_close_exit; -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ -+graph_close_exit: -+ complete(&ip->graph_close); -+} -+ -+void ipu7_psys_handle_events(struct ipu7_psys *psys) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ struct device *dev = &psys->dev; -+ u32 error = 0; -+ int ret = 0; -+ -+ do { -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_psys_get_log(psys); -+#endif -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ break; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, -+ ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); -+ -+ if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { -+ dev_err(dev, "group %d, code %d, detail: %d, %d\n", -+ ack_header->err.err_group, -+ ack_header->err.err_code, -+ ack_header->err.err_detail[0], -+ ack_header->err.err_detail[1]); -+ error = (ack_header->err.err_group == -+ IPU_MSG_ERR_GROUP_TASK) ? -+ IPU_PSYS_EVT_ERROR_FRAME : -+ IPU_PSYS_EVT_ERROR_INTERNAL; -+ } -+ -+ switch (ack_header->header.tlv_header.tlv_type) { -+ case IPU_MSG_TYPE_GRAPH_OPEN_ACK: -+ ipu7_psys_handle_graph_open_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_TASK_DONE: -+ ipu7_psys_handle_task_done(psys, buffer, error); -+ break; -+ case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: -+ ipu7_psys_handle_graph_close_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_GENERAL_ERR: -+ /* already printed the log above for general error */ -+ break; -+ default: -+ dev_err(&psys->dev, "Unknown type %d\n", -+ ack_header->header.tlv_header.tlv_type); -+ } -+ } while (1); -+} -+ -+static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, -+ struct ipu_psys_event *event) -+{ -+ struct ipu_psys_task_ack *ack; -+ -+ mutex_lock(&ip->event_mutex); -+ /* Check if there is already an event in the list */ -+ if (list_empty(&ip->ack_list)) { -+ mutex_unlock(&ip->event_mutex); -+ return -EAGAIN; -+ } -+ -+ ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); -+ -+ event->graph_id = ack->graph_id; -+ event->node_ctx_id = ack->node_ctx_id; -+ event->frame_id = ack->frame_id; -+ event->error = ack->err_code; -+ -+ list_move_tail(&ack->list, &ip->event_list); -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", -+ event->graph_id, event->node_ctx_id, event->frame_id); -+ -+ return 0; -+} -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); -+ -+ if (!(f_flags & O_NONBLOCK)) { -+ ret = wait_event_interruptible(fh->wait, -+ !ipu7_psys_get_event(fh->ip, -+ event)); -+ if (ret == -ERESTARTSYS) -+ return ret; -+ } else { -+ ret = ipu7_psys_get_event(fh->ip, event); -+ } -+ -+ return ret; -+} -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -new file mode 100644 -index 000000000000..a72ce4a7c5b9 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -@@ -0,0 +1,184 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2013 - 2024 Intel Corporation */ -+ -+#ifndef IPU7_PSYS_H -+#define IPU7_PSYS_H -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-fw-psys.h" -+ -+#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq -+ -+#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 -+#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 -+#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 -+#define IPU_PSYS_LOG_QUEUE_SIZE 256 -+#define IPU_PSYS_OUT_MSG_SIZE 256 -+ -+/** -+ * Each event from FW will be first queued into a -+ * event queue, define the queue depth here -+ */ -+#define TASK_EVENT_QUEUE_SIZE 3U -+/** -+ * Each task queue from user will be first queued into -+ * a task queue, define the queue depth here -+ */ -+#define TASK_REQUEST_QUEUE_SIZE 8U -+ -+#define INVALID_STREAM_ID 0xFF -+/** -+ * Task request queues per stream -+ * -+ * Each task will first assigned a task queue buffer here, -+ * all the nodes will share the same task queue, maximum -+ * queue will be full there. -+ */ -+struct ipu_psys_task_queue { -+ struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; -+ dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; -+ -+ struct ipu7_msg_task *msg_task; -+ dma_addr_t task_dma_addr; -+ -+ struct list_head list; -+ -+ /* task state of each task input, represent ipu7_msg_task_state */ -+ enum ipu7_msg_task_state task_state; -+}; -+ -+struct psys_fw_log { -+ struct mutex mutex; /* protect whole struct */ -+ void *head; -+ void *addr; -+ u32 count; /* running counter of log */ -+ u32 size; /* actual size of log content, in bits */ -+}; -+ -+/** -+ * Task quest event context -+ * -+ * Each task request should get its event ack from FW and save -+ * to this structure and for user dequeue purpose. -+ */ -+struct ipu_psys_task_ack { -+ u8 graph_id; /* graph id of the task request */ -+ u8 node_ctx_id; /* logical node id */ -+ u8 frame_id; /* frame id of the original task request */ -+ -+ struct list_head list; -+ -+ u32 err_code; /* error indication to user */ -+}; -+ -+/** -+ * stream here is equal to pipe, each stream has -+ * its dedicated graph_id, and task request queue. -+ * -+ * For multiple stream supported design. -+ */ -+struct ipu7_psys_stream { -+ struct ipu7_psys_fh *fh; -+ -+ u8 graph_id; /* graph_id on this stream */ -+ -+ /* Handle events from FW */ -+ struct mutex event_mutex; -+ struct list_head event_list; /* Reserved event list */ -+ struct list_head ack_list; /* Received ack from FW */ -+ -+ /* Serialize task queue */ -+ struct mutex task_mutex; -+ struct list_head tq_list; /* Reserved task queue list */ -+ struct list_head tq_running_list; /* Running task sent to FW */ -+ -+ u8 num_nodes; /* Number of enabled nodes */ -+ struct graph_node nodes[MAX_GRAPH_NODES]; -+ u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ -+ -+ struct completion graph_open; -+ struct completion graph_close; -+ -+ /* Graph state, represent enum ipu7_msg_graph_state */ -+ enum ipu7_msg_graph_state graph_state; -+}; -+ -+struct task_struct; -+struct ipu7_psys { -+ struct cdev cdev; -+ struct device dev; -+ -+ struct mutex mutex; /* Psys various */ -+ int ready; /* psys fw status */ -+ spinlock_t ready_lock; /* protect psys firmware state */ -+ -+ struct list_head fhs; -+ -+ struct ipu7_psys_pdata *pdata; -+ struct ipu7_bus_device *adev; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif -+ -+ unsigned long timeout; -+ -+ struct psys_fw_log *fw_log; -+ -+ /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ -+ u8 graph_id[IPU_PSYS_NUM_STREAMS]; -+ -+ /* Device state, represent enum ipu7_msg_dev_state */ -+ enum ipu7_msg_dev_state dev_state; -+ -+ struct ipu7_psys_config *subsys_config; -+ dma_addr_t subsys_config_dma_addr; -+}; -+ -+struct ipu7_psys_fh { -+ struct ipu7_psys *psys; -+ struct mutex mutex; /* Protects bufmap & kcmds fields */ -+ struct list_head list; -+ struct list_head bufmap; -+ wait_queue_head_t wait; -+ -+ struct ipu7_psys_stream *ip; -+}; -+ -+struct ipu7_dma_buf_attach { -+ struct device *dev; -+ u64 len; -+ uintptr_t *userptr; -+ struct sg_table *sgt; -+ struct page **pages; -+ size_t npages; -+}; -+ -+struct ipu7_psys_kbuffer { -+ u64 len; -+ uintptr_t *userptr; -+ u32 flags; -+ int fd; -+ void *kaddr; -+ struct list_head list; -+ dma_addr_t dma_addr; -+ struct sg_table *sgt; -+ struct dma_buf_attachment *db_attach; -+ struct dma_buf *dbuf; -+ bool valid; /* True when buffer is usable */ -+}; -+ -+#define inode_to_ipu_psys(inode) \ -+ container_of((inode)->i_cdev, struct ipu7_psys, cdev) -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys); -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); -+void ipu7_psys_handle_events(struct ipu7_psys *psys); -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags); -+ -+#endif /* IPU7_PSYS_H */ -diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig -index 724e80a9086d..f8affa636c5c 100644 ---- a/drivers/media/platform/intel/Kconfig -+++ b/drivers/media/platform/intel/Kconfig -@@ -1,14 +1,19 @@ --# SPDX-License-Identifier: GPL-2.0-only -+config INTEL_IPU7_FPGA_PDATA -+ bool "Enable built in platform data for ipu7 fpga" -+ depends on VIDEO_INTEL_IPU7 -+ help -+ Pre-ACPI system platform data is compiled inside kernel. - --comment "Intel media platform drivers" -+ This platform data is only used for PSS stage software -+ development and mainly used for pixter or sensor enablement -+ without ACPI support. - --config VIDEO_PXA27x -- tristate "PXA27x Quick Capture Interface driver" -- depends on V4L_PLATFORM_DRIVERS -- depends on VIDEO_DEV -- depends on PXA27x || COMPILE_TEST -- select VIDEOBUF2_DMA_SG -- select SG_SPLIT -- select V4L2_FWNODE -+config INTEL_IPU7_ACPI -+ tristate "Enable IPU ACPI driver" -+ default VIDEO_INTEL_IPU7 -+ depends on I2C -+ depends on ACPI - help -- This is a v4l2 driver for the PXA27x Quick Capture Interface -+ Driver to read ACPI data from BIOS -+ -+ This driver is used to read ACPI data from BIOS -diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile -index 7e8889cbd2df..d0c41b731794 100644 ---- a/drivers/media/platform/intel/Makefile -+++ b/drivers/media/platform/intel/Makefile -@@ -1,2 +1,19 @@ --# SPDX-License-Identifier: GPL-2.0-only --obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2010 - 2022 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+ccflags-y += -I$(src)/../../pci/intel/ipu7/ -+ -+ifneq ($(filter y m, $(CONFIG_INTEL_IPU7_ACPI)),) -+obj-$(CONFIG_INTEL_IPU7_ACPI) += ipu-acpi.o \ -+ ipu-acpi-pdata.o \ -+ ipu-acpi-common.o -+else -+obj-y += ipu7-fpga-pdata.o -+endif -diff --git a/drivers/media/platform/intel/ipu-acpi-common.c b/drivers/media/platform/intel/ipu-acpi-common.c -new file mode 100644 -index 000000000000..ee7bd6c93f9a ---- /dev/null -+++ b/drivers/media/platform/intel/ipu-acpi-common.c -@@ -0,0 +1,441 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016-2024 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+ -+static int get_integer_dsdt_data(struct device *dev, const u8 *dsdt, -+ int func, u64 *out) -+{ -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ union acpi_object *obj; -+ -+ obj = acpi_evaluate_dsm(dev_handle, (void *)dsdt, 0, func, NULL); -+ if (!obj) { -+ pr_err("IPU ACPI: Could not find corresponding DSM\n"); -+ return -ENODEV; -+ } -+ -+ if (obj->type != ACPI_TYPE_INTEGER) { -+ ACPI_FREE(obj); -+ return -ENODEV; -+ } -+ *out = obj->integer.value; -+ ACPI_FREE(obj); -+ return 0; -+} -+ -+static int read_acpi_block(struct device *dev, char *id, void *data, u32 size) -+{ -+ union acpi_object *obj; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ int status; -+ u32 buffer_length; -+ -+ status = acpi_evaluate_object(dev_handle, id, NULL, &buffer); -+ if (!ACPI_SUCCESS(status)) { -+ pr_err("IPU ACPI: Could not find ACPI block with err %d", status); -+ return -ENODEV; -+ } -+ -+ obj = (union acpi_object *)buffer.pointer; -+ if (!obj || obj->type != ACPI_TYPE_BUFFER) { -+ pr_err("IPU ACPI: Could not read ACPI buffer\n"); -+ status = -ENODEV; -+ goto err; -+ } -+ -+ if (obj->buffer.length > size) { -+ pr_err("IPU ACPI: Given buffer is too small\n"); -+ status = -ENODEV; -+ goto err; -+ } -+ -+ memcpy(data, obj->buffer.pointer, min(size, obj->buffer.length)); -+ buffer_length = obj->buffer.length; -+ kfree(buffer.pointer); -+ -+ return buffer_length; -+err: -+ kfree(buffer.pointer); -+ return status; -+} -+ -+static int ipu_acpi_get_gpio_data(struct device *dev, struct ipu_gpio_info *gpio, int size, -+ u64 *gpio_num) -+{ -+ const u8 dsdt_cam_gpio[] = { -+ 0x40, 0x46, 0x23, 0x79, 0x10, 0x9e, 0xea, 0x4f, -+ 0xa5, 0xc1, 0xB5, 0xaa, 0x8b, 0x19, 0x75, 0x6f }; -+ -+ int i = 0, j = 0, retries = 0, loop = 0; -+ u64 num_gpio; -+ int rval; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_gpio, 1, &num_gpio); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get number of GPIO pins\n"); -+ return rval; -+ } -+ -+ pr_info("IPU APCI: Num of gpio found = %lld", num_gpio); -+ -+ if (num_gpio == 0) { -+ *gpio_num = num_gpio; -+ return rval; -+ } -+ -+ if (num_gpio > size) { -+ pr_err("IPU ACPI: Incorrect number of GPIO pins\n"); -+ return rval; -+ } -+ -+ /* repeat until all gpio pin is saved */ -+ while (i < num_gpio && loop <= LOOP_SIZE) { -+ u64 data; -+ struct gpio_desc *desc = NULL; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_gpio, i + 2, &data); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get GPIO data\n"); -+ return -ENODEV; -+ } -+ -+ gpio[i].func = (data & 0xff); -+ gpio[i].valid = FALSE; -+ -+ desc = gpiod_get_index(dev, NULL, i + retries, GPIOD_ASIS); -+ -+ if (!IS_ERR(desc)) { -+ unsigned short pin = desc_to_gpio(desc); -+ bool save = TRUE; -+ -+ /* always save first GPIO pin */ -+ if (i == 0) -+ save = TRUE; -+ -+ /* check subsequent GPIO pin for replicate */ -+ else { -+ for (j = 0; j <= i; j++) { -+ /* retry if same as previous pin */ -+ if (gpio[j].pin == pin) { -+ retries++; -+ save = FALSE; -+ gpiod_put(desc); -+ break; -+ } -+ } -+ } -+ -+ /* save into array */ -+ if (save == TRUE) { -+ pr_info("IPU ACPI: DSM: Pin number = %d. Func = %x.", pin, gpio[i].func); -+ gpio[i].pin = pin; -+ gpio[i].valid = TRUE; -+ gpiod_put(desc); -+ i++; -+ retries = 0; -+ } -+ } -+ loop++; -+ } -+ *gpio_num = num_gpio; -+ -+ return rval; -+} -+ -+// Callback to parse I2C resources from _CRS -+static acpi_status parse_i2c_resource(struct acpi_resource *res, void *data) -+{ -+ char **controller_path = data; -+ struct acpi_resource_i2c_serialbus *i2c; -+ -+ if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) -+ return AE_OK; -+ -+ i2c = &res->data.i2c_serial_bus; -+ if (i2c->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) -+ return AE_OK; -+ -+ *controller_path = kstrdup(i2c->resource_source.string_ptr, GFP_KERNEL); -+ return AE_CTRL_TERMINATE; -+} -+ -+// Get I2C bdf via _CRS -+static int ipu_get_i2c_bdf_crs(struct device *dev, struct ipu_i2c_info *info) -+{ -+ acpi_handle handle = ACPI_HANDLE(dev); -+ acpi_status status; -+ struct acpi_device *controller_adev; -+ struct pci_dev *pci_dev; -+ char *controller_path = NULL; -+ -+ if (!handle) { -+ dev_err(dev, "No ACPI handle\n"); -+ return -EINVAL; -+ } -+ -+ // Parse _CRS for I2C resource -+ status = acpi_walk_resources(handle, "_CRS", parse_i2c_resource, &controller_path); -+ if (ACPI_FAILURE(status) || !controller_path) { -+ dev_err(dev, "Failed to parse _CRS: %s\n", acpi_format_exception(status)); -+ return -EIO; -+ } -+ -+ // Get the I2C controller's ACPI device -+ status = acpi_get_handle(NULL, controller_path, &handle); -+ if (ACPI_FAILURE(status)) { -+ dev_err(dev, "Invalid controller path: %s\n", controller_path); -+ kfree(controller_path); -+ controller_path = NULL; -+ return -ENODEV; -+ } -+ -+ controller_adev = acpi_fetch_acpi_dev(handle); -+ if (!controller_adev) { -+ dev_err(dev, "No ACPI device for controller: %s\n", controller_path); -+ kfree(controller_path); -+ return -ENODEV; -+ } -+ -+ // Map to PCI device -+ pci_dev = acpi_get_pci_dev(handle); -+ if (!pci_dev) { -+ dev_err(dev, "No PCI device for controller: %s\n", controller_path); -+ kfree(controller_path); -+ return -ENODEV; -+ } -+ -+ // Extract bdf using pci_domain_nr() -+ snprintf(info->bdf, sizeof(info->bdf), "%04x:%02x:%02x.%x", -+ pci_domain_nr(pci_dev->bus), pci_dev->bus->number, -+ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); -+ pci_dev_put(pci_dev); -+ -+ return 0; -+} -+ -+static int ipu_acpi_get_i2c_info(struct device *dev, struct ipu_i2c_info *i2c, int size, u64 *num) -+{ -+ const u8 dsdt_cam_i2c[] = { -+ 0x49, 0x75, 0x25, 0x26, 0x71, 0x92, 0xA4, 0x4C, -+ 0xBB, 0x43, 0xC4, 0x89, 0x9D, 0x5A, 0x48, 0x81}; -+ -+ u64 num_i2c; -+ int i; -+ int rval; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, 1, &num_i2c); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get number of I2C\n"); -+ return -ENODEV; -+ } -+ -+ for (i = 0; i < num_i2c && i < size; i++) { -+ u64 data; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, i + 2, -+ &data); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get I2C data\n"); -+ return -ENODEV; -+ } -+ -+ i2c[i].bus = ((data >> 24) & 0xff); -+ i2c[i].addr = (data >> 8) & 0xff; -+ rval = ipu_get_i2c_bdf_crs(dev, i2c + i); -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get i2c bdf\n"); -+ return -ENODEV; -+ } -+ pr_info("IPU ACPI: DSM: i2c bus %d addr %x bdf: %s\n", -+ i2c[i].bus, i2c[i].addr, i2c[i].bdf); -+ } -+ -+ *num = num_i2c; -+ -+ return 0; -+} -+ -+static int match_depend(struct device *dev, const void *data) -+{ -+ return (dev && dev->fwnode == data) ? 1 : 0; -+} -+ -+int ipu_acpi_get_control_logic_data(struct device *dev, -+ struct control_logic_data **ctl_data) -+{ -+ /* CLDB data */ -+ struct control_logic_data_packed ctl_logic_data; -+ int ret; -+ -+ ret = read_acpi_block(dev, "CLDB", &ctl_logic_data, -+ sizeof(ctl_logic_data)); -+ -+ if (ret < 0) { -+ pr_err("IPU ACPI: Fail to read from CLDB"); -+ return ret; -+ } -+ -+ (*ctl_data)->type = ctl_logic_data.controllogictype; -+ (*ctl_data)->id = ctl_logic_data.controllogicid; -+ (*ctl_data)->sku = ctl_logic_data.sensorcardsku; -+ -+ pr_info("IPU ACPI: CLDB: version %d clid %d cltype %d sku %d", -+ ctl_logic_data.version, -+ ctl_logic_data.controllogicid, -+ ctl_logic_data.controllogictype, -+ ctl_logic_data.sensorcardsku); -+ -+ /* GPIO data */ -+ ret = ipu_acpi_get_gpio_data(dev, (*ctl_data)->gpio, ARRAY_SIZE((*ctl_data)->gpio), -+ &((*ctl_data)->gpio_num)); -+ -+ if (ret < 0) { -+ dev_err(dev, "Failed to get GPIO data"); -+ return ret; -+ } -+ return 0; -+} -+ -+int ipu_acpi_get_dep_data(struct device *dev, -+ struct control_logic_data *ctl_data) -+{ -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ struct acpi_handle_list dep_devices; -+ acpi_status status; -+ int i; -+ int rval; -+ -+ ctl_data->completed = false; -+ -+ if (!acpi_has_method(dev_handle, "_DEP")) { -+ pr_err("IPU ACPI: %s does not have _DEP method", dev_name(dev)); -+ return 0; -+ } -+ -+ if (!acpi_evaluate_reference(dev_handle, "_DEP", NULL, &dep_devices)) { -+ pr_err("IPU ACPI: %s failed to evaluate _DEP", dev_name(dev)); -+ return -ENODEV; -+ } -+ -+ for (i = 0; i < dep_devices.count; i++) { -+ struct acpi_device *device; -+ struct acpi_device_info *info; -+ struct device *p_dev; -+ int match; -+ -+ status = acpi_get_object_info(dep_devices.handles[i], &info); -+ if (ACPI_FAILURE(status)) { -+ pr_err("IPU ACPI: %s error reading _DEP device info", dev_name(dev)); -+ continue; -+ } -+ -+ match = info->valid & ACPI_VALID_HID && -+ !strcmp(info->hardware_id.string, "INT3472"); -+ -+ kfree(info); -+ -+ if (!match) -+ continue; -+ -+ /* Process device IN3472 created by acpi */ -+ device = acpi_fetch_acpi_dev(dep_devices.handles[i]); -+ if (!device) { -+ pr_err("IPU ACPI: Failed to get ACPI device"); -+ return -ENODEV; -+ } -+ -+ pr_info("IPU ACPI: Dependent ACPI device found: %s\n", -+ dev_name(&device->dev)); -+ -+ p_dev = bus_find_device(&platform_bus_type, NULL, -+ &device->fwnode, match_depend); -+ -+ if (p_dev) { -+ pr_info("IPU ACPI: Dependent platform device found %s\n", -+ dev_name(p_dev)); -+ -+ /* obtain Control Logic Data from BIOS */ -+ rval = ipu_acpi_get_control_logic_data(p_dev, &ctl_data); -+ -+ if (rval) { -+ pr_err("IPU ACPI: Error getting Control Logic Data"); -+ return rval; -+ } -+ -+ ctl_data->completed = true; -+ } else -+ pr_err("IPU ACPI: Dependent platform device not found for %s\n", dev_name(dev)); -+ } -+ -+ if (!ctl_data->completed) { -+ ctl_data->type = CL_EMPTY; -+ pr_err("IPU APCI: No control logic data available"); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_acpi_get_dep_data); -+ -+int ipu_acpi_get_cam_data(struct device *dev, -+ struct sensor_bios_data *sensor) -+{ -+ /* SSDB */ -+ struct sensor_bios_data_packed sensor_data; -+ int ret; -+ -+ ret = read_acpi_block(dev, "SSDB", &sensor_data, -+ sizeof(sensor_data)); -+ -+ if (ret < 0) { -+ pr_err("IPU ACPI: Fail to read from SSDB with err %d", ret); -+ return ret; -+ } -+ -+ /* Xshutdown is not part of the ssdb data */ -+ sensor->link = sensor_data.link; -+ sensor->lanes = sensor_data.lanes; -+ sensor->pprval = sensor_data.pprval; -+ sensor->pprunit = sensor_data.pprunit; -+ sensor->bus_type = sensor_data.phyconfig; -+ sensor->degree = sensor_data.degree; -+ -+ pr_info("IPU ACPI: SSDB: name %s. link %d. lanes %d. pprval %d. pprunit %x. degree %d", -+ dev_name(dev), sensor->link, sensor->lanes, sensor->pprval, sensor->pprunit, -+ sensor->degree); -+ -+ /* I2C */ -+ ret = ipu_acpi_get_i2c_info(dev, sensor->i2c, ARRAY_SIZE(sensor->i2c), &sensor->i2c_num); -+ -+ if (ret < 0) { -+ pr_err("IPU ACPI: Failed to get I2C info"); -+ return ret; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_acpi_get_cam_data); -+ -+MODULE_AUTHOR("Samu Onkalo "); -+MODULE_AUTHOR("Khai Wen Ng "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU ACPI support"); -diff --git a/drivers/media/platform/intel/ipu-acpi-pdata.c b/drivers/media/platform/intel/ipu-acpi-pdata.c -new file mode 100644 -index 000000000000..0a84dccf980c ---- /dev/null -+++ b/drivers/media/platform/intel/ipu-acpi-pdata.c -@@ -0,0 +1,594 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+ -+#define MIN_SENSOR_I2C 1 -+#define MIN_SERDES_I2C 3 -+#define SUFFIX_BASE 97 -+ -+struct ipu7_isys_subdev_pdata acpi_subdev_pdata = { -+ .subdevs = (struct ipu7_isys_subdev_info *[]) { -+ NULL, -+ } -+}; -+ -+struct serdes_local serdes_info; -+ -+/* -+ * The dev_id was hard code in platform data, as i2c bus number -+ * may change dynamiclly, we need to update this bus id -+ * accordingly. -+ * -+ * @adapter_id: hardware i2c adapter id, this was fixed in platform data -+ * return: i2c bus id registered in system -+ */ -+static int get_i2c_bus_id(int adapter_id, char *adapter_bdf, int bdf_len) -+{ -+ struct i2c_adapter *adapter; -+ char name[32]; -+ int i = 0; -+ -+ if (adapter_bdf) { -+ while ((adapter = i2c_get_adapter(i)) != NULL) { -+ struct device *parent = adapter->dev.parent; -+ struct device *pp = parent->parent; -+ -+ if (pp && !strncmp(adapter_bdf, dev_name(pp), bdf_len)) -+ return i; -+ i++; -+ } -+ } -+ -+ i = 0; -+ snprintf(name, sizeof(name), "i2c_designware.%d", adapter_id); -+ while ((adapter = i2c_get_adapter(i)) != NULL) { -+ struct device *parent = adapter->dev.parent; -+ -+ if (parent && !strncmp(name, dev_name(parent), sizeof(name))) -+ return i; -+ i++; -+ } -+ -+ /* Not found, should never happen! */ -+ WARN_ON_ONCE(1); -+ return -1; -+} -+ -+/* -+ * update i2c bus here to avoid deadlock between i2c_for_each_dev -+ * and i2c_get_adapter -+ */ -+static void update_i2c_bus_id(void) -+{ -+ struct ipu7_isys_subdev_info **subdevs = acpi_subdev_pdata.subdevs; -+ for (int i = 0; subdevs[i] != NULL; i++) { -+ subdevs[i]->i2c.i2c_adapter_id = -+ get_i2c_bus_id(subdevs[i]->i2c.i2c_adapter_id, -+ subdevs[i]->i2c.i2c_adapter_bdf, -+ sizeof(subdevs[i]->i2c.i2c_adapter_bdf)); -+ } -+} -+ -+struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void) -+{ -+ struct ipu7_isys_subdev_pdata *ptr; -+ -+ update_i2c_bus_id(); -+ ptr = &acpi_subdev_pdata; -+ return ptr; -+} -+EXPORT_SYMBOL(get_acpi_subdev_pdata); -+ -+static void print_serdes_sdinfo(struct serdes_subdev_info *sdinfo) -+{ -+ int i; -+ struct serdes_module_pdata *sd_mpdata = sdinfo->board_info.platform_data; -+ -+ if (!sd_mpdata) { -+ pr_err("Empty serdes module pdata"); -+ return; -+ } -+ -+ pr_debug("\t\trx_port \t\t= %d", sdinfo->rx_port); -+ pr_debug("\t\tphy_i2c_addr \t\t= 0x%x", sdinfo->phy_i2c_addr); -+ pr_debug("\t\tser_alias \t\t= 0x%x", sdinfo->ser_alias); -+ pr_debug("\t\tser_phys_addr \t\t= 0x%x", sdinfo->ser_phys_addr); -+ pr_debug("\t\tsuffix \t\t\t= %s", sdinfo->suffix); -+ pr_debug("\t\tboard_info.type \t= %s", sdinfo->board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sdinfo->board_info.addr); -+ -+ pr_debug("serdes board_info.platform_data"); -+ pr_debug("\t\tlanes \t\t\t= %d", sd_mpdata->lanes); -+ pr_debug("\t\tmodule_name \t\t= %s", sd_mpdata->module_name); -+ pr_debug("\t\tfsin \t\t\t= %d", sd_mpdata->fsin); -+ -+ if (serdes_info.gpio_powerup_seq > 0) -+ for (i = 0; i < serdes_info.gpio_powerup_seq; i++) -+ pr_debug("\t\t gpio_powerup_seq[%d] \t= %d", i, -+ (int)sd_mpdata->gpio_powerup_seq[i]); -+} -+ -+static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) -+{ -+ struct serdes_platform_data *sd_pdata = sd->i2c.board_info.platform_data; -+ int i; -+ struct serdes_subdev_info *sd_sdinfo; -+ struct serdes_module_pdata *sd_mpdata; -+ -+ if (!sd_pdata) { -+ pr_err("Empty serdes subdev pdata"); -+ return; -+ } -+ -+ pr_debug("IPU ACPI: %s", __func__); -+ pr_debug("sd_csi2"); -+ pr_debug("\t\tnlanes \t\t\t= %d", sd->csi2->nlanes); -+ pr_debug("\t\tport \t\t\t= %d", sd->csi2->port); -+ pr_debug("\t\ttype \t\t\t= %d", sd->csi2->bus_type); -+ -+ pr_debug("sd->i2c"); -+ pr_debug("\t\ti2c_adapter_bdf \t= %s", sd->i2c.i2c_adapter_bdf); -+ pr_debug("\t\tboard_info.type \t= %s", sd->i2c.board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sd->i2c.board_info.addr); -+ -+ pr_debug("sd->i2c.board_info.platform_data"); -+ pr_debug("\t\treset_gpio \t\t= %d", sd_pdata->reset_gpio); -+ pr_debug("\t\tFPD_gpio \t\t= %d", sd_pdata->FPD_gpio); -+ pr_debug("\t\tsuffix \t\t\t= %c", sd_pdata->suffix); -+ -+ pr_debug("\t\tlink_freq_mbps \t\t= %d", sd_pdata->link_freq_mbps); -+ pr_debug("\t\tdeser_nlanes \t\t= %d", sd_pdata->deser_nlanes); -+ pr_debug("\t\tser_nlanes \t\t= %d", sd_pdata->ser_nlanes); -+ -+ for (i = 0; i < serdes_info.rx_port; i++) { -+ sd_sdinfo = &sd_pdata->subdev_info[i]; -+ sd_mpdata = sd_sdinfo->board_info.platform_data; -+ -+ if (!sd_mpdata) -+ continue; -+ -+ pr_debug("serdes subdev_info[%d]", i); -+ print_serdes_sdinfo(sd_sdinfo); -+ } -+ -+} -+ -+static void print_subdev(struct ipu7_isys_subdev_info *sd) -+{ -+ struct sensor_platform_data *spdata = sd->i2c.board_info.platform_data; -+ int i; -+ -+ if (!spdata) { -+ pr_err("IPU ACPI: Empty sensor subdev"); -+ return; -+ } -+ -+ pr_debug("IPU ACPI: %s", __func__); -+ pr_debug("sd->csi2"); -+ pr_debug("\t\tnlanes \t\t\t= %d", sd->csi2->nlanes); -+ pr_debug("\t\tport \t\t\t= %d", sd->csi2->port); -+ pr_debug("\t\ttype \t\t\t= %d", sd->csi2->bus_type); -+ -+ pr_debug("sd->i2c"); -+ pr_debug("\t\ti2c_adapter_bdf \t= %s", sd->i2c.i2c_adapter_bdf); -+ pr_debug("\t\tboard_info.type \t= %s", sd->i2c.board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sd->i2c.board_info.addr); -+ -+ pr_debug("sd->i2c.platform_data"); -+ pr_debug("\t\tport \t\t\t= %d", spdata->port); -+ pr_debug("\t\tlanes \t\t\t= %d", spdata->lanes); -+ pr_debug("\t\ti2c_slave_address \t= 0x%x", spdata->i2c_slave_address); -+ pr_debug("\t\tirq_pin \t\t= %d", spdata->irq_pin); -+ pr_debug("\t\tirq_pin_name \t\t= %s", spdata->irq_pin_name); -+ pr_debug("\t\tsuffix \t\t\t= %c", spdata->suffix); -+ pr_debug("\t\treset_pin \t\t= %d", spdata->reset_pin); -+ pr_debug("\t\tdetect_pin \t\t= %d", spdata->detect_pin); -+ -+ for (i = 0; i < IPU7_SPDATA_GPIO_NUM; i++) -+ pr_debug("\t\tgpios[%d] \t\t= %d", i, spdata->gpios[i]); -+} -+ -+static void set_common_gpio(struct control_logic_data *ctl_data, -+ struct sensor_platform_data **pdata) -+{ -+ int i; -+ -+ /* TODO: consider remove specific naming such as irq_pin, and use gpios[] */ -+ (*pdata)->irq_pin = -1; -+ (*pdata)->reset_pin = -1; -+ (*pdata)->detect_pin = -1; -+ -+ (*pdata)->gpios[0] = -1; -+ (*pdata)->gpios[1] = 0; -+ (*pdata)->gpios[2] = 0; -+ (*pdata)->gpios[3] = 0; -+ -+ /* all sensors should have RESET GPIO */ -+ if (ctl_data->completed && ctl_data->gpio_num > 0) -+ for (i = 0; i < ctl_data->gpio_num; i++) -+ if (ctl_data->gpio[i].func != GPIO_RESET) -+ dev_err(ctl_data->dev, -+ "IPU ACPI: Invalid GPIO func: %d\n", -+ ctl_data->gpio[i].func); -+} -+ -+static int set_csi2(struct ipu7_isys_subdev_info **sensor_sd, -+ unsigned int lanes, unsigned int port, -+ unsigned int bus_type) -+{ -+ struct ipu7_isys_csi2_config *csi2_config; -+ -+ csi2_config = kzalloc(sizeof(*csi2_config), GFP_KERNEL); -+ if (!csi2_config) -+ return -ENOMEM; -+ -+ csi2_config->nlanes = lanes; -+ csi2_config->port = port; -+ if (bus_type == PHY_MODE_DPHY) -+ csi2_config->bus_type = V4L2_MBUS_CSI2_DPHY; -+ else if (bus_type == PHY_MODE_CPHY) -+ csi2_config->bus_type = V4L2_MBUS_CSI2_CPHY; -+ else -+ csi2_config->bus_type = V4L2_MBUS_UNKNOWN; -+ -+ (*sensor_sd)->csi2 = csi2_config; -+ -+ return 0; -+} -+ -+static void set_i2c(struct ipu7_isys_subdev_info **sensor_sd, -+ struct device *dev, -+ const char *sensor_name, -+ unsigned int addr, -+ const char *i2c_adapter_bdf) -+{ -+ dev_info(dev, "IPU ACPI: kernel I2C BDF: %s", i2c_adapter_bdf); -+ (*sensor_sd)->i2c.board_info.addr = addr; -+ strscpy((*sensor_sd)->i2c.board_info.type, sensor_name, I2C_NAME_SIZE); -+ strscpy((*sensor_sd)->i2c.i2c_adapter_bdf, i2c_adapter_bdf, -+ sizeof((*sensor_sd)->i2c.i2c_adapter_bdf)); -+} -+ -+static void set_serdes_sd_pdata(struct serdes_module_pdata **module_pdata, -+ const char *sensor_name, const char *hid_name, -+ unsigned int lanes) -+{ -+ /* general */ -+ (*module_pdata)->lanes = lanes; -+ strscpy((*module_pdata)->module_name, sensor_name, I2C_NAME_SIZE); -+} -+ -+#define PORT_NR 8 -+ -+static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, -+ struct device *dev, -+ struct serdes_platform_data **pdata, -+ const char *sensor_name, -+ const char *hid_name, -+ unsigned int lanes, -+ unsigned int addr, -+ unsigned int subdev_num) -+{ -+ int i; -+ struct serdes_module_pdata *module_pdata[PORT_NR]; -+ struct serdes_subdev_info *serdes_sdinfo; -+ size_t subdev_size = subdev_num * sizeof(*serdes_sdinfo); -+ unsigned int port = (*pdata)->suffix - SUFFIX_BASE; -+ -+ serdes_sdinfo = kzalloc(subdev_size, GFP_KERNEL); -+ if (!serdes_sdinfo) -+ return -ENOMEM; -+ -+ for (i = 0; i < subdev_num; i++) { -+ module_pdata[i] = kzalloc(sizeof(*module_pdata[i]), GFP_KERNEL); -+ if (!module_pdata[i]) { -+ kfree(serdes_sdinfo); -+ return -ENOMEM; -+ } -+ -+ set_serdes_sd_pdata(&module_pdata[i], sensor_name, hid_name, lanes); -+ -+ /* board info */ -+ strscpy(serdes_sdinfo[i].board_info.type, sensor_name, I2C_NAME_SIZE); -+ serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ serdes_sdinfo[i].board_info.platform_data = module_pdata[i]; -+ -+ /* serdes_subdev_info */ -+ serdes_sdinfo[i].rx_port = i; -+ serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; -+ -+ serdes_sdinfo[i].phy_i2c_addr = serdes_info.phy_i2c_addr; -+ snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", -+ SUFFIX_BASE + i, port); -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ serdes_sdinfo[i].ser_phys_addr = 0x40; -+ serdes_sdinfo[i].sensor_dt = 0x1e; -+#endif -+ } -+ -+ (*pdata)->subdev_info = serdes_sdinfo; -+ (*pdata)->subdev_num = subdev_num; -+ -+ return 0; -+} -+ -+static int set_pdata(struct ipu7_isys_subdev_info **sensor_sd, -+ struct device *dev, -+ const char *sensor_name, -+ const char *hid_name, -+ struct control_logic_data *ctl_data, -+ unsigned int port, -+ unsigned int lanes, -+ unsigned int addr, -+ unsigned int subdev_num, -+ unsigned int deser_lanes, -+ bool is_dummy, -+ enum connection_type connect, -+ int link_freq, -+ int des_port) -+{ -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *pdata; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pr_debug("IPU ACPI: %s - Direct connection", __func__); -+ /* use ascii */ -+ /* port for start from 0 */ -+ if (port >= 0) { -+ pdata->suffix = port + SUFFIX_BASE; -+ pr_info("IPU ACPI: create %s on port %d", -+ sensor_name, port); -+ } else -+ dev_err(dev, "INVALID MIPI PORT"); -+ -+ pdata->port = port; -+ pdata->lanes = lanes; -+ pdata->i2c_slave_address = addr; -+ -+ /* gpio */ -+ set_common_gpio(ctl_data, &pdata); -+ -+ (*sensor_sd)->i2c.board_info.platform_data = pdata; -+ } else if (connect == TYPE_SERDES) { -+ struct serdes_platform_data *pdata; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pr_debug("IPU ACPI: %s - Serdes connection", __func__); -+ /* use ascii */ -+ if (port >= 0) { -+ pdata->suffix = port + SUFFIX_BASE; -+ pr_info("IPU ACPI: create %s on mipi port %d", -+ sensor_name, port); -+ } else -+ pr_err("IPU ACPI: Invalid MIPI Port : %d", port); -+ -+ pdata->link_freq_mbps = link_freq; -+ pdata->bus_type = (*sensor_sd)->csi2->bus_type; -+ pdata->deser_nlanes = deser_lanes; -+ pdata->ser_nlanes = lanes; -+ pdata->des_port = des_port; -+ strscpy(pdata->ser_name, (*sensor_sd)->i2c.board_info.type, I2C_NAME_SIZE); -+ set_serdes_subdev(sensor_sd, dev, &pdata, sensor_name, hid_name, lanes, addr, subdev_num); -+ -+ (*sensor_sd)->i2c.board_info.platform_data = pdata; -+ pdata->deser_board_info = &(*sensor_sd)->i2c.board_info; -+ } -+ -+ return 0; -+} -+ -+static void set_serdes_info(struct device *dev, const char *sensor_name, -+ const char *serdes_name, -+ struct sensor_bios_data *cam_data, -+ int sensor_physical_addr) -+{ -+ int i; -+ -+ serdes_info.deser_num = 0; -+ /* pprunit as num of sensor connected to deserializer */ -+ serdes_info.rx_port = cam_data->pprunit; -+ -+ /* i2c devices */ -+ serdes_info.i2c_num = cam_data->i2c_num; -+ -+ i = 1; -+ /* serializer mapped addr */ -+ serdes_info.ser_map_addr = cam_data->i2c[i++].addr; -+ /* sensor mapped addr */ -+ serdes_info.sensor_map_addr = cam_data->i2c[i++].addr; -+ -+ serdes_info.gpio_powerup_seq = 0; -+ -+ serdes_info.phy_i2c_addr = sensor_physical_addr; -+} -+ -+static int populate_sensor_pdata(struct device *dev, -+ struct ipu7_isys_subdev_info **sensor_sd, -+ struct sensor_bios_data *cam_data, -+ struct control_logic_data *ctl_data, -+ enum connection_type connect, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq) -+{ -+ struct ipu7_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -+ int i = 0; -+ int ret; -+ -+ if (connect == TYPE_DIRECT) { -+ /* sensor csi2 info */ -+ ret = set_csi2(sensor_sd, cam_data->lanes, cam_data->link, cam_data->bus_type); -+ if (ret) -+ return ret; -+ -+ /* sensor i2c info */ -+ if (cam_data->i2c_num == MIN_SENSOR_I2C) { -+ pr_debug("IPU ACPI: num of I2C device for Direct connection: %lld is Correct.", -+ cam_data->i2c_num); -+ set_i2c(sensor_sd, dev, sensor_name, cam_data->i2c[0].addr, cam_data->i2c[0].bdf); -+ } else { -+ pr_err("IPU ACPI: num of I2C device for Direct connection : %lld is Incorrect", -+ cam_data->i2c_num); -+ return -1; -+ } -+ /* Others use DISCRETE Control Logic */ -+ if (ctl_data->type != CL_DISCRETE) { -+ dev_err(dev, "IPU ACPI: Control Logic Type\n"); -+ dev_err(dev, "for %s: %d is Incorrect\n", -+ sensor_name, ctl_data->type); -+ return -EINVAL; -+ } -+ } else if (connect == TYPE_SERDES) { -+ /* serdes csi2 info. pprval as deserializer lane */ -+ ret = set_csi2(sensor_sd, cam_data->pprval, cam_data->link, cam_data->bus_type); -+ if (ret) -+ return ret; -+ -+ /* Use DISCRETE Control Logic or No Control Logic for serdes */ -+ if (ctl_data->type != CL_DISCRETE && ctl_data->type != CL_EMPTY) { -+ pr_err("IPU ACPI: Control Logic Type for serdes: %d is Incorrect", -+ ctl_data->type); -+ return -1; -+ } -+ -+ /* serdes i2c info */ -+ if (cam_data->i2c_num >= MIN_SERDES_I2C) { -+ pr_debug("IPU ACPI: num of I2C device for Serdes connection: %lld is Correct", -+ cam_data->i2c_num); -+ set_i2c(sensor_sd, dev, serdes_name, cam_data->i2c[0].addr, cam_data->i2c[0].bdf); -+ } else { -+ pr_err("IPU ACPI: num of I2C device for Serdes connection: %lld is Incorrect", -+ cam_data->i2c_num); -+ return -1; -+ } -+ -+ /* local serdes info */ -+ set_serdes_info(dev, sensor_name, serdes_name, cam_data, sensor_physical_addr); -+ } -+ -+ /* Use last I2C device */ -+ ret = set_pdata(sensor_sd, dev, sensor_name, hid_name, ctl_data, cam_data->link, -+ cam_data->lanes, cam_data->i2c[cam_data->i2c_num - 1].addr, -+ cam_data->pprunit, cam_data->pprval, false, connect, link_freq, cam_data->degree); -+ if (ret) -+ return ret; -+ -+ /* update local ipu7_isys_subdev_pdata */ -+ while (i <= MAX_ACPI_SENSOR_NUM) { -+ if (!ptr_acpi_subdev_pdata->subdevs[i]) { -+ ptr_acpi_subdev_pdata->subdevs[i] = *sensor_sd; -+ ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -+ break; -+ } -+ i++; -+ } -+ -+ /* print new subdev */ -+ if (connect == TYPE_DIRECT) { -+ pr_debug("New sensor subdev\n"); -+ print_subdev(*sensor_sd); -+ } else { -+ pr_debug("New serdes subdev\n"); -+ print_serdes_subdev(*sensor_sd); -+ } -+ -+ /* update total num of sensor connected */ -+ if (connect == TYPE_SERDES) -+ serdes_info.deser_num++; -+ -+ return 0; -+} -+ -+int get_sensor_pdata(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, size_t size, -+ enum connection_type connect, const char *sensor_name, -+ const char *serdes_name, const char *hid_name, -+ int sensor_physical_addr, int link_freq) -+{ -+ struct sensor_bios_data *cam_data; -+ struct control_logic_data *ctl_data; -+ struct ipu7_isys_subdev_info *sensor_sd; -+ int rval; -+ -+ cam_data = kzalloc(sizeof(*cam_data), GFP_KERNEL); -+ if (!cam_data) -+ return -ENOMEM; -+ cam_data->dev = dev; -+ -+ ctl_data = kzalloc(sizeof(*ctl_data), GFP_KERNEL); -+ if (!ctl_data) { -+ kfree(cam_data); -+ return -ENOMEM; -+ } -+ ctl_data->dev = dev; -+ -+ sensor_sd = kzalloc(sizeof(*sensor_sd), GFP_KERNEL); -+ if (!sensor_sd) { -+ kfree(cam_data); -+ kfree(ctl_data); -+ return -ENOMEM; -+ } -+ -+ /* camera info */ -+ rval = ipu_acpi_get_cam_data(dev, cam_data); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ /* control logic info */ -+ rval = ipu_acpi_get_dep_data(dev, ctl_data); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ /* populate pdata */ -+ rval = populate_sensor_pdata(dev, &sensor_sd, cam_data, ctl_data, -+ connect, sensor_name, serdes_name, hid_name, -+ sensor_physical_addr, link_freq); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ dev->platform_data = sensor_sd; -+ -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+} -+EXPORT_SYMBOL(get_sensor_pdata); -+ -+MODULE_AUTHOR("Khai Wen, Ng "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU ACPI support"); -diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c -new file mode 100644 -index 000000000000..deed3bba52bb ---- /dev/null -+++ b/drivers/media/platform/intel/ipu-acpi.c -@@ -0,0 +1,224 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+#include "ipu7.h" -+#include "ipu7-isys.h" -+ -+static LIST_HEAD(devices); -+ -+static struct ipu_camera_module_data *add_device_to_list( -+ struct list_head *devices) -+{ -+ struct ipu_camera_module_data *cam_device; -+ -+ cam_device = kzalloc(sizeof(*cam_device), GFP_KERNEL); -+ if (!cam_device) -+ return NULL; -+ -+ list_add(&cam_device->list, devices); -+ return cam_device; -+} -+ -+static const struct ipu_acpi_devices supported_devices[] = { -+/* -+ * { "ACPI ID", sensor_name, get_sensor_pdata, NULL, 0, TYPE, serdes_name, -+ * sensor_physical_addr, link_freq(mbps) }, // Custom HID -+ */ -+ -+#if IS_ENABLED(CONFIG_VIDEO_MAX9X) -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ { "INTC031M", ISX031_NAME, get_sensor_pdata, NULL, 0, TYPE_SERDES, "max9x", -+ ISX031_I2C_ADDRESS, 1600 }, // D3 ISX031 HID -+#endif -+#endif -+}; -+ -+static int get_table_index(const char *acpi_name) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { -+ if (!strncmp(supported_devices[i].hid_name, acpi_name, -+ strlen(supported_devices[i].hid_name))) -+ return i; -+ } -+ -+ return -ENODEV; -+} -+ -+/* List of ACPI devices what we can handle */ -+/* Must match with HID in BIOS option. Add new sensor if required */ -+static const struct acpi_device_id ipu_acpi_match[] = { -+/* -+ * { "AR0234A", 0 }, // Custom HID -+ */ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ { "INTC1031", 0 }, // ISX031 HID -+ { "INTC031M", 0 }, // D3CMC68N-115-084 ISX031 HID -+#endif -+ {}, -+}; -+ -+static int ipu_acpi_get_pdata(struct device *dev, int index) -+{ -+ struct ipu_camera_module_data *camdata; -+ int rval; -+ -+ if (index < 0) { -+ pr_err("Device is not in supported devices list\n"); -+ return -ENODEV; -+ } -+ -+ camdata = add_device_to_list(&devices); -+ if (!camdata) -+ return -ENOMEM; -+ -+ pr_info("IPU ACPI: Getting BIOS data for %s (%s)", -+ supported_devices[index].real_driver, dev_name(dev)); -+ -+ rval = supported_devices[index].get_platform_data( -+ dev, camdata, -+ supported_devices[index].priv_data, -+ supported_devices[index].priv_size, -+ supported_devices[index].connect, -+ supported_devices[index].real_driver, -+ supported_devices[index].serdes_name, -+ supported_devices[index].hid_name, -+ supported_devices[index].sensor_physical_addr, -+ supported_devices[index].link_freq); -+ -+ if (rval) -+ return -EPROBE_DEFER; -+ -+ return 0; -+} -+ -+/* -+ * different acpi devices may have same HID, so acpi_dev_get_first_match_dev -+ * will always match device to simple fwnode. -+ */ -+static int ipu_acpi_test(struct device *dev, void *priv) -+{ -+ struct acpi_device *adev = NULL; -+ int rval; -+ int acpi_idx = get_table_index(dev_name(dev)); -+ -+ if (acpi_idx < 0) -+ return 0; -+ else -+ dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); -+ -+ const char *target_hid = supported_devices[acpi_idx].hid_name; -+ -+ if (!ACPI_COMPANION(dev)) { -+ while ((adev = acpi_dev_get_next_match_dev(adev, target_hid, -+ NULL, -1))) { -+ if (adev->flags.reserved == 0) { -+ adev->flags.reserved = 1; -+ break; -+ } -+ acpi_dev_put(adev); -+ } -+ -+ if (!adev) { -+ dev_dbg(dev, "No ACPI device found for %s\n", target_hid); -+ return 0; -+ } else { -+ set_primary_fwnode(dev, &adev->fwnode); -+ dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); -+ } -+ } -+ -+ if (ACPI_COMPANION(dev) != adev) { -+ dev_err(dev, "Failed to set ACPI companion for %s\n", -+ dev_name(dev)); -+ acpi_dev_put(adev); -+ return 0; -+ } -+ -+ acpi_dev_put(adev); -+ -+ rval = ipu_acpi_get_pdata(dev, acpi_idx); -+ if (rval) { -+ pr_err("IPU6 ACPI: Failed to process ACPI data"); -+ return rval; -+ } -+ -+ return 0; /* Continue iteration */ -+} -+ -+/* Try to get all IPU related devices mentioned in BIOS and all related information -+ * return a new generated existing pdata -+ */ -+ -+int ipu_get_acpi_devices(void **spdata) -+{ -+ struct ipu_i2c_helper helper = {0}; -+ int rval; -+ struct ipu7_isys_subdev_pdata *ptr = NULL; -+ -+ rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); -+ if (rval < 0) -+ return rval; -+ -+ ptr = get_acpi_subdev_pdata(); -+ if (ptr && *ptr->subdevs) -+ *spdata = ptr; -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_get_acpi_devices); -+ -+static int __init ipu_acpi_init(void) -+{ -+ return 0; -+} -+ -+static void __exit ipu_acpi_exit(void) -+{ -+} -+ -+module_init(ipu_acpi_init); -+module_exit(ipu_acpi_exit); -+ -+MODULE_AUTHOR("Samu Onkalo "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU ACPI support"); -diff --git a/drivers/media/platform/intel/ipu7-fpga-pdata.c b/drivers/media/platform/intel/ipu7-fpga-pdata.c -new file mode 100644 -index 000000000000..9f60a38d1536 ---- /dev/null -+++ b/drivers/media/platform/intel/ipu7-fpga-pdata.c -@@ -0,0 +1,60 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2020 Intel Corporation -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-isys.h" -+ -+#define OV13B10_LANES 4 -+#define OV13B10_2LANES 2 -+#define OV13B10_I2C_ADDRESS 0x10 -+ -+static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_0 = { -+ .nlanes = OV13B10_LANES, -+ .port = 0, -+}; -+ -+static struct ipu7_isys_subdev_info ov13b10_sd_0 = { -+ .csi2 = &ov13b10_csi2_cfg_0, -+ .i2c = { -+ .board_info = { -+ I2C_BOARD_INFO("ov13b10", OV13B10_I2C_ADDRESS), -+ }, -+ .i2c_adapter_id = 0, -+ } -+}; -+ -+static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_1 = { -+ .nlanes = OV13B10_2LANES, -+ .port = 2, -+}; -+ -+static struct ipu7_isys_subdev_info ov13b10_sd_1 = { -+ .csi2 = &ov13b10_csi2_cfg_1, -+ .i2c = { -+ .board_info = { -+ I2C_BOARD_INFO("ov13b10", OV13B10_I2C_ADDRESS), -+ }, -+ .i2c_adapter_id = 1, -+ } -+}; -+ -+static struct ipu7_isys_subdev_pdata pdata = { -+ .subdevs = (struct ipu7_isys_subdev_info *[]) { -+ &ov13b10_sd_0, -+ &ov13b10_sd_1, -+ NULL, -+ }, -+}; -+ -+static void ipu7_quirk(struct pci_dev *pci_dev) -+{ -+ dev_info(&pci_dev->dev, "%s() attach the platform data", __func__); -+ pci_dev->dev.platform_data = &pdata; -+} -+ -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID, ipu7_quirk); -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID, ipu7_quirk); -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID, ipu7_quirk); -diff --git a/include/media/i2c/isx031.h b/include/media/i2c/isx031.h -new file mode 100644 -index 000000000000..4542f150695f ---- /dev/null -+++ b/include/media/i2c/isx031.h -@@ -0,0 +1,24 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2014 - 2025 Intel Corporation */ -+ -+#ifndef __ISX031_H -+#define __ISX031_H -+ -+#include -+ -+#define ISX031_NAME "isx031" -+ -+#define ISX031_I2C_ADDRESS 0x1a -+ -+struct isx031_platform_data { -+ unsigned int port; -+ unsigned int lanes; -+ uint32_t i2c_slave_address; -+ int irq_pin; -+ unsigned int irq_pin_flags; -+ char irq_pin_name[16]; -+ char suffix[5]; -+ int gpios[4]; -+}; -+ -+#endif /* __ISX031_H */ -diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h -new file mode 100644 -index 000000000000..e2074419dd88 ---- /dev/null -+++ b/include/media/ipu-acpi-pdata.h -@@ -0,0 +1,120 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2023-2025 Intel Corporation */ -+#include -+#include -+#include -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+#define CL_EMPTY 0 -+#define CL_DISCRETE 1 -+#define SERDES_MAX_GPIO_POWERUP_SEQ 4 -+#define LOOP_SIZE 10 -+ -+/* CPHY is supported since ipu7*/ -+#define PHY_MODE_DPHY 0 -+#define PHY_MODE_CPHY 1 -+ -+int get_sensor_pdata(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, size_t size, -+ enum connection_type connect, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq); -+ -+struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void); -+ -+struct sensor_platform_data { -+ unsigned int port; -+ unsigned int lanes; -+ uint32_t i2c_slave_address; -+ int irq_pin; -+ unsigned int irq_pin_flags; -+ char irq_pin_name[IPU7_SPDATA_IRQ_PIN_NAME_LEN]; -+ int reset_pin; -+ int detect_pin; -+ char suffix; -+ int gpios[IPU7_SPDATA_GPIO_NUM]; -+}; -+ -+struct serdes_platform_data { -+ unsigned int subdev_num; -+ struct serdes_subdev_info *subdev_info; -+ unsigned int reset_gpio; -+ unsigned int FPD_gpio; -+ char suffix; -+ unsigned int link_freq_mbps; -+ enum v4l2_mbus_type bus_type; -+ unsigned int deser_nlanes; -+ unsigned int ser_nlanes; -+ unsigned int des_port; -+ char ser_name[I2C_NAME_SIZE]; -+ struct i2c_board_info *deser_board_info; -+}; -+ -+struct serdes_subdev_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ unsigned short rx_port; -+ unsigned short phy_i2c_addr; -+ unsigned short ser_alias; -+ char suffix[5]; /* suffix for subdevs */ -+ unsigned short ser_phys_addr; -+ unsigned int sensor_dt; -+}; -+ -+struct serdes_module_pdata { -+ unsigned short i2c_addr; -+ unsigned short i2c_adapter; -+ unsigned int lanes; -+ int xshutdown; -+ int fsin; -+ int reset; -+ char gpio_powerup_seq[SERDES_MAX_GPIO_POWERUP_SEQ]; -+ unsigned int module_flags; -+ char module_name[I2C_NAME_SIZE]; -+ char suffix; -+}; -+ -+struct serdes_local { -+ /* num of camera sensor connected to current mipi port */ -+ unsigned int rx_port; -+ -+ /* num of i2c addr for current ACPI device */ -+ unsigned int i2c_num; -+ -+ /* current sensor_addr */ -+ unsigned short sensor_addr; -+ -+ /* physical i2c addr */ -+ unsigned short phy_i2c_addr; -+ -+ /* last mapped addr */ -+ unsigned short sensor_map_addr; -+ -+ /* current serializer_addr */ -+ unsigned short ser_addr; -+ -+ /* last mapped addr */ -+ unsigned short ser_map_addr; -+ -+ /* 2nd group of mapped addr for 2x sensors */ -+ unsigned short sensor_map_addr_2; -+ unsigned short ser_map_addr_2; -+ -+ /* current gpio_powerup_seq */ -+ unsigned int gpio_powerup_seq; -+ -+ /* current module flag */ -+ unsigned int module_flags; -+ -+ /* counter for total camera sensor connected */ -+ unsigned int sensor_num; -+ -+ /* counter for total deser connected */ -+ unsigned int deser_num; -+}; -diff --git a/include/media/ipu-acpi.h b/include/media/ipu-acpi.h -new file mode 100644 -index 000000000000..bc63240d7c24 ---- /dev/null -+++ b/include/media/ipu-acpi.h -@@ -0,0 +1,186 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#ifndef MEDIA_INTEL_IPU_ACPI_H -+#define MEDIA_INTEL_IPU_ACPI_H -+ -+#include "ipu7-isys.h" -+ -+#define MAX_ACPI_SENSOR_NUM 4 -+#define MAX_ACPI_I2C_NUM 12 -+#define MAX_ACPI_GPIO_NUM 12 -+ -+#define GPIO_RESET 0x0 -+ -+#define IPU7_SPDATA_GPIO_NUM 4 -+#define IPU7_SPDATA_IRQ_PIN_NAME_LEN 16 -+ -+enum connection_type { -+ TYPE_DIRECT, -+ TYPE_SERDES -+}; -+ -+/* Data representation as it is in ACPI SSDB buffer */ -+struct sensor_bios_data_packed { -+ u8 version; -+ u8 sku; -+ u8 guid_csi2[16]; -+ u8 devfunction; -+ u8 bus; -+ u32 dphylinkenfuses; -+ u32 clockdiv; -+ u8 link; -+ u8 lanes; -+ u32 csiparams[10]; -+ u32 maxlanespeed; -+ u8 sensorcalibfileidx; -+ u8 sensorcalibfileidxInMBZ[3]; -+ u8 romtype; -+ u8 vcmtype; -+ u8 platforminfo; -+ u8 platformsubinfo; -+ u8 flash; -+ u8 privacyled; -+ u8 degree; -+ u8 mipilinkdefined; -+ u32 mclkspeed; -+ u8 controllogicid; -+ u8 mipidataformat; -+ u8 siliconversion; -+ u8 customerid; -+ u8 mclkport; -+ u8 pmicpos; -+ u8 voltagerail; -+ u8 pprval; -+ u8 pprunit; -+ u8 flashid; -+ u8 phyconfig; -+ u8 reserved2[7]; -+} __attribute__((__packed__)); -+ -+struct ipu_i2c_info { -+ unsigned short bus; -+ unsigned short addr; -+ char bdf[32]; -+}; -+ -+/* Fields needed by ipu driver */ -+/* Each I2C client can have 12 device */ -+struct sensor_bios_data { -+ struct device *dev; -+ u8 link; -+ u8 lanes; -+ u8 vcmtype; -+ u8 flash; -+ u8 degree; -+ u8 mclkport; -+ u32 mclkspeed; -+ u16 xshutdown; -+ u8 controllogicid; -+ u8 pprval; -+ u8 pprunit; -+ struct ipu_i2c_info i2c[MAX_ACPI_I2C_NUM]; -+ u64 i2c_num; -+ u8 bus_type; -+}; -+ -+struct control_logic_data_packed { -+ u8 version; -+ u8 controllogictype; -+ u8 controllogicid; -+ u8 sensorcardsku; -+ u8 inputclk; -+ u8 platformid; -+ u8 subplatformid; -+ u8 customerid; -+ u8 wled1_maxflashcurrent; -+ u8 wled1_maxtorchcurrent; -+ u8 wled2_maxflashcurrent; -+ u8 wled2_maxtorchcurrent; -+ u8 wled1_type; -+ u8 wled2_type; -+ u8 pch_clk_src; -+ u8 reserved2[17]; -+} __attribute__((__packed__)); -+ -+struct ipu_gpio_info { -+ unsigned short init_state; -+ unsigned short pin; -+ unsigned short func; -+ bool valid; -+}; -+ -+/* Each I2C client linked to 1 set of CTL Logic */ -+struct control_logic_data { -+ struct device *dev; -+ u8 id; -+ u8 type; -+ u8 sku; -+ u64 gpio_num; -+ struct ipu_gpio_info gpio[MAX_ACPI_GPIO_NUM]; -+ bool completed; -+}; -+ -+int ipu_get_acpi_devices(void **spdata); -+ -+struct ipu7_isys_subdev_pdata *get_built_in_pdata(void); -+ -+int ipu_acpi_get_cam_data(struct device *dev, -+ struct sensor_bios_data *sensor); -+ -+int ipu_acpi_get_dep_data(struct device *dev, -+ struct control_logic_data *ctl_data); -+ -+int ipu_acpi_get_control_logic_data(struct device *dev, -+ struct control_logic_data **ctl_data); -+ -+struct ipu_i2c_helper { -+ int (*fn)(struct device *dev, void *priv, -+ struct ipu7_isys_csi2_config *csi2, -+ bool reprobe); -+ void *driver_data; -+}; -+ -+struct ipu_camera_module_data { -+ struct list_head list; -+ struct ipu7_isys_subdev_info sd; -+ struct ipu7_isys_csi2_config csi2; -+ unsigned int ext_clk; -+ void *pdata; /* Ptr to generated platform data*/ -+ void *priv; /* Private for specific subdevice */ -+}; -+ -+struct ipu_acpi_devices { -+ const char *hid_name; -+ const char *real_driver; -+ int (*get_platform_data)(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, -+ size_t size, -+ enum connection_type type, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq); -+ void *priv_data; -+ size_t priv_size; -+ enum connection_type connect; -+ const char *serdes_name; -+ int sensor_physical_addr; -+ int link_freq; /* in mbps */ -+}; -+ -+#endif -diff --git a/include/uapi/linux/ipu7-psys.h b/include/uapi/linux/ipu7-psys.h -new file mode 100644 -index 000000000000..f7b841faa215 ---- /dev/null -+++ b/include/uapi/linux/ipu7-psys.h -@@ -0,0 +1,246 @@ -+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef _UAPI_IPU_PSYS_H -+#define _UAPI_IPU_PSYS_H -+ -+#ifdef __KERNEL__ -+#include -+#else -+#include -+#endif -+ -+struct ipu_psys_capability { -+ uint32_t version; -+ uint8_t driver[20]; -+ uint8_t dev_model[32]; -+ uint32_t reserved[17]; -+} __attribute__ ((packed)); -+ -+/** -+ * PSYS event error to user -+ */ -+enum ipu_psys_event_error { -+ IPU_PSYS_EVT_ERROR_NONE = 0U, -+ IPU_PSYS_EVT_ERROR_INTERNAL = 1U, -+ IPU_PSYS_EVT_ERROR_FRAME = 2U, -+ IPU_PSYS_EVT_ERROR_FORCE_CLOSED = 3U, -+ IPU_PSYS_EVT_ERROR_MAX -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_event - event back from driver to user for requested tasks -+ * @graph_id: unique id per graph -+ * @node_ctx_id: unique logical context id per cb -+ * @frame_id: unique id per frame, originally assigned by user -+ * @error: error code of ipu_psys_event_error type -+ */ -+struct ipu_psys_event { -+ uint8_t graph_id; -+ uint8_t node_ctx_id; -+ uint8_t frame_id; -+ uint32_t error; -+ int32_t reserved[2]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_buffer - for input/output terminals -+ * @len: total allocated size @ base address -+ * @userptr: user pointer -+ * @fd: DMA-BUF handle -+ * @data_offset:offset to valid data -+ * @bytes_used: amount of valid data including offset -+ * @flags: flags -+ */ -+struct ipu_psys_buffer { -+ uint64_t len; -+ union { -+ int fd; -+ void __user *userptr; -+ uint64_t reserved; -+ } base; -+ uint32_t data_offset; -+ uint32_t bytes_used; -+ uint32_t flags; -+ uint32_t reserved[2]; -+} __attribute__ ((packed)); -+ -+/**< Max number of logical node */ -+#define MAX_GRAPH_NODES 5U -+/**< Max number of profile */ -+#define MAX_GRAPH_NODE_PROFILES 1U -+#define MAX_GRAPH_LINKS 10U -+#define MAX_GRAPH_TERMINALS 32U -+ -+/** -+ * Settings per node on the bitmap -+ * @teb: Terminal Enable bitmap -+ * @deb: Device Enable bitmap -+ * @rbm: Routing bitmap -+ * @reb: Routing Enable bitmap -+ */ -+struct node_profile { -+ uint32_t teb[2]; -+ uint32_t deb[4]; -+ uint32_t rbm[4]; -+ uint32_t reb[4]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct node_ternimal - terminal description on the node -+ * -+ * Terminal is the logical connection entity that is in the node, -+ * it can be different types, one node could have multiple terminal. -+ * -+ * @term_id: id of the terminal -+ * @buf_size: payload(PAC or SDI) size of the certain terminal -+ */ -+struct node_ternimal { -+ uint8_t term_id; -+ uint32_t buf_size; -+} __attribute__ ((packed)); -+ -+/** -+ * struct graph_node - Description of graph that will be used for device -+ * and graph open purpose -+ * -+ * Node is the logical entity of a graph, one graph could have multiple -+ * nodes and it could have connection between each node with terminal. -+ * -+ * @node_rsrc_id: Physical node id -+ * @node_ctx_id: Logical node id, unique per graph -+ * @num_terms: Number of enabled terms in the node -+ * @profiles: bitmap settings on the node -+ * @terminals: terminal info on the node -+ * @num_frags: Number of fragments -+ */ -+struct graph_node { -+ uint8_t node_rsrc_id; -+ uint8_t node_ctx_id; -+ uint8_t num_terms; -+ struct node_profile profiles[MAX_GRAPH_NODE_PROFILES]; -+ struct node_ternimal terminals[MAX_GRAPH_TERMINALS]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct graph_link_ep - link endpoint description -+ * -+ * Link endpoint is used to describe the connection between different nodes. -+ * -+ * @node_ctx_id: Node ID as described in the list of nodes in the fgraph -+ * @term_id: Term ID as described in the list of terms in the fgraph -+ */ -+struct graph_link_ep { -+ uint8_t node_ctx_id; -+ uint8_t term_id; -+} __attribute__ ((packed)); -+ -+/** -+ * All local links (links between nodes within a subsystem) require this -+ * value to be set. -+ */ -+#define IPU_PSYS_FOREIGN_KEY_NONE UINT16_MAX -+/** None value of TOP pbk id if not used */ -+#define IPU_PSYS_LINK_PBK_ID_NONE UINT8_MAX -+/** None value of TOP pbk slot id if not used */ -+#define IPU_PSYS_LINK_PBK_SLOT_ID_NONE UINT8_MAX -+/** Static Offline */ -+#define IPU_PSYS_LINK_STREAMING_MODE_SOFF 0U -+ -+/** -+ * struct graph_link - graph link to connect between cbs -+ * -+ * The sink and source links are defined with terminal information. -+ * -+ * @ep_src: Source side of the link -+ * @ep_dst: Destination side of the link -+ * @foreign_key: MUST set to IPU_PSYS_FOREIGN_KEY_NONE -+ * @streaming_mode: Value should be set from IPU_PSYS_LINK_STREAMING_MODE_X -+ * @pbk_id: TOP PBK id that used to connected to external IP -+ * @pbk_slot_id: TOP PBK slot id that used to connected to external IP -+ * @delayed_link: A delay link between producer N and consumer N+1 frame -+ */ -+struct graph_link { -+ struct graph_link_ep ep_src; -+ struct graph_link_ep ep_dst; -+ uint16_t foreign_key; -+ uint8_t streaming_mode; -+ uint8_t pbk_id; -+ uint8_t pbk_slot_id; -+ uint8_t delayed_link; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_graph_info -+ * -+ * Topology that describes an IPU internal connection includes CB and terminal -+ * information. -+ * -+ * @graph_id: id of graph, set initial to 0xFF by user, returned by driver -+ * @num_nodes: number of nodes in graph -+ * @nodes: node entity -+ * @links: link entity -+ */ -+struct ipu_psys_graph_info { -+ uint8_t graph_id; -+ uint8_t num_nodes; -+ struct graph_node __user *nodes; -+ struct graph_link links[MAX_GRAPH_LINKS]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_term_buffers -+ * -+ * Descprion of each terminal payload buffer -+ * -+ * @term_id: terminal id -+ * @term_buf: terminal buffer -+ */ -+struct ipu_psys_term_buffers { -+ uint8_t term_id; -+ struct ipu_psys_buffer term_buf; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_task_request -+ * -+ * Task request is for user to send a request associated with terminal -+ * payload and expect IPU to process, each task request would expect -+ * an event, @see ipu_psys_event -+ * -+ * @graph_id: graph id returned from graph open -+ * @node_ctx_id: unique logical context id per cb -+ * @frame_id: frame id -+ * @payload_reuse_bm: Any terminal marked here must be enabled -+ * @term_buf_count: the number of terminal buffers -+ * @task_buffers: terminal buffers on the task request -+ * @num_frags: the number of fragments -+ * @frag_buffers: the buffer information of fragments -+ */ -+struct ipu_psys_task_request { -+ uint8_t graph_id; -+ uint8_t node_ctx_id; -+ uint8_t frame_id; -+ uint32_t payload_reuse_bm[2]; -+ uint8_t term_buf_count; -+ struct ipu_psys_term_buffers __user *task_buffers; -+} __attribute__ ((packed)); -+ -+#define IPU_BUFFER_FLAG_INPUT (1 << 0) -+#define IPU_BUFFER_FLAG_OUTPUT (1 << 1) -+#define IPU_BUFFER_FLAG_MAPPED (1 << 2) -+#define IPU_BUFFER_FLAG_NO_FLUSH (1 << 3) -+#define IPU_BUFFER_FLAG_DMA_HANDLE (1 << 4) -+#define IPU_BUFFER_FLAG_USERPTR (1 << 5) -+ -+#define IPU_IOC_QUERYCAP _IOR('A', 1, struct ipu_psys_capability) -+#define IPU_IOC_MAPBUF _IOWR('A', 2, int) -+#define IPU_IOC_UNMAPBUF _IOWR('A', 3, int) -+#define IPU_IOC_GETBUF _IOWR('A', 4, struct ipu_psys_buffer) -+#define IPU_IOC_PUTBUF _IOWR('A', 5, struct ipu_psys_buffer) -+#define IPU_IOC_DQEVENT _IOWR('A', 6, struct ipu_psys_event) -+#define IPU_IOC_GRAPH_OPEN _IOWR('A', 7, struct ipu_psys_graph_info) -+#define IPU_IOC_TASK_REQUEST _IOWR('A', 8, struct ipu_psys_task_request) -+#define IPU_IOC_GRAPH_CLOSE _IOWR('A', 9, int) -+ -+#endif /* _UAPI_IPU_PSYS_H */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-mei-bus-fix-device-leak.security b/SPECS/kernel-rt/0001-mei-bus-fix-device-leak.security new file mode 100644 index 000000000..9059d93d7 --- /dev/null +++ b/SPECS/kernel-rt/0001-mei-bus-fix-device-leak.security @@ -0,0 +1,52 @@ +From d2f493bb12ab187899e8e2437e19b601ff34e529 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Sun, 11 May 2025 14:27:37 +0300 +Subject: [PATCH 1/8] mei: bus: fix device leak + +The bus rescan function creates bus devices for all clients. +The fixup routine is executed on all devices, unneeded +devices are removed and fully initialized once set +is_added flag to 1. + +If link to firmware is reset right after all devices are +initialized, but before fixup is executed, the rescan tries +to remove devices. +The is_added flag is not set the mei_cl_bus_dev_destroy +returns prematurely. +Allow to clean up device when is_added flag is unset to +account for above scenario. + +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/bus.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c +index 2c810ab12e620..abded7457894d 100644 +--- a/drivers/misc/mei/bus.c ++++ b/drivers/misc/mei/bus.c +@@ -1452,17 +1452,14 @@ static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) + */ + static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) + { +- + WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); + +- if (!cldev->is_added) +- return; +- +- device_del(&cldev->dev); ++ if (cldev->is_added) { ++ device_del(&cldev->dev); ++ cldev->is_added = 0; ++ } + + list_del_init(&cldev->bus_list); +- +- cldev->is_added = 0; + put_device(&cldev->dev); + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-mei-gsc-add-dependency-on-Xe-driver.rt b/SPECS/kernel-rt/0001-mei-gsc-add-dependency-on-Xe-driver.rt deleted file mode 100644 index 533678538..000000000 --- a/SPECS/kernel-rt/0001-mei-gsc-add-dependency-on-Xe-driver.rt +++ /dev/null @@ -1,33 +0,0 @@ -From 381ca3ef54389d673887c371b0bd7bf277106ba6 Mon Sep 17 00:00:00 2001 -From: Junxiao Chang -Date: Sun, 9 Nov 2025 17:35:33 +0200 -Subject: [PATCH 1/2] mei: gsc: add dependency on Xe driver - -INTEL_MEI_GSC depends on either i915 or Xe -and can be present when either of above is present. - -Cc: -Fixes: 87a4c85d3a3e ("drm/xe/gsc: add gsc device support") -Tested-by: Baoli Zhang -Signed-off-by: Junxiao Chang -Signed-off-by: Alexander Usyskin ---- - drivers/misc/mei/Kconfig | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig -index 7575fee96cc6..59025f0a0f57 100644 ---- a/drivers/misc/mei/Kconfig -+++ b/drivers/misc/mei/Kconfig -@@ -49,7 +49,7 @@ config INTEL_MEI_TXE - config INTEL_MEI_GSC - tristate "Intel MEI GSC embedded device" - depends on INTEL_MEI_ME -- depends on DRM_I915 -+ depends on DRM_I915 || DRM_XE - help - Intel auxiliary driver for GSC devices embedded in Intel graphics devices. - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet b/SPECS/kernel-rt/0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet new file mode 100644 index 000000000..035553b4d --- /dev/null +++ b/SPECS/kernel-rt/0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet @@ -0,0 +1,181 @@ +From 8bc3d99d56e4782d8249523be7ea84dc6c27b4c2 Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Wed, 9 Jun 2021 15:12:19 +0800 +Subject: [PATCH 01/18] net: pcs: xpcs: enable xpcs reset skipping + +Some platforms such as the Intel AlderLake configure the xPCS in the +BIOS and a xPCS Soft Reset is not required during driver init. + +This changes the xpcs_create function to take in an additional argument +to check if the platform request to skip xpcs reset during device +initialization. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/dsa/microchip/ksz9477.c | 2 +- + drivers/net/dsa/sja1105/sja1105_mdio.c | 2 +- + drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 4 +++- + drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c | 2 +- + drivers/net/pcs/pcs-xpcs.c | 14 +++++++------- + include/linux/pcs/pcs-xpcs.h | 4 ++-- + include/linux/stmmac.h | 1 + + 7 files changed, 16 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c +index 5df8f153d511b..451e173408564 100644 +--- a/drivers/net/dsa/microchip/ksz9477.c ++++ b/drivers/net/dsa/microchip/ksz9477.c +@@ -336,7 +336,7 @@ int ksz9477_pcs_create(struct ksz_device *dev) + if (ret) + return ret; + +- pcs = xpcs_create_pcs_mdiodev(bus, 0); ++ pcs = xpcs_create_pcs_mdiodev(bus, 0, false); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + p->pcs = pcs; +diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c +index 8d535c033cef7..d751b2c6f6f9d 100644 +--- a/drivers/net/dsa/sja1105/sja1105_mdio.c ++++ b/drivers/net/dsa/sja1105/sja1105_mdio.c +@@ -409,7 +409,7 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv) + priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX) + continue; + +- pcs = xpcs_create_pcs_mdiodev(bus, port); ++ pcs = xpcs_create_pcs_mdiodev(bus, port, false); + if (IS_ERR(pcs)) { + rc = PTR_ERR(pcs); + goto out_pcs_free; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +index f408737f6fc73..117ce5da99792 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +@@ -429,8 +429,10 @@ int stmmac_pcs_setup(struct net_device *ndev) + struct fwnode_handle *devnode, *pcsnode; + struct dw_xpcs *xpcs = NULL; + int addr, ret; ++ bool skip_reset; + + devnode = priv->plat->port_node; ++ skip_reset = priv->plat->skip_reset; + + if (priv->plat->pcs_init) { + ret = priv->plat->pcs_init(priv); +@@ -442,7 +444,7 @@ int stmmac_pcs_setup(struct net_device *ndev) + } else if (priv->plat->mdio_bus_data && + priv->plat->mdio_bus_data->pcs_mask) { + addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1; +- xpcs = xpcs_create_mdiodev(priv->mii, addr); ++ xpcs = xpcs_create_mdiodev(priv->mii, addr, skip_reset); + ret = PTR_ERR_OR_ZERO(xpcs); + } else { + return 0; +diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +index 03f1b9bc604d5..d7e95106a45a9 100644 +--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c ++++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +@@ -151,7 +151,7 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe) + if (ret) + return ret; + +- pcs = xpcs_create_pcs_mdiodev(mii_bus, 0); ++ pcs = xpcs_create_pcs_mdiodev(mii_bus, 0, false); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + +diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c +index 3d1bd5aac0937..532d8da588b58 100644 +--- a/drivers/net/pcs/pcs-xpcs.c ++++ b/drivers/net/pcs/pcs-xpcs.c +@@ -1476,7 +1476,7 @@ static int xpcs_init_id(struct dw_xpcs *xpcs) + return xpcs_identify(xpcs); + } + +-static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) ++static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, bool skip_reset) + { + struct dw_xpcs *xpcs; + int ret; +@@ -1495,7 +1495,7 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) + + xpcs_get_interfaces(xpcs, xpcs->pcs.supported_interfaces); + +- if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) ++ if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID || skip_reset) + xpcs->pcs.poll = false; + else + xpcs->need_reset = true; +@@ -1520,7 +1520,7 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) + * the PCS device couldn't be found on the bus and other negative errno related + * to the data allocation and MDIO-bus communications. + */ +-struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) ++struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, bool skip_reset) + { + struct mdio_device *mdiodev; + struct dw_xpcs *xpcs; +@@ -1529,7 +1529,7 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) + if (IS_ERR(mdiodev)) + return ERR_CAST(mdiodev); + +- xpcs = xpcs_create(mdiodev); ++ xpcs = xpcs_create(mdiodev, skip_reset); + + /* xpcs_create() has taken a refcount on the mdiodev if it was + * successful. If xpcs_create() fails, this will free the mdio +@@ -1543,11 +1543,11 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) + } + EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); + +-struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr) ++struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr, bool skip_reset) + { + struct dw_xpcs *xpcs; + +- xpcs = xpcs_create_mdiodev(bus, addr); ++ xpcs = xpcs_create_mdiodev(bus, addr, skip_reset); + if (IS_ERR(xpcs)) + return ERR_CAST(xpcs); + +@@ -1577,7 +1577,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode) + if (!mdiodev) + return ERR_PTR(-EPROBE_DEFER); + +- xpcs = xpcs_create(mdiodev); ++ xpcs = xpcs_create(mdiodev, false); + + /* xpcs_create() has taken a refcount on the mdiodev if it was + * successful. If xpcs_create() fails, this will free the mdio +diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h +index e40f554ff717a..f521032124927 100644 +--- a/include/linux/pcs/pcs-xpcs.h ++++ b/include/linux/pcs/pcs-xpcs.h +@@ -51,11 +51,11 @@ struct dw_xpcs; + struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs); + int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface); + void xpcs_config_eee_mult_fact(struct dw_xpcs *xpcs, u8 mult_fact); +-struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr); ++struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, bool skip_reset); + struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode); + void xpcs_destroy(struct dw_xpcs *xpcs); + +-struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr); ++struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr, bool skip_reset); + void xpcs_destroy_pcs(struct phylink_pcs *pcs); + + #endif /* __LINUX_PCS_XPCS_H */ +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index fa1318bac06c4..d7d6998fa5bd0 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -299,5 +299,6 @@ struct plat_stmmacenet_data { + int msi_tx_base_vec; + const struct dwmac4_addrs *dwmac4_addrs; + unsigned int flags; ++ bool skip_reset; + }; + #endif +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf b/SPECS/kernel-rt/0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf new file mode 100644 index 000000000..03329f3ba --- /dev/null +++ b/SPECS/kernel-rt/0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf @@ -0,0 +1,81 @@ +From ae02bb21a81488358e7fddc6e5ac9f201be722c7 Mon Sep 17 00:00:00 2001 +From: Zhang Rui +Date: Thu, 23 Oct 2025 15:37:53 -0700 +Subject: [PATCH 01/13] perf/x86/intel/cstate: Add Pantherlake support + +Like Lunarlake, Pantherlake supports CC1/CC6/CC7 and PC2/PC6/PC10. + +Signed-off-by: Zhang Rui +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Kan Liang +Reviewed-by: Dapeng Mi +Link: https://patch.msgid.link/20251023223754.1743928-4-zide.chen@intel.com +--- + arch/x86/events/intel/cstate.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c +index 6f5286a99e0c3..369b0d204ff07 100644 +--- a/arch/x86/events/intel/cstate.c ++++ b/arch/x86/events/intel/cstate.c +@@ -41,7 +41,7 @@ + * MSR_CORE_C1_RES: CORE C1 Residency Counter + * perf code: 0x00 + * Available model: SLM,AMT,GLM,CNL,ICX,TNT,ADL,RPL +- * MTL,SRF,GRR,ARL,LNL ++ * MTL,SRF,GRR,ARL,LNL,PTL + * Scope: Core (each processor core has a MSR) + * MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter + * perf code: 0x01 +@@ -53,18 +53,19 @@ + * Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW, + * SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX, + * TGL,TNT,RKL,ADL,RPL,SPR,MTL,SRF, +- * GRR,ARL,LNL ++ * GRR,ARL,LNL,PTL + * Scope: Core + * MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter + * perf code: 0x03 + * Available model: SNB,IVB,HSW,BDW,SKL,CNL,KBL,CML, +- * ICL,TGL,RKL,ADL,RPL,MTL,ARL,LNL ++ * ICL,TGL,RKL,ADL,RPL,MTL,ARL,LNL, ++ * PTL + * Scope: Core + * MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter. + * perf code: 0x00 + * Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL, + * KBL,CML,ICL,ICX,TGL,TNT,RKL,ADL, +- * RPL,SPR,MTL,ARL,LNL,SRF ++ * RPL,SPR,MTL,ARL,LNL,SRF,PTL + * Scope: Package (physical package) + * MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter. + * perf code: 0x01 +@@ -77,7 +78,7 @@ + * Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW, + * SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX, + * TGL,TNT,RKL,ADL,RPL,SPR,MTL,SRF, +- * ARL,LNL ++ * ARL,LNL,PTL + * Scope: Package (physical package) + * MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter. + * perf code: 0x03 +@@ -96,7 +97,7 @@ + * MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter. + * perf code: 0x06 + * Available model: HSW ULT,KBL,GLM,CNL,CML,ICL,TGL, +- * TNT,RKL,ADL,RPL,MTL,ARL,LNL ++ * TNT,RKL,ADL,RPL,MTL,ARL,LNL,PTL + * Scope: Package (physical package) + * MSR_MODULE_C6_RES_MS: Module C6 Residency Counter. + * perf code: 0x00 +@@ -651,6 +652,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = { + X86_MATCH_VFM(INTEL_ARROWLAKE_H, &adl_cstates), + X86_MATCH_VFM(INTEL_ARROWLAKE_U, &adl_cstates), + X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_cstates), ++ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &lnl_cstates), + { }, + }; + MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf b/SPECS/kernel-rt/0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf deleted file mode 100644 index 960534c90..000000000 --- a/SPECS/kernel-rt/0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf +++ /dev/null @@ -1,119 +0,0 @@ -From 71aa7fcebba9ce3bf185178c914873c382112b04 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Thu, 13 Feb 2025 13:24:53 -0800 -Subject: [PATCH 001/100] perf/x86/msr: Make SMI and PPERF on by default - -The MSRs, SMI_COUNT and PPERF, are model-specific MSRs. A very long -CPU ID list is maintained to indicate the supported platforms. With more -and more platforms being introduced, new CPU IDs have to be kept adding. -Also, the old kernel has to be updated to apply the new CPU ID. - -The MSRs have been introduced for a long time. There is no plan to -change them in the near future. Furthermore, the current code utilizes -rdmsr_safe() to check the availability of MSRs before using it. - -Make them on by default. It should be good enough to only rely on the -rdmsr_safe() to check their availability for both existing and future -platforms. - -Signed-off-by: Kan Liang ---- - arch/x86/events/msr.c | 80 ++----------------------------------------- - 1 file changed, 3 insertions(+), 77 deletions(-) - -diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c -index 7f5007a4752a..c6bb7a0f6251 100644 ---- a/arch/x86/events/msr.c -+++ b/arch/x86/events/msr.c -@@ -41,85 +41,11 @@ static bool test_therm_status(int idx, void *data) - - static bool test_intel(int idx, void *data) - { -- if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || -- boot_cpu_data.x86 != 6) -+ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) - return false; - -- switch (boot_cpu_data.x86_vfm) { -- case INTEL_NEHALEM: -- case INTEL_NEHALEM_G: -- case INTEL_NEHALEM_EP: -- case INTEL_NEHALEM_EX: -- -- case INTEL_WESTMERE: -- case INTEL_WESTMERE_EP: -- case INTEL_WESTMERE_EX: -- -- case INTEL_SANDYBRIDGE: -- case INTEL_SANDYBRIDGE_X: -- -- case INTEL_IVYBRIDGE: -- case INTEL_IVYBRIDGE_X: -- -- case INTEL_HASWELL: -- case INTEL_HASWELL_X: -- case INTEL_HASWELL_L: -- case INTEL_HASWELL_G: -- -- case INTEL_BROADWELL: -- case INTEL_BROADWELL_D: -- case INTEL_BROADWELL_G: -- case INTEL_BROADWELL_X: -- case INTEL_SAPPHIRERAPIDS_X: -- case INTEL_EMERALDRAPIDS_X: -- case INTEL_GRANITERAPIDS_X: -- case INTEL_GRANITERAPIDS_D: -- -- case INTEL_ATOM_SILVERMONT: -- case INTEL_ATOM_SILVERMONT_D: -- case INTEL_ATOM_AIRMONT: -- -- case INTEL_ATOM_GOLDMONT: -- case INTEL_ATOM_GOLDMONT_D: -- case INTEL_ATOM_GOLDMONT_PLUS: -- case INTEL_ATOM_TREMONT_D: -- case INTEL_ATOM_TREMONT: -- case INTEL_ATOM_TREMONT_L: -- -- case INTEL_XEON_PHI_KNL: -- case INTEL_XEON_PHI_KNM: -- if (idx == PERF_MSR_SMI) -- return true; -- break; -- -- case INTEL_SKYLAKE_L: -- case INTEL_SKYLAKE: -- case INTEL_SKYLAKE_X: -- case INTEL_KABYLAKE_L: -- case INTEL_KABYLAKE: -- case INTEL_COMETLAKE_L: -- case INTEL_COMETLAKE: -- case INTEL_ICELAKE_L: -- case INTEL_ICELAKE: -- case INTEL_ICELAKE_X: -- case INTEL_ICELAKE_D: -- case INTEL_TIGERLAKE_L: -- case INTEL_TIGERLAKE: -- case INTEL_ROCKETLAKE: -- case INTEL_ALDERLAKE: -- case INTEL_ALDERLAKE_L: -- case INTEL_ATOM_GRACEMONT: -- case INTEL_RAPTORLAKE: -- case INTEL_RAPTORLAKE_P: -- case INTEL_RAPTORLAKE_S: -- case INTEL_METEORLAKE: -- case INTEL_METEORLAKE_L: -- if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF) -- return true; -- break; -- } -- -- return false; -+ /* Rely on perf_msr_probe() to check the availability */ -+ return true; - } - - PMU_EVENT_ATTR_STRING(tsc, attr_tsc, "event=0x00" ); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core b/SPECS/kernel-rt/0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core deleted file mode 100644 index fa243f522..000000000 --- a/SPECS/kernel-rt/0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core +++ /dev/null @@ -1,609 +0,0 @@ -From 884a526208947014c57524dad63cb6f3ff413760 Mon Sep 17 00:00:00 2001 -From: Xi Pardee -Date: Tue, 26 Aug 2025 11:39:43 -0700 -Subject: [PATCH 1/2] platform/x86/intel/pmc: Add Wildcat Lake support to - intel_pmc_core -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add Wildcat Lake support to intel_pmc_core driver. - -Signed-off-by: Xi Pardee -Link: https://lore.kernel.org/r/20250826183946.802684-2-xi.pardee@linux.intel.com -[ij: added #include for BIT()] -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - drivers/platform/x86/intel/pmc/Makefile | 2 +- - drivers/platform/x86/intel/pmc/core.c | 1 + - drivers/platform/x86/intel/pmc/core.h | 8 + - drivers/platform/x86/intel/pmc/ptl.c | 6 +- - drivers/platform/x86/intel/pmc/wcl.c | 486 ++++++++++++++++++++++++ - 5 files changed, 499 insertions(+), 4 deletions(-) - create mode 100644 drivers/platform/x86/intel/pmc/wcl.c - -diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile -index 5f68c8503a56..bb960c8721d7 100644 ---- a/drivers/platform/x86/intel/pmc/Makefile -+++ b/drivers/platform/x86/intel/pmc/Makefile -@@ -4,7 +4,7 @@ - # - - intel_pmc_core-y := core.o spt.o cnp.o icl.o \ -- tgl.o adl.o mtl.o arl.o lnl.o ptl.o -+ tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o - obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o - intel_pmc_core_pltdrv-y := pltdrv.o - obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o -diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c -index d040290e80ff..21ab0f0764d1 100644 ---- a/drivers/platform/x86/intel/pmc/core.c -+++ b/drivers/platform/x86/intel/pmc/core.c -@@ -1632,6 +1632,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { - X86_MATCH_VFM(INTEL_ARROWLAKE_U, &arl_h_pmc_dev), - X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev), - X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev), -+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev), - {} - }; - -diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h -index 4a94a4ee031e..9d54eef6c921 100644 ---- a/drivers/platform/x86/intel/pmc/core.h -+++ b/drivers/platform/x86/intel/pmc/core.h -@@ -298,6 +298,10 @@ enum ppfear_regs { - #define PTL_PMC_LTR_CUR_PLT 0x1C2C - #define PTL_PCD_PMC_MMIO_REG_LEN 0x31A8 - -+/* Wildcat Lake */ -+#define WCL_PMC_LTR_RESERVED 0x1B64 -+#define WCL_PCD_PMC_MMIO_REG_LEN 0x3178 -+ - /* SSRAM PMC Device ID */ - /* LNL */ - #define PMC_DEVID_LNL_SOCM 0xa87f -@@ -505,6 +509,9 @@ extern const struct pmc_bit_map mtl_socm_vnn_misc_status_map[]; - extern const struct pmc_bit_map mtl_socm_signal_status_map[]; - extern const struct pmc_reg_map mtl_socm_reg_map; - extern const struct pmc_reg_map mtl_ioep_reg_map; -+extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[]; -+extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[]; -+extern const struct pmc_bit_map ptl_pcdp_signal_status_map[]; - - void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); - int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); -@@ -528,6 +535,7 @@ extern struct pmc_dev_info arl_pmc_dev; - extern struct pmc_dev_info arl_h_pmc_dev; - extern struct pmc_dev_info lnl_pmc_dev; - extern struct pmc_dev_info ptl_pmc_dev; -+extern struct pmc_dev_info wcl_pmc_dev; - - void cnl_suspend(struct pmc_dev *pmcdev); - int cnl_resume(struct pmc_dev *pmcdev); -diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c -index 394515af60d6..1bbec9856867 100644 ---- a/drivers/platform/x86/intel/pmc/ptl.c -+++ b/drivers/platform/x86/intel/pmc/ptl.c -@@ -162,7 +162,7 @@ static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = { - {} - }; - --static const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = { -+const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = { - {"AON2_OFF_STS", BIT(0), 1}, - {"AON3_OFF_STS", BIT(1), 0}, - {"AON4_OFF_STS", BIT(2), 1}, -@@ -382,7 +382,7 @@ static const struct pmc_bit_map ptl_pcdp_vnn_req_status_2_map[] = { - {} - }; - --static const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = { -+const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = { - {"DTS0_VNN_REQ_STS", BIT(7), 0}, - {"GPIOCOM5_VNN_REQ_STS", BIT(11), 1}, - {} -@@ -421,7 +421,7 @@ static const struct pmc_bit_map ptl_pcdp_vnn_misc_status_map[] = { - {} - }; - --static const struct pmc_bit_map ptl_pcdp_signal_status_map[] = { -+const struct pmc_bit_map ptl_pcdp_signal_status_map[] = { - {"LSX_Wake0_STS", BIT(0), 0}, - {"LSX_Wake1_STS", BIT(1), 0}, - {"LSX_Wake2_STS", BIT(2), 0}, -diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c -new file mode 100644 -index 000000000000..85e90a639e65 ---- /dev/null -+++ b/drivers/platform/x86/intel/pmc/wcl.c -@@ -0,0 +1,486 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This file contains platform specific structure definitions -+ * and init function used by Wildcat Lake PCH. -+ * -+ * Copyright (c) 2025, Intel Corporation. -+ */ -+ -+#include -+#include -+ -+#include "core.h" -+ -+static const struct pmc_bit_map wcl_pcdn_pfear_map[] = { -+ {"PMC_0", BIT(0)}, -+ {"FUSE_OSSE", BIT(1)}, -+ {"ESPISPI", BIT(2)}, -+ {"XHCI", BIT(3)}, -+ {"SPA", BIT(4)}, -+ {"RSVD", BIT(5)}, -+ {"MPFPW2", BIT(6)}, -+ {"GBE", BIT(7)}, -+ -+ {"SBR16B21", BIT(0)}, -+ {"SBR16B5", BIT(1)}, -+ {"SBR8B1", BIT(2)}, -+ {"SBR8B0", BIT(3)}, -+ {"P2SB0", BIT(4)}, -+ {"D2D_DISP_1", BIT(5)}, -+ {"LPSS", BIT(6)}, -+ {"LPC", BIT(7)}, -+ -+ {"SMB", BIT(0)}, -+ {"ISH", BIT(1)}, -+ {"DBG_SBR16B", BIT(2)}, -+ {"NPK_0", BIT(3)}, -+ {"D2D_NOC_1", BIT(4)}, -+ {"FIA_P", BIT(5)}, -+ {"FUSE", BIT(6)}, -+ {"DBG_PSF", BIT(7)}, -+ -+ {"DISP_PGA1", BIT(0)}, -+ {"XDCI", BIT(1)}, -+ {"EXI", BIT(2)}, -+ {"CSE", BIT(3)}, -+ {"KVMCC", BIT(4)}, -+ {"PMT", BIT(5)}, -+ {"CLINK", BIT(6)}, -+ {"PTIO", BIT(7)}, -+ -+ {"USBR0", BIT(0)}, -+ {"SBR16B22", BIT(1)}, -+ {"SMT1", BIT(2)}, -+ {"MPFPW1", BIT(3)}, -+ {"SMS2", BIT(4)}, -+ {"SMS1", BIT(5)}, -+ {"CSMERTC", BIT(6)}, -+ {"CSMEPSF", BIT(7)}, -+ -+ {"D2D_NOC_0", BIT(0)}, -+ {"ESE", BIT(1)}, -+ {"FIACPCB_P", BIT(2)}, -+ {"RSVD", BIT(3)}, -+ {"SBR8B2", BIT(4)}, -+ {"OSSE_SMT1", BIT(5)}, -+ {"D2D_DISP", BIT(6)}, -+ {"P2SB1", BIT(7)}, -+ -+ {"U3FPW1", BIT(0)}, -+ {"SBR16B3", BIT(1)}, -+ {"PSF4", BIT(2)}, -+ {"CNVI", BIT(3)}, -+ {"UFSX2", BIT(4)}, -+ {"ENDBG", BIT(5)}, -+ {"DBC", BIT(6)}, -+ {"SBRG", BIT(7)}, -+ -+ {"RSVD", BIT(0)}, -+ {"NPK1", BIT(1)}, -+ {"SBR16B7", BIT(2)}, -+ {"SBR16B4", BIT(3)}, -+ {"FIA_XG", BIT(4)}, -+ {"PSF6", BIT(5)}, -+ {"UFSPW1", BIT(6)}, -+ {"FIA_U", BIT(7)}, -+ -+ {"PSF8", BIT(0)}, -+ {"PSF0", BIT(1)}, -+ {"RSVD", BIT(2)}, -+ {"FIACPCB_U", BIT(3)}, -+ {"TAM", BIT(4)}, -+ {"SBR16B0", BIT(5)}, -+ {"TBTLSX", BIT(6)}, -+ {"THC0", BIT(7)}, -+ -+ {"THC1", BIT(0)}, -+ {"PMC_1", BIT(1)}, -+ {"FIACPCB_XG", BIT(2)}, -+ {"TCSS", BIT(3)}, -+ {"DISP_PGA", BIT(4)}, -+ {"SBR16B20", BIT(5)}, -+ {"SBR8B20", BIT(6)}, -+ {"DBG_SBR", BIT(7)}, -+ -+ {"SPC", BIT(0)}, -+ {"ACE_0", BIT(1)}, -+ {"ACE_1", BIT(2)}, -+ {"ACE_2", BIT(3)}, -+ {"ACE_3", BIT(4)}, -+ {"ACE_4", BIT(5)}, -+ {"ACE_5", BIT(6)}, -+ {"ACE_6", BIT(7)}, -+ -+ {"ACE_7", BIT(0)}, -+ {"ACE_8", BIT(1)}, -+ {"ACE_9", BIT(2)}, -+ {"ACE_10", BIT(3)}, -+ {"SBR16B2", BIT(4)}, -+ {"SBR8B4", BIT(5)}, -+ {"OSSE", BIT(6)}, -+ {"SBR16B1", BIT(7)}, -+ {} -+}; -+ -+static const struct pmc_bit_map *ext_wcl_pcdn_pfear_map[] = { -+ wcl_pcdn_pfear_map, -+ NULL -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_ltr_show_map[] = { -+ {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, -+ {"RSVD", WCL_PMC_LTR_RESERVED}, -+ {"SATA", CNP_PMC_LTR_SATA}, -+ {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, -+ {"XHCI", CNP_PMC_LTR_XHCI}, -+ {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, -+ {"ME", CNP_PMC_LTR_ME}, -+ {"SATA1", CNP_PMC_LTR_EVA}, -+ {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, -+ {"HD_AUDIO", CNP_PMC_LTR_AZ}, -+ {"CNV", CNP_PMC_LTR_CNV}, -+ {"LPSS", CNP_PMC_LTR_LPSS}, -+ {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, -+ {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, -+ {"SATA2", PTL_PMC_LTR_SATA2}, -+ {"ESPI", CNP_PMC_LTR_ESPI}, -+ {"SCC", CNP_PMC_LTR_SCC}, -+ {"ISH", CNP_PMC_LTR_ISH}, -+ {"UFSX2", CNP_PMC_LTR_UFSX2}, -+ {"EMMC", CNP_PMC_LTR_EMMC}, -+ {"WIGIG", ICL_PMC_LTR_WIGIG}, -+ {"THC0", TGL_PMC_LTR_THC0}, -+ {"THC1", TGL_PMC_LTR_THC1}, -+ {"SOUTHPORT_G", MTL_PMC_LTR_SPG}, -+ {"ESE", MTL_PMC_LTR_ESE}, -+ {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, -+ {"DMI3", ARL_PMC_LTR_DMI3}, -+ {"OSSE", LNL_PMC_LTR_OSSE}, -+ -+ /* Below two cannot be used for LTR_IGNORE */ -+ {"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT}, -+ {"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_power_gating_status_0_map[] = { -+ {"PMC_PGD0_PG_STS", BIT(0), 0}, -+ {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0}, -+ {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, -+ {"XHCI_PGD0_PG_STS", BIT(3), 1}, -+ {"SPA_PGD0_PG_STS", BIT(4), 1}, -+ {"RSVD_5", BIT(5), 0}, -+ {"MPFPW2_PGD0_PG_STS", BIT(6), 0}, -+ {"GBE_PGD0_PG_STS", BIT(7), 1}, -+ {"SBR16B21_PGD0_PG_STS", BIT(8), 0}, -+ {"SBR16B5_PGD0_PG_STS", BIT(9), 0}, -+ {"SBR8B1_PGD0_PG_STS", BIT(10), 0}, -+ {"SBR8B0_PGD0_PG_STS", BIT(11), 0}, -+ {"P2SB0_PG_STS", BIT(12), 1}, -+ {"D2D_DISP_PGD1_PG_STS", BIT(13), 0}, -+ {"LPSS_PGD0_PG_STS", BIT(14), 1}, -+ {"LPC_PGD0_PG_STS", BIT(15), 0}, -+ {"SMB_PGD0_PG_STS", BIT(16), 0}, -+ {"ISH_PGD0_PG_STS", BIT(17), 0}, -+ {"DBG_SBR16B_PGD0_PG_STS", BIT(18), 0}, -+ {"NPK_PGD0_PG_STS", BIT(19), 0}, -+ {"D2D_NOC_PGD1_PG_STS", BIT(20), 0}, -+ {"FIA_P_PGD0_PG_STS", BIT(21), 0}, -+ {"FUSE_PGD0_PG_STS", BIT(22), 0}, -+ {"DBG_PSF_PGD0_PG_STS", BIT(23), 0}, -+ {"DISP_PGA1_PGD0_PG_STS", BIT(24), 0}, -+ {"XDCI_PGD0_PG_STS", BIT(25), 1}, -+ {"EXI_PGD0_PG_STS", BIT(26), 0}, -+ {"CSE_PGD0_PG_STS", BIT(27), 1}, -+ {"KVMCC_PGD0_PG_STS", BIT(28), 1}, -+ {"PMT_PGD0_PG_STS", BIT(29), 1}, -+ {"CLINK_PGD0_PG_STS", BIT(30), 1}, -+ {"PTIO_PGD0_PG_STS", BIT(31), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_power_gating_status_1_map[] = { -+ {"USBR0_PGD0_PG_STS", BIT(0), 1}, -+ {"SBR16B22_PGD0_PG_STS", BIT(1), 0}, -+ {"SMT1_PGD0_PG_STS", BIT(2), 1}, -+ {"MPFPW1_PGD0_PG_STS", BIT(3), 0}, -+ {"SMS2_PGD0_PG_STS", BIT(4), 1}, -+ {"SMS1_PGD0_PG_STS", BIT(5), 1}, -+ {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, -+ {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, -+ {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, -+ {"ESE_PGD0_PG_STS", BIT(9), 1}, -+ {"FIACPCB_P_PGD0_PG_STS", BIT(10), 0}, -+ {"SBR8B2_PGD0_PG_STS", BIT(12), 0}, -+ {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1}, -+ {"D2D_DISP_PGD0_PG_STS", BIT(14), 0}, -+ {"P2SB1_PGD0_PG_STS", BIT(15), 1}, -+ {"U3FPW1_PGD0_PG_STS", BIT(16), 0}, -+ {"SBR16B3_PGD0_PG_STS", BIT(17), 0}, -+ {"PSF4_PGD0_PG_STS", BIT(18), 0}, -+ {"CNVI_PGD0_PG_STS", BIT(19), 0}, -+ {"UFSX2_PGD0_PG_STS", BIT(20), 1}, -+ {"ENDBG_PGD0_PG_STS", BIT(21), 0}, -+ {"DBC_PGD0_PG_STS", BIT(22), 0}, -+ {"SBRG_PGD0_PG_STS", BIT(23), 0}, -+ {"NPK_PGD1_PG_STS", BIT(25), 0}, -+ {"SBR16B7_PGD0_PG_STS", BIT(26), 0}, -+ {"SBR16B4_PGD0_PG_STS", BIT(27), 0}, -+ {"FIA_XG_PSF_PGD0_PG_STS", BIT(28), 0}, -+ {"PSF6_PGD0_PG_STS", BIT(29), 0}, -+ {"UFSPW1_PGD0_PG_STS", BIT(30), 0}, -+ {"FIA_U_PGD0_PG_STS", BIT(31), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_power_gating_status_2_map[] = { -+ {"PSF8_PGD0_PG_STS", BIT(0), 0}, -+ {"PSF0_PGD0_PG_STS", BIT(1), 0}, -+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, -+ {"TAM_PGD0_PG_STS", BIT(4), 1}, -+ {"SBR16B0_PGD0_PG_STS", BIT(5), 0}, -+ {"TBTLSX_PGD0_PG_STS", BIT(6), 1}, -+ {"THC0_PGD0_PG_STS", BIT(7), 1}, -+ {"THC1_PGD0_PG_STS", BIT(8), 1}, -+ {"PMC_PGD1_PG_STS", BIT(9), 0}, -+ {"FIACPCB_XG_PGD0_PG_STS", BIT(10), 0}, -+ {"TCSS_PGD0_PG_STS", BIT(11), 0}, -+ {"DISP_PGA_PGD0_PG_STS", BIT(12), 0}, -+ {"SBR8B4_PGD0_PG_STS", BIT(13), 0}, -+ {"SBR8B20_PGD0_PG_STS", BIT(14), 0}, -+ {"DBG_PGD0_PG_STS", BIT(15), 0}, -+ {"SPC_PGD0_PG_STS", BIT(16), 1}, -+ {"ACE_PGD0_PG_STS", BIT(17), 0}, -+ {"ACE_PGD1_PG_STS", BIT(18), 0}, -+ {"ACE_PGD2_PG_STS", BIT(19), 0}, -+ {"ACE_PGD3_PG_STS", BIT(20), 0}, -+ {"ACE_PGD4_PG_STS", BIT(21), 0}, -+ {"ACE_PGD5_PG_STS", BIT(22), 0}, -+ {"ACE_PGD6_PG_STS", BIT(23), 0}, -+ {"ACE_PGD7_PG_STS", BIT(24), 0}, -+ {"ACE_PGD8_PG_STS", BIT(25), 0}, -+ {"ACE_PGD9_PG_STS", BIT(26), 0}, -+ {"ACE_PGD10_PG_STS", BIT(27), 0}, -+ {"SBR16B2_PG_PGD0_PG_STS", BIT(28), 0}, -+ {"SBR16B20_PGD0_PG_STS", BIT(29), 0}, -+ {"OSSE_PGD0_PG_STS", BIT(30), 1}, -+ {"SBR16B1_PGD0_PG_STS", BIT(31), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_0_map[] = { -+ {"LPSS_D3_STS", BIT(3), 1}, -+ {"XDCI_D3_STS", BIT(4), 1}, -+ {"XHCI_D3_STS", BIT(5), 1}, -+ {"SPA_D3_STS", BIT(12), 0}, -+ {"SPC_D3_STS", BIT(14), 0}, -+ {"OSSE_D3_STS", BIT(15), 0}, -+ {"ESPISPI_D3_STS", BIT(18), 0}, -+ {"PSTH_D3_STS", BIT(21), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_1_map[] = { -+ {"OSSE_SMT1_D3_STS", BIT(16), 0}, -+ {"GBE_D3_STS", BIT(19), 0}, -+ {"ITSS_D3_STS", BIT(23), 0}, -+ {"CNVI_D3_STS", BIT(27), 0}, -+ {"UFSX2_D3_STS", BIT(28), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_2_map[] = { -+ {"CSMERTC_D3_STS", BIT(1), 0}, -+ {"ESE_D3_STS", BIT(2), 0}, -+ {"CSE_D3_STS", BIT(4), 0}, -+ {"KVMCC_D3_STS", BIT(5), 0}, -+ {"USBR0_D3_STS", BIT(6), 0}, -+ {"ISH_D3_STS", BIT(7), 0}, -+ {"SMT1_D3_STS", BIT(8), 0}, -+ {"SMT2_D3_STS", BIT(9), 0}, -+ {"SMT3_D3_STS", BIT(10), 0}, -+ {"CLINK_D3_STS", BIT(14), 0}, -+ {"PTIO_D3_STS", BIT(16), 0}, -+ {"PMT_D3_STS", BIT(17), 0}, -+ {"SMS1_D3_STS", BIT(18), 0}, -+ {"SMS2_D3_STS", BIT(19), 0}, -+ {"OSSE_SMT2_D3_STS", BIT(22), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_3_map[] = { -+ {"THC0_D3_STS", BIT(14), 1}, -+ {"THC1_D3_STS", BIT(15), 1}, -+ {"OSSE_SMT3_D3_STS", BIT(16), 0}, -+ {"ACE_D3_STS", BIT(23), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_req_status_0_map[] = { -+ {"LPSS_VNN_REQ_STS", BIT(3), 1}, -+ {"OSSE_VNN_REQ_STS", BIT(15), 1}, -+ {"ESPISPI_VNN_REQ_STS", BIT(18), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_req_status_1_map[] = { -+ {"NPK_VNN_REQ_STS", BIT(4), 1}, -+ {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, -+ {"EXI_VNN_REQ_STS", BIT(9), 1}, -+ {"OSSE_SMT1_VNN_REQ_STS", BIT(16), 1}, -+ {"P2D_VNN_REQ_STS", BIT(18), 1}, -+ {"GBE_VNN_REQ_STS", BIT(19), 1}, -+ {"SMB_VNN_REQ_STS", BIT(25), 1}, -+ {"LPC_VNN_REQ_STS", BIT(26), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_req_status_2_map[] = { -+ {"CSMERTC_VNN_REQ_STS", BIT(1), 1}, -+ {"ESE_VNN_REQ_STS", BIT(2), 1}, -+ {"CSE_VNN_REQ_STS", BIT(4), 1}, -+ {"ISH_VNN_REQ_STS", BIT(7), 1}, -+ {"SMT1_VNN_REQ_STS", BIT(8), 1}, -+ {"CLINK_VNN_REQ_STS", BIT(14), 1}, -+ {"SMS1_VNN_REQ_STS", BIT(18), 1}, -+ {"SMS2_VNN_REQ_STS", BIT(19), 1}, -+ {"GPIOCOM4_VNN_REQ_STS", BIT(20), 1}, -+ {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1}, -+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1}, -+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1}, -+ {"DISP_SHIM_VNN_REQ_STS", BIT(31), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_misc_status_map[] = { -+ {"CPU_C10_REQ_STS", BIT(0), 0}, -+ {"TS_OFF_REQ_STS", BIT(1), 0}, -+ {"PNDE_MET_REQ_STS", BIT(2), 1}, -+ {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, -+ {"VNN_SOC_REQ_STS", BIT(6), 1}, -+ {"ISH_VNNAON_REQ_STS", BIT(7), 0}, -+ {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1}, -+ {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1}, -+ {"PLT_GREATER_REQ_STS", BIT(11), 1}, -+ {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, -+ {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, -+ {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, -+ {"EA_REQ_STS", BIT(15), 0}, -+ {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0}, -+ {"BRK_EV_EN_REQ_STS", BIT(17), 0}, -+ {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, -+ {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, -+ {"ARC_IDLE_REQ_STS", BIT(21), 0}, -+ {"FIA_DEEP_PM_REQ_STS", BIT(23), 0}, -+ {"XDCI_ATTACHED_REQ_STS", BIT(24), 1}, -+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, -+ {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1}, -+ {"PRE_WAKE0_REQ_STS", BIT(27), 1}, -+ {"PRE_WAKE1_REQ_STS", BIT(28), 1}, -+ {"PRE_WAKE2_REQ_STS", BIT(29), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_rsc_status_map[] = { -+ {"Memory", 0, 1}, -+ {"PSF0", 0, 1}, -+ {"PSF6", 0, 1}, -+ {"PSF8", 0, 1}, -+ {"SAF_CFI_LINK", 0, 1}, -+ {"SB", 0, 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map *wcl_pcdn_lpm_maps[] = { -+ ptl_pcdp_clocksource_status_map, -+ wcl_pcdn_power_gating_status_0_map, -+ wcl_pcdn_power_gating_status_1_map, -+ wcl_pcdn_power_gating_status_2_map, -+ wcl_pcdn_d3_status_0_map, -+ wcl_pcdn_d3_status_1_map, -+ wcl_pcdn_d3_status_2_map, -+ wcl_pcdn_d3_status_3_map, -+ wcl_pcdn_vnn_req_status_0_map, -+ wcl_pcdn_vnn_req_status_1_map, -+ wcl_pcdn_vnn_req_status_2_map, -+ ptl_pcdp_vnn_req_status_3_map, -+ wcl_pcdn_vnn_misc_status_map, -+ ptl_pcdp_signal_status_map, -+ NULL -+}; -+ -+static const struct pmc_bit_map *wcl_pcdn_blk_maps[] = { -+ wcl_pcdn_power_gating_status_0_map, -+ wcl_pcdn_power_gating_status_1_map, -+ wcl_pcdn_power_gating_status_2_map, -+ wcl_pcdn_rsc_status_map, -+ wcl_pcdn_vnn_req_status_0_map, -+ wcl_pcdn_vnn_req_status_1_map, -+ wcl_pcdn_vnn_req_status_2_map, -+ ptl_pcdp_vnn_req_status_3_map, -+ wcl_pcdn_d3_status_0_map, -+ wcl_pcdn_d3_status_1_map, -+ wcl_pcdn_d3_status_2_map, -+ wcl_pcdn_d3_status_3_map, -+ ptl_pcdp_clocksource_status_map, -+ wcl_pcdn_vnn_misc_status_map, -+ ptl_pcdp_signal_status_map, -+ NULL -+}; -+ -+static const struct pmc_reg_map wcl_pcdn_reg_map = { -+ .pfear_sts = ext_wcl_pcdn_pfear_map, -+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, -+ .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, -+ .ltr_show_sts = wcl_pcdn_ltr_show_map, -+ .msr_sts = msr_map, -+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, -+ .regmap_length = WCL_PCD_PMC_MMIO_REG_LEN, -+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, -+ .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, -+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, -+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, -+ .lpm_num_maps = PTL_LPM_NUM_MAPS, -+ .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, -+ .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, -+ .etr3_offset = ETR3_OFFSET, -+ .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, -+ .lpm_priority_offset = MTL_LPM_PRI_OFFSET, -+ .lpm_en_offset = MTL_LPM_EN_OFFSET, -+ .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, -+ .lpm_sts = wcl_pcdn_lpm_maps, -+ .lpm_status_offset = MTL_LPM_STATUS_OFFSET, -+ .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, -+ .s0ix_blocker_maps = wcl_pcdn_blk_maps, -+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, -+}; -+ -+#define WCL_NPU_PCI_DEV 0xfd3e -+ -+/* -+ * Set power state of select devices that do not have drivers to D3 -+ * so that they do not block Package C entry. -+ */ -+static void wcl_d3_fixup(void) -+{ -+ pmc_core_set_device_d3(WCL_NPU_PCI_DEV); -+} -+ -+static int wcl_resume(struct pmc_dev *pmcdev) -+{ -+ wcl_d3_fixup(); -+ return cnl_resume(pmcdev); -+} -+ -+static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) -+{ -+ wcl_d3_fixup(); -+ return generic_core_init(pmcdev, pmc_dev_info); -+} -+ -+struct pmc_dev_info wcl_pmc_dev = { -+ .map = &wcl_pcdn_reg_map, -+ .suspend = cnl_suspend, -+ .resume = wcl_resume, -+ .init = wcl_core_init, -+}; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt b/SPECS/kernel-rt/0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt new file mode 100644 index 000000000..4d32a0766 --- /dev/null +++ b/SPECS/kernel-rt/0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt @@ -0,0 +1,158 @@ +From 16ba458229f01daba619c55e6dc608f11f8f9298 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:30 -0700 +Subject: [PATCH 1/6] platform/x86:intel/pmc: Add support for multiple DMU + GUIDs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Enable support for multiple DMU GUIDs to accommodate Arrow +Lake H/U platforms. Arrow Lake U/H may have several GUIDs +pointing to a single telemetry region providing die C6 value +Add support to search for available GUIDs. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-3-xi.pardee@linux.intel.com +[ij: add include & reverse logic in a loop] +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/arl.c | 6 ++++-- + drivers/platform/x86/intel/pmc/core.c | 22 ++++++++++++++++++---- + drivers/platform/x86/intel/pmc/core.h | 6 +++--- + drivers/platform/x86/intel/pmc/mtl.c | 3 ++- + 4 files changed, 27 insertions(+), 10 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c +index 17ad87b392abe..cc05a168c3721 100644 +--- a/drivers/platform/x86/intel/pmc/arl.c ++++ b/drivers/platform/x86/intel/pmc/arl.c +@@ -720,9 +720,10 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ + return generic_core_init(pmcdev, pmc_dev_info); + } + ++static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info arl_pmc_dev = { + .pci_func = 0, +- .dmu_guid = ARL_PMT_DMU_GUID, ++ .dmu_guids = ARL_PMT_DMU_GUIDS, + .regmap_list = arl_pmc_info_list, + .map = &arl_socs_reg_map, + .sub_req_show = &pmc_core_substate_req_regs_fops, +@@ -732,9 +733,10 @@ struct pmc_dev_info arl_pmc_dev = { + .sub_req = pmc_core_pmt_get_lpm_req, + }; + ++static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info arl_h_pmc_dev = { + .pci_func = 2, +- .dmu_guid = ARL_PMT_DMU_GUID, ++ .dmu_guids = ARL_H_PMT_DMU_GUIDS, + .regmap_list = arl_pmc_info_list, + .map = &mtl_socm_reg_map, + .sub_req_show = &pmc_core_substate_req_regs_fops, +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index ac3d19ae8c56d..ca126a253f9d0 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -20,6 +20,7 @@ enum header_type { + #include + #include + #include ++#include + #include + #include + #include +@@ -1281,7 +1282,20 @@ int get_primary_reg_base(struct pmc *pmc) + return 0; + } + +-void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) ++static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, u32 *guids) ++{ ++ struct telem_endpoint *ep; ++ unsigned int i; ++ ++ for (i = 0; guids[i]; i++) { ++ ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); ++ if (!IS_ERR(ep)) ++ return ep; ++ } ++ return ERR_PTR(-ENODEV); ++} ++ ++void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) + { + struct telem_endpoint *ep; + struct pci_dev *pcidev; +@@ -1292,7 +1306,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) + return; + } + +- ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); ++ ep = pmc_core_register_endpoint(pcidev, guids); + pci_dev_put(pcidev); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, +@@ -1689,8 +1703,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) + } + + pmc_core_get_low_power_modes(pmcdev); +- if (pmc_dev_info->dmu_guid) +- pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid); ++ if (pmc_dev_info->dmu_guids) ++ pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids); + + if (ssram) { + ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info); +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index d6818bd34768e..83d6e2e833785 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -481,7 +481,7 @@ enum pmc_index { + /** + * struct pmc_dev_info - Structure to keep PMC device info + * @pci_func: Function number of the primary PMC +- * @dmu_guid: Die Management Unit GUID ++ * @dmu_guids: List of Die Management Unit GUID + * @regmap_list: Pointer to a list of pmc_info structure that could be + * available for the platform. When set, this field implies + * SSRAM support. +@@ -495,7 +495,7 @@ enum pmc_index { + */ + struct pmc_dev_info { + u8 pci_func; +- u32 dmu_guid; ++ u32 *dmu_guids; + struct pmc_info *regmap_list; + const struct pmc_reg_map *map; + const struct file_operations *sub_req_show; +@@ -532,7 +532,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); + int pmc_core_resume_common(struct pmc_dev *pmcdev); + int get_primary_reg_base(struct pmc *pmc); + void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); +-void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid); ++void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids); + void pmc_core_set_device_d3(unsigned int device); + + int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); +diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c +index 0b87e10f864e6..19470ca311cf7 100644 +--- a/drivers/platform/x86/intel/pmc/mtl.c ++++ b/drivers/platform/x86/intel/pmc/mtl.c +@@ -992,9 +992,10 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in + return generic_core_init(pmcdev, pmc_dev_info); + } + ++static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info mtl_pmc_dev = { + .pci_func = 2, +- .dmu_guid = MTL_PMT_DMU_GUID, ++ .dmu_guids = MTL_PMT_DMU_GUIDS, + .regmap_list = mtl_pmc_info_list, + .map = &mtl_socm_reg_map, + .sub_req_show = &pmc_core_substate_req_regs_fops, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency b/SPECS/kernel-rt/0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency deleted file mode 100644 index e72db8be8..000000000 --- a/SPECS/kernel-rt/0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency +++ /dev/null @@ -1,38 +0,0 @@ -From a8b215172a0115f51ecbc6e4a536889d5aa65054 Mon Sep 17 00:00:00 2001 -From: Kuppuswamy Sathyanarayanan -Date: Wed, 22 Oct 2025 14:17:33 -0700 -Subject: [PATCH] platform/x86: intel-uncore-freq: Add additional client - processors -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add Intel uncore frequency driver support for Pantherlake, Wildcatlake -and Novalake processors. - -Signed-off-by: Kuppuswamy Sathyanarayanan -Link: https://patch.msgid.link/20251022211733.3565526-1-sathyanarayanan.kuppuswamy@linux.intel.com -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - .../platform/x86/intel/uncore-frequency/uncore-frequency.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c -index 2a6897035150..0dfc552b2802 100644 ---- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c -+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c -@@ -256,6 +256,10 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = { - X86_MATCH_VFM(INTEL_ARROWLAKE, NULL), - X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL), - X86_MATCH_VFM(INTEL_LUNARLAKE_M, NULL), -+ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL), -+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL), -+ X86_MATCH_VFM(INTEL_NOVALAKE, NULL), -+ X86_MATCH_VFM(INTEL_NOVALAKE_L, NULL), - {} - }; - MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt b/SPECS/kernel-rt/0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt new file mode 100644 index 000000000..f2997a500 --- /dev/null +++ b/SPECS/kernel-rt/0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt @@ -0,0 +1,41 @@ +From bb452b663c49326fb098a232162a79a15b4b4869 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Wed, 5 Nov 2025 13:50:14 -0800 +Subject: [PATCH] platform/x86/intel/vsec: Add support for Wildcat Lake +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Wildcat Lake PMT telemetry support. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251105215020.1984036-1-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/vsec.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c +index f66f0ce8559b1..ecfc7703f2019 100644 +--- a/drivers/platform/x86/intel/vsec.c ++++ b/drivers/platform/x86/intel/vsec.c +@@ -765,6 +765,7 @@ static const struct intel_vsec_platform_info lnl_info = { + #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d + #define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d + #define PCI_DEVICE_ID_INTEL_VSEC_PTL 0xb07d ++#define PCI_DEVICE_ID_INTEL_VSEC_WCL 0xfd7d + static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, +@@ -776,6 +777,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_PTL, &mtl_info) }, ++ { PCI_DEVICE_DATA(INTEL, VSEC_WCL, &mtl_info) }, + { } + }; + MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio b/SPECS/kernel-rt/0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio new file mode 100644 index 000000000..6039d60fa --- /dev/null +++ b/SPECS/kernel-rt/0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio @@ -0,0 +1,94 @@ +From adce89d037cdf0dca931ea2ab3edaf9170778a70 Mon Sep 17 00:00:00 2001 +From: "Baoli.Zhang" +Date: Thu, 22 Jan 2026 18:07:15 +0800 +Subject: [PATCH] soundwire: fix bug in sdw_add_element_group_count found by + syzkaller + +In the original implementation, there is illegal memory access when i is +equal with num in the for-loop in sdw_add_element_group_count as below. + +for (i = 0; i <= num; i++) { + if (rate == group->rates[i] && lane == group->lanes[i]) + ... + +To fix it, we check if rate/lane have already been existed first. And then +add them if not. + +There is no further functional changes beside this error. + +Fixes: 9026118f20e2 ("soundwire: Add generic bandwidth allocation algorithm") +Reviewed-by: Bard Liao +Signed-off-by: Baoli.Zhang +--- + .../soundwire/generic_bandwidth_allocation.c | 47 +++++++++---------- + 1 file changed, 22 insertions(+), 25 deletions(-) + +diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c +index c18f0c16f929..e52c536d663c 100644 +--- a/drivers/soundwire/generic_bandwidth_allocation.c ++++ b/drivers/soundwire/generic_bandwidth_allocation.c +@@ -296,39 +296,36 @@ static int sdw_add_element_group_count(struct sdw_group *group, + int num = group->count; + int i; + +- for (i = 0; i <= num; i++) { ++ for (i = 0; i < num; i++) { + if (rate == group->rates[i] && lane == group->lanes[i]) +- break; +- +- if (i != num) +- continue; +- +- if (group->count >= group->max_size) { +- unsigned int *rates; +- unsigned int *lanes; ++ return 0; ++ } + +- group->max_size += 1; +- rates = krealloc(group->rates, +- (sizeof(int) * group->max_size), +- GFP_KERNEL); +- if (!rates) +- return -ENOMEM; ++ if (group->count >= group->max_size) { ++ unsigned int *rates; ++ unsigned int *lanes; + +- group->rates = rates; ++ group->max_size += 1; ++ rates = krealloc(group->rates, ++ (sizeof(int) * group->max_size), ++ GFP_KERNEL); ++ if (!rates) ++ return -ENOMEM; + +- lanes = krealloc(group->lanes, +- (sizeof(int) * group->max_size), +- GFP_KERNEL); +- if (!lanes) +- return -ENOMEM; ++ group->rates = rates; + +- group->lanes = lanes; +- } ++ lanes = krealloc(group->lanes, ++ (sizeof(int) * group->max_size), ++ GFP_KERNEL); ++ if (!lanes) ++ return -ENOMEM; + +- group->rates[group->count] = rate; +- group->lanes[group->count++] = lane; ++ group->lanes = lanes; + } + ++ group->rates[group->count] = rate; ++ group->lanes[group->count++] = lane; ++ + return 0; + } + +-- +2.34.1 + diff --git a/SPECS/kernel-rt/0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal b/SPECS/kernel-rt/0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal deleted file mode 100644 index edd4332b3..000000000 --- a/SPECS/kernel-rt/0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal +++ /dev/null @@ -1,34 +0,0 @@ -From 4ab127a1503cab71b6cbcecb50c96bd5d7802251 Mon Sep 17 00:00:00 2001 -From: Salah Triki -Date: Fri, 25 Jul 2025 06:07:01 +0100 -Subject: [PATCH 01/11] thermal: intel: int340x: Remove redundant - acpi_has_method() call - -acpi_evaluate_object() returns an error if the needed method does not -exist, so remove an unnecessary acpi_has_method() call preceding it. - -Signed-off-by: Salah Triki -Link: https://patch.msgid.link/aIMQ9RFciI8jmmAh@pc -[ rjw: Subject adjustment ] -Signed-off-by: Rafael J. Wysocki ---- - drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c -index cb149bcdd7d5..ce5d53be108b 100644 ---- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c -+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c -@@ -220,9 +220,6 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps - int i, result = 0; - struct psvt *psvts; - -- if (!acpi_has_method(handle, "PSVT")) -- return -ENODEV; -- - status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi b/SPECS/kernel-rt/0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi deleted file mode 100644 index efbb39062..000000000 --- a/SPECS/kernel-rt/0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi +++ /dev/null @@ -1,31 +0,0 @@ -From 1f0c8463542c88d50607c5724d7729d6a5c06ecf Mon Sep 17 00:00:00 2001 -From: Tao Yu -Date: Mon, 3 Nov 2025 09:13:14 +0000 -Subject: [PATCH] x86/fred: Revert "x86/fred: Enable FRED by default" - -This reverts commit 9bb180134e3af: due to Ubuntu VMs -unable to recover after system wakes up from S3 for -multiple iterations, this commit will be temporarily -reverted to avoid blocking release. - -Signed-off-by: Tao Yu ---- - arch/x86/kernel/cpu/common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c -index 54c123f3c305..135d3b315b63 100644 ---- a/arch/x86/kernel/cpu/common.c -+++ b/arch/x86/kernel/cpu/common.c -@@ -1717,7 +1717,7 @@ static void __init cpu_parse_early_param(void) - - /* Minimize the gap between FRED is available and available but disabled. */ - arglen = cmdline_find_option(boot_command_line, "fred", arg, sizeof(arg)); -- if (arglen == 3 && !strncmp(arg, "off", 3)) -+ if (arglen != 2 || strncmp(arg, "on", 2)) - setup_clear_cpu_cap(X86_FEATURE_FRED); - - arglen = cmdline_find_option(boot_command_line, "clearcpuid", arg, sizeof(arg)); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio b/SPECS/kernel-rt/0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio new file mode 100644 index 000000000..4a7d769cd --- /dev/null +++ b/SPECS/kernel-rt/0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio @@ -0,0 +1,75 @@ +From 8baf781b45472d10a67908aecdbcdc02fab5ae45 Mon Sep 17 00:00:00 2001 +From: Balamurugan C +Date: Tue, 18 Nov 2025 13:53:41 +0530 +Subject: [PATCH 2/2] ASoC: SOF: Intel: hda: Only check SSP MCLK mask in case + of IPC3 + +IPC4 is using the NHLT blob itself and sends the SSP blob from it +directly to the firmware, there is no need for the MCLK quirk +based on the SSP blob since the SSP blob is in use. + +At the same time reword the error, info and debug messages for clarity. + +Signed-off-by: Peter Ujfalusi +Signed-off-by: Balamurugan C +--- + sound/soc/sof/intel/hda.c | 36 ++++++++++++++++++++++-------------- + 1 file changed, 22 insertions(+), 14 deletions(-) + +diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c +index 52e86fa600778..8b43a0447d252 100644 +--- a/sound/soc/sof/intel/hda.c ++++ b/sound/soc/sof/intel/hda.c +@@ -1403,7 +1403,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) + mach->mach_params.i2s_link_mask) { + const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); + int ssp_num; +- int mclk_mask; + + if (hweight_long(mach->mach_params.i2s_link_mask) > 1 && + !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB)) +@@ -1428,19 +1427,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) + + sof_pdata->tplg_filename = tplg_filename; + +- mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num); +- +- if (mclk_mask < 0) { +- dev_err(sdev->dev, "Invalid MCLK configuration\n"); +- return NULL; +- } +- +- dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask); +- +- if (mclk_mask) { +- dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask); +- sdev->mclk_id_override = true; +- sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; ++ if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) { ++ int mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ++ ssp_num); ++ ++ if (mclk_mask < 0) { ++ dev_err(sdev->dev, ++ "Invalid MCLK configuration for SSP%d\n", ++ ssp_num); ++ return NULL; ++ } ++ ++ if (mclk_mask) { ++ sdev->mclk_id_override = true; ++ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; ++ dev_info(sdev->dev, ++ "SSP%d to use MCLK id %d (mask: %#x)\n", ++ ssp_num, sdev->mclk_id_quirk, mclk_mask); ++ } else { ++ dev_dbg(sdev->dev, ++ "MCLK mask is empty for SSP%d in NHLT\n", ++ ssp_num); ++ } + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security b/SPECS/kernel-rt/0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security new file mode 100644 index 000000000..c579aa530 --- /dev/null +++ b/SPECS/kernel-rt/0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security @@ -0,0 +1,344 @@ +From 7e9b607ccd9a4d4220d9f8054b80311f16f9f642 Mon Sep 17 00:00:00 2001 +From: Adam Pawlicki +Date: Thu, 13 Nov 2025 10:32:40 +0100 +Subject: [PATCH 2/2] Add updated TPR (TXT Protected Regions) support to + kernel. + +This commit applies a TPR-support patch from Tboot Live Image project. +Its intent is to make kernel aware of the TPR memory and to disable +it before proceeding with system boot so that kernel can establish its +own DMA protection. + +Signed-off-by: AdamX Pawlicki +Signed-off-by: Mateusz Mowka +--- + arch/x86/kernel/tboot.c | 109 +++++++++++++++++++++++++++++++++++- + drivers/iommu/intel/dmar.c | 8 +++ + drivers/iommu/intel/iommu.c | 7 ++- + include/acpi/actbl1.h | 86 ++++++++++++++++++++++++++++ + include/linux/tboot.h | 6 ++ + 5 files changed, 214 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c +index 46b8f1f16676e..04929aba6a5cc 100644 +--- a/arch/x86/kernel/tboot.c ++++ b/arch/x86/kernel/tboot.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -453,8 +454,14 @@ struct sha1_hash { + u8 hash[SHA1_SIZE]; + }; + ++struct heap_ext_data_elt { ++ u32 type; ++ u32 size; ++ u8 data[]; ++} __packed; ++ + struct sinit_mle_data { +- u32 version; /* currently 6 */ ++ u32 version; /* currently 9 */ + struct sha1_hash bios_acm_id; + u32 edx_senter_flags; + u64 mseg_valid; +@@ -469,8 +476,18 @@ struct sinit_mle_data { + u32 mdrs_off; + u32 num_vtd_dmars; + u32 vtd_dmars_off; ++ u32 proc_scrtm_status; /* version 8 or later only*/ ++ struct heap_ext_data_elt ext_data_elts[]; + } __packed; + ++#define HEAP_EXTDATA_TYPE_DTPR 14 ++ ++struct acpi_dtpr_serialize_req { ++ u64 sts : 1; ++ u64 ctrl : 1; ++ u64 unused : 62; ++}; ++ + struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl) + { + void *heap_base, *heap_ptr, *config; +@@ -514,3 +531,93 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb + + return dmar_tbl; + } ++ ++struct acpi_table_dtpr *tboot_get_dtpr_table(void) ++{ ++ void *heap_base, *heap_ptr, *config; ++ struct sinit_mle_data *sinit_mle; ++ struct heap_ext_data_elt *elt; ++ u64 sinit_mle_size; ++ ++ if (!tboot_enabled()) ++ return NULL; ++ /* ++ * ACPI tables may not be DMA protected by tboot, so use DMAR copy ++ * SINIT saved in SinitMleData in TXT heap (which is DMA protected) ++ */ ++ ++ /* map config space in order to get heap addr */ ++ config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES * ++ PAGE_SIZE); ++ if (!config) ++ return NULL; ++ ++ /* now map TXT heap */ ++ heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE), ++ *(u64 *)(config + TXTCR_HEAP_SIZE)); ++ iounmap(config); ++ if (!heap_base) ++ return NULL; ++ ++ /* walk heap to SinitMleData */ ++ /* skip BiosData */ ++ heap_ptr = heap_base + *(u64 *)heap_base; ++ /* skip OsMleData */ ++ heap_ptr += *(u64 *)heap_ptr; ++ /* skip OsSinitData */ ++ heap_ptr += *(u64 *)heap_ptr; ++ /* now points to SinitMleDataSize; set to SinitMleData */ ++ sinit_mle_size = *(u64 *)heap_ptr; ++ heap_ptr += sizeof(u64); ++ ++ sinit_mle = (struct sinit_mle_data *)heap_ptr; ++ if (sinit_mle->version < 9) ++ return NULL; ++ ++ elt = sinit_mle->ext_data_elts; ++ while (elt->type != HEAP_EXTDATA_TYPE_DTPR) { ++ elt = (void *)elt + elt->size; ++ if ((u64)elt > (u64)sinit_mle + sinit_mle_size) ++ return NULL; ++ } ++ return (struct acpi_table_dtpr *)elt->data; ++} ++ ++static bool tboot_tpr_enabled; ++void tboot_parse_dtpr_table(void) ++{ ++ struct acpi_table_dtpr *dtpr; ++ struct acpi_dtpr_instance *tpr_inst; ++ u32 *instance_cnt; ++ u32 i, j; ++ ++ if (!tboot_enabled()) ++ return; ++ dtpr = tboot_get_dtpr_table(); ++ if (dtpr == NULL) ++ return; ++ tboot_tpr_enabled = 1; ++ instance_cnt = (u32 *)(dtpr + 1); ++ tpr_inst = (struct acpi_dtpr_instance *)(instance_cnt + 1); ++ for (i = 0; i < *instance_cnt; ++i) { ++ for (j = 0; j < tpr_inst->tpr_cnt; ++j) { ++ uint64_t *base = ioremap(tpr_inst->tpr_array[j].base, 16); ++ ++ if (base != NULL) { ++ pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", ++ i, j, readq(base), readq(base + 1)); ++ *base |= (1 << 4); ++ iounmap(base); ++ } ++ } ++ tpr_inst = (struct acpi_dtpr_instance *)((u8 *)tpr_inst ++ + sizeof(*tpr_inst) + j*sizeof(*(tpr_inst->tpr_array))); ++ } ++ if (tboot_tpr_enabled) ++ pr_debug("TPR protection detected, PMR will be disabled\n"); ++} ++ ++bool tboot_is_tpr_enabled(void) ++{ ++ return tboot_tpr_enabled; ++} +diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c +index ec975c73cfe6c..9c6f93cf1b3f6 100644 +--- a/drivers/iommu/intel/dmar.c ++++ b/drivers/iommu/intel/dmar.c +@@ -635,6 +635,7 @@ static int __init + parse_dmar_table(void) + { + struct acpi_table_dmar *dmar; ++ struct acpi_table_dtpr *dtpr; + int drhd_count = 0; + int ret; + struct dmar_res_callback cb = { +@@ -670,6 +671,13 @@ parse_dmar_table(void) + return -EINVAL; + } + ++ dtpr = tboot_get_dtpr_table(); ++ if (dtpr) { ++ //TPR is enabled ++ //This will also tell not to establish IOMMU PMRs ++ tboot_parse_dtpr_table(); ++ } ++ + pr_info("Host address width %d\n", dmar->width + 1); + ret = dmar_walk_dmar_table(dmar, &cb); + if (ret == 0 && drhd_count == 0) +diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c +index e236c7ec221f4..5847fb589c892 100644 +--- a/drivers/iommu/intel/iommu.c ++++ b/drivers/iommu/intel/iommu.c +@@ -3017,6 +3017,11 @@ static __init int tboot_force_iommu(void) + if (!tboot_enabled()) + return 0; + ++ //If TPR is enabled we don't need to force IOMMU, ++ //TPR set by SINIT ACM will take care of DMA protection ++ if (tboot_is_tpr_enabled()) ++ return 0; ++ + if (no_iommu || dmar_disabled) + pr_warn("Forcing Intel-IOMMU to enabled\n"); + +@@ -3074,7 +3079,7 @@ int __init intel_iommu_init(void) + * calling SENTER, but the kernel is expected to reset/tear + * down the PMRs. + */ +- if (intel_iommu_tboot_noforce) { ++ if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) { + for_each_iommu(iommu, drhd) + iommu_disable_protect_mem_regions(iommu); + } +diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h +index 7f35eb0e84586..c77b0890970c6 100644 +--- a/include/acpi/actbl1.h ++++ b/include/acpi/actbl1.h +@@ -47,6 +47,7 @@ + #define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ + #define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */ + #define ACPI_SIG_MSCT "MSCT" /* Maximum System Characteristics Table */ ++#define ACPI_SIG_DTPR "DTPR" /* TXT DMA Protection Ranges reporting table */ + + #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ + #define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ +@@ -1973,6 +1974,91 @@ struct acpi_ibft_target { + u16 reverse_chap_secret_offset; + }; + ++/******************************************************************************* ++ * ++ * DTPR - DMA TPR Reporting ++ * Version 1 ++ * ++ * Conforms to "Intel TXT DMA Protection Ranges", ++ * Version xxx, April 2021 ++ * ++ ******************************************************************************/ ++ ++struct acpi_table_dtpr { ++ struct acpi_table_header header; ++ u32 flags; // 36 ++}; ++ ++struct acpi_tpr_array { ++ u64 base; ++}; ++ ++struct acpi_dtpr_instance { ++ u32 flags; ++ u32 tpr_cnt; ++ struct acpi_tpr_array tpr_array[]; ++}; ++ ++/******************************************************************************* ++ * TPRn_BASE ++ * ++ * Specifies the start address of TPRn region. TPR region address and size must ++ * be with 1MB resolution. These bits are compared with the result of the ++ * TPRn_LIMIT[63:20] * applied to the incoming address, to determine if an ++ * access fall within the TPRn defined region. ++ ******************************************************************************/ ++struct acpi_dtprn_base_reg { ++ u64 reserved0 : 3; ++ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) ++ u64 enable : 1; // 0 == range enabled, 1 == range disabled ++ u64 reserved1 : 15; ++ // Minimal TPRn_Base resolution is 1MB. ++ // Applied to the incoming address, to determine if an access ++ // fall within the TPRn defined region. ++ // Width is determined by a bus width which can be obtained ++ // via CPUID function 0x80000008. ++ u64 tpr_base_rw : 44; ++ //u64 unused : 1; ++}; ++ ++/******************************************************************************* ++ * TPRn_LIMIT ++ * ++ * This register defines an isolated region of memory that can be enabled ++ * to prohibit certain system agents from accessing memory. When an agent ++ * sends a request upstream, whether snooped or not, a TPR prevents that ++ * transaction from changing the state of memory. ++ ******************************************************************************/ ++ ++struct acpi_dtprn_limit_reg { ++ u64 reserved0 : 3; ++ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) ++ u64 enable : 1; // 0 == range enabled, 1 == range disabled ++ u64 reserved1 : 15; ++ // Minimal TPRn_Limit resolution is 1MB. ++ // These bits define TPR limit address. ++ // Width is determined by a bus width. ++ u64 tpr_limit_rw : 44; ++ //u64 unused : 1; ++}; ++ ++/******************************************************************************* ++ * SERIALIZE_REQUEST ++ * ++ * This register is used to request serialization of non-coherent DMA ++ * transactions. OS shall issue it before changing of TPR settings ++ * (base / size). ++ ******************************************************************************/ ++ ++struct acpi_tpr_serialize_request { ++ u64 sts : 1; // Status of serialization request (RO) ++ // 0 == register idle, 1 == serialization in progress ++ u64 ctrl : 1; // Control field to initiate serialization (RW) ++ // 0 == normal, 1 == initialize serialization ++ // (self-clear to allow multiple serialization requests) ++ u64 unused : 62; ++}; ++ + /* Reset to default packing */ + + #pragma pack() +diff --git a/include/linux/tboot.h b/include/linux/tboot.h +index d2279160ef39d..95f784b0bde0a 100644 +--- a/include/linux/tboot.h ++++ b/include/linux/tboot.h +@@ -126,6 +126,9 @@ extern void tboot_probe(void); + extern void tboot_shutdown(u32 shutdown_type); + extern struct acpi_table_header *tboot_get_dmar_table( + struct acpi_table_header *dmar_tbl); ++extern struct acpi_table_dtpr *tboot_get_dtpr_table(void); ++extern void tboot_parse_dtpr_table(void); ++extern bool tboot_is_tpr_enabled(void); + + #else + +@@ -135,6 +138,9 @@ extern struct acpi_table_header *tboot_get_dmar_table( + #define tboot_sleep(sleep_state, pm1a_control, pm1b_control) \ + do { } while (0) + #define tboot_get_dmar_table(dmar_tbl) (dmar_tbl) ++#define tboot_get_dtpr_table() 0 ++#define tboot_parse_dtpr_table() do { } while (0) ++#define tboot_is_tpr_enabled() 0 + + #endif /* !CONFIG_INTEL_TXT */ + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac b/SPECS/kernel-rt/0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac new file mode 100644 index 000000000..d797f85e3 --- /dev/null +++ b/SPECS/kernel-rt/0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac @@ -0,0 +1,54 @@ +From 81409764b31a445a2e7981b938bbf5568633277b Mon Sep 17 00:00:00 2001 +From: Lili Li +Date: Tue, 11 Mar 2025 11:00:23 +0800 +Subject: [PATCH 2/3] EDAC/igen6: Add more Intel Panther Lake-H SoCs support + +Add more Intel Panther Lake-H SoC compute die IDs for EDAC support. + +Reviewed-by: Qiuxu Zhuo +Signed-off-by: Lili Li +--- + drivers/edac/igen6_edac.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 092397a154d99..3e9d0118e3958 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -276,6 +276,16 @@ static struct work_struct ecclog_work; + #define DID_PTL_H_SKU1 0xb000 + #define DID_PTL_H_SKU2 0xb001 + #define DID_PTL_H_SKU3 0xb002 ++#define DID_PTL_H_SKU4 0xb003 ++#define DID_PTL_H_SKU5 0xb004 ++#define DID_PTL_H_SKU6 0xb005 ++#define DID_PTL_H_SKU7 0xb008 ++#define DID_PTL_H_SKU8 0xb011 ++#define DID_PTL_H_SKU9 0xb014 ++#define DID_PTL_H_SKU10 0xb015 ++#define DID_PTL_H_SKU11 0xb028 ++#define DID_PTL_H_SKU12 0xb029 ++#define DID_PTL_H_SKU13 0xb02a + + /* Compute die IDs for Wildcat Lake with IBECC */ + #define DID_WCL_SKU1 0xfd00 +@@ -640,6 +650,16 @@ static struct pci_device_id igen6_pci_tbl[] = { + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, + { }, + }; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac b/SPECS/kernel-rt/0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac deleted file mode 100644 index 0d91f65b4..000000000 --- a/SPECS/kernel-rt/0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac +++ /dev/null @@ -1,188 +0,0 @@ -From ae4d7c15450606f3121d104cdeffced1efb93122 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Tue, 22 Jul 2025 11:25:20 +0800 -Subject: [PATCH 02/13] EDAC/{skx_common,skx}: Use configuration data, not - global macros - -Use model-specific configuration data for the number of memory controllers -per socket, channels per memory controller, and DIMMs per channel as -intended, instead of relying on global macros for maximum values. - -No functional changes intended. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_base.c | 33 ++++++++++++++++++++------------- - drivers/edac/skx_common.c | 16 +++++++++------- - drivers/edac/skx_common.h | 1 + - 3 files changed, 30 insertions(+), 20 deletions(-) - -diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c -index 29897b21fb8e..078ddf95cc6e 100644 ---- a/drivers/edac/skx_base.c -+++ b/drivers/edac/skx_base.c -@@ -33,6 +33,15 @@ static unsigned int nvdimm_count; - #define MASK26 0x3FFFFFF /* Mask for 2^26 */ - #define MASK29 0x1FFFFFFF /* Mask for 2^29 */ - -+static struct res_config skx_cfg = { -+ .type = SKX, -+ .decs_did = 0x2016, -+ .busno_cfg_offset = 0xcc, -+ .ddr_imc_num = 2, -+ .ddr_chan_num = 3, -+ .ddr_dimm_num = 2, -+}; -+ - static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx) - { - struct skx_dev *d; -@@ -52,7 +61,7 @@ enum munittype { - - struct munit { - u16 did; -- u16 devfn[SKX_NUM_IMC]; -+ u16 devfn[2]; - u8 busidx; - u8 per_socket; - enum munittype mtype; -@@ -89,11 +98,11 @@ static int get_all_munits(const struct munit *m) - if (!pdev) - break; - ndev++; -- if (m->per_socket == SKX_NUM_IMC) { -- for (i = 0; i < SKX_NUM_IMC; i++) -+ if (m->per_socket == skx_cfg.ddr_imc_num) { -+ for (i = 0; i < skx_cfg.ddr_imc_num; i++) - if (m->devfn[i] == pdev->devfn) - break; -- if (i == SKX_NUM_IMC) -+ if (i == skx_cfg.ddr_imc_num) - goto fail; - } - d = get_skx_dev(pdev->bus, m->busidx); -@@ -157,12 +166,6 @@ static int get_all_munits(const struct munit *m) - return -ENODEV; - } - --static struct res_config skx_cfg = { -- .type = SKX, -- .decs_did = 0x2016, -- .busno_cfg_offset = 0xcc, --}; -- - static const struct x86_cpu_id skx_cpuids[] = { - X86_MATCH_VFM(INTEL_SKYLAKE_X, &skx_cfg), - { } -@@ -186,11 +189,11 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci, struct res_config *cfg) - /* Only the mcmtr on the first channel is effective */ - pci_read_config_dword(imc->chan[0].cdev, 0x87c, &mcmtr); - -- for (i = 0; i < SKX_NUM_CHANNELS; i++) { -+ for (i = 0; i < cfg->ddr_chan_num; i++) { - ndimms = 0; - pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap); - pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg); -- for (j = 0; j < SKX_NUM_DIMMS; j++) { -+ for (j = 0; j < cfg->ddr_dimm_num; j++) { - dimm = edac_get_dimm(mci, i, j, 0); - pci_read_config_dword(imc->chan[i].cdev, - 0x80 + 4 * j, &mtr); -@@ -620,6 +623,7 @@ static int __init skx_init(void) - return -ENODEV; - - cfg = (struct res_config *)id->driver_data; -+ skx_set_res_cfg(cfg); - - rc = skx_get_hi_lo(0x2034, off, &skx_tolm, &skx_tohm); - if (rc) -@@ -652,10 +656,13 @@ static int __init skx_init(void) - goto fail; - - edac_dbg(2, "src_id = %d\n", src_id); -- for (i = 0; i < SKX_NUM_IMC; i++) { -+ for (i = 0; i < cfg->ddr_imc_num; i++) { - d->imc[i].mc = mc++; - d->imc[i].lmc = i; - d->imc[i].src_id = src_id; -+ d->imc[i].num_channels = cfg->ddr_chan_num; -+ d->imc[i].num_dimms = cfg->ddr_dimm_num; -+ - rc = skx_register_mci(&d->imc[i], d->imc[i].chan[0].cdev, - "Skylake Socket", EDAC_MOD_STR, - skx_get_dimm_config, cfg); -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index 39c733dbc5b9..cc7d36cf7f3b 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -320,10 +320,10 @@ static int get_width(u32 mtr) - */ - int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) - { -+ int ndev = 0, imc_num = cfg->ddr_imc_num + cfg->hbm_imc_num; - struct pci_dev *pdev, *prev; - struct skx_dev *d; - u32 reg; -- int ndev = 0; - - prev = NULL; - for (;;) { -@@ -354,8 +354,10 @@ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) - d->seg = GET_BITFIELD(reg, 16, 23); - } - -- edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n", -- d->bus[0], d->bus[1], d->bus[2], d->bus[3]); -+ d->num_imc = imc_num; -+ -+ edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x, imcs %d\n", -+ d->bus[0], d->bus[1], d->bus[2], d->bus[3], imc_num); - list_add_tail(&d->list, &dev_edac_list); - prev = pdev; - -@@ -541,10 +543,10 @@ int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, - - /* Allocate a new MC control structure */ - layers[0].type = EDAC_MC_LAYER_CHANNEL; -- layers[0].size = NUM_CHANNELS; -+ layers[0].size = imc->num_channels; - layers[0].is_virt_csrow = false; - layers[1].type = EDAC_MC_LAYER_SLOT; -- layers[1].size = NUM_DIMMS; -+ layers[1].size = imc->num_dimms; - layers[1].is_virt_csrow = true; - mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers, - sizeof(struct skx_pvt)); -@@ -784,7 +786,7 @@ void skx_remove(void) - - list_for_each_entry_safe(d, tmp, &dev_edac_list, list) { - list_del(&d->list); -- for (i = 0; i < NUM_IMC; i++) { -+ for (i = 0; i < d->num_imc; i++) { - if (d->imc[i].mci) - skx_unregister_mci(&d->imc[i]); - -@@ -794,7 +796,7 @@ void skx_remove(void) - if (d->imc[i].mbase) - iounmap(d->imc[i].mbase); - -- for (j = 0; j < NUM_CHANNELS; j++) { -+ for (j = 0; j < d->imc[i].num_channels; j++) { - if (d->imc[i].chan[j].cdev) - pci_dev_put(d->imc[i].chan[j].cdev); - } -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index ec4966f7ea40..3f6007a97267 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -134,6 +134,7 @@ struct skx_dev { - struct pci_dev *uracu; /* for i10nm CPU */ - struct pci_dev *pcu_cr3; /* for HBM memory detection */ - u32 mcroute; -+ int num_imc; - /* - * Some server BIOS may hide certain memory controllers, and the - * EDAC driver skips those hidden memory controllers. However, the --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-INT3472-Support-LT6911GXD.ipu b/SPECS/kernel-rt/0002-INT3472-Support-LT6911GXD.ipu deleted file mode 100644 index 1a83fe435..000000000 --- a/SPECS/kernel-rt/0002-INT3472-Support-LT6911GXD.ipu +++ /dev/null @@ -1,26 +0,0 @@ -From 801344b0e847b7915a9f133ddb08b824fee13658 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Wed, 1 Oct 2025 08:02:47 +0800 -Subject: [PATCH 02/11] INT3472: Support LT6911GXD - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/platform/x86/intel/int3472/discrete.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c -index bdfb8a800c54..eb389888a145 100644 ---- a/drivers/platform/x86/intel/int3472/discrete.c -+++ b/drivers/platform/x86/intel/int3472/discrete.c -@@ -436,7 +436,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) - return ret; - } - -- if (cldb.control_logic_type != 1) { -+ if (cldb.control_logic_type != 1 && cldb.control_logic_type != 5) { - dev_err(&pdev->dev, "Unsupported control logic type %u\n", - cldb.control_logic_type); - return -EINVAL; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi b/SPECS/kernel-rt/0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi deleted file mode 100644 index a6d1105c8..000000000 --- a/SPECS/kernel-rt/0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi +++ /dev/null @@ -1,218 +0,0 @@ -From 3ffaf7c418929b7b97e564a0cf78d5e74bce5a0a Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 15:51:20 -0700 -Subject: [PATCH 02/44] KVM: VMX: Add support for the secondary VM exit - controls - -Always load the secondary VM exit controls to prepare for FRED enabling. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Changes in v4: -* Fix clearing VM_EXIT_ACTIVATE_SECONDARY_CONTROLS (Chao Gao). -* Check VM exit/entry consistency based on the new macro from Sean - Christopherson. - -Change in v3: -* Do FRED controls consistency checks in the VM exit/entry consistency - check framework (Sean Christopherson). - -Change in v2: -* Always load the secondary VM exit controls (Sean Christopherson). ---- - arch/x86/include/asm/msr-index.h | 1 + - arch/x86/include/asm/vmx.h | 3 +++ - arch/x86/kvm/vmx/capabilities.h | 9 ++++++++- - arch/x86/kvm/vmx/vmcs.h | 1 + - arch/x86/kvm/vmx/vmx.c | 29 +++++++++++++++++++++++++++-- - arch/x86/kvm/vmx/vmx.h | 7 ++++++- - 6 files changed, 46 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 6b22abdc856a..b56d14b74b21 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -1240,6 +1240,7 @@ - #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 - #define MSR_IA32_VMX_VMFUNC 0x00000491 - #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 -+#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 - - /* Resctrl MSRs: */ - /* - Intel: */ -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index b92ff87e3560..5b53b8da4ce0 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -108,6 +108,7 @@ - #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 - #define VM_EXIT_LOAD_CET_STATE 0x10000000 - #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 -+#define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 - - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff - -@@ -263,6 +264,8 @@ enum vmcs_field { - SHARED_EPT_POINTER = 0x0000203C, - PID_POINTER_TABLE = 0x00002042, - PID_POINTER_TABLE_HIGH = 0x00002043, -+ SECONDARY_VM_EXIT_CONTROLS = 0x00002044, -+ SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, - GUEST_PHYSICAL_ADDRESS = 0x00002400, - GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, - VMCS_LINK_POINTER = 0x00002800, -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 79cd9d316ba4..422d2846980c 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -55,8 +55,9 @@ struct vmcs_config { - u32 cpu_based_exec_ctrl; - u32 cpu_based_2nd_exec_ctrl; - u64 cpu_based_3rd_exec_ctrl; -- u32 vmexit_ctrl; - u32 vmentry_ctrl; -+ u32 vmexit_ctrl; -+ u64 vmexit_2nd_ctrl; - u64 misc; - struct nested_vmx_msrs nested; - }; -@@ -147,6 +148,12 @@ static inline bool cpu_has_tertiary_exec_ctrls(void) - CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; - } - -+static inline bool cpu_has_secondary_vmexit_ctrls(void) -+{ -+ return vmcs_config.vmexit_ctrl & -+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; -+} -+ - static inline bool cpu_has_vmx_virtualize_apic_accesses(void) - { - return vmcs_config.cpu_based_2nd_exec_ctrl & -diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h -index b25625314658..ae152a9d1963 100644 ---- a/arch/x86/kvm/vmx/vmcs.h -+++ b/arch/x86/kvm/vmx/vmcs.h -@@ -47,6 +47,7 @@ struct vmcs_host_state { - struct vmcs_controls_shadow { - u32 vm_entry; - u32 vm_exit; -+ u64 secondary_vm_exit; - u32 pin; - u32 exec; - u32 secondary_exec; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index bec76fbd0b09..bad778acf9b5 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2598,8 +2598,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - u32 _cpu_based_exec_control = 0; - u32 _cpu_based_2nd_exec_control = 0; - u64 _cpu_based_3rd_exec_control = 0; -- u32 _vmexit_control = 0; - u32 _vmentry_control = 0; -+ u32 _vmexit_control = 0; -+ u64 _vmexit2_control = 0; - u64 basic_msr; - u64 misc_msr; - -@@ -2620,6 +2621,12 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - { VM_ENTRY_LOAD_CET_STATE, VM_EXIT_LOAD_CET_STATE }, - }; - -+ struct { -+ u32 entry_control; -+ u64 exit_control; -+ } const vmcs_entry_exit2_pairs[] = { -+ }; -+ - memset(vmcs_conf, 0, sizeof(*vmcs_conf)); - - if (adjust_vmx_controls(KVM_REQUIRED_VMX_CPU_BASED_VM_EXEC_CONTROL, -@@ -2706,10 +2713,19 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - &_vmentry_control)) - return -EIO; - -+ if (_vmexit_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) -+ _vmexit2_control = -+ adjust_vmx_controls64(KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS, -+ MSR_IA32_VMX_EXIT_CTLS2); -+ - if (vmx_check_entry_exit_pairs(vmcs_entry_exit_pairs, - _vmentry_control, _vmexit_control)) - return -EIO; - -+ if (vmx_check_entry_exit_pairs(vmcs_entry_exit2_pairs, -+ _vmentry_control, _vmexit2_control)) -+ return -EIO; -+ - /* - * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they - * can't be used due to an errata where VM Exit may incorrectly clear -@@ -2758,8 +2774,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control; - vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control; - vmcs_conf->cpu_based_3rd_exec_ctrl = _cpu_based_3rd_exec_control; -- vmcs_conf->vmexit_ctrl = _vmexit_control; - vmcs_conf->vmentry_ctrl = _vmentry_control; -+ vmcs_conf->vmexit_ctrl = _vmexit_control; -+ vmcs_conf->vmexit_2nd_ctrl = _vmexit2_control; - vmcs_conf->misc = misc_msr; - - #if IS_ENABLED(CONFIG_HYPERV) -@@ -4480,6 +4497,11 @@ static u32 vmx_get_initial_vmexit_ctrl(void) - VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL); - } - -+static u64 vmx_secondary_vmexit_ctrl(void) -+{ -+ return vmcs_config.vmexit_2nd_ctrl; -+} -+ - void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) - { - struct vcpu_vmx *vmx = to_vmx(vcpu); -@@ -4822,6 +4844,9 @@ static void init_vmcs(struct vcpu_vmx *vmx) - - vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl()); - -+ if (cpu_has_secondary_vmexit_ctrls()) -+ secondary_vm_exit_controls_set(vmx, vmx_secondary_vmexit_ctrl()); -+ - /* 22.2.1, 20.8.1 */ - vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl()); - -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index ef8fd345f1ae..47395cb38d88 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -512,7 +512,11 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_PT_CONCEAL_PIP | \ - VM_EXIT_CLEAR_IA32_RTIT_CTL | \ - VM_EXIT_LOAD_CET_STATE | \ -- VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL | \ -+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) -+ -+#define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) -+#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ - (PIN_BASED_EXT_INTR_MASK | \ -@@ -625,6 +629,7 @@ static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u## - } - BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) - BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) -+BUILD_CONTROLS_SHADOW(secondary_vm_exit, SECONDARY_VM_EXIT_CONTROLS, 64) - BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL, 32) - BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL, 32) - BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL, 32) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi b/SPECS/kernel-rt/0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi new file mode 100644 index 000000000..4b7d28260 --- /dev/null +++ b/SPECS/kernel-rt/0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi @@ -0,0 +1,106 @@ +From 9f046ab7d611686d27760591e399cb83da0d35e5 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Wed, 30 Aug 2023 23:55:54 -0700 +Subject: [PATCH 02/44] KVM: VMX: Initialize VM entry/exit FRED controls in + vmcs_config + +Setup VM entry/exit FRED controls in the global vmcs_config for proper +FRED VMCS fields management: + 1) load guest FRED state upon VM entry. + 2) save guest FRED state during VM exit. + 3) load host FRED state during VM exit. + +Also add FRED control consistency checks to the existing VM entry/exit +consistency check framework. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +Reviewed-by: Chao Gao +--- + +Change in v5: +* Remove the pair VM_ENTRY_LOAD_IA32_FRED/VM_EXIT_ACTIVATE_SECONDARY_CONTROLS, + since the secondary VM exit controls are unconditionally enabled anyway, and + there are features other than FRED needing it (Chao Gao). +* Add TB from Xuelian Guo. + +Change in v4: +* Do VM exit/entry consistency checks using the new macro from Sean + Christopherson. + +Changes in v3: +* Add FRED control consistency checks to the existing VM entry/exit + consistency check framework (Sean Christopherson). +* Just do the unnecessary FRED state load/store on every VM entry/exit + (Sean Christopherson). +--- + arch/x86/include/asm/vmx.h | 4 ++++ + arch/x86/kvm/vmx/vmx.c | 2 ++ + arch/x86/kvm/vmx/vmx.h | 7 +++++-- + 3 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index 1f60c04d11fbd..dd79d027ea700 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -109,6 +109,9 @@ + #define VM_EXIT_LOAD_CET_STATE 0x10000000 + #define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 + ++#define SECONDARY_VM_EXIT_SAVE_IA32_FRED BIT_ULL(0) ++#define SECONDARY_VM_EXIT_LOAD_IA32_FRED BIT_ULL(1) ++ + #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff + + #define VM_ENTRY_LOAD_DEBUG_CONTROLS 0x00000004 +@@ -122,6 +125,7 @@ + #define VM_ENTRY_PT_CONCEAL_PIP 0x00020000 + #define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 + #define VM_ENTRY_LOAD_CET_STATE 0x00100000 ++#define VM_ENTRY_LOAD_IA32_FRED 0x00800000 + + #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index f27d29c836298..fbeb85c4673e5 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -2622,6 +2622,8 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + u32 entry_control; + u64 exit_control; + } const vmcs_entry_exit2_pairs[] = { ++ { VM_ENTRY_LOAD_IA32_FRED, ++ SECONDARY_VM_EXIT_SAVE_IA32_FRED | SECONDARY_VM_EXIT_LOAD_IA32_FRED }, + }; + + memset(vmcs_conf, 0, sizeof(*vmcs_conf)); +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index b2724aab48d2b..2cf599211ab30 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -488,7 +488,8 @@ static inline u8 vmx_get_rvi(void) + VM_ENTRY_LOAD_BNDCFGS | \ + VM_ENTRY_PT_CONCEAL_PIP | \ + VM_ENTRY_LOAD_IA32_RTIT_CTL | \ +- VM_ENTRY_LOAD_CET_STATE) ++ VM_ENTRY_LOAD_CET_STATE | \ ++ VM_ENTRY_LOAD_IA32_FRED) + + #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS \ + (VM_EXIT_SAVE_DEBUG_CONTROLS | \ +@@ -515,7 +516,9 @@ static inline u8 vmx_get_rvi(void) + VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) + + #define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) +-#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) ++#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS \ ++ (SECONDARY_VM_EXIT_SAVE_IA32_FRED | \ ++ SECONDARY_VM_EXIT_LOAD_IA32_FRED) + + #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ + (PIN_BASED_EXT_INTR_MASK | \ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet b/SPECS/kernel-rt/0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet deleted file mode 100644 index 6013f9686..000000000 --- a/SPECS/kernel-rt/0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet +++ /dev/null @@ -1,76 +0,0 @@ -From 5e1b71248258e5e6a04bb3311d5f9a54afa79882 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 29 Jul 2025 11:13:48 -0700 -Subject: [PATCH 02/24] KVM: x86: Use double-underscore read/write MSR helpers - as appropriate - -Use the double-underscore helpers for emulating MSR reads and writes in -he no-underscore versions to better capture the relationship between the -two sets of APIs (the double-underscore versions don't honor userspace MSR -filters). - -No functional change intended. - -Signed-off-by: Sean Christopherson -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 29 ++++++++++++++++------------- - 1 file changed, 16 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 00e8501f88c8..9e78753b36a4 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1936,11 +1936,24 @@ static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu, - __kvm_get_msr); - } - -+int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+{ -+ return kvm_get_msr_ignored_check(vcpu, index, data, false); -+} -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_read); -+ -+int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) -+{ -+ return kvm_set_msr_ignored_check(vcpu, index, data, false); -+} -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_write); -+ - int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ)) - return KVM_MSR_RET_FILTERED; -- return kvm_get_msr_ignored_check(vcpu, index, data, false); -+ -+ return __kvm_emulate_msr_read(vcpu, index, data); - } - EXPORT_SYMBOL_GPL(kvm_emulate_msr_read); - -@@ -1948,21 +1961,11 @@ int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE)) - return KVM_MSR_RET_FILTERED; -- return kvm_set_msr_ignored_check(vcpu, index, data, false); --} --EXPORT_SYMBOL_GPL(kvm_emulate_msr_write); - --int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) --{ -- return kvm_get_msr_ignored_check(vcpu, index, data, false); -+ return __kvm_emulate_msr_write(vcpu, index, data); - } --EXPORT_SYMBOL_GPL(__kvm_emulate_msr_read); -+EXPORT_SYMBOL_GPL(kvm_emulate_msr_write); - --int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) --{ -- return kvm_set_msr_ignored_check(vcpu, index, data, false); --} --EXPORT_SYMBOL_GPL(__kvm_emulate_msr_write); - - static void complete_userspace_rdmsr(struct kvm_vcpu *vcpu) - { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss b/SPECS/kernel-rt/0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss deleted file mode 100644 index 4cb902e48..000000000 --- a/SPECS/kernel-rt/0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss +++ /dev/null @@ -1,55 +0,0 @@ -From e3a43a95d50ac13d0f2bdca89f48f0ccab0a1ff1 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Mon, 21 Nov 2022 14:01:14 +0200 -Subject: [PATCH 02/16] PCI/portdrv: Do not require an interrupt for all AER - capable ports - -Only Root Ports and Event Collectors use MSI for AER. PCIe Switch ports -or endpoints on the other hand only send messages (that get collected by -the former). For this reason do not require PCIe switch ports and -endpoints to use interrupt if they support AER. - -This allows portdrv to attach PCIe switch ports of Intel DG1 and DG2 -discrete graphics cards. These do not declare MSI or legacy interrupts. - -Signed-off-by: Mika Westerberg ---- - drivers/pci/pcie/portdrv.c | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) - -diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c -index d1b68c18444f..cabf6261779b 100644 ---- a/drivers/pci/pcie/portdrv.c -+++ b/drivers/pci/pcie/portdrv.c -@@ -176,7 +176,7 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) - */ - static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - { -- int ret, i; -+ int ret, i, type; - - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) - irqs[i] = -1; -@@ -189,6 +189,19 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) - goto intx_irq; - -+ /* -+ * Only root ports and event collectors use MSI for errors. Endpoints, -+ * switch ports send messages to them but don't use MSI for that (PCIe -+ * 5.0 sec 6.2.3.2). -+ */ -+ type = pci_pcie_type(dev); -+ if ((mask & PCIE_PORT_SERVICE_AER) && -+ type != PCI_EXP_TYPE_ROOT_PORT && type != PCI_EXP_TYPE_RC_EC) -+ mask &= ~PCIE_PORT_SERVICE_AER; -+ -+ if (!mask) -+ return 0; -+ - /* Try to use MSI-X or MSI if supported */ - if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0) - return 0; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu b/SPECS/kernel-rt/0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu deleted file mode 100644 index ca0386962..000000000 --- a/SPECS/kernel-rt/0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu +++ /dev/null @@ -1,876 +0,0 @@ -From f753404ff8871c4779b3b9a6b807c8c08b910b3a Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 4 Nov 2025 14:28:40 +0800 -Subject: [PATCH 2/2] Revert "media: i2c: max9x: fix S3/S4 error for max9x" - -This reverts commit 34b0b6fcd8a20612950a7a8a7d1b252c43313119. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/isx031.c | 70 +++++------- - drivers/media/i2c/max9x/Makefile | 2 +- - drivers/media/i2c/max9x/max9295.c | 39 +++---- - drivers/media/i2c/max9x/max9296.h | 2 +- - drivers/media/i2c/max9x/max96724.c | 7 +- - drivers/media/i2c/max9x/regmap-retry.c | 52 --------- - drivers/media/i2c/max9x/regmap-retry.h | 18 --- - drivers/media/i2c/max9x/serdes.c | 148 +++++++++++++++++-------- - 8 files changed, 150 insertions(+), 188 deletions(-) - delete mode 100644 drivers/media/i2c/max9x/regmap-retry.c - delete mode 100644 drivers/media/i2c/max9x/regmap-retry.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index e613cdb06b83..ddc3e512efa4 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -33,8 +33,6 @@ - #define ISX031_MODE_4LANES_30FPS 0x17 - #define ISX031_MODE_2LANES_30FPS 0x18 - --static DEFINE_MUTEX(isx031_mutex); -- - struct isx031_reg { - enum { - ISX031_REG_LEN_DELAY = 0, -@@ -97,7 +95,7 @@ struct isx031 { - u8 lanes; - - /* To serialize asynchronus callbacks */ -- struct mutex *mutex; -+ struct mutex mutex; - - /* i2c client */ - struct i2c_client *client; -@@ -308,21 +306,6 @@ static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) - return 0; - } - --static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) --{ -- int ret; -- int retry = 100; -- -- while (retry--) { -- ret = isx031_write_reg(isx031, reg, len, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- - static int isx031_write_reg_list(struct isx031 *isx031, - const struct isx031_reg_list *r_list) - { -@@ -335,7 +318,7 @@ static int isx031_write_reg_list(struct isx031 *isx031, - msleep(r_list->regs[i].val); - continue; - } -- ret = isx031_write_reg_retry(isx031, r_list->regs[i].address, -+ ret = isx031_write_reg(isx031, r_list->regs[i].address, - ISX031_REG_LEN_08BIT, - r_list->regs[i].val); - if (ret) { -@@ -370,7 +353,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); - return ret; - } - -@@ -523,12 +506,13 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - if (isx031->streaming == enable) - return 0; - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); -- goto err_unlock; -+ mutex_unlock(&isx031->mutex); -+ return ret; - } - - ret = isx031_start_streaming(isx031); -@@ -544,8 +528,7 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - - isx031->streaming = enable; - --err_unlock: -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return ret; - } -@@ -570,11 +553,11 @@ static int __maybe_unused isx031_suspend(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - if (isx031->streaming) - isx031_stop_streaming(isx031); - -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -585,9 +568,7 @@ static int __maybe_unused isx031_resume(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - const struct isx031_reg_list *reg_list; -- int ret = 0; -- -- mutex_lock(isx031->mutex); -+ int ret; - - if (isx031->reset_gpio != NULL) - isx031_reset(isx031->reset_gpio); -@@ -598,26 +579,27 @@ static int __maybe_unused isx031_resume(struct device *dev) - ret = isx031_write_reg_list(isx031, reg_list); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); -- goto err_unlock; -+ return ret; - } - } else { - dev_err(&client->dev, "isx031 resume failed"); -- goto err_unlock; -+ return ret; - } - -+ mutex_lock(&isx031->mutex); - if (isx031->streaming) { - ret = isx031_start_streaming(isx031); - if (ret) { - isx031->streaming = false; - isx031_stop_streaming(isx031); -- goto err_unlock; -+ mutex_unlock(&isx031->mutex); -+ return ret; - } - } - --err_unlock: -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - -- return ret; -+ return 0; - } - - static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -@@ -656,7 +638,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - if (i >= ARRAY_SIZE(supported_modes)) - mode = &supported_modes[0]; - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -@@ -665,7 +647,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - isx031->cur_mode = mode; - } - -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -676,14 +658,14 @@ static int isx031_get_format(struct v4l2_subdev *sd, - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - isx031_update_pad_format(isx031->cur_mode, &fmt->format); - -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -692,10 +674,10 @@ static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - isx031_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -728,10 +710,12 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { - static void isx031_remove(struct i2c_client *client) - { - struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); - - v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); - - } - -@@ -792,7 +776,8 @@ static int isx031_probe(struct i2c_client *client) - if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - -- isx031->mutex = &isx031_mutex; -+ mutex_init(&isx031->mutex); -+ - /* 1920x1536 default */ - isx031->cur_mode = NULL; - isx031->pre_mode = &supported_modes[0]; -@@ -823,6 +808,7 @@ static int isx031_probe(struct i2c_client *client) - probe_error_media_entity_cleanup: - media_entity_cleanup(&isx031->sd.entity); - pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); - - return ret; - } -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -index ec275fe74e94..ab533b790f72 100644 ---- a/drivers/media/i2c/max9x/Makefile -+++ b/drivers/media/i2c/max9x/Makefile -@@ -7,4 +7,4 @@ ccflags-y += -I$(src)/../../pci/intel/ipu6/ - endif - - obj-m += max9x.o --max9x-y += regmap-retry.o serdes.o max9296.o max96724.o max9295.o max96717.o -+max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index a5bacc3684a9..cc1ee2f5ff92 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -30,7 +30,6 @@ - #include - - #include "max9295.h" --#include "regmap-retry.h" - - static const char *const max9295_gpio_chip_names[] = { - "MFP1", -@@ -262,14 +261,14 @@ static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, - struct device *dev = common->dev; - struct regmap *map = common->map; - int data_type_slot, dt; -- int ret = 0; -+ int ret; - - for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { - dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; - dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", - pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); - -- TRY(ret, regmap_update_bits_retry(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), - MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -@@ -511,35 +510,35 @@ static int max9295_enable_rclk(struct max9x_common *common) - dev_info(dev, "Enable RCLK: 27MHz"); - - // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_PREDEF_FREQ_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) - ); - - // Enable reference generation -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) - ); - - // Configure reference generation output on MFP4 -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, - MAX9295_PCLK_GPIO_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) - ); - - // Enable output -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, - MAX9295_PCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) - ); - -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REG3, -+ TRY(ret, regmap_update_bits(map, MAX9295_REG3, - MAX9295_RCLK_SEL_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) - ); - -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REG6, -+ TRY(ret, regmap_update_bits(map, MAX9295_REG6, - MAX9295_RCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) - ); -@@ -554,7 +553,7 @@ static int max9295_set_local_control_channel_enabled(struct max9x_common *common - - dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); - -- return regmap_update_bits_retry(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, - MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); - } - -@@ -576,8 +575,8 @@ static int max9295_verify_devid(struct max9x_common *common) - int ret; - - // Fetch and output chip name + revision -- TRY(ret, regmap_read_retry(map, MAX9295_DEV_ID, &dev_id)); -- TRY(ret, regmap_read_retry(map, MAX9295_DEV_REV, &dev_rev)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); - - switch (dev_id) { - case MAX9295A: -@@ -618,10 +617,10 @@ static int max9295_enable(struct max9x_common *common) - TRY(ret, max9295_set_local_control_channel_enabled(common, false)); - - /* Clear the pipe maps */ -- TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_9, 0)); -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); - - /* Clear the csi port selections */ -- TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); - - return 0; - } -@@ -686,8 +685,6 @@ static int max9295_remap_addr(struct max9x_common *common) - TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, - MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); - -- msleep(20); -- - return 0; - } - -@@ -720,18 +717,18 @@ static int max9295_add_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr || src == 0) { - dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", - MAX9295_I2C_SRC(i2c_id, alias), virt_addr, - MAX9295_I2C_DST(i2c_id, alias), phys_addr); -- TRY(ret, regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), -+ TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) - ); - -- TRY(ret, regmap_write_retry(map, MAX9295_I2C_SRC(i2c_id, alias), -+ TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) - ); - } -@@ -749,10 +746,10 @@ static int max9295_remove_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr) { -- return regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), -+ return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); - } - } -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -index acc6bb03405b..38c344669df4 100644 ---- a/drivers/media/i2c/max9x/max9296.h -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -49,7 +49,7 @@ enum max9296_link_mode { - #define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ - /* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ - --#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 350 -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 - - #define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 - -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index 37a29d2ec046..b214e1176963 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -30,7 +30,6 @@ - #include - - #include "max96724.h" --#include "regmap-retry.h" - - // Params - int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -@@ -338,7 +337,7 @@ static int max96724_set_all_reset(struct max9x_common *common, bool enable) - - dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); - -- return regmap_update_bits_retry(map, MAX96724_RESET_ALL, -+ return regmap_update_bits(map, MAX96724_RESET_ALL, - MAX96724_RESET_ALL_FIELD, - MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); - } -@@ -355,8 +354,8 @@ static int max96724_soft_reset(struct max9x_common *common) - return ret; - - /* Wait for hardware available after soft reset */ -- /* TODO: Optimize sleep time 20 ms */ -- msleep(20); -+ /* TODO: Optimize sleep time 45 ms */ -+ msleep(45); - - ret = max96724_set_all_reset(common, 0); - if (ret) -diff --git a/drivers/media/i2c/max9x/regmap-retry.c b/drivers/media/i2c/max9x/regmap-retry.c -deleted file mode 100644 -index 48e5ec4bcb4f..000000000000 ---- a/drivers/media/i2c/max9x/regmap-retry.c -+++ /dev/null -@@ -1,52 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013--2025 Intel Corporation -- */ -- --#include "regmap-retry.h" -- --int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val) --{ -- int ret = 0; -- int retry = 50; -- -- while (retry--) { -- ret = regmap_read(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- --int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val) --{ -- int ret = 0; -- int retry = 50; -- -- while (retry--) { -- ret = regmap_write(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- --int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -- unsigned int mask, unsigned int val) --{ -- int ret = 0; -- int retry = 50; -- -- while (retry--) { -- ret = regmap_update_bits(map, reg, mask, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -diff --git a/drivers/media/i2c/max9x/regmap-retry.h b/drivers/media/i2c/max9x/regmap-retry.h -deleted file mode 100644 -index 9e9ac63878a0..000000000000 ---- a/drivers/media/i2c/max9x/regmap-retry.h -+++ /dev/null -@@ -1,18 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013--2025 Intel Corporation -- */ -- --#ifndef _REGMAP_RETRY_H --#define _REGMAP_RETRY_H -- --#include -- --int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val); -- --int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val); -- --int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -- unsigned int mask, unsigned int val); -- --#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index fce930a1fdea..633c55504273 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -31,7 +31,6 @@ - #include - - #include "serdes.h" --#include "regmap-retry.h" - - static const s64 max9x_op_sys_clock[] = { - MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -@@ -398,6 +397,22 @@ static void *parse_serdes_pdata(struct device *dev) - return des_pdata; - } - -+static int regmap_read_retry(struct regmap *map, unsigned int reg, -+ unsigned int *val) -+{ -+ int ret = 0; -+ int i = 0; -+ -+ for (i = 0; i < 50; i++) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ - static int max9x_enable_resume(struct max9x_common *common) - { - struct device *dev = common->dev; -@@ -442,7 +457,6 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - unsigned int phys_addr, virt_addr; - struct i2c_client *phys_client; - struct regmap *phys_map, *virt_map; -- struct device *dev = &serial_link->remote.client->dev; - unsigned int val; - const struct regmap_config regmap_config = { - .reg_bits = 16, -@@ -452,23 +466,27 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - if (!serial_link->remote.pdata) - return 0; - -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; - if (phys_addr == virt_addr) - return 0; - -- dev_info(dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); - - phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); - if (IS_ERR_OR_NULL(phys_client)) { -- dev_err(dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_client); - return ret; - } - - phys_map = regmap_init_i2c(phys_client, ®map_config); - if (IS_ERR_OR_NULL(phys_map)) { -- dev_err(dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_map); - goto err_client; - } -@@ -476,36 +494,36 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); - - virt_map = ser_common->map; -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { -- dev_info(dev, "Remap not necessary"); -+ dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_regmap; - } - - ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(dev, "Device not present at 0x%02x", phys_addr); -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_regmap; - } else { -- dev_info(dev, "DEV_ID before: 0x%02x", val); -+ dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { -- dev_err(dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); - goto err_regmap; - } - - msleep(100); - -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(dev, "Device not present after remap to 0x%02x", virt_addr); -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_regmap; - } else { -- dev_info(dev, "DEV_ID after: 0x%02x", val); -+ dev_info(common->dev, "DEV_ID after: 0x%02x", val); - } - - err_regmap: -@@ -513,6 +531,8 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - err_client: - i2c_unregister_device(phys_client); - -+ max9x_des_deisolate_serial_link(common, link_id); -+ - return ret; - } - -@@ -562,31 +582,42 @@ int max9x_common_resume(struct max9x_common *common) - struct max9x_common *des_common = NULL; - struct device *dev = common->dev; - u32 des_link; -+ u32 phys_addr, virt_addr; - int ret = 0; -+ int retry = 50; - -- if (dev->platform_data && common->type == MAX9X_SERIALIZER) { -+ if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -- WARN_ON(pdata->num_serial_links < 1); -- -- des_common = max9x_client_to_common( -- pdata->serial_links[0].des_client); -- if (des_common) { -- des_link = pdata->serial_links[0].des_link_id; -- ret = max9x_des_isolate_serial_link(des_common, des_link); -- if (ret) -- goto err_deisolate; -- ret = max9x_remap_serializers_resume(des_common, des_link); -- if (ret) -- goto err_deisolate; -- } else { -- return ret; -+ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ return ret; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_reset_serializer; -+ } - } - } - -- ret = max9x_verify_devid(common); -+ while (retry--) { -+ ret = max9x_verify_devid(common); -+ if (ret) -+ msleep(100); -+ else -+ break; -+ } - if (ret) { - dev_err(dev, "can't get devid after resume"); -- goto err_reset_serializer; -+ goto err_deisolate; - } - - ret = max9x_enable_resume(common); -@@ -610,6 +641,11 @@ int max9x_common_resume(struct max9x_common *common) - err_disable: - max9x_disable(common); - -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ - err_reset_serializer: - if (common->type == MAX9X_SERIALIZER) { - if (common->common_ops && common->common_ops->remap_reset) { -@@ -619,11 +655,6 @@ int max9x_common_resume(struct max9x_common *common) - } - } - --err_deisolate: -- if (common->type == MAX9X_SERIALIZER && des_common) { -- max9x_des_deisolate_serial_link(des_common, des_link); -- } -- - return ret; - } - -@@ -633,12 +664,26 @@ int max9x_common_suspend(struct max9x_common *common) - - dev_dbg(common->dev, "try to suspend"); - -+ max9x_disable_translations(common); - - for (link_id = 0; link_id < common->num_serial_links; link_id++) - max9x_disable_serial_link(common, link_id); - - max9x_disable(common); - -+ if (common->type == MAX9X_SERIALIZER) { -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (dev->platform_data) { -+ if (common->common_ops && common->common_ops->remap_reset) { -+ ret = common->common_ops->remap_reset(common); -+ if (ret) -+ return ret; -+ } -+ } -+ } -+ - return 0; - } - -@@ -1054,7 +1099,7 @@ int max9x_verify_devid(struct max9x_common *common) - * Fetch and output chip name + revision - * try both virtual address and physical address - */ -- ret = regmap_read_retry(map, MAX9X_DEV_ID, &dev_id); -+ ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); - if (ret) { - dev_warn(dev, "Failed to read chip ID from virtual address"); - if (phys_map) { -@@ -1074,7 +1119,7 @@ int max9x_verify_devid(struct max9x_common *common) - } - common->des = &max9x_chips[chip_type]; - common->type = common->des->serdes_type; -- TRY(ret, regmap_read_retry(map, common->des->rev_reg, &dev_rev)); -+ TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); - dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); - - dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -@@ -1131,14 +1176,14 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - if (IS_ERR_OR_NULL(virt_map)) - goto err_virt_client; - -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { - dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_virt_regmap; - } - -- ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_virt_regmap; -@@ -1146,7 +1191,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { - dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); -@@ -1155,7 +1200,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - usleep_range(1000, 1050); - -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_virt_regmap; -@@ -1173,6 +1218,10 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - err_client: - i2c_unregister_device(phys_client); - -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ - return ret; - } - -@@ -1737,7 +1786,6 @@ static int max9x_registered(struct v4l2_subdev *sd) - if (subdev_pdata) { - struct max9x_pdata *ser_pdata = - subdev_pdata->board_info.platform_data; -- struct v4l2_subdev *subdev = NULL; - - WARN_ON(ser_pdata->num_serial_links < 1); - -@@ -1749,13 +1797,13 @@ static int max9x_registered(struct v4l2_subdev *sd) - * physical i2c at the same time - */ - ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; - -- if (!ret) -- subdev = v4l2_i2c_new_subdev_board( -- sd->v4l2_dev, -- common->muxc->adapter[link_id], -- &subdev_pdata->board_info, -- NULL); -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); - - ret = max9x_des_deisolate_serial_link(common, link_id); - if (ret) -@@ -2585,6 +2633,8 @@ int max9x_setup_translations(struct max9x_common *common) - break; - } - -+ msleep(10); -+ - return err; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet b/SPECS/kernel-rt/0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet new file mode 100644 index 000000000..c28246970 --- /dev/null +++ b/SPECS/kernel-rt/0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet @@ -0,0 +1,85 @@ +From 413ce7c231c970971e284e16f9cef2c0e49e04aa Mon Sep 17 00:00:00 2001 +From: Vinicius Costa Gomes +Date: Wed, 19 Mar 2025 11:17:06 +0500 +Subject: [PATCH 2/4] af_packet: Fix wrong timestamps in tcpdump + +Since commit '97dc7cd ("ptp: Support late timestamp +determination")', timestamps may need to be retrieved with input from +the driver (.ndo_get_tstamp()). Support was missing from mmap'ed +AF_PACKET sockets. + +The problem was noticed using the igc driver, that since commit +'069b142 ("igc: Add support for PTP .getcyclesx64()")' has migrated +to use .ndo_get_tstamp() that timestamps were wrong on the receiving +side, when using tcpdump. + +Signed-off-by: Vinicius Costa Gomes +--- + net/packet/af_packet.c | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 173e6edda08f8..6904a5a810a80 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -450,15 +450,20 @@ static int __packet_get_status(const struct packet_sock *po, void *frame) + } + } + +-static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, +- unsigned int flags) ++static __u32 tpacket_get_timestamp(struct net_device *dev, struct sk_buff *skb, ++ struct timespec64 *ts, unsigned int flags, ++ unsigned int sk_tsflags) + { + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + +- if (shhwtstamps && +- (flags & SOF_TIMESTAMPING_RAW_HARDWARE) && +- ktime_to_timespec64_cond(shhwtstamps->hwtstamp, ts)) +- return TP_STATUS_TS_RAW_HARDWARE; ++ bool cycles = sk_tsflags & SOF_TIMESTAMPING_BIND_PHC; ++ ++ if (shhwtstamps && shhwtstamps->hwtstamp && ++ (flags & SOF_TIMESTAMPING_RAW_HARDWARE)) { ++ ktime_t tstamp = netdev_get_tstamp(dev, shhwtstamps, cycles); ++ ++ return ktime_to_timespec64_cond(tstamp, ts) ? TP_STATUS_TS_RAW_HARDWARE : 0; ++ } + + if ((flags & SOF_TIMESTAMPING_SOFTWARE) && + ktime_to_timespec64_cond(skb_tstamp(skb), ts)) +@@ -470,11 +475,16 @@ static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, + static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame, + struct sk_buff *skb) + { ++ struct net_device *dev = skb->dev; ++ unsigned int sk_tsflags; + union tpacket_uhdr h; + struct timespec64 ts; + __u32 ts_status; + +- if (!(ts_status = tpacket_get_timestamp(skb, &ts, READ_ONCE(po->tp_tstamp)))) ++ sk_tsflags = READ_ONCE(po->sk.sk_tsflags); ++ ts_status = tpacket_get_timestamp(dev, skb, &ts, READ_ONCE(po->tp_tstamp), sk_tsflags); ++ ++ if (!(ts_status)) + return 0; + + h.raw = frame; +@@ -2392,9 +2402,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + /* Always timestamp; prefer an existing software timestamp taken + * closer to the time of capture. + */ +- ts_status = tpacket_get_timestamp(skb, &ts, ++ ts_status = tpacket_get_timestamp(dev, skb, &ts, + READ_ONCE(po->tp_tstamp) | +- SOF_TIMESTAMPING_SOFTWARE); ++ SOF_TIMESTAMPING_SOFTWARE, ++ READ_ONCE(sk->sk_tsflags)); + if (!ts_status) + ktime_get_real_ts64(&ts); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-bpf-add-btf-register-unregister-API.ethernet b/SPECS/kernel-rt/0002-bpf-add-btf-register-unregister-API.ethernet new file mode 100644 index 000000000..4073d0468 --- /dev/null +++ b/SPECS/kernel-rt/0002-bpf-add-btf-register-unregister-API.ethernet @@ -0,0 +1,153 @@ +From 1f6f920dfedac24ab1ab03977b223d461fbda737 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Mon, 14 Jun 2021 09:03:35 +0800 +Subject: [PATCH 02/14] bpf: add btf register/unregister API + +A device driver can register own BTF format buffers into the kernel. +Will be used in downstream patches by mlx5 XDP driver to advertise the +types and populated XDP meta data. + +Signed-off-by: Saeed Mahameed +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + include/linux/btf.h | 8 ++++ + kernel/bpf/btf.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 98 insertions(+) + +diff --git a/include/linux/btf.h b/include/linux/btf.h +index f06976ffb63f9..96d6d07ea5a91 100644 +--- a/include/linux/btf.h ++++ b/include/linux/btf.h +@@ -604,6 +604,8 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type + + return btf_type_is_struct(t); + } ++struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size); ++void btf_unregister(struct btf *btf); + #else + static inline const struct btf_type *btf_type_by_id(const struct btf *btf, + u32 type_id) +@@ -682,5 +684,11 @@ static inline int btf_check_iter_arg(struct btf *btf, const struct btf_type *fun + { + return -EOPNOTSUPP; + } ++static inline struct btf * ++btf_register(const char *name, void *btf_data, u32 btf_data_size) ++{ ++ return NULL; ++} ++static inline void btf_unregister(struct btf *btf) { } + #endif + #endif +diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c +index 0de8fc8a0e0b3..2abbd84dc7afe 100644 +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -6380,6 +6380,95 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, + + #endif /* CONFIG_DEBUG_INFO_BTF_MODULES */ + ++/* TODO: reuse btf_parse_raw in btf_parse_module */ ++static struct btf *btf_parse_raw(const char *name, const void *data, unsigned int data_size) ++{ ++ struct btf_verifier_env *env = NULL; ++ struct bpf_verifier_log *log; ++ struct btf *btf = NULL; ++ int err; ++ ++ env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN); ++ if (!env) ++ return ERR_PTR(-ENOMEM); ++ ++ log = &env->log; ++ log->level = BPF_LOG_KERNEL; ++ ++ btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN); ++ if (!btf) { ++ err = -ENOMEM; ++ goto errout; ++ } ++ env->btf = btf; ++ btf->kernel_btf = true; ++ snprintf(btf->name, sizeof(btf->name), "%s", name); ++ ++ btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN); ++ if (!btf->data) { ++ err = -ENOMEM; ++ goto errout; ++ } ++ memcpy(btf->data, data, data_size); ++ btf->data_size = data_size; ++ ++ err = btf_parse_hdr(env); ++ if (err) ++ goto errout; ++ ++ btf->nohdr_data = btf->data + btf->hdr.hdr_len; ++ ++ err = btf_parse_str_sec(env); ++ if (err) ++ goto errout; ++ ++ err = btf_check_all_metas(env); ++ if (err) ++ goto errout; ++ ++ btf_verifier_env_free(env); ++ refcount_set(&btf->refcnt, 1); ++ return btf; ++ ++errout: ++ btf_verifier_env_free(env); ++ if (btf) { ++ kvfree(btf->data); ++ kvfree(btf->types); ++ kfree(btf); ++ } ++ return ERR_PTR(err); ++} ++ ++/* TODO: reuse btf_register in btf_module_notify */ ++struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size) ++{ ++ struct btf *btf; ++ int err; ++ ++ btf = btf_parse_raw(name, btf_data, btf_data_size); ++ if (IS_ERR(btf)) ++ return btf; ++ ++ err = btf_alloc_id(btf); ++ if (err) { ++ btf_free(btf); ++ btf = ERR_PTR(err); ++ } ++ return btf; ++} ++EXPORT_SYMBOL(btf_register); ++ ++void btf_unregister(struct btf *btf) ++{ ++ if (IS_ERR(btf)) ++ return; ++ ++ /* btf_put since btf might be held by user */ ++ btf_put(btf); ++} ++EXPORT_SYMBOL(btf_unregister); ++ + struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog) + { + struct bpf_prog *tgt_prog = prog->aux->dst_prog; +@@ -8142,6 +8231,7 @@ u32 btf_obj_id(const struct btf *btf) + { + return btf->id; + } ++EXPORT_SYMBOL(btf_obj_id); + + bool btf_is_kernel(const struct btf *btf) + { +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt b/SPECS/kernel-rt/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt index a8159f81b..e02ab65e1 100644 --- a/SPECS/kernel-rt/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt +++ b/SPECS/kernel-rt/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt @@ -1,4 +1,4 @@ -From 85467ee02ac6e068c96101ee3fbe7292a743ca83 Mon Sep 17 00:00:00 2001 +From cfb6ba1919984dd554322c67c8684f9d824cf7d3 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Sat, 27 Feb 2016 09:01:42 +0100 Subject: [PATCH 2/9] drm/i915: Don't disable interrupts on PREEMPT_RT during @@ -38,7 +38,7 @@ Signed-off-by: Sebastian Andrzej Siewior 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c -index a187db6df2d3..dbdc4d9b2a33 100644 +index a187db6df2d36..dbdc4d9b2a33c 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -562,7 +562,8 @@ void intel_pipe_update_start(struct intel_atomic_state *state, @@ -72,10 +72,10 @@ index a187db6df2d3..dbdc4d9b2a33 100644 if (intel_vgpu_active(dev_priv)) goto out; diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c -index 198e69efe9ac..2d86b2c3f863 100644 +index d4d181f9dca5f..58f2f074aff20 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c -@@ -929,13 +929,15 @@ intel_legacy_cursor_update(struct drm_plane *_plane, +@@ -919,13 +919,15 @@ intel_legacy_cursor_update(struct drm_plane *_plane, */ intel_psr_wait_for_idle_locked(crtc_state); @@ -93,7 +93,7 @@ index 198e69efe9ac..2d86b2c3f863 100644 } if (new_plane_state->uapi.visible) { -@@ -945,7 +947,8 @@ intel_legacy_cursor_update(struct drm_plane *_plane, +@@ -935,7 +937,8 @@ intel_legacy_cursor_update(struct drm_plane *_plane, intel_plane_disable_arm(NULL, plane, crtc_state); } @@ -104,10 +104,10 @@ index 198e69efe9ac..2d86b2c3f863 100644 intel_psr_unlock(crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c -index b82e9fc51af0..d0197fd96d6c 100644 +index d0d9e2c7d5476..590a079f926b2 100644 --- a/drivers/gpu/drm/i915/display/intel_vblank.c +++ b/drivers/gpu/drm/i915/display/intel_vblank.c -@@ -753,11 +753,13 @@ int intel_vblank_evade(struct intel_vblank_evade_ctx *evade) +@@ -761,11 +761,13 @@ int intel_vblank_evade(struct intel_vblank_evade_ctx *evade) break; } diff --git a/SPECS/kernel-rt/0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt b/SPECS/kernel-rt/0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt deleted file mode 100644 index 29935b51c..000000000 --- a/SPECS/kernel-rt/0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt +++ /dev/null @@ -1,51 +0,0 @@ -From d993b6ff2332536674c05fbbae6d78d6ae35ec3f Mon Sep 17 00:00:00 2001 -From: Junxiao Chang -Date: Fri, 7 Nov 2025 11:31:52 +0800 -Subject: [PATCH 2/2] drm/me/gsc: mei interrupt top half should be in irq - disabled context - -MEI GSC interrupt comes from i915 or xe driver. It has top half and -bottom half. Top half is called from i915/xe interrupt handler. It -should be in irq disabled context. - -With RT kernel(PREEMPT_RT enabled), by default IRQ handler is in -threaded IRQ. MEI GSC top half might be in threaded IRQ context. -generic_handle_irq_safe API could be called from either IRQ or -process context, it disables local IRQ then calls MEI GSC interrupt -top half. - -This change fixes B580 GPU boot issue with RT enabled. - -Fixes: e02cea83d32d ("drm/xe/gsc: add Battlemage support") -Tested-by: Baoli Zhang -Signed-off-by: Junxiao Chang -Reviewed-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/xe/xe_heci_gsc.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/xe/xe_heci_gsc.c b/drivers/gpu/drm/xe/xe_heci_gsc.c -index 6d7b62724126..bd5afa675679 100644 ---- a/drivers/gpu/drm/xe/xe_heci_gsc.c -+++ b/drivers/gpu/drm/xe/xe_heci_gsc.c -@@ -221,7 +221,7 @@ void xe_heci_gsc_irq_handler(struct xe_device *xe, u32 iir) - if (xe->heci_gsc.irq < 0) - return; - -- ret = generic_handle_irq(xe->heci_gsc.irq); -+ ret = generic_handle_irq_safe(xe->heci_gsc.irq); - if (ret) - drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret); - } -@@ -241,7 +241,7 @@ void xe_heci_csc_irq_handler(struct xe_device *xe, u32 iir) - if (xe->heci_gsc.irq < 0) - return; - -- ret = generic_handle_irq(xe->heci_gsc.irq); -+ ret = generic_handle_irq_safe(xe->heci_gsc.irq); - if (ret) - drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm b/SPECS/kernel-rt/0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm new file mode 100644 index 000000000..6591aec3d --- /dev/null +++ b/SPECS/kernel-rt/0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm @@ -0,0 +1,155 @@ +From 48372bf2a639efaabe83a9b940ec4e315800391c Mon Sep 17 00:00:00 2001 +From: Dongwon Kim +Date: Tue, 28 Jun 2022 11:23:10 -0700 +Subject: [PATCH 2/3] drm/virtio: freeze and restore hooks to support suspend + and resume + +virtio device needs to delete before VM suspend happens +then reinitialize all virtqueues again upon resume + +Signed-off-by: Dongwon Kim +--- + drivers/gpu/drm/virtio/virtgpu_drv.c | 53 +++++++++++++++++++++++++++- + drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + + drivers/gpu/drm/virtio/virtgpu_kms.c | 25 +++++++++---- + 3 files changed, 72 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c +index 71c6ccad4b99b..905246316dc9e 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.c ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.c +@@ -163,6 +163,53 @@ static unsigned int features[] = { + VIRTIO_GPU_F_RESOURCE_BLOB, + VIRTIO_GPU_F_CONTEXT_INIT, + }; ++ ++#ifdef CONFIG_PM_SLEEP ++static int virtgpu_freeze(struct virtio_device *vdev) ++{ ++ struct drm_device *dev = vdev->priv; ++ struct virtio_gpu_device *vgdev = dev->dev_private; ++ int error; ++ ++ error = drm_mode_config_helper_suspend(dev); ++ if (error) { ++ DRM_ERROR("suspend error %d\n", error); ++ return error; ++ } ++ ++ flush_work(&vgdev->obj_free_work); ++ flush_work(&vgdev->ctrlq.dequeue_work); ++ flush_work(&vgdev->cursorq.dequeue_work); ++ flush_work(&vgdev->config_changed_work); ++ vdev->config->del_vqs(vdev); ++ ++ return 0; ++} ++ ++static int virtgpu_restore(struct virtio_device *vdev) ++{ ++ struct drm_device *dev = vdev->priv; ++ struct virtio_gpu_device *vgdev = dev->dev_private; ++ int error; ++ ++ error = virtio_gpu_find_vqs(vgdev); ++ if (error) { ++ DRM_ERROR("failed to find virt queues\n"); ++ return error; ++ } ++ ++ virtio_device_ready(vdev); ++ ++ error = drm_mode_config_helper_resume(dev); ++ if (error) { ++ DRM_ERROR("resume error %d\n", error); ++ return error; ++ } ++ ++ return 0; ++} ++#endif ++ + static struct virtio_driver virtio_gpu_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), +@@ -171,7 +218,11 @@ static struct virtio_driver virtio_gpu_driver = { + .probe = virtio_gpu_probe, + .remove = virtio_gpu_remove, + .shutdown = virtio_gpu_shutdown, +- .config_changed = virtio_gpu_config_changed ++ .config_changed = virtio_gpu_config_changed, ++#ifdef CONFIG_PM_SLEEP ++ .freeze = virtgpu_freeze, ++ .restore = virtgpu_restore, ++#endif + }; + + static int __init virtio_gpu_driver_init(void) +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h +index f17660a71a3e7..1279f998c8e0d 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.h ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.h +@@ -300,6 +300,7 @@ void virtio_gpu_deinit(struct drm_device *dev); + void virtio_gpu_release(struct drm_device *dev); + int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); + void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); ++int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev); + + /* virtgpu_gem.c */ + int virtio_gpu_gem_object_open(struct drm_gem_object *obj, +diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c +index 1c15cbf326b78..d16be811a2bdc 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_kms.c ++++ b/drivers/gpu/drm/virtio/virtgpu_kms.c +@@ -114,15 +114,29 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, + vgdev->num_capsets = num_capsets; + } + +-int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) ++int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev) + { + struct virtqueue_info vqs_info[] = { + { "control", virtio_gpu_ctrl_ack }, + { "cursor", virtio_gpu_cursor_ack }, + }; +- struct virtio_gpu_device *vgdev; +- /* this will expand later */ ++ + struct virtqueue *vqs[2]; ++ int ret; ++ ++ ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); ++ if (ret) ++ return ret; ++ ++ vgdev->ctrlq.vq = vqs[0]; ++ vgdev->cursorq.vq = vqs[1]; ++ ++ return 0; ++} ++ ++int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) ++{ ++ struct virtio_gpu_device *vgdev; + u32 num_scanouts, num_capsets; + int ret = 0; + +@@ -206,13 +220,12 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) + DRM_INFO("features: %ccontext_init\n", + vgdev->has_context_init ? '+' : '-'); + +- ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); ++ ret = virtio_gpu_find_vqs(vgdev); + if (ret) { + DRM_ERROR("failed to find virt queues\n"); + goto err_vqs; + } +- vgdev->ctrlq.vq = vqs[0]; +- vgdev->cursorq.vq = vqs[1]; ++ + ret = virtio_gpu_alloc_vbufs(vgdev); + if (ret) { + DRM_ERROR("failed to alloc vbufs\n"); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm b/SPECS/kernel-rt/0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm deleted file mode 100644 index f632331d0..000000000 --- a/SPECS/kernel-rt/0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm +++ /dev/null @@ -1,187 +0,0 @@ -From 0f5cf73f36b21910dc66c741721326225a610494 Mon Sep 17 00:00:00 2001 -From: Dongwon Kim -Date: Thu, 18 Aug 2022 16:19:33 -0700 -Subject: [PATCH 2/2] drm/virtio: save and restore virtio_gpu_objects - -Host KVM/QEMU loses all graphic resourses submitted by guest OS upon -resumption from sleep or hibernation. This will cause invalid resource -errors when the guest OS makes any request to the host regarding those -resources. One way to prevent this problem is to let virtio-gpu driver -resubmit all existing resources upon resumption. A linked-list for -keeping references of all created virtio_gpu_object and its params is -added for this save and restore mechanism. Virtio-gpu objects are added -to the list whenever a new object is created and sent to the host. -All backed-up objects will then be re-sent to the host in .resume -function with 'create resource' virtio gpu command. - -Signed-off-by: Dongwon Kim ---- - drivers/gpu/drm/virtio/virtgpu_drv.c | 6 +++ - drivers/gpu/drm/virtio/virtgpu_drv.h | 10 ++++ - drivers/gpu/drm/virtio/virtgpu_kms.c | 1 + - drivers/gpu/drm/virtio/virtgpu_object.c | 65 +++++++++++++++++++++++++ - 4 files changed, 82 insertions(+) - -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c -index 905246316dc9..8f333ed92836 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.c -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c -@@ -200,6 +200,12 @@ static int virtgpu_restore(struct virtio_device *vdev) - - virtio_device_ready(vdev); - -+ error = virtio_gpu_object_restore_all(vgdev); -+ if (error) { -+ DRM_ERROR("Failed to recover objects\n"); -+ return error; -+ } -+ - error = drm_mode_config_helper_resume(dev); - if (error) { - DRM_ERROR("resume error %d\n", error); -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h -index 1279f998c8e0..189e2282af19 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.h -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h -@@ -126,6 +126,12 @@ struct virtio_gpu_object_array { - struct drm_gem_object *objs[] __counted_by(total); - }; - -+struct virtio_gpu_object_restore { -+ struct virtio_gpu_object *bo; -+ struct virtio_gpu_object_params params; -+ struct list_head node; -+}; -+ - struct virtio_gpu_vbuffer; - struct virtio_gpu_device; - -@@ -265,6 +271,7 @@ struct virtio_gpu_device { - struct work_struct obj_free_work; - spinlock_t obj_free_lock; - struct list_head obj_free_list; -+ struct list_head obj_rec; - - struct virtio_gpu_drv_capset *capsets; - uint32_t num_capsets; -@@ -479,6 +486,9 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo); - - int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, - uint32_t *resid); -+ -+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev); -+ - /* virtgpu_prime.c */ - int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, - struct virtio_gpu_object *bo); -diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c -index 46963f050b88..e087aeeef074 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_kms.c -+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c -@@ -163,6 +163,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) - vgdev->fence_drv.context = dma_fence_context_alloc(1); - spin_lock_init(&vgdev->fence_drv.lock); - INIT_LIST_HEAD(&vgdev->fence_drv.fences); -+ INIT_LIST_HEAD(&vgdev->obj_rec); - INIT_LIST_HEAD(&vgdev->cap_cache); - INIT_WORK(&vgdev->config_changed_work, - virtio_gpu_config_changed_work_func); -diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c -index 5517cff8715c..4e2dbd96bc16 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_object.c -+++ b/drivers/gpu/drm/virtio/virtgpu_object.c -@@ -61,6 +61,38 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t - } - } - -+static void virtio_gpu_object_save_restore_list(struct virtio_gpu_device *vgdev, -+ struct virtio_gpu_object *bo, -+ struct virtio_gpu_object_params *params) -+{ -+ struct virtio_gpu_object_restore *new; -+ -+ new = kvmalloc(sizeof(*new), GFP_KERNEL); -+ if (!new) { -+ DRM_ERROR("Fail to allocate virtio_gpu_object_restore"); -+ return; -+ } -+ -+ new->bo = bo; -+ memcpy(&new->params, params, sizeof(*params)); -+ -+ list_add_tail(&new->node, &vgdev->obj_rec); -+} -+ -+static void virtio_gpu_object_del_restore_list(struct virtio_gpu_device *vgdev, -+ struct virtio_gpu_object *bo) -+{ -+ struct virtio_gpu_object_restore *curr, *tmp; -+ -+ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { -+ if (bo == curr->bo) { -+ list_del(&curr->node); -+ kvfree(curr); -+ break; -+ } -+ } -+} -+ - void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) - { - struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; -@@ -84,6 +116,7 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) - drm_gem_object_release(&bo->base.base); - kfree(bo); - } -+ virtio_gpu_object_del_restore_list(vgdev, bo); - } - - static void virtio_gpu_free_object(struct drm_gem_object *obj) -@@ -257,8 +290,11 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, - objs, fence); - virtio_gpu_object_attach(vgdev, bo, ents, nents); - } -+ /* add submitted object to restore list */ -+ virtio_gpu_object_save_restore_list(vgdev, bo, params); - - *bo_ptr = bo; -+ - return 0; - - err_put_objs: -@@ -271,3 +307,32 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, - drm_gem_shmem_free(shmem_obj); - return ret; - } -+ -+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev) -+{ -+ struct virtio_gpu_object_restore *curr, *tmp; -+ struct virtio_gpu_mem_entry *ents; -+ unsigned int nents; -+ int ret; -+ -+ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { -+ ret = virtio_gpu_object_shmem_init(vgdev, curr->bo, &ents, &nents); -+ if (ret) -+ break; -+ -+ if (curr->params.blob) { -+ virtio_gpu_cmd_resource_create_blob(vgdev, curr->bo, &curr->params, -+ ents, nents); -+ } else if (curr->params.virgl) { -+ virtio_gpu_cmd_resource_create_3d(vgdev, curr->bo, &curr->params, -+ NULL, NULL); -+ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); -+ } else { -+ virtio_gpu_cmd_create_resource(vgdev, curr->bo, &curr->params, -+ NULL, NULL); -+ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); -+ } -+ } -+ -+ return ret; -+} --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu b/SPECS/kernel-rt/0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu deleted file mode 100644 index d6ce738f8..000000000 --- a/SPECS/kernel-rt/0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu +++ /dev/null @@ -1,76 +0,0 @@ -From f5ccdd6257bea670f612e8bdabe5052d698dd031 Mon Sep 17 00:00:00 2001 -From: Khai Wen Ng -Date: Thu, 11 Sep 2025 08:38:49 +0800 -Subject: [PATCH 2/6] i2c: i2c-core-acpi: clear dependency for MUX or ATR - adapters - -In ACPI table, devices that should residing on parent MUX or ATR -adapters have dependencies on parent devices. Hence, clear the -dependencies if the adapter created is based by parent through -ATR or MUX. ACPI driver can then call register client for the -devices - -Signed-off-by: Khai Wen Ng -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/i2c-core-acpi.c | 32 +++++++++++++++++++++++++++++++- - 1 file changed, 31 insertions(+), 1 deletion(-) - -diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c -index ed90858a27b7..2f2e26a92937 100644 ---- a/drivers/i2c/i2c-core-acpi.c -+++ b/drivers/i2c/i2c-core-acpi.c -@@ -285,11 +285,40 @@ static void i2c_acpi_register_device(struct i2c_adapter *adapter, - if (acpi_quirk_skip_i2c_client_enumeration(adev)) - return; - -+ /* Check if Device is on ATR or MUX adapter */ -+ if (adapter->is_atr || adapter->is_mux) { -+ u32 channel; -+ -+ if (fwnode_property_present(&adev->fwnode, "channel")) { -+ if (fwnode_property_read_u32(&adev->fwnode, "channel", &channel)) { -+ dev_err(&adev->dev, "failed to read channel property\n"); -+ return; -+ } -+ -+ if (adapter->chan_id != channel) { -+ dev_err(&adev->dev, "device is not on current adapter %d\n", -+ adapter->chan_id); -+ goto err; -+ } else { -+ acpi_dev_clear_dependencies(ACPI_COMPANION(adapter->dev.parent)); -+ } -+ -+ if (adev->dep_unmet) { -+ dev_err(&adev->dev, "device has unmet dependencies\n"); -+ return; -+ } -+ } else { -+ dev_err(&adev->dev, "channel property not present\n"); -+ return; -+ } -+ } -+ - adev->power.flags.ignore_parent = true; - acpi_device_set_enumerated(adev); - - if (IS_ERR(i2c_new_client_device(adapter, info))) - adev->power.flags.ignore_parent = false; -+err: - } - - static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, -@@ -323,7 +352,8 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap) - acpi_status status; - - if (!has_acpi_companion(&adap->dev)) -- return; -+ if (!adap->dev.fwnode) -+ return; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - I2C_ACPI_MAX_SCAN_DEPTH, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c b/SPECS/kernel-rt/0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c deleted file mode 100644 index f084b1dfa..000000000 --- a/SPECS/kernel-rt/0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c +++ /dev/null @@ -1,170 +0,0 @@ -From 5bdce02f216d9519ec9615248ff5363811fbac2c Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:27 +0300 -Subject: [PATCH 02/11] i3c: master: Add helpers for DMA mapping and bounce - buffer handling - -Some I3C controllers such as MIPI I3C HCI may pad the last DWORD (32-bit) -with stale data from the RX FIFO in DMA transfers if the receive length -is not DWORD aligned and when the device DMA is IOMMU mapped. - -In such a case, a properly sized bounce buffer is required in order to -avoid possible data corruption. In a review discussion, proposal was to -have a common helpers in I3C core for DMA mapping and bounce buffer -handling. - -Drivers may use the helper i3c_master_dma_map_single() to map a buffer -for a DMA transfer. It internally allocates a bounce buffer if buffer is -not DMA'able or when the driver requires it for a transfer. - -Helper i3c_master_dma_unmap_single() does the needed cleanups and -data copying from the bounce buffer. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-2-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master.c | 74 ++++++++++++++++++++++++++++++++++++++ - include/linux/i3c/master.h | 26 ++++++++++++++ - 2 files changed, 100 insertions(+) - -diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c -index 2ef898a8fd80..033d06cabca2 100644 ---- a/drivers/i3c/master.c -+++ b/drivers/i3c/master.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1727,6 +1728,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master) - } - EXPORT_SYMBOL_GPL(i3c_master_do_daa); - -+/** -+ * i3c_master_dma_map_single() - Map buffer for single DMA transfer -+ * @dev: device object of a device doing DMA -+ * @buf: destination/source buffer for DMA -+ * @len: length of transfer -+ * @force_bounce: true, force to use a bounce buffer, -+ * false, function will auto check is a bounce buffer required -+ * @dir: DMA direction -+ * -+ * Map buffer for a DMA transfer and allocate a bounce buffer if required. -+ * -+ * Return: I3C DMA transfer descriptor or NULL in case of error. -+ */ -+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf, -+ size_t len, bool force_bounce, enum dma_data_direction dir) -+{ -+ struct i3c_dma *dma_xfer __free(kfree) = NULL; -+ void *bounce __free(kfree) = NULL; -+ void *dma_buf = buf; -+ -+ dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL); -+ if (!dma_xfer) -+ return NULL; -+ -+ dma_xfer->dev = dev; -+ dma_xfer->buf = buf; -+ dma_xfer->dir = dir; -+ dma_xfer->len = len; -+ dma_xfer->map_len = len; -+ -+ if (is_vmalloc_addr(buf)) -+ force_bounce = true; -+ -+ if (force_bounce) { -+ dma_xfer->map_len = ALIGN(len, cache_line_size()); -+ if (dir == DMA_FROM_DEVICE) -+ bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL); -+ else -+ bounce = kmemdup(buf, dma_xfer->map_len, GFP_KERNEL); -+ if (!bounce) -+ return NULL; -+ dma_buf = bounce; -+ } -+ -+ dma_xfer->addr = dma_map_single(dev, dma_buf, dma_xfer->map_len, dir); -+ if (dma_mapping_error(dev, dma_xfer->addr)) -+ return NULL; -+ -+ dma_xfer->bounce_buf = no_free_ptr(bounce); -+ return no_free_ptr(dma_xfer); -+} -+EXPORT_SYMBOL_GPL(i3c_master_dma_map_single); -+ -+/** -+ * i3c_master_dma_unmap_single() - Unmap buffer after DMA -+ * @dma_xfer: DMA transfer and mapping descriptor -+ * -+ * Unmap buffer and cleanup DMA transfer descriptor. -+ */ -+void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer) -+{ -+ dma_unmap_single(dma_xfer->dev, dma_xfer->addr, -+ dma_xfer->map_len, dma_xfer->dir); -+ if (dma_xfer->bounce_buf) { -+ if (dma_xfer->dir == DMA_FROM_DEVICE) -+ memcpy(dma_xfer->buf, dma_xfer->bounce_buf, -+ dma_xfer->len); -+ kfree(dma_xfer->bounce_buf); -+ } -+ kfree(dma_xfer); -+} -+EXPORT_SYMBOL_GPL(i3c_master_dma_unmap_single); -+ - /** - * i3c_master_set_info() - set master device information - * @master: master used to send frames on the bus -diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h -index 043f5c7ff398..c52a82dd79a6 100644 ---- a/include/linux/i3c/master.h -+++ b/include/linux/i3c/master.h -@@ -558,6 +558,26 @@ struct i3c_master_controller { - #define i3c_bus_for_each_i3cdev(bus, dev) \ - list_for_each_entry(dev, &(bus)->devs.i3c, common.node) - -+/** -+ * struct i3c_dma - DMA transfer and mapping descriptor -+ * @dev: device object of a device doing DMA -+ * @buf: destination/source buffer for DMA -+ * @len: length of transfer -+ * @map_len: length of DMA mapping -+ * @addr: mapped DMA address for a Host Controller Driver -+ * @dir: DMA direction -+ * @bounce_buf: an allocated bounce buffer if transfer needs it or NULL -+ */ -+struct i3c_dma { -+ struct device *dev; -+ void *buf; -+ size_t len; -+ size_t map_len; -+ dma_addr_t addr; -+ enum dma_data_direction dir; -+ void *bounce_buf; -+}; -+ - int i3c_master_do_i2c_xfers(struct i3c_master_controller *master, - const struct i2c_msg *xfers, - int nxfers); -@@ -575,6 +595,12 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master, - int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, - u8 addr); - int i3c_master_do_daa(struct i3c_master_controller *master); -+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr, -+ size_t len, bool force_bounce, -+ enum dma_data_direction dir); -+void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer); -+DEFINE_FREE(i3c_master_dma_unmap_single, void *, -+ if (_T) i3c_master_dma_unmap_single(_T)) - - int i3c_master_set_info(struct i3c_master_controller *master, - const struct i3c_device_info *info); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet b/SPECS/kernel-rt/0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet deleted file mode 100644 index c663b76bf..000000000 --- a/SPECS/kernel-rt/0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet +++ /dev/null @@ -1,248 +0,0 @@ -From 78661fcc4bca178d718f5e1834ca6f6bf26c3a06 Mon Sep 17 00:00:00 2001 -From: Vinicius Costa Gomes -Date: Tue, 4 May 2021 13:47:03 -0700 -Subject: [PATCH 02/19] igc: Add support for DMA timestamp for non-PTP packets - -For PTP traffic, timestamp is retrieved from TXSTMP register. -For all other packets, DMA timestamp field of the Transmit -Descriptor Write-back is used. - -This is required to work around the HW time stamp misses in -TXSTAMP register due to the HW limitation, when heavy traffic -is initiated. - -Signed-off-by: Vinicius Costa Gomes -Signed-off-by: Aravindhan Gunasekaran -Signed-off-by: Muhammad Husaini Zulkifli ---- - drivers/net/ethernet/intel/igc/igc.h | 3 + - drivers/net/ethernet/intel/igc/igc_base.h | 2 +- - drivers/net/ethernet/intel/igc/igc_defines.h | 2 + - drivers/net/ethernet/intel/igc/igc_main.c | 21 +++++- - drivers/net/ethernet/intel/igc/igc_ptp.c | 72 ++++++++++++++++++++ - 5 files changed, 98 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h -index a427f05814c1..b9f846389e75 100644 ---- a/drivers/net/ethernet/intel/igc/igc.h -+++ b/drivers/net/ethernet/intel/igc/igc.h -@@ -559,6 +559,7 @@ enum igc_tx_flags { - IGC_TX_FLAGS_TSTAMP_3 = 0x400, - - IGC_TX_FLAGS_TSTAMP_TIMER_1 = 0x800, -+ IGC_TX_FLAGS_DMA_TSTAMP = 0x200, - }; - - enum igc_boards { -@@ -780,6 +781,8 @@ int igc_ptp_hwtstamp_get(struct net_device *netdev, - int igc_ptp_hwtstamp_set(struct net_device *netdev, - struct kernel_hwtstamp_config *config, - struct netlink_ext_ack *extack); -+void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, -+ struct sk_buff *skb, u64 tstamp); - void igc_ptp_tx_hang(struct igc_adapter *adapter); - void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); - void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); -diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h -index eaf17cd031c3..b05cdfaa5b94 100644 ---- a/drivers/net/ethernet/intel/igc/igc_base.h -+++ b/drivers/net/ethernet/intel/igc/igc_base.h -@@ -18,7 +18,7 @@ union igc_adv_tx_desc { - __le32 olinfo_status; - } read; - struct { -- __le64 rsvd; /* Reserved */ -+ __le64 dma_tstamp; - __le32 nxtseq_seed; - __le32 status; - } wb; -diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h -index 498ba1522ca4..ea673df406a9 100644 ---- a/drivers/net/ethernet/intel/igc/igc_defines.h -+++ b/drivers/net/ethernet/intel/igc/igc_defines.h -@@ -315,6 +315,7 @@ - #define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ - #define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ - #define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */ -+#define IGC_TXD_STAT_TS_STAT 0x00000002 /* DMA Timestamp in packet */ - #define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */ - #define IGC_TXD_CMD_IP 0x02000000 /* IP packet */ - #define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ -@@ -585,6 +586,7 @@ - /* Transmit Scheduling */ - #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001 - #define IGC_TQAVCTRL_PREEMPT_ENA 0x00000002 -+#define IGC_TQAVCTRL_1588_STAT_EN 0x00000004 - #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008 - #define IGC_TQAVCTRL_FUTSCDDIS 0x00000080 - #define IGC_TQAVCTRL_MIN_FRAG_MASK 0x0000C000 -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 728d7ca5338b..caf7bed4c8ae 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -1556,6 +1556,14 @@ static bool igc_request_tx_tstamp(struct igc_adapter *adapter, struct sk_buff *s - return false; - } - -+static bool igc_is_ptp_packet(struct sk_buff *skb) -+{ -+ __be16 protocol = vlan_get_protocol(skb); -+ -+ /* FIXME: also handle UDP packets */ -+ return protocol == htons(ETH_P_1588); -+} -+ - static int igc_insert_empty_frame(struct igc_ring *tx_ring) - { - struct igc_tx_buffer *empty_info; -@@ -1659,16 +1667,21 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, - - if (unlikely(test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags) && - skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { -+ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); -+ bool is_ptp = igc_is_ptp_packet(skb); - unsigned long flags; - u32 tstamp_flags; - - spin_lock_irqsave(&adapter->ptp_tx_lock, flags); -- if (igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { -+ if (is_ptp && igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - tx_flags |= IGC_TX_FLAGS_TSTAMP | tstamp_flags; - if (skb->sk && - READ_ONCE(skb->sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC) - tx_flags |= IGC_TX_FLAGS_TSTAMP_TIMER_1; -+ } else if (!is_ptp) { -+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; -+ tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; - } else { - adapter->tx_hwtstamp_skipped++; - } -@@ -3170,6 +3183,12 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - if (tx_buffer->type == IGC_TX_BUFFER_TYPE_XSK && - tx_buffer->xsk_pending_ts) - break; -+ if (eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_TS_STAT) && -+ tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { -+ u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); -+ -+ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); -+ } - - /* clear next_to_watch to prevent false hangs */ - tx_buffer->next_to_watch = NULL; -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index b7b46d863bee..61fe47c2a5d3 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -446,6 +446,31 @@ static int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, - return 0; - } - -+static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, -+ struct skb_shared_hwtstamps *hwtstamps, -+ u64 systim) -+{ -+ struct igc_hw *hw = &adapter->hw; -+ u32 sec, nsec; -+ -+ /* FIXME: use a workqueue to read these values to avoid -+ * reading these registers in the hot path. -+ */ -+ nsec = rd32(IGC_SYSTIML); -+ sec = rd32(IGC_SYSTIMH); -+ -+ switch (adapter->hw.mac.type) { -+ case igc_i225: -+ memset(hwtstamps, 0, sizeof(*hwtstamps)); -+ -+ /* HACK */ -+ hwtstamps->hwtstamp = ktime_set(sec, systim & 0xFFFFFFFF); -+ break; -+ default: -+ break; -+ } -+} -+ - /** - * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer - * @adapter: Pointer to adapter the packet buffer belongs to -@@ -579,6 +604,7 @@ static void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter) - static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) - { - struct igc_hw *hw = &adapter->hw; -+ u32 tqavctrl; - int i; - - /* Clear the flags first to avoid new packets to be enqueued -@@ -593,14 +619,26 @@ static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) - /* Now we can clean the pending TX timestamp requests. */ - igc_ptp_clear_tx_tstamp(adapter); - -+ tqavctrl = rd32(IGC_TQAVCTRL); -+ tqavctrl &= ~IGC_TQAVCTRL_1588_STAT_EN; -+ -+ wr32(IGC_TQAVCTRL, tqavctrl); -+ - wr32(IGC_TSYNCTXCTL, 0); - } - - static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) - { - struct igc_hw *hw = &adapter->hw; -+ u32 tqavctrl; - int i; - -+ /* Enable DMA Fetch timestamping */ -+ tqavctrl = rd32(IGC_TQAVCTRL); -+ tqavctrl |= IGC_TQAVCTRL_1588_STAT_EN; -+ -+ wr32(IGC_TQAVCTRL, tqavctrl); -+ - wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); - - /* Read TXSTMP registers to discard any timestamp previously stored. */ -@@ -834,6 +872,40 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) - } - } - -+void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, -+ struct sk_buff *skb, u64 tstamp) -+{ -+ struct skb_shared_hwtstamps shhwtstamps; -+ int adjust = 0; -+ -+ if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) -+ return; -+ -+ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); -+ -+ /* FIXME: Use different latencies for DMA timestamps? */ -+ switch (adapter->link_speed) { -+ case SPEED_10: -+ adjust = IGC_I225_TX_LATENCY_10; -+ break; -+ case SPEED_100: -+ adjust = IGC_I225_TX_LATENCY_100; -+ break; -+ case SPEED_1000: -+ adjust = IGC_I225_TX_LATENCY_1000; -+ break; -+ case SPEED_2500: -+ adjust = IGC_I225_TX_LATENCY_2500; -+ break; -+ } -+ -+ shhwtstamps.hwtstamp = -+ ktime_add_ns(shhwtstamps.hwtstamp, adjust); -+ -+ /* Notify the stack and free the skb after we've unlocked */ -+ skb_tstamp_tx(skb, &shhwtstamps); -+} -+ - /** - * igc_ptp_tx_tstamp_event - * @adapter: board private structure --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-issei-add-firmware-and-host-clients-implementatio.security b/SPECS/kernel-rt/0002-issei-add-firmware-and-host-clients-implementatio.security index 250db1613..73d617832 100644 --- a/SPECS/kernel-rt/0002-issei-add-firmware-and-host-clients-implementatio.security +++ b/SPECS/kernel-rt/0002-issei-add-firmware-and-host-clients-implementatio.security @@ -1,7 +1,7 @@ -From f6896065a2048df5ca9aa66bc01ee1058022ba10 Mon Sep 17 00:00:00 2001 +From e46a110e08da5c96d76e4bb79db4a74a525eff87 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 31 Oct 2024 09:57:32 +0200 -Subject: [PATCH 2/5] issei: add firmware and host clients implementation, +Subject: [PATCH 2/7] issei: add firmware and host clients implementation, finish character device Add the core implementation for firmware and host client @@ -29,12 +29,12 @@ Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-issei | 78 ++++ drivers/misc/issei/Makefile | 2 + - drivers/misc/issei/cdev.c | 229 ++++++++++ - drivers/misc/issei/fw_client.c | 176 +++++++ - drivers/misc/issei/fw_client.h | 51 +++ - drivers/misc/issei/host_client.c | 482 ++++++++++++++++++++ + drivers/misc/issei/cdev.c | 237 ++++++++++ + drivers/misc/issei/fw_client.c | 162 +++++++ + drivers/misc/issei/fw_client.h | 51 ++ + drivers/misc/issei/host_client.c | 489 ++++++++++++++++++++ drivers/misc/issei/host_client.h | 70 +++ - 7 files changed, 1088 insertions(+) + 7 files changed, 1089 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-issei create mode 100644 drivers/misc/issei/fw_client.c create mode 100644 drivers/misc/issei/fw_client.h @@ -43,7 +43,7 @@ Signed-off-by: Alexander Usyskin diff --git a/Documentation/ABI/testing/sysfs-class-issei b/Documentation/ABI/testing/sysfs-class-issei new file mode 100644 -index 000000000000..61af59cd87e1 +index 0000000000000..61af59cd87e1d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-issei @@ -0,0 +1,78 @@ @@ -126,7 +126,7 @@ index 000000000000..61af59cd87e1 + + The version of the firmware client diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile -index 9e3ef22305ac..4e8f6a435a31 100644 +index 9e3ef22305ac9..4e8f6a435a318 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -5,3 +5,5 @@ ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"' @@ -136,14 +136,15 @@ index 9e3ef22305ac..4e8f6a435a31 100644 +issei-objs += fw_client.o +issei-objs += host_client.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c -index 26ac95e5f818..c7e204374eba 100644 +index c265012062c71..4296af754830b 100644 --- a/drivers/misc/issei/cdev.c +++ b/drivers/misc/issei/cdev.c -@@ -7,8 +7,11 @@ - #include - #include +@@ -7,8 +7,12 @@ + #include + #include #include +#include ++#include +#include #include "issei_dev.h" @@ -151,7 +152,7 @@ index 26ac95e5f818..c7e204374eba 100644 #include "cdev.h" struct class *issei_class; -@@ -19,6 +22,225 @@ static dev_t issei_devt; +@@ -19,6 +23,232 @@ static dev_t issei_devt; static DEFINE_MUTEX(issei_minor_lock); static DEFINE_IDR(issei_idr); @@ -160,11 +161,16 @@ index 26ac95e5f818..c7e204374eba 100644 + struct issei_host_client *cl; + struct issei_device *idev; + -+ idev = container_of(inode->i_cdev, struct issei_device, cdev); ++ idev = idr_find(&issei_idr, iminor(inode)); ++ if (!idev) ++ return -ENODEV; ++ get_device(&idev->dev); + + cl = issei_cl_create(idev, fp); -+ if (IS_ERR(cl)) ++ if (IS_ERR(cl)) { ++ put_device(&idev->dev); + return PTR_ERR(cl); ++ } + fp->private_data = cl; + + return nonseekable_open(inode, fp); @@ -173,8 +179,10 @@ index 26ac95e5f818..c7e204374eba 100644 +static int issei_release(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl = fp->private_data; ++ struct issei_device *idev = cl->idev; + + issei_cl_remove(cl); ++ put_device(&idev->dev); + + return 0; +} @@ -196,15 +204,15 @@ index 26ac95e5f818..c7e204374eba 100644 + + switch (cmd) { + case IOCTL_ISSEI_CONNECT_CLIENT: -+ dev_dbg(idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); ++ dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { -+ dev_dbg(idev->dev, "failed to copy data from userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return -EFAULT; + } + @@ -216,16 +224,16 @@ index 26ac95e5f818..c7e204374eba 100644 + return ret; + + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { -+ dev_dbg(idev->dev, "failed to copy data to userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data to userland\n"); + return -EFAULT; + } + break; + + case IOCTL_ISSEI_DISCONNECT_CLIENT: -+ dev_dbg(idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); ++ dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + @@ -253,20 +261,20 @@ index 26ac95e5f818..c7e204374eba 100644 + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.h2f) { -+ dev_dbg(idev->dev, "Write is too big %zu > %zu\n", ++ dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", + length, idev->dma.length.h2f); + return -EFBIG; + } + + buf = memdup_user(ubuf, length); + if (IS_ERR(buf)) { -+ dev_dbg(idev->dev, "failed to copy data from userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return PTR_ERR(buf); + } + @@ -299,13 +307,13 @@ index 26ac95e5f818..c7e204374eba 100644 + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.f2h) { -+ dev_dbg(idev->dev, "Read is too big %zu > %zu\n", ++ dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", + length, idev->dma.length.f2h); + return -EFBIG; + } @@ -328,7 +336,7 @@ index 26ac95e5f818..c7e204374eba 100644 + +copy: + if (copy_to_user(ubuf, data, data_size)) { -+ dev_dbg(idev->dev, "failed to copy data to userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data to userland\n"); + ret = -EFAULT; + } else { + *offset = 0; @@ -349,7 +357,7 @@ index 26ac95e5f818..c7e204374eba 100644 + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return EPOLLERR; + } + @@ -377,7 +385,7 @@ index 26ac95e5f818..c7e204374eba 100644 static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr, char *buf) { -@@ -37,6 +259,13 @@ ATTRIBUTE_GROUPS(issei); +@@ -37,6 +267,13 @@ ATTRIBUTE_GROUPS(issei); static const struct file_operations issei_fops = { .owner = THIS_MODULE, @@ -393,16 +401,15 @@ index 26ac95e5f818..c7e204374eba 100644 static int issei_minor_get(struct issei_device *idev) diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c new file mode 100644 -index 000000000000..5f9ba46bdfe7 +index 0000000000000..43c42f16b9cbc --- /dev/null +++ b/drivers/misc/issei/fw_client.c -@@ -0,0 +1,176 @@ +@@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include +#include +#include -+#include +#include +#include +#include @@ -470,13 +477,8 @@ index 000000000000..5f9ba46bdfe7 + const uuid_t *uuid, u32 mtu, u32 flags) +{ + struct issei_fw_client *fw_cl; -+ struct device *clsdev; + int ret; + -+ clsdev = class_find_device_by_devt(issei_class, idev->cdev.dev); -+ if (!clsdev) -+ return ERR_PTR(-ENODEV); -+ + guard(mutex)(&idev->fw_client_lock); + + fw_cl = kzalloc(sizeof(*fw_cl), GFP_KERNEL); @@ -506,34 +508,26 @@ index 000000000000..5f9ba46bdfe7 + + list_add_tail(&fw_cl->list, &idev->fw_client_list); + -+ ret = sysfs_create_group(&clsdev->kobj, &fw_cl->attr_grp); ++ ret = sysfs_create_group(&idev->dev.kobj, &fw_cl->attr_grp); + if (ret) -+ dev_err(idev->dev, "Attr group for client %pUb failed %d\n", uuid, ret); ++ dev_err(&idev->dev, "Attr group for client %pUb failed %d\n", uuid, ret); + -+ dev_dbg(idev->dev, "FW client %pUb created\n", uuid); -+ put_device(clsdev); ++ dev_dbg(&idev->dev, "FW client %pUb created\n", uuid); + return fw_cl; + +free: + kfree(fw_cl); +err: -+ put_device(clsdev); + return ERR_PTR(ret); +} + +static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl) +{ -+ struct device *clsdev; -+ + WARN(fw_cl->cl, "Removing connected client!\n"); + -+ dev_dbg(idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); ++ dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); + -+ clsdev = class_find_device_by_devt(issei_class, idev->cdev.dev); -+ if (clsdev) { -+ sysfs_remove_group(&clsdev->kobj, &fw_cl->attr_grp); -+ put_device(clsdev); -+ } ++ sysfs_remove_group(&idev->dev.kobj, &fw_cl->attr_grp); + kfree(fw_cl->attr_grp.name); + list_del(&fw_cl->list); + kfree(fw_cl); @@ -575,7 +569,7 @@ index 000000000000..5f9ba46bdfe7 +} diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h new file mode 100644 -index 000000000000..208fdb235b1b +index 0000000000000..208fdb235b1b1 --- /dev/null +++ b/drivers/misc/issei/fw_client.h @@ -0,0 +1,51 @@ @@ -632,10 +626,10 @@ index 000000000000..208fdb235b1b +#endif /* _ISSEI_FW_CLIENT_H_ */ diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c new file mode 100644 -index 000000000000..1eaf33df1da3 +index 0000000000000..6b8dbe566f5f3 --- /dev/null +++ b/drivers/misc/issei/host_client.c -@@ -0,0 +1,482 @@ +@@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include @@ -650,13 +644,7 @@ index 000000000000..1eaf33df1da3 +#include "host_client.h" +#include "fw_client.h" + -+/** -+ * issei_cl_fw_id - fw client id in safe way -+ * @cl: host client -+ * -+ * Return: fw client id or 0 if client is not connected -+ */ -+static inline u8 issei_cl_fw_id(const struct issei_host_client *cl) ++static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) +{ + return cl->fw_cl ? cl->fw_cl->id : 0; +} @@ -665,17 +653,20 @@ index 000000000000..1eaf33df1da3 + +#define cl_dbg(_dev_, _cl_, format, arg...) { \ + struct issei_host_client *_l_cl_ = _cl_; \ -+ dev_dbg((_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, issei_cl_fw_id(_l_cl_), ##arg); \ ++ dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, \ ++ __issei_cl_fw_id(_l_cl_), ##arg); \ +} + +#define cl_warn(_dev_, _cl_, format, arg...) { \ + struct issei_host_client *_l_cl_ = _cl_; \ -+ dev_warn((_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, issei_cl_fw_id(_l_cl_), ##arg); \ ++ dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, \ ++ __issei_cl_fw_id(_l_cl_), ##arg); \ +} + +#define cl_err(_dev_, _cl_, format, arg...) { \ + struct issei_host_client *_l_cl_ = _cl_; \ -+ dev_err((_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, issei_cl_fw_id(_l_cl_), ##arg); \ ++ dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, \ ++ __issei_cl_fw_id(_l_cl_), ##arg); \ +} + +static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf) @@ -758,6 +749,13 @@ index 000000000000..1eaf33df1da3 + __issei_cl_release_rbuf(cl); +} + ++/** ++ * issei_cl_create - create the host client ++ * @idev: issei device ++ * @fp: file pointer to associate with host client ++ * ++ * Return: client pointer on success, ERR_PTR on error ++ */ +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp) +{ + struct issei_host_client *cl; @@ -766,7 +764,7 @@ index 000000000000..1eaf33df1da3 + guard(mutex)(&idev->host_client_lock); + + if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) { -+ dev_err(idev->dev, "Maximum open clients %d is reached.", ISSEI_HOST_CLIENTS_MAX); ++ dev_err(&idev->dev, "Maximum open clients %d is reached.", ISSEI_HOST_CLIENTS_MAX); + return ERR_PTR(-EMFILE); + } + @@ -792,6 +790,10 @@ index 000000000000..1eaf33df1da3 + return cl; +} + ++/** ++ * issei_cl_remove - disconnect and free the host client ++ * @cl: host client ++ */ +void issei_cl_remove(struct issei_host_client *cl) +{ + struct issei_device *idev; @@ -1044,8 +1046,7 @@ index 000000000000..1eaf33df1da3 + +/** + * issei_cl_read - read data from firmware to provided buffer -+ * @idev: issei device -+ * @length: buffer length ++ * @cl: host client + * @buf: buffer to store data + * @buf_size: maximum buffer size passed and actual buffer size on return + * @@ -1120,7 +1121,7 @@ index 000000000000..1eaf33df1da3 +} diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h new file mode 100644 -index 000000000000..9f081bed190d +index 0000000000000..9f081bed190d2 --- /dev/null +++ b/drivers/misc/issei/host_client.h @@ -0,0 +1,70 @@ diff --git a/SPECS/kernel-rt/0002-macintosh-mac_hid-fix-race-condition-in-mac_hid_togg.patch b/SPECS/kernel-rt/0002-macintosh-mac_hid-fix-race-condition-in-mac_hid_togg.patch deleted file mode 100644 index 54abdf824..000000000 --- a/SPECS/kernel-rt/0002-macintosh-mac_hid-fix-race-condition-in-mac_hid_togg.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 6d30ba14d485c6fac4eb518879b8cca0060e9cc0 Mon Sep 17 00:00:00 2001 -From: Long Li -Date: Tue, 19 Aug 2025 17:10:35 +0800 -Subject: [PATCH 02/51] macintosh/mac_hid: fix race condition in - mac_hid_toggle_emumouse - -The following warning appears when running syzkaller, and this issue also -exists in the mainline code. - - ------------[ cut here ]------------ - list_add double add: new=ffffffffa57eee28, prev=ffffffffa57eee28, next=ffffffffa5e63100. - WARNING: CPU: 0 PID: 1491 at lib/list_debug.c:35 __list_add_valid_or_report+0xf7/0x130 - Modules linked in: - CPU: 0 PID: 1491 Comm: syz.1.28 Not tainted 6.6.0+ #3 - Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 - RIP: 0010:__list_add_valid_or_report+0xf7/0x130 - RSP: 0018:ff1100010dfb7b78 EFLAGS: 00010282 - RAX: 0000000000000000 RBX: ffffffffa57eee18 RCX: ffffffff97fc9817 - RDX: 0000000000040000 RSI: ffa0000002383000 RDI: 0000000000000001 - RBP: ffffffffa57eee28 R08: 0000000000000001 R09: ffe21c0021bf6f2c - R10: 0000000000000001 R11: 6464615f7473696c R12: ffffffffa5e63100 - R13: ffffffffa57eee28 R14: ffffffffa57eee28 R15: ff1100010dfb7d48 - FS: 00007fb14398b640(0000) GS:ff11000119600000(0000) knlGS:0000000000000000 - CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 - CR2: 0000000000000000 CR3: 000000010d096005 CR4: 0000000000773ef0 - DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 - DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 - PKRU: 80000000 - Call Trace: - - input_register_handler+0xb3/0x210 - mac_hid_start_emulation+0x1c5/0x290 - mac_hid_toggle_emumouse+0x20a/0x240 - proc_sys_call_handler+0x4c2/0x6e0 - new_sync_write+0x1b1/0x2d0 - vfs_write+0x709/0x950 - ksys_write+0x12a/0x250 - do_syscall_64+0x5a/0x110 - entry_SYSCALL_64_after_hwframe+0x78/0xe2 - -The WARNING occurs when two processes concurrently write to the mac-hid -emulation sysctl, causing a race condition in mac_hid_toggle_emumouse(). -Both processes read old_val=0, then both try to register the input handler, -leading to a double list_add of the same handler. - - CPU0 CPU1 - ------------------------- ------------------------- - vfs_write() //write 1 vfs_write() //write 1 - proc_sys_write() proc_sys_write() - mac_hid_toggle_emumouse() mac_hid_toggle_emumouse() - old_val = *valp // old_val=0 - old_val = *valp // old_val=0 - mutex_lock_killable() - proc_dointvec() // *valp=1 - mac_hid_start_emulation() - input_register_handler() - mutex_unlock() - mutex_lock_killable() - proc_dointvec() - mac_hid_start_emulation() - input_register_handler() //Trigger Warning - mutex_unlock() - -Fix this by moving the old_val read inside the mutex lock region. - -Fixes: 99b089c3c38a ("Input: Mac button emulation - implement as an input filter") -Signed-off-by: Long Li -Signed-off-by: Madhavan Srinivasan -Link: https://patch.msgid.link/20250819091035.2263329-1-leo.lilong@huaweicloud.com ---- - drivers/macintosh/mac_hid.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c -index 369d72f59b3c..06fd910b3fd1 100644 ---- a/drivers/macintosh/mac_hid.c -+++ b/drivers/macintosh/mac_hid.c -@@ -187,13 +187,14 @@ static int mac_hid_toggle_emumouse(const struct ctl_table *table, int write, - void *buffer, size_t *lenp, loff_t *ppos) - { - int *valp = table->data; -- int old_val = *valp; -+ int old_val; - int rc; - - rc = mutex_lock_killable(&mac_hid_emumouse_mutex); - if (rc) - return rc; - -+ old_val = *valp; - rc = proc_dointvec(table, write, buffer, lenp, ppos); - - if (rc == 0 && write && *valp != old_val) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu b/SPECS/kernel-rt/0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu deleted file mode 100644 index a0e2e2c79..000000000 --- a/SPECS/kernel-rt/0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu +++ /dev/null @@ -1,171 +0,0 @@ -From 9e46b764cbbd8def4373a51b40bf7abd62c736e3 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Fri, 10 Oct 2025 18:13:01 +0800 -Subject: [PATCH 2/2] media: i2c: max9x: uniform serdes driver compilation - -Tracked-On: #JILCNT-701 -Signed-off-by: hepengpx ---- - drivers/media/i2c/isx031.c | 4 ++-- - drivers/media/i2c/max9x/max9295.c | 9 ++++----- - drivers/media/i2c/max9x/max9296.c | 2 +- - drivers/media/i2c/max9x/serdes.c | 22 ++++++---------------- - 4 files changed, 13 insertions(+), 24 deletions(-) - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index e613cdb06b83..2d669e964b08 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -370,7 +370,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); - return ret; - } - -@@ -411,7 +411,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return ret; - } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -- mode); -+ (u32)mode); - if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", - cur_mode, mode); -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index a5bacc3684a9..8fbd4215e110 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -177,7 +177,7 @@ static int max9295_setup_gpio(struct max9x_common *common) - { - struct device *dev = common->dev; - int ret; -- struct max9x_gpio_pdata *gpio_pdata; -+ struct max9x_gpio_pdata *gpio_pdata = NULL; - - if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -@@ -697,15 +697,14 @@ static int max9295_remap_reset(struct max9x_common *common) - struct device *dev = common->dev; - struct max9x_pdata *pdata = dev->platform_data; - u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -- common->client->addr; -+ common->client->addr; - u32 virt_addr = common->client->addr; - - dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, - phys_addr); - -- TRY(ret, regmap_update_bits( -- common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -- FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ TRY(ret, regmap_update_bits(common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); - - return 0; - } -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index 41074d60cc01..a28aa190364d 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -730,7 +730,7 @@ static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned i - link_cfg = MAX9296_LINK_B; - else { - dev_err(dev, "No link was detected"); -- return -1; -+ return -EINVAL; - } - - dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index fce930a1fdea..8c1db87ed8c2 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -133,7 +133,7 @@ static const struct of_device_id max9x_of_match[] = { - MODULE_DEVICE_TABLE(of, max9x_of_match); - - static const struct i2c_device_id max9x_id[] = { -- { "max9x", MAX9296 }, -+ { "max9x", 0 }, - { "max9296", MAX9296 }, - { "max96724", MAX96724 }, - { "max9295", MAX9295 }, -@@ -841,6 +841,7 @@ void max9x_destroy(struct max9x_common *common) - /* unregister devices? */ - - v4l2_async_unregister_subdev(&common->v4l.sd); -+ v4l2_subdev_cleanup(&common->v4l.sd); - media_entity_cleanup(&common->v4l.sd.entity); - - i2c_mux_del_adapters(common->muxc); -@@ -1483,10 +1484,7 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); - -- if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -- return &common->v4l.ffmts[fmt->pad]; -- -- return ERR_PTR(-EINVAL); -+ return &common->v4l.ffmts[fmt->pad]; - } - - static int max9x_get_fmt(struct v4l2_subdev *sd, -@@ -1562,7 +1560,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - for_each_active_route(&state->routing, route) { - if (route->source_pad != pad) - continue; -- if (route->sink_pad >= common->v4l.num_pads) { -+ if (unlikely(route->sink_pad >= common->v4l.num_pads)) { - ret = -EINVAL; - dev_err(common->dev, "Found invalid route sink_pad!"); - goto out_unlock; -@@ -2092,7 +2090,7 @@ int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) - struct device *dev = common->dev; - int ret; - -- if (link_id >= common->num_serial_links) -+ if (unlikely(link_id >= common->num_serial_links)) - return 0; - - serial_link = &common->serial_link[link_id]; -@@ -2413,13 +2411,9 @@ static int max9x_parse_subdev_pdata(struct max9x_common *common, - int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(10000); - -- dev_dbg(common->dev, "try to select %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2433,7 +2427,7 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - usleep_range(1000, 1050); - - if (time_is_before_jiffies(timeout)) { -- dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ dev_warn(common->dev, "select %d TIMEOUT", chan_id); - return -ETIMEDOUT; - } - } while (1); -@@ -2451,12 +2445,8 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - -- dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security b/SPECS/kernel-rt/0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security new file mode 100644 index 000000000..cbfae4dc6 --- /dev/null +++ b/SPECS/kernel-rt/0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security @@ -0,0 +1,60 @@ +From 2a787d0659de0d74530788eeb3d0f1b5f2997c97 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Wed, 10 Feb 2021 12:14:55 +0200 +Subject: [PATCH 2/8] mei: bus: add api to query capabilities of ME clients + +Add api to query capabilities of ME clients. +Use bit 0 in bitmask to conway VTag support. + +Co-developed-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/bus.c | 17 +++++++++++++++++ + include/linux/mei_cl_bus.h | 3 +++ + 2 files changed, 20 insertions(+) + +diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c +index abded7457894d..58893e860209b 100644 +--- a/drivers/misc/mei/bus.c ++++ b/drivers/misc/mei/bus.c +@@ -640,6 +640,23 @@ bool mei_cldev_enabled(const struct mei_cl_device *cldev) + } + EXPORT_SYMBOL_GPL(mei_cldev_enabled); + ++/** ++ * mei_cldev_get_capabilities - obtain client capabilities ++ * ++ * @cldev: mei client device ++ * ++ * Return: client capabilities bitmap ++ */ ++u32 mei_cldev_get_capabilities(const struct mei_cl_device *cldev) ++{ ++ u32 cap = 0; ++ ++ if (!mei_cl_vt_support_check(cldev->cl)) ++ cap |= MEI_CLDEV_CAPABILITY_VTAG; ++ ++ return cap; ++} ++ + /** + * mei_cl_bus_module_get - acquire module of the underlying + * hw driver. +diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h +index a82755e1fc40a..709c91489b2a3 100644 +--- a/include/linux/mei_cl_bus.h ++++ b/include/linux/mei_cl_bus.h +@@ -115,6 +115,9 @@ int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, + u8 mei_cldev_ver(const struct mei_cl_device *cldev); + size_t mei_cldev_mtu(const struct mei_cl_device *cldev); + ++#define MEI_CLDEV_CAPABILITY_VTAG BIT(0) ++u32 mei_cldev_get_capabilities(const struct mei_cl_device *cldev); ++ + void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev); + void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet b/SPECS/kernel-rt/0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet new file mode 100644 index 000000000..a44f9313b --- /dev/null +++ b/SPECS/kernel-rt/0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet @@ -0,0 +1,33 @@ +From 048939bd5fb36e11f912ed5fbe412cb35d6a6e3b Mon Sep 17 00:00:00 2001 +From: "Tan, Tee Min" +Date: Mon, 25 Jun 2018 10:34:14 +0800 +Subject: [PATCH 02/18] net: stmmac: Bugfix on stmmac_interrupt() for WOL + +Modify pm_wakeup_event to pm_wakeup_hard_event. + +With the newly introduced pm_wakeup_hard_event function, +WOL only able to functions properly with using this new +function instead of pm_wakeup_event. + +Signed-off-by: Tan, Tee Min +Signed-off-by: Voon Weifeng +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 0dd17179c85d2..8b9dbf6449f1c 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -5988,7 +5988,7 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv) + queues_count = (rx_cnt > tx_cnt) ? rx_cnt : tx_cnt; + + if (priv->irq_wake) +- pm_wakeup_event(priv->device, 0); ++ pm_wakeup_hard_event(priv->device); + + if (priv->dma_cap.estsel) + stmmac_est_irq_status(priv, priv, priv->dev, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-patch-staging-add-ipu7-isys-reset-code.ipu b/SPECS/kernel-rt/0002-patch-staging-add-ipu7-isys-reset-code.ipu deleted file mode 100644 index 3fcaaa1fc..000000000 --- a/SPECS/kernel-rt/0002-patch-staging-add-ipu7-isys-reset-code.ipu +++ /dev/null @@ -1,824 +0,0 @@ -From 5b41ef3bcf3d8c0c5e027f84f97e1122c1f4ce2c Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 11:46:21 +0800 -Subject: [PATCH 02/21] patch: staging add ipu7 isys reset code - -Adding IPU7 isys reset code macro when -CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET is enabled. - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/Kconfig | 10 + - drivers/staging/media/ipu7/ipu7-isys-queue.c | 379 ++++++++++++++++++- - drivers/staging/media/ipu7/ipu7-isys-queue.h | 3 + - drivers/staging/media/ipu7/ipu7-isys-video.c | 101 +++++ - drivers/staging/media/ipu7/ipu7-isys-video.h | 8 + - drivers/staging/media/ipu7/ipu7-isys.c | 16 + - drivers/staging/media/ipu7/ipu7-isys.h | 16 + - 7 files changed, 532 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig -index 7d831ba7501d..c4eee7c3e6d7 100644 ---- a/drivers/staging/media/ipu7/Kconfig -+++ b/drivers/staging/media/ipu7/Kconfig -@@ -17,3 +17,13 @@ config VIDEO_INTEL_IPU7 - - To compile this driver, say Y here! It contains 2 modules - - intel_ipu7 and intel_ipu7_isys. -+ -+config VIDEO_INTEL_IPU7_ISYS_RESET -+ bool "IPU7 ISYS RESET" -+ depends on VIDEO_INTEL_IPU7 -+ default n -+ help -+ This option enables IPU7 ISYS reset feature to support -+ HDMI-MIPI converter hot-plugging. -+ -+ If doubt, say N here. -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c -index 7046c29141f8..1f5fb7d20d81 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c -@@ -11,6 +11,9 @@ - #include - #include - #include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif - - #include - #include -@@ -26,6 +29,9 @@ - #include "ipu7-isys-csi2-regs.h" - #include "ipu7-isys-video.h" - #include "ipu7-platform-regs.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include "ipu7-cpd.h" -+#endif - - #define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) - -@@ -225,6 +231,16 @@ static int buffer_list_get(struct ipu7_isys_stream *stream, - ib = list_last_entry(&aq->incoming, - struct ipu7_isys_buffer, head); - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ -+ if (av->skipframe) { -+ atomic_set(&ib->skipframe_flag, 1); -+ av->skipframe--; -+ } else { -+ atomic_set(&ib->skipframe_flag, 0); -+ } -+#endif - dev_dbg(dev, "buffer: %s: buffer %u\n", - ipu7_isys_queue_to_video(aq)->vdev.name, - ipu7_isys_buffer_to_vb2_buffer(ib)->index); -@@ -379,6 +395,18 @@ static void buf_queue(struct vb2_buffer *vb) - return; - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&av->isys->reset_mutex); -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ dev_dbg(dev, "in reset, adding to incoming\n"); -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ /* ip may be cleared in ipu reset */ -+ stream = av->stream; -+#endif - mutex_lock(&stream->mutex); - - if (stream->nr_streaming != stream->nr_queues) { -@@ -480,6 +508,10 @@ static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) - static void return_buffers(struct ipu7_isys_queue *aq, - enum vb2_buffer_state state) - { -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ bool need_reset = false; -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+#endif - struct ipu7_isys_buffer *ib; - struct vb2_buffer *vb; - unsigned long flags; -@@ -501,6 +533,9 @@ static void return_buffers(struct ipu7_isys_queue *aq, - vb2_buffer_done(vb, state); - - spin_lock_irqsave(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ need_reset = true; -+#endif - } - - while (!list_empty(&aq->incoming)) { -@@ -516,6 +551,14 @@ static void return_buffers(struct ipu7_isys_queue *aq, - } - - spin_unlock_irqrestore(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ -+ if (need_reset) { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = true; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+#endif - } - - static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) -@@ -594,6 +637,9 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) - - out: - mutex_unlock(&stream->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->start_streaming = 1; -+#endif - - return 0; - -@@ -614,17 +660,292 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) - return ret; - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static void reset_stop_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ mutex_lock(&stream->mutex); -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ while (!list_empty(&aq->active)) { -+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+ -+static int reset_start_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer_list __bl, *bl = NULL; -+ struct ipu7_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues; -+ int ret; -+ -+ dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); -+ -+ av->skipframe = 1; -+ -+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_dbg(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu7_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_dbg(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu7_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) { -+ mutex_unlock(&stream->mutex); -+ goto out_pipeline_stop; -+ } -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ int retry = 5; -+ while (retry--) { -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) { -+ dev_dbg(dev, "wait for incoming buffer, retry %d\n", retry); -+ usleep_range(100000, 110000); -+ continue; -+ } -+ break; -+ } -+ -+ /* -+ * In reset start streaming and no buffer available, -+ * it is considered that gstreamer has been closed, -+ * and reset start is no needed, not driver bug. -+ */ -+ if (ret) { -+ dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ goto out_stream_start; -+ } -+ -+ ret = ipu7_isys_fw_open(av->isys); -+ if (ret) -+ goto out_stream_start; -+ -+ ipu7_isys_setup_hw(av->isys); -+ -+ ret = ipu7_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_isys_fw_close; -+ -+out: -+ mutex_unlock(&stream->mutex); -+ av->start_streaming = 1; -+ return 0; -+ -+out_isys_fw_close: -+ ipu7_isys_fw_close(av->isys); -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ mutex_unlock(&stream->mutex); -+ -+out_pipeline_stop: -+ ipu7_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ av->start_streaming = 0; -+ dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); -+ return ret; -+} -+ -+static int ipu_isys_reset(struct ipu7_isys_video *self_av, -+ struct ipu7_isys_stream *self_stream) -+{ -+ struct ipu7_isys *isys = self_av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ struct ipu7_isys_video *av = NULL; -+ struct ipu7_isys_stream *stream = NULL; -+ struct device *dev = &adev->auxdev.dev; -+ int ret = 0; -+ int i, j; -+ int has_streaming = 0; -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&isys->reset_mutex); -+ return 0; -+ } -+ isys->state |= RESET_STATE_IN_RESET; -+ dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); -+ -+ while (isys->state & RESET_STATE_IN_STOP_STREAMING) { -+ dev_dbg(dev, "isys reset: %s: wait for stop\n", -+ self_av->vdev.name); -+ mutex_unlock(&isys->reset_mutex); -+ usleep_range(10000, 11000); -+ mutex_lock(&isys->reset_mutex); -+ } -+ -+ mutex_unlock(&isys->reset_mutex); -+ -+ dev_dbg(dev, "reset stop streams\n"); -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ av = &isys->csi2[i].av[j]; -+ if (av == self_av) -+ continue; -+ -+ stream = av->stream; -+ if (!stream || stream == self_stream) -+ continue; -+ -+ if (!stream->streaming && !stream->nr_streaming) -+ continue; -+ -+ av->reset = true; -+ has_streaming = true; -+ reset_stop_streaming(av); -+ } -+ } -+ -+ if (!has_streaming) -+ goto end_of_reset; -+ -+ ipu7_cleanup_fw_msg_bufs(isys); -+ -+ dev_dbg(dev, "reset start streams\n"); -+ -+ for (j = 0; j < csi2_pdata->nports; j++) { -+ for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { -+ av = &isys->csi2[j].av[i]; -+ if (!av->reset) -+ continue; -+ -+ av->reset = false; -+ ret = reset_start_streaming(av); -+ if (ret) -+ break; -+ } -+ if (ret) -+ break; -+ } -+ -+end_of_reset: -+ mutex_lock(&isys->reset_mutex); -+ isys->state &= ~RESET_STATE_IN_RESET; -+ mutex_unlock(&isys->reset_mutex); -+ dev_dbg(dev, "reset done\n"); -+ -+ return 0; -+} -+ - static void stop_streaming(struct vb2_queue *q) - { - struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); - struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); - struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ int times = 5; -+ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ bool need_reset; -+ -+ dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); -+ -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(dev, "stop: %s: wait for reset or stop, isys->state = %d\n", -+ av->vdev.name, av->isys->state); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ -+ if (!av->start_streaming) { -+ mutex_unlock(&av->isys->reset_mutex); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ return; -+ } -+ -+ av->isys->state |= RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ while (!list_empty(&aq->active) && times--) { -+ usleep_range(30000, 31000); -+ } -+ -+ stream = av->stream; -+ if (!stream) { -+ dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } - - mutex_lock(&stream->mutex); - mutex_lock(&av->isys->stream_mutex); - if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ipu7_isys_video_set_streaming(av, 0, NULL); -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); - mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(dev, "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } - - stream->nr_streaming--; - list_del(&aq->node); -@@ -637,7 +958,58 @@ static void stop_streaming(struct vb2_queue *q) - return_buffers(aq, VB2_BUF_STATE_ERROR); - - ipu7_isys_fw_close(av->isys); -+ -+ av->start_streaming = 0; -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ need_reset = av->isys->need_reset; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ if (need_reset) { -+ if (!stream->nr_streaming) { -+ ipu_isys_reset(av, stream); -+ } else { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = false; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+ } -+ -+ dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); - } -+#else -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ -+ mutex_lock(&stream->mutex); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(&av->isys->adev->auxdev.dev, -+ "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+#endif - - static unsigned int - get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) -@@ -719,6 +1091,11 @@ static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) - * to the userspace when it is de-queued - */ - atomic_set(&ib->str2mmio_flag, 0); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ } else if (atomic_read(&ib->skipframe_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ atomic_set(&ib->skipframe_flag, 0); -+#endif - } else { - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.h b/drivers/staging/media/ipu7/ipu7-isys-queue.h -index 0cb08a38f756..5a909c3a78d2 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.h -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.h -@@ -31,6 +31,9 @@ struct ipu7_isys_queue { - struct ipu7_isys_buffer { - struct list_head head; - atomic_t str2mmio_flag; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ atomic_t skipframe_flag; -+#endif - }; - - struct ipu7_isys_video_buffer { -diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c -index 173afd405d9b..b3d337fe786b 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-video.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-video.c -@@ -18,6 +18,9 @@ - #include - #include - #include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif - - #include - #include -@@ -90,9 +93,43 @@ const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { - - static int video_open(struct file *file) - { -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = video_drvdata(file); -+ struct ipu7_isys *isys = av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -+ return -EIO; -+ } -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif - return v4l2_fh_open(file); - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static int video_release(struct file *file) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: enter\n", av->vdev.name); -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: wait for reset\n", av->vdev.name); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ return vb2_fop_release(file); -+} -+ -+#endif - const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) - { - unsigned int i; -@@ -589,7 +626,11 @@ static void stop_streaming_firmware(struct ipu7_isys_video *av) - } - - tout = wait_for_completion_timeout(&stream->stream_stop_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else - FW_CALL_TIMEOUT_JIFFIES); -+#endif - if (!tout) - dev_warn(dev, "stream stop time out\n"); - else if (stream->error) -@@ -614,7 +655,11 @@ static void close_streaming_firmware(struct ipu7_isys_video *av) - } - - tout = wait_for_completion_timeout(&stream->stream_close_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else - FW_CALL_TIMEOUT_JIFFIES); -+#endif - if (!tout) - dev_warn(dev, "stream close time out\n"); - else if (stream->error) -@@ -622,6 +667,12 @@ static void close_streaming_firmware(struct ipu7_isys_video *av) - else - dev_dbg(dev, "close stream: complete\n"); - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ stream->last_sequence = atomic_read(&stream->sequence); -+ dev_dbg(dev, "ip->last_sequence = %d\n", -+ stream->last_sequence); -+ -+#endif - put_stream_opened(av); - } - -@@ -636,7 +687,18 @@ int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, - return -EINVAL; - - stream->nr_queues = nr_queues; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ atomic_set(&stream->sequence, stream->last_sequence); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "atomic_set : stream->last_sequence = %d\n", -+ stream->last_sequence); -+ } else { -+ atomic_set(&stream->sequence, 0); -+ } -+#else - atomic_set(&stream->sequence, 0); -+#endif - atomic_set(&stream->buf_id, 0); - - stream->seq_index = 0; -@@ -897,7 +959,11 @@ static const struct v4l2_file_operations isys_fops = { - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, - .open = video_open, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ .release = video_release, -+#else - .release = vb2_fop_release, -+#endif - }; - - int ipu7_isys_fw_open(struct ipu7_isys *isys) -@@ -936,6 +1002,35 @@ int ipu7_isys_fw_open(struct ipu7_isys *isys) - return ret; - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+void ipu7_isys_fw_close(struct ipu7_isys *isys) -+{ -+ int ret = 0; -+ -+ mutex_lock(&isys->mutex); -+ isys->ref_count--; -+ if (!isys->ref_count) { -+ /* need reset when fw close is abnormal */ -+ ret = ipu7_fw_isys_close(isys); -+ if (ret) { -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = true; -+ mutex_unlock(&isys->reset_mutex); -+ } -+ } -+ -+ mutex_unlock(&isys->mutex); -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put_sync(&isys->adev->auxdev.dev); -+ } else { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put(&isys->adev->auxdev.dev); -+ } -+} -+#else - void ipu7_isys_fw_close(struct ipu7_isys *isys) - { - mutex_lock(&isys->mutex); -@@ -948,6 +1043,7 @@ void ipu7_isys_fw_close(struct ipu7_isys *isys) - mutex_unlock(&isys->mutex); - pm_runtime_put(&isys->adev->auxdev.dev); - } -+#endif - - int ipu7_isys_setup_video(struct ipu7_isys_video *av, - struct media_entity **source_entity, int *nr_queues) -@@ -1082,6 +1178,11 @@ int ipu7_isys_video_init(struct ipu7_isys_video *av) - - __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); - av->pix_fmt = format.fmt.pix; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->reset = false; -+ av->skipframe = 0; -+ av->start_streaming = 0; -+#endif - - set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); - video_set_drvdata(&av->vdev, av); -diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.h b/drivers/staging/media/ipu7/ipu7-isys-video.h -index 1ac1787fabef..e6d1da2b7b47 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-video.h -+++ b/drivers/staging/media/ipu7/ipu7-isys-video.h -@@ -53,6 +53,9 @@ struct ipu7_isys_stream { - struct mutex mutex; - struct media_entity *source_entity; - atomic_t sequence; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ int last_sequence; -+#endif - atomic_t buf_id; - unsigned int seq_index; - struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; -@@ -89,6 +92,11 @@ struct ipu7_isys_video { - unsigned int streaming; - u8 vc; - u8 dt; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ unsigned int reset; -+ unsigned int skipframe; -+ unsigned int start_streaming; -+#endif - }; - - #define ipu7_isys_queue_to_video(__aq) \ -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index cb2f49f3e0fa..cfb3989fe719 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -540,6 +540,12 @@ static int isys_runtime_pm_suspend(struct device *dev) - isys->power = 0; - spin_unlock_irqrestore(&isys->power_lock, flags); - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = false; -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif - cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); - - ipu7_mmu_hw_cleanup(adev->mmu); -@@ -594,6 +600,9 @@ static void isys_remove(struct auxiliary_device *auxdev) - - mutex_destroy(&isys->stream_mutex); - mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif - } - - static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) -@@ -738,6 +747,10 @@ static int isys_probe(struct auxiliary_device *auxdev, - - mutex_init(&isys->mutex); - mutex_init(&isys->stream_mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_init(&isys->reset_mutex); -+ isys->state = 0; -+#endif - - spin_lock_init(&isys->listlock); - INIT_LIST_HEAD(&isys->framebuflist); -@@ -767,6 +780,9 @@ static int isys_probe(struct auxiliary_device *auxdev, - if (ret) - goto out_cleanup; - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif - ipu7_mmu_hw_cleanup(adev->mmu); - pm_runtime_put(&auxdev->dev); - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index ef1ab1b42f6c..17d4d5630169 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -44,8 +44,16 @@ - #define IPU_ISYS_MAX_WIDTH 8160U - #define IPU_ISYS_MAX_HEIGHT 8190U - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define RESET_STATE_IN_RESET 1U -+#define RESET_STATE_IN_STOP_STREAMING 2U -+ -+#endif - #define FW_CALL_TIMEOUT_JIFFIES \ - msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) -+#endif - - struct isys_fw_log { - struct mutex mutex; /* protect whole struct */ -@@ -68,6 +76,9 @@ struct isys_fw_log { - * @streams_lock: serialise access to streams - * @streams: streams per firmware stream ID - * @syscom: fw communication layer context -+ #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ * @need_reset: Isys requires d0i0->i3 transition -+ #endif - * @ref_count: total number of callers fw open - * @mutex: serialise access isys video open/release related operations - * @stream_mutex: serialise stream start and stop, queueing requests -@@ -109,6 +120,11 @@ struct ipu7_isys { - - struct ipu7_insys_config *subsys_config; - dma_addr_t subsys_config_dma_addr; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct mutex reset_mutex; -+ bool need_reset; -+ int state; -+#endif - }; - - struct isys_fw_msgs { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf b/SPECS/kernel-rt/0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf deleted file mode 100644 index e0d5f16f8..000000000 --- a/SPECS/kernel-rt/0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf +++ /dev/null @@ -1,223 +0,0 @@ -From 2e3221601dc344723969ff1499a2a86bdbbc62b8 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 12 May 2025 08:37:39 -0700 -Subject: [PATCH 002/100] perf/x86/intel: Add a check for dynamic constraints - -The current event scheduler has a limit. If the counter constraint of an -event is not a subset of any other counter constraint with an equal or -higher weight. The counters may not be fully utilized. - -To workaround it, the commit bc1738f6ee83 ("perf, x86: Fix event -scheduler for constraints with overlapping counters") introduced an -overlap flag, which is hardcoded to the event constraint that may -trigger the limit. It only works for static constraints. - -Many features on and after Intel PMON v6 require dynamic constraints. An -event constraint is decided by both static and dynamic constraints at -runtime. See commit 4dfe3232cc04 ("perf/x86: Add dynamic constraint"). -The dynamic constraints are from CPUID enumeration. It's impossible to -hardcode it in advance. It's not practical to set the overlap flag to all -events. It's harmful to the scheduler. - -For the existing Intel platforms, the dynamic constraints don't trigger -the limit. A real fix is not required. - -However, for virtualization, VMM may give a weird CPUID enumeration to a -guest. It's impossible to indicate what the weird enumeration is. A -check is introduced, which can list the possible breaks if a weird -enumeration is used. - -Check the dynamic constraints enumerated for normal, branch counters -logging, and auto-counter reload. -Check both PEBS and non-PEBS constratins. - -Closes: https://lore.kernel.org/lkml/20250416195610.GC38216@noisy.programming.kicks-ass.net/ -Signed-off-by: Kan Liang ---- - arch/x86/events/intel/core.c | 156 +++++++++++++++++++++++++++++++++-- - 1 file changed, 148 insertions(+), 8 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index c2fb729c270e..60c950e4527a 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5260,6 +5260,151 @@ static void intel_pmu_check_event_constraints(struct event_constraint *event_con - u64 fixed_cntr_mask, - u64 intel_ctrl); - -+enum dyn_constr_type { -+ DYN_CONSTR_NONE, -+ DYN_CONSTR_BR_CNTR, -+ DYN_CONSTR_ACR_CNTR, -+ DYN_CONSTR_ACR_CAUSE, -+ -+ DYN_CONSTR_MAX, -+}; -+ -+static const char * const dyn_constr_type_name[] = { -+ [DYN_CONSTR_NONE] = "a normal event", -+ [DYN_CONSTR_BR_CNTR] = "a branch counter logging event", -+ [DYN_CONSTR_ACR_CNTR] = "an auto-counter reload event", -+ [DYN_CONSTR_ACR_CAUSE] = "an auto-counter reload cause event", -+}; -+ -+static void __intel_pmu_check_dyn_constr(struct event_constraint *constr, -+ enum dyn_constr_type type, u64 mask) -+{ -+ struct event_constraint *c1, *c2; -+ int new_weight, check_weight; -+ u64 new_mask, check_mask; -+ -+ for_each_event_constraint(c1, constr) { -+ new_mask = c1->idxmsk64 & mask; -+ new_weight = hweight64(new_mask); -+ -+ /* ignore topdown perf metrics event */ -+ if (c1->idxmsk64 & INTEL_PMC_MSK_TOPDOWN) -+ continue; -+ -+ if (!new_weight && fls64(c1->idxmsk64) < INTEL_PMC_IDX_FIXED) { -+ pr_info("The event 0x%llx is not supported as %s.\n", -+ c1->code, dyn_constr_type_name[type]); -+ } -+ -+ if (new_weight <= 1) -+ continue; -+ -+ for_each_event_constraint(c2, c1 + 1) { -+ bool check_fail = false; -+ -+ check_mask = c2->idxmsk64 & mask; -+ check_weight = hweight64(check_mask); -+ -+ if (c2->idxmsk64 & INTEL_PMC_MSK_TOPDOWN || -+ !check_weight) -+ continue; -+ -+ /* The same constraints or no overlap */ -+ if (new_mask == check_mask || -+ (new_mask ^ check_mask) == (new_mask | check_mask)) -+ continue; -+ -+ /* -+ * A scheduler issue may be triggered in the following cases. -+ * - Two overlap constraints have the same weight. -+ * E.g., A constraints: 0x3, B constraints: 0x6 -+ * event counter failure case -+ * B PMC[2:1] 1 -+ * A PMC[1:0] 0 -+ * A PMC[1:0] FAIL -+ * - Two overlap constraints have different weight. -+ * The constraint has a low weight, but has high last bit. -+ * E.g., A constraints: 0x7, B constraints: 0xC -+ * event counter failure case -+ * B PMC[3:2] 2 -+ * A PMC[2:0] 0 -+ * A PMC[2:0] 1 -+ * A PMC[2:0] FAIL -+ */ -+ if (new_weight == check_weight) { -+ check_fail = true; -+ } else if (new_weight < check_weight) { -+ if ((new_mask | check_mask) != check_mask && -+ fls64(new_mask) > fls64(check_mask)) -+ check_fail = true; -+ } else { -+ if ((new_mask | check_mask) != new_mask && -+ fls64(new_mask) < fls64(check_mask)) -+ check_fail = true; -+ } -+ -+ if (check_fail) { -+ pr_info("The two events 0x%llx and 0x%llx may not be " -+ "fully scheduled under some circumstances as " -+ "%s.\n", -+ c1->code, c2->code, dyn_constr_type_name[type]); -+ } -+ } -+ } -+} -+ -+static void intel_pmu_check_dyn_constr(struct pmu *pmu, -+ struct event_constraint *constr, -+ u64 cntr_mask) -+{ -+ enum dyn_constr_type i; -+ u64 mask; -+ -+ for (i = DYN_CONSTR_NONE; i < DYN_CONSTR_MAX; i++) { -+ mask = 0; -+ switch (i) { -+ case DYN_CONSTR_NONE: -+ mask = cntr_mask; -+ break; -+ case DYN_CONSTR_BR_CNTR: -+ if (x86_pmu.flags & PMU_FL_BR_CNTR) -+ mask = x86_pmu.lbr_counters; -+ break; -+ case DYN_CONSTR_ACR_CNTR: -+ mask = hybrid(pmu, acr_cntr_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0); -+ break; -+ case DYN_CONSTR_ACR_CAUSE: -+ if (hybrid(pmu, acr_cntr_mask64) == hybrid(pmu, acr_cause_mask64)) -+ continue; -+ mask = hybrid(pmu, acr_cause_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0); -+ break; -+ default: -+ pr_warn("Unsupported dynamic constraint type %d\n", i); -+ } -+ -+ if (mask) -+ __intel_pmu_check_dyn_constr(constr, i, mask); -+ } -+} -+ -+static void intel_pmu_check_event_constraints_all(struct pmu *pmu) -+{ -+ struct event_constraint *event_constraints = hybrid(pmu, event_constraints); -+ struct event_constraint *pebs_constraints = hybrid(pmu, pebs_constraints); -+ u64 cntr_mask = hybrid(pmu, cntr_mask64); -+ u64 fixed_cntr_mask = hybrid(pmu, fixed_cntr_mask64); -+ u64 intel_ctrl = hybrid(pmu, intel_ctrl); -+ -+ intel_pmu_check_event_constraints(event_constraints, cntr_mask, -+ fixed_cntr_mask, intel_ctrl); -+ -+ if (event_constraints) -+ intel_pmu_check_dyn_constr(pmu, event_constraints, cntr_mask); -+ -+ if (pebs_constraints) -+ intel_pmu_check_dyn_constr(pmu, pebs_constraints, cntr_mask); -+} -+ - static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs); - - static inline bool intel_pmu_broken_perf_cap(void) -@@ -5322,10 +5467,7 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) - else - pmu->intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS); - -- intel_pmu_check_event_constraints(pmu->event_constraints, -- pmu->cntr_mask64, -- pmu->fixed_cntr_mask64, -- pmu->intel_ctrl); -+ intel_pmu_check_event_constraints_all(&pmu->pmu); - - intel_pmu_check_extra_regs(pmu->extra_regs); - } -@@ -7737,10 +7879,8 @@ __init int intel_pmu_init(void) - if (x86_pmu.intel_cap.anythread_deprecated) - x86_pmu.format_attrs = intel_arch_formats_attr; - -- intel_pmu_check_event_constraints(x86_pmu.event_constraints, -- x86_pmu.cntr_mask64, -- x86_pmu.fixed_cntr_mask64, -- x86_pmu.intel_ctrl); -+ intel_pmu_check_event_constraints_all(NULL); -+ - /* - * Access LBR MSR may cause #GP under certain circumstances. - * Check all LBR MSR here. --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf b/SPECS/kernel-rt/0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf new file mode 100644 index 000000000..041f015a4 --- /dev/null +++ b/SPECS/kernel-rt/0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf @@ -0,0 +1,316 @@ +From 369c34bbb00ae12a88ad60817c463254b56bab99 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Mon, 29 Dec 2025 10:58:52 -0800 +Subject: [PATCH 02/13] perf/x86/intel/uncore: Move uncore discovery init + struct to header + +The discovery base MSR or PCI device is platform-specific and must be +defined statically in the per-platform init table and passed to the +discovery code. + +Move the definition of struct intel_uncore_init_fun to uncore.h so it +can be accessed by discovery code, and rename it to reflect that it +now carries more than just init callbacks. + +Shorten intel_uncore_has_discovery_tables[_pci/msr] to +uncore_discovery[_pci/msr] for improved readability and alignment. + +Drop the `intel_` prefix from new names since the code is under the +intel directory and long identifiers make alignment harder. Further +cleanups will continue removing `intel_` prefixes. + +No functional change intended. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 72 ++++++++++-------------- + arch/x86/events/intel/uncore.h | 10 ++++ + arch/x86/events/intel/uncore_discovery.c | 12 ++-- + arch/x86/events/intel/uncore_discovery.h | 2 +- + 4 files changed, 49 insertions(+), 47 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index e228e564b15ea..cd561290be8ce 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1697,133 +1697,123 @@ static int __init uncore_mmio_init(void) + return ret; + } + +-struct intel_uncore_init_fun { +- void (*cpu_init)(void); +- int (*pci_init)(void); +- void (*mmio_init)(void); +- /* Discovery table is required */ +- bool use_discovery; +- /* The units in the discovery table should be ignored. */ +- int *uncore_units_ignore; +-}; +- +-static const struct intel_uncore_init_fun nhm_uncore_init __initconst = { ++static const struct uncore_plat_init nhm_uncore_init __initconst = { + .cpu_init = nhm_uncore_cpu_init, + }; + +-static const struct intel_uncore_init_fun snb_uncore_init __initconst = { ++static const struct uncore_plat_init snb_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = snb_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun ivb_uncore_init __initconst = { ++static const struct uncore_plat_init ivb_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = ivb_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun hsw_uncore_init __initconst = { ++static const struct uncore_plat_init hsw_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = hsw_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun bdw_uncore_init __initconst = { ++static const struct uncore_plat_init bdw_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = bdw_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun snbep_uncore_init __initconst = { ++static const struct uncore_plat_init snbep_uncore_init __initconst = { + .cpu_init = snbep_uncore_cpu_init, + .pci_init = snbep_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun nhmex_uncore_init __initconst = { ++static const struct uncore_plat_init nhmex_uncore_init __initconst = { + .cpu_init = nhmex_uncore_cpu_init, + }; + +-static const struct intel_uncore_init_fun ivbep_uncore_init __initconst = { ++static const struct uncore_plat_init ivbep_uncore_init __initconst = { + .cpu_init = ivbep_uncore_cpu_init, + .pci_init = ivbep_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun hswep_uncore_init __initconst = { ++static const struct uncore_plat_init hswep_uncore_init __initconst = { + .cpu_init = hswep_uncore_cpu_init, + .pci_init = hswep_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun bdx_uncore_init __initconst = { ++static const struct uncore_plat_init bdx_uncore_init __initconst = { + .cpu_init = bdx_uncore_cpu_init, + .pci_init = bdx_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun knl_uncore_init __initconst = { ++static const struct uncore_plat_init knl_uncore_init __initconst = { + .cpu_init = knl_uncore_cpu_init, + .pci_init = knl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun skl_uncore_init __initconst = { ++static const struct uncore_plat_init skl_uncore_init __initconst = { + .cpu_init = skl_uncore_cpu_init, + .pci_init = skl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun skx_uncore_init __initconst = { ++static const struct uncore_plat_init skx_uncore_init __initconst = { + .cpu_init = skx_uncore_cpu_init, + .pci_init = skx_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun icl_uncore_init __initconst = { ++static const struct uncore_plat_init icl_uncore_init __initconst = { + .cpu_init = icl_uncore_cpu_init, + .pci_init = skl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun tgl_uncore_init __initconst = { ++static const struct uncore_plat_init tgl_uncore_init __initconst = { + .cpu_init = tgl_uncore_cpu_init, + .mmio_init = tgl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun tgl_l_uncore_init __initconst = { ++static const struct uncore_plat_init tgl_l_uncore_init __initconst = { + .cpu_init = tgl_uncore_cpu_init, + .mmio_init = tgl_l_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun rkl_uncore_init __initconst = { ++static const struct uncore_plat_init rkl_uncore_init __initconst = { + .cpu_init = tgl_uncore_cpu_init, + .pci_init = skl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun adl_uncore_init __initconst = { ++static const struct uncore_plat_init adl_uncore_init __initconst = { + .cpu_init = adl_uncore_cpu_init, + .mmio_init = adl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun mtl_uncore_init __initconst = { ++static const struct uncore_plat_init mtl_uncore_init __initconst = { + .cpu_init = mtl_uncore_cpu_init, + .mmio_init = adl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun lnl_uncore_init __initconst = { ++static const struct uncore_plat_init lnl_uncore_init __initconst = { + .cpu_init = lnl_uncore_cpu_init, + .mmio_init = lnl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun ptl_uncore_init __initconst = { ++static const struct uncore_plat_init ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, + .use_discovery = true, + }; + +-static const struct intel_uncore_init_fun icx_uncore_init __initconst = { ++static const struct uncore_plat_init icx_uncore_init __initconst = { + .cpu_init = icx_uncore_cpu_init, + .pci_init = icx_uncore_pci_init, + .mmio_init = icx_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun snr_uncore_init __initconst = { ++static const struct uncore_plat_init snr_uncore_init __initconst = { + .cpu_init = snr_uncore_cpu_init, + .pci_init = snr_uncore_pci_init, + .mmio_init = snr_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun spr_uncore_init __initconst = { ++static const struct uncore_plat_init spr_uncore_init __initconst = { + .cpu_init = spr_uncore_cpu_init, + .pci_init = spr_uncore_pci_init, + .mmio_init = spr_uncore_mmio_init, +@@ -1831,7 +1821,7 @@ static const struct intel_uncore_init_fun spr_uncore_init __initconst = { + .uncore_units_ignore = spr_uncore_units_ignore, + }; + +-static const struct intel_uncore_init_fun gnr_uncore_init __initconst = { ++static const struct uncore_plat_init gnr_uncore_init __initconst = { + .cpu_init = gnr_uncore_cpu_init, + .pci_init = gnr_uncore_pci_init, + .mmio_init = gnr_uncore_mmio_init, +@@ -1839,7 +1829,7 @@ static const struct intel_uncore_init_fun gnr_uncore_init __initconst = { + .uncore_units_ignore = gnr_uncore_units_ignore, + }; + +-static const struct intel_uncore_init_fun generic_uncore_init __initconst = { ++static const struct uncore_plat_init generic_uncore_init __initconst = { + .cpu_init = intel_uncore_generic_uncore_cpu_init, + .pci_init = intel_uncore_generic_uncore_pci_init, + .mmio_init = intel_uncore_generic_uncore_mmio_init, +@@ -1910,7 +1900,7 @@ MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match); + static int __init intel_uncore_init(void) + { + const struct x86_cpu_id *id; +- struct intel_uncore_init_fun *uncore_init; ++ struct uncore_plat_init *uncore_init; + int pret = 0, cret = 0, mret = 0, ret; + + if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) +@@ -1921,16 +1911,16 @@ static int __init intel_uncore_init(void) + + id = x86_match_cpu(intel_uncore_match); + if (!id) { +- if (!uncore_no_discover && intel_uncore_has_discovery_tables(NULL)) +- uncore_init = (struct intel_uncore_init_fun *)&generic_uncore_init; ++ if (!uncore_no_discover && uncore_discovery(NULL)) ++ uncore_init = (struct uncore_plat_init *)&generic_uncore_init; + else + return -ENODEV; + } else { +- uncore_init = (struct intel_uncore_init_fun *)id->driver_data; ++ uncore_init = (struct uncore_plat_init *)id->driver_data; + if (uncore_no_discover && uncore_init->use_discovery) + return -ENODEV; + if (uncore_init->use_discovery && +- !intel_uncore_has_discovery_tables(uncore_init->uncore_units_ignore)) ++ !uncore_discovery(uncore_init)) + return -ENODEV; + } + +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index d8815fff75882..568536ef28ee6 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -47,6 +47,16 @@ struct uncore_event_desc; + struct freerunning_counters; + struct intel_uncore_topology; + ++struct uncore_plat_init { ++ void (*cpu_init)(void); ++ int (*pci_init)(void); ++ void (*mmio_init)(void); ++ /* Discovery table is required */ ++ bool use_discovery; ++ /* The units in the discovery table should be ignored. */ ++ int *uncore_units_ignore; ++}; ++ + struct intel_uncore_type { + const char *name; + int num_counters; +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index 7d57ce706feb1..d39f6a0b8cc35 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -350,7 +350,7 @@ static int parse_discovery_table(struct pci_dev *dev, int die, + return __parse_discovery_table(addr, die, parsed, ignore); + } + +-static bool intel_uncore_has_discovery_tables_pci(int *ignore) ++static bool uncore_discovery_pci(int *ignore) + { + u32 device, val, entry_id, bar_offset; + int die, dvsec = 0, ret = true; +@@ -399,7 +399,7 @@ static bool intel_uncore_has_discovery_tables_pci(int *ignore) + return ret; + } + +-static bool intel_uncore_has_discovery_tables_msr(int *ignore) ++static bool uncore_discovery_msr(int *ignore) + { + unsigned long *die_mask; + bool parsed = false; +@@ -432,10 +432,12 @@ static bool intel_uncore_has_discovery_tables_msr(int *ignore) + return parsed; + } + +-bool intel_uncore_has_discovery_tables(int *ignore) ++bool uncore_discovery(struct uncore_plat_init *init) + { +- return intel_uncore_has_discovery_tables_msr(ignore) || +- intel_uncore_has_discovery_tables_pci(ignore); ++ int *ignore = init ? init->uncore_units_ignore : NULL; ++ ++ return uncore_discovery_msr(ignore) || ++ uncore_discovery_pci(ignore); + } + + void intel_uncore_clear_discovery_tables(void) +diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h +index dff75c98e22f4..dfc237a2b6dfc 100644 +--- a/arch/x86/events/intel/uncore_discovery.h ++++ b/arch/x86/events/intel/uncore_discovery.h +@@ -136,7 +136,7 @@ struct intel_uncore_discovery_type { + u16 num_units; /* number of units */ + }; + +-bool intel_uncore_has_discovery_tables(int *ignore); ++bool uncore_discovery(struct uncore_plat_init *init); + void intel_uncore_clear_discovery_tables(void); + void intel_uncore_generic_uncore_cpu_init(void); + int intel_uncore_generic_uncore_pci_init(void); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt b/SPECS/kernel-rt/0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt new file mode 100644 index 000000000..a1f56f4d3 --- /dev/null +++ b/SPECS/kernel-rt/0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt @@ -0,0 +1,49 @@ +From 7f70318e388fac188633c893e76bc36ecd1a6df3 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:31 -0700 +Subject: [PATCH 2/6] platform/x86:intel/pmc: Add DMU GUID to Arrow Lake U/H +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Arrow Lake U/H platforms may have multiple GUIDs pointing to the +same telemetry region. Add the second possible GUID to the GUID +list to support the Arrow Lake U/H platforms with this GUID. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-4-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/arl.c | 2 +- + drivers/platform/x86/intel/pmc/core.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c +index cc05a168c3721..c0698ef35df89 100644 +--- a/drivers/platform/x86/intel/pmc/arl.c ++++ b/drivers/platform/x86/intel/pmc/arl.c +@@ -733,7 +733,7 @@ struct pmc_dev_info arl_pmc_dev = { + .sub_req = pmc_core_pmt_get_lpm_req, + }; + +-static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; ++static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info arl_h_pmc_dev = { + .pci_func = 2, + .dmu_guids = ARL_H_PMT_DMU_GUIDS, +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index 83d6e2e833785..d80257b37ca98 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -283,6 +283,7 @@ enum ppfear_regs { + #define MTL_PMT_DMU_DIE_C6_OFFSET 15 + #define MTL_PMT_DMU_GUID 0x1A067102 + #define ARL_PMT_DMU_GUID 0x1A06A102 ++#define ARL_H_PMT_DMU_GUID 0x1A06A101 + + #define LNL_PMC_MMIO_REG_LEN 0x2708 + #define LNL_PMC_LTR_OSSE 0x1B88 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core b/SPECS/kernel-rt/0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core deleted file mode 100644 index 52350d592..000000000 --- a/SPECS/kernel-rt/0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core +++ /dev/null @@ -1,49 +0,0 @@ -From 589b198fa391f75259fc1e706cf47783651c6463 Mon Sep 17 00:00:00 2001 -From: Xi Pardee -Date: Tue, 26 Aug 2025 11:39:42 -0700 -Subject: [PATCH 2/2] platform/x86/intel/pmc: Add Wildcat Lake support to Intel - PMC SSRAM Telemetry -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add Wildcat Lake support to Intel PMC SSRAM Telemetry driver. - -Signed-off-by: Xi Pardee -Link: https://lore.kernel.org/r/20250826183946.802684-1-xi.pardee@linux.intel.com -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - drivers/platform/x86/intel/pmc/core.h | 3 +++ - drivers/platform/x86/intel/pmc/ssram_telemetry.c | 1 + - 2 files changed, 4 insertions(+) - -diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h -index 9d54eef6c921..47101f0dd09c 100644 ---- a/drivers/platform/x86/intel/pmc/core.h -+++ b/drivers/platform/x86/intel/pmc/core.h -@@ -310,6 +310,9 @@ enum ppfear_regs { - #define PMC_DEVID_PTL_PCDH 0xe37f - #define PMC_DEVID_PTL_PCDP 0xe47f - -+/* WCL */ -+#define PMC_DEVID_WCL_PCDN 0x4d7f -+ - /* ARL */ - #define PMC_DEVID_ARL_SOCM 0x777f - #define PMC_DEVID_ARL_SOCS 0xae7f -diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c -index 93579152188e..03fad9331fc0 100644 ---- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c -+++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c -@@ -190,6 +190,7 @@ static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) }, -+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) }, - { } - }; - MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-thermal-intel-int340x-Add-support-for-power-slider.thermal b/SPECS/kernel-rt/0002-thermal-intel-int340x-Add-support-for-power-slider.thermal deleted file mode 100644 index f58293d38..000000000 --- a/SPECS/kernel-rt/0002-thermal-intel-int340x-Add-support-for-power-slider.thermal +++ /dev/null @@ -1,339 +0,0 @@ -From 36bdbac0744bddcabf7a59dcab04e3169ea7c078 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:12 -0700 -Subject: [PATCH 02/11] thermal: intel: int340x: Add support for power slider - -Add support for system wide energy performance preference using a SoC -slider interface defined via processor thermal PCI device MMIO space. - -Using Linux platform-profile class API, register a new platform profile. -Provide three platform power profile choices: -"performance", "balanced" and "low-power". - -Profile sysfs is located at: -/sys/class/platform-profile/platform-profile-* -where attribute "name" is presented as "SoC Power Slider". - -At boot by default the slider is set to balanced mode. This profile is -changed by user space based on user preference via power profile daemon -or directly writing to the "profile" sysfs attribute. - -Add a CPU model specific processor thermal device feature -PROC_THERMAL_FEATURE_SOC_POWER_SLIDER. When enabled for a CPU model, -slider interface is registered. - -During system suspend callback save slider register and restore during -resume callback. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-2-srinivas.pandruvada@linux.intel.com -[ rjw: Removal of redundant outer parens from one expression ] -Signed-off-by: Rafael J. Wysocki ---- - drivers/thermal/intel/int340x_thermal/Kconfig | 1 + - .../thermal/intel/int340x_thermal/Makefile | 1 + - .../processor_thermal_device.c | 20 ++ - .../processor_thermal_device.h | 6 + - .../processor_thermal_soc_slider.c | 194 ++++++++++++++++++ - 5 files changed, 222 insertions(+) - create mode 100644 drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c - -diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig -index 4c699f0896b5..4ced7bdcd62c 100644 ---- a/drivers/thermal/intel/int340x_thermal/Kconfig -+++ b/drivers/thermal/intel/int340x_thermal/Kconfig -@@ -12,6 +12,7 @@ config INT340X_THERMAL - select ACPI_THERMAL_LIB - select INTEL_SOC_DTS_IOSF_CORE - select INTEL_TCC -+ select ACPI_PLATFORM_PROFILE - select PROC_THERMAL_MMIO_RAPL if POWERCAP - help - Newer laptops and tablets that use ACPI may have thermal sensors and -diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile -index 184318d1792b..436be34b21a9 100644 ---- a/drivers/thermal/intel/int340x_thermal/Makefile -+++ b/drivers/thermal/intel/int340x_thermal/Makefile -@@ -14,5 +14,6 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o - obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o - obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o - obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_power_floor.o -+obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_soc_slider.o - obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o - obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c -index 29fcece48cad..48e7849d4816 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c -@@ -338,10 +338,17 @@ static int tcc_offset_save = -1; - - int proc_thermal_suspend(struct device *dev) - { -+ struct proc_thermal_device *proc_dev; -+ - tcc_offset_save = intel_tcc_get_offset(-1); - if (tcc_offset_save < 0) - dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save); - -+ proc_dev = dev_get_drvdata(dev); -+ -+ if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) -+ proc_thermal_soc_power_slider_suspend(proc_dev); -+ - return 0; - } - EXPORT_SYMBOL_GPL(proc_thermal_suspend); -@@ -357,6 +364,9 @@ int proc_thermal_resume(struct device *dev) - if (tcc_offset_save >= 0) - intel_tcc_set_offset(-1, tcc_offset_save); - -+ if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) -+ proc_thermal_soc_power_slider_resume(proc_dev); -+ - return 0; - } - EXPORT_SYMBOL_GPL(proc_thermal_resume); -@@ -432,8 +442,18 @@ int proc_thermal_mmio_add(struct pci_dev *pdev, - } - } - -+ if (feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) { -+ ret = proc_thermal_soc_power_slider_add(pdev, proc_priv); -+ if (ret) { -+ dev_info(&pdev->dev, "failed to add soc power efficiency slider\n"); -+ goto err_rem_wlt; -+ } -+ } -+ - return 0; - -+err_rem_wlt: -+ proc_thermal_wt_hint_remove(pdev); - err_rem_rfim: - proc_thermal_rfim_remove(pdev); - err_rem_ptc: -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h -index 49398794124a..30760475102f 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h -@@ -69,6 +69,7 @@ struct rapl_mmio_regs { - #define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40 - #define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80 - #define PROC_THERMAL_FEATURE_PTC 0x100 -+#define PROC_THERMAL_FEATURE_SOC_POWER_SLIDER 0x200 - - #if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL) - int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); -@@ -127,4 +128,9 @@ int proc_thermal_mmio_add(struct pci_dev *pdev, - void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); - int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); - void proc_thermal_ptc_remove(struct pci_dev *pdev); -+ -+int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); -+void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv); -+void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv); -+ - #endif -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -new file mode 100644 -index 000000000000..57782a63b9b5 ---- /dev/null -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -0,0 +1,194 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Processor Thermal Device Interface for Reading and Writing -+ * SoC Power Slider Values from User Space. -+ * -+ * Operation: -+ * The SOC_EFFICIENCY_SLIDER_0_0_0_MCHBAR register is accessed -+ * using the MMIO (Memory-Mapped I/O) interface with an MMIO offset of 0x5B38. -+ * Although this register is 64 bits wide, only bits 7:0 are used, -+ * and the other bits remain unchanged. -+ * -+ * Bit definitions -+ * -+ * Bits 2:0 (Slider value): -+ * The SoC optimizer slider value indicates the system wide energy performance -+ * hint. The slider has no specific units and ranges from 0 (highest -+ * performance) to 6 (highest energy efficiency). Value of 7 is reserved. -+ * Bits 3 : Reserved -+ * Bits 6:4 (Offset) -+ * Offset allows the SoC to automatically switch slider position in range -+ * [slider value (bits 2:0) + offset] to improve power efficiency based on -+ * internal SoC algorithms. -+ * Bit 7 (Enable): -+ * If this bit is set, the SoC Optimization sliders will be processed by the -+ * SoC firmware. -+ * -+ * Copyright (c) 2025, Intel Corporation. -+ */ -+ -+#include -+#include -+#include -+#include "processor_thermal_device.h" -+ -+#define SOC_POWER_SLIDER_OFFSET 0x5B38 -+ -+enum power_slider_preference { -+ SOC_POWER_SLIDER_PERFORMANCE, -+ SOC_POWER_SLIDER_BALANCE, -+ SOC_POWER_SLIDER_POWERSAVE, -+}; -+ -+#define SOC_SLIDER_VALUE_MINIMUM 0x00 -+#define SOC_SLIDER_VALUE_BALANCE 0x03 -+#define SOC_SLIDER_VALUE_MAXIMUM 0x06 -+ -+#define SLIDER_MASK GENMASK_ULL(2, 0) -+#define SLIDER_ENABLE_BIT 7 -+ -+static u8 slider_values[] = { -+ [SOC_POWER_SLIDER_PERFORMANCE] = SOC_SLIDER_VALUE_MINIMUM, -+ [SOC_POWER_SLIDER_BALANCE] = SOC_SLIDER_VALUE_BALANCE, -+ [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM, -+}; -+ -+/* Convert from platform power profile option to SoC slider value */ -+static int convert_profile_to_power_slider(enum platform_profile_option profile) -+{ -+ switch (profile) { -+ case PLATFORM_PROFILE_LOW_POWER: -+ return slider_values[SOC_POWER_SLIDER_POWERSAVE]; -+ case PLATFORM_PROFILE_BALANCED: -+ return slider_values[SOC_POWER_SLIDER_BALANCE]; -+ case PLATFORM_PROFILE_PERFORMANCE: -+ return slider_values[SOC_POWER_SLIDER_PERFORMANCE]; -+ default: -+ break; -+ } -+ -+ return -EOPNOTSUPP; -+} -+ -+/* Convert to platform power profile option from SoC slider values */ -+static int convert_power_slider_to_profile(u8 slider) -+{ -+ if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE]) -+ return PLATFORM_PROFILE_PERFORMANCE; -+ if (slider == slider_values[SOC_POWER_SLIDER_BALANCE]) -+ return PLATFORM_PROFILE_BALANCED; -+ if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE]) -+ return PLATFORM_PROFILE_LOW_POWER; -+ -+ return -EOPNOTSUPP; -+} -+ -+static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv) -+{ -+ return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); -+} -+ -+static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val) -+{ -+ writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); -+} -+ -+static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider) -+{ -+ u64 val; -+ -+ val = read_soc_slider(proc_priv); -+ val &= ~SLIDER_MASK; -+ val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT); -+ write_soc_slider(proc_priv, val); -+} -+ -+/* profile get/set callbacks are called with a profile lock, so no need for local locks */ -+ -+static int power_slider_platform_profile_set(struct device *dev, -+ enum platform_profile_option profile) -+{ -+ struct proc_thermal_device *proc_priv; -+ int slider; -+ -+ proc_priv = dev_get_drvdata(dev); -+ if (!proc_priv) -+ return -EOPNOTSUPP; -+ -+ slider = convert_profile_to_power_slider(profile); -+ if (slider < 0) -+ return slider; -+ -+ set_soc_power_profile(proc_priv, slider); -+ -+ return 0; -+} -+ -+static int power_slider_platform_profile_get(struct device *dev, -+ enum platform_profile_option *profile) -+{ -+ struct proc_thermal_device *proc_priv; -+ int slider, ret; -+ u64 val; -+ -+ proc_priv = dev_get_drvdata(dev); -+ if (!proc_priv) -+ return -EOPNOTSUPP; -+ -+ val = read_soc_slider(proc_priv); -+ slider = FIELD_GET(SLIDER_MASK, val); -+ -+ ret = convert_power_slider_to_profile(slider); -+ if (ret < 0) -+ return ret; -+ -+ *profile = ret; -+ -+ return 0; -+} -+ -+static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices) -+{ -+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices); -+ set_bit(PLATFORM_PROFILE_BALANCED, choices); -+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); -+ -+ return 0; -+} -+ -+static const struct platform_profile_ops power_slider_platform_profile_ops = { -+ .probe = power_slider_platform_profile_probe, -+ .profile_get = power_slider_platform_profile_get, -+ .profile_set = power_slider_platform_profile_set, -+}; -+ -+int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) -+{ -+ struct device *ppdev; -+ -+ set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]); -+ -+ ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv, -+ &power_slider_platform_profile_ops); -+ -+ return PTR_ERR_OR_ZERO(ppdev); -+} -+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL"); -+ -+static u64 soc_slider_save; -+ -+void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv) -+{ -+ soc_slider_save = read_soc_slider(proc_priv); -+} -+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL"); -+ -+void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv) -+{ -+ write_soc_slider(proc_priv, soc_slider_save); -+} -+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL"); -+ -+MODULE_IMPORT_NS("INT340X_THERMAL"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Processor Thermal Power Slider Interface"); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt b/SPECS/kernel-rt/0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt deleted file mode 100644 index 905ec964c..000000000 --- a/SPECS/kernel-rt/0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt +++ /dev/null @@ -1,109 +0,0 @@ -From e58ae10aa522b056caa9a802693b14e557be015d Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Fri, 11 Aug 2023 12:59:09 +0300 -Subject: [PATCH 2/4] thunderbolt: Make XDomain lane bonding comply with the - USB4 v2 spec - -The USB4 v2 Inter-Domain spec "unified" the lane bonding flow so that -when the other end (with higher UUID) is not yet set the target link -width accordingly it is expected to reply with ERROR_NOT_READY. -Implement this for Linux. - -Signed-off-by: Mika Westerberg ---- - drivers/thunderbolt/xdomain.c | 65 +++++++++++++++++++++++++---------- - 1 file changed, 47 insertions(+), 18 deletions(-) - -diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c -index b0630e6d9472..88e5bcb6640e 100644 ---- a/drivers/thunderbolt/xdomain.c -+++ b/drivers/thunderbolt/xdomain.c -@@ -535,29 +535,19 @@ static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route, - } - - static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl, -- struct tb_xdomain *xd, u8 sequence) -+ struct tb_xdomain *xd, u8 sequence, -+ u8 slw, u8 sls, u8 tls, u8 tlw) - { - struct tb_xdp_link_state_status_response res; -- struct tb_port *port = tb_xdomain_downstream_port(xd); -- u32 val[2]; -- int ret; - - memset(&res, 0, sizeof(res)); - tb_xdp_fill_header(&res.hdr, xd->route, sequence, - LINK_STATE_STATUS_RESPONSE, sizeof(res)); - -- ret = tb_port_read(port, val, TB_CFG_PORT, -- port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val)); -- if (ret) -- return ret; -- -- res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> -- LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; -- res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> -- LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; -- res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; -- res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> -- LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; -+ res.slw = slw; -+ res.sls = sls; -+ res.tls = tls; -+ res.tlw = tlw; - - return __tb_xdomain_response(ctl, &res, sizeof(res), - TB_CFG_PKG_XDOMAIN_RESP); -@@ -802,8 +792,47 @@ static void tb_xdp_handle_request(struct work_struct *work) - route); - - if (xd) { -- ret = tb_xdp_link_state_status_response(tb, ctl, xd, -- sequence); -+ struct tb_port *port = tb_xdomain_downstream_port(xd); -+ u8 slw, sls, tls, tlw; -+ u32 val[2]; -+ -+ /* -+ * Read the adapter supported and target widths -+ * and speeds. -+ */ -+ ret = tb_port_read(port, val, TB_CFG_PORT, -+ port->cap_phy + LANE_ADP_CS_0, -+ ARRAY_SIZE(val)); -+ if (ret) -+ break; -+ -+ slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> -+ LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; -+ sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> -+ LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; -+ tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; -+ tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> -+ LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; -+ -+ /* -+ * When we have higher UUID, we are supposed to -+ * return ERROR_NOT_READY if the tlw is not yet -+ * set according to the Inter-Domain spec for -+ * USB4 v2. -+ */ -+ if (xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH && -+ xd->target_link_width && -+ xd->target_link_width != tlw) { -+ tb_dbg(tb, "%llx: target link width not yet set %#x != %#x\n", -+ route, tlw, xd->target_link_width); -+ tb_xdp_error_response(ctl, route, sequence, -+ ERROR_NOT_READY); -+ } else { -+ tb_dbg(tb, "%llx: replying with target link width set to %#x\n", -+ route, tlw); -+ ret = tb_xdp_link_state_status_response(tb, ctl, -+ xd, sequence, slw, sls, tls, tlw); -+ } - } else { - tb_xdp_error_response(ctl, route, sequence, - ERROR_NOT_READY); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo b/SPECS/kernel-rt/0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo new file mode 100644 index 000000000..6754a7165 --- /dev/null +++ b/SPECS/kernel-rt/0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo @@ -0,0 +1,40 @@ +From 353fba46fd0866f0bbbca8c86c315f54baa2e3db Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Thu, 14 Aug 2025 23:29:57 -0400 +Subject: [PATCH 02/21] tools/power turbostat: Add Wildcat Lake and Nova Lake + support + +Treat Wildcat Lake and Nova Lake (and Panther Lake) +the same as Lunar Lake, for now. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 1b5ca2f4e92ff..7c24c2f9a0752 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -1210,6 +1210,9 @@ static const struct platform_data turbostat_pdata[] = { + { INTEL_ARROWLAKE, &adl_features }, + { INTEL_LUNARLAKE_M, &lnl_features }, + { INTEL_PANTHERLAKE_L, &lnl_features }, ++ { INTEL_NOVALAKE, &lnl_features }, ++ { INTEL_NOVALAKE_L, &lnl_features }, ++ { INTEL_WILDCATLAKE_L, &lnl_features }, + { INTEL_ATOM_SILVERMONT, &slv_features }, + { INTEL_ATOM_SILVERMONT_D, &slvd_features }, + { INTEL_ATOM_AIRMONT, &amt_features }, +@@ -10126,7 +10129,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.09.09 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.10.18 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac b/SPECS/kernel-rt/0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac new file mode 100644 index 000000000..b1093cd11 --- /dev/null +++ b/SPECS/kernel-rt/0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac @@ -0,0 +1,163 @@ +From 5b64d5870c761a47c148280951e13ffd4ac6d00b Mon Sep 17 00:00:00 2001 +From: Qiuxu Zhuo +Date: Thu, 13 Nov 2025 13:01:01 +0800 +Subject: [PATCH 3/3] EDAC/igen6: Fix masks of {MCHBAR, TOM, TOUUD} registers + +The masks used to retrieve base addresses from {MCHBAR, TOM, TOUUD} +registers are CPU model-specific. Currently, igen6_edac uses the +same masks for all CPUs, which is incorrect. Add three new fields +to structure res_config to make these masks CPU model-specific and +configure them properly. + +Fixes: 0b7338b27e82 ("EDAC/igen6: Add Intel Tiger Lake SoC support") +Signed-off-by: Qiuxu Zhuo +--- + drivers/edac/igen6_edac.c | 41 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 37 insertions(+), 4 deletions(-) + +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 3e9d0118e3958..5f851c772a321 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -87,7 +87,6 @@ + /* Host MMIO base address */ + #define MCHBAR_OFFSET 0x48 + #define MCHBAR_EN BIT_ULL(0) +-#define MCHBAR_BASE(v) (GET_BITFIELD(v, 16, 38) << 16) + #define MCHBAR_SIZE 0x10000 + + /* Parameters for the channel decode stage */ +@@ -129,6 +128,12 @@ static struct res_config { + bool machine_check; + /* The number of present memory controllers. */ + int num_imc; ++ /* Host MMIO configuration register */ ++ u64 reg_mchbar_mask; ++ /* Top of Memory */ ++ u64 reg_tom_mask; ++ /* Top of Upper Usable DRAM */ ++ u64 reg_touud_mask; + u32 imc_base; + u32 cmf_base; + u32 cmf_size; +@@ -315,7 +320,8 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar) + return -ENODEV; + } + +- *mchbar = MCHBAR_BASE(u.v); ++ *mchbar = u.v & res_cfg->reg_mchbar_mask; ++ edac_dbg(2, "MCHBAR 0x%llx (reg 0x%llx)\n", *mchbar, u.v); + + return 0; + } +@@ -496,6 +502,9 @@ static u64 rpl_p_err_addr(u64 ecclog) + + static struct res_config ehl_cfg = { + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(38, 16), ++ .reg_tom_mask = GENMASK_ULL(38, 20), ++ .reg_touud_mask = GENMASK_ULL(38, 20), + .imc_base = 0x5000, + .ibecc_base = 0xdc00, + .ibecc_available = ehl_ibecc_available, +@@ -506,6 +515,9 @@ static struct res_config ehl_cfg = { + + static struct res_config icl_cfg = { + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(38, 16), ++ .reg_tom_mask = GENMASK_ULL(38, 20), ++ .reg_touud_mask = GENMASK_ULL(38, 20), + .imc_base = 0x5000, + .ibecc_base = 0xd800, + .ibecc_error_log_offset = 0x170, +@@ -517,6 +529,9 @@ static struct res_config icl_cfg = { + static struct res_config tgl_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(38, 17), ++ .reg_tom_mask = GENMASK_ULL(38, 20), ++ .reg_touud_mask = GENMASK_ULL(38, 20), + .imc_base = 0x5000, + .cmf_base = 0x11000, + .cmf_size = 0x800, +@@ -531,6 +546,9 @@ static struct res_config tgl_cfg = { + static struct res_config adl_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, +@@ -542,6 +560,9 @@ static struct res_config adl_cfg = { + static struct res_config adl_n_cfg = { + .machine_check = true, + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, +@@ -553,6 +574,9 @@ static struct res_config adl_n_cfg = { + static struct res_config rpl_p_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, +@@ -565,6 +589,9 @@ static struct res_config rpl_p_cfg = { + static struct res_config mtl_ps_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, +@@ -576,6 +603,9 @@ static struct res_config mtl_ps_cfg = { + static struct res_config mtl_p_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, +@@ -587,6 +617,9 @@ static struct res_config mtl_p_cfg = { + static struct res_config wcl_cfg = { + .machine_check = true, + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, +@@ -1216,7 +1249,7 @@ static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) + goto fail; + } + +- igen6_tom = u.v & GENMASK_ULL(38, 20); ++ igen6_tom = u.v & res_cfg->reg_tom_mask; + + if (get_mchbar(pdev, mchbar)) + goto fail; +@@ -1227,7 +1260,7 @@ static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) + else if (pci_read_config_dword(pdev, TOUUD_OFFSET + 4, &u.v_hi)) + edac_dbg(2, "Failed to read upper TOUUD\n"); + else +- igen6_touud = u.v & GENMASK_ULL(38, 20); ++ igen6_touud = u.v & res_cfg->reg_touud_mask; + #endif + + return 0; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac b/SPECS/kernel-rt/0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac deleted file mode 100644 index 521c8c95b..000000000 --- a/SPECS/kernel-rt/0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac +++ /dev/null @@ -1,93 +0,0 @@ -From 7fe5d4b4ea29feb773895fd8b007774e2682bba0 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Wed, 23 Jul 2025 12:47:56 +0800 -Subject: [PATCH 03/13] EDAC/skx_common: Move mc_mapping to be a field inside - struct skx_imc - -The mc_mapping and imc fields of struct skx_dev have the same size, -NUM_IMC. Move mc_mapping to be a field inside struct skx_imc to prepare -for making the imc array of memory controller instances a flexible array. - -No functional changes intended. - -Suggested-by: Tony Luck -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 8 ++++---- - drivers/edac/skx_common.h | 20 ++++++++++---------- - 2 files changed, 14 insertions(+), 14 deletions(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index cc7d36cf7f3b..3f6a074d685c 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -131,7 +131,7 @@ static void skx_init_mc_mapping(struct skx_dev *d) - * EDAC driver. - */ - for (int i = 0; i < NUM_IMC; i++) -- d->mc_mapping[i] = i; -+ d->imc[i].mc_mapping = i; - } - - void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc) -@@ -139,16 +139,16 @@ void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc) - edac_dbg(0, "Set the mapping of mc phy idx to logical idx: %02d -> %02d\n", - pmc, lmc); - -- d->mc_mapping[pmc] = lmc; -+ d->imc[pmc].mc_mapping = lmc; - } - EXPORT_SYMBOL_GPL(skx_set_mc_mapping); - - static u8 skx_get_mc_mapping(struct skx_dev *d, u8 pmc) - { - edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n", -- pmc, d->mc_mapping[pmc]); -+ pmc, d->imc[pmc].mc_mapping); - -- return d->mc_mapping[pmc]; -+ return d->imc[pmc].mc_mapping; - } - - static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index 3f6007a97267..95d61d23f89e 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -135,16 +135,6 @@ struct skx_dev { - struct pci_dev *pcu_cr3; /* for HBM memory detection */ - u32 mcroute; - int num_imc; -- /* -- * Some server BIOS may hide certain memory controllers, and the -- * EDAC driver skips those hidden memory controllers. However, the -- * ADXL still decodes memory error address using physical memory -- * controller indices. The mapping table is used to convert the -- * physical indices (reported by ADXL) to the logical indices -- * (used the EDAC driver) of present memory controllers during the -- * error handling process. -- */ -- u8 mc_mapping[NUM_IMC]; - struct skx_imc { - struct mem_ctl_info *mci; - struct pci_dev *mdev; /* for i10nm CPU */ -@@ -156,6 +146,16 @@ struct skx_dev { - u8 mc; /* system wide mc# */ - u8 lmc; /* socket relative mc# */ - u8 src_id; -+ /* -+ * Some server BIOS may hide certain memory controllers, and the -+ * EDAC driver skips those hidden memory controllers. However, the -+ * ADXL still decodes memory error address using physical memory -+ * controller indices. The mapping table is used to convert the -+ * physical indices (reported by ADXL) to the logical indices -+ * (used the EDAC driver) of present memory controllers during the -+ * error handling process. -+ */ -+ u8 mc_mapping; - struct skx_channel { - struct pci_dev *cdev; - struct pci_dev *edev; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi b/SPECS/kernel-rt/0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi new file mode 100644 index 000000000..ef41cff6c --- /dev/null +++ b/SPECS/kernel-rt/0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi @@ -0,0 +1,68 @@ +From d9ee7982e5ed07c369987e8944cb49a3eda82f1f Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Fri, 17 Nov 2023 21:34:27 -0800 +Subject: [PATCH 03/44] KVM: VMX: Disable FRED if FRED consistency checks fail + +Do not virtualize FRED if FRED consistency checks fail. + +Either on broken hardware, or when run KVM on top of another hypervisor +before the underlying hypervisor implements nested FRED correctly. + +Suggested-by: Chao Gao +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Reviewed-by: Chao Gao +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v5: +* Drop the cpu_feature_enabled() in cpu_has_vmx_fred() (Sean). +* Add TB from Xuelian Guo. + +Change in v4: +* Call out the reason why not check FRED VM-exit controls in + cpu_has_vmx_fred() (Chao Gao). +--- + arch/x86/kvm/vmx/capabilities.h | 10 ++++++++++ + arch/x86/kvm/vmx/vmx.c | 3 +++ + 2 files changed, 13 insertions(+) + +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index 6bd67c40ca3b8..651507627ef32 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -405,6 +405,16 @@ static inline bool vmx_pebs_supported(void) + return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept; + } + ++static inline bool cpu_has_vmx_fred(void) ++{ ++ /* ++ * setup_vmcs_config() guarantees FRED VM-entry/exit controls ++ * are either all set or none. So, no need to check FRED VM-exit ++ * controls. ++ */ ++ return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED); ++} ++ + static inline bool cpu_has_notify_vmexit(void) + { + return vmcs_config.cpu_based_2nd_exec_ctrl & +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index fbeb85c4673e5..90cbc73b21288 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -8009,6 +8009,9 @@ static __init void vmx_set_cpu_caps(void) + kvm_cpu_cap_check_and_set(X86_FEATURE_DTES64); + } + ++ if (!cpu_has_vmx_fred()) ++ kvm_cpu_cap_clear(X86_FEATURE_FRED); ++ + if (!enable_pmu) + kvm_cpu_cap_clear(X86_FEATURE_PDCM); + kvm_caps.supported_perf_cap = vmx_get_perf_capabilities(); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi b/SPECS/kernel-rt/0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi deleted file mode 100644 index cdde05251..000000000 --- a/SPECS/kernel-rt/0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi +++ /dev/null @@ -1,106 +0,0 @@ -From ef2e33eab5f2cfc9e295f0dca5009d67e7f7a642 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Wed, 30 Aug 2023 23:55:54 -0700 -Subject: [PATCH 03/44] KVM: VMX: Initialize VM entry/exit FRED controls in - vmcs_config - -Setup VM entry/exit FRED controls in the global vmcs_config for proper -FRED VMCS fields management: - 1) load guest FRED state upon VM entry. - 2) save guest FRED state during VM exit. - 3) load host FRED state during VM exit. - -Also add FRED control consistency checks to the existing VM entry/exit -consistency check framework. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo -Reviewed-by: Chao Gao ---- - -Change in v5: -* Remove the pair VM_ENTRY_LOAD_IA32_FRED/VM_EXIT_ACTIVATE_SECONDARY_CONTROLS, - since the secondary VM exit controls are unconditionally enabled anyway, and - there are features other than FRED needing it (Chao Gao). -* Add TB from Xuelian Guo. - -Change in v4: -* Do VM exit/entry consistency checks using the new macro from Sean - Christopherson. - -Changes in v3: -* Add FRED control consistency checks to the existing VM entry/exit - consistency check framework (Sean Christopherson). -* Just do the unnecessary FRED state load/store on every VM entry/exit - (Sean Christopherson). ---- - arch/x86/include/asm/vmx.h | 4 ++++ - arch/x86/kvm/vmx/vmx.c | 2 ++ - arch/x86/kvm/vmx/vmx.h | 7 +++++-- - 3 files changed, 11 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index 5b53b8da4ce0..ab70f52798d4 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -110,6 +110,9 @@ - #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 - #define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 - -+#define SECONDARY_VM_EXIT_SAVE_IA32_FRED BIT_ULL(0) -+#define SECONDARY_VM_EXIT_LOAD_IA32_FRED BIT_ULL(1) -+ - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff - - #define VM_ENTRY_LOAD_DEBUG_CONTROLS 0x00000004 -@@ -123,6 +126,7 @@ - #define VM_ENTRY_PT_CONCEAL_PIP 0x00020000 - #define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 - #define VM_ENTRY_LOAD_CET_STATE 0x00100000 -+#define VM_ENTRY_LOAD_IA32_FRED 0x00800000 - - #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index bad778acf9b5..aa23ecf18cda 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2625,6 +2625,8 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - u32 entry_control; - u64 exit_control; - } const vmcs_entry_exit2_pairs[] = { -+ { VM_ENTRY_LOAD_IA32_FRED, -+ SECONDARY_VM_EXIT_SAVE_IA32_FRED | SECONDARY_VM_EXIT_LOAD_IA32_FRED }, - }; - - memset(vmcs_conf, 0, sizeof(*vmcs_conf)); -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 47395cb38d88..50656aa3f57a 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -488,7 +488,8 @@ static inline u8 vmx_get_rvi(void) - VM_ENTRY_LOAD_BNDCFGS | \ - VM_ENTRY_PT_CONCEAL_PIP | \ - VM_ENTRY_LOAD_IA32_RTIT_CTL | \ -- VM_ENTRY_LOAD_CET_STATE) -+ VM_ENTRY_LOAD_CET_STATE | \ -+ VM_ENTRY_LOAD_IA32_FRED) - - #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS \ - (VM_EXIT_SAVE_DEBUG_CONTROLS | \ -@@ -516,7 +517,9 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) - - #define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) --#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) -+#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS \ -+ (SECONDARY_VM_EXIT_SAVE_IA32_FRED | \ -+ SECONDARY_VM_EXIT_LOAD_IA32_FRED) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ - (PIN_BASED_EXT_INTR_MASK | \ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet b/SPECS/kernel-rt/0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet deleted file mode 100644 index ca4e96f2b..000000000 --- a/SPECS/kernel-rt/0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet +++ /dev/null @@ -1,96 +0,0 @@ -From 7152d3ef6b9033f8e8801694f93b04a4b161e194 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:33 -0700 -Subject: [PATCH] KVM: x86: Add kvm_msr_{read,write}() helpers - -Wrap __kvm_{get,set}_msr() into two new helpers for KVM usage and use the -helpers to replace existing usage of the raw functions. -kvm_msr_{read,write}() are KVM-internal helpers, i.e. used when KVM needs -to get/set a MSR value for emulating CPU behavior, i.e., host_initiated == -%true in the helpers. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 3 ++- - arch/x86/kvm/cpuid.c | 2 +- - arch/x86/kvm/x86.c | 16 +++++++++++++--- - 3 files changed, 16 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index b3fff43e0510..8ab2e9f5ff60 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -2158,9 +2158,10 @@ void kvm_enable_efer_bits(u64); - bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer); - int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); - int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); --int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiated); - int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); - int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); -+int kvm_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); -+int kvm_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); - int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu); - int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg); - int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu); -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index cc16e28bfab2..5e43a4999a4b 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -2003,7 +2003,7 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, - if (function == 7 && index == 0) { - u64 data; - if ((*ebx & (feature_bit(RTM) | feature_bit(HLE))) && -- !__kvm_get_msr(vcpu, MSR_IA32_TSX_CTRL, &data, true) && -+ !kvm_msr_read(vcpu, MSR_IA32_TSX_CTRL, &data) && - (data & TSX_CTRL_CPUID_CLEAR)) - *ebx &= ~(feature_bit(RTM) | feature_bit(HLE)); - } else if (function == 0x80000007) { -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 9bfc44972d26..3f6cd49e172f 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1909,8 +1909,8 @@ static int kvm_set_msr_ignored_check(struct kvm_vcpu *vcpu, - * Returns 0 on success, non-0 otherwise. - * Assumes vcpu_load() was already called. - */ --int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, -- bool host_initiated) -+static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, -+ bool host_initiated) - { - struct msr_data msr; - int ret; -@@ -1936,6 +1936,16 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, - return ret; - } - -+int kvm_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) -+{ -+ return __kvm_set_msr(vcpu, index, data, true); -+} -+ -+int kvm_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+{ -+ return __kvm_get_msr(vcpu, index, data, true); -+} -+ - static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu, - u32 index, u64 *data, bool host_initiated) - { -@@ -12527,7 +12537,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - MSR_IA32_MISC_ENABLE_BTS_UNAVAIL; - - __kvm_set_xcr(vcpu, 0, XFEATURE_MASK_FP); -- __kvm_set_msr(vcpu, MSR_IA32_XSS, 0, true); -+ kvm_msr_write(vcpu, MSR_IA32_XSS, 0); - } - - /* All GPRs except RDX (handled below) are zeroed on RESET/INIT. */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss b/SPECS/kernel-rt/0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss deleted file mode 100644 index 4e9e86f02..000000000 --- a/SPECS/kernel-rt/0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss +++ /dev/null @@ -1,148 +0,0 @@ -From 67beb7e6e71fcc0220699ead6e277a145fd4c1ed Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Fri, 4 Jan 2019 17:30:12 +0300 -Subject: [PATCH 03/16] PCI: Add sysfs attribute for disabling PCIe link to - downstream component - -PCIe root and downstream ports have link control register that can be -used disable the link from software. This can be useful for instance -when performing "software" hotplug on systems that do not support real -PCIe/ACPI hotplug. - -For example when used with FPGA card we can burn a new FPGA image -without need to reboot the system. - -First we remove the FGPA device from Linux PCI stack: - - # echo 1 > /sys/bus/pci/devices/0000:00:01.1/0000:02:00.0/remove - -Then we disable the link: - - # echo 1 > /sys/bus/pci/devices/0000:00:01.1/link_disable - -By doing this we prevent the kernel from accessing the hardware while we -burn the new FPGA image. Once the new FPGA is burned we can re-enable -the link and rescan the new and possibly different device: - - # echo 0 > /sys/bus/pci/devices/0000:00:01.1/link_disable - # echo 1 > /sys/bus/pci/devices/0000:00:01.1/rescan - -Signed-off-by: Mika Westerberg ---- - Documentation/ABI/testing/sysfs-bus-pci | 8 +++ - drivers/pci/pci-sysfs.c | 65 ++++++++++++++++++++++++- - 2 files changed, 72 insertions(+), 1 deletion(-) - -diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci -index 69f952fffec7..29ece4a2d5f9 100644 ---- a/Documentation/ABI/testing/sysfs-bus-pci -+++ b/Documentation/ABI/testing/sysfs-bus-pci -@@ -394,6 +394,14 @@ Description: - This is similar to /sys/bus/pci/drivers_autoprobe, but - affects only the VFs associated with a specific PF. - -+What: /sys/bus/pci/devices/.../link_disable -+Date: September 2019 -+Contact: Mika Westerberg -+Description: -+ PCIe root and downstream ports have this attribute. Writing -+ 1 causes the link to downstream component be disabled. -+ Re-enabling the link happens by writing 0 instead. -+ - What: /sys/bus/pci/devices/.../p2pmem/size - Date: November 2017 - Contact: Logan Gunthorpe -diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c -index 5eea14c1f7f5..da48d0bbfd39 100644 ---- a/drivers/pci/pci-sysfs.c -+++ b/drivers/pci/pci-sysfs.c -@@ -239,6 +239,56 @@ static ssize_t current_link_width_show(struct device *dev, - } - static DEVICE_ATTR_RO(current_link_width); - -+static ssize_t link_disable_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pci_dev *pci_dev = to_pci_dev(dev); -+ u16 linkctl; -+ int ret; -+ -+ ret = pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, &linkctl); -+ if (ret) -+ return -EINVAL; -+ -+ return sprintf(buf, "%d\n", !!(linkctl & PCI_EXP_LNKCTL_LD)); -+} -+ -+static ssize_t link_disable_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct pci_dev *pci_dev = to_pci_dev(dev); -+ u16 linkctl; -+ bool disable; -+ int ret; -+ -+ ret = kstrtobool(buf, &disable); -+ if (ret) -+ return ret; -+ -+ ret = pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, &linkctl); -+ if (ret) -+ return -EINVAL; -+ -+ if (disable) { -+ if (linkctl & PCI_EXP_LNKCTL_LD) -+ goto out; -+ linkctl |= PCI_EXP_LNKCTL_LD; -+ } else { -+ if (!(linkctl & PCI_EXP_LNKCTL_LD)) -+ goto out; -+ linkctl &= ~PCI_EXP_LNKCTL_LD; -+ } -+ -+ ret = pcie_capability_write_word(pci_dev, PCI_EXP_LNKCTL, linkctl); -+ if (ret) -+ return ret; -+ -+out: -+ return count; -+} -+static DEVICE_ATTR_RW(link_disable); -+ - static ssize_t secondary_bus_number_show(struct device *dev, - struct device_attribute *attr, - char *buf) -@@ -660,6 +710,7 @@ static struct attribute *pcie_dev_attrs[] = { - &dev_attr_current_link_width.attr, - &dev_attr_max_link_width.attr, - &dev_attr_max_link_speed.attr, -+ &dev_attr_link_disable.attr, - NULL, - }; - -@@ -1749,8 +1800,20 @@ static umode_t pcie_dev_attrs_are_visible(struct kobject *kobj, - struct device *dev = kobj_to_dev(kobj); - struct pci_dev *pdev = to_pci_dev(dev); - -- if (pci_is_pcie(pdev)) -+ if (pci_is_pcie(pdev)) { -+ if (a == &dev_attr_link_disable.attr) { -+ switch (pci_pcie_type(pdev)) { -+ case PCI_EXP_TYPE_ROOT_PORT: -+ case PCI_EXP_TYPE_DOWNSTREAM: -+ break; -+ -+ default: -+ return 0; -+ } -+ } -+ - return a->mode; -+ } - - return 0; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-bpf-add-btf-register-unregister-API.ethernet b/SPECS/kernel-rt/0003-bpf-add-btf-register-unregister-API.ethernet deleted file mode 100644 index cb414d1b0..000000000 --- a/SPECS/kernel-rt/0003-bpf-add-btf-register-unregister-API.ethernet +++ /dev/null @@ -1,153 +0,0 @@ -From 532613b50ef36ffd757593933f64dde097636f30 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Mon, 14 Jun 2021 09:03:35 +0800 -Subject: [PATCH 03/19] bpf: add btf register/unregister API - -A device driver can register own BTF format buffers into the kernel. -Will be used in downstream patches by mlx5 XDP driver to advertise the -types and populated XDP meta data. - -Signed-off-by: Saeed Mahameed -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - include/linux/btf.h | 8 ++++ - kernel/bpf/btf.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 98 insertions(+) - -diff --git a/include/linux/btf.h b/include/linux/btf.h -index 9eda6b113f9b..4152af147d87 100644 ---- a/include/linux/btf.h -+++ b/include/linux/btf.h -@@ -604,6 +604,8 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type - - return btf_type_is_struct(t); - } -+struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size); -+void btf_unregister(struct btf *btf); - #else - static inline const struct btf_type *btf_type_by_id(const struct btf *btf, - u32 type_id) -@@ -682,5 +684,11 @@ static inline int btf_check_iter_arg(struct btf *btf, const struct btf_type *fun - { - return -EOPNOTSUPP; - } -+static inline struct btf * -+btf_register(const char *name, void *btf_data, u32 btf_data_size) -+{ -+ return NULL; -+} -+static inline void btf_unregister(struct btf *btf) { } - #endif - #endif -diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c -index 64739308902f..1478010a1629 100644 ---- a/kernel/bpf/btf.c -+++ b/kernel/bpf/btf.c -@@ -6391,6 +6391,95 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, - - #endif /* CONFIG_DEBUG_INFO_BTF_MODULES */ - -+/* TODO: reuse btf_parse_raw in btf_parse_module */ -+static struct btf *btf_parse_raw(const char *name, const void *data, unsigned int data_size) -+{ -+ struct btf_verifier_env *env = NULL; -+ struct bpf_verifier_log *log; -+ struct btf *btf = NULL; -+ int err; -+ -+ env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN); -+ if (!env) -+ return ERR_PTR(-ENOMEM); -+ -+ log = &env->log; -+ log->level = BPF_LOG_KERNEL; -+ -+ btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN); -+ if (!btf) { -+ err = -ENOMEM; -+ goto errout; -+ } -+ env->btf = btf; -+ btf->kernel_btf = true; -+ snprintf(btf->name, sizeof(btf->name), "%s", name); -+ -+ btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN); -+ if (!btf->data) { -+ err = -ENOMEM; -+ goto errout; -+ } -+ memcpy(btf->data, data, data_size); -+ btf->data_size = data_size; -+ -+ err = btf_parse_hdr(env); -+ if (err) -+ goto errout; -+ -+ btf->nohdr_data = btf->data + btf->hdr.hdr_len; -+ -+ err = btf_parse_str_sec(env); -+ if (err) -+ goto errout; -+ -+ err = btf_check_all_metas(env); -+ if (err) -+ goto errout; -+ -+ btf_verifier_env_free(env); -+ refcount_set(&btf->refcnt, 1); -+ return btf; -+ -+errout: -+ btf_verifier_env_free(env); -+ if (btf) { -+ kvfree(btf->data); -+ kvfree(btf->types); -+ kfree(btf); -+ } -+ return ERR_PTR(err); -+} -+ -+/* TODO: reuse btf_register in btf_module_notify */ -+struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size) -+{ -+ struct btf *btf; -+ int err; -+ -+ btf = btf_parse_raw(name, btf_data, btf_data_size); -+ if (IS_ERR(btf)) -+ return btf; -+ -+ err = btf_alloc_id(btf); -+ if (err) { -+ btf_free(btf); -+ btf = ERR_PTR(err); -+ } -+ return btf; -+} -+EXPORT_SYMBOL(btf_register); -+ -+void btf_unregister(struct btf *btf) -+{ -+ if (IS_ERR(btf)) -+ return; -+ -+ /* btf_put since btf might be held by user */ -+ btf_put(btf); -+} -+EXPORT_SYMBOL(btf_unregister); -+ - struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog) - { - struct bpf_prog *tgt_prog = prog->aux->dst_prog; -@@ -8153,6 +8242,7 @@ u32 btf_obj_id(const struct btf *btf) - { - return btf->id; - } -+EXPORT_SYMBOL(btf_obj_id); - - bool btf_is_kernel(const struct btf *btf) - { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl b/SPECS/kernel-rt/0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl new file mode 100644 index 000000000..e7d8e5d08 --- /dev/null +++ b/SPECS/kernel-rt/0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl @@ -0,0 +1,65 @@ +From cf5cc639f8231faee39df708cef6edbe4802cb56 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Fri, 7 Nov 2025 20:07:28 +0100 +Subject: [PATCH 03/17] cpuidle: Add sanity check for exit latency and target + residency + +Make __cpuidle_driver_init() fail if the exit latency of one of the +driver's idle states is less than its target residency which would +break cpuidle assumptions. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Artem Bityutskiy +Reviewed-by: Christian Loehle +[ rjw: Changelog fix ] +Link: https://patch.msgid.link/12779486.O9o76ZdvQC@rafael.j.wysocki +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/driver.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c +index 9bbfa594c4425..1c295a93d5829 100644 +--- a/drivers/cpuidle/driver.c ++++ b/drivers/cpuidle/driver.c +@@ -152,7 +152,7 @@ static void cpuidle_setup_broadcast_timer(void *arg) + * __cpuidle_driver_init - initialize the driver's internal data + * @drv: a valid pointer to a struct cpuidle_driver + */ +-static void __cpuidle_driver_init(struct cpuidle_driver *drv) ++static int __cpuidle_driver_init(struct cpuidle_driver *drv) + { + int i; + +@@ -193,7 +193,17 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) + s->exit_latency_ns = 0; + else + s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); ++ ++ /* ++ * Ensure that the exit latency of a CPU idle state does not ++ * exceed its target residency which is assumed in cpuidle in ++ * multiple places. ++ */ ++ if (s->exit_latency_ns > s->target_residency_ns) ++ return -EINVAL; + } ++ ++ return 0; + } + + /** +@@ -223,7 +233,9 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) + if (cpuidle_disabled()) + return -ENODEV; + +- __cpuidle_driver_init(drv); ++ ret = __cpuidle_driver_init(drv); ++ if (ret) ++ return ret; + + ret = __cpuidle_set_driver(drv); + if (ret) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt b/SPECS/kernel-rt/0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt new file mode 100644 index 000000000..c21313475 --- /dev/null +++ b/SPECS/kernel-rt/0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt @@ -0,0 +1,83 @@ +From 5507a6988da65bb3566d330c76261fa9e7823ad4 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Thu, 6 Dec 2018 09:52:20 +0100 +Subject: [PATCH 3/9] drm/i915: Disable tracing points on PREEMPT_RT + +Luca Abeni reported this: +| BUG: scheduling while atomic: kworker/u8:2/15203/0x00000003 +| CPU: 1 PID: 15203 Comm: kworker/u8:2 Not tainted 4.19.1-rt3 #10 +| Call Trace: +| rt_spin_lock+0x3f/0x50 +| gen6_read32+0x45/0x1d0 [i915] +| g4x_get_vblank_counter+0x36/0x40 [i915] +| trace_event_raw_event_i915_pipe_update_start+0x7d/0xf0 [i915] + +The tracing events use trace_intel_pipe_update_start() among other events +use functions acquire spinlock_t locks which are transformed into +sleeping locks on PREEMPT_RT. A few trace points use +intel_get_crtc_scanline(), others use ->get_vblank_counter() wich also +might acquire a sleeping locks on PREEMPT_RT. +At the time the arguments are evaluated within trace point, preemption +is disabled and so the locks must not be acquired on PREEMPT_RT. + +Based on this I don't see any other way than disable trace points on +PREMPT_RT. + +Acked-by: Tvrtko Ursulin +Reported-by: Luca Abeni +Cc: Steven Rostedt +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/display/intel_display_trace.h | 4 ++++ + drivers/gpu/drm/i915/i915_trace.h | 4 ++++ + drivers/gpu/drm/i915/intel_uncore_trace.h | 4 ++++ + 3 files changed, 12 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_trace.h b/drivers/gpu/drm/i915/display/intel_display_trace.h +index 27ebc32cb61a5..a519d94700c36 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_trace.h ++++ b/drivers/gpu/drm/i915/display/intel_display_trace.h +@@ -13,6 +13,10 @@ + #if !defined(__INTEL_DISPLAY_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) + #define __INTEL_DISPLAY_TRACE_H__ + ++#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) ++#define NOTRACE ++#endif ++ + #include + #include + #include +diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h +index 7ed41ce9b7085..6b87ef6005c69 100644 +--- a/drivers/gpu/drm/i915/i915_trace.h ++++ b/drivers/gpu/drm/i915/i915_trace.h +@@ -6,6 +6,10 @@ + #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + #define _I915_TRACE_H_ + ++#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) ++#define NOTRACE ++#endif ++ + #include + #include + #include +diff --git a/drivers/gpu/drm/i915/intel_uncore_trace.h b/drivers/gpu/drm/i915/intel_uncore_trace.h +index f13ff71edf2db..3c67e267fb602 100644 +--- a/drivers/gpu/drm/i915/intel_uncore_trace.h ++++ b/drivers/gpu/drm/i915/intel_uncore_trace.h +@@ -7,6 +7,10 @@ + #if !defined(__INTEL_UNCORE_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) + #define __INTEL_UNCORE_TRACE_H__ + ++#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) ++#define NOTRACE ++#endif ++ + #include "i915_reg_defs.h" + + #include +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt b/SPECS/kernel-rt/0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt deleted file mode 100644 index db980bbe3..000000000 --- a/SPECS/kernel-rt/0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt +++ /dev/null @@ -1,44 +0,0 @@ -From 191ddbc03ac12821fda0363a4bd26042839a423c Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Mon, 25 Oct 2021 15:05:18 +0200 -Subject: [PATCH 3/9] drm/i915: Don't check for atomic context on PREEMPT_RT - -The !in_atomic() check in _wait_for_atomic() triggers on PREEMPT_RT -because the uncore::lock is a spinlock_t and does not disable -preemption or interrupts. - -Changing the uncore:lock to a raw_spinlock_t doubles the worst case -latency on an otherwise idle testbox during testing. - -Ignore _WAIT_FOR_ATOMIC_CHECK() on PREEMPT_RT. - -Reviewed-by: Tvrtko Ursulin -Link: https://lore.kernel.org/all/20211006164628.s2mtsdd2jdbfyf7g@linutronix.de/ -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/i915_utils.h | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h -index f7fb40cfdb70..9cb40c2c4b12 100644 ---- a/drivers/gpu/drm/i915/i915_utils.h -+++ b/drivers/gpu/drm/i915/i915_utils.h -@@ -267,8 +267,13 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms) - (Wmax)) - #define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000) - --/* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */ --#if IS_ENABLED(CONFIG_DRM_I915_DEBUG) && IS_ENABLED(CONFIG_PREEMPT_COUNT) -+/* -+ * If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. -+ * On PREEMPT_RT the context isn't becoming atomic because it is used in an -+ * interrupt handler or because a spinlock_t is acquired. This leads to -+ * warnings which don't occur otherwise and therefore the check is disabled. -+ */ -+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG) && IS_ENABLED(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT) - # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) WARN_ON_ONCE((ATOMIC) && !in_atomic()) - #else - # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) do { } while (0) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm b/SPECS/kernel-rt/0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm new file mode 100644 index 000000000..865f1ffa1 --- /dev/null +++ b/SPECS/kernel-rt/0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm @@ -0,0 +1,189 @@ +From c6a6bbf8d3a8e0f06cfe948db0ef9597fb3bf78c Mon Sep 17 00:00:00 2001 +From: Dongwon Kim +Date: Thu, 18 Aug 2022 16:19:33 -0700 +Subject: [PATCH 3/3] drm/virtio: save and restore virtio_gpu_objects + +Host KVM/QEMU loses all graphic resourses submitted by guest OS upon +resumption from sleep or hibernation. This will cause invalid resource +errors when the guest OS makes any request to the host regarding those +resources. One way to prevent this problem is to let virtio-gpu driver +resubmit all existing resources upon resumption. A linked-list for +keeping references of all created virtio_gpu_object and its params is +added for this save and restore mechanism. Virtio-gpu objects are added +to the list whenever a new object is created and sent to the host. +All backed-up objects will then be re-sent to the host in .resume +function with 'create resource' virtio gpu command. + +Signed-off-by: Dongwon Kim +--- + drivers/gpu/drm/virtio/virtgpu_drv.c | 7 +++ + drivers/gpu/drm/virtio/virtgpu_drv.h | 10 ++++ + drivers/gpu/drm/virtio/virtgpu_kms.c | 1 + + drivers/gpu/drm/virtio/virtgpu_object.c | 65 +++++++++++++++++++++++++ + 4 files changed, 83 insertions(+) + +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c +index 905246316dc9e..e0af6aa705c3b 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.c ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.c +@@ -200,6 +200,13 @@ static int virtgpu_restore(struct virtio_device *vdev) + + virtio_device_ready(vdev); + ++ error = virtio_gpu_object_restore_all(vgdev); ++ ++ if (error) { ++ DRM_ERROR("Failed to recover objects\n"); ++ return error; ++ } ++ + error = drm_mode_config_helper_resume(dev); + if (error) { + DRM_ERROR("resume error %d\n", error); +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h +index 1279f998c8e0d..189e2282af191 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.h ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.h +@@ -126,6 +126,12 @@ struct virtio_gpu_object_array { + struct drm_gem_object *objs[] __counted_by(total); + }; + ++struct virtio_gpu_object_restore { ++ struct virtio_gpu_object *bo; ++ struct virtio_gpu_object_params params; ++ struct list_head node; ++}; ++ + struct virtio_gpu_vbuffer; + struct virtio_gpu_device; + +@@ -265,6 +271,7 @@ struct virtio_gpu_device { + struct work_struct obj_free_work; + spinlock_t obj_free_lock; + struct list_head obj_free_list; ++ struct list_head obj_rec; + + struct virtio_gpu_drv_capset *capsets; + uint32_t num_capsets; +@@ -479,6 +486,9 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo); + + int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid); ++ ++int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev); ++ + /* virtgpu_prime.c */ + int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo); +diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c +index d16be811a2bdc..3aaf9ad387cab 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_kms.c ++++ b/drivers/gpu/drm/virtio/virtgpu_kms.c +@@ -163,6 +163,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) + vgdev->fence_drv.context = dma_fence_context_alloc(1); + spin_lock_init(&vgdev->fence_drv.lock); + INIT_LIST_HEAD(&vgdev->fence_drv.fences); ++ INIT_LIST_HEAD(&vgdev->obj_rec); + INIT_LIST_HEAD(&vgdev->cap_cache); + INIT_WORK(&vgdev->config_changed_work, + virtio_gpu_config_changed_work_func); +diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c +index e6363c887500a..f3d70c417c4b3 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_object.c ++++ b/drivers/gpu/drm/virtio/virtgpu_object.c +@@ -61,6 +61,38 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t + ida_free(&vgdev->resource_ida, id - 1); + } + ++static void virtio_gpu_object_save_restore_list(struct virtio_gpu_device *vgdev, ++ struct virtio_gpu_object *bo, ++ struct virtio_gpu_object_params *params) ++{ ++ struct virtio_gpu_object_restore *new; ++ ++ new = kvmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) { ++ DRM_ERROR("Fail to allocate virtio_gpu_object_restore"); ++ return; ++ } ++ ++ new->bo = bo; ++ memcpy(&new->params, params, sizeof(*params)); ++ ++ list_add_tail(&new->node, &vgdev->obj_rec); ++} ++ ++static void virtio_gpu_object_del_restore_list(struct virtio_gpu_device *vgdev, ++ struct virtio_gpu_object *bo) ++{ ++ struct virtio_gpu_object_restore *curr, *tmp; ++ ++ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { ++ if (bo == curr->bo) { ++ list_del(&curr->node); ++ kvfree(curr); ++ break; ++ } ++ } ++} ++ + void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) + { + struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; +@@ -84,6 +116,7 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) + drm_gem_object_release(&bo->base.base); + kfree(bo); + } ++ virtio_gpu_object_del_restore_list(vgdev, bo); + } + + static void virtio_gpu_free_object(struct drm_gem_object *obj) +@@ -257,8 +290,11 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + objs, fence); + virtio_gpu_object_attach(vgdev, bo, ents, nents); + } ++ /* add submitted object to restore list */ ++ virtio_gpu_object_save_restore_list(vgdev, bo, params); + + *bo_ptr = bo; ++ + return 0; + + err_put_objs: +@@ -271,3 +307,32 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + drm_gem_shmem_free(shmem_obj); + return ret; + } ++ ++int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev) ++{ ++ struct virtio_gpu_object_restore *curr, *tmp; ++ struct virtio_gpu_mem_entry *ents; ++ unsigned int nents; ++ int ret; ++ ++ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { ++ ret = virtio_gpu_object_shmem_init(vgdev, curr->bo, &ents, &nents); ++ if (ret) ++ break; ++ ++ if (curr->params.blob) { ++ virtio_gpu_cmd_resource_create_blob(vgdev, curr->bo, &curr->params, ++ ents, nents); ++ } else if (curr->params.virgl) { ++ virtio_gpu_cmd_resource_create_3d(vgdev, curr->bo, &curr->params, ++ NULL, NULL); ++ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); ++ } else { ++ virtio_gpu_cmd_create_resource(vgdev, curr->bo, &curr->params, ++ NULL, NULL); ++ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); ++ } ++ } ++ ++ return ret; ++} +\ No newline at end of file +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-i2c-atr-Add-fwnode-handling.ipu b/SPECS/kernel-rt/0003-i2c-atr-Add-fwnode-handling.ipu deleted file mode 100644 index 164ed3640..000000000 --- a/SPECS/kernel-rt/0003-i2c-atr-Add-fwnode-handling.ipu +++ /dev/null @@ -1,52 +0,0 @@ -From bbe1d540eb9feae0b2a06aa9d8854048f161ab21 Mon Sep 17 00:00:00 2001 -From: Khai Wen Ng -Date: Thu, 28 Aug 2025 14:46:45 +0800 -Subject: [PATCH 3/6] i2c: atr: Add fwnode handling - -fwnode does not support i2c-atr nodes structure. -points to parent fwnode instead. - -Signed-off-by: Khai Wen Ng -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/i2c-atr.c | 23 +++++++++++++---------- - 1 file changed, 13 insertions(+), 10 deletions(-) - -diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c -index f998e5f43fc0..e1285a48841a 100644 ---- a/drivers/i2c/i2c-atr.c -+++ b/drivers/i2c/i2c-atr.c -@@ -836,17 +836,20 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) - u32 reg; - - atr_node = device_get_named_child_node(dev, "i2c-atr"); -- -- fwnode_for_each_child_node(atr_node, child) { -- ret = fwnode_property_read_u32(child, "reg", ®); -- if (ret) -- continue; -- if (chan_id == reg) -- break; -+ if (atr_node) { -+ fwnode_for_each_child_node(atr_node, child) { -+ ret = fwnode_property_read_u32(child, "reg", ®); -+ if (ret) -+ continue; -+ if (chan_id == reg) -+ break; -+ } -+ -+ device_set_node(&chan->adap.dev, child); -+ fwnode_handle_put(atr_node); -+ } else if (dev_fwnode(dev)) { -+ device_set_node(&chan->adap.dev, fwnode_handle_get(dev_fwnode(dev))); - } -- -- device_set_node(&chan->adap.dev, child); -- fwnode_handle_put(atr_node); - } - - if (desc->num_aliases > 0) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c b/SPECS/kernel-rt/0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c deleted file mode 100644 index 8bfab0fa8..000000000 --- a/SPECS/kernel-rt/0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c +++ /dev/null @@ -1,179 +0,0 @@ -From aceac75fc246f697442498cf7fa3d6f58be88a76 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:28 +0300 -Subject: [PATCH 03/11] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and - bounce buffering - -So far only I3C private and I2C transfers have required a bounce buffer -for DMA transfers when buffer is not DMA'able. - -It was observed that when the device DMA is IOMMU mapped and the receive -length is not a multiple of DWORDs (32-bit), the last DWORD is padded -with stale data from the RX FIFO, corrupting 1-3 bytes beyond the -expected data. - -A similar issue, though less severe, occurs when an I3C target returns -less data than requested. In this case, the padding does not exceed the -requested number of bytes, assuming the device DMA is not IOMMU mapped. - -Therefore, all I3C private transfer, CCC command payload and I2C -transfer receive buffers must be properly sized for the DMA being IOMMU -mapped. Even if those buffers are already DMA safe, their size may not -be DWORD aligned. - -To prepare for the device DMA being IOMMU mapped and to address the -above issue, use helpers from I3C core for DMA mapping and bounce -buffering for all DMA transfers. - -For now, require bounce buffer only when the buffer is in the -vmalloc() area to avoid unnecessary copying with CCC commands and -DMA-safe I2C transfers. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-3-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 34 -------------------------- - drivers/i3c/master/mipi-i3c-hci/dma.c | 27 +++++++++----------- - drivers/i3c/master/mipi-i3c-hci/hci.h | 3 +-- - 3 files changed, 12 insertions(+), 52 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index 60f1175f1f37..b2977b6ac9f7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -272,34 +272,6 @@ static int i3c_hci_daa(struct i3c_master_controller *m) - return hci->cmd->perform_daa(hci); - } - --static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci, -- struct hci_xfer *xfer) --{ -- if (hci->io != &mipi_i3c_hci_dma || -- xfer->data == NULL || !is_vmalloc_addr(xfer->data)) -- return 0; -- -- if (xfer->rnw) -- xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL); -- else -- xfer->bounce_buf = kmemdup(xfer->data, -- xfer->data_len, GFP_KERNEL); -- -- return xfer->bounce_buf == NULL ? -ENOMEM : 0; --} -- --static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci, -- struct hci_xfer *xfer) --{ -- if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL) -- return; -- -- if (xfer->rnw) -- memcpy(xfer->data, xfer->bounce_buf, xfer->data_len); -- -- kfree(xfer->bounce_buf); --} -- - static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - struct i3c_priv_xfer *i3c_xfers, - int nxfers) -@@ -333,9 +305,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - } - hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]); - xfer[i].cmd_desc[0] |= CMD_0_ROC; -- ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]); -- if (ret) -- goto out; - } - last = i - 1; - xfer[last].cmd_desc[0] |= CMD_0_TOC; -@@ -359,9 +328,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - } - - out: -- for (i = 0; i < nxfers; i++) -- i3c_hci_free_safe_xfer_buf(hci, &xfer[i]); -- - hci_free_xfer(xfer, nxfers); - return ret; - } -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 491dfe70b660..351851859f02 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -349,9 +349,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci, - xfer = xfer_list + i; - if (!xfer->data) - continue; -- dma_unmap_single(&hci->master.dev, -- xfer->data_dma, xfer->data_len, -- xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE); -+ i3c_master_dma_unmap_single(xfer->dma); - } - } - -@@ -362,7 +360,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - struct hci_rh_data *rh; - unsigned int i, ring, enqueue_ptr; - u32 op1_val, op2_val; -- void *buf; - - /* For now we only use ring 0 */ - ring = 0; -@@ -373,6 +370,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - for (i = 0; i < n; i++) { - struct hci_xfer *xfer = xfer_list + i; - u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr; -+ enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE : -+ DMA_TO_DEVICE; - - /* store cmd descriptor */ - *ring_data++ = xfer->cmd_desc[0]; -@@ -391,21 +390,17 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - - /* 2nd and 3rd words of Data Buffer Descriptor Structure */ - if (xfer->data) { -- buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data; -- xfer->data_dma = -- dma_map_single(&hci->master.dev, -- buf, -- xfer->data_len, -- xfer->rnw ? -- DMA_FROM_DEVICE : -- DMA_TO_DEVICE); -- if (dma_mapping_error(&hci->master.dev, -- xfer->data_dma)) { -+ xfer->dma = i3c_master_dma_map_single(&hci->master.dev, -+ xfer->data, -+ xfer->data_len, -+ false, -+ dir); -+ if (!xfer->dma) { - hci_dma_unmap_xfer(hci, xfer_list, i); - return -ENOMEM; - } -- *ring_data++ = lower_32_bits(xfer->data_dma); -- *ring_data++ = upper_32_bits(xfer->data_dma); -+ *ring_data++ = lower_32_bits(xfer->dma->addr); -+ *ring_data++ = upper_32_bits(xfer->dma->addr); - } else { - *ring_data++ = 0; - *ring_data++ = 0; -diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h -index 69ea1d10414b..33bc4906df1f 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/hci.h -+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h -@@ -94,8 +94,7 @@ struct hci_xfer { - }; - struct { - /* DMA specific */ -- dma_addr_t data_dma; -- void *bounce_buf; -+ struct i3c_dma *dma; - int ring_number; - int ring_entry; - }; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-issei-implement-main-thread-and-ham-messages.security b/SPECS/kernel-rt/0003-issei-implement-main-thread-and-ham-messages.security index 3f44cc70c..2dd880258 100644 --- a/SPECS/kernel-rt/0003-issei-implement-main-thread-and-ham-messages.security +++ b/SPECS/kernel-rt/0003-issei-implement-main-thread-and-ham-messages.security @@ -1,7 +1,7 @@ -From d11bfa704500611a05aca620fe7e58d982bf1aac Mon Sep 17 00:00:00 2001 +From 851cf5152bb84e8ebdec88a6298f272e7b105875 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 31 Oct 2024 10:00:50 +0200 -Subject: [PATCH 3/5] issei: implement main thread and ham messages +Subject: [PATCH 3/7] issei: implement main thread and ham messages Introduce the main thread and HECI Active Management (HAM) message handling for the ISSEI (Intel Silicon Security Engine @@ -20,17 +20,17 @@ Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- drivers/misc/issei/Makefile | 2 + - drivers/misc/issei/ham.c | 142 +++++++++++++++ + drivers/misc/issei/ham.c | 142 ++++++++++++++++ drivers/misc/issei/ham.h | 20 +++ - drivers/misc/issei/issei_dev.h | 9 + - drivers/misc/issei/main.c | 320 +++++++++++++++++++++++++++++++++ - 5 files changed, 493 insertions(+) + drivers/misc/issei/issei_dev.h | 5 + + drivers/misc/issei/main.c | 287 +++++++++++++++++++++++++++++++++ + 5 files changed, 456 insertions(+) create mode 100644 drivers/misc/issei/ham.c create mode 100644 drivers/misc/issei/ham.h create mode 100644 drivers/misc/issei/main.c diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile -index 4e8f6a435a31..712d62eb9790 100644 +index 4e8f6a435a318..712d62eb97900 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -7,3 +7,5 @@ issei-objs += cdev.o @@ -41,7 +41,7 @@ index 4e8f6a435a31..712d62eb9790 100644 +issei-objs += main.o diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c new file mode 100644 -index 000000000000..d6a81b68e351 +index 0000000000000..0fd589ffa8a87 --- /dev/null +++ b/drivers/misc/issei/ham.c @@ -0,0 +1,142 @@ @@ -94,26 +94,26 @@ index 000000000000..d6a81b68e351 + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_START) { -+ dev_err(idev->dev, "Wrong state %d != %d\n", ++ dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_START); + return -EPROTO; + } + + if (length < sizeof(*res)) { -+ dev_err(idev->dev, "Small start response size %zu < %zu\n", ++ dev_err(&idev->dev, "Small start response size %zu < %zu\n", + length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->heci_capabilities_length) { -+ dev_err(idev->dev, "Wrong start response size %zu != %u\n", ++ dev_err(&idev->dev, "Wrong start response size %zu != %u\n", + length - sizeof(*res), res->heci_capabilities_length); + return -EPROTO; + } + + memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); + idev->fw_protocol_ver = res->supported_version; -+ dev_dbg(idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, ++ dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, + idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); + @@ -131,18 +131,18 @@ index 000000000000..d6a81b68e351 + size_t i; + + if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) { -+ dev_err(idev->dev, "Wrong state %d != %d\n", ++ dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); + return -EPROTO; + } + + if (length < sizeof(*res)) { -+ dev_err(idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); ++ dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) { -+ dev_err(idev->dev, "Wrong response size %zu < %zu\n", ++ dev_err(&idev->dev, "Wrong response size %zu < %zu\n", + length - sizeof(*res), + res->client_count * sizeof(struct ham_client_properties)); + return -EPROTO; @@ -150,7 +150,7 @@ index 000000000000..d6a81b68e351 + + for (i = 0; i < res->client_count; i++) { + client = &res->clients_props[i]; -+ dev_dbg(idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", ++ dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", + client->client_number, client->protocol_ver, &client->client_uuid, + client->client_mtu, client->flags); + issei_fw_cl_create(idev, client->client_number, client->protocol_ver, @@ -166,7 +166,7 @@ index 000000000000..d6a81b68e351 + + /* process error */ + if (status != HAMS_SUCCESS) { -+ dev_err(idev->dev, "HAM command %u failed %u", hdr->cmd, status); ++ dev_err(&idev->dev, "HAM command %u failed %u", hdr->cmd, status); + return -EIO; + } + @@ -180,7 +180,7 @@ index 000000000000..d6a81b68e351 + break; + + default: -+ dev_err(idev->dev, "Unexpected command 0x%x", hdr->cmd); ++ dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); + ret = -EPROTO; + break; + } @@ -189,7 +189,7 @@ index 000000000000..d6a81b68e351 +} diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h new file mode 100644 -index 000000000000..33947d455302 +index 0000000000000..33947d4553029 --- /dev/null +++ b/drivers/misc/issei/ham.h @@ -0,0 +1,20 @@ @@ -214,17 +214,13 @@ index 000000000000..33947d455302 + +#endif /* _ISSEI_HAM_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h -index 4e507ce4d10e..4de4e48fb8d4 100644 +index cfac43c14ba24..082ac5b9acc2f 100644 --- a/drivers/misc/issei/issei_dev.h +++ b/drivers/misc/issei/issei_dev.h -@@ -150,4 +150,13 @@ struct issei_device { +@@ -152,4 +152,9 @@ struct issei_device { char hw[] __aligned(sizeof(void *)); }; -+void issei_device_init(struct issei_device *idev, struct device *device, -+ const struct issei_dma_length *dma_length, -+ const struct issei_hw_ops *ops); -+ +int issei_start(struct issei_device *idev); +void issei_stop(struct issei_device *idev); + @@ -233,13 +229,14 @@ index 4e507ce4d10e..4de4e48fb8d4 100644 #endif /* _ISSEI_DEV_H_ */ diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c new file mode 100644 -index 000000000000..84b58730bc23 +index 0000000000000..2e2b5b6b5aa42 --- /dev/null +++ b/drivers/misc/issei/main.c -@@ -0,0 +1,320 @@ +@@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include ++#include +#include +#include +#include @@ -272,12 +269,12 @@ index 000000000000..84b58730bc23 + ret = idev->ops->hw_reset(idev, !idev->power_down); + issei_dmam_clean(idev); + if (ret) { -+ dev_err(idev->dev, "hw_reset failed ret = %d\n", ret); ++ dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret); + return ret; + } + + if (idev->power_down) { -+ dev_dbg(idev->dev, "powering down: end of reset\n"); ++ dev_dbg(&idev->dev, "powering down: end of reset\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + return -ENODEV; + } @@ -293,13 +290,13 @@ index 000000000000..84b58730bc23 + if (ret) + return ret; + -+ dev_dbg(idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, ++ dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, + data.status, data.length); + if (issei_is_ham_rsp(data.fw_id, data.host_id)) { + ret = issei_ham_process_ham_rsp(idev, data.status, data.buf, data.length); + kfree(data.buf); + } else { -+ dev_dbg(idev->dev, "Client data\n"); ++ dev_dbg(&idev->dev, "Client data\n"); + ret = issei_cl_read_buf(idev, data.host_id, data.buf, data.length); + if (ret) + kfree(data.buf); @@ -330,22 +327,22 @@ index 000000000000..84b58730bc23 + int ret; + + while (!kthread_should_stop()) { -+ dev_dbg(idev->dev, "process_work in %d\n", idev->rst_state); ++ dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); + if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) { + if (!idev->power_down) -+ dev_dbg(idev->dev, "HW not ready, resetting\n"); ++ dev_dbg(&idev->dev, "HW not ready, resetting\n"); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + if (idev->power_down) + idev->rst_state = ISSEI_RST_STATE_INIT; + atomic_set(&idev->rst_irq, 0); -+ dev_dbg(idev->dev, "reset_step in %d\n", idev->rst_state); ++ dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); + timeout = MAX_SCHEDULE_TIMEOUT; + ret = 0; + switch (idev->rst_state) { + case ISSEI_RST_STATE_DISABLED: + if (idev->power_down) { -+ dev_dbg(idev->dev, "Interrupt in power down?\n"); ++ dev_dbg(&idev->dev, "Interrupt in power down?\n"); + break; + } + idev->rst_state = ISSEI_RST_STATE_INIT; @@ -358,7 +355,7 @@ index 000000000000..84b58730bc23 + if (!idev->power_down) { + idev->reset_count++; + if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { -+ dev_err(idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); ++ dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + break; + } @@ -366,7 +363,7 @@ index 000000000000..84b58730bc23 + + ret = issei_reset(idev); + if (idev->power_down) { -+ dev_dbg(idev->dev, "Powering down\n"); ++ dev_dbg(&idev->dev, "Powering down\n"); + return 0; + } + if (!ret) { @@ -377,7 +374,7 @@ index 000000000000..84b58730bc23 + + case ISSEI_RST_STATE_HW_READY: + if (idev->ops->hw_is_ready(idev)) { -+ dev_dbg(idev->dev, "HW is ready\n"); ++ dev_dbg(&idev->dev, "HW is ready\n"); + idev->ops->hw_reset_release(idev); + idev->ops->host_set_ready(idev); + ret = idev->ops->setup_message_send(idev); @@ -386,7 +383,7 @@ index 000000000000..84b58730bc23 + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + } + } else { -+ dev_dbg(idev->dev, "HW is not ready?\n"); ++ dev_dbg(&idev->dev, "HW is not ready?\n"); + timeout = old_timeout; + } + break; @@ -419,7 +416,7 @@ index 000000000000..84b58730bc23 + if (!ret) { + idev->reset_count = 0; + idev->rst_state = ISSEI_RST_STATE_DONE; -+ dev_dbg(idev->dev, "Reset finished successfully\n"); ++ dev_dbg(&idev->dev, "Reset finished successfully\n"); + } else if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; @@ -432,21 +429,21 @@ index 000000000000..84b58730bc23 + } + + if (ret) { -+ dev_dbg(idev->dev, "Process failed ret = %d\n", ret); ++ dev_dbg(&idev->dev, "Process failed ret = %d\n", ret); + idev->rst_state = ISSEI_RST_STATE_INIT; + continue; + } + old_timeout = wait_event_interruptible_timeout(idev->wait_rst_irq, + atomic_read(&idev->rst_irq), + timeout); -+ dev_dbg(idev->dev, "Out of wait %d %d %ld\n", idev->rst_state, ++ dev_dbg(&idev->dev, "Out of wait %d %d %ld\n", idev->rst_state, + atomic_read(&idev->rst_irq), old_timeout); + + if (idev->rst_state == ISSEI_RST_STATE_DISABLED) + continue; + + if (!atomic_read(&idev->rst_irq)) { -+ dev_dbg(idev->dev, "Timed out at state %d, resetting\n", idev->rst_state); ++ dev_dbg(&idev->dev, "Timed out at state %d, resetting\n", idev->rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + } @@ -455,40 +452,6 @@ index 000000000000..84b58730bc23 +} + +/** -+ * issei_device_init - initialize issei device structure. -+ * @idev: the device structure -+ * @dev: parent device structure -+ * @dma_length: structure with DMA sizes -+ * @ops: hardware-related operations -+ */ -+void issei_device_init(struct issei_device *idev, struct device *dev, -+ const struct issei_dma_length *dma_length, -+ const struct issei_hw_ops *ops) -+{ -+ idev->dev = dev; -+ idev->power_down = false; -+ init_waitqueue_head(&idev->wait_rst_irq); -+ atomic_set(&idev->rst_irq, 0); -+ init_waitqueue_head(&idev->wait_rst_state); -+ idev->rst_state = ISSEI_RST_STATE_INIT; -+ -+ mutex_init(&idev->host_client_lock); -+ INIT_LIST_HEAD(&idev->host_client_list); -+ idev->host_client_last_id = 0; -+ idev->host_client_count = 0; -+ -+ mutex_init(&idev->fw_client_lock); -+ INIT_LIST_HEAD(&idev->fw_client_list); -+ -+ idev->dma.length = *dma_length; -+ -+ INIT_LIST_HEAD(&idev->write_queue); -+ -+ idev->ops = ops; -+} -+EXPORT_SYMBOL_GPL(issei_device_init); -+ -+/** + * issei_start - configure HW device and start processing thread. + * @idev: the device structure + * @@ -511,9 +474,9 @@ index 000000000000..84b58730bc23 + return ret; + + idev->reset_thread = kthread_run(issei_process_thread, idev, -+ "kisseiprocess/%s", dev_name(idev->dev)); ++ "kisseiprocess/%s", dev_name(&idev->dev)); + if (IS_ERR(idev->reset_thread)) { -+ dev_err(idev->dev, "unable to create process thread. ret = %d\n", ret); ++ dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret); + return PTR_ERR(idev->reset_thread); + } + diff --git a/SPECS/kernel-rt/0003-media-i2c-add-support-for-lt6911gxd.ipu b/SPECS/kernel-rt/0003-media-i2c-add-support-for-lt6911gxd.ipu deleted file mode 100644 index c6133b98e..000000000 --- a/SPECS/kernel-rt/0003-media-i2c-add-support-for-lt6911gxd.ipu +++ /dev/null @@ -1,47 +0,0 @@ -From 07da798345a021ab91021da8dae73e071acfcc0d Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Wed, 11 Dec 2024 17:11:18 +0800 -Subject: [PATCH 03/11] media: i2c: add support for lt6911gxd - -Signed-off-by: linya14x -Signed-off-by: zouxiaoh ---- - drivers/media/i2c/Kconfig | 12 ++++++++++++ - drivers/media/i2c/Makefile | 1 + - 2 files changed, 13 insertions(+) - -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index 6237fe804a5c..7ec788adeec1 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -267,6 +267,18 @@ config VIDEO_IMX415 - To compile this driver as a module, choose M here: the - module will be called imx415. - -+config VIDEO_LT6911GXD -+ tristate "Lontium LT6911GXD decoder" -+ depends on ACPI || COMPILE_TEST -+ select V4L2_CCI_I2C -+ help -+ This is a Video4Linux2 sensor-level driver for the Lontium -+ LT6911GXD HDMI to MIPI CSI-2 bridge. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called lt6911gxd. -+ -+ - config VIDEO_MAX9271_LIB - tristate - -diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile -index 5873d29433ee..2fbdd4c181d1 100644 ---- a/drivers/media/i2c/Makefile -+++ b/drivers/media/i2c/Makefile -@@ -162,3 +162,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o - obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o - obj-$(CONFIG_VIDEO_WM8739) += wm8739.o - obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -+obj-$(CONFIG_VIDEO_LT6911GXD) += lt6911gxd.o --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-mei-expose-device-kind-for-ioe-device.security b/SPECS/kernel-rt/0003-mei-expose-device-kind-for-ioe-device.security new file mode 100644 index 000000000..d7a7afe60 --- /dev/null +++ b/SPECS/kernel-rt/0003-mei-expose-device-kind-for-ioe-device.security @@ -0,0 +1,386 @@ +From ecfca416c07938af73aa64b8a1d1f79c57a32659 Mon Sep 17 00:00:00 2001 +From: "Abliyev, Reuven" +Date: Sun, 9 Mar 2025 23:53:00 +0200 +Subject: [PATCH 3/8] mei: expose device kind for ioe device + +Detect IO extender device and set appropriate kind. +Rewrite device kind to store index instead of string internally. + +Signed-off-by: Abliyev, Reuven +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/hw-me-regs.h | 4 ++ + drivers/misc/mei/hw-me.c | 80 ++++++++++++++++++++++++++++----- + drivers/misc/mei/hw-me.h | 4 +- + drivers/misc/mei/main.c | 20 ++++++--- + drivers/misc/mei/mei_dev.h | 17 ++++--- + drivers/misc/mei/platform-vsc.c | 2 +- + 6 files changed, 104 insertions(+), 23 deletions(-) + +diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h +index a4f75dc369292..8bc61ffa123fc 100644 +--- a/drivers/misc/mei/hw-me-regs.h ++++ b/drivers/misc/mei/hw-me-regs.h +@@ -6,6 +6,8 @@ + #ifndef _MEI_HW_MEI_REGS_H_ + #define _MEI_HW_MEI_REGS_H_ + ++#include ++ + /* + * MEI device IDs + */ +@@ -139,6 +141,8 @@ + # define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070 + # define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000 + # define PCI_CFG_HFS_3_FW_SKU_SPS 0x00000060 ++# define PCI_CFG_HFS_3_EXT_SKU_MSK GENMASK(3, 0) /* IOE detection bits */ ++# define PCI_CFG_HFS_3_EXT_SKU_IOE 0x00000001 + #define PCI_CFG_HFS_4 0x64 + #define PCI_CFG_HFS_5 0x68 + # define GSC_CFG_HFS_5_BOOT_TYPE_MSK 0x00000003 +diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c +index d4612c6597844..c92a0e27787bb 100644 +--- a/drivers/misc/mei/hw-me.c ++++ b/drivers/misc/mei/hw-me.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + #include "mei_dev.h" + #include "hbm.h" +@@ -1573,14 +1574,50 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) + fw_type == PCI_CFG_HFS_3_FW_SKU_SPS; + } + +-#define MEI_CFG_KIND_ITOUCH \ +- .kind = "itouch" ++static enum mei_dev_kind mei_cfg_kind_mei(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_MEI; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_itouch(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_ITOUCH; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_gsc(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_GSC; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_gscfi(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_GSCFI; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_ioe(const struct pci_dev *pdev) ++{ ++ u32 reg; + +-#define MEI_CFG_TYPE_GSC \ +- .kind = "gsc" ++ pci_bus_read_config_dword(pdev->bus, 0, PCI_CFG_HFS_3, ®); ++ trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg); ++ return FIELD_GET(PCI_CFG_HFS_3_EXT_SKU_MSK, reg) == PCI_CFG_HFS_3_EXT_SKU_IOE ? ++ MEI_DEV_KIND_IOE : MEI_DEV_KIND_MEI; ++} ++ ++#define MEI_CFG_KIND_MEI \ ++ .get_kind = mei_cfg_kind_mei ++ ++#define MEI_CFG_KIND_ITOUCH \ ++ .get_kind = mei_cfg_kind_itouch + +-#define MEI_CFG_TYPE_GSCFI \ +- .kind = "gscfi" ++#define MEI_CFG_KIND_GSC \ ++ .get_kind = mei_cfg_kind_gsc ++ ++#define MEI_CFG_KIND_GSCFI \ ++ .get_kind = mei_cfg_kind_gscfi ++ ++#define MEI_CFG_KIND_IOE \ ++ .get_kind = mei_cfg_kind_ioe + + #define MEI_CFG_FW_SPS_IGN \ + .quirk_probe = mei_me_fw_type_sps_ign +@@ -1619,27 +1656,32 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) + + /* ICH Legacy devices */ + static const struct mei_cfg mei_me_ich_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_ICH_HFS, + }; + + /* ICH devices */ + static const struct mei_cfg mei_me_ich10_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_ICH10_HFS, + }; + + /* PCH6 devices */ + static const struct mei_cfg mei_me_pch6_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH_HFS, + }; + + /* PCH7 devices */ + static const struct mei_cfg mei_me_pch7_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH_HFS, + MEI_CFG_FW_VER_SUPP, + }; + + /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ + static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_FW_NM, +@@ -1647,6 +1689,7 @@ static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { + + /* PCH8 Lynx Point and newer devices */ + static const struct mei_cfg mei_me_pch8_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + }; +@@ -1660,6 +1703,7 @@ static const struct mei_cfg mei_me_pch8_itouch_cfg = { + + /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ + static const struct mei_cfg mei_me_pch8_sps_4_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_FW_SPS_4, +@@ -1667,6 +1711,7 @@ static const struct mei_cfg mei_me_pch8_sps_4_cfg = { + + /* LBG with quirk for SPS (4.0) Firmware exclusion */ + static const struct mei_cfg mei_me_pch12_sps_4_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_FW_SPS_4, +@@ -1674,6 +1719,7 @@ static const struct mei_cfg mei_me_pch12_sps_4_cfg = { + + /* Cannon Lake and newer devices */ + static const struct mei_cfg mei_me_pch12_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, +@@ -1681,6 +1727,7 @@ static const struct mei_cfg mei_me_pch12_cfg = { + + /* Cannon Lake with quirk for SPS 5.0 and newer Firmware exclusion */ + static const struct mei_cfg mei_me_pch12_sps_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, +@@ -1699,14 +1746,26 @@ static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = { + + /* Tiger Lake and newer devices */ + static const struct mei_cfg mei_me_pch15_cfg = { ++ MEI_CFG_KIND_MEI, ++ MEI_CFG_PCH8_HFS, ++ MEI_CFG_FW_VER_SUPP, ++ MEI_CFG_DMA_128, ++ MEI_CFG_TRC, ++}; ++ ++/* NVL-H devices */ ++static const struct mei_cfg mei_me_pch22_cfg = { ++ MEI_CFG_KIND_IOE, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, + MEI_CFG_TRC, + }; + ++ + /* Tiger Lake with quirk for SPS 5.0 and newer Firmware exclusion */ + static const struct mei_cfg mei_me_pch15_sps_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, +@@ -1716,14 +1775,14 @@ static const struct mei_cfg mei_me_pch15_sps_cfg = { + + /* Graphics System Controller */ + static const struct mei_cfg mei_me_gsc_cfg = { +- MEI_CFG_TYPE_GSC, ++ MEI_CFG_KIND_GSC, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + }; + + /* Graphics System Controller Firmware Interface */ + static const struct mei_cfg mei_me_gscfi_cfg = { +- MEI_CFG_TYPE_GSCFI, ++ MEI_CFG_KIND_GSCFI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + }; +@@ -1750,6 +1809,7 @@ static const struct mei_cfg *const mei_cfg_list[] = { + [MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg, + [MEI_ME_GSC_CFG] = &mei_me_gsc_cfg, + [MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg, ++ [MEI_ME_PCH22_CFG] = &mei_me_pch22_cfg, + }; + + const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) +@@ -1777,6 +1837,7 @@ struct mei_device *mei_me_dev_init(struct device *parent, + { + struct mei_device *dev; + struct mei_me_hw *hw; ++ struct pci_dev *pdev = to_pci_dev(parent); + int i; + + dev = kzalloc(sizeof(*dev) + sizeof(*hw), GFP_KERNEL); +@@ -1792,8 +1853,7 @@ struct mei_device *mei_me_dev_init(struct device *parent, + hw->cfg = cfg; + + dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; +- +- dev->kind = cfg->kind; ++ dev->kind = cfg->get_kind(pdev); + + return dev; + } +diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h +index 204b92af6c478..520f2b7bd301a 100644 +--- a/drivers/misc/mei/hw-me.h ++++ b/drivers/misc/mei/hw-me.h +@@ -27,7 +27,7 @@ + struct mei_cfg { + const struct mei_fw_status fw_status; + bool (*quirk_probe)(const struct pci_dev *pdev); +- const char *kind; ++ enum mei_dev_kind (*get_kind)(const struct pci_dev *pdev); + size_t dma_size[DMA_DSCR_NUM]; + u32 fw_ver_supported:1; + u32 hw_trc_supported:1; +@@ -110,6 +110,7 @@ static inline bool mei_me_hw_use_polling(const struct mei_me_hw *hw) + * SPS firmware exclusion. + * @MEI_ME_GSC_CFG: Graphics System Controller + * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface ++ * @MEI_ME_PCH22_CFG: Platform Controller Hub Gen22 and newer + * @MEI_ME_NUM_CFG: Upper Sentinel. + */ + enum mei_cfg_idx { +@@ -130,6 +131,7 @@ enum mei_cfg_idx { + MEI_ME_PCH15_SPS_CFG, + MEI_ME_GSC_CFG, + MEI_ME_GSCFI_CFG, ++ MEI_ME_PCH22_CFG, + MEI_ME_NUM_CFG, + }; + +diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c +index 86a73684a3732..00b0781fc2933 100644 +--- a/drivers/misc/mei/main.c ++++ b/drivers/misc/mei/main.c +@@ -1155,6 +1155,15 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) + } + } + ++static const char *mei_kind_names[] = { ++ "mei", ++ "itouch", ++ "gsc", ++ "gscfi", ++ "ioe", ++ "ivsc", ++}; ++ + /** + * kind_show - display device kind + * +@@ -1168,14 +1177,13 @@ static ssize_t kind_show(struct device *device, + struct device_attribute *attr, char *buf) + { + struct mei_device *dev = dev_get_drvdata(device); +- ssize_t ret; + +- if (dev->kind) +- ret = sprintf(buf, "%s\n", dev->kind); +- else +- ret = sprintf(buf, "%s\n", "mei"); ++ BUILD_BUG_ON(ARRAY_SIZE(mei_kind_names) != MEI_DEV_KIND_MAX); + +- return ret; ++ if (dev->kind < MEI_DEV_KIND_MEI || dev->kind >= MEI_DEV_KIND_MAX) ++ return -EINVAL; ++ ++ return sysfs_emit(buf, "%s\n", mei_kind_names[dev->kind]); + } + static DEVICE_ATTR_RO(kind); + +diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h +index 0bf8d552c3eab..1db718d5c93f6 100644 +--- a/drivers/misc/mei/mei_dev.h ++++ b/drivers/misc/mei/mei_dev.h +@@ -469,6 +469,15 @@ struct mei_dev_timeouts { + unsigned long link_reset_wait; /* link reset wait timeout, in jiffies */ + }; + ++enum mei_dev_kind { ++ MEI_DEV_KIND_MEI, ++ MEI_DEV_KIND_ITOUCH, ++ MEI_DEV_KIND_GSC, ++ MEI_DEV_KIND_GSCFI, ++ MEI_DEV_KIND_IOE, ++ MEI_DEV_KIND_IVSC, ++ MEI_DEV_KIND_MAX, ++}; + /** + * struct mei_device - MEI private device struct + * +@@ -645,7 +654,7 @@ struct mei_device { + struct list_head device_list; + struct mutex cl_bus_lock; + +- const char *kind; ++ enum mei_dev_kind kind; + + #if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +@@ -906,8 +915,7 @@ static inline ssize_t mei_fw_status_str(struct mei_device *dev, + */ + static inline bool kind_is_gsc(struct mei_device *dev) + { +- /* check kind for NULL because it may be not set, like at the fist call to hw_start */ +- return dev->kind && (strcmp(dev->kind, "gsc") == 0); ++ return dev->kind == MEI_DEV_KIND_GSC; + } + + /** +@@ -919,7 +927,6 @@ static inline bool kind_is_gsc(struct mei_device *dev) + */ + static inline bool kind_is_gscfi(struct mei_device *dev) + { +- /* check kind for NULL because it may be not set, like at the fist call to hw_start */ +- return dev->kind && (strcmp(dev->kind, "gscfi") == 0); ++ return dev->kind == MEI_DEV_KIND_GSCFI; + } + #endif +diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c +index 9787b9cee71ca..5100234ba5b69 100644 +--- a/drivers/misc/mei/platform-vsc.c ++++ b/drivers/misc/mei/platform-vsc.c +@@ -350,7 +350,7 @@ static int mei_vsc_probe(struct platform_device *pdev) + mei_device_init(mei_dev, dev, false, &mei_vsc_hw_ops); + + mei_dev->fw_f_fw_ver_supported = 0; +- mei_dev->kind = "ivsc"; ++ mei_dev->kind = MEI_DEV_KIND_IVSC; + + hw = mei_dev_to_vsc_hw(mei_dev); + atomic_set(&hw->write_lock_cnt, 0); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-net-core-XDP-metadata-BTF-netlink-API.ethernet b/SPECS/kernel-rt/0003-net-core-XDP-metadata-BTF-netlink-API.ethernet new file mode 100644 index 000000000..d11d794ab --- /dev/null +++ b/SPECS/kernel-rt/0003-net-core-XDP-metadata-BTF-netlink-API.ethernet @@ -0,0 +1,213 @@ +From 6fec66c6a28573522d164b3bd0e09b96d3364ac5 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Mon, 14 Jun 2021 11:16:14 +0800 +Subject: [PATCH 03/14] net/core: XDP metadata BTF netlink API + +Add new devlink XDP attributes to be used to query or setup XDP metadata +BTF state. + +IFLA_XDP_MD_BTF_ID: type NLA_U32. +IFLA_XDP_MD_BTF_STATE: type = NLA_U8. + +On XDP query driver reports current loaded BTF ID, and its state if +active or not. + +On XDP setup, driver will use these attributes to activate/deactivate +a specific BTF ID. + +Signed-off-by: Saeed Mahameed +Signed-off-by: Jithu Joseph +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + include/linux/netdevice.h | 15 +++++++++- + include/uapi/linux/if_link.h | 2 ++ + net/core/dev.c | 53 ++++++++++++++++++++++++++++++++++++ + net/core/rtnetlink.c | 18 +++++++++++- + 4 files changed, 86 insertions(+), 2 deletions(-) + +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index c6c04cd0a6816..35a5666aa86dd 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -965,6 +965,10 @@ enum bpf_netdev_command { + */ + XDP_SETUP_PROG, + XDP_SETUP_PROG_HW, ++ /* Setup/query XDP Meta Data BTF */ ++ XDP_SETUP_MD_BTF, ++ XDP_QUERY_MD_BTF, ++ + /* BPF program for offload callbacks, invoked at program load time. */ + BPF_OFFLOAD_MAP_ALLOC, + BPF_OFFLOAD_MAP_FREE, +@@ -988,6 +992,7 @@ struct bpf_xdp_entity { + struct bpf_prog *prog; + struct bpf_xdp_link *link; + }; ++struct btf; + + struct netdev_bpf { + enum bpf_netdev_command command; +@@ -996,7 +1001,11 @@ struct netdev_bpf { + struct { + u32 flags; + struct bpf_prog *prog; +- struct netlink_ext_ack *extack; ++ }; ++ /* XDP_{SETUP/QUERY}_MD_BTF */ ++ struct { ++ u8 btf_enable; ++ u32 btf_id; + }; + /* BPF_OFFLOAD_MAP_ALLOC, BPF_OFFLOAD_MAP_FREE */ + struct { +@@ -1007,6 +1016,7 @@ struct netdev_bpf { + struct xsk_buff_pool *pool; + u16 queue_id; + } xsk; ++ struct netlink_ext_ack *extack; + }; + }; + +@@ -4271,6 +4281,9 @@ u32 dev_xdp_prog_id(struct net_device *dev, enum bpf_xdp_mode mode); + + u32 dev_get_min_mp_channel_count(const struct net_device *dev); + ++int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, ++ u8 enable); ++u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled); + int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); + int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); + int dev_forward_skb_nomtu(struct net_device *dev, struct sk_buff *skb); +diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h +index 3b491d96e52eb..611f1d846001f 100644 +--- a/include/uapi/linux/if_link.h ++++ b/include/uapi/linux/if_link.h +@@ -1912,6 +1912,8 @@ enum { + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, ++ IFLA_XDP_MD_BTF_ID, ++ IFLA_XDP_MD_BTF_STATE, + __IFLA_XDP_MAX, + }; + +diff --git a/net/core/dev.c b/net/core/dev.c +index 5b536860138d1..9e6c1580dac2f 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -10659,6 +10659,59 @@ u32 dev_get_min_mp_channel_count(const struct net_device *dev) + return 0; + } + ++/** ++ * dev_xdp_query_md_btf - Query meta data btf of a device ++ * @dev: device ++ * @enabled: 1 if enabled, 0 otherwise ++ * ++ * Returns btf id > 0 if valid ++ */ ++u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled) ++{ ++ struct netdev_bpf xdp; ++ bpf_op_t ndo_bpf; ++ ++ ndo_bpf = dev->netdev_ops->ndo_bpf; ++ if (!ndo_bpf) ++ return 0; ++ ++ memset(&xdp, 0, sizeof(xdp)); ++ xdp.command = XDP_QUERY_MD_BTF; ++ ++ if (ndo_bpf(dev, &xdp)) ++ return 0; /* 0 is an invalid btf id */ ++ ++ *enabled = xdp.btf_enable; ++ return xdp.btf_id; ++} ++ ++/** ++ * dev_xdp_setup_md_btf - enable or disable meta data btf for a device ++ * @dev: device ++ * @extack: netlink extended ack ++ * @enable: 1 to enable, 0 to disable ++ * ++ * Returns 0 on success ++ */ ++int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, ++ u8 enable) ++{ ++ struct netdev_bpf xdp; ++ bpf_op_t ndo_bpf; ++ ++ ndo_bpf = dev->netdev_ops->ndo_bpf; ++ if (!ndo_bpf) ++ return -EOPNOTSUPP; ++ ++ memset(&xdp, 0, sizeof(xdp)); ++ ++ xdp.command = XDP_SETUP_MD_BTF; ++ xdp.btf_enable = enable; ++ xdp.extack = extack; ++ ++ return ndo_bpf(dev, &xdp); ++} ++ + /** + * dev_index_reserve() - allocate an ifindex in a namespace + * @net: the applicable net namespace +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index 576d5ec3bb364..b00c602d676fe 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1734,8 +1734,9 @@ static int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev, + + static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + { ++ u32 prog_id, md_btf_id; ++ u8 md_btf_enabled = 0; + struct nlattr *xdp; +- u32 prog_id; + int err; + u8 mode; + +@@ -1768,6 +1769,10 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + goto err_cancel; + } + ++ md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); ++ nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); ++ nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); ++ + nla_nest_end(skb, xdp); + return 0; + +@@ -2302,6 +2307,8 @@ static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { + [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, + [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, + [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, ++ [IFLA_XDP_MD_BTF_ID] = { .type = NLA_U32 }, ++ [IFLA_XDP_MD_BTF_STATE] = { .type = NLA_U8 }, + }; + + static struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla, +@@ -3390,6 +3397,15 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev, + goto errout; + status |= DO_SETLINK_NOTIFY; + } ++ ++ if (xdp[IFLA_XDP_MD_BTF_STATE]) { ++ u8 enable = nla_get_u8(xdp[IFLA_XDP_MD_BTF_STATE]); ++ ++ err = dev_xdp_setup_md_btf(dev, extack, enable); ++ if (err) ++ goto errout; ++ status |= DO_SETLINK_NOTIFY; ++ } + } + + errout: +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-net-phy-increase-gpy-loopback-test-delay.ethernet b/SPECS/kernel-rt/0003-net-phy-increase-gpy-loopback-test-delay.ethernet new file mode 100644 index 000000000..11366bfa2 --- /dev/null +++ b/SPECS/kernel-rt/0003-net-phy-increase-gpy-loopback-test-delay.ethernet @@ -0,0 +1,29 @@ +From f5645b8738dd3b92a99c1dfffbc9859c3a509e6f Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Tue, 18 Oct 2022 17:12:48 +0800 +Subject: [PATCH 03/18] net: phy: increase gpy loopback test delay + +Increase the gpy loopback delay to avoid phy getting stuck in an unknown +state. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/phy/mxl-gpy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c +index 2a873f791733a..2ea1d3ba28055 100644 +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -838,7 +838,7 @@ static int gpy_loopback(struct phy_device *phydev, bool enable, int speed) + /* It takes some time for PHY device to switch into + * loopback mode. + */ +- msleep(100); ++ msleep(600); + } else { + priv->lb_dis_to = get_jiffies_64() + HZ * 3; + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt b/SPECS/kernel-rt/0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt deleted file mode 100644 index 4af4cb4f3..000000000 --- a/SPECS/kernel-rt/0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt +++ /dev/null @@ -1,42 +0,0 @@ -From fc4e455b2a02e268ac0e3d6e579b8295487ea614 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Thu, 24 Apr 2025 13:33:05 +0300 -Subject: [PATCH 3/4] net: thunderbolt: Allow changing MTU of the device - -This adds possibility to affect the MTU of the device. - -Signed-off-by: Mika Westerberg ---- - drivers/net/thunderbolt/main.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c -index dcaa62377808..9e46f713c390 100644 ---- a/drivers/net/thunderbolt/main.c -+++ b/drivers/net/thunderbolt/main.c -@@ -1257,11 +1257,22 @@ static void tbnet_get_stats64(struct net_device *dev, - stats->rx_missed_errors = net->stats.rx_missed_errors; - } - -+static int tbnet_change_mtu(struct net_device *dev, int new_mtu) -+{ -+ /* MTU < 68 is an error and causes problems on some kernels */ -+ if (new_mtu < 68 || new_mtu > (TBNET_MAX_MTU - ETH_HLEN)) -+ return -EINVAL; -+ -+ dev->mtu = new_mtu; -+ return 0; -+} -+ - static const struct net_device_ops tbnet_netdev_ops = { - .ndo_open = tbnet_open, - .ndo_stop = tbnet_stop, - .ndo_start_xmit = tbnet_start_xmit, - .ndo_get_stats64 = tbnet_get_stats64, -+ .ndo_change_mtu = tbnet_change_mtu, - }; - - static void tbnet_generate_mac(struct net_device *dev) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu b/SPECS/kernel-rt/0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu deleted file mode 100644 index 0838230b8..000000000 --- a/SPECS/kernel-rt/0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu +++ /dev/null @@ -1,207 +0,0 @@ -From 4e55336cf7c4aab293b4b2e2cc3bceb01924da23 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:09:58 +0800 -Subject: [PATCH 03/21] patch: staging add enbaled IPU8_INSYS_NEW_ABI - -Signed-off-by: linya14x ---- - .../staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 47 +++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-fw-isys.c | 29 ++++++++++++ - drivers/staging/media/ipu7/ipu7-isys-queue.c | 6 +++ - drivers/staging/media/ipu7/ipu7-isys-video.c | 17 +++++++ - 4 files changed, 99 insertions(+) - -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -index c42d0b7a2627..45db85eb13ec 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -@@ -251,6 +251,10 @@ struct ipu7_insys_output_link { - struct ipu7_insys_output_cropping { - u16 line_top; - u16 line_bottom; -+#ifdef IPU8_INSYS_NEW_ABI -+ u16 column_left; -+ u16 column_right; -+#endif - }; - - struct ipu7_insys_output_dpcm { -@@ -260,16 +264,55 @@ struct ipu7_insys_output_dpcm { - u8 pad; - }; - -+#ifdef IPU8_INSYS_NEW_ABI -+enum ipu_insys_cfa_dim { -+ IPU_INSYS_CFA_DIM_2x2 = 0, -+ IPU_INSYS_CFA_DIM_4x4 = 1, -+ N_IPU_INSYS_CFA_DIM -+}; -+ -+#define IPU_INSYS_MAX_BINNING_FACTOR (4U) -+#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) -+#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) -+#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) -+#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) -+ -+struct ipu7_insys_upipe_output_pin { -+ ia_gofo_addr_t opaque_pin_cfg; -+ u16 plane_offset_1; -+ u16 plane_offset_2; -+ u8 single_uob_fifo; -+ u8 shared_uob_fifo; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_capture_output_pin_cfg { -+ struct ipu7_insys_capture_output_pin_payload pin_payload; -+ ia_gofo_addr_t upipe_capture_cfg; -+}; -+ -+#endif - struct ipu7_insys_output_pin { - struct ipu7_insys_output_link link; - struct ipu7_insys_output_cropping crop; - struct ipu7_insys_output_dpcm dpcm; -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_upipe_output_pin upipe_pin_cfg; -+#endif - u32 stride; - u16 ft; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 upipe_enable; -+#endif - u8 send_irq; - u8 input_pin_id; - u8 early_ack_en; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 cfa_dim; -+ u8 binning_factor; -+#else - u8 pad[3]; -+#endif - }; - - struct ipu7_insys_input_pin { -@@ -294,7 +337,11 @@ struct ipu7_insys_stream_cfg { - }; - - struct ipu7_insys_buffset { -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_capture_output_pin_cfg output_pins[4]; -+#else - struct ipu7_insys_capture_output_pin_payload output_pins[4]; -+#endif - u8 capture_msg_map; - u8 frame_id; - u8 skip_frame; -diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c -index e4b9c364572b..c98326bd9fee 100644 ---- a/drivers/staging/media/ipu7/ipu7-fw-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c -@@ -268,6 +268,12 @@ void ipu7_fw_isys_dump_stream_cfg(struct device *dev, - cfg->output_pins[i].crop.line_top); - dev_dbg(dev, "\t.crop.line_bottom = %d\n", - cfg->output_pins[i].crop.line_bottom); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.crop.column_left = %d\n", -+ cfg->output_pins[i].crop.column_left); -+ dev_dbg(dev, "\t.crop.colunm_right = %d\n", -+ cfg->output_pins[i].crop.column_right); -+#endif - - dev_dbg(dev, "\t.dpcm_enable = %d\n", - cfg->output_pins[i].dpcm.enable); -@@ -275,6 +281,20 @@ void ipu7_fw_isys_dump_stream_cfg(struct device *dev, - cfg->output_pins[i].dpcm.type); - dev_dbg(dev, "\t.dpcm.predictor = %d\n", - cfg->output_pins[i].dpcm.predictor); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.upipe_enable = %d\n", -+ cfg->output_pins[i].upipe_enable); -+ dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); -+ dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); -+ dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); -+#endif - } - dev_dbg(dev, "---------------------------\n"); - } -@@ -293,9 +313,18 @@ void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, - - for (i = 0; i < outputs; i++) { - dev_dbg(dev, ".output_pin[%d]:\n", i); -+#ifndef IPU8_INSYS_NEW_ABI - dev_dbg(dev, "\t.user_token = %llx\n", - buf->output_pins[i].user_token); - dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); -+#else -+ dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", -+ buf->output_pins[i].pin_payload.user_token); -+ dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", -+ buf->output_pins[i].pin_payload.addr); -+ dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", -+ buf->output_pins[i].upipe_capture_cfg); -+#endif - } - dev_dbg(dev, "---------------------------\n"); - } -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c -index 1f5fb7d20d81..527fdfe2f11c 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c -@@ -264,8 +264,14 @@ static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, - struct ipu7_isys_video_buffer *ivb = - vb2_buffer_to_ipu7_isys_video_buffer(vvb); - -+#ifndef IPU8_INSYS_NEW_ABI - set->output_pins[aq->fw_output].addr = ivb->dma_addr; - set->output_pins[aq->fw_output].user_token = (uintptr_t)set; -+#else -+ set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; -+ set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; -+ set->output_pins[aq->fw_output].upipe_capture_cfg = 0; -+#endif - } - - /* -diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c -index b3d337fe786b..de3ebf9e3d3a 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-video.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-video.c -@@ -457,10 +457,27 @@ static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, - /* output pin crop */ - output_pin->crop.line_top = 0; - output_pin->crop.line_bottom = 0; -+#ifdef IPU8_INSYS_NEW_ABI -+ output_pin->crop.column_left = 0; -+ output_pin->crop.column_right = 0; -+#endif - - /* output de-compression */ - output_pin->dpcm.enable = 0; - -+#ifdef IPU8_INSYS_NEW_ABI -+ /* upipe_cfg */ -+ output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; -+ output_pin->upipe_pin_cfg.plane_offset_1 = 0; -+ output_pin->upipe_pin_cfg.plane_offset_2 = 0; -+ output_pin->upipe_pin_cfg.single_uob_fifo = 0; -+ output_pin->upipe_pin_cfg.shared_uob_fifo = 0; -+ output_pin->upipe_enable = 0; -+ output_pin->binning_factor = 0; -+ /* stupid setting, even unused, SW still need to set a valid value */ -+ output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; -+#endif -+ - /* frame format type */ - pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); - output_pin->ft = (u16)pfmt->css_pixelformat; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf b/SPECS/kernel-rt/0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf new file mode 100644 index 000000000..42b964fac --- /dev/null +++ b/SPECS/kernel-rt/0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf @@ -0,0 +1,289 @@ +From dd312063b1c9c1f5f3dddeb6c015bb05504609f9 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Tue, 30 Dec 2025 15:05:54 -0800 +Subject: [PATCH 03/13] perf/x86/intel/uncore: Support per-platform discovery + base devices + +On DMR platforms, IMH discovery tables are enumerated via PCI, while +CBB domains use MSRs, unlike earlier platforms which relied on either +PCI or MSR exclusively. + +DMR also uses different MSRs and PCI devices, requiring support for +multiple, platform-specific discovery bases. + +Introduce struct uncore_discovery_domain to hold the discovery base and +other domain-specific configuration. + +Move uncore_units_ignore into uncore_discovery_domain so a single +structure can be passed to uncore_discovery_[pci/msr]. + +No functional change intended. + +Co-developed-by: Dapeng Mi +Signed-off-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 32 +++++++++---- + arch/x86/events/intel/uncore.h | 15 ++++-- + arch/x86/events/intel/uncore_discovery.c | 58 +++++++++++++++--------- + 3 files changed, 70 insertions(+), 35 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index cd561290be8ce..e3193a37a2150 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1798,7 +1798,7 @@ static const struct uncore_plat_init lnl_uncore_init __initconst = { + static const struct uncore_plat_init ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, +- .use_discovery = true, ++ .domain[0].discovery_base = UNCORE_DISCOVERY_MSR, + }; + + static const struct uncore_plat_init icx_uncore_init __initconst = { +@@ -1817,16 +1817,18 @@ static const struct uncore_plat_init spr_uncore_init __initconst = { + .cpu_init = spr_uncore_cpu_init, + .pci_init = spr_uncore_pci_init, + .mmio_init = spr_uncore_mmio_init, +- .use_discovery = true, +- .uncore_units_ignore = spr_uncore_units_ignore, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = UNCORE_DISCOVERY_TABLE_DEVICE, ++ .domain[0].units_ignore = spr_uncore_units_ignore, + }; + + static const struct uncore_plat_init gnr_uncore_init __initconst = { + .cpu_init = gnr_uncore_cpu_init, + .pci_init = gnr_uncore_pci_init, + .mmio_init = gnr_uncore_mmio_init, +- .use_discovery = true, +- .uncore_units_ignore = gnr_uncore_units_ignore, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = UNCORE_DISCOVERY_TABLE_DEVICE, ++ .domain[0].units_ignore = gnr_uncore_units_ignore, + }; + + static const struct uncore_plat_init generic_uncore_init __initconst = { +@@ -1897,6 +1899,17 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = { + }; + MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match); + ++static bool uncore_use_discovery(struct uncore_plat_init *config) ++{ ++ int i; ++ ++ for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) ++ if (config->domain[i].discovery_base) ++ return true; ++ ++ return false; ++} ++ + static int __init intel_uncore_init(void) + { + const struct x86_cpu_id *id; +@@ -1911,15 +1924,14 @@ static int __init intel_uncore_init(void) + + id = x86_match_cpu(intel_uncore_match); + if (!id) { +- if (!uncore_no_discover && uncore_discovery(NULL)) +- uncore_init = (struct uncore_plat_init *)&generic_uncore_init; +- else ++ uncore_init = (struct uncore_plat_init *)&generic_uncore_init; ++ if (uncore_no_discover || !uncore_discovery(uncore_init)) + return -ENODEV; + } else { + uncore_init = (struct uncore_plat_init *)id->driver_data; +- if (uncore_no_discover && uncore_init->use_discovery) ++ if (uncore_no_discover && uncore_use_discovery(uncore_init)) + return -ENODEV; +- if (uncore_init->use_discovery && ++ if (uncore_use_discovery(uncore_init) && + !uncore_discovery(uncore_init)) + return -ENODEV; + } +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 568536ef28ee6..1574ffc7ee053 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -47,14 +47,21 @@ struct uncore_event_desc; + struct freerunning_counters; + struct intel_uncore_topology; + ++struct uncore_discovery_domain { ++ /* MSR address or PCI device used as the discovery base */ ++ u32 discovery_base; ++ bool base_is_pci; ++ /* The units in the discovery table should be ignored. */ ++ int *units_ignore; ++}; ++ ++#define UNCORE_DISCOVERY_DOMAINS 2 + struct uncore_plat_init { + void (*cpu_init)(void); + int (*pci_init)(void); + void (*mmio_init)(void); +- /* Discovery table is required */ +- bool use_discovery; +- /* The units in the discovery table should be ignored. */ +- int *uncore_units_ignore; ++ ++ struct uncore_discovery_domain domain[UNCORE_DISCOVERY_DOMAINS]; + }; + + struct intel_uncore_type { +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index d39f6a0b8cc35..10957163301b9 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -259,23 +259,25 @@ uncore_insert_box_info(struct uncore_unit_discovery *unit, + } + + static bool +-uncore_ignore_unit(struct uncore_unit_discovery *unit, int *ignore) ++uncore_ignore_unit(struct uncore_unit_discovery *unit, ++ struct uncore_discovery_domain *domain) + { + int i; + +- if (!ignore) ++ if (!domain || !domain->units_ignore) + return false; + +- for (i = 0; ignore[i] != UNCORE_IGNORE_END ; i++) { +- if (unit->box_type == ignore[i]) ++ for (i = 0; domain->units_ignore[i] != UNCORE_IGNORE_END ; i++) { ++ if (unit->box_type == domain->units_ignore[i]) + return true; + } + + return false; + } + +-static int __parse_discovery_table(resource_size_t addr, int die, +- bool *parsed, int *ignore) ++static int __parse_discovery_table(char *type, ++ struct uncore_discovery_domain *domain, ++ resource_size_t addr, int die, bool *parsed) + { + struct uncore_global_discovery global; + struct uncore_unit_discovery unit; +@@ -314,7 +316,7 @@ static int __parse_discovery_table(resource_size_t addr, int die, + if (unit.access_type >= UNCORE_ACCESS_MAX) + continue; + +- if (uncore_ignore_unit(&unit, ignore)) ++ if (uncore_ignore_unit(&unit, domain)) + continue; + + uncore_insert_box_info(&unit, die); +@@ -325,9 +327,9 @@ static int __parse_discovery_table(resource_size_t addr, int die, + return 0; + } + +-static int parse_discovery_table(struct pci_dev *dev, int die, +- u32 bar_offset, bool *parsed, +- int *ignore) ++static int parse_discovery_table(struct uncore_discovery_domain *domain, ++ struct pci_dev *dev, int die, ++ u32 bar_offset, bool *parsed) + { + resource_size_t addr; + u32 val; +@@ -347,17 +349,19 @@ static int parse_discovery_table(struct pci_dev *dev, int die, + } + #endif + +- return __parse_discovery_table(addr, die, parsed, ignore); ++ return __parse_discovery_table("PCI", domain, addr, die, parsed); + } + +-static bool uncore_discovery_pci(int *ignore) ++static bool uncore_discovery_pci(struct uncore_discovery_domain *domain) + { + u32 device, val, entry_id, bar_offset; + int die, dvsec = 0, ret = true; + struct pci_dev *dev = NULL; + bool parsed = false; + +- if (has_generic_discovery_table()) ++ if (domain->discovery_base) ++ device = domain->discovery_base; ++ else if (has_generic_discovery_table()) + device = UNCORE_DISCOVERY_TABLE_DEVICE; + else + device = PCI_ANY_ID; +@@ -386,7 +390,7 @@ static bool uncore_discovery_pci(int *ignore) + if (die < 0) + continue; + +- parse_discovery_table(dev, die, bar_offset, &parsed, ignore); ++ parse_discovery_table(domain, dev, die, bar_offset, &parsed); + } + } + +@@ -399,11 +403,11 @@ static bool uncore_discovery_pci(int *ignore) + return ret; + } + +-static bool uncore_discovery_msr(int *ignore) ++static bool uncore_discovery_msr(struct uncore_discovery_domain *domain) + { + unsigned long *die_mask; + bool parsed = false; +- int cpu, die; ++ int cpu, die, msr; + u64 base; + + die_mask = kcalloc(BITS_TO_LONGS(uncore_max_dies()), +@@ -411,19 +415,22 @@ static bool uncore_discovery_msr(int *ignore) + if (!die_mask) + return false; + ++ msr = domain->discovery_base ? ++ domain->discovery_base : UNCORE_DISCOVERY_MSR; ++ + cpus_read_lock(); + for_each_online_cpu(cpu) { + die = topology_logical_die_id(cpu); + if (__test_and_set_bit(die, die_mask)) + continue; + +- if (rdmsrq_safe_on_cpu(cpu, UNCORE_DISCOVERY_MSR, &base)) ++ if (rdmsrq_safe_on_cpu(cpu, msr, &base)) + continue; + + if (!base) + continue; + +- __parse_discovery_table(base, die, &parsed, ignore); ++ __parse_discovery_table("MSR", domain, base, die, &parsed); + } + + cpus_read_unlock(); +@@ -434,10 +441,19 @@ static bool uncore_discovery_msr(int *ignore) + + bool uncore_discovery(struct uncore_plat_init *init) + { +- int *ignore = init ? init->uncore_units_ignore : NULL; ++ struct uncore_discovery_domain *domain; ++ bool ret = false; ++ int i; + +- return uncore_discovery_msr(ignore) || +- uncore_discovery_pci(ignore); ++ for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) { ++ domain = &init->domain[i]; ++ if (!domain->base_is_pci) ++ ret |= uncore_discovery_msr(domain); ++ else ++ ret |= uncore_discovery_pci(domain); ++ } ++ ++ return ret; + } + + void intel_uncore_clear_discovery_tables(void) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt b/SPECS/kernel-rt/0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt new file mode 100644 index 000000000..34c48796e --- /dev/null +++ b/SPECS/kernel-rt/0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt @@ -0,0 +1,371 @@ +From bd20eea645052e21bab1baadb1565b8dafafdac1 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:32 -0700 +Subject: [PATCH 3/6] platform/x86:intel/pmc: Rename PMC index variable to + pmc_idx +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Rename all PMC index variables to pmc_idx in core.c. This improves +code readability and consistency. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-5-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/core.c | 108 +++++++++++++------------- + 1 file changed, 54 insertions(+), 54 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index ca126a253f9d0..5f58dfa989ad5 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -312,20 +312,20 @@ static inline u8 pmc_core_reg_read_byte(struct pmc *pmc, int offset) + } + + static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip, +- int pmc_index, u8 pf_reg, const struct pmc_bit_map **pf_map) ++ int pmc_idx, u8 pf_reg, const struct pmc_bit_map **pf_map) + { + seq_printf(s, "PMC%d:PCH IP: %-2d - %-32s\tState: %s\n", +- pmc_index, ip, pf_map[idx][index].name, ++ pmc_idx, ip, pf_map[idx][index].name, + pf_map[idx][index].bit_mask & pf_reg ? "Off" : "On"); + } + + static int pmc_core_ppfear_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES]; + unsigned int index, iter, idx, ip = 0; +@@ -343,7 +343,7 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused) + for (idx = 0; maps[idx]; idx++) { + for (index = 0; maps[idx][index].name && + index < pmc->map->ppfear_buckets * 8; ip++, index++) +- pmc_core_display_map(s, index, idx, ip, i, ++ pmc_core_display_map(s, index, idx, ip, pmc_idx, + pf_regs[index / 8], maps); + } + } +@@ -472,7 +472,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) + struct pmc *pmc; + const struct pmc_reg_map *map; + u32 reg; +- unsigned int pmc_index; ++ unsigned int pmc_idx; + int ltr_index; + + ltr_index = value; +@@ -480,8 +480,8 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) + * is based on the contiguous indexes from ltr_show output. + * pmc index and ltr index needs to be calculated from it. + */ +- for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_index++) { +- pmc = pmcdev->pmcs[pmc_index]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_idx++) { ++ pmc = pmcdev->pmcs[pmc_idx]; + + if (!pmc) + continue; +@@ -498,10 +498,10 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) + ltr_index = ltr_index - (map->ltr_ignore_max + 2) - 1; + } + +- if (pmc_index >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) ++ if (pmc_idx >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) + return -EINVAL; + +- pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index); ++ pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_idx, ltr_index); + + guard(mutex)(&pmcdev->lock); + +@@ -636,14 +636,14 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) + u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val; + u32 ltr_raw_data, scale; + u16 snoop_ltr, nonsnoop_ltr; +- unsigned int i, index, ltr_index = 0; ++ unsigned int pmc_idx, index, ltr_index = 0; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc; + const struct pmc_bit_map *map; + u32 ltr_ign_reg; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -677,7 +677,7 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) + } + + seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n", +- ltr_index, i, map[index].name, ltr_raw_data, ++ ltr_index, pmc_idx, map[index].name, ltr_raw_data, + decoded_non_snoop_ltr, + decoded_snoop_ltr, ltr_ign_data); + ltr_index++; +@@ -690,15 +690,15 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr); + static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int pmcidx; ++ unsigned int pmc_idx; + +- for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { + const struct pmc_bit_map **maps; + unsigned int arr_size, r_idx; + u32 offset, counter; + struct pmc *pmc; + +- pmc = pmcdev->pmcs[pmcidx]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + maps = pmc->map->s0ix_blocker_maps; +@@ -712,7 +712,7 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) + if (!map->blk) + continue; + counter = pmc_core_reg_read(pmc, offset); +- seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx, ++ seq_printf(s, "PMC%d:%-30s %-30d\n", pmc_idx, + map->name, counter); + offset += map->blk * S0IX_BLK_SIZE; + } +@@ -724,13 +724,13 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker); + + static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) + { +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { + struct pmc *pmc; + u32 ltr_ign; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -751,12 +751,12 @@ static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) + + static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev) + { +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { + struct pmc *pmc; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -795,10 +795,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_res); + static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + u32 offset; + +@@ -806,7 +806,7 @@ static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) + continue; + maps = pmc->map->lpm_sts; + offset = pmc->map->lpm_status_offset; +- pmc_core_lpm_display(pmc, NULL, s, offset, i, "STATUS", maps); ++ pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "STATUS", maps); + } + + return 0; +@@ -816,10 +816,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_sts_regs); + static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + u32 offset; + +@@ -827,7 +827,7 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) + continue; + maps = pmc->map->lpm_sts; + offset = pmc->map->lpm_live_status_offset; +- pmc_core_lpm_display(pmc, NULL, s, offset, i, "LIVE_STATUS", maps); ++ pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "LIVE_STATUS", maps); + } + + return 0; +@@ -920,11 +920,11 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) + u32 sts_offset; + u32 sts_offset_live; + u32 *lpm_req_regs; +- unsigned int mp, pmc_index; ++ unsigned int mp, pmc_idx; + int num_maps; + +- for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) { +- struct pmc *pmc = pmcdev->pmcs[pmc_index]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + + if (!pmc) +@@ -945,7 +945,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) + continue; + + /* Display the header */ +- pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS); ++ pmc_core_substate_req_header_show(s, pmc_idx, HEADER_STATUS); + + /* Loop over maps */ + for (mp = 0; mp < num_maps; mp++) { +@@ -983,7 +983,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) + } + + /* Display the element name in the first column */ +- seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name); ++ seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name); + + /* Loop over the enabled states and display if required */ + pmc_for_each_mode(mode, pmcdev) { +@@ -1567,7 +1567,7 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + { + struct pci_dev *pcidev __free(pci_dev_put) = NULL; + struct telem_endpoint *ep; +- unsigned int i; ++ unsigned int pmc_idx; + u32 guid; + int ret; + +@@ -1575,10 +1575,10 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + if (!pcidev) + return -ENODEV; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -1610,7 +1610,7 @@ static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 + return NULL; + } + +-static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) ++static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) + + { + struct pmc_ssram_telemetry pmc_ssram_telemetry; +@@ -1618,7 +1618,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + struct pmc *pmc; + int ret; + +- ret = pmc_ssram_telemetry_get_pmc_info(pmc_index, &pmc_ssram_telemetry); ++ ret = pmc_ssram_telemetry_get_pmc_info(pmc_idx, &pmc_ssram_telemetry); + if (ret) + return ret; + +@@ -1626,7 +1626,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + if (!map) + return -ENODEV; + +- pmc = pmcdev->pmcs[pmc_index]; ++ pmc = pmcdev->pmcs[pmc_idx]; + /* Memory for primary PMC has been allocated */ + if (!pmc) { + pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL); +@@ -1643,7 +1643,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + return -ENOMEM; + } + +- pmcdev->pmcs[pmc_index] = pmc; ++ pmcdev->pmcs[pmc_idx] = pmc; + + return 0; + } +@@ -1715,8 +1715,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) + return 0; + + unmap_regbase: +- for (unsigned int i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (unsigned int pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + + if (pmc && pmc->regbase) + iounmap(pmc->regbase); +@@ -1809,10 +1809,10 @@ static void pmc_core_do_dmi_quirks(struct pmc *pmc) + static void pmc_core_clean_structure(struct platform_device *pdev) + { + struct pmc_dev *pmcdev = platform_get_drvdata(pdev); +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + + if (pmc && pmc->regbase) + iounmap(pmc->regbase); +@@ -1972,7 +1972,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) + struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; + const struct pmc_bit_map **maps = pmc->map->lpm_sts; + int offset = pmc->map->lpm_status_offset; +- unsigned int i; ++ unsigned int pmc_idx, i; + + /* Check if the syspend used S0ix */ + if (pm_suspend_via_firmware()) +@@ -2010,13 +2010,13 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) + if (pmc->map->slps0_dbg_maps) + pmc_core_slps0_display(pmc, dev, NULL); + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + + if (!pmc) + continue; + if (pmc->map->lpm_sts) +- pmc_core_lpm_display(pmc, dev, NULL, offset, i, "STATUS", maps); ++ pmc_core_lpm_display(pmc, dev, NULL, offset, pmc_idx, "STATUS", maps); + } + + return 0; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal b/SPECS/kernel-rt/0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal deleted file mode 100644 index 996d8fa2a..000000000 --- a/SPECS/kernel-rt/0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal +++ /dev/null @@ -1,33 +0,0 @@ -From d307ddbc6471709664c7c7e0d75e5a213d901416 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:13 -0700 -Subject: [PATCH 03/11] thermal: intel: int340x: Enable power slider interface - for Panther Lake - -Set the PROC_THERMAL_FEATURE_SOC_POWER_SLIDER feature flag in -proc_thermal_pci_ids[] for Panther Lake to enable power slider interface. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-3-srinivas.pandruvada@linux.intel.com -Signed-off-by: Rafael J. Wysocki ---- - .../intel/int340x_thermal/processor_thermal_device_pci.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c -index d4d7e8e147d2..e2471768d355 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c -@@ -498,7 +498,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { - { PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL | - PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS | - PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT | -- PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) }, -+ PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC | -+ PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) }, - { PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT | - PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | - PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT | --- -2.43.0 - diff --git a/SPECS/kernel-rt/0003-tools-power-turbostat-Refactor-added-column-header-p.turbo b/SPECS/kernel-rt/0003-tools-power-turbostat-Refactor-added-column-header-p.turbo new file mode 100644 index 000000000..257a5858c --- /dev/null +++ b/SPECS/kernel-rt/0003-tools-power-turbostat-Refactor-added-column-header-p.turbo @@ -0,0 +1,216 @@ +From 34544cbf2afc6465c4f19762dc10950293ec1a2c Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 17:19:43 -0300 +Subject: [PATCH 03/21] tools/power turbostat: Refactor added column header + printing + +Over time, we built up many copies of nearly identical code... + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 137 +++++++------------------- + 1 file changed, 36 insertions(+), 101 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 7c24c2f9a0752..5d753df8706d0 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2705,6 +2705,24 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + } + } + ++/* ++ * print_name() ++ * Print column header name for 64-bit counter in 16 columns (at least 8-char plus a tab) ++ * Otherwise, allow the name + tab to fit within 8-coumn tab-stop. ++ * In both cases, left justififed, just like other turbostat columns, ++ * to allow the column values to consume the tab. ++ * ++ * Yes, 32-bit counters can overflow 8-columns, but then they are usually 64-bit counters. ++ * 64-bit counters can overflow 16-columns, but rarely do. ++ */ ++static inline int print_name(int width, int *printed, char *delim, char *name) ++{ ++ if (width == 64) ++ return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); ++ else ++ return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); ++} ++ + void print_header(char *delim) + { + struct msr_counter *mp; +@@ -2760,50 +2778,22 @@ void print_header(char *delim) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%sSMI", (printed++ ? delim : "")); + +- for (mp = sys.tp; mp; mp = mp->next) { ++ for (mp = sys.tp; mp; mp = mp->next) ++ outp += print_name(mp->width, &printed, delim, mp->name); + +- if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { +- if (mp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), mp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), mp->name); +- } else { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), mp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), mp->name); +- } +- } +- +- for (pp = sys.perf_tp; pp; pp = pp->next) { +- +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name); +- } else { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name); +- } +- } ++ for (pp = sys.perf_tp; pp; pp = pp->next) ++ outp += print_name(pp->width, &printed, delim, pp->name); + + ppmt = sys.pmt_tp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name); +- else +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name); +- ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name); + break; + } + +@@ -2836,49 +2826,23 @@ void print_header(char *delim) + outp += sprintf(outp, "%sCor_J", (printed++ ? delim : "")); + } + +- for (mp = sys.cp; mp; mp = mp->next) { +- if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { +- if (mp->width == 64) +- outp += sprintf(outp, "%s%18.18s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%10.10s", delim, mp->name); +- } else { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%s", delim, mp->name); +- } +- } +- +- for (pp = sys.perf_cp; pp; pp = pp->next) { ++ for (mp = sys.cp; mp; mp = mp->next) ++ outp += print_name(mp->width, &printed, delim, mp->name); + +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name); +- } else { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name); +- } +- } ++ for (pp = sys.perf_cp; pp; pp = pp->next) ++ outp += print_name(pp->width, &printed, delim, pp->name); + + ppmt = sys.pmt_cp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name); +- else +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); + + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name); + break; + } + +@@ -2966,51 +2930,22 @@ void print_header(char *delim) + if (DO_BIC(BIC_UNCORE_MHZ)) + outp += sprintf(outp, "%sUncMHz", (printed++ ? delim : "")); + +- for (mp = sys.pp; mp; mp = mp->next) { +- if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { +- if (mp->width == 64) +- outp += sprintf(outp, "%s%18.18s", delim, mp->name); +- else if (mp->width == 32) +- outp += sprintf(outp, "%s%10.10s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%7.7s", delim, mp->name); +- } else { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%7.7s", delim, mp->name); +- } +- } +- +- for (pp = sys.perf_pp; pp; pp = pp->next) { ++ for (mp = sys.pp; mp; mp = mp->next) ++ outp += print_name(mp->width, &printed, delim, mp->name); + +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name); +- } else { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name); +- } +- } ++ for (pp = sys.perf_pp; pp; pp = pp->next) ++ outp += print_name(pp->width, &printed, delim, pp->name); + + ppmt = sys.pmt_pp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name); +- else +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name); +- ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name); + break; + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss b/SPECS/kernel-rt/0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss deleted file mode 100644 index ac273cb98..000000000 --- a/SPECS/kernel-rt/0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss +++ /dev/null @@ -1,50 +0,0 @@ -From 574db93bef3983313bf866d37c250c1076a22e76 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Tue, 12 Nov 2019 15:41:42 +0300 -Subject: [PATCH 04/16] ACPI / hotplug / PCI: Take runtime PM autosuspend into - account - -PCIe ports (the only ones we do runtime PM) are using runtime PM -autosuspend to keep the port powered on for a while after it becomes -idle. However, ACPI hotplug does not take this into account so if we get -multiple hotplug events in a short period of time we may be powering -ports on and off and then back on unnecessarily. - -For this reason call pm_runtime_put_autosuspend() for them (with the -accompanying pm_runtime_mark_last_busy()). - -Signed-off-by: Mika Westerberg ---- - drivers/pci/hotplug/acpiphp_glue.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c -index 5b1f271c6034..58686705824d 100644 ---- a/drivers/pci/hotplug/acpiphp_glue.c -+++ b/drivers/pci/hotplug/acpiphp_glue.c -@@ -683,7 +683,8 @@ static void trim_stale_devices(struct pci_dev *dev) - list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list) - trim_stale_devices(child); - -- pm_runtime_put(&dev->dev); -+ pm_runtime_mark_last_busy(&dev->dev); -+ pm_runtime_put_autosuspend(&dev->dev); - } - } - -@@ -725,8 +726,10 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) - } - } - -- if (bridge->pci_dev) -- pm_runtime_put(&bridge->pci_dev->dev); -+ if (bridge->pci_dev) { -+ pm_runtime_mark_last_busy(&bridge->pci_dev->dev); -+ pm_runtime_put_autosuspend(&bridge->pci_dev->dev); -+ } - } - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac b/SPECS/kernel-rt/0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac deleted file mode 100644 index 95e7e27aa..000000000 --- a/SPECS/kernel-rt/0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac +++ /dev/null @@ -1,95 +0,0 @@ -From 0d54237ef5bc3d6d48ebe4a737f963130089726d Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Wed, 23 Jul 2025 13:44:54 +0800 -Subject: [PATCH 04/13] EDAC/skx_common: Swap memory controller index mapping - -The current mapping of memory controller indices is from physical index [1] -to logical index [2], as show below: - - skx_dev->imc[pmc].mc_mapping = lmc - -Since skx_dev->imc[] is an array of present memory controller instances, -mapping memory controller indices from logical index to physical index, -as show below, is more reasonable. This is also a preparatory step for -making skx_dev->imc[] a flexible array. - - skx_dev->imc[lmc].mc_mapping = pmc - -Both mappings are equivalent. No functional changes intended. - -[1] Indices for memory controllers include both those present to the - OS and those disabled by BIOS. - -[2] Indices for memory controllers present to the OS. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 28 ++++++++++++++++++++-------- - 1 file changed, 20 insertions(+), 8 deletions(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index 3f6a074d685c..e03268e9073f 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -130,7 +130,7 @@ static void skx_init_mc_mapping(struct skx_dev *d) - * the logical indices of the memory controllers enumerated by the - * EDAC driver. - */ -- for (int i = 0; i < NUM_IMC; i++) -+ for (int i = 0; i < d->num_imc; i++) - d->imc[i].mc_mapping = i; - } - -@@ -139,22 +139,28 @@ void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc) - edac_dbg(0, "Set the mapping of mc phy idx to logical idx: %02d -> %02d\n", - pmc, lmc); - -- d->imc[pmc].mc_mapping = lmc; -+ d->imc[lmc].mc_mapping = pmc; - } - EXPORT_SYMBOL_GPL(skx_set_mc_mapping); - --static u8 skx_get_mc_mapping(struct skx_dev *d, u8 pmc) -+static int skx_get_mc_mapping(struct skx_dev *d, u8 pmc) - { -- edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n", -- pmc, d->imc[pmc].mc_mapping); -+ for (int lmc = 0; lmc < d->num_imc; lmc++) { -+ if (d->imc[lmc].mc_mapping == pmc) { -+ edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n", -+ pmc, lmc); - -- return d->imc[pmc].mc_mapping; -+ return lmc; -+ } -+ } -+ -+ return -1; - } - - static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) - { -+ int i, lmc, len = 0; - struct skx_dev *d; -- int i, len = 0; - - if (res->addr >= skx_tohm || (res->addr >= skx_tolm && - res->addr < BIT_ULL(32))) { -@@ -218,7 +224,13 @@ static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) - return false; - } - -- res->imc = skx_get_mc_mapping(d, res->imc); -+ lmc = skx_get_mc_mapping(d, res->imc); -+ if (lmc < 0) { -+ skx_printk(KERN_ERR, "No lmc for imc %d\n", res->imc); -+ return false; -+ } -+ -+ res->imc = lmc; - - for (i = 0; i < adxl_component_count; i++) { - if (adxl_values[i] == ~0x0ull) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi b/SPECS/kernel-rt/0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi deleted file mode 100644 index ce98f839b..000000000 --- a/SPECS/kernel-rt/0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi +++ /dev/null @@ -1,68 +0,0 @@ -From 165783514c08480544f0318242343073c2feb6b3 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Fri, 17 Nov 2023 21:34:27 -0800 -Subject: [PATCH 04/44] KVM: VMX: Disable FRED if FRED consistency checks fail - -Do not virtualize FRED if FRED consistency checks fail. - -Either on broken hardware, or when run KVM on top of another hypervisor -before the underlying hypervisor implements nested FRED correctly. - -Suggested-by: Chao Gao -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Reviewed-by: Chao Gao -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Drop the cpu_feature_enabled() in cpu_has_vmx_fred() (Sean). -* Add TB from Xuelian Guo. - -Change in v4: -* Call out the reason why not check FRED VM-exit controls in - cpu_has_vmx_fred() (Chao Gao). ---- - arch/x86/kvm/vmx/capabilities.h | 10 ++++++++++ - arch/x86/kvm/vmx/vmx.c | 3 +++ - 2 files changed, 13 insertions(+) - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 422d2846980c..1100291d42ea 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -412,6 +412,16 @@ static inline bool vmx_pebs_supported(void) - !enable_mediated_pmu; - } - -+static inline bool cpu_has_vmx_fred(void) -+{ -+ /* -+ * setup_vmcs_config() guarantees FRED VM-entry/exit controls -+ * are either all set or none. So, no need to check FRED VM-exit -+ * controls. -+ */ -+ return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED); -+} -+ - static inline bool cpu_has_notify_vmexit(void) - { - return vmcs_config.cpu_based_2nd_exec_ctrl & -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index aa23ecf18cda..24c447935e9b 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -8041,6 +8041,9 @@ static __init void vmx_set_cpu_caps(void) - kvm_cpu_cap_check_and_set(X86_FEATURE_DTES64); - } - -+ if (!cpu_has_vmx_fred()) -+ kvm_cpu_cap_clear(X86_FEATURE_FRED); -+ - if (!enable_pmu) - kvm_cpu_cap_clear(X86_FEATURE_PDCM); - kvm_caps.supported_perf_cap = vmx_get_perf_capabilities(); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet b/SPECS/kernel-rt/0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet deleted file mode 100644 index 2acc5420f..000000000 --- a/SPECS/kernel-rt/0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet +++ /dev/null @@ -1,89 +0,0 @@ -From 11cb235bf8f729277eb398a45062222e55e95f2f Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 4 Jul 2025 01:49:34 -0700 -Subject: [PATCH 04/24] KVM: x86: Manually clear MPX state only on INIT - -Don't manually clear/zero MPX state on RESET, as the guest FPU state is -zero allocated and KVM only does RESET during vCPU creation, i.e. the -relevant state is guaranteed to be all zeroes. - -Opportunistically move the relevant code into a helper in anticipation of -adding support for CET shadow stacks, which also has state that is zeroed -on INIT. - -Signed-off-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 46 ++++++++++++++++++++++++++++++---------------- - 1 file changed, 30 insertions(+), 16 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 7a3a62adab95..a72f5f986984 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -12395,6 +12395,35 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) - kvfree(vcpu->arch.cpuid_entries); - } - -+static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) -+{ -+ struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate; -+ -+ /* -+ * Guest FPU state is zero allocated and so doesn't need to be manually -+ * cleared on RESET, i.e. during vCPU creation. -+ */ -+ if (!init_event || !fpstate) -+ return; -+ -+ /* -+ * On INIT, only select XSTATE components are zeroed, most components -+ * are unchanged. Currently, the only components that are zeroed and -+ * supported by KVM are MPX related. -+ */ -+ if (!kvm_mpx_supported()) -+ return; -+ -+ /* -+ * All paths that lead to INIT are required to load the guest's FPU -+ * state (because most paths are buried in KVM_RUN). -+ */ -+ kvm_put_guest_fpu(vcpu); -+ fpstate_clear_xstate_component(fpstate, XFEATURE_BNDREGS); -+ fpstate_clear_xstate_component(fpstate, XFEATURE_BNDCSR); -+ kvm_load_guest_fpu(vcpu); -+} -+ - void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - { - struct kvm_cpuid_entry2 *cpuid_0x1; -@@ -12452,22 +12481,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - kvm_async_pf_hash_reset(vcpu); - vcpu->arch.apf.halted = false; - -- if (vcpu->arch.guest_fpu.fpstate && kvm_mpx_supported()) { -- struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate; -- -- /* -- * All paths that lead to INIT are required to load the guest's -- * FPU state (because most paths are buried in KVM_RUN). -- */ -- if (init_event) -- kvm_put_guest_fpu(vcpu); -- -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDREGS); -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDCSR); -- -- if (init_event) -- kvm_load_guest_fpu(vcpu); -- } -+ kvm_xstate_reset(vcpu, init_event); - - if (!init_event) { - vcpu->arch.smbase = 0x30000; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl b/SPECS/kernel-rt/0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl new file mode 100644 index 000000000..6115857ef --- /dev/null +++ b/SPECS/kernel-rt/0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl @@ -0,0 +1,97 @@ +From 7e185270dc31e49b386520fae18a574bcd9613c6 Mon Sep 17 00:00:00 2001 +From: Christian Loehle +Date: Mon, 10 Nov 2025 12:08:19 +0000 +Subject: [PATCH 04/17] cpuidle: teo: Use this_cpu_ptr() where possible + +The cpuidle governor callbacks for update, select and reflect +are always running on the actual idle entering/exiting CPU, so +use the more optimized this_cpu_ptr() to access the internal teo +data. + +This brings down the latency-critical teo_reflect() from +static void teo_reflect(struct cpuidle_device *dev, int state) +{ +ffffffc080ffcff0: hint #0x19 +ffffffc080ffcff4: stp x29, x30, [sp, #-48]! + struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); +ffffffc080ffcff8: adrp x2, ffffffc0848c0000 +{ +ffffffc080ffcffc: add x29, sp, #0x0 +ffffffc080ffd000: stp x19, x20, [sp, #16] +ffffffc080ffd004: orr x20, xzr, x0 + struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); +ffffffc080ffd008: add x0, x2, #0xc20 +{ +ffffffc080ffd00c: stp x21, x22, [sp, #32] + struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); +ffffffc080ffd010: adrp x19, ffffffc083eb5000 +ffffffc080ffd014: add x19, x19, #0xbb0 +ffffffc080ffd018: ldr w3, [x20, #4] + + dev->last_state_idx = state; + +to + +static void teo_reflect(struct cpuidle_device *dev, int state) +{ +ffffffc080ffd034: hint #0x19 +ffffffc080ffd038: stp x29, x30, [sp, #-48]! +ffffffc080ffd03c: add x29, sp, #0x0 +ffffffc080ffd040: stp x19, x20, [sp, #16] +ffffffc080ffd044: orr x20, xzr, x0 + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); +ffffffc080ffd048: adrp x19, ffffffc083eb5000 +{ +ffffffc080ffd04c: stp x21, x22, [sp, #32] + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); +ffffffc080ffd050: add x19, x19, #0xbb0 + + dev->last_state_idx = state; + +This saves us: + adrp x2, ffffffc0848c0000 + add x0, x2, #0xc20 + ldr w3, [x20, #4] + +Signed-off-by: Christian Loehle +[ rjw: Subject tweak ] +Link: https://patch.msgid.link/20251110120819.714560-1-christian.loehle@arm.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/governors/teo.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index bfa55c1eab5bc..a3ebc2cda0933 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -155,7 +155,7 @@ static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); + */ + static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + { +- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); ++ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + int i, idx_timer = 0, idx_duration = 0; + s64 target_residency_ns; + u64 measured_ns; +@@ -268,7 +268,7 @@ static int teo_find_shallower_state(struct cpuidle_driver *drv, + static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + bool *stop_tick) + { +- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); ++ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + s64 latency_req = cpuidle_governor_latency_req(dev->cpu); + ktime_t delta_tick = TICK_NSEC / 2; + unsigned int idx_intercept_sum = 0; +@@ -504,7 +504,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + */ + static void teo_reflect(struct cpuidle_device *dev, int state) + { +- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); ++ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + + dev->last_state_idx = state; + if (dev->poll_time_limit || +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt b/SPECS/kernel-rt/0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt deleted file mode 100644 index ebcb5aa3a..000000000 --- a/SPECS/kernel-rt/0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt +++ /dev/null @@ -1,83 +0,0 @@ -From 8b1eeb83d121280a2f8f91c616e80b79a0a6223a Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Thu, 6 Dec 2018 09:52:20 +0100 -Subject: [PATCH 4/9] drm/i915: Disable tracing points on PREEMPT_RT - -Luca Abeni reported this: -| BUG: scheduling while atomic: kworker/u8:2/15203/0x00000003 -| CPU: 1 PID: 15203 Comm: kworker/u8:2 Not tainted 4.19.1-rt3 #10 -| Call Trace: -| rt_spin_lock+0x3f/0x50 -| gen6_read32+0x45/0x1d0 [i915] -| g4x_get_vblank_counter+0x36/0x40 [i915] -| trace_event_raw_event_i915_pipe_update_start+0x7d/0xf0 [i915] - -The tracing events use trace_intel_pipe_update_start() among other events -use functions acquire spinlock_t locks which are transformed into -sleeping locks on PREEMPT_RT. A few trace points use -intel_get_crtc_scanline(), others use ->get_vblank_counter() wich also -might acquire a sleeping locks on PREEMPT_RT. -At the time the arguments are evaluated within trace point, preemption -is disabled and so the locks must not be acquired on PREEMPT_RT. - -Based on this I don't see any other way than disable trace points on -PREMPT_RT. - -Acked-by: Tvrtko Ursulin -Reported-by: Luca Abeni -Cc: Steven Rostedt -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/display/intel_display_trace.h | 4 ++++ - drivers/gpu/drm/i915/i915_trace.h | 4 ++++ - drivers/gpu/drm/i915/intel_uncore_trace.h | 4 ++++ - 3 files changed, 12 insertions(+) - -diff --git a/drivers/gpu/drm/i915/display/intel_display_trace.h b/drivers/gpu/drm/i915/display/intel_display_trace.h -index 27ebc32cb61a..a519d94700c3 100644 ---- a/drivers/gpu/drm/i915/display/intel_display_trace.h -+++ b/drivers/gpu/drm/i915/display/intel_display_trace.h -@@ -13,6 +13,10 @@ - #if !defined(__INTEL_DISPLAY_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) - #define __INTEL_DISPLAY_TRACE_H__ - -+#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) -+#define NOTRACE -+#endif -+ - #include - #include - #include -diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h -index 7ed41ce9b708..6b87ef6005c6 100644 ---- a/drivers/gpu/drm/i915/i915_trace.h -+++ b/drivers/gpu/drm/i915/i915_trace.h -@@ -6,6 +6,10 @@ - #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) - #define _I915_TRACE_H_ - -+#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) -+#define NOTRACE -+#endif -+ - #include - #include - #include -diff --git a/drivers/gpu/drm/i915/intel_uncore_trace.h b/drivers/gpu/drm/i915/intel_uncore_trace.h -index f13ff71edf2d..3c67e267fb60 100644 ---- a/drivers/gpu/drm/i915/intel_uncore_trace.h -+++ b/drivers/gpu/drm/i915/intel_uncore_trace.h -@@ -7,6 +7,10 @@ - #if !defined(__INTEL_UNCORE_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) - #define __INTEL_UNCORE_TRACE_H__ - -+#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) -+#define NOTRACE -+#endif -+ - #include "i915_reg_defs.h" - - #include --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt b/SPECS/kernel-rt/0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt new file mode 100644 index 000000000..19132ca85 --- /dev/null +++ b/SPECS/kernel-rt/0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt @@ -0,0 +1,94 @@ +From 71e4d60f9596e1bd36d28f8f1b84b1e2ee16bfa6 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Wed, 8 Sep 2021 19:03:41 +0200 +Subject: [PATCH 4/9] drm/i915/gt: Use spin_lock_irq() instead of + local_irq_disable() + spin_lock() + +execlists_dequeue() is invoked from a function which uses +local_irq_disable() to disable interrupts so the spin_lock() behaves +like spin_lock_irq(). +This breaks PREEMPT_RT because local_irq_disable() + spin_lock() is not +the same as spin_lock_irq(). + +execlists_dequeue_irq() and execlists_dequeue() has each one caller +only. If intel_engine_cs::active::lock is acquired and released with the +_irq suffix then it behaves almost as if execlists_dequeue() would be +invoked with disabled interrupts. The difference is the last part of the +function which is then invoked with enabled interrupts. +I can't tell if this makes a difference. From looking at it, it might +work to move the last unlock at the end of the function as I didn't find +anything that would acquire the lock again. + +Reported-by: Clark Williams +Signed-off-by: Sebastian Andrzej Siewior +Reviewed-by: Maarten Lankhorst +--- + .../drm/i915/gt/intel_execlists_submission.c | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +index 7f389cb0bde44..607c9a7b3a0a6 100644 +--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c ++++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +@@ -1298,7 +1298,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + * and context switches) submission. + */ + +- spin_lock(&sched_engine->lock); ++ spin_lock_irq(&sched_engine->lock); + + /* + * If the queue is higher priority than the last +@@ -1398,7 +1398,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + * Even if ELSP[1] is occupied and not worthy + * of timeslices, our queue might be. + */ +- spin_unlock(&sched_engine->lock); ++ spin_unlock_irq(&sched_engine->lock); + return; + } + } +@@ -1424,7 +1424,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + + if (last && !can_merge_rq(last, rq)) { + spin_unlock(&ve->base.sched_engine->lock); +- spin_unlock(&engine->sched_engine->lock); ++ spin_unlock_irq(&engine->sched_engine->lock); + return; /* leave this for another sibling */ + } + +@@ -1586,7 +1586,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + */ + sched_engine->queue_priority_hint = queue_prio(sched_engine); + i915_sched_engine_reset_on_empty(sched_engine); +- spin_unlock(&sched_engine->lock); ++ spin_unlock_irq(&sched_engine->lock); + + /* + * We can skip poking the HW if we ended up with exactly the same set +@@ -1612,13 +1612,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + } + } + +-static void execlists_dequeue_irq(struct intel_engine_cs *engine) +-{ +- local_irq_disable(); /* Suspend interrupts across request submission */ +- execlists_dequeue(engine); +- local_irq_enable(); /* flush irq_work (e.g. breadcrumb enabling) */ +-} +- + static void clear_ports(struct i915_request **ports, int count) + { + memset_p((void **)ports, NULL, count); +@@ -2473,7 +2466,7 @@ static void execlists_submission_tasklet(struct tasklet_struct *t) + } + + if (!engine->execlists.pending[0]) { +- execlists_dequeue_irq(engine); ++ execlists_dequeue(engine); + start_timeslice(engine); + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c b/SPECS/kernel-rt/0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c deleted file mode 100644 index 56c27efd4..000000000 --- a/SPECS/kernel-rt/0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c +++ /dev/null @@ -1,194 +0,0 @@ -From 25bca0aade9d59b2c2977c68b8d2d0ae91873c30 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:29 +0300 -Subject: [PATCH 04/11] i3c: mipi-i3c-hci: Use physical device pointer with DMA - API - -DMA transfer faults on Intel hardware when the IOMMU is enabled and -driver initialization will fail when attempting to do the first transfer: - - DMAR: DRHD: handling fault status reg 2 - DMAR: [DMA Read NO_PASID] Request device [00:11.0] fault addr 0x676e3000 [fault reason 0x71] SM: Present bit in first-level paging entry is clear - i3c mipi-i3c-hci.0: ring 0: Transfer Aborted - mipi-i3c-hci mipi-i3c-hci.0: probe with driver mipi-i3c-hci failed with error -62 - -Reason for this is that the IOMMU setup is done for the physical devices -only and not for the virtual I3C Controller device object. - -Therefore use the pointer to a physical device object with the DMA API. - -Due to a data corruption observation when the device DMA is IOMMU -mapped, a properly sized receive bounce buffer is required if transfer -length is not a multiple of DWORDs. - -Reported-by: -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-4-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/dma.c | 46 +++++++++++++++++++-------- - 1 file changed, 33 insertions(+), 13 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 351851859f02..09688ada4912 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - - #include "hci.h" - #include "cmd.h" -@@ -138,6 +139,7 @@ struct hci_rh_data { - }; - - struct hci_rings_data { -+ struct device *sysdev; - unsigned int total; - struct hci_rh_data headers[] __counted_by(total); - }; -@@ -165,20 +167,20 @@ static void hci_dma_cleanup(struct i3c_hci *hci) - rh_reg_write(IBI_SETUP, 0); - - if (rh->xfer) -- dma_free_coherent(&hci->master.dev, -+ dma_free_coherent(rings->sysdev, - rh->xfer_struct_sz * rh->xfer_entries, - rh->xfer, rh->xfer_dma); - if (rh->resp) -- dma_free_coherent(&hci->master.dev, -+ dma_free_coherent(rings->sysdev, - rh->resp_struct_sz * rh->xfer_entries, - rh->resp, rh->resp_dma); - kfree(rh->src_xfers); - if (rh->ibi_status) -- dma_free_coherent(&hci->master.dev, -+ dma_free_coherent(rings->sysdev, - rh->ibi_status_sz * rh->ibi_status_entries, - rh->ibi_status, rh->ibi_status_dma); - if (rh->ibi_data_dma) -- dma_unmap_single(&hci->master.dev, rh->ibi_data_dma, -+ dma_unmap_single(rings->sysdev, rh->ibi_data_dma, - rh->ibi_chunk_sz * rh->ibi_chunks_total, - DMA_FROM_DEVICE); - kfree(rh->ibi_data); -@@ -194,11 +196,23 @@ static int hci_dma_init(struct i3c_hci *hci) - { - struct hci_rings_data *rings; - struct hci_rh_data *rh; -+ struct device *sysdev; - u32 regval; - unsigned int i, nr_rings, xfers_sz, resps_sz; - unsigned int ibi_status_ring_sz, ibi_data_ring_sz; - int ret; - -+ /* -+ * Set pointer to a physical device that does DMA and has IOMMU setup -+ * done for it in case of enabled IOMMU and use it with the DMA API. -+ * Here such device is either -+ * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or -+ * grandparent (PCI enumeration). -+ */ -+ sysdev = hci->master.dev.parent; -+ if (sysdev->parent && dev_is_pci(sysdev->parent)) -+ sysdev = sysdev->parent; -+ - regval = rhs_reg_read(CONTROL); - nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval); - dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings); -@@ -213,6 +227,7 @@ static int hci_dma_init(struct i3c_hci *hci) - return -ENOMEM; - hci->io_data = rings; - rings->total = nr_rings; -+ rings->sysdev = sysdev; - - regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total); - rhs_reg_write(CONTROL, regval); -@@ -239,9 +254,9 @@ static int hci_dma_init(struct i3c_hci *hci) - xfers_sz = rh->xfer_struct_sz * rh->xfer_entries; - resps_sz = rh->resp_struct_sz * rh->xfer_entries; - -- rh->xfer = dma_alloc_coherent(&hci->master.dev, xfers_sz, -+ rh->xfer = dma_alloc_coherent(rings->sysdev, xfers_sz, - &rh->xfer_dma, GFP_KERNEL); -- rh->resp = dma_alloc_coherent(&hci->master.dev, resps_sz, -+ rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz, - &rh->resp_dma, GFP_KERNEL); - rh->src_xfers = - kmalloc_array(rh->xfer_entries, sizeof(*rh->src_xfers), -@@ -295,16 +310,16 @@ static int hci_dma_init(struct i3c_hci *hci) - ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total; - - rh->ibi_status = -- dma_alloc_coherent(&hci->master.dev, ibi_status_ring_sz, -+ dma_alloc_coherent(rings->sysdev, ibi_status_ring_sz, - &rh->ibi_status_dma, GFP_KERNEL); - rh->ibi_data = kmalloc(ibi_data_ring_sz, GFP_KERNEL); - ret = -ENOMEM; - if (!rh->ibi_status || !rh->ibi_data) - goto err_out; - rh->ibi_data_dma = -- dma_map_single(&hci->master.dev, rh->ibi_data, -+ dma_map_single(rings->sysdev, rh->ibi_data, - ibi_data_ring_sz, DMA_FROM_DEVICE); -- if (dma_mapping_error(&hci->master.dev, rh->ibi_data_dma)) { -+ if (dma_mapping_error(rings->sysdev, rh->ibi_data_dma)) { - rh->ibi_data_dma = 0; - ret = -ENOMEM; - goto err_out; -@@ -372,6 +387,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr; - enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE : - DMA_TO_DEVICE; -+ bool need_bounce; - - /* store cmd descriptor */ - *ring_data++ = xfer->cmd_desc[0]; -@@ -390,10 +406,13 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - - /* 2nd and 3rd words of Data Buffer Descriptor Structure */ - if (xfer->data) { -- xfer->dma = i3c_master_dma_map_single(&hci->master.dev, -+ need_bounce = device_iommu_mapped(rings->sysdev) && -+ xfer->rnw && -+ xfer->data_len != ALIGN(xfer->data_len, 4); -+ xfer->dma = i3c_master_dma_map_single(rings->sysdev, - xfer->data, - xfer->data_len, -- false, -+ need_bounce, - dir); - if (!xfer->dma) { - hci_dma_unmap_xfer(hci, xfer_list, i); -@@ -581,6 +600,7 @@ static void hci_dma_recycle_ibi_slot(struct i3c_hci *hci, - - static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - { -+ struct hci_rings_data *rings = hci->io_data; - struct i3c_dev_desc *dev; - struct i3c_hci_dev_data *dev_data; - struct hci_dma_dev_ibi_data *dev_ibi; -@@ -691,7 +711,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - * rh->ibi_chunk_sz; - if (first_part > ibi_size) - first_part = ibi_size; -- dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma, -+ dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma, - first_part, DMA_FROM_DEVICE); - memcpy(slot->data, ring_ibi_data, first_part); - -@@ -700,7 +720,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - /* we wrap back to the start and copy remaining data */ - ring_ibi_data = rh->ibi_data; - ring_ibi_data_dma = rh->ibi_data_dma; -- dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma, -+ dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma, - ibi_size - first_part, DMA_FROM_DEVICE); - memcpy(slot->data + first_part, ring_ibi_data, - ibi_size - first_part); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-issei-add-heci-hardware-module.security b/SPECS/kernel-rt/0004-issei-add-heci-hardware-module.security index 846da87b5..076f29c9e 100644 --- a/SPECS/kernel-rt/0004-issei-add-heci-hardware-module.security +++ b/SPECS/kernel-rt/0004-issei-add-heci-hardware-module.security @@ -1,7 +1,7 @@ -From 973f2c7ef086e185271b1e8b12f3a9383a087b26 Mon Sep 17 00:00:00 2001 +From dfc608acd03571d2a2093d92a2c4ed4f372cd908 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 31 Oct 2024 10:08:57 +0200 -Subject: [PATCH 4/5] issei: add heci hardware module +Subject: [PATCH 4/7] issei: add heci hardware module Add support for the ISSEI (Intel Silicon Security Engine Interface) HECI PCI devices. @@ -16,18 +16,18 @@ Signed-off-by: Alexander Usyskin --- drivers/misc/issei/Kconfig | 14 + drivers/misc/issei/Makefile | 4 + - drivers/misc/issei/hw_heci.c | 552 ++++++++++++++++++++++++++++++ + drivers/misc/issei/hw_heci.c | 548 ++++++++++++++++++++++++++++++ drivers/misc/issei/hw_heci.h | 54 +++ drivers/misc/issei/hw_heci_regs.h | 35 ++ - drivers/misc/issei/pci_heci.c | 166 +++++++++ - 6 files changed, 825 insertions(+) + drivers/misc/issei/pci_heci.c | 144 ++++++++ + 6 files changed, 799 insertions(+) create mode 100644 drivers/misc/issei/hw_heci.c create mode 100644 drivers/misc/issei/hw_heci.h create mode 100644 drivers/misc/issei/hw_heci_regs.h create mode 100644 drivers/misc/issei/pci_heci.c diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig -index ffd027e826df..5fbdcb30e20d 100644 +index ffd027e826dfd..5fbdcb30e20d5 100644 --- a/drivers/misc/issei/Kconfig +++ b/drivers/misc/issei/Kconfig @@ -11,3 +11,17 @@ config INTEL_SSEI @@ -49,7 +49,7 @@ index ffd027e826df..5fbdcb30e20d 100644 + +endif diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile -index 712d62eb9790..fb904fda3949 100644 +index 712d62eb97900..fb904fda39499 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -9,3 +9,7 @@ issei-objs += fw_client.o @@ -62,10 +62,10 @@ index 712d62eb9790..fb904fda3949 100644 +issei-heci-objs += hw_heci.o diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c new file mode 100644 -index 000000000000..7e6ca72e873c +index 0000000000000..6efb29f5d759e --- /dev/null +++ b/drivers/misc/issei/hw_heci.c -@@ -0,0 +1,552 @@ +@@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include @@ -217,7 +217,7 @@ index 000000000000..7e6ca72e873c + if (filled_slots > buffer_depth) + return -EOVERFLOW; + -+ dev_dbg(idev->dev, "filled_slots = %08x\n", filled_slots); ++ dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots); + return filled_slots; +} + @@ -357,7 +357,7 @@ index 000000000000..7e6ca72e873c + * we need to clean H_CSR_RST bit to start a successful reset sequence. + */ + if (reg & H_CSR_RST) { -+ dev_warn(idev->dev, "H_CSR_RST is set = 0x%08X", reg); ++ dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg); + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); + reg = heci_hcsr_read(idev); @@ -377,10 +377,10 @@ index 000000000000..7e6ca72e873c + reg = heci_hcsr_read(idev); + + if (!(reg & H_CSR_RST)) -+ dev_warn(idev->dev, "H_CSR_RST is not set = 0x%08X", reg); ++ dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg); + + if (reg & H_CSR_RDY) -+ dev_warn(idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); ++ dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); + + if (!enable) + issei_heci_hw_reset_release(idev); @@ -435,7 +435,7 @@ index 000000000000..7e6ca72e873c + struct issei_heci_hw *hw = to_heci_hw(idev); + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { -+ dev_err(idev->dev, "Data size %zu not aligned to slot size %lu\n", ++ dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } @@ -465,7 +465,7 @@ index 000000000000..7e6ca72e873c + u32 *reg_buf = data; + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { -+ dev_err(idev->dev, "Data size %zu not aligned to slot size %lu\n", ++ dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } @@ -499,7 +499,7 @@ index 000000000000..7e6ca72e873c + + ret = heci_write_hbuf(idev, &req, sizeof(req)); + if (ret) -+ dev_err(idev->dev, "Shared memory req write failed ret = %d\n", ret); ++ dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret); + + return ret; +} @@ -516,23 +516,23 @@ index 000000000000..7e6ca72e873c + int ret; + + if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) { -+ dev_dbg(idev->dev, "Setup response is not fully received\n"); ++ dev_dbg(&idev->dev, "Setup response is not fully received\n"); + return -ENODATA; + } + + ret = heci_read_hbuf(idev, &res, sizeof(res)); + if (ret) { -+ dev_err(idev->dev, "Shared memory res read failed ret = %d\n", ret); ++ dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret); + return ret; + } + + if (res.msg_id != HAM_CB_MESSAGE_ID_RES) { -+ dev_err(idev->dev, "Shared memory res header 0x%x != 0x%x\n", ++ dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n", + res.msg_id, HAM_CB_MESSAGE_ID_RES); + return -EPROTO; + } + if (res.status != 0) { -+ dev_err(idev->dev, "Shared memory res status %d != 0\n", res.status); ++ dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status); + return -EPROTO; + } + @@ -557,6 +557,11 @@ index 000000000000..7e6ca72e873c + .irq_write_generate = issei_heci_irq_write_generate, +}; + ++const struct issei_hw_ops *issei_heci_get_ops(void) ++{ ++ return &hw_heci_ops; ++} ++ +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id) +{ + struct issei_device *idev = dev_id; @@ -575,7 +580,7 @@ index 000000000000..7e6ca72e873c + heci_hcsr_set_hig(idev); + } + -+ dev_dbg(idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); ++ dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); + + atomic_set(&idev->rst_irq, 1); + wake_up_interruptible(&idev->wait_rst_irq); @@ -595,32 +600,23 @@ index 000000000000..7e6ca72e873c +} + +/** -+ * issei_heci_dev_init - allocates and initializes the issei device structure with hw_heci -+ * @parent: device associated with physical device (pci/platform) ++ * issei_heci_dev_init - initializes the issei device structure with hw_heci ++ * @idev: device structure ++ * @mem_addr: memory address on bar + * @cfg: per device generation config -+ * -+ * Return: The issei_device pointer on success, NULL on failure. + */ -+struct issei_device *issei_heci_dev_init(struct device *parent, const struct hw_heci_cfg *cfg) ++void issei_heci_dev_init(struct issei_device *idev, ++ void __iomem *mem_addr, const struct hw_heci_cfg *cfg) +{ -+ struct issei_device *idev; -+ struct issei_heci_hw *hw; -+ -+ idev = devm_kzalloc(parent, sizeof(*idev) + sizeof(*hw), GFP_KERNEL); -+ if (!idev) -+ return NULL; ++ struct issei_heci_hw *hw = to_heci_hw(idev); + -+ hw = to_heci_hw(idev); + spin_lock_init(&hw->access_lock); -+ -+ issei_device_init(idev, parent, &cfg->dma_length, &hw_heci_ops); ++ hw->mem_addr = mem_addr; + hw->cfg = cfg; -+ -+ return idev; +} diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h new file mode 100644 -index 000000000000..7ba160c3db54 +index 0000000000000..bec2ce8937d1d --- /dev/null +++ b/drivers/misc/issei/hw_heci.h @@ -0,0 +1,54 @@ @@ -635,7 +631,6 @@ index 000000000000..7ba160c3db54 +#include "issei_dev.h" + +struct device; -+struct issei_device; + +/* + * hw_heci_cfg - issei heci device configuration @@ -671,16 +666,17 @@ index 000000000000..7ba160c3db54 +#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw) + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx); ++const struct issei_hw_ops *issei_heci_get_ops(void); + -+struct issei_device *issei_heci_dev_init(struct device *parent, const struct hw_heci_cfg *cfg); ++void issei_heci_dev_init(struct issei_device *idev, ++ void __iomem *mem_addr, const struct hw_heci_cfg *cfg); + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id); -+irqreturn_t issei_heci_irq_thread_handler(int irq, void *dev_id); + +#endif /* _ISSEI_HW_HECI_H_ */ diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h new file mode 100644 -index 000000000000..5e804bdb2e94 +index 0000000000000..5e804bdb2e944 --- /dev/null +++ b/drivers/misc/issei/hw_heci_regs.h @@ -0,0 +1,35 @@ @@ -721,12 +717,12 @@ index 000000000000..5e804bdb2e94 +#endif /* _ISSEI_HW_HECI_REGS_H_ */ diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c new file mode 100644 -index 000000000000..b1fafd284521 +index 0000000000000..003cda7b5e8a7 --- /dev/null +++ b/drivers/misc/issei/pci_heci.c -@@ -0,0 +1,166 @@ +@@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 -+/* Copyright (C) 2023-2024 Intel Corporation */ ++/* Copyright (C) 2023-2025 Intel Corporation */ +#include +#include + @@ -736,89 +732,74 @@ index 000000000000..b1fafd284521 + +static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ ++ struct device *dev = &pdev->dev; + const struct hw_heci_cfg *cfg; + struct issei_device *idev; + struct issei_heci_hw *hw; ++ char __iomem *registers; + int err; + + cfg = issei_heci_get_cfg(ent->driver_data); + if (!cfg) -+ return -ENODEV; ++ return dev_err_probe(dev, -ENODEV, "no usable configuration.\n"); + + err = pcim_enable_device(pdev); -+ if (err) { -+ dev_err(&pdev->dev, "failed to enable pci device. err = %d\n", err); -+ return err; -+ } ++ if (err) ++ return dev_err_probe(dev, err, "failed to enable pci device.\n"); + -+ err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); -+ if (err) { -+ dev_err(&pdev->dev, "failed to get pci regions. err = %d\n", err); -+ return err; -+ } ++ pci_set_master(pdev); + -+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); -+ if (err) { -+ dev_err(&pdev->dev, "no usable DMA configuration, aborting. err = %d\n", err); -+ return err; -+ } ++ registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); ++ if (IS_ERR(registers)) ++ return dev_err_probe(dev, IS_ERR(registers), "failed to get pci region.\n"); + -+ pci_set_master(pdev); ++ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ++ if (err) ++ return dev_err_probe(dev, err, "no usable DMA configuration.\n"); + -+ idev = issei_heci_dev_init(&pdev->dev, cfg); -+ if (!idev) -+ return -ENOMEM; -+ hw = to_heci_hw(idev); -+ hw->mem_addr = pcim_iomap_table(pdev)[0]; ++ idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops()); ++ if (IS_ERR(idev)) ++ return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n"); ++ ++ issei_heci_dev_init(idev, registers, cfg); ++ ++ pci_set_drvdata(pdev, idev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (err < 0) { -+ dev_err(&pdev->dev, "pci_alloc_irq_vectors failure. err = %d\n", err); -+ return err; ++ dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n"); ++ goto deregister; + } + ++ hw = to_heci_hw(idev); + hw->irq = pci_irq_vector(pdev, 0); + -+ err = devm_request_threaded_irq(&pdev->dev, hw->irq, ++ err = request_threaded_irq(hw->irq, + issei_heci_irq_quick_handler, + NULL, + IRQF_ONESHOT, KBUILD_MODNAME, idev); -+ if (err) { -+ dev_err(&pdev->dev, "request_threaded_irq failure. err = %d, irq = %d\n", -+ err, hw->irq); ++ if (err) + goto release_irq; -+ } + + err = issei_start(idev); -+ if (err) { -+ dev_err(&pdev->dev, "init hw failure. err = %d.\n", err); -+ goto free_irq; -+ } -+ -+ err = issei_register(idev, &pdev->dev); + if (err) { -+ dev_err(&pdev->dev, "register failure. err = %d.\n", err); -+ goto stop; ++ dev_err_probe(dev, err, "init hw failure.\n"); ++ goto free_irq; + } + -+ pci_set_drvdata(pdev, idev); -+ -+ dev_dbg(&pdev->dev, "initialization successful.\n"); -+ + return 0; + -+stop: -+ issei_stop(idev); +free_irq: -+ /* Manually free IRQ otherwise PCI free irq vectors will fail */ -+ devm_free_irq(&pdev->dev, hw->irq, idev); -+release_irq: + idev->ops->irq_disable(idev); ++ free_irq(hw->irq, idev); ++release_irq: + pci_free_irq_vectors(pdev); ++deregister: ++ issei_deregister(idev); + return err; +} + -+static void __issei_heci_deconstruct(struct pci_dev *pdev) ++static void issei_heci_shutdown(struct pci_dev *pdev) +{ + struct issei_device *idev = pci_get_drvdata(pdev); + struct issei_heci_hw *hw = to_heci_hw(idev); @@ -826,22 +807,13 @@ index 000000000000..b1fafd284521 + issei_stop(idev); + + idev->ops->irq_disable(idev); -+ /* Manually free IRQ otherwise PCI free irq vectors will fail */ -+ devm_free_irq(&pdev->dev, hw->irq, idev); ++ free_irq(hw->irq, idev); + pci_free_irq_vectors(pdev); +} + -+ -+static void issei_heci_shutdown(struct pci_dev *pdev) -+{ -+ dev_dbg(&pdev->dev, "shutdown\n"); -+ __issei_heci_deconstruct(pdev); -+} -+ +static void issei_heci_remove(struct pci_dev *pdev) +{ -+ dev_dbg(&pdev->dev, "stop\n"); -+ __issei_heci_deconstruct(pdev); ++ issei_heci_shutdown(pdev); + + issei_deregister(pci_get_drvdata(pdev)); +} @@ -871,6 +843,8 @@ index 000000000000..b1fafd284521 + {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */ + {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panter Lake H */ + {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panter Lake P */ ++ {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */ ++ {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */ + + {0, } +}; diff --git a/SPECS/kernel-rt/0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu b/SPECS/kernel-rt/0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu deleted file mode 100644 index 13c898b95..000000000 --- a/SPECS/kernel-rt/0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu +++ /dev/null @@ -1,26 +0,0 @@ -From cff0a1ac43aa4e4aab73d712a38311789db15a9e Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Sat, 6 Sep 2025 17:14:52 +0800 -Subject: [PATCH 04/11] media: pci: enable lt6911gxd in ipu-bridge - -Signed-off-by: linya14x ---- - drivers/media/pci/intel/ipu-bridge.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c -index 4e579352ab2c..e58b354ad9e6 100644 ---- a/drivers/media/pci/intel/ipu-bridge.c -+++ b/drivers/media/pci/intel/ipu-bridge.c -@@ -92,6 +92,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { - IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), - /* Toshiba T4KA3 */ - IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), -+ /* Lontium lt6911gxd */ -+ IPU_SENSOR_CONFIG("INTC1124", 0), - }; - - static const struct ipu_property_names prop_names = { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu b/SPECS/kernel-rt/0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu deleted file mode 100644 index 98784366e..000000000 --- a/SPECS/kernel-rt/0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu +++ /dev/null @@ -1,124 +0,0 @@ -From 6b7a8bf64caba09b3b26b377641e2246ffbff13b Mon Sep 17 00:00:00 2001 -From: "Yew, Chang Ching" -Date: Thu, 27 Nov 2025 02:49:53 +0800 -Subject: [PATCH 4/6] media: v4l2-async: Fix error handling on steps after - finding a match - -Once an async connection is found to be matching with an fwnode, a -sub-device may be registered (in case it wasn't already), its bound -operation is called, ancillary links are created, the async connection is -added to the sub-device's list of connections and removed from the global -waiting connection list. Further on, the sub-device's possible own -notifier is searched for possible additional matches. - -Fix these specific issues: -- If v4l2_async_match_notify() failed before the sub-notifier handling, the - async connection was unbound and its entry removed from the sub-device's - async connection list. The latter part was also done in - v4l2_async_match_notify(). - -- The async connection's sd field was only set after creating ancillary - links in v4l2_async_match_notify(). It was however dereferenced in - v4l2_async_unbind_subdev_one(), which was called on error path of - v4l2_async_match_notify() failure. - -Signed-off-by: Sakari Ailus ---- - drivers/media/v4l2-core/v4l2-async.c | 45 +++++++++++++++++++--------- - 1 file changed, 31 insertions(+), 14 deletions(-) - -diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c -index ee884a8221fb..1c08bba9ecb9 100644 ---- a/drivers/media/v4l2-core/v4l2-async.c -+++ b/drivers/media/v4l2-core/v4l2-async.c -@@ -343,7 +343,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_connection *asc) - { -- struct v4l2_async_notifier *subdev_notifier; - bool registered = false; - int ret; - -@@ -389,6 +388,25 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, - dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n", - dev_name(sd->dev), ret); - -+ return 0; -+ -+err_call_unbind: -+ v4l2_async_nf_call_unbind(notifier, sd, asc); -+ list_del(&asc->asc_subdev_entry); -+ -+err_unregister_subdev: -+ if (registered) -+ v4l2_device_unregister_subdev(sd); -+ -+ return ret; -+} -+ -+static int -+v4l2_async_nf_try_subdev_notifier(struct v4l2_async_notifier *notifier, -+ struct v4l2_subdev *sd) -+{ -+ struct v4l2_async_notifier *subdev_notifier; -+ - /* - * See if the sub-device has a notifier. If not, return here. - */ -@@ -404,16 +422,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, - subdev_notifier->parent = notifier; - - return v4l2_async_nf_try_all_subdevs(subdev_notifier); -- --err_call_unbind: -- v4l2_async_nf_call_unbind(notifier, sd, asc); -- list_del(&asc->asc_subdev_entry); -- --err_unregister_subdev: -- if (registered) -- v4l2_device_unregister_subdev(sd); -- -- return ret; - } - - /* Test all async sub-devices in a notifier for a match. */ -@@ -445,6 +453,10 @@ v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier) - if (ret < 0) - return ret; - -+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd); -+ if (ret < 0) -+ return ret; -+ - /* - * v4l2_async_match_notify() may lead to registering a - * new notifier and thus changing the async subdevs -@@ -829,7 +841,11 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) - ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, - asc); - if (ret) -- goto err_unbind; -+ goto err_unlock; -+ -+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd); -+ if (ret) -+ goto err_unbind_one; - - ret = v4l2_async_nf_try_complete(notifier); - if (ret) -@@ -853,9 +869,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) - if (subdev_notifier) - v4l2_async_nf_unbind_all_subdevs(subdev_notifier); - -- if (asc) -- v4l2_async_unbind_subdev_one(notifier, asc); -+err_unbind_one: -+ v4l2_async_unbind_subdev_one(notifier, asc); - -+err_unlock: - mutex_unlock(&list_lock); - - sd->owner = NULL; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-mei-virtio-virtualization-frontend-driver.security b/SPECS/kernel-rt/0004-mei-virtio-virtualization-frontend-driver.security new file mode 100644 index 000000000..48a2cdab7 --- /dev/null +++ b/SPECS/kernel-rt/0004-mei-virtio-virtualization-frontend-driver.security @@ -0,0 +1,935 @@ +From e5479216aa5ef4d1d806c4ec9d7e7b6fded0dff1 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Tue, 18 Aug 2020 14:51:47 +0300 +Subject: [PATCH 4/8] mei: virtio: virtualization frontend driver + +This frontend driver implements MEI hw interface based on virtio +framework to let MEI driver work without changes under virtualization. +It requires a backend service in the ACRN device-model on the service +OS side to make it work. The backend service will emulate mei routing +and assign vtags for each mei vritio device. + +The backend service is available in ACRN device-model at github. +For more information, please refer to https://projectacrn.org + +The ACRN virtio sub device id for MEI is is 0x8602. + +Co-developed-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +Signed-off-by: Wang Yu +Signed-off-by: Liu Shuo +Link: https://lore.kernel.org/r/20200818115147.2567012-14-tomas.winkler@intel.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/mei/Kconfig | 9 + + drivers/misc/mei/Makefile | 3 + + drivers/misc/mei/hw-virtio.c | 863 +++++++++++++++++++++++++++++++++++ + 3 files changed, 875 insertions(+) + create mode 100644 drivers/misc/mei/hw-virtio.c + +diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig +index f4eb307cd35ed..2b1330b35454e 100644 +--- a/drivers/misc/mei/Kconfig ++++ b/drivers/misc/mei/Kconfig +@@ -94,6 +94,15 @@ config INTEL_MEI_LB + authenticated and versioned, and do not require firmware flashing + or system reboot. + ++config INTEL_MEI_VIRTIO ++ tristate "Intel MEI interface emulation with virtio framework" ++ depends on INTEL_MEI && VIRTIO_PCI ++ help ++ This module implements mei hw emulation over virtio transport. ++ The module will be called mei_virtio. ++ Enable this if your virtual machine supports virtual mei ++ device over virtio. ++ + source "drivers/misc/mei/hdcp/Kconfig" + source "drivers/misc/mei/pxp/Kconfig" + source "drivers/misc/mei/gsc_proxy/Kconfig" +diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile +index a203ed766b338..9de79da3d7027 100644 +--- a/drivers/misc/mei/Makefile ++++ b/drivers/misc/mei/Makefile +@@ -25,6 +25,9 @@ obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o + mei-txe-objs := pci-txe.o + mei-txe-objs += hw-txe.o + ++obj-$(CONFIG_INTEL_MEI_VIRTIO) += mei-virtio.o ++mei-virtio-objs := hw-virtio.o ++ + mei-$(CONFIG_EVENT_TRACING) += mei-trace.o + CFLAGS_mei-trace.o = -I$(src) + +diff --git a/drivers/misc/mei/hw-virtio.c b/drivers/misc/mei/hw-virtio.c +new file mode 100644 +index 0000000000000..5e3f405d11deb +--- /dev/null ++++ b/drivers/misc/mei/hw-virtio.c +@@ -0,0 +1,863 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Intel Management Engine Interface (Intel MEI) Linux driver ++ * Copyright (c) 2018-2020, Intel Corporation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mei_dev.h" ++#include "hbm.h" ++#include "client.h" ++ ++#define MEI_VIRTIO_RPM_TIMEOUT 500 ++/* ACRN virtio device types */ ++#ifndef VIRTIO_ID_MEI ++#define VIRTIO_ID_MEI 0xFFFE /* virtio mei */ ++#endif ++ ++/** ++ * struct mei_virtio_cfg - settings passed from the virtio backend ++ * @buf_depth: read buffer depth in slots (4bytes) ++ * @hw_ready: hw is ready for operation ++ * @host_reset: synchronize reset with virtio backend ++ * @reserved: reserved for alignment ++ * @fw_status: FW status ++ */ ++struct mei_virtio_cfg { ++ u32 buf_depth; ++ u8 hw_ready; ++ u8 host_reset; ++ u8 reserved[2]; ++ u32 fw_status[MEI_FW_STATUS_MAX]; ++} __packed; ++ ++struct mei_virtio_hw { ++ struct mei_device mdev; ++ char name[32]; ++ ++ struct virtqueue *in; ++ struct virtqueue *out; ++ ++ bool host_ready; ++ struct work_struct intr_handler; ++ ++ u32 *recv_buf; ++ u8 recv_rdy; ++ size_t recv_sz; ++ u32 recv_idx; ++ u32 recv_len; ++ ++ /* send buffer */ ++ atomic_t hbuf_ready; ++ const void *send_hdr; ++ const void *send_buf; ++ ++ struct mei_virtio_cfg cfg; ++}; ++ ++#define to_virtio_hw(_dev) container_of(_dev, struct mei_virtio_hw, mdev) ++ ++/** ++ * mei_virtio_fw_status() - read status register of mei ++ * @dev: mei device ++ * @fw_status: fw status register values ++ * ++ * Return: always 0 ++ */ ++static int mei_virtio_fw_status(struct mei_device *dev, ++ struct mei_fw_status *fw_status) ++{ ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ fw_status->count = MEI_FW_STATUS_MAX; ++ virtio_cread_bytes(vdev, offsetof(struct mei_virtio_cfg, fw_status), ++ fw_status->status, sizeof(fw_status->status)); ++ return 0; ++} ++ ++/** ++ * mei_virtio_pg_state() - translate internal pg state ++ * to the mei power gating state ++ * There is no power management in ACRN mode always return OFF ++ * @dev: mei device ++ * ++ * Return: ++ * * MEI_PG_OFF - if aliveness is on (always) ++ * * MEI_PG_ON - (never) ++ */ ++static inline enum mei_pg_state mei_virtio_pg_state(struct mei_device *dev) ++{ ++ return MEI_PG_OFF; ++} ++ ++/** ++ * mei_virtio_hw_config() - configure hw dependent settings ++ * ++ * @dev: mei device ++ * ++ * Return: always 0 ++ */ ++static int mei_virtio_hw_config(struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_virtio_hbuf_empty_slots() - counts write empty slots. ++ * @dev: the device structure ++ * ++ * Return: always return frontend buf size if buffer is ready, 0 otherwise ++ */ ++static int mei_virtio_hbuf_empty_slots(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ return (atomic_read(&hw->hbuf_ready) == 1) ? hw->cfg.buf_depth : 0; ++} ++ ++/** ++ * mei_virtio_hbuf_is_ready() - checks if write buffer is ready ++ * @dev: the device structure ++ * ++ * Return: true if hbuf is ready ++ */ ++static bool mei_virtio_hbuf_is_ready(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ return atomic_read(&hw->hbuf_ready) == 1; ++} ++ ++/** ++ * mei_virtio_hbuf_depth() - returns depth of FE write buffer. ++ * @dev: the device structure ++ * ++ * Return: size of frontend write buffer in bytes ++ */ ++static u32 mei_virtio_hbuf_depth(const struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ return hw->cfg.buf_depth; ++} ++ ++/** ++ * mei_virtio_intr_clear() - clear and stop interrupts ++ * @dev: the device structure ++ */ ++static void mei_virtio_intr_clear(struct mei_device *dev) ++{ ++ /* ++ * In our virtio solution, there are two types of interrupts, ++ * vq interrupt and config change interrupt. ++ * 1) start/reset rely on virtio config changed interrupt; ++ * 2) send/recv rely on virtio virtqueue interrupts. ++ * They are all virtual interrupts. So, we don't have corresponding ++ * operation to do here. ++ */ ++} ++ ++/** ++ * mei_virtio_intr_enable() - enables mei BE virtqueues callbacks ++ * @dev: the device structure ++ */ ++static void mei_virtio_intr_enable(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ virtqueue_enable_cb(hw->in); ++ virtqueue_enable_cb(hw->out); ++} ++ ++/** ++ * mei_virtio_intr_disable() - disables mei BE virtqueues callbacks ++ * ++ * @dev: the device structure ++ */ ++static void mei_virtio_intr_disable(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ virtqueue_disable_cb(hw->in); ++ virtqueue_disable_cb(hw->out); ++} ++ ++/** ++ * mei_virtio_synchronize_irq() - wait for pending IRQ handlers for all ++ * virtqueue ++ * @dev: the device structure ++ */ ++static void mei_virtio_synchronize_irq(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ /* ++ * Now, all IRQ handlers are converted to workqueue. ++ * Change synchronize irq to flush this work. ++ */ ++ flush_work(&hw->intr_handler); ++} ++ ++static void mei_virtio_free_outbufs(struct mei_virtio_hw *hw) ++{ ++ kfree(hw->send_hdr); ++ kfree(hw->send_buf); ++ hw->send_hdr = NULL; ++ hw->send_buf = NULL; ++} ++ ++/** ++ * mei_virtio_write_message() - writes a message to mei virtio back-end service. ++ * @dev: the device structure ++ * @hdr: mei header of message ++ * @hdr_len: header length ++ * @data: message payload will be written ++ * @data_len: message payload length ++ * ++ * Return: ++ * * 0: on success ++ * * -EIO: if write has failed ++ * * -ENOMEM: on memory allocation failure ++ */ ++static int mei_virtio_write_message(struct mei_device *dev, ++ const void *hdr, size_t hdr_len, ++ const void *data, size_t data_len) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct scatterlist sg[2]; ++ const void *hbuf, *dbuf; ++ int ret; ++ ++ if (WARN_ON(!atomic_add_unless(&hw->hbuf_ready, -1, 0))) ++ return -EIO; ++ ++ hbuf = kmemdup(hdr, hdr_len, GFP_KERNEL); ++ hw->send_hdr = hbuf; ++ ++ dbuf = kmemdup(data, data_len, GFP_KERNEL); ++ hw->send_buf = dbuf; ++ ++ if (!hbuf || !dbuf) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ sg_init_table(sg, 2); ++ sg_set_buf(&sg[0], hbuf, hdr_len); ++ sg_set_buf(&sg[1], dbuf, data_len); ++ ++ ret = virtqueue_add_outbuf(hw->out, sg, 2, hw, GFP_KERNEL); ++ if (ret) { ++ dev_err(&dev->dev, "failed to add outbuf\n"); ++ goto fail; ++ } ++ ++ virtqueue_kick(hw->out); ++ return 0; ++fail: ++ ++ mei_virtio_free_outbufs(hw); ++ ++ return ret; ++} ++ ++/** ++ * mei_virtio_count_full_read_slots() - counts read full slots. ++ * @dev: the device structure ++ * ++ * Return: -EOVERFLOW if overflow, otherwise filled slots count ++ */ ++static int mei_virtio_count_full_read_slots(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ if (hw->recv_idx > hw->recv_len) ++ return -EOVERFLOW; ++ ++ return hw->recv_len - hw->recv_idx; ++} ++ ++/** ++ * mei_virtio_read_hdr() - Reads 32bit dword from mei virtio receive buffer ++ * ++ * @dev: the device structure ++ * ++ * Return: 32bit dword of receive buffer (u32) ++ */ ++static inline u32 mei_virtio_read_hdr(const struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ WARN_ON(hw->cfg.buf_depth < hw->recv_idx + 1); ++ ++ return hw->recv_buf[hw->recv_idx++]; ++} ++ ++static int mei_virtio_read(struct mei_device *dev, unsigned char *buffer, ++ unsigned long len) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ u32 slots = mei_data2slots(len); ++ ++ if (WARN_ON(hw->cfg.buf_depth < hw->recv_idx + slots)) ++ return -EOVERFLOW; ++ ++ /* ++ * Assumption: There is only one MEI message in recv_buf each time. ++ * Backend service need follow this rule too. ++ */ ++ memcpy(buffer, hw->recv_buf + hw->recv_idx, len); ++ hw->recv_idx += slots; ++ ++ return 0; ++} ++ ++static bool mei_virtio_pg_is_enabled(struct mei_device *dev) ++{ ++ return false; ++} ++ ++static bool mei_virtio_pg_in_transition(struct mei_device *dev) ++{ ++ return false; ++} ++ ++static void mei_virtio_add_recv_buf(struct mei_virtio_hw *hw) ++{ ++ struct scatterlist sg; ++ ++ if (hw->recv_rdy) /* not needed */ ++ return; ++ ++ /* refill the recv_buf to IN virtqueue to get next message */ ++ sg_init_one(&sg, hw->recv_buf, mei_slots2data(hw->cfg.buf_depth)); ++ hw->recv_len = 0; ++ hw->recv_idx = 0; ++ hw->recv_rdy = 1; ++ virtqueue_add_inbuf(hw->in, &sg, 1, hw->recv_buf, GFP_KERNEL); ++ virtqueue_kick(hw->in); ++} ++ ++/** ++ * mei_virtio_hw_is_ready() - check whether the BE(hw) has turned ready ++ * @dev: mei device ++ * Return: bool ++ */ ++static bool mei_virtio_hw_is_ready(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ virtio_cread(vdev, struct mei_virtio_cfg, ++ hw_ready, &hw->cfg.hw_ready); ++ ++ dev_dbg(&dev->dev, "hw ready %d\n", hw->cfg.hw_ready); ++ ++ return hw->cfg.hw_ready; ++} ++ ++/** ++ * mei_virtio_hw_reset - resets virtio hw. ++ * ++ * @dev: the device structure ++ * @intr_enable: virtio use data/config callbacks ++ * ++ * Return: 0 on success an error code otherwise ++ */ ++static int mei_virtio_hw_reset(struct mei_device *dev, bool intr_enable) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ dev_dbg(&dev->dev, "hw reset\n"); ++ ++ dev->recvd_hw_ready = false; ++ hw->host_ready = false; ++ atomic_set(&hw->hbuf_ready, 0); ++ hw->recv_len = 0; ++ hw->recv_idx = 0; ++ ++ hw->cfg.host_reset = 1; ++ virtio_cwrite(vdev, struct mei_virtio_cfg, ++ host_reset, &hw->cfg.host_reset); ++ ++ mei_virtio_hw_is_ready(dev); ++ ++ if (intr_enable) ++ mei_virtio_intr_enable(dev); ++ ++ return 0; ++} ++ ++/** ++ * mei_virtio_hw_reset_release() - release device from the reset ++ * @dev: the device structure ++ */ ++static void mei_virtio_hw_reset_release(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ dev_dbg(&dev->dev, "hw reset release\n"); ++ hw->cfg.host_reset = 0; ++ virtio_cwrite(vdev, struct mei_virtio_cfg, ++ host_reset, &hw->cfg.host_reset); ++} ++ ++/** ++ * mei_virtio_hw_ready_wait() - wait until the virtio(hw) has turned ready ++ * or timeout is reached ++ * @dev: mei device ++ * ++ * Return: 0 on success, error otherwise ++ */ ++static int mei_virtio_hw_ready_wait(struct mei_device *dev) ++{ ++ mutex_unlock(&dev->device_lock); ++ wait_event_timeout(dev->wait_hw_ready, ++ dev->recvd_hw_ready, ++ mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); ++ mutex_lock(&dev->device_lock); ++ if (!dev->recvd_hw_ready) { ++ dev_err(&dev->dev, "wait hw ready failed\n"); ++ return -ETIMEDOUT; ++ } ++ ++ dev->recvd_hw_ready = false; ++ return 0; ++} ++ ++/** ++ * mei_virtio_hw_start() - hw start routine ++ * @dev: mei device ++ * ++ * Return: 0 on success, error otherwise ++ */ ++static int mei_virtio_hw_start(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ int ret; ++ ++ dev_dbg(&dev->dev, "hw start\n"); ++ mei_virtio_hw_reset_release(dev); ++ ++ ret = mei_virtio_hw_ready_wait(dev); ++ if (ret) ++ return ret; ++ ++ mei_virtio_add_recv_buf(hw); ++ atomic_set(&hw->hbuf_ready, 1); ++ dev_dbg(&dev->dev, "hw is ready\n"); ++ hw->host_ready = true; ++ ++ return 0; ++} ++ ++/** ++ * mei_virtio_host_is_ready() - check whether the FE has turned ready ++ * @dev: mei device ++ * ++ * Return: bool ++ */ ++static bool mei_virtio_host_is_ready(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ dev_dbg(&dev->dev, "host ready %d\n", hw->host_ready); ++ ++ return hw->host_ready; ++} ++ ++/** ++ * mei_virtio_data_in() - The callback of recv virtqueue of virtio mei ++ * @vq: receiving virtqueue ++ */ ++static void mei_virtio_data_in(struct virtqueue *vq) ++{ ++ struct mei_virtio_hw *hw = vq->vdev->priv; ++ ++ /* disable interrupts (enabled again from in the interrupt worker) */ ++ virtqueue_disable_cb(hw->in); ++ ++ schedule_work(&hw->intr_handler); ++} ++ ++/** ++ * mei_virtio_data_out() - The callback of send virtqueue of virtio mei ++ * @vq: transmitting virtqueue ++ */ ++static void mei_virtio_data_out(struct virtqueue *vq) ++{ ++ struct mei_virtio_hw *hw = vq->vdev->priv; ++ ++ schedule_work(&hw->intr_handler); ++} ++ ++static void mei_virtio_intr_handler(struct work_struct *work) ++{ ++ struct mei_virtio_hw *hw = ++ container_of(work, struct mei_virtio_hw, intr_handler); ++ struct mei_device *dev = &hw->mdev; ++ LIST_HEAD(complete_list); ++ s32 slots; ++ int rets = 0; ++ void *data; ++ unsigned int len; ++ ++ mutex_lock(&dev->device_lock); ++ ++ if (dev->dev_state == MEI_DEV_DISABLED) { ++ dev_warn(&dev->dev, "Interrupt in disabled state.\n"); ++ mei_virtio_intr_disable(dev); ++ goto end; ++ } ++ ++ /* check if ME wants a reset */ ++ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { ++ dev_warn(&dev->dev, "BE service not ready: resetting.\n"); ++ schedule_work(&dev->reset_work); ++ goto end; ++ } ++ ++ /* check if we need to start the dev */ ++ if (!mei_host_is_ready(dev)) { ++ if (mei_hw_is_ready(dev)) { ++ dev_dbg(&dev->dev, "we need to start the dev.\n"); ++ dev->recvd_hw_ready = true; ++ wake_up(&dev->wait_hw_ready); ++ } else { ++ dev_warn(&dev->dev, "Spurious Interrupt\n"); ++ } ++ goto end; ++ } ++ ++ /* read */ ++ if (hw->recv_rdy) { ++ data = virtqueue_get_buf(hw->in, &len); ++ if (!data || !len) { ++ dev_dbg(&dev->dev, "No data %d", len); ++ } else { ++ dev_dbg(&dev->dev, "data_in %d\n", len); ++ WARN_ON(data != hw->recv_buf); ++ hw->recv_len = mei_data2slots(len); ++ hw->recv_rdy = 0; ++ } ++ } ++ ++ /* write */ ++ if (!atomic_read(&hw->hbuf_ready)) { ++ if (!virtqueue_get_buf(hw->out, &len)) { ++ dev_warn(&dev->dev, "Failed to getbuf\n"); ++ } else { ++ mei_virtio_free_outbufs(hw); ++ atomic_inc(&hw->hbuf_ready); ++ } ++ } ++ ++ /* check slots available for reading */ ++ slots = mei_count_full_read_slots(dev); ++ while (slots > 0) { ++ dev_dbg(&dev->dev, "slots to read = %08x\n", slots); ++ rets = mei_irq_read_handler(dev, &complete_list, &slots); ++ ++ if (rets && ++ (dev->dev_state != MEI_DEV_RESETTING && ++ dev->dev_state != MEI_DEV_POWER_DOWN)) { ++ dev_err(&dev->dev, "mei_irq_read_handler ret = %d.\n", ++ rets); ++ schedule_work(&dev->reset_work); ++ goto end; ++ } ++ } ++ ++ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); ++ ++ mei_irq_write_handler(dev, &complete_list); ++ ++ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); ++ ++ mei_irq_compl_handler(dev, &complete_list); ++ ++ mei_virtio_add_recv_buf(hw); ++ ++end: ++ if (dev->dev_state != MEI_DEV_DISABLED) { ++ if (!virtqueue_enable_cb(hw->in)) ++ schedule_work(&hw->intr_handler); ++ } ++ ++ mutex_unlock(&dev->device_lock); ++} ++ ++static void mei_virtio_config_changed(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ struct mei_device *dev = &hw->mdev; ++ ++ virtio_cread(vdev, struct mei_virtio_cfg, ++ hw_ready, &hw->cfg.hw_ready); ++ ++ if (dev->dev_state == MEI_DEV_DISABLED) { ++ dev_dbg(&dev->dev, "disabled state don't start\n"); ++ return; ++ } ++ ++ /* Run intr handler once to handle reset notify */ ++ schedule_work(&hw->intr_handler); ++} ++ ++static void mei_virtio_remove_vqs(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ virtqueue_detach_unused_buf(hw->in); ++ hw->recv_len = 0; ++ hw->recv_idx = 0; ++ hw->recv_rdy = 0; ++ ++ virtqueue_detach_unused_buf(hw->out); ++ ++ mei_virtio_free_outbufs(hw); ++ ++ vdev->config->del_vqs(vdev); ++} ++ ++/* ++ * There are two virtqueues, one is for send and another is for recv. ++ */ ++static int mei_virtio_init_vqs(struct mei_virtio_hw *hw, ++ struct virtio_device *vdev) ++{ ++ struct virtqueue *vqs[2]; ++ struct virtqueue_info vqs_info[] = { ++ { "in", mei_virtio_data_in }, ++ { "out", mei_virtio_data_out }, ++ }; ++ int ret; ++ ++ ret = virtio_find_vqs(vdev, 2, vqs, vqs_info, NULL); ++ if (ret) ++ return ret; ++ ++ hw->in = vqs[0]; ++ hw->out = vqs[1]; ++ ++ return 0; ++} ++ ++static const struct mei_hw_ops mei_virtio_ops = { ++ .fw_status = mei_virtio_fw_status, ++ .pg_state = mei_virtio_pg_state, ++ ++ .host_is_ready = mei_virtio_host_is_ready, ++ ++ .hw_is_ready = mei_virtio_hw_is_ready, ++ .hw_reset = mei_virtio_hw_reset, ++ .hw_config = mei_virtio_hw_config, ++ .hw_start = mei_virtio_hw_start, ++ ++ .pg_in_transition = mei_virtio_pg_in_transition, ++ .pg_is_enabled = mei_virtio_pg_is_enabled, ++ ++ .intr_clear = mei_virtio_intr_clear, ++ .intr_enable = mei_virtio_intr_enable, ++ .intr_disable = mei_virtio_intr_disable, ++ .synchronize_irq = mei_virtio_synchronize_irq, ++ ++ .hbuf_free_slots = mei_virtio_hbuf_empty_slots, ++ .hbuf_is_ready = mei_virtio_hbuf_is_ready, ++ .hbuf_depth = mei_virtio_hbuf_depth, ++ ++ .write = mei_virtio_write_message, ++ ++ .rdbuf_full_slots = mei_virtio_count_full_read_slots, ++ .read_hdr = mei_virtio_read_hdr, ++ .read = mei_virtio_read, ++}; ++ ++static int mei_virtio_probe(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw; ++ int ret; ++ ++ hw = devm_kzalloc(&vdev->dev, sizeof(*hw), GFP_KERNEL); ++ if (!hw) ++ return -ENOMEM; ++ ++ vdev->priv = hw; ++ ++ INIT_WORK(&hw->intr_handler, mei_virtio_intr_handler); ++ ++ ret = mei_virtio_init_vqs(hw, vdev); ++ if (ret) ++ goto vqs_failed; ++ ++ virtio_cread(vdev, struct mei_virtio_cfg, ++ buf_depth, &hw->cfg.buf_depth); ++ ++ hw->recv_buf = kzalloc(mei_slots2data(hw->cfg.buf_depth), GFP_KERNEL); ++ if (!hw->recv_buf) { ++ ret = -ENOMEM; ++ goto hbuf_failed; ++ } ++ atomic_set(&hw->hbuf_ready, 0); ++ ++ virtio_device_ready(vdev); ++ ++ mei_device_init(&hw->mdev, &vdev->dev, false, &mei_virtio_ops); ++ ++ pm_runtime_get_noresume(&vdev->dev); ++ pm_runtime_set_active(&vdev->dev); ++ pm_runtime_enable(&vdev->dev); ++ ++ ret = mei_start(&hw->mdev); ++ if (ret) ++ goto mei_start_failed; ++ ++ pm_runtime_set_autosuspend_delay(&vdev->dev, MEI_VIRTIO_RPM_TIMEOUT); ++ pm_runtime_use_autosuspend(&vdev->dev); ++ ++ ret = mei_register(&hw->mdev, &vdev->dev); ++ if (ret) ++ goto mei_failed; ++ ++ pm_runtime_put(&vdev->dev); ++ ++ return 0; ++ ++mei_failed: ++ mei_stop(&hw->mdev); ++mei_start_failed: ++ mei_cancel_work(&hw->mdev); ++ mei_disable_interrupts(&hw->mdev); ++ kfree(hw->recv_buf); ++hbuf_failed: ++ vdev->config->del_vqs(vdev); ++vqs_failed: ++ return ret; ++} ++ ++static int __maybe_unused mei_virtio_pm_runtime_idle(struct device *device) ++{ ++ struct virtio_device *vdev = dev_to_virtio(device); ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ dev_dbg(&vdev->dev, "rpm: mei_virtio : runtime_idle\n"); ++ ++ if (!hw) ++ return -ENODEV; ++ ++ if (mei_write_is_idle(&hw->mdev)) ++ pm_runtime_autosuspend(device); ++ ++ return -EBUSY; ++} ++ ++static int __maybe_unused mei_virtio_pm_runtime_suspend(struct device *device) ++{ ++ return 0; ++} ++ ++static int __maybe_unused mei_virtio_pm_runtime_resume(struct device *device) ++{ ++ return 0; ++} ++ ++static int __maybe_unused mei_virtio_freeze(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ dev_dbg(&vdev->dev, "freeze\n"); ++ ++ if (!hw) ++ return -ENODEV; ++ ++ mei_stop(&hw->mdev); ++ mei_disable_interrupts(&hw->mdev); ++ cancel_work_sync(&hw->intr_handler); ++ vdev->config->reset(vdev); ++ mei_virtio_remove_vqs(vdev); ++ ++ return 0; ++} ++ ++static int __maybe_unused mei_virtio_restore(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ int ret; ++ ++ dev_dbg(&vdev->dev, "restore\n"); ++ ++ if (!hw) ++ return -ENODEV; ++ ++ ret = mei_virtio_init_vqs(hw, vdev); ++ if (ret) ++ return ret; ++ ++ virtio_device_ready(vdev); ++ ++ ret = mei_restart(&hw->mdev); ++ if (ret) ++ return ret; ++ ++ /* Start timer if stopped in suspend */ ++ schedule_delayed_work(&hw->mdev.timer_work, HZ); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops mei_virtio_pm_ops = { ++ SET_RUNTIME_PM_OPS(mei_virtio_pm_runtime_suspend, ++ mei_virtio_pm_runtime_resume, ++ mei_virtio_pm_runtime_idle) ++}; ++ ++static void mei_virtio_remove(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ mei_stop(&hw->mdev); ++ mei_disable_interrupts(&hw->mdev); ++ cancel_work_sync(&hw->intr_handler); ++ mei_deregister(&hw->mdev); ++ vdev->config->reset(vdev); ++ mei_virtio_remove_vqs(vdev); ++ kfree(hw->recv_buf); ++ pm_runtime_disable(&vdev->dev); ++} ++ ++static struct virtio_device_id id_table[] = { ++ { VIRTIO_ID_MEI, VIRTIO_DEV_ANY_ID }, ++ { } ++}; ++ ++static struct virtio_driver mei_virtio_driver = { ++ .id_table = id_table, ++ .probe = mei_virtio_probe, ++ .remove = mei_virtio_remove, ++ .config_changed = mei_virtio_config_changed, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .pm = &mei_virtio_pm_ops, ++ }, ++#ifdef CONFIG_PM_SLEEP ++ .freeze = mei_virtio_freeze, ++ .restore = mei_virtio_restore, ++#endif ++}; ++ ++module_virtio_driver(mei_virtio_driver); ++MODULE_DEVICE_TABLE(virtio, id_table); ++MODULE_DESCRIPTION("Virtio MEI frontend driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-net-core-XDP-metadata-BTF-netlink-API.ethernet b/SPECS/kernel-rt/0004-net-core-XDP-metadata-BTF-netlink-API.ethernet deleted file mode 100644 index ca4a7f691..000000000 --- a/SPECS/kernel-rt/0004-net-core-XDP-metadata-BTF-netlink-API.ethernet +++ /dev/null @@ -1,213 +0,0 @@ -From 66599d365b29a2a9306b4e686289e1c8ccb38e12 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Mon, 14 Jun 2021 11:16:14 +0800 -Subject: [PATCH 04/19] net/core: XDP metadata BTF netlink API - -Add new devlink XDP attributes to be used to query or setup XDP metadata -BTF state. - -IFLA_XDP_MD_BTF_ID: type NLA_U32. -IFLA_XDP_MD_BTF_STATE: type = NLA_U8. - -On XDP query driver reports current loaded BTF ID, and its state if -active or not. - -On XDP setup, driver will use these attributes to activate/deactivate -a specific BTF ID. - -Signed-off-by: Saeed Mahameed -Signed-off-by: Jithu Joseph -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - include/linux/netdevice.h | 15 +++++++++- - include/uapi/linux/if_link.h | 2 ++ - net/core/dev.c | 53 ++++++++++++++++++++++++++++++++++++ - net/core/rtnetlink.c | 18 +++++++++++- - 4 files changed, 86 insertions(+), 2 deletions(-) - -diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h -index f3a3b761abfb..f16a70c4298c 100644 ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -965,6 +965,10 @@ enum bpf_netdev_command { - */ - XDP_SETUP_PROG, - XDP_SETUP_PROG_HW, -+ /* Setup/query XDP Meta Data BTF */ -+ XDP_SETUP_MD_BTF, -+ XDP_QUERY_MD_BTF, -+ - /* BPF program for offload callbacks, invoked at program load time. */ - BPF_OFFLOAD_MAP_ALLOC, - BPF_OFFLOAD_MAP_FREE, -@@ -988,6 +992,7 @@ struct bpf_xdp_entity { - struct bpf_prog *prog; - struct bpf_xdp_link *link; - }; -+struct btf; - - struct netdev_bpf { - enum bpf_netdev_command command; -@@ -996,7 +1001,11 @@ struct netdev_bpf { - struct { - u32 flags; - struct bpf_prog *prog; -- struct netlink_ext_ack *extack; -+ }; -+ /* XDP_{SETUP/QUERY}_MD_BTF */ -+ struct { -+ u8 btf_enable; -+ u32 btf_id; - }; - /* BPF_OFFLOAD_MAP_ALLOC, BPF_OFFLOAD_MAP_FREE */ - struct { -@@ -1007,6 +1016,7 @@ struct netdev_bpf { - struct xsk_buff_pool *pool; - u16 queue_id; - } xsk; -+ struct netlink_ext_ack *extack; - }; - }; - -@@ -4244,6 +4254,9 @@ u32 dev_xdp_prog_id(struct net_device *dev, enum bpf_xdp_mode mode); - - u32 dev_get_min_mp_channel_count(const struct net_device *dev); - -+int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, -+ u8 enable); -+u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled); - int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); - int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); - int dev_forward_skb_nomtu(struct net_device *dev, struct sk_buff *skb); -diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h -index 784ace3a519c..d7f118af1147 100644 ---- a/include/uapi/linux/if_link.h -+++ b/include/uapi/linux/if_link.h -@@ -1909,6 +1909,8 @@ enum { - IFLA_XDP_SKB_PROG_ID, - IFLA_XDP_HW_PROG_ID, - IFLA_XDP_EXPECTED_FD, -+ IFLA_XDP_MD_BTF_ID, -+ IFLA_XDP_MD_BTF_STATE, - __IFLA_XDP_MAX, - }; - -diff --git a/net/core/dev.c b/net/core/dev.c -index 8d49b2198d07..e5768993cc72 100644 ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -10558,6 +10558,59 @@ u32 dev_get_min_mp_channel_count(const struct net_device *dev) - return 0; - } - -+/** -+ * dev_xdp_query_md_btf - Query meta data btf of a device -+ * @dev: device -+ * @enabled: 1 if enabled, 0 otherwise -+ * -+ * Returns btf id > 0 if valid -+ */ -+u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled) -+{ -+ struct netdev_bpf xdp; -+ bpf_op_t ndo_bpf; -+ -+ ndo_bpf = dev->netdev_ops->ndo_bpf; -+ if (!ndo_bpf) -+ return 0; -+ -+ memset(&xdp, 0, sizeof(xdp)); -+ xdp.command = XDP_QUERY_MD_BTF; -+ -+ if (ndo_bpf(dev, &xdp)) -+ return 0; /* 0 is an invalid btf id */ -+ -+ *enabled = xdp.btf_enable; -+ return xdp.btf_id; -+} -+ -+/** -+ * dev_xdp_setup_md_btf - enable or disable meta data btf for a device -+ * @dev: device -+ * @extack: netlink extended ack -+ * @enable: 1 to enable, 0 to disable -+ * -+ * Returns 0 on success -+ */ -+int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, -+ u8 enable) -+{ -+ struct netdev_bpf xdp; -+ bpf_op_t ndo_bpf; -+ -+ ndo_bpf = dev->netdev_ops->ndo_bpf; -+ if (!ndo_bpf) -+ return -EOPNOTSUPP; -+ -+ memset(&xdp, 0, sizeof(xdp)); -+ -+ xdp.command = XDP_SETUP_MD_BTF; -+ xdp.btf_enable = enable; -+ xdp.extack = extack; -+ -+ return ndo_bpf(dev, &xdp); -+} -+ - /** - * dev_index_reserve() - allocate an ifindex in a namespace - * @net: the applicable net namespace -diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -index 094b085cff20..aa18bd5b8c63 100644 ---- a/net/core/rtnetlink.c -+++ b/net/core/rtnetlink.c -@@ -1732,8 +1732,9 @@ static int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev, - - static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - { -+ u32 prog_id, md_btf_id; -+ u8 md_btf_enabled = 0; - struct nlattr *xdp; -- u32 prog_id; - int err; - u8 mode; - -@@ -1766,6 +1767,10 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - goto err_cancel; - } - -+ md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); -+ nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); -+ nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); -+ - nla_nest_end(skb, xdp); - return 0; - -@@ -2294,6 +2299,8 @@ static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { - [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, - [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, - [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, -+ [IFLA_XDP_MD_BTF_ID] = { .type = NLA_U32 }, -+ [IFLA_XDP_MD_BTF_STATE] = { .type = NLA_U8 }, - }; - - static struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla, -@@ -3382,6 +3389,15 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev, - goto errout; - status |= DO_SETLINK_NOTIFY; - } -+ -+ if (xdp[IFLA_XDP_MD_BTF_STATE]) { -+ u8 enable = nla_get_u8(xdp[IFLA_XDP_MD_BTF_STATE]); -+ -+ err = dev_xdp_setup_md_btf(dev, extack, enable); -+ if (err) -+ goto errout; -+ status |= DO_SETLINK_NOTIFY; -+ } - } - - errout: --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet b/SPECS/kernel-rt/0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet new file mode 100644 index 000000000..922619eee --- /dev/null +++ b/SPECS/kernel-rt/0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet @@ -0,0 +1,63 @@ +From cd577235483d3d7348ebcda3f5c72f8b69083567 Mon Sep 17 00:00:00 2001 +From: Ling Pei Lee +Date: Fri, 10 Sep 2021 23:49:43 +0800 +Subject: [PATCH 04/18] net: stmmac: Resolve poor line rate after switching + from TSO off to TSO on + +Clear mss in TDES and call stmmac_enable_tso() to indicate +a new TSO transmission when it is enabled from TSO off using +ethtool command. + +TSO Driver is disable when 'priv->tso = 0' in this same function. +The reason this is put as part of fix_features rather than set_features is +because the commit f748be531d70("stmmac: support new GMAC4") has +already introduced the following codes in fix_features:- + ++ /* Disable tso if asked by ethtool */ ++ if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) { ++ if (features & NETIF_F_TSO) ++ priv->tso = true; ++ else ++ priv->tso = false; ++ } + +Fixes: f748be531d70 ("stmmac: support new GMAC4") +Signed-off-by: Ling Pei Lee +Signed-off-by: Looi Hong Aun +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 8b9dbf6449f1c..2cd7548edd82f 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -5915,6 +5915,8 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, + netdev_features_t features) + { + struct stmmac_priv *priv = netdev_priv(dev); ++ u32 tx_cnt = priv->plat->tx_queues_to_use; ++ u32 chan; + + if (priv->plat->rx_coe == STMMAC_RX_COE_NONE) + features &= ~NETIF_F_RXCSUM; +@@ -5938,6 +5940,16 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, + priv->tso = false; + } + ++ for (chan = 0; chan < tx_cnt; chan++) { ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; ++ ++ /* TSO and TBS cannot co-exist */ ++ if (tx_q->tbs & STMMAC_TBS_AVAIL) ++ continue; ++ ++ tx_q->mss = 0; ++ stmmac_enable_tso(priv, priv->ioaddr, priv->tso, chan); ++ } + return features; + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu b/SPECS/kernel-rt/0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu deleted file mode 100644 index d5ab0f361..000000000 --- a/SPECS/kernel-rt/0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu +++ /dev/null @@ -1,272 +0,0 @@ -From fa11e4f711b6ccb10163eeee10d01be41041e9c5 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Mon, 20 Oct 2025 12:36:12 +0800 -Subject: [PATCH 04/21] patch: staging add enable CONFIG_DEBUG_FS - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 84 ++++++++++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-isys.h | 7 +++ - drivers/staging/media/ipu7/ipu7.c | 62 ++++++++++++++++++- - drivers/staging/media/ipu7/ipu7.h | 3 + - 4 files changed, 155 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index cfb3989fe719..44ea4eef1976 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -9,6 +9,9 @@ - #include - #include - #include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif - #include - #include - #include -@@ -582,6 +585,10 @@ static void isys_remove(struct auxiliary_device *auxdev) - struct isys_fw_msgs *fwmsg, *safe; - struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); - -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif - for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) - mutex_destroy(&isys->streams[i].mutex); - -@@ -605,6 +612,78 @@ static void isys_remove(struct auxiliary_device *auxdev) - #endif - } - -+#ifdef CONFIG_DEBUG_FS -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_isys *isys = file->private_data; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 log_size; -+ int ret = 0; -+ void *buf; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_info(dev, "copy %d bytes fw log to user...\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations isys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, isys, &isys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ isys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ - static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) - { - struct ipu7_bus_device *adev = isys->adev; -@@ -763,6 +842,11 @@ static int isys_probe(struct auxiliary_device *auxdev, - - isys_stream_init(isys); - -+#ifdef CONFIG_DEBUG_FS -+ /* Debug fs failure is not fatal. */ -+ ipu7_isys_init_debugfs(isys); -+#endif -+ - cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); - ret = alloc_fw_msg_bufs(isys, 20); - if (ret < 0) -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index 17d4d5630169..cd7375417452 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -25,6 +25,10 @@ - #include "ipu7-isys-csi2.h" - #include "ipu7-isys-video.h" - -+#ifdef CONFIG_DEBUG_FS -+struct dentry; -+ -+#endif - #define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" - - /* FW support max 16 streams */ -@@ -103,6 +107,9 @@ struct ipu7_isys { - unsigned int ref_count; - unsigned int stream_opened; - -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif - struct mutex mutex; /* Serialise isys video open/release related */ - struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ - -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index ee6b63717ed3..5649d8e1c352 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -7,6 +7,9 @@ - #include - #include - #include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif - #include - #include - #include -@@ -2248,7 +2251,50 @@ void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) - } - EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); - --static void ipu7_pci_config_setup(struct pci_dev *dev) -+#ifdef CONFIG_DEBUG_FS -+static struct debugfs_blob_wrapper isys_fw_error; -+static struct debugfs_blob_wrapper psys_fw_error; -+ -+static int ipu7_init_debugfs(struct ipu7_device *isp) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -+ if (!dir) -+ return -ENOMEM; -+ -+ isys_fw_error.data = &fw_error_log[IPU_IS]; -+ isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); -+ file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); -+ if (!file) -+ goto err; -+ psys_fw_error.data = &fw_error_log[IPU_PS]; -+ psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); -+ file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); -+ if (!file) -+ goto err; -+ -+ isp->ipu7_dir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+ -+static void ipu7_remove_debugfs(struct ipu7_device *isp) -+{ -+ /* -+ * Since isys and psys debugfs dir will be created under ipu root dir, -+ * mark its dentry to NULL to avoid duplicate removal. -+ */ -+ debugfs_remove_recursive(isp->ipu7_dir); -+ isp->ipu7_dir = NULL; -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+static int ipu7_pci_config_setup(struct pci_dev *dev) - { - u16 pci_command; - -@@ -2604,6 +2650,13 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - pm_runtime_put(&isp->psys->auxdev.dev); - } - -+#ifdef CONFIG_DEBUG_FS -+ ret = ipu7_init_debugfs(isp); -+ if (ret) { -+ dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); -+ goto out_ipu_bus_del_devices; -+ } -+#endif - pm_runtime_put_noidle(dev); - pm_runtime_allow(dev); - -@@ -2616,6 +2669,10 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - ipu7_unmap_fw_code_region(isp->isys); - if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) - ipu7_unmap_fw_code_region(isp->psys); -+#ifdef CONFIG_DEBUG_FS -+ if (!IS_ERR_OR_NULL(isp->fw_code_region)) -+ vfree(isp->fw_code_region); -+#endif - if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) - ipu7_mmu_cleanup(isp->psys->mmu); - if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -@@ -2636,6 +2693,9 @@ static void ipu7_pci_remove(struct pci_dev *pdev) - { - struct ipu7_device *isp = pci_get_drvdata(pdev); - -+#ifdef CONFIG_DEBUG_FS -+ ipu7_remove_debugfs(isp); -+#endif - if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) - ipu7_unmap_fw_code_region(isp->isys); - if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h -index ac8ac0689468..3b3ea5fb613f 100644 ---- a/drivers/staging/media/ipu7/ipu7.h -+++ b/drivers/staging/media/ipu7/ipu7.h -@@ -81,6 +81,9 @@ struct ipu7_device { - - void __iomem *base; - void __iomem *pb_base; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *ipu7_dir; -+#endif - u8 hw_ver; - bool ipc_reinit; - bool secure_mode; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf b/SPECS/kernel-rt/0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf new file mode 100644 index 000000000..54a69abf9 --- /dev/null +++ b/SPECS/kernel-rt/0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf @@ -0,0 +1,123 @@ +From a7b7cbaf27f9c2a83ab053d7048a83126e06654a Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Tue, 30 Dec 2025 16:17:29 -0800 +Subject: [PATCH 04/13] perf/x86/intel/uncore: Remove + has_generic_discovery_table() + +In the !x86_match_cpu() fallback path, has_generic_discovery_table() +is removed because it does not handle multiple PCI devices. Instead, +use PCI_ANY_ID in generic_uncore_init[] to probe all PCI devices. + +For MSR portals, only probe MSR 0x201e to keep the fallback simple, as +this path is best-effort only. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 3 ++ + arch/x86/events/intel/uncore_discovery.c | 42 +++++------------------- + 2 files changed, 12 insertions(+), 33 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index e3193a37a2150..5135a39d81f6b 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1835,6 +1835,9 @@ static const struct uncore_plat_init generic_uncore_init __initconst = { + .cpu_init = intel_uncore_generic_uncore_cpu_init, + .pci_init = intel_uncore_generic_uncore_pci_init, + .mmio_init = intel_uncore_generic_uncore_mmio_init, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = PCI_ANY_ID, ++ .domain[1].discovery_base = UNCORE_DISCOVERY_MSR, + }; + + static const struct x86_cpu_id intel_uncore_match[] __initconst = { +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index 10957163301b9..24b8da18ee6c8 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -12,24 +12,6 @@ + static struct rb_root discovery_tables = RB_ROOT; + static int num_discovered_types[UNCORE_ACCESS_MAX]; + +-static bool has_generic_discovery_table(void) +-{ +- struct pci_dev *dev; +- int dvsec; +- +- dev = pci_get_device(PCI_VENDOR_ID_INTEL, UNCORE_DISCOVERY_TABLE_DEVICE, NULL); +- if (!dev) +- return false; +- +- /* A discovery table device has the unique capability ID. */ +- dvsec = pci_find_next_ext_capability(dev, 0, UNCORE_EXT_CAP_ID_DISCOVERY); +- pci_dev_put(dev); +- if (dvsec) +- return true; +- +- return false; +-} +- + static int logical_die_id; + + static int get_device_die_id(struct pci_dev *dev) +@@ -359,12 +341,7 @@ static bool uncore_discovery_pci(struct uncore_discovery_domain *domain) + struct pci_dev *dev = NULL; + bool parsed = false; + +- if (domain->discovery_base) +- device = domain->discovery_base; +- else if (has_generic_discovery_table()) +- device = UNCORE_DISCOVERY_TABLE_DEVICE; +- else +- device = PCI_ANY_ID; ++ device = domain->discovery_base; + + /* + * Start a new search and iterates through the list of +@@ -407,7 +384,7 @@ static bool uncore_discovery_msr(struct uncore_discovery_domain *domain) + { + unsigned long *die_mask; + bool parsed = false; +- int cpu, die, msr; ++ int cpu, die; + u64 base; + + die_mask = kcalloc(BITS_TO_LONGS(uncore_max_dies()), +@@ -415,16 +392,13 @@ static bool uncore_discovery_msr(struct uncore_discovery_domain *domain) + if (!die_mask) + return false; + +- msr = domain->discovery_base ? +- domain->discovery_base : UNCORE_DISCOVERY_MSR; +- + cpus_read_lock(); + for_each_online_cpu(cpu) { + die = topology_logical_die_id(cpu); + if (__test_and_set_bit(die, die_mask)) + continue; + +- if (rdmsrq_safe_on_cpu(cpu, msr, &base)) ++ if (rdmsrq_safe_on_cpu(cpu, domain->discovery_base, &base)) + continue; + + if (!base) +@@ -447,10 +421,12 @@ bool uncore_discovery(struct uncore_plat_init *init) + + for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) { + domain = &init->domain[i]; +- if (!domain->base_is_pci) +- ret |= uncore_discovery_msr(domain); +- else +- ret |= uncore_discovery_pci(domain); ++ if (domain->discovery_base) { ++ if (!domain->base_is_pci) ++ ret |= uncore_discovery_msr(domain); ++ else ++ ret |= uncore_discovery_pci(domain); ++ } + } + + return ret; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt b/SPECS/kernel-rt/0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt new file mode 100644 index 000000000..940626dd7 --- /dev/null +++ b/SPECS/kernel-rt/0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt @@ -0,0 +1,224 @@ +From 0a6b452a5a68677c64f98d54872fdf065cacb6dc Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:33 -0700 +Subject: [PATCH 4/6] platform/x86:intel/pmc: Relocate lpm_req_guid to + pmc_reg_map +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Relocate the lpm_req_guid field from pmc_info to pmc_reg_map. The +previous implementation stored lpm_req_guid in pmc_info and relied +on pmc_core_find_guid() to retrieve the correct GUID, which was +unnecessary. Since lpm_req_guid is specific to PMC, pmc_reg_map is +a more appropriate location for this information. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-6-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/arl.c | 6 ++---- + drivers/platform/x86/intel/pmc/core.c | 15 ++------------- + drivers/platform/x86/intel/pmc/core.h | 4 +++- + drivers/platform/x86/intel/pmc/lnl.c | 2 +- + drivers/platform/x86/intel/pmc/mtl.c | 6 +++--- + drivers/platform/x86/intel/pmc/ptl.c | 3 +-- + 6 files changed, 12 insertions(+), 24 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c +index c0698ef35df89..eb23bc68340ab 100644 +--- a/drivers/platform/x86/intel/pmc/arl.c ++++ b/drivers/platform/x86/intel/pmc/arl.c +@@ -281,6 +281,7 @@ static const struct pmc_reg_map arl_socs_reg_map = { + .etr3_offset = ETR3_OFFSET, + .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, + .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, ++ .lpm_req_guid = SOCS_LPM_REQ_GUID, + }; + + static const struct pmc_bit_map arl_pchs_ltr_show_map[] = { +@@ -648,26 +649,23 @@ static const struct pmc_reg_map arl_pchs_reg_map = { + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_reg_index = ARL_LPM_REG_INDEX, + .etr3_offset = ETR3_OFFSET, ++ .lpm_req_guid = PCHS_LPM_REQ_GUID, + }; + + static struct pmc_info arl_pmc_info_list[] = { + { +- .guid = IOEP_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_IOEP, + .map = &mtl_ioep_reg_map, + }, + { +- .guid = SOCS_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_SOCS, + .map = &arl_socs_reg_map, + }, + { +- .guid = PCHS_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_PCHS, + .map = &arl_pchs_reg_map, + }, + { +- .guid = SOCM_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_SOCM, + .map = &mtl_socm_reg_map, + }, +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index 5f58dfa989ad5..e272c971d73a5 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -1444,15 +1444,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info + } + } + +-static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) +-{ +- for (; list->map; ++list) +- if (list->map == map) +- return list->guid; +- +- return 0; +-} +- + /* + * This function retrieves low power mode requirement data from PMC Low + * Power Mode (LPM) table. +@@ -1568,7 +1559,6 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + struct pci_dev *pcidev __free(pci_dev_put) = NULL; + struct telem_endpoint *ep; + unsigned int pmc_idx; +- u32 guid; + int ret; + + pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func)); +@@ -1582,11 +1572,10 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + if (!pmc) + continue; + +- guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); +- if (!guid) ++ if (!pmc->map->lpm_req_guid) + return -ENXIO; + +- ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); ++ ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); + if (IS_ERR(ep)) { + dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); + return -EPROBE_DEFER; +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index d80257b37ca98..cccd3bcafe00d 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -356,6 +356,7 @@ struct pmc_bit_map { + * @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter + * @num_s0ix_blocker: Number of S0ix blockers + * @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table ++ * @lpm_req_guid: Telemetry GUID to read low power mode substate requirement table + * + * Each PCH has unique set of register offsets and bit indexes. This structure + * captures them to have a common implementation. +@@ -397,6 +398,8 @@ struct pmc_reg_map { + const u8 *lpm_reg_index; + const u32 pson_residency_offset; + const u32 pson_residency_counter_step; ++ /* GUID for telemetry regions */ ++ const u32 lpm_req_guid; + }; + + /** +@@ -406,7 +409,6 @@ struct pmc_reg_map { + * specific attributes + */ + struct pmc_info { +- u32 guid; + u16 devid; + const struct pmc_reg_map *map; + }; +diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c +index 6fa027e7071f4..1cd81ee54dcf8 100644 +--- a/drivers/platform/x86/intel/pmc/lnl.c ++++ b/drivers/platform/x86/intel/pmc/lnl.c +@@ -533,11 +533,11 @@ static const struct pmc_reg_map lnl_socm_reg_map = { + .s0ix_blocker_maps = lnl_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .lpm_reg_index = LNL_LPM_REG_INDEX, ++ .lpm_req_guid = SOCM_LPM_REQ_GUID, + }; + + static struct pmc_info lnl_pmc_info_list[] = { + { +- .guid = SOCM_LPM_REQ_GUID, + .devid = PMC_DEVID_LNL_SOCM, + .map = &lnl_socm_reg_map, + }, +diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c +index 19470ca311cf7..57508cbf9cd42 100644 +--- a/drivers/platform/x86/intel/pmc/mtl.c ++++ b/drivers/platform/x86/intel/pmc/mtl.c +@@ -473,6 +473,7 @@ const struct pmc_reg_map mtl_socm_reg_map = { + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, ++ .lpm_req_guid = SOCP_LPM_REQ_GUID, + }; + + static const struct pmc_bit_map mtl_ioep_pfear_map[] = { +@@ -797,6 +798,7 @@ const struct pmc_reg_map mtl_ioep_reg_map = { + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, ++ .lpm_req_guid = IOEP_LPM_REQ_GUID, + }; + + static const struct pmc_bit_map mtl_ioem_pfear_map[] = { +@@ -944,21 +946,19 @@ static const struct pmc_reg_map mtl_ioem_reg_map = { + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, ++ .lpm_req_guid = IOEM_LPM_REQ_GUID, + }; + + static struct pmc_info mtl_pmc_info_list[] = { + { +- .guid = SOCP_LPM_REQ_GUID, + .devid = PMC_DEVID_MTL_SOCM, + .map = &mtl_socm_reg_map, + }, + { +- .guid = IOEP_LPM_REQ_GUID, + .devid = PMC_DEVID_MTL_IOEP, + .map = &mtl_ioep_reg_map, + }, + { +- .guid = IOEM_LPM_REQ_GUID, + .devid = PMC_DEVID_MTL_IOEM, + .map = &mtl_ioem_reg_map + }, +diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c +index 1b35b84e06fa2..1f48e2bbc699f 100644 +--- a/drivers/platform/x86/intel/pmc/ptl.c ++++ b/drivers/platform/x86/intel/pmc/ptl.c +@@ -528,16 +528,15 @@ static const struct pmc_reg_map ptl_pcdp_reg_map = { + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = PTL_NUM_S0IX_BLOCKER, + .blocker_req_offset = PTL_BLK_REQ_OFFSET, ++ .lpm_req_guid = PCDP_LPM_REQ_GUID, + }; + + static struct pmc_info ptl_pmc_info_list[] = { + { +- .guid = PCDP_LPM_REQ_GUID, + .devid = PMC_DEVID_PTL_PCDH, + .map = &ptl_pcdp_reg_map, + }, + { +- .guid = PCDP_LPM_REQ_GUID, + .devid = PMC_DEVID_PTL_PCDP, + .map = &ptl_pcdp_reg_map, + }, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet b/SPECS/kernel-rt/0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet new file mode 100644 index 000000000..a9f770343 --- /dev/null +++ b/SPECS/kernel-rt/0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet @@ -0,0 +1,32 @@ +From 0518f6873bfdb48c1767661c518f95346f22a62a Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Fri, 30 Jul 2021 08:33:53 +0800 +Subject: [PATCH 04/14] rtnetlink: Fix unchecked return value of + dev_xdp_query_md_btf() + +This patch is to check the return value of dev_xdp_query_md_btf() +whether it contain a valid btf id or vice versa. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + net/core/rtnetlink.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index b00c602d676fe..b4ac5dedaed0a 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1770,6 +1770,9 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + } + + md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); ++ if (!md_btf_id) ++ goto err_cancel; ++ + nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); + nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal b/SPECS/kernel-rt/0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal deleted file mode 100644 index 6421c83a6..000000000 --- a/SPECS/kernel-rt/0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal +++ /dev/null @@ -1,86 +0,0 @@ -From f7522111511911d62f066a39ac1a66c5d6f7c493 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:14 -0700 -Subject: [PATCH 04/11] thermal: intel: int340x: Add module parameter for - balanced Slider - -By default, the SoC slider value for the "balanced" platform profile is -set to 3. This update introduces a new module parameter, allowing users -to modify this default value. - -The module parameter can be specified during load time to set a custom -slider value for the "balanced" profile. If the module parameter is not -specified at load time and is updated later, the new value will only take -effect after the next write of "balanced" to the sysfs "profile" -attribute. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-4-srinivas.pandruvada@linux.intel.com -[ rjw: Minor adjustments of module param description ] -Signed-off-by: Rafael J. Wysocki ---- - .../processor_thermal_soc_slider.c | 41 +++++++++++++++++++ - 1 file changed, 41 insertions(+) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -index 57782a63b9b5..ffbad6b4326e 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -53,6 +53,43 @@ static u8 slider_values[] = { - [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM, - }; - -+/* Lock to protect module param updates */ -+static DEFINE_MUTEX(slider_param_lock); -+ -+static int slider_balanced_param = SOC_SLIDER_VALUE_BALANCE; -+ -+static int slider_def_balance_set(const char *arg, const struct kernel_param *kp) -+{ -+ u8 slider_val; -+ int ret; -+ -+ guard(mutex)(&slider_param_lock); -+ -+ ret = kstrtou8(arg, 16, &slider_val); -+ if (!ret) { -+ if (slider_val > SOC_SLIDER_VALUE_MAXIMUM) -+ return -EINVAL; -+ -+ slider_balanced_param = slider_val; -+ } -+ -+ return ret; -+} -+ -+static int slider_def_balance_get(char *buf, const struct kernel_param *kp) -+{ -+ guard(mutex)(&slider_param_lock); -+ return sysfs_emit(buf, "%02x\n", slider_values[SOC_POWER_SLIDER_BALANCE]); -+} -+ -+static const struct kernel_param_ops slider_def_balance_ops = { -+ .set = slider_def_balance_set, -+ .get = slider_def_balance_get, -+}; -+ -+module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644); -+MODULE_PARM_DESC(slider_balance, "Set slider default value for balance"); -+ - /* Convert from platform power profile option to SoC slider value */ - static int convert_profile_to_power_slider(enum platform_profile_option profile) - { -@@ -115,6 +152,10 @@ static int power_slider_platform_profile_set(struct device *dev, - if (!proc_priv) - return -EOPNOTSUPP; - -+ guard(mutex)(&slider_param_lock); -+ -+ slider_values[SOC_POWER_SLIDER_BALANCE] = slider_balanced_param; -+ - slider = convert_profile_to_power_slider(profile); - if (slider < 0) - return slider; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt b/SPECS/kernel-rt/0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt deleted file mode 100644 index 9a17548d0..000000000 --- a/SPECS/kernel-rt/0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt +++ /dev/null @@ -1,142 +0,0 @@ -From 7218ac5ef718c410845cfdbf55822b3953aeba36 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Mon, 3 Jun 2024 08:32:00 +0300 -Subject: [PATCH 4/4] thunderbolt: Add Kconfig option to disable PCIe tunneling - -In typical cases PCIe tunneling is needed to make the devices fully -usable for the host system. However, it poses a security issue because -they can also use DMA to access the host memory. We already have two -ways of preventing this, one an IOMMU that is enabled on recent systems -by default and the second is the "authorized" attribute under each -connected device that needs to be written by userspace before a PCIe -tunnel is created. This option adds one more by adding a Kconfig option, -which is enabled by default, that can be used to make kernel binaries -where PCIe tunneling is completely disabled. - -Signed-off-by: Mika Westerberg ---- - drivers/thunderbolt/Kconfig | 18 ++++++++++++++++++ - drivers/thunderbolt/tb.c | 2 +- - drivers/thunderbolt/tb.h | 9 +++++++++ - drivers/thunderbolt/tunnel.c | 8 ++++---- - drivers/thunderbolt/usb4.c | 2 +- - 5 files changed, 33 insertions(+), 6 deletions(-) - -diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig -index 0abdb69ee9f4..8bf4ecf7f76e 100644 ---- a/drivers/thunderbolt/Kconfig -+++ b/drivers/thunderbolt/Kconfig -@@ -18,6 +18,24 @@ menuconfig USB4 - - if USB4 - -+config USB4_PCIE_TUNNELING -+ bool "Allow PCI Express tunneling over USB4 fabric" -+ depends on PCI -+ default y -+ help -+ USB4 and Thunderbolt devices typically include PCIe switch -+ with a number of PCIe endpoints such as USB host controllers, -+ GPUs and network adapters. These are made available to the -+ host system through PCIe tunneling. These can use DMA and -+ therefore have access to the host memory which is typically -+ guarded by an IOMMU. This option allows disabling PCIe -+ tunneling completely. -+ -+ For devices to be usable it is recommended to say Y here. -+ -+ Note this only works with systems that use Software Based -+ Connection Manager (this is most USB4 hosts). -+ - config USB4_DEBUGFS_WRITE - bool "Enable write by debugfs to configuration spaces (DANGEROUS)" - help -diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c -index c14ab1fbeeaf..0514a673471a 100644 ---- a/drivers/thunderbolt/tb.c -+++ b/drivers/thunderbolt/tb.c -@@ -3364,7 +3364,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) - if (!tb) - return NULL; - -- if (tb_acpi_may_tunnel_pcie()) -+ if (tb_may_tunnel_pcie()) - tb->security_level = TB_SECURITY_USER; - else - tb->security_level = TB_SECURITY_NOPCIE; -diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h -index f503bad86413..7d5c673412f4 100644 ---- a/drivers/thunderbolt/tb.h -+++ b/drivers/thunderbolt/tb.h -@@ -1518,6 +1518,15 @@ static inline int tb_acpi_power_on_retimers(struct tb_port *port) { return 0; } - static inline int tb_acpi_power_off_retimers(struct tb_port *port) { return 0; } - #endif - -+static inline bool tb_may_tunnel_pcie(void) -+{ -+#ifdef CONFIG_USB4_PCIE_TUNNELING -+ return tb_acpi_may_tunnel_pcie(); -+#else -+ return false; -+#endif -+} -+ - #ifdef CONFIG_DEBUG_FS - void tb_debugfs_init(void); - void tb_debugfs_exit(void); -diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c -index d52efe3f658c..6b85da2eea60 100644 ---- a/drivers/thunderbolt/tunnel.c -+++ b/drivers/thunderbolt/tunnel.c -@@ -130,7 +130,7 @@ static unsigned int tb_available_credits(const struct tb_port *port, - size_t ndp; - - usb3 = tb_acpi_may_tunnel_usb3() ? sw->max_usb3_credits : 0; -- pcie = tb_acpi_may_tunnel_pcie() ? sw->max_pcie_credits : 0; -+ pcie = tb_may_tunnel_pcie() ? sw->max_pcie_credits : 0; - - if (tb_acpi_is_xdomain_allowed()) { - spare = min_not_zero(sw->max_dma_credits, dma_credits); -@@ -553,7 +553,7 @@ bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up, - if (WARN_ON_ONCE(!port->remote)) - return false; - -- if (!tb_acpi_may_tunnel_pcie()) -+ if (!tb_may_tunnel_pcie()) - return false; - - if (tb_port_get_link_generation(port) < 4) -@@ -1720,7 +1720,7 @@ static unsigned int tb_dma_available_credits(const struct tb_port *port) - int credits; - - credits = tb_available_credits(port, NULL); -- if (tb_acpi_may_tunnel_pcie()) -+ if (tb_may_tunnel_pcie()) - credits -= sw->max_pcie_credits; - credits -= port->dma_credits; - -@@ -2031,7 +2031,7 @@ static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel, - int *consumed_up, int *consumed_down) - { - struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); -- int pcie_weight = tb_acpi_may_tunnel_pcie() ? TB_PCI_WEIGHT : 0; -+ int pcie_weight = tb_may_tunnel_pcie() ? TB_PCI_WEIGHT : 0; - - /* - * PCIe tunneling, if enabled, affects the USB3 bandwidth so -diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c -index fdae76c8f728..b3d22873e837 100644 ---- a/drivers/thunderbolt/usb4.c -+++ b/drivers/thunderbolt/usb4.c -@@ -276,7 +276,7 @@ int usb4_switch_setup(struct tb_switch *sw) - * Only enable PCIe tunneling if the parent router supports it - * and it is not disabled. - */ -- if (tb_acpi_may_tunnel_pcie() && -+ if (tb_may_tunnel_pcie() && - tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) { - val |= ROUTER_CS_5_PTO; - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo b/SPECS/kernel-rt/0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo new file mode 100644 index 000000000..2324125c7 --- /dev/null +++ b/SPECS/kernel-rt/0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo @@ -0,0 +1,258 @@ +From 19762cd26107878f80efcb5e366a47cee62e1b31 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 19:41:30 -0300 +Subject: [PATCH 04/21] tools/power turbostat: Refactor added-counter value + printing code + +We build up many copies of very similar code... + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 153 ++++++++++---------------- + 1 file changed, 57 insertions(+), 96 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 5d753df8706d0..f9b99940b2478 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2717,10 +2717,24 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + */ + static inline int print_name(int width, int *printed, char *delim, char *name) + { +- if (width == 64) ++ if (width <= 32) ++ return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); ++ else + return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); ++} ++static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) ++{ ++ if (width <= 32) ++ return (sprintf(outp, "%s%08x", (*printed++ ? delim : ""), (unsigned int)value)); + else +- return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); ++ return (sprintf(outp, "%s%016llx", (*printed++ ? delim : ""), value)); ++} ++static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) ++{ ++ if (width <= 32) ++ return (sprintf(outp, "%s%d", (*printed++ ? delim : ""), (unsigned int)value)); ++ else ++ return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + } + + void print_header(char *delim) +@@ -3221,20 +3235,13 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->smi_count); + +- /* Added counters */ ++ /* Added Thread Counters */ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { +- if (mp->format == FORMAT_RAW) { +- if (mp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)t->counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->counter[i]); +- } else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), t->counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->counter[i]); +- } else if (mp->format == FORMAT_PERCENT) { ++ if (mp->format == FORMAT_RAW) ++ outp += print_hex_value(mp->width, &printed, delim, t->counter[i]); ++ else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(mp->width, &printed, delim, t->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) { + if (mp->type == COUNTER_USEC) + outp += + sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +@@ -3244,21 +3251,13 @@ int format_counters(PER_THREAD_PARAMS) + } + } + +- /* Added perf counters */ ++ /* Added perf Thread Counters */ + for (i = 0, pp = sys.perf_tp; pp; ++i, pp = pp->next) { +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)t->perf_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->perf_counter[i]); +- } else if (pp->format == FORMAT_DELTA) { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), t->perf_counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->perf_counter[i]); +- } else if (pp->format == FORMAT_PERCENT) { ++ if (pp->format == FORMAT_RAW) ++ outp += print_hex_value(pp->width, &printed, delim, t->perf_counter[i]); ++ else if (pp->format == FORMAT_DELTA) ++ outp += print_decimal_value(pp->width, &printed, delim, t->perf_counter[i]); ++ else if (pp->format == FORMAT_PERCENT) { + if (pp->type == COUNTER_USEC) + outp += + sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +@@ -3269,17 +3268,13 @@ int format_counters(PER_THREAD_PARAMS) + } + } + ++ /* Added PMT Thread Counters */ + for (i = 0, ppmt = sys.pmt_tp; ppmt; i++, ppmt = ppmt->next) { + const unsigned long value_raw = t->pmt_counter[i]; + double value_converted; + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)t->pmt_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->pmt_counter[i]); +- ++ outp += print_hex_value(pmt_counter_get_width(ppmt), &printed, delim, t->pmt_counter[i]); + break; + + case PMT_TYPE_XTAL_TIME: +@@ -3319,52 +3314,35 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_CORE_THROT_CNT)) + outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->core_throt_cnt); + ++ /* Added Core Counters */ + for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { +- if (mp->format == FORMAT_RAW) { +- if (mp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)c->counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->counter[i]); +- } else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), c->counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->counter[i]); +- } else if (mp->format == FORMAT_PERCENT) { ++ if (mp->format == FORMAT_RAW) ++ outp += print_hex_value(mp->width, &printed, delim, c->counter[i]); ++ else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(mp->width, &printed, delim, c->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->counter[i] / tsc); + } + } + ++ /* Added perf Core counters */ + for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) { +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)c->perf_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->perf_counter[i]); +- } else if (pp->format == FORMAT_DELTA) { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), c->perf_counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->perf_counter[i]); +- } else if (pp->format == FORMAT_PERCENT) { ++ if (pp->format == FORMAT_RAW) ++ outp += print_hex_value(pp->width, &printed, delim, c->perf_counter[i]); ++ else if (pp->format == FORMAT_DELTA) ++ outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); ++ else if (pp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->perf_counter[i] / tsc); + } + } + ++ /* Added PMT Core counters */ + for (i = 0, ppmt = sys.pmt_cp; ppmt; i++, ppmt = ppmt->next) { + const unsigned long value_raw = c->pmt_counter[i]; + double value_converted; + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)c->pmt_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->pmt_counter[i]); +- ++ outp += print_hex_value(pmt_counter_get_width(ppmt), &printed, delim, c->pmt_counter[i]); + break; + + case PMT_TYPE_XTAL_TIME: +@@ -3518,37 +3496,24 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_UNCORE_MHZ)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), p->uncore_mhz); + ++ /* Added Package Counters */ + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { +- if (mp->format == FORMAT_RAW) { +- if (mp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)p->counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->counter[i]); +- } else if (mp->format == FORMAT_DELTA) { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), p->counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), p->counter[i]); +- } else if (mp->format == FORMAT_PERCENT) { ++ if (mp->format == FORMAT_RAW) ++ outp += print_hex_value(mp->width, &printed, delim, p->counter[i]); ++ else if (mp->format == FORMAT_DELTA) ++ outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->counter[i] / tsc); + } else if (mp->type == COUNTER_K2M) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->counter[i] / 1000); + } + ++ /* Added perf Package Counters */ + for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) { +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)p->perf_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->perf_counter[i]); +- } else if (pp->format == FORMAT_DELTA) { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), p->perf_counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), p->perf_counter[i]); ++ if (pp->format == FORMAT_RAW) ++ outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); ++ else if (pp->format == FORMAT_DELTA) { ++ outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); + } else if (pp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->perf_counter[i] / tsc); + } else if (pp->type == COUNTER_K2M) { +@@ -3557,17 +3522,13 @@ int format_counters(PER_THREAD_PARAMS) + } + } + ++ /* Added PMT Package Counters */ + for (i = 0, ppmt = sys.pmt_pp; ppmt; i++, ppmt = ppmt->next) { + const unsigned long value_raw = p->pmt_counter[i]; + double value_converted; + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)p->pmt_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->pmt_counter[i]); +- ++ outp += print_hex_value(pmt_counter_get_width(ppmt), &printed, delim, p->pmt_counter[i]); + break; + + case PMT_TYPE_XTAL_TIME: +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi b/SPECS/kernel-rt/0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi new file mode 100644 index 000000000..a2f08b20d --- /dev/null +++ b/SPECS/kernel-rt/0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi @@ -0,0 +1,228 @@ +From 52c71c618c4e3b0ab4515b0f0eaa2cdc1205c36d Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Thu, 28 Aug 2025 13:26:38 -0400 +Subject: [PATCH 04/44] x86/cea: Prefix event stack names with ESTACK_ + +Add the ESTACK_ prefix to event stack names to improve clarity and +readability. Without the prefix, names like DF, NMI, and DB are too +brief and potentially ambiguous. + +This renaming also prepares for converting __this_cpu_ist_top_va from +a macro into a function that accepts an enum exception_stack_ordering +argument, without requiring changes to existing callsites. + +Acked-by: Dave Hansen +Signed-off-by: Xin Li (Intel) +--- + +Changes in v7: +* Move rename code to this patch (Dave Hansen). +* Fix a vertical alignment (Dave Hansen). +--- + arch/x86/coco/sev/noinstr.c | 4 ++-- + arch/x86/coco/sev/vc-handle.c | 2 +- + arch/x86/include/asm/cpu_entry_area.h | 26 +++++++++++++------------- + arch/x86/kernel/cpu/common.c | 10 +++++----- + arch/x86/kernel/dumpstack_64.c | 14 +++++++------- + arch/x86/kernel/fred.c | 6 +++--- + arch/x86/kernel/traps.c | 2 +- + arch/x86/mm/cpu_entry_area.c | 12 ++++++------ + arch/x86/mm/fault.c | 2 +- + 9 files changed, 39 insertions(+), 39 deletions(-) + +diff --git a/arch/x86/coco/sev/noinstr.c b/arch/x86/coco/sev/noinstr.c +index b527eafb63123..c3985c9b232c5 100644 +--- a/arch/x86/coco/sev/noinstr.c ++++ b/arch/x86/coco/sev/noinstr.c +@@ -30,7 +30,7 @@ static __always_inline bool on_vc_stack(struct pt_regs *regs) + if (ip_within_syscall_gap(regs)) + return false; + +- return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC))); ++ return ((sp >= __this_cpu_ist_bottom_va(ESTACK_VC)) && (sp < __this_cpu_ist_top_va(ESTACK_VC))); + } + + /* +@@ -82,7 +82,7 @@ void noinstr __sev_es_ist_exit(void) + /* Read IST entry */ + ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]); + +- if (WARN_ON(ist == __this_cpu_ist_top_va(VC))) ++ if (WARN_ON(ist == __this_cpu_ist_top_va(ESTACK_VC))) + return; + + /* Read back old IST entry and write it to the TSS */ +diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c +index 7fc136a353347..1d3f086ae4c3d 100644 +--- a/arch/x86/coco/sev/vc-handle.c ++++ b/arch/x86/coco/sev/vc-handle.c +@@ -871,7 +871,7 @@ static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt, + + static __always_inline bool is_vc2_stack(unsigned long sp) + { +- return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2)); ++ return (sp >= __this_cpu_ist_bottom_va(ESTACK_VC2) && sp < __this_cpu_ist_top_va(ESTACK_VC2)); + } + + static __always_inline bool vc_from_invalid_context(struct pt_regs *regs) +diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h +index 462fc34f13176..d0f884c281787 100644 +--- a/arch/x86/include/asm/cpu_entry_area.h ++++ b/arch/x86/include/asm/cpu_entry_area.h +@@ -18,19 +18,19 @@ + + /* Macro to enforce the same ordering and stack sizes */ + #define ESTACKS_MEMBERS(guardsize, optional_stack_size) \ +- char DF_stack_guard[guardsize]; \ +- char DF_stack[EXCEPTION_STKSZ]; \ +- char NMI_stack_guard[guardsize]; \ +- char NMI_stack[EXCEPTION_STKSZ]; \ +- char DB_stack_guard[guardsize]; \ +- char DB_stack[EXCEPTION_STKSZ]; \ +- char MCE_stack_guard[guardsize]; \ +- char MCE_stack[EXCEPTION_STKSZ]; \ +- char VC_stack_guard[guardsize]; \ +- char VC_stack[optional_stack_size]; \ +- char VC2_stack_guard[guardsize]; \ +- char VC2_stack[optional_stack_size]; \ +- char IST_top_guard[guardsize]; \ ++ char ESTACK_DF_stack_guard[guardsize]; \ ++ char ESTACK_DF_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_NMI_stack_guard[guardsize]; \ ++ char ESTACK_NMI_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_DB_stack_guard[guardsize]; \ ++ char ESTACK_DB_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_MCE_stack_guard[guardsize]; \ ++ char ESTACK_MCE_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_VC_stack_guard[guardsize]; \ ++ char ESTACK_VC_stack[optional_stack_size]; \ ++ char ESTACK_VC2_stack_guard[guardsize]; \ ++ char ESTACK_VC2_stack[optional_stack_size]; \ ++ char ESTACK_IST_top_guard[guardsize]; \ + + /* The exception stacks' physical storage. No guard pages required */ + struct exception_stacks { +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +index 02d97834a1d4d..a2a251549fa15 100644 +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -2336,12 +2336,12 @@ static inline void setup_getcpu(int cpu) + static inline void tss_setup_ist(struct tss_struct *tss) + { + /* Set up the per-CPU TSS IST stacks */ +- tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(DF); +- tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(NMI); +- tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(DB); +- tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(MCE); ++ tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(ESTACK_DF); ++ tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(ESTACK_NMI); ++ tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(ESTACK_DB); ++ tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(ESTACK_MCE); + /* Only mapped when SEV-ES is active */ +- tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(VC); ++ tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(ESTACK_VC); + } + #else /* CONFIG_X86_64 */ + static inline void tss_setup_ist(struct tss_struct *tss) { } +diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c +index 6c5defd6569a3..40f51e2781715 100644 +--- a/arch/x86/kernel/dumpstack_64.c ++++ b/arch/x86/kernel/dumpstack_64.c +@@ -73,7 +73,7 @@ struct estack_pages { + PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = { \ + .offs = CEA_ESTACK_OFFS(st), \ + .size = CEA_ESTACK_SIZE(st), \ +- .type = STACK_TYPE_EXCEPTION + ESTACK_ ##st, } ++ .type = STACK_TYPE_EXCEPTION + st, } + + /* + * Array of exception stack page descriptors. If the stack is larger than +@@ -83,12 +83,12 @@ struct estack_pages { + */ + static const + struct estack_pages estack_pages[CEA_ESTACK_PAGES] ____cacheline_aligned = { +- EPAGERANGE(DF), +- EPAGERANGE(NMI), +- EPAGERANGE(DB), +- EPAGERANGE(MCE), +- EPAGERANGE(VC), +- EPAGERANGE(VC2), ++ EPAGERANGE(ESTACK_DF), ++ EPAGERANGE(ESTACK_NMI), ++ EPAGERANGE(ESTACK_DB), ++ EPAGERANGE(ESTACK_MCE), ++ EPAGERANGE(ESTACK_VC), ++ EPAGERANGE(ESTACK_VC2), + }; + + static __always_inline bool in_exception_stack(unsigned long *stack, struct stack_info *info) +diff --git a/arch/x86/kernel/fred.c b/arch/x86/kernel/fred.c +index 816187da3a47c..06d944a3d0511 100644 +--- a/arch/x86/kernel/fred.c ++++ b/arch/x86/kernel/fred.c +@@ -87,7 +87,7 @@ void cpu_init_fred_rsps(void) + FRED_STKLVL(X86_TRAP_DF, FRED_DF_STACK_LEVEL)); + + /* The FRED equivalents to IST stacks... */ +- wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(DB)); +- wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(NMI)); +- wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(DF)); ++ wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); ++ wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); ++ wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); + } +diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c +index 6b22611e69cc8..47b7b7495114f 100644 +--- a/arch/x86/kernel/traps.c ++++ b/arch/x86/kernel/traps.c +@@ -954,7 +954,7 @@ asmlinkage __visible noinstr struct pt_regs *vc_switch_off_ist(struct pt_regs *r + + if (!get_stack_info_noinstr(stack, current, &info) || info.type == STACK_TYPE_ENTRY || + info.type > STACK_TYPE_EXCEPTION_LAST) +- sp = __this_cpu_ist_top_va(VC2); ++ sp = __this_cpu_ist_top_va(ESTACK_VC2); + + sync: + /* +diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c +index 575f863f3c75e..9fa371af8abc7 100644 +--- a/arch/x86/mm/cpu_entry_area.c ++++ b/arch/x86/mm/cpu_entry_area.c +@@ -151,15 +151,15 @@ static void __init percpu_setup_exception_stacks(unsigned int cpu) + * by guard pages so each stack must be mapped separately. DB2 is + * not mapped; it just exists to catch triple nesting of #DB. + */ +- cea_map_stack(DF); +- cea_map_stack(NMI); +- cea_map_stack(DB); +- cea_map_stack(MCE); ++ cea_map_stack(ESTACK_DF); ++ cea_map_stack(ESTACK_NMI); ++ cea_map_stack(ESTACK_DB); ++ cea_map_stack(ESTACK_MCE); + + if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) { + if (cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT)) { +- cea_map_stack(VC); +- cea_map_stack(VC2); ++ cea_map_stack(ESTACK_VC); ++ cea_map_stack(ESTACK_VC2); + } + } + } +diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c +index 998bd807fc7ba..6d59ff7c73e17 100644 +--- a/arch/x86/mm/fault.c ++++ b/arch/x86/mm/fault.c +@@ -671,7 +671,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code, + * and then double-fault, though, because we're likely to + * break the console driver and lose most of the stack dump. + */ +- call_on_stack(__this_cpu_ist_top_va(DF) - sizeof(void*), ++ call_on_stack(__this_cpu_ist_top_va(ESTACK_DF) - sizeof(void *), + handle_stack_overflow, + ASM_CALL_ARG3, + , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info)); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac b/SPECS/kernel-rt/0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac deleted file mode 100644 index 2c16ee695..000000000 --- a/SPECS/kernel-rt/0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac +++ /dev/null @@ -1,59 +0,0 @@ -From 5deabf6f8b12692ad9f18b425c2290e2f0d9c107 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Wed, 23 Jul 2025 20:31:05 +0800 -Subject: [PATCH 05/13] EDAC/skx_common: Make skx_dev->imc[] a flexible array - -The current skx->imc[NUM_IMC] array of memory controller instances is -sized using the macro NUM_IMC. Each time EDAC support is added for a -new CPU, NUM_IMC needs to be updated to ensure it is greater than or -equal to the number of memory controllers for the new CPU. This approach -is inconvenient and results in memory waste for older CPUs with fewer -memory controllers. - -To address this, make skx->imc[] a flexible array and determine its size -from configuration data or at runtime. - -Suggested-by: Tony Luck -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 3 ++- - drivers/edac/skx_common.h | 2 +- - 2 files changed, 3 insertions(+), 2 deletions(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index e03268e9073f..09187043c045 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -343,7 +344,7 @@ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) - if (!pdev) - break; - ndev++; -- d = kzalloc(sizeof(*d), GFP_KERNEL); -+ d = kzalloc(struct_size(d, imc, imc_num), GFP_KERNEL); - if (!d) { - pci_dev_put(pdev); - return -ENOMEM; -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index 95d61d23f89e..e7038fd45d06 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -172,7 +172,7 @@ struct skx_dev { - u8 colbits; - } dimms[NUM_DIMMS]; - } chan[NUM_CHANNELS]; -- } imc[NUM_IMC]; -+ } imc[]; - }; - - struct skx_pvt { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security b/SPECS/kernel-rt/0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security new file mode 100644 index 000000000..5269ebf2d --- /dev/null +++ b/SPECS/kernel-rt/0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security @@ -0,0 +1,180 @@ +From 6ddc16899f378f03affaac0b7a2a864938afe044 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Tue, 1 Mar 2022 16:36:25 +0200 +Subject: [PATCH 5/8] INTEL_DII: mei: avoid reset if fw is down + +If FW is not ready and FW reset flag is off +do not start reset flow as FW will not answer anyway. +Wait till interrupt with FW reset flag on to start reset flow. + +Flow: +FW on the way down unset ME_RDY_HRA and ME_RST_HRA and sends interrupt. +Driver receives interrupt and stops all communication. +(Before the patch here the driver starts reset flow). +FW on the way up set ME_RST_HRA and sends interrupt. +Driver receives interrupt and starts the link reset flow. +FW sets ME_RDY_HRA and unsets ME_RST_HRA and sends interrupt. +Driver continues with link reset flow. + +Limit the flow to GSC for now. + +Co-developed-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/gsc-me.c | 2 +- + drivers/misc/mei/hw-me.c | 16 ++++++++++++++++ + drivers/misc/mei/hw-me.h | 2 ++ + drivers/misc/mei/init.c | 7 +++++-- + drivers/misc/mei/main.c | 1 + + drivers/misc/mei/mei_dev.h | 3 ++- + 6 files changed, 27 insertions(+), 4 deletions(-) + +diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c +index 93cba090ea088..72e1d195dce9a 100644 +--- a/drivers/misc/mei/gsc-me.c ++++ b/drivers/misc/mei/gsc-me.c +@@ -21,7 +21,7 @@ + + #include "mei-trace.h" + +-#define MEI_GSC_RPM_TIMEOUT 500 ++#define MEI_GSC_RPM_TIMEOUT 2000 + + static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) + { +diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c +index c92a0e27787bb..78b3050f2f801 100644 +--- a/drivers/misc/mei/hw-me.c ++++ b/drivers/misc/mei/hw-me.c +@@ -1299,6 +1299,7 @@ EXPORT_SYMBOL_GPL(mei_me_irq_quick_handler); + irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) + { + struct mei_device *dev = (struct mei_device *) dev_id; ++ struct mei_me_hw *hw = to_me_hw(dev); + struct list_head cmpl_list; + s32 slots; + u32 hcsr; +@@ -1313,6 +1314,16 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) + + INIT_LIST_HEAD(&cmpl_list); + ++ /* HW not ready without reset - HW is powering down */ ++ if (hw->cfg->hw_down_supported && !mei_hw_is_ready(dev) && !mei_me_hw_is_resetting(dev)) { ++ dev_notice(&dev->dev, "FW not ready and not resetting\n"); ++ mei_cl_all_disconnect(dev); ++ /* move device to fw down state to allow reset flow on next interrupt */ ++ mei_set_devstate(dev, MEI_DEV_FW_DOWN); ++ pm_runtime_mark_last_busy(dev->parent); ++ goto end; ++ } ++ + /* check if ME wants a reset */ + if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { + if (kind_is_gsc(dev) || kind_is_gscfi(dev)) { +@@ -1654,6 +1665,9 @@ static enum mei_dev_kind mei_cfg_kind_ioe(const struct pci_dev *pdev) + #define MEI_CFG_TRC \ + .hw_trc_supported = 1 + ++#define MEI_CFG_DOWN \ ++ .hw_down_supported = 1 ++ + /* ICH Legacy devices */ + static const struct mei_cfg mei_me_ich_cfg = { + MEI_CFG_KIND_MEI, +@@ -1778,6 +1792,7 @@ static const struct mei_cfg mei_me_gsc_cfg = { + MEI_CFG_KIND_GSC, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, ++ MEI_CFG_DOWN, + }; + + /* Graphics System Controller Firmware Interface */ +@@ -1785,6 +1800,7 @@ static const struct mei_cfg mei_me_gscfi_cfg = { + MEI_CFG_KIND_GSCFI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, ++ MEI_CFG_DOWN, + }; + + /* +diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h +index 520f2b7bd301a..db5df58b26c88 100644 +--- a/drivers/misc/mei/hw-me.h ++++ b/drivers/misc/mei/hw-me.h +@@ -23,6 +23,7 @@ + * @dma_size: device DMA buffers size + * @fw_ver_supported: is fw version retrievable from FW + * @hw_trc_supported: does the hw support trc register ++ * @hw_down_supported: can go down + */ + struct mei_cfg { + const struct mei_fw_status fw_status; +@@ -31,6 +32,7 @@ struct mei_cfg { + size_t dma_size[DMA_DSCR_NUM]; + u32 fw_ver_supported:1; + u32 hw_trc_supported:1; ++ u32 hw_down_supported:1; + }; + + +diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c +index b789c4d9c709f..c5ed8b29e4d4e 100644 +--- a/drivers/misc/mei/init.c ++++ b/drivers/misc/mei/init.c +@@ -27,6 +27,7 @@ const char *mei_dev_state_str(int state) + MEI_DEV_STATE(POWERING_DOWN); + MEI_DEV_STATE(POWER_DOWN); + MEI_DEV_STATE(POWER_UP); ++ MEI_DEV_STATE(FW_DOWN); + default: + return "unknown"; + } +@@ -105,7 +106,8 @@ int mei_reset(struct mei_device *dev) + if (state != MEI_DEV_INITIALIZING && + state != MEI_DEV_DISABLED && + state != MEI_DEV_POWER_DOWN && +- state != MEI_DEV_POWER_UP) { ++ state != MEI_DEV_POWER_UP && ++ state != MEI_DEV_FW_DOWN) { + char fw_sts_str[MEI_FW_STATUS_STR_SZ]; + + mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ); +@@ -343,7 +345,8 @@ EXPORT_SYMBOL_GPL(mei_stop); + */ + bool mei_write_is_idle(struct mei_device *dev) + { +- bool idle = (dev->dev_state == MEI_DEV_ENABLED && ++ bool idle = ((dev->dev_state == MEI_DEV_ENABLED || ++ dev->dev_state == MEI_DEV_FW_DOWN) && + list_empty(&dev->ctrl_wr_list) && + list_empty(&dev->write_list) && + list_empty(&dev->write_waiting_list)); +diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c +index 00b0781fc2933..c13679ff721d4 100644 +--- a/drivers/misc/mei/main.c ++++ b/drivers/misc/mei/main.c +@@ -1154,6 +1154,7 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) + put_device(clsdev); + } + } ++EXPORT_SYMBOL_GPL(mei_set_devstate); + + static const char *mei_kind_names[] = { + "mei", +diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h +index 1db718d5c93f6..ad41d88fafe16 100644 +--- a/drivers/misc/mei/mei_dev.h ++++ b/drivers/misc/mei/mei_dev.h +@@ -65,7 +65,8 @@ enum mei_dev_state { + MEI_DEV_DISABLED, + MEI_DEV_POWERING_DOWN, + MEI_DEV_POWER_DOWN, +- MEI_DEV_POWER_UP ++ MEI_DEV_POWER_UP, ++ MEI_DEV_FW_DOWN, + }; + + /** +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet b/SPECS/kernel-rt/0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet deleted file mode 100644 index f06af8ff7..000000000 --- a/SPECS/kernel-rt/0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet +++ /dev/null @@ -1,59 +0,0 @@ -From 296265e9e4a461e10613e4e4b6df6715f171b1e7 Mon Sep 17 00:00:00 2001 -From: Chao Gao -Date: Fri, 4 Jul 2025 01:49:35 -0700 -Subject: [PATCH 05/24] KVM: x86: Zero XSTATE components on INIT by iterating - over supported features - -Tweak the code a bit to facilitate resetting more xstate components in -the future, e.g., CET's xstate-managed MSRs. - -No functional change intended. - -Suggested-by: Sean Christopherson -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a72f5f986984..a6765d1d1741 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -12398,6 +12398,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) - static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) - { - struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate; -+ u64 xfeatures_mask; -+ int i; - - /* - * Guest FPU state is zero allocated and so doesn't need to be manually -@@ -12411,16 +12413,20 @@ static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) - * are unchanged. Currently, the only components that are zeroed and - * supported by KVM are MPX related. - */ -- if (!kvm_mpx_supported()) -+ xfeatures_mask = (kvm_caps.supported_xcr0 | kvm_caps.supported_xss) & -+ (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); -+ if (!xfeatures_mask) - return; - -+ BUILD_BUG_ON(sizeof(xfeatures_mask) * BITS_PER_BYTE <= XFEATURE_MAX); -+ - /* - * All paths that lead to INIT are required to load the guest's FPU - * state (because most paths are buried in KVM_RUN). - */ - kvm_put_guest_fpu(vcpu); -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDREGS); -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDCSR); -+ for_each_set_bit(i, (unsigned long *)&xfeatures_mask, XFEATURE_MAX) -+ fpstate_clear_xstate_component(fpstate, i); - kvm_load_guest_fpu(vcpu); - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-comedi-multiq3-sanitize-config-options-in-multiq3_at.patch b/SPECS/kernel-rt/0005-comedi-multiq3-sanitize-config-options-in-multiq3_at.patch deleted file mode 100644 index de00a0262..000000000 --- a/SPECS/kernel-rt/0005-comedi-multiq3-sanitize-config-options-in-multiq3_at.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 84a7d0659251f918ab696c643b2e3381e72b49a6 Mon Sep 17 00:00:00 2001 -From: Nikita Zhandarovich -Date: Thu, 23 Oct 2025 16:22:04 +0300 -Subject: [PATCH 05/16] comedi: multiq3: sanitize config options in - multiq3_attach() - -Syzbot identified an issue [1] in multiq3_attach() that induces a -task timeout due to open() or COMEDI_DEVCONFIG ioctl operations, -specifically, in the case of multiq3 driver. - -This problem arose when syzkaller managed to craft weird configuration -options used to specify the number of channels in encoder subdevice. -If a particularly great number is passed to s->n_chan in -multiq3_attach() via it->options[2], then multiple calls to -multiq3_encoder_reset() at the end of driver-specific attach() method -will be running for minutes, thus blocking tasks and affected devices -as well. - -While this issue is most likely not too dangerous for real-life -devices, it still makes sense to sanitize configuration inputs. Enable -a sensible limit on the number of encoder chips (4 chips max, each -with 2 channels) to stop this behaviour from manifesting. - -[1] Syzbot crash: -INFO: task syz.2.19:6067 blocked for more than 143 seconds. -... -Call Trace: - - context_switch kernel/sched/core.c:5254 [inline] - __schedule+0x17c4/0x4d60 kernel/sched/core.c:6862 - __schedule_loop kernel/sched/core.c:6944 [inline] - schedule+0x165/0x360 kernel/sched/core.c:6959 - schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7016 - __mutex_lock_common kernel/locking/mutex.c:676 [inline] - __mutex_lock+0x7e6/0x1350 kernel/locking/mutex.c:760 - comedi_open+0xc0/0x590 drivers/comedi/comedi_fops.c:2868 - chrdev_open+0x4cc/0x5e0 fs/char_dev.c:414 - do_dentry_open+0x953/0x13f0 fs/open.c:965 - vfs_open+0x3b/0x340 fs/open.c:1097 -... - -Reported-by: syzbot+7811bb68a317954a0347@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=7811bb68a317954a0347 -Fixes: 77e01cdbad51 ("Staging: comedi: add multiq3 driver") -Cc: stable -Signed-off-by: Nikita Zhandarovich -Reviewed-by: Ian Abbott -Link: https://patch.msgid.link/20251023132205.395753-1-n.zhandarovich@fintech.ru -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/drivers/multiq3.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/comedi/drivers/multiq3.c b/drivers/comedi/drivers/multiq3.c -index 07ff5383da99..ac369e9a262d 100644 ---- a/drivers/comedi/drivers/multiq3.c -+++ b/drivers/comedi/drivers/multiq3.c -@@ -67,6 +67,11 @@ - #define MULTIQ3_TRSFRCNTR_OL 0x10 /* xfer CNTR to OL (x and y) */ - #define MULTIQ3_EFLAG_RESET 0x06 /* reset E bit of flag reg */ - -+/* -+ * Limit on the number of optional encoder channels -+ */ -+#define MULTIQ3_MAX_ENC_CHANS 8 -+ - static void multiq3_set_ctrl(struct comedi_device *dev, unsigned int bits) - { - /* -@@ -312,6 +317,10 @@ static int multiq3_attach(struct comedi_device *dev, - s->insn_read = multiq3_encoder_insn_read; - s->insn_config = multiq3_encoder_insn_config; - -+ /* sanity check for number of encoder channels */ -+ if (s->n_chan > MULTIQ3_MAX_ENC_CHANS) -+ s->n_chan = MULTIQ3_MAX_ENC_CHANS; -+ - for (i = 0; i < s->n_chan; i++) - multiq3_encoder_reset(dev, i); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-drm-i915-Drop-the-irqs_disabled-check.rt b/SPECS/kernel-rt/0005-drm-i915-Drop-the-irqs_disabled-check.rt new file mode 100644 index 000000000..491bdd1db --- /dev/null +++ b/SPECS/kernel-rt/0005-drm-i915-Drop-the-irqs_disabled-check.rt @@ -0,0 +1,45 @@ +From 19ac9d48ac2ac510b2c8cea7c5bc70e88ee9a261 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Fri, 1 Oct 2021 20:01:03 +0200 +Subject: [PATCH 5/9] drm/i915: Drop the irqs_disabled() check + +The !irqs_disabled() check triggers on PREEMPT_RT even with +i915_sched_engine::lock acquired. The reason is the lock is transformed +into a sleeping lock on PREEMPT_RT and does not disable interrupts. + +There is no need to check for disabled interrupts. The lockdep +annotation below already check if the lock has been acquired by the +caller and will yell if the interrupts are not disabled. + +Remove the !irqs_disabled() check. + +Reported-by: Maarten Lankhorst +Acked-by: Tvrtko Ursulin +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/i915_request.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c +index b9a2b2194c8ff..e24798e4b44ea 100644 +--- a/drivers/gpu/drm/i915/i915_request.c ++++ b/drivers/gpu/drm/i915/i915_request.c +@@ -608,7 +608,6 @@ bool __i915_request_submit(struct i915_request *request) + + RQ_TRACE(request, "\n"); + +- GEM_BUG_ON(!irqs_disabled()); + lockdep_assert_held(&engine->sched_engine->lock); + + /* +@@ -717,7 +716,6 @@ void __i915_request_unsubmit(struct i915_request *request) + */ + RQ_TRACE(request, "\n"); + +- GEM_BUG_ON(!irqs_disabled()); + lockdep_assert_held(&engine->sched_engine->lock); + + /* +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt b/SPECS/kernel-rt/0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt deleted file mode 100644 index 0235f1712..000000000 --- a/SPECS/kernel-rt/0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt +++ /dev/null @@ -1,94 +0,0 @@ -From 1d150a832087e70e0612d41bb918bfae6ff74955 Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Wed, 8 Sep 2021 19:03:41 +0200 -Subject: [PATCH 5/9] drm/i915/gt: Use spin_lock_irq() instead of - local_irq_disable() + spin_lock() - -execlists_dequeue() is invoked from a function which uses -local_irq_disable() to disable interrupts so the spin_lock() behaves -like spin_lock_irq(). -This breaks PREEMPT_RT because local_irq_disable() + spin_lock() is not -the same as spin_lock_irq(). - -execlists_dequeue_irq() and execlists_dequeue() has each one caller -only. If intel_engine_cs::active::lock is acquired and released with the -_irq suffix then it behaves almost as if execlists_dequeue() would be -invoked with disabled interrupts. The difference is the last part of the -function which is then invoked with enabled interrupts. -I can't tell if this makes a difference. From looking at it, it might -work to move the last unlock at the end of the function as I didn't find -anything that would acquire the lock again. - -Reported-by: Clark Williams -Signed-off-by: Sebastian Andrzej Siewior -Reviewed-by: Maarten Lankhorst ---- - .../drm/i915/gt/intel_execlists_submission.c | 17 +++++------------ - 1 file changed, 5 insertions(+), 12 deletions(-) - -diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c -index 03baa7fa0a27..799e943d9fac 100644 ---- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c -+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c -@@ -1294,7 +1294,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - * and context switches) submission. - */ - -- spin_lock(&sched_engine->lock); -+ spin_lock_irq(&sched_engine->lock); - - /* - * If the queue is higher priority than the last -@@ -1394,7 +1394,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - * Even if ELSP[1] is occupied and not worthy - * of timeslices, our queue might be. - */ -- spin_unlock(&sched_engine->lock); -+ spin_unlock_irq(&sched_engine->lock); - return; - } - } -@@ -1420,7 +1420,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - - if (last && !can_merge_rq(last, rq)) { - spin_unlock(&ve->base.sched_engine->lock); -- spin_unlock(&engine->sched_engine->lock); -+ spin_unlock_irq(&engine->sched_engine->lock); - return; /* leave this for another sibling */ - } - -@@ -1582,7 +1582,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - */ - sched_engine->queue_priority_hint = queue_prio(sched_engine); - i915_sched_engine_reset_on_empty(sched_engine); -- spin_unlock(&sched_engine->lock); -+ spin_unlock_irq(&sched_engine->lock); - - /* - * We can skip poking the HW if we ended up with exactly the same set -@@ -1608,13 +1608,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - } - } - --static void execlists_dequeue_irq(struct intel_engine_cs *engine) --{ -- local_irq_disable(); /* Suspend interrupts across request submission */ -- execlists_dequeue(engine); -- local_irq_enable(); /* flush irq_work (e.g. breadcrumb enabling) */ --} -- - static void clear_ports(struct i915_request **ports, int count) - { - memset_p((void **)ports, NULL, count); -@@ -2469,7 +2462,7 @@ static void execlists_submission_tasklet(struct tasklet_struct *t) - } - - if (!engine->execlists.pending[0]) { -- execlists_dequeue_irq(engine); -+ execlists_dequeue(engine); - start_timeslice(engine); - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c b/SPECS/kernel-rt/0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c deleted file mode 100644 index 769c09cb7..000000000 --- a/SPECS/kernel-rt/0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c +++ /dev/null @@ -1,48 +0,0 @@ -From 5813378b99d2bf90c5dc4502ccdaae64df478f12 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:30 +0300 -Subject: [PATCH 05/11] i3c: mipi-i3c-hci: Use own DMA bounce buffer management - for I2C transfers - -Stop using I2C DMA-safe API for two reasons: -- Not needed if driver is using PIO mode. -- DMA transfers needs a DWORD align sized receive bounce buffer when the - device DMA is IOMMU mapped, which is causing needless double bounce - buffering in that case. - -Cc: Billy Tsai -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-5-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 6 +----- - 1 file changed, 1 insertion(+), 5 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index b2977b6ac9f7..7a467ef65787 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -348,7 +348,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - return -ENOMEM; - - for (i = 0; i < nxfers; i++) { -- xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1); -+ xfer[i].data = i2c_xfers[i].buf; - xfer[i].data_len = i2c_xfers[i].len; - xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD; - hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]); -@@ -374,10 +374,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - } - - out: -- for (i = 0; i < nxfers; i++) -- i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i], -- ret ? false : true); -- - hci_free_xfer(xfer, nxfers); - return ret; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-ipu-bridge-add-CPHY-support.ipu b/SPECS/kernel-rt/0005-ipu-bridge-add-CPHY-support.ipu deleted file mode 100644 index 5395dfc32..000000000 --- a/SPECS/kernel-rt/0005-ipu-bridge-add-CPHY-support.ipu +++ /dev/null @@ -1,110 +0,0 @@ -From 9c7744f1489c6c1f4be1ac2b59ad08b059d633aa Mon Sep 17 00:00:00 2001 -From: Chen Meng J -Date: Wed, 20 Nov 2024 17:56:07 +0800 -Subject: [PATCH 05/11] ipu-bridge: add CPHY support - -get DPHY or CPHY mode when parse ssdb - -Signed-off-by: Chen Meng J -Signed-off-by: zouxiaoh ---- - drivers/media/pci/intel/ipu-bridge.c | 23 ++++++++++++++++++++++- - include/media/ipu-bridge.h | 7 +++++-- - 2 files changed, 27 insertions(+), 3 deletions(-) - -diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c -index e58b354ad9e6..3c947d1a487c 100644 ---- a/drivers/media/pci/intel/ipu-bridge.c -+++ b/drivers/media/pci/intel/ipu-bridge.c -@@ -35,6 +35,9 @@ - */ - #define IVSC_DEV_NAME "intel_vsc" - -+#define PHY_MODE_DPHY 0 -+#define PHY_MODE_CPHY 1 -+ - /* - * Extend this array with ACPI Hardware IDs of devices known to be working - * plus the number of link-frequencies expected by their drivers, along with -@@ -314,6 +317,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) - - sensor->link = ssdb.link; - sensor->lanes = ssdb.lanes; -+ sensor->phyconfig = ssdb.phyconfig; - sensor->mclkspeed = ssdb.mclkspeed; - sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); - sensor->orientation = ipu_bridge_parse_orientation(adev); -@@ -332,6 +336,7 @@ static void ipu_bridge_create_fwnode_properties( - { - struct ipu_property_names *names = &sensor->prop_names; - struct software_node *nodes = sensor->swnodes; -+ u8 bus_type; - - sensor->prop_names = prop_names; - -@@ -389,9 +394,16 @@ static void ipu_bridge_create_fwnode_properties( - PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); - } - -+ if (sensor->phyconfig == PHY_MODE_DPHY) -+ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; -+ else if (sensor->phyconfig == PHY_MODE_CPHY) -+ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; -+ else -+ bus_type = V4L2_FWNODE_BUS_TYPE_GUESS; -+ - sensor->ep_properties[0] = PROPERTY_ENTRY_U32( - sensor->prop_names.bus_type, -- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); -+ bus_type); - sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( - sensor->prop_names.data_lanes, - bridge->data_lanes, sensor->lanes); -@@ -411,6 +423,15 @@ static void ipu_bridge_create_fwnode_properties( - sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( - sensor->prop_names.remote_endpoint, - sensor->remote_ref); -+ -+ /* -+ * TODO: Remove the bus_type property for IPU -+ * 1. keep fwnode property list no change. -+ * 2. IPU driver needs to get bus_type from remote sensor ep. -+ */ -+ sensor->ipu_properties[2] = PROPERTY_ENTRY_U32 -+ (sensor->prop_names.bus_type, -+ bus_type); - } - - static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) -diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h -index 16fac765456e..f8642d09968d 100644 ---- a/include/media/ipu-bridge.h -+++ b/include/media/ipu-bridge.h -@@ -91,7 +91,9 @@ struct ipu_sensor_ssdb { - u8 controllogicid; - u8 reserved1[3]; - u8 mclkport; -- u8 reserved2[13]; -+ u8 reserved2[5]; -+ u8 phyconfig; -+ u8 reserved3[7]; - } __packed; - - struct ipu_property_names { -@@ -139,11 +141,12 @@ struct ipu_sensor { - u32 rotation; - enum v4l2_fwnode_orientation orientation; - const char *vcm_type; -+ u8 phyconfig; - - struct ipu_property_names prop_names; - struct property_entry ep_properties[5]; - struct property_entry dev_properties[5]; -- struct property_entry ipu_properties[3]; -+ struct property_entry ipu_properties[4]; - struct property_entry ivsc_properties[1]; - struct property_entry ivsc_sensor_ep_properties[4]; - struct property_entry ivsc_ipu_ep_properties[4]; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-issei-update-MAINTAINERS-file.security b/SPECS/kernel-rt/0005-issei-update-MAINTAINERS-file.security index e41c30d60..e58b2a59d 100644 --- a/SPECS/kernel-rt/0005-issei-update-MAINTAINERS-file.security +++ b/SPECS/kernel-rt/0005-issei-update-MAINTAINERS-file.security @@ -1,7 +1,7 @@ -From ed686509ba6d9731d3c7824f1745f05a6c8fd372 Mon Sep 17 00:00:00 2001 +From 61f2cb1a90329ae3ca5ee90a8f8695663bf6fed2 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 14 May 2025 11:31:24 +0300 -Subject: [PATCH 5/5] issei: update MAINTAINERS file +Subject: [PATCH 5/7] issei: update MAINTAINERS file Add ISSEI entry to the MAINTAINERS file. @@ -11,10 +11,10 @@ Signed-off-by: Alexander Usyskin 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS -index 97d958c945e4..37c00034d0a0 100644 +index e8f06145fb54c..0a36f929d6f4b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -12624,6 +12624,15 @@ F: drivers/platform/x86/intel/sdsi.c +@@ -12848,6 +12848,15 @@ F: drivers/platform/x86/intel/sdsi.c F: tools/arch/x86/intel_sdsi/ F: tools/testing/selftests/drivers/sdsi/ diff --git a/SPECS/kernel-rt/0005-media-mc-Add-INTERNAL-pad-flag.ipu b/SPECS/kernel-rt/0005-media-mc-Add-INTERNAL-pad-flag.ipu deleted file mode 100644 index ab1b6ce39..000000000 --- a/SPECS/kernel-rt/0005-media-mc-Add-INTERNAL-pad-flag.ipu +++ /dev/null @@ -1,107 +0,0 @@ -From 8ca39c83ce030a6bf6826b230b91408d5a74c9d1 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus -Date: Fri, 14 Nov 2025 16:51:41 +0200 -Subject: [PATCH 5/6] media: mc: Add INTERNAL pad flag - -Internal sink pads will be used as routing endpoints in V4L2 [GS]_ROUTING -IOCTLs, to indicate that the stream begins in the entity. Internal sink -pads are pads that have both SINK and INTERNAL flags set. - -Also prevent creating links to pads that have been flagged as internal and -initialising SOURCE pads with INTERNAL flag set. - -Signed-off-by: Sakari Ailus -Reviewed-by: Tomi Valkeinen -Reviewed-by: Laurent Pinchart -Reviewed-by: Mirela Rabulea ---- - .../userspace-api/media/mediactl/media-types.rst | 9 +++++++++ - drivers/media/mc/mc-entity.c | 15 ++++++++++++--- - include/uapi/linux/media.h | 1 + - 3 files changed, 22 insertions(+), 3 deletions(-) - -diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst -index 6332e8395263..200c37a1da26 100644 ---- a/Documentation/userspace-api/media/mediactl/media-types.rst -+++ b/Documentation/userspace-api/media/mediactl/media-types.rst -@@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements - .. _MEDIA-PAD-FL-SINK: - .. _MEDIA-PAD-FL-SOURCE: - .. _MEDIA-PAD-FL-MUST-CONNECT: -+.. _MEDIA-PAD-FL-INTERNAL: - - .. flat-table:: Media pad flags - :header-rows: 0 -@@ -381,6 +382,14 @@ Types and flags used to represent the media graph elements - enabled links even when this flag isn't set; the absence of the flag - doesn't imply there is none. - -+ * - ``MEDIA_PAD_FL_INTERNAL`` -+ - The internal flag indicates an internal pad that has no external -+ connections. As they are internal to entities, internal pads shall not -+ be connected with links. -+ -+ The internal flag may currently be present only in a sink pad where it -+ indicates that the :ref:``stream `` originates -+ from within the entity. - - One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE`` - must be set for every pad. -diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c -index 307920c8b354..928613d60e8f 100644 ---- a/drivers/media/mc/mc-entity.c -+++ b/drivers/media/mc/mc-entity.c -@@ -209,11 +209,16 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, - mutex_lock(&mdev->graph_mutex); - - media_entity_for_each_pad(entity, iter) { -+ const u32 pad_flags = iter->flags & (MEDIA_PAD_FL_SINK | -+ MEDIA_PAD_FL_SOURCE | -+ MEDIA_PAD_FL_INTERNAL); -+ - iter->entity = entity; - iter->index = i++; - -- if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK | -- MEDIA_PAD_FL_SOURCE)) != 1) { -+ if (pad_flags != MEDIA_PAD_FL_SINK && -+ pad_flags != (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL) && -+ pad_flags != MEDIA_PAD_FL_SOURCE) { - ret = -EINVAL; - break; - } -@@ -1118,7 +1123,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type, - - for (i = 0; i < entity->num_pads; i++) { - if ((entity->pads[i].flags & -- (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) -+ (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE | -+ MEDIA_PAD_FL_INTERNAL)) != pad_type) - continue; - - if (entity->pads[i].sig_type == sig_type) -@@ -1148,6 +1154,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, - return -EINVAL; - if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) - return -EINVAL; -+ if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL) || -+ WARN_ON(sink->pads[sink_pad].flags & MEDIA_PAD_FL_INTERNAL)) -+ return -EINVAL; - - link = media_add_link(&source->links); - if (link == NULL) -diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h -index 1c80b1d6bbaf..80cfd12a43fc 100644 ---- a/include/uapi/linux/media.h -+++ b/include/uapi/linux/media.h -@@ -208,6 +208,7 @@ struct media_entity_desc { - #define MEDIA_PAD_FL_SINK (1U << 0) - #define MEDIA_PAD_FL_SOURCE (1U << 1) - #define MEDIA_PAD_FL_MUST_CONNECT (1U << 2) -+#define MEDIA_PAD_FL_INTERNAL (1U << 3) - - struct media_pad_desc { - __u32 entity; /* entity ID */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet b/SPECS/kernel-rt/0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet new file mode 100644 index 000000000..8586713f4 --- /dev/null +++ b/SPECS/kernel-rt/0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet @@ -0,0 +1,54 @@ +From 222770f6becdaf5dac70947d371aa235313f563d Mon Sep 17 00:00:00 2001 +From: Gan Yi Fang +Date: Mon, 21 Nov 2022 01:28:57 -0500 +Subject: [PATCH 05/18] net: phy: dp83867: perform restart AN after modifying + AN setting + +When changing link speed, the MAC side is not ready when the PHY +side is up. This is causing the configuration is not updated at +the MAC side. By restarting the auto negotiation, MAC side is able +to update the in-band message. + +Fixes: 50ca4e7f91ff ("net: phy: dp83867: retrigger SGMII AN when link change") +Signed-off-by: Gan Yi Fang +--- + drivers/net/phy/Kconfig | 1 + + drivers/net/phy/dp83867.c | 3 +++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 98700d069191a..075bc9a5fe9dc 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -428,6 +428,7 @@ config DP83848_PHY + + config DP83867_PHY + tristate "Texas Instruments DP83867 Gigabit PHY" ++ depends on PHYLINK + help + Currently supports the DP83867 PHY. + +diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c +index 36a0c1b7f59c7..324e73b4eda45 100644 +--- a/drivers/net/phy/dp83867.c ++++ b/drivers/net/phy/dp83867.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include + +@@ -947,6 +948,8 @@ static void dp83867_link_change_notify(struct phy_device *phydev) + phy_set_bits(phydev, DP83867_CFG2, + DP83867_SGMII_AUTONEG_EN); + } ++ if (phydev->state == PHY_NOLINK) ++ phylink_mii_c22_pcs_an_restart(&phydev->mdio); + } + + static int dp83867_loopback(struct phy_device *phydev, bool enable, int speed) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu b/SPECS/kernel-rt/0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu deleted file mode 100644 index afd14a3ac..000000000 --- a/SPECS/kernel-rt/0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu +++ /dev/null @@ -1,404 +0,0 @@ -From abb1539a9573373c7d63922be8fcb4c34dd45a34 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:16:13 +0800 -Subject: [PATCH 05/21] patch: staging add enable CONFIG_INTEL_IPU_ACPI - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 231 ++++++++++++++++++++++++- - drivers/staging/media/ipu7/ipu7-isys.h | 15 ++ - drivers/staging/media/ipu7/ipu7.c | 10 ++ - drivers/staging/media/ipu7/ipu7.h | 3 + - 4 files changed, 257 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index 44ea4eef1976..547d5fd6a036 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -96,6 +96,126 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, - return ret; - } - -+struct isys_i2c_test { -+ u8 bus_nr; -+ u16 addr; -+ struct i2c_client *client; -+}; -+ -+static int isys_i2c_test(struct device *dev, void *priv) -+{ -+ struct i2c_client *client = i2c_verify_client(dev); -+ struct isys_i2c_test *test = priv; -+ -+ if (!client) -+ return 0; -+ -+ if (i2c_adapter_id(client->adapter) != test->bus_nr || -+ client->addr != test->addr) -+ return 0; -+ -+ test->client = client; -+ -+ return 0; -+} -+ -+static -+struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct i2c_board_info *info = &sd_info->i2c.board_info; -+ struct isys_i2c_test test = { -+ .bus_nr = i2c_adapter_id(adapter), -+ .addr = info->addr, -+ }; -+ int ret; -+ -+ ret = i2c_for_each_dev(&test, isys_i2c_test); -+ if (ret || !test.client) -+ return NULL; -+ return test.client; -+} -+ -+static int isys_register_ext_subdev(struct ipu7_isys *isys, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct i2c_adapter *adapter; -+ struct v4l2_subdev *sd; -+ struct i2c_client *client; -+ int ret; -+ int bus; -+ -+ bus = sd_info->i2c.i2c_adapter_id; -+ adapter = i2c_get_adapter(bus); -+ if (!adapter) { -+ dev_warn(dev, "can't find adapter\n"); -+ return -ENOENT; -+ } -+ -+ dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -+ sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -+ bus); -+ -+ if (sd_info->csi2) { -+ dev_info(dev, "sensor device on CSI port: %d\n", -+ sd_info->csi2->port); -+ if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -+ !isys->csi2[sd_info->csi2->port].isys) { -+ dev_warn(dev, "invalid csi2 port %u\n", -+ sd_info->csi2->port); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ } else { -+ dev_info(dev, "No camera subdevice\n"); -+ } -+ -+ client = isys_find_i2c_subdev(adapter, sd_info); -+ if (client) { -+ dev_warn(dev, "Device exists\n"); -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ /* TODO: remove i2c_unregister_device() */ -+ i2c_unregister_device(client); -+#else -+ ret = 0; -+ goto skip_put_adapter; -+#endif -+ } -+ -+ sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -+ &sd_info->i2c.board_info, NULL); -+ if (!sd) { -+ dev_warn(dev, "can't create new i2c subdev\n"); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ -+ if (!sd_info->csi2) -+ return 0; -+ -+ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -+ -+skip_put_adapter: -+ i2c_put_adapter(adapter); -+ -+ return ret; -+} -+ -+static void isys_register_ext_subdevs(struct ipu7_isys *isys) -+{ -+ struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -+ struct ipu7_isys_subdev_info **sd_info; -+ -+ if (!spdata) { -+ dev_info(&isys->adev->auxdev.dev, -+ "no subdevice info provided\n"); -+ return; -+ } -+ for (sd_info = spdata->subdevs; *sd_info; sd_info++) -+ isys_register_ext_subdev(isys, *sd_info); -+} -+ - static void isys_stream_init(struct ipu7_isys *isys) - { - unsigned int i; -@@ -142,6 +262,7 @@ static int isys_fw_log_init(struct ipu7_isys *isys) - return 0; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) - /* The .bound() notifier callback when a match is found */ - static int isys_notifier_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, -@@ -260,6 +381,7 @@ static void isys_notifier_cleanup(struct ipu7_isys *isys) - v4l2_async_nf_unregister(&isys->notifier); - v4l2_async_nf_cleanup(&isys->notifier); - } -+#endif - - static void isys_unregister_video_devices(struct ipu7_isys *isys) - { -@@ -374,6 +496,73 @@ static int isys_csi2_create_media_links(struct ipu7_isys *isys) - return 0; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+static int isys_register_devices(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_video_unregister_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ -+ if (!isys->pdata->spdata) { -+ ret = isys_notifier_init(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } else { -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } -+ -+ return 0; -+ -+out_csi2_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_video_unregister_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+#else - static int isys_register_devices(struct ipu7_isys *isys) - { - struct device *dev = &isys->adev->auxdev.dev; -@@ -409,7 +598,8 @@ static int isys_register_devices(struct ipu7_isys *isys) - if (ret) - goto out_csi2_unregister_subdevices; - -- ret = isys_notifier_init(isys); -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); - if (ret) - goto out_csi2_unregister_subdevices; - -@@ -432,6 +622,7 @@ static int isys_register_devices(struct ipu7_isys *isys) - - return ret; - } -+#endif - - static void isys_unregister_devices(struct ipu7_isys *isys) - { -@@ -579,6 +770,7 @@ static const struct dev_pm_ops isys_pm_ops = { - .resume = isys_resume, - }; - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) - static void isys_remove(struct auxiliary_device *auxdev) - { - struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -@@ -600,7 +792,9 @@ static void isys_remove(struct auxiliary_device *auxdev) - ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - -- isys_notifier_cleanup(isys); -+ if (!isys->pdata->spdata) -+ isys_notifier_cleanup(isys); -+ - isys_unregister_devices(isys); - - cpu_latency_qos_remove_request(&isys->pm_qos); -@@ -611,6 +805,39 @@ static void isys_remove(struct auxiliary_device *auxdev) - mutex_destroy(&isys->reset_mutex); - #endif - } -+#else -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct isys_fw_msgs *fwmsg, *safe; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ isys_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#endif - - #ifdef CONFIG_DEBUG_FS - static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index cd7375417452..2e45258bb6ab 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -150,6 +150,21 @@ struct ipu7_isys_csi2_config { - enum v4l2_mbus_type bus_type; - }; - -+struct ipu7_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu7_isys_subdev_info { -+ struct ipu7_isys_csi2_config *csi2; -+ struct ipu7_isys_subdev_i2c_info i2c; -+}; -+ -+struct ipu7_isys_subdev_pdata { -+ struct ipu7_isys_subdev_info **subdevs; -+}; -+ - struct sensor_async_sd { - struct v4l2_async_connection asc; - struct ipu7_isys_csi2_config csi2; -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 5649d8e1c352..06e5f08bed4d 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -40,6 +40,10 @@ - #include "ipu7-mmu.h" - #include "ipu7-platform-regs.h" - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+#include -+ -+#endif - #define IPU_PCI_BAR 0 - #define IPU_PCI_PBBAR 4 - -@@ -2130,6 +2134,7 @@ static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) - static struct ipu7_bus_device * - ipu7_isys_init(struct pci_dev *pdev, struct device *parent, - const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ struct ipu7_isys_subdev_pdata *spdata, - const struct ipu_isys_internal_pdata *ipdata, - unsigned int nr) - { -@@ -2160,6 +2165,7 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, - - pdata->base = base; - pdata->ipdata = ipdata; -+ pdata->spdata = spdata; - - isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, - IPU_ISYS_NAME); -@@ -2582,7 +2588,11 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - goto out_ipu_bus_del_devices; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ ipu_get_acpi_devices_new(&dev->platform_data); -+#endif - isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -+ dev->platform_data, - isys_ipdata, 0); - if (IS_ERR(isp->isys)) { - ret = PTR_ERR(isp->isys); -diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h -index 3b3ea5fb613f..21988ce41acf 100644 ---- a/drivers/staging/media/ipu7/ipu7.h -+++ b/drivers/staging/media/ipu7/ipu7.h -@@ -144,6 +144,8 @@ struct ipu7_device { - /* Currently chosen arbitration mechanism for VC1 */ - #define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB - -+struct ipu7_isys_subdev_pdata; -+ - /* One L2 entry maps 1024 L1 entries and one L1 entry per page */ - #define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) - /* Max L2 blocks per stream */ -@@ -226,6 +228,7 @@ struct ipu_isys_internal_pdata { - struct ipu7_isys_pdata { - void __iomem *base; - const struct ipu_isys_internal_pdata *ipdata; -+ struct ipu7_isys_subdev_pdata *spdata; - }; - - struct ipu_psys_internal_pdata { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf b/SPECS/kernel-rt/0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf deleted file mode 100644 index 8631728c5..000000000 --- a/SPECS/kernel-rt/0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf +++ /dev/null @@ -1,129 +0,0 @@ -From c945e3b2bac5e06b93d5b331fd99a20a0e22a595 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 11 Aug 2025 17:00:31 +0800 -Subject: [PATCH 005/100] perf/x86: Check if cpuc->events[*] pointer exists - before accessing it - -The PMI handler could disable some events as the interrupt throttling -and clear the corresponding items in cpuc->events[] array. - -perf_event_overflow() - -> __perf_event_overflow() - ->__perf_event_account_interrupt() - -> perf_event_throttle_group() - -> perf_event_throttle() - -> event->pmu->stop() - -> x86_pmu_stop() - -Moreover PMI is NMI on x86 platform and it could interrupt other perf -code like setup_pebs_adaptive_sample_data(). So once PMI handling -finishes and returns into setup_pebs_adaptive_sample_data() and it could -find the cpuc->events[*] becomes NULL and accessing this NULL pointer -triggers an invalid memory access and leads to kernel crashes eventually. - -Thus add NULL check before accessing cpuc->events[*] pointer. - -Reported-by: kernel test robot -Closes: https://lore.kernel.org/oe-lkp/202507042103.a15d2923-lkp@intel.com -Fixes: 9734e25fbf5a ("perf: Fix the throttle logic for a group") -Signed-off-by: Dapeng Mi -Tested-by: kernel test robot ---- - arch/x86/events/core.c | 3 +++ - arch/x86/events/intel/core.c | 6 +++++- - arch/x86/events/intel/ds.c | 13 ++++++------- - 3 files changed, 14 insertions(+), 8 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 7610f26dfbd9..f0a3bc57157d 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1711,6 +1711,9 @@ int x86_pmu_handle_irq(struct pt_regs *regs) - continue; - - event = cpuc->events[idx]; -+ if (!event) -+ continue; -+ - last_period = event->hw.last_period; - - val = static_call(x86_pmu_update)(event); -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index b08896e06f72..602a7c33f711 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -2718,6 +2718,8 @@ static void update_saved_topdown_regs(struct perf_event *event, u64 slots, - if (!is_topdown_idx(idx)) - continue; - other = cpuc->events[idx]; -+ if (!other) -+ continue; - other->hw.saved_slots = slots; - other->hw.saved_metric = metrics; - } -@@ -2761,6 +2763,8 @@ static u64 intel_update_topdown_event(struct perf_event *event, int metric_end, - if (!is_topdown_idx(idx)) - continue; - other = cpuc->events[idx]; -+ if (!other) -+ continue; - __icl_update_topdown_event(other, slots, metrics, - event ? event->hw.saved_slots : 0, - event ? event->hw.saved_metric : 0); -@@ -3138,7 +3142,7 @@ static void x86_pmu_handle_guest_pebs(struct pt_regs *regs, - - for_each_set_bit(bit, (unsigned long *)&guest_pebs_idxs, X86_PMC_IDX_MAX) { - event = cpuc->events[bit]; -- if (!event->attr.precise_ip) -+ if (!event || !event->attr.precise_ip) - continue; - - perf_sample_data_init(data, 0, event->hw.last_period); -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index c0b7ac1c7594..b23c49e2e06f 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2480,6 +2480,8 @@ static void intel_pmu_pebs_event_update_no_drain(struct cpu_hw_events *cpuc, u64 - */ - for_each_set_bit(bit, (unsigned long *)&pebs_enabled, X86_PMC_IDX_MAX) { - event = cpuc->events[bit]; -+ if (!event) -+ continue; - if (event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD) - intel_pmu_save_and_restart_reload(event, 0); - } -@@ -2579,10 +2581,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d - continue; - - event = cpuc->events[bit]; -- if (WARN_ON_ONCE(!event)) -- continue; -- -- if (WARN_ON_ONCE(!event->attr.precise_ip)) -+ if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) - continue; - - /* log dropped samples number */ -@@ -2645,9 +2644,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - pebs_status = basic->applicable_counters & cpuc->pebs_enabled & mask; - for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) { - event = cpuc->events[bit]; -- -- if (WARN_ON_ONCE(!event) || -- WARN_ON_ONCE(!event->attr.precise_ip)) -+ if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) - continue; - - if (counts[bit]++) { -@@ -2663,6 +2660,8 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - continue; - - event = cpuc->events[bit]; -+ if (!event) -+ continue; - - __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit], - counts[bit], setup_pebs_adaptive_sample_data); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf b/SPECS/kernel-rt/0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf new file mode 100644 index 000000000..b01d55fd2 --- /dev/null +++ b/SPECS/kernel-rt/0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf @@ -0,0 +1,349 @@ +From 11a27e519981f2409c3efc10f085f0b2dad547dc Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Tue, 12 Aug 2025 12:12:08 -0700 +Subject: [PATCH 05/13] perf/x86/intel/uncore: Add IMH PMON support for Diamond + Rapids + +DMR supports IMH PMON units for PCU, UBox, iMC, and CXL: +- PCU and UBox are same with SPR. +- iMC is similar to SPR but uses different offsets for fixed registers. +- CXL introduces a new port_enable field and changes the position of + the threshold field. + +DMR also introduces additional PMON units: SCA, HAMVF, D2D_ULA, UBR, +PCIE4, CRS, CPC, ITC, OTC, CMS, and PCIE6. Among these, PCIE4 and +PCIE6 use different unit types, but share the same config register +layout, and the generic PCIe PMON events apply to both. + +Additionally, ignore the broken MSE unit. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 9 + + arch/x86/events/intel/uncore.h | 3 + + arch/x86/events/intel/uncore_discovery.h | 2 + + arch/x86/events/intel/uncore_snbep.c | 229 +++++++++++++++++++++++ + 4 files changed, 243 insertions(+) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index 5135a39d81f6b..c07d2c88604ef 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1831,6 +1831,14 @@ static const struct uncore_plat_init gnr_uncore_init __initconst = { + .domain[0].units_ignore = gnr_uncore_units_ignore, + }; + ++static const struct uncore_plat_init dmr_uncore_init __initconst = { ++ .pci_init = dmr_uncore_pci_init, ++ .mmio_init = dmr_uncore_mmio_init, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = DMR_UNCORE_DISCOVERY_TABLE_DEVICE, ++ .domain[0].units_ignore = dmr_uncore_imh_units_ignore, ++}; ++ + static const struct uncore_plat_init generic_uncore_init __initconst = { + .cpu_init = intel_uncore_generic_uncore_cpu_init, + .pci_init = intel_uncore_generic_uncore_pci_init, +@@ -1898,6 +1906,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = { + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &gnr_uncore_init), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &gnr_uncore_init), + X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &gnr_uncore_init), ++ X86_MATCH_VFM(INTEL_DIAMONDRAPIDS_X, &dmr_uncore_init), + {}, + }; + MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match); +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 1574ffc7ee053..1e4b3a22403c5 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -614,6 +614,7 @@ extern struct pci_extra_dev *uncore_extra_pci_dev; + extern struct event_constraint uncore_constraint_empty; + extern int spr_uncore_units_ignore[]; + extern int gnr_uncore_units_ignore[]; ++extern int dmr_uncore_imh_units_ignore[]; + + /* uncore_snb.c */ + int snb_uncore_pci_init(void); +@@ -662,6 +663,8 @@ void spr_uncore_mmio_init(void); + int gnr_uncore_pci_init(void); + void gnr_uncore_cpu_init(void); + void gnr_uncore_mmio_init(void); ++int dmr_uncore_pci_init(void); ++void dmr_uncore_mmio_init(void); + + /* uncore_nhmex.c */ + void nhmex_uncore_cpu_init(void); +diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h +index dfc237a2b6dfc..618788c30ac62 100644 +--- a/arch/x86/events/intel/uncore_discovery.h ++++ b/arch/x86/events/intel/uncore_discovery.h +@@ -5,6 +5,8 @@ + + /* Generic device ID of a discovery table device */ + #define UNCORE_DISCOVERY_TABLE_DEVICE 0x09a7 ++/* Device ID used on DMR */ ++#define DMR_UNCORE_DISCOVERY_TABLE_DEVICE 0x09a1 + /* Capability ID for a discovery table device */ + #define UNCORE_EXT_CAP_ID_DISCOVERY 0x23 + /* First DVSEC offset */ +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index e1f370b8d065f..4b72560dc13ff 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -471,6 +471,14 @@ + + #define SPR_C0_MSR_PMON_BOX_FILTER0 0x200e + ++/* DMR */ ++#define DMR_CXLCM_EVENT_MASK_EXT 0xf ++#define DMR_HAMVF_EVENT_MASK_EXT 0xffffffff ++#define DMR_PCIE4_EVENT_MASK_EXT 0xffffff ++ ++#define DMR_IMC_PMON_FIXED_CTR 0x18 ++#define DMR_IMC_PMON_FIXED_CTL 0x10 ++ + DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); + DEFINE_UNCORE_FORMAT_ATTR(event2, event, "config:0-6"); + DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21"); +@@ -486,6 +494,10 @@ DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18"); + DEFINE_UNCORE_FORMAT_ATTR(tid_en, tid_en, "config:19"); + DEFINE_UNCORE_FORMAT_ATTR(tid_en2, tid_en, "config:16"); + DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23"); ++DEFINE_UNCORE_FORMAT_ATTR(inv2, inv, "config:21"); ++DEFINE_UNCORE_FORMAT_ATTR(thresh_ext, thresh_ext, "config:32-35"); ++DEFINE_UNCORE_FORMAT_ATTR(thresh10, thresh, "config:23-32"); ++DEFINE_UNCORE_FORMAT_ATTR(thresh9_2, thresh, "config:23-31"); + DEFINE_UNCORE_FORMAT_ATTR(thresh9, thresh, "config:24-35"); + DEFINE_UNCORE_FORMAT_ATTR(thresh8, thresh, "config:24-31"); + DEFINE_UNCORE_FORMAT_ATTR(thresh6, thresh, "config:24-29"); +@@ -494,6 +506,13 @@ DEFINE_UNCORE_FORMAT_ATTR(occ_sel, occ_sel, "config:14-15"); + DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30"); + DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51"); + DEFINE_UNCORE_FORMAT_ATTR(occ_edge_det, occ_edge_det, "config:31"); ++DEFINE_UNCORE_FORMAT_ATTR(port_en, port_en, "config:32-35"); ++DEFINE_UNCORE_FORMAT_ATTR(rs3_sel, rs3_sel, "config:36"); ++DEFINE_UNCORE_FORMAT_ATTR(rx_sel, rx_sel, "config:37"); ++DEFINE_UNCORE_FORMAT_ATTR(tx_sel, tx_sel, "config:38"); ++DEFINE_UNCORE_FORMAT_ATTR(iep_sel, iep_sel, "config:39"); ++DEFINE_UNCORE_FORMAT_ATTR(vc_sel, vc_sel, "config:40-47"); ++DEFINE_UNCORE_FORMAT_ATTR(port_sel, port_sel, "config:48-55"); + DEFINE_UNCORE_FORMAT_ATTR(ch_mask, ch_mask, "config:36-43"); + DEFINE_UNCORE_FORMAT_ATTR(ch_mask2, ch_mask, "config:36-47"); + DEFINE_UNCORE_FORMAT_ATTR(fc_mask, fc_mask, "config:44-46"); +@@ -6709,3 +6728,213 @@ void gnr_uncore_mmio_init(void) + } + + /* end of GNR uncore support */ ++ ++/* DMR uncore support */ ++#define UNCORE_DMR_NUM_UNCORE_TYPES 52 ++ ++static struct attribute *dmr_imc_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv.attr, ++ &format_attr_thresh10.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_imc_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_imc_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_imc = { ++ .name = "imc", ++ .fixed_ctr_bits = 48, ++ .fixed_ctr = DMR_IMC_PMON_FIXED_CTR, ++ .fixed_ctl = DMR_IMC_PMON_FIXED_CTL, ++ .ops = &spr_uncore_mmio_ops, ++ .format_group = &dmr_imc_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct attribute *dmr_sca_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask_ext5.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv.attr, ++ &format_attr_thresh8.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_sca_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_sca_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_sca = { ++ .name = "sca", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct attribute *dmr_cxlcm_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv2.attr, ++ &format_attr_thresh9_2.attr, ++ &format_attr_port_en.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_cxlcm_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_cxlcm_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cxlcm = { ++ .name = "cxlcm", ++ .event_mask = GENERIC_PMON_RAW_EVENT_MASK, ++ .event_mask_ext = DMR_CXLCM_EVENT_MASK_EXT, ++ .format_group = &dmr_cxlcm_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_hamvf = { ++ .name = "hamvf", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_ula = { ++ .name = "ula", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_ubr = { ++ .name = "ubr", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct attribute *dmr_pcie4_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv.attr, ++ &format_attr_thresh8.attr, ++ &format_attr_thresh_ext.attr, ++ &format_attr_rs3_sel.attr, ++ &format_attr_rx_sel.attr, ++ &format_attr_tx_sel.attr, ++ &format_attr_iep_sel.attr, ++ &format_attr_vc_sel.attr, ++ &format_attr_port_sel.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_pcie4_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_pcie4_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_pcie4 = { ++ .name = "pcie4", ++ .event_mask_ext = DMR_PCIE4_EVENT_MASK_EXT, ++ .format_group = &dmr_pcie4_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_crs = { ++ .name = "crs", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cpc = { ++ .name = "cpc", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_itc = { ++ .name = "itc", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_otc = { ++ .name = "otc", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cms = { ++ .name = "cms", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_pcie6 = { ++ .name = "pcie6", ++ .event_mask_ext = DMR_PCIE4_EVENT_MASK_EXT, ++ .format_group = &dmr_pcie4_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type *dmr_uncores[UNCORE_DMR_NUM_UNCORE_TYPES] = { ++ NULL, NULL, NULL, NULL, ++ &spr_uncore_pcu, ++ &gnr_uncore_ubox, ++ &dmr_uncore_imc, ++ NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, ++ &dmr_uncore_sca, ++ &dmr_uncore_cxlcm, ++ NULL, NULL, NULL, ++ NULL, NULL, ++ &dmr_uncore_hamvf, ++ NULL, ++ NULL, NULL, NULL, ++ &dmr_uncore_ula, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, ++ &dmr_uncore_ubr, ++ NULL, ++ &dmr_uncore_pcie4, ++ &dmr_uncore_crs, ++ &dmr_uncore_cpc, ++ &dmr_uncore_itc, ++ &dmr_uncore_otc, ++ &dmr_uncore_cms, ++ &dmr_uncore_pcie6, ++}; ++ ++int dmr_uncore_imh_units_ignore[] = { ++ 0x13, /* MSE */ ++ UNCORE_IGNORE_END ++}; ++ ++int dmr_uncore_pci_init(void) ++{ ++ uncore_pci_uncores = uncore_get_uncores(UNCORE_ACCESS_PCI, 0, NULL, ++ UNCORE_DMR_NUM_UNCORE_TYPES, ++ dmr_uncores); ++ return 0; ++} ++void dmr_uncore_mmio_init(void) ++{ ++ uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, 0, NULL, ++ UNCORE_DMR_NUM_UNCORE_TYPES, ++ dmr_uncores); ++} ++ ++/* end of DMR uncore support */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt b/SPECS/kernel-rt/0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt new file mode 100644 index 000000000..b06a1591b --- /dev/null +++ b/SPECS/kernel-rt/0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt @@ -0,0 +1,59 @@ +From 30c1cb566d0e0ca91a48cbef72e9d31be27a12a8 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:34 -0700 +Subject: [PATCH 5/6] platform/x86:intel/pmc: Remove redundant has_die_c6 + variable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove has_die_c6 variable from the pmc_dev struct. This variable +is unnecessary as the availability of die C6 could be inferred by +the punit_ep variable. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-7-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/core.c | 4 +--- + drivers/platform/x86/intel/pmc/core.h | 1 - + 2 files changed, 1 insertion(+), 4 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index e272c971d73a5..7d7ae8a40b0ec 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -1316,8 +1316,6 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) + } + + pmcdev->punit_ep = ep; +- +- pmcdev->has_die_c6 = true; + pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; + } + +@@ -1437,7 +1435,7 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info + pmcdev->dbgfs_dir, primary_pmc, &pmc_core_pson_residency); + } + +- if (pmcdev->has_die_c6) { ++ if (pmcdev->punit_ep) { + debugfs_create_file("die_c6_us_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_die_c6_us_fops); +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index cccd3bcafe00d..61c8d3c5faa0f 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -468,7 +468,6 @@ struct pmc_dev { + u64 *pkgc_res_cnt; + u8 num_of_pkgc; + +- bool has_die_c6; + u32 die_c6_offset; + struct telem_endpoint *punit_ep; + struct pmc_info *regmap_list; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-rtnetlink-Add-return-value-check.ethernet b/SPECS/kernel-rt/0005-rtnetlink-Add-return-value-check.ethernet new file mode 100644 index 000000000..dc4816e2f --- /dev/null +++ b/SPECS/kernel-rt/0005-rtnetlink-Add-return-value-check.ethernet @@ -0,0 +1,36 @@ +From 2946c5c1c060d0fb4fe339f304477df6335035ac Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Tue, 3 Aug 2021 08:49:34 +0800 +Subject: [PATCH 05/14] rtnetlink: Add return value check + +This patch add return value checking for both of the nla_put_u32() and +nla_put_u8() in rtnl_xdp_fill(). + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + net/core/rtnetlink.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index b4ac5dedaed0a..5e2da0bcffb95 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1773,8 +1773,12 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + if (!md_btf_id) + goto err_cancel; + +- nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); +- nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); ++ err = nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); ++ if (err) ++ goto err_cancel; ++ err = nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); ++ if (err) ++ goto err_cancel; + + nla_nest_end(skb, xdp); + return 0; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet b/SPECS/kernel-rt/0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet deleted file mode 100644 index fa0cca452..000000000 --- a/SPECS/kernel-rt/0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet +++ /dev/null @@ -1,32 +0,0 @@ -From c274317949704de49a2f7c002fc461be620f5e73 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Fri, 30 Jul 2021 08:33:53 +0800 -Subject: [PATCH 05/19] rtnetlink: Fix unchecked return value of - dev_xdp_query_md_btf() - -This patch is to check the return value of dev_xdp_query_md_btf() -whether it contain a valid btf id or vice versa. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - net/core/rtnetlink.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -index aa18bd5b8c63..e4716ed7643b 100644 ---- a/net/core/rtnetlink.c -+++ b/net/core/rtnetlink.c -@@ -1768,6 +1768,9 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - } - - md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); -+ if (!md_btf_id) -+ goto err_cancel; -+ - nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); - nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss b/SPECS/kernel-rt/0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss deleted file mode 100644 index 00f7f1d9d..000000000 --- a/SPECS/kernel-rt/0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss +++ /dev/null @@ -1,29 +0,0 @@ -From a11716020c1ebcccaf2cba042eec1a7f3366ee0e Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Wed, 25 Oct 2023 11:30:29 +0300 -Subject: [PATCH 05/16] spi: intel-pci: Add support for Arrow Lake-H SPI serial - flash - -Add Intel Arrow Lake-H PCI ID to the driver list of supported devices. -This is the same controller found in previous generations. - -Signed-off-by: Mika Westerberg ---- - drivers/spi/spi-intel-pci.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c -index 4b63cb98df9c..49b4d3061197 100644 ---- a/drivers/spi/spi-intel-pci.c -+++ b/drivers/spi/spi-intel-pci.c -@@ -79,6 +79,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x5794), (unsigned long)&cnl_info }, -+ { PCI_VDEVICE(INTEL, 0x7723), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info }, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal b/SPECS/kernel-rt/0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal deleted file mode 100644 index c24a73c31..000000000 --- a/SPECS/kernel-rt/0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal +++ /dev/null @@ -1,102 +0,0 @@ -From b49702e5fcfff00a428b019b48b97020d6cd3555 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:15 -0700 -Subject: [PATCH 05/11] thermal: intel: int340x: Add module parameter to change - slider offset - -SoC slider value is set by the user (or the default when user has not -modified it). To enhance power efficiency dynamically, the firmware can -optionally auto-adjust the slider value based on the current workload. -This adjustment is governed by an additional parameter known as the -"slider offset". This offset permits the firmware to increase the slider -value up to and including "SoC slider + slider offset". - -Add a module parameter to specify this "slier offset" value. - -By default, the SoC slider offset is set to 0. This means that SoC is not -allowed to switch slider position. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-5-srinivas.pandruvada@linux.intel.com -[ rjw: Comment and module param description adjustments ] -Signed-off-by: Rafael J. Wysocki ---- - .../processor_thermal_soc_slider.c | 48 +++++++++++++++++++ - 1 file changed, 48 insertions(+) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -index ffbad6b4326e..20d70cb01542 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -90,6 +90,47 @@ static const struct kernel_param_ops slider_def_balance_ops = { - module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644); - MODULE_PARM_DESC(slider_balance, "Set slider default value for balance"); - -+static u8 slider_offset; -+ -+static int slider_def_offset_set(const char *arg, const struct kernel_param *kp) -+{ -+ u8 offset; -+ int ret; -+ -+ guard(mutex)(&slider_param_lock); -+ -+ ret = kstrtou8(arg, 16, &offset); -+ if (!ret) { -+ if (offset > SOC_SLIDER_VALUE_MAXIMUM) -+ return -EINVAL; -+ -+ slider_offset = offset; -+ } -+ -+ return ret; -+} -+ -+static int slider_def_offset_get(char *buf, const struct kernel_param *kp) -+{ -+ guard(mutex)(&slider_param_lock); -+ return sysfs_emit(buf, "%02x\n", slider_offset); -+} -+ -+static const struct kernel_param_ops slider_offset_ops = { -+ .set = slider_def_offset_set, -+ .get = slider_def_offset_get, -+}; -+ -+/* -+ * To enhance power efficiency dynamically, the firmware can optionally -+ * auto-adjust the slider value based on the current workload. This -+ * adjustment is controlled by the "slider_offset" module parameter. -+ * This offset permits the firmware to increase the slider value -+ * up to and including "SoC slider + slider offset,". -+ */ -+module_param_cb(slider_offset, &slider_offset_ops, NULL, 0644); -+MODULE_PARM_DESC(slider_offset, "Set slider offset"); -+ - /* Convert from platform power profile option to SoC slider value */ - static int convert_profile_to_power_slider(enum platform_profile_option profile) - { -@@ -130,6 +171,8 @@ static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 v - writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); - } - -+#define SLIDER_OFFSET_MASK GENMASK_ULL(6, 4) -+ - static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider) - { - u64 val; -@@ -137,6 +180,11 @@ static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int sli - val = read_soc_slider(proc_priv); - val &= ~SLIDER_MASK; - val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT); -+ -+ /* Set the slider offset from module params */ -+ val &= ~SLIDER_OFFSET_MASK; -+ val |= FIELD_PREP(SLIDER_OFFSET_MASK, slider_offset); -+ - write_soc_slider(proc_priv, val); - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-tools-power-turbostat.8-Update-example.turbo b/SPECS/kernel-rt/0005-tools-power-turbostat.8-Update-example.turbo new file mode 100644 index 000000000..50beb6d09 --- /dev/null +++ b/SPECS/kernel-rt/0005-tools-power-turbostat.8-Update-example.turbo @@ -0,0 +1,56 @@ +From d0927b8efda675c1b4dea7718a967e9f213befb9 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 18:48:39 -0300 +Subject: [PATCH 05/21] tools/power turbostat.8: Update example + +Update the added-counters example to print counters in decimal +rather than hex -- now that it is working... + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.8 | 21 ++++++++++----------- + 1 file changed, 10 insertions(+), 11 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 +index 3340def58d015..ad3fc201552f7 100644 +--- a/tools/power/x86/turbostat/turbostat.8 ++++ b/tools/power/x86/turbostat/turbostat.8 +@@ -410,25 +410,24 @@ CPU pCPU%c1 CPU%c1 + .fi + + .SH ADD PERF COUNTER EXAMPLE #2 (using virtual cpu device) +-Here we run on hybrid, Raptor Lake platform. +-We limit turbostat to show output for just cpu0 (pcore) and cpu12 (ecore). ++Here we run on hybrid, Meteor Lake platform. ++We limit turbostat to show output for just cpu0 (pcore) and cpu4 (ecore). + We add a counter showing number of L3 cache misses, using virtual "cpu" device, + labeling it with the column header, "VCMISS". + We add a counter showing number of L3 cache misses, using virtual "cpu_core" device, +-labeling it with the column header, "PCMISS". This will fail on ecore cpu12. ++labeling it with the column header, "PCMISS". This will fail on ecore cpu4. + We add a counter showing number of L3 cache misses, using virtual "cpu_atom" device, + labeling it with the column header, "ECMISS". This will fail on pcore cpu0. + We display it only once, after the conclusion of 0.1 second sleep. + .nf +-sudo ./turbostat --quiet --cpu 0,12 --show CPU --add perf/cpu/cache-misses,cpu,delta,raw,VCMISS --add perf/cpu_core/cache-misses,cpu,delta,raw,PCMISS --add perf/cpu_atom/cache-misses,cpu,delta,raw,ECMISS sleep .1 ++sudo ./turbostat --quiet --cpu 0,4 --show CPU --add perf/cpu/cache-misses,cpu,delta,VCMISS --add perf/cpu_core/cache-misses,cpu,delta,PCMISS --add perf/cpu_atom/cache-misses,cpu,delta,ECMISS sleep 5 + turbostat: added_perf_counters_init_: perf/cpu_atom/cache-misses: failed to open counter on cpu0 +-turbostat: added_perf_counters_init_: perf/cpu_core/cache-misses: failed to open counter on cpu12 +-0.104630 sec +-CPU ECMISS PCMISS VCMISS +-- 0x0000000000000000 0x0000000000000000 0x0000000000000000 +-0 0x0000000000000000 0x0000000000007951 0x0000000000007796 +-12 0x000000000001137a 0x0000000000000000 0x0000000000011392 +- ++turbostat: added_perf_counters_init_: perf/cpu_core/cache-misses: failed to open counter on cpu4 ++5.001207 sec ++CPU ECMISS PCMISS VCMISS ++- 41586506 46291219 87877749 ++4 83173012 0 83173040 ++0 0 92582439 92582458 + .fi + + .SH ADD PMT COUNTER EXAMPLE +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi b/SPECS/kernel-rt/0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi deleted file mode 100644 index 055435632..000000000 --- a/SPECS/kernel-rt/0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi +++ /dev/null @@ -1,216 +0,0 @@ -From c61e1f8da92e23e84dcf5c395d4d8db7e33870a0 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Tue, 20 Aug 2024 23:31:06 -0700 -Subject: [PATCH 05/44] x86/cea: Export an API to get per CPU exception stacks - for KVM to use - -FRED introduced new fields in the host-state area of the VMCS for -stack levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively -corresponding to per CPU exception stacks for #DB, NMI and #DF. -KVM must populate these each time a vCPU is loaded onto a CPU. - -Convert the __this_cpu_ist_{top,bottom}_va() macros into real -functions and export __this_cpu_ist_top_va(). - -Suggested-by: Christoph Hellwig -Suggested-by: Dave Hansen -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Export accessor instead of data (Christoph Hellwig). -* Add TB from Xuelian Guo. - -Change in v4: -* Rewrite the change log and add comments to the export (Dave Hansen). ---- - arch/x86/coco/sev/sev-nmi.c | 4 ++-- - arch/x86/coco/sev/vc-handle.c | 2 +- - arch/x86/include/asm/cpu_entry_area.h | 17 ++++------------- - arch/x86/kernel/cpu/common.c | 10 +++++----- - arch/x86/kernel/fred.c | 6 +++--- - arch/x86/kernel/traps.c | 2 +- - arch/x86/mm/cpu_entry_area.c | 21 +++++++++++++++++++++ - arch/x86/mm/fault.c | 2 +- - 8 files changed, 38 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/coco/sev/sev-nmi.c b/arch/x86/coco/sev/sev-nmi.c -index d8dfaddfb367..73e34ad7a1a9 100644 ---- a/arch/x86/coco/sev/sev-nmi.c -+++ b/arch/x86/coco/sev/sev-nmi.c -@@ -30,7 +30,7 @@ static __always_inline bool on_vc_stack(struct pt_regs *regs) - if (ip_within_syscall_gap(regs)) - return false; - -- return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC))); -+ return ((sp >= __this_cpu_ist_bottom_va(ESTACK_VC)) && (sp < __this_cpu_ist_top_va(ESTACK_VC))); - } - - /* -@@ -82,7 +82,7 @@ void noinstr __sev_es_ist_exit(void) - /* Read IST entry */ - ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]); - -- if (WARN_ON(ist == __this_cpu_ist_top_va(VC))) -+ if (WARN_ON(ist == __this_cpu_ist_top_va(ESTACK_VC))) - return; - - /* Read back old IST entry and write it to the TSS */ -diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c -index c3b4acbde0d8..88b6bc518a5a 100644 ---- a/arch/x86/coco/sev/vc-handle.c -+++ b/arch/x86/coco/sev/vc-handle.c -@@ -859,7 +859,7 @@ static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt, - - static __always_inline bool is_vc2_stack(unsigned long sp) - { -- return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2)); -+ return (sp >= __this_cpu_ist_bottom_va(ESTACK_VC2) && sp < __this_cpu_ist_top_va(ESTACK_VC2)); - } - - static __always_inline bool vc_from_invalid_context(struct pt_regs *regs) -diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h -index 462fc34f1317..8e17f0ca74e6 100644 ---- a/arch/x86/include/asm/cpu_entry_area.h -+++ b/arch/x86/include/asm/cpu_entry_area.h -@@ -46,7 +46,7 @@ struct cea_exception_stacks { - * The exception stack ordering in [cea_]exception_stacks - */ - enum exception_stack_ordering { -- ESTACK_DF, -+ ESTACK_DF = 0, - ESTACK_NMI, - ESTACK_DB, - ESTACK_MCE, -@@ -58,18 +58,15 @@ enum exception_stack_ordering { - #define CEA_ESTACK_SIZE(st) \ - sizeof(((struct cea_exception_stacks *)0)->st## _stack) - --#define CEA_ESTACK_BOT(ceastp, st) \ -- ((unsigned long)&(ceastp)->st## _stack) -- --#define CEA_ESTACK_TOP(ceastp, st) \ -- (CEA_ESTACK_BOT(ceastp, st) + CEA_ESTACK_SIZE(st)) -- - #define CEA_ESTACK_OFFS(st) \ - offsetof(struct cea_exception_stacks, st## _stack) - - #define CEA_ESTACK_PAGES \ - (sizeof(struct cea_exception_stacks) / PAGE_SIZE) - -+extern unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack); -+extern unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack); -+ - #endif - - #ifdef CONFIG_X86_32 -@@ -144,10 +141,4 @@ static __always_inline struct entry_stack *cpu_entry_stack(int cpu) - return &get_cpu_entry_area(cpu)->entry_stack_page.stack; - } - --#define __this_cpu_ist_top_va(name) \ -- CEA_ESTACK_TOP(__this_cpu_read(cea_exception_stacks), name) -- --#define __this_cpu_ist_bottom_va(name) \ -- CEA_ESTACK_BOT(__this_cpu_read(cea_exception_stacks), name) -- - #endif -diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c -index 34a054181c4d..cb14919f92da 100644 ---- a/arch/x86/kernel/cpu/common.c -+++ b/arch/x86/kernel/cpu/common.c -@@ -2307,12 +2307,12 @@ static inline void setup_getcpu(int cpu) - static inline void tss_setup_ist(struct tss_struct *tss) - { - /* Set up the per-CPU TSS IST stacks */ -- tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(DF); -- tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(NMI); -- tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(DB); -- tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(MCE); -+ tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(ESTACK_DF); -+ tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(ESTACK_NMI); -+ tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(ESTACK_DB); -+ tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(ESTACK_MCE); - /* Only mapped when SEV-ES is active */ -- tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(VC); -+ tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(ESTACK_VC); - } - #else /* CONFIG_X86_64 */ - static inline void tss_setup_ist(struct tss_struct *tss) { } -diff --git a/arch/x86/kernel/fred.c b/arch/x86/kernel/fred.c -index 816187da3a47..06d944a3d051 100644 ---- a/arch/x86/kernel/fred.c -+++ b/arch/x86/kernel/fred.c -@@ -87,7 +87,7 @@ void cpu_init_fred_rsps(void) - FRED_STKLVL(X86_TRAP_DF, FRED_DF_STACK_LEVEL)); - - /* The FRED equivalents to IST stacks... */ -- wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(DB)); -- wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(NMI)); -- wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(DF)); -+ wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); -+ wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); -+ wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); - } -diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c -index 36354b470590..5c9c5ebf5e73 100644 ---- a/arch/x86/kernel/traps.c -+++ b/arch/x86/kernel/traps.c -@@ -954,7 +954,7 @@ asmlinkage __visible noinstr struct pt_regs *vc_switch_off_ist(struct pt_regs *r - - if (!get_stack_info_noinstr(stack, current, &info) || info.type == STACK_TYPE_ENTRY || - info.type > STACK_TYPE_EXCEPTION_LAST) -- sp = __this_cpu_ist_top_va(VC2); -+ sp = __this_cpu_ist_top_va(ESTACK_VC2); - - sync: - /* -diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c -index 575f863f3c75..eedaf103c8ad 100644 ---- a/arch/x86/mm/cpu_entry_area.c -+++ b/arch/x86/mm/cpu_entry_area.c -@@ -18,6 +18,27 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage) - static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks); - DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks); - -+/* -+ * FRED introduced new fields in the host-state area of the VMCS for -+ * stack levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively -+ * corresponding to per CPU stacks for #DB, NMI and #DF. KVM must -+ * populate these each time a vCPU is loaded onto a CPU. -+ * -+ * Called from entry code, so must be noinstr. -+ */ -+noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack) -+{ -+ unsigned long base = (unsigned long)&(__this_cpu_read(cea_exception_stacks)->DF_stack); -+ return base + EXCEPTION_STKSZ + stack * (EXCEPTION_STKSZ + PAGE_SIZE); -+} -+EXPORT_SYMBOL(__this_cpu_ist_top_va); -+ -+noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack) -+{ -+ unsigned long base = (unsigned long)&(__this_cpu_read(cea_exception_stacks)->DF_stack); -+ return base + stack * (EXCEPTION_STKSZ + PAGE_SIZE); -+} -+ - static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset); - - static __always_inline unsigned int cea_offset(unsigned int cpu) -diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c -index 998bd807fc7b..6d59ff7c73e1 100644 ---- a/arch/x86/mm/fault.c -+++ b/arch/x86/mm/fault.c -@@ -671,7 +671,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code, - * and then double-fault, though, because we're likely to - * break the console driver and lose most of the stack dump. - */ -- call_on_stack(__this_cpu_ist_top_va(DF) - sizeof(void*), -+ call_on_stack(__this_cpu_ist_top_va(ESTACK_DF) - sizeof(void *), - handle_stack_overflow, - ASM_CALL_ARG3, - , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info)); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi b/SPECS/kernel-rt/0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi new file mode 100644 index 000000000..04da0600f --- /dev/null +++ b/SPECS/kernel-rt/0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi @@ -0,0 +1,181 @@ +From c1fdba5f755f5423b3b90b7554164da03ba9fdb6 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Thu, 28 Aug 2025 20:16:09 -0400 +Subject: [PATCH 05/44] x86/cea: Use array indexing to simplify exception stack + access + +Refactor struct cea_exception_stacks to leverage array indexing for +exception stack access, improving code clarity and eliminating the +need for the ESTACKS_MEMBERS() macro. + +Convert __this_cpu_ist_{bottom,top}_va() from macros to functions, +allowing removal of the now-obsolete CEA_ESTACK_BOT and CEA_ESTACK_TOP +macros. + +Also drop CEA_ESTACK_SIZE, which just duplicated EXCEPTION_STKSZ. + +Signed-off-by: Xin Li (Intel) +--- + +Change in v9: +* Refactor first and then export in a separate patch (Dave Hansen). + +Change in v7: +* Access cea_exception_stacks using array indexing (Dave Hansen). +* Use BUILD_BUG_ON(ESTACK_DF != 0) to ensure the starting index is 0 + (Dave Hansen). +* Remove Suggested-bys (Dave Hansen). +* Move rename code in a separate patch (Dave Hansen). + +Change in v5: +* Export accessor instead of data (Christoph Hellwig). +* Add TB from Xuelian Guo. + +Change in v4: +* Rewrite the change log and add comments to the export (Dave Hansen). +--- + arch/x86/include/asm/cpu_entry_area.h | 52 ++++++++++++--------------- + arch/x86/kernel/dumpstack_64.c | 4 +-- + arch/x86/mm/cpu_entry_area.c | 21 ++++++++++- + 3 files changed, 44 insertions(+), 33 deletions(-) + +diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h +index d0f884c281787..509e52fc3a0f6 100644 +--- a/arch/x86/include/asm/cpu_entry_area.h ++++ b/arch/x86/include/asm/cpu_entry_area.h +@@ -16,6 +16,19 @@ + #define VC_EXCEPTION_STKSZ 0 + #endif + ++/* ++ * The exception stack ordering in [cea_]exception_stacks ++ */ ++enum exception_stack_ordering { ++ ESTACK_DF, ++ ESTACK_NMI, ++ ESTACK_DB, ++ ESTACK_MCE, ++ ESTACK_VC, ++ ESTACK_VC2, ++ N_EXCEPTION_STACKS ++}; ++ + /* Macro to enforce the same ordering and stack sizes */ + #define ESTACKS_MEMBERS(guardsize, optional_stack_size) \ + char ESTACK_DF_stack_guard[guardsize]; \ +@@ -39,37 +52,22 @@ struct exception_stacks { + + /* The effective cpu entry area mapping with guard pages. */ + struct cea_exception_stacks { +- ESTACKS_MEMBERS(PAGE_SIZE, EXCEPTION_STKSZ) +-}; +- +-/* +- * The exception stack ordering in [cea_]exception_stacks +- */ +-enum exception_stack_ordering { +- ESTACK_DF, +- ESTACK_NMI, +- ESTACK_DB, +- ESTACK_MCE, +- ESTACK_VC, +- ESTACK_VC2, +- N_EXCEPTION_STACKS ++ struct { ++ char stack_guard[PAGE_SIZE]; ++ char stack[EXCEPTION_STKSZ]; ++ } event_stacks[N_EXCEPTION_STACKS]; ++ char IST_top_guard[PAGE_SIZE]; + }; + +-#define CEA_ESTACK_SIZE(st) \ +- sizeof(((struct cea_exception_stacks *)0)->st## _stack) +- +-#define CEA_ESTACK_BOT(ceastp, st) \ +- ((unsigned long)&(ceastp)->st## _stack) +- +-#define CEA_ESTACK_TOP(ceastp, st) \ +- (CEA_ESTACK_BOT(ceastp, st) + CEA_ESTACK_SIZE(st)) +- + #define CEA_ESTACK_OFFS(st) \ +- offsetof(struct cea_exception_stacks, st## _stack) ++ offsetof(struct cea_exception_stacks, event_stacks[st].stack) + + #define CEA_ESTACK_PAGES \ + (sizeof(struct cea_exception_stacks) / PAGE_SIZE) + ++extern unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack); ++extern unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack); ++ + #endif + + #ifdef CONFIG_X86_32 +@@ -144,10 +142,4 @@ static __always_inline struct entry_stack *cpu_entry_stack(int cpu) + return &get_cpu_entry_area(cpu)->entry_stack_page.stack; + } + +-#define __this_cpu_ist_top_va(name) \ +- CEA_ESTACK_TOP(__this_cpu_read(cea_exception_stacks), name) +- +-#define __this_cpu_ist_bottom_va(name) \ +- CEA_ESTACK_BOT(__this_cpu_read(cea_exception_stacks), name) +- + #endif +diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c +index 40f51e2781715..93b10b264e53b 100644 +--- a/arch/x86/kernel/dumpstack_64.c ++++ b/arch/x86/kernel/dumpstack_64.c +@@ -70,9 +70,9 @@ struct estack_pages { + + #define EPAGERANGE(st) \ + [PFN_DOWN(CEA_ESTACK_OFFS(st)) ... \ +- PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = { \ ++ PFN_DOWN(CEA_ESTACK_OFFS(st) + EXCEPTION_STKSZ - 1)] = { \ + .offs = CEA_ESTACK_OFFS(st), \ +- .size = CEA_ESTACK_SIZE(st), \ ++ .size = EXCEPTION_STKSZ, \ + .type = STACK_TYPE_EXCEPTION + st, } + + /* +diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c +index 9fa371af8abc7..b3d90f9cfbb11 100644 +--- a/arch/x86/mm/cpu_entry_area.c ++++ b/arch/x86/mm/cpu_entry_area.c +@@ -18,6 +18,25 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage) + static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks); + DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks); + ++/* ++ * Typically invoked by entry code, so must be noinstr. ++ */ ++noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack) ++{ ++ struct cea_exception_stacks *s; ++ ++ BUILD_BUG_ON(ESTACK_DF != 0); ++ ++ s = __this_cpu_read(cea_exception_stacks); ++ ++ return (unsigned long)&s->event_stacks[stack].stack; ++} ++ ++noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack) ++{ ++ return __this_cpu_ist_bottom_va(stack) + EXCEPTION_STKSZ; ++} ++ + static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset); + + static __always_inline unsigned int cea_offset(unsigned int cpu) +@@ -132,7 +151,7 @@ static void __init percpu_setup_debug_store(unsigned int cpu) + + #define cea_map_stack(name) do { \ + npages = sizeof(estacks->name## _stack) / PAGE_SIZE; \ +- cea_map_percpu_pages(cea->estacks.name## _stack, \ ++ cea_map_percpu_pages(cea->estacks.event_stacks[name].stack, \ + estacks->name## _stack, npages, PAGE_KERNEL); \ + } while (0) + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac b/SPECS/kernel-rt/0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac deleted file mode 100644 index 0ab3e5ba0..000000000 --- a/SPECS/kernel-rt/0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac +++ /dev/null @@ -1,37 +0,0 @@ -From d30443ac72e0c28fc76598befc751143987d6107 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Tue, 29 Jul 2025 15:22:48 +0800 -Subject: [PATCH 06/13] EDAC/skx_common: Remove redundant upper bound check for - res->imc - -The following upper bound check for the memory controller physical index -decoded by ADXL is the only place where use the macro 'NUM_IMC' is used: - - res->imc > NUM_IMC - 1 - -Since this check is already covered by skx_get_mc_mapping(), meaning no -memory controller logical index exists for an invalid memory controller -physical index decoded by ADXL, remove the redundant upper bound check -so that the definition for 'NUM_IMC' can be cleaned up (in another patch). - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index 09187043c045..5899a1110495 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -207,7 +207,7 @@ static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) - res->cs = (int)adxl_values[component_indices[INDEX_CS]]; - } - -- if (res->imc > NUM_IMC - 1 || res->imc < 0) { -+ if (res->imc < 0) { - skx_printk(KERN_ERR, "Bad imc %d\n", res->imc); - return false; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security b/SPECS/kernel-rt/0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security new file mode 100644 index 000000000..db2617fce --- /dev/null +++ b/SPECS/kernel-rt/0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security @@ -0,0 +1,461 @@ +From 511d9f589f959d58452c83753b1d9b402d100298 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Mon, 5 Oct 2020 13:04:48 +0300 +Subject: [PATCH 6/8] INTEL_DII: mei: iaf: add iaf (Intel Accelerator Fabric) + component driver + +On every boot the GSC firmware programs +"the IAF minimum allowed SVN register" with its minimal allowed value +in all tiles. SVN stands for security version number. + +mei_iaf driver provides an interface for IAF to increment the +minimal allowed SVN. +mei_aif is represented as a device over mei client bus. + +To enable this select CONFIG_INTEL_MEI_IAF + +V2: +Updated the parsing of values returned by the firmware. + +V3: +Replaced error messages in cases of SVN DISABLED, SVM SAME +and SVM SMALLER with the debug messages instead of the error ones. +Those cases are legitimate and should not be reported as errors. + +v4: +Move MCHI_GROUP_ID definition to this patch. + +Co-developed-by: Tomas Winkler +Co-developed-by: Vitaly Lubart +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/Kconfig | 1 + + drivers/misc/mei/Makefile | 1 + + drivers/misc/mei/iaf/Kconfig | 12 ++ + drivers/misc/mei/iaf/Makefile | 9 + + drivers/misc/mei/iaf/mei_iaf.c | 295 +++++++++++++++++++++++++++ + drivers/misc/mei/mkhi.h | 2 + + include/drm/i915_mei_iaf_interface.h | 25 +++ + include/drm/intel/i915_component.h | 1 + + 8 files changed, 346 insertions(+) + create mode 100644 drivers/misc/mei/iaf/Kconfig + create mode 100644 drivers/misc/mei/iaf/Makefile + create mode 100644 drivers/misc/mei/iaf/mei_iaf.c + create mode 100644 include/drm/i915_mei_iaf_interface.h + +diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig +index 2b1330b35454e..729aea4fe3212 100644 +--- a/drivers/misc/mei/Kconfig ++++ b/drivers/misc/mei/Kconfig +@@ -106,5 +106,6 @@ config INTEL_MEI_VIRTIO + source "drivers/misc/mei/hdcp/Kconfig" + source "drivers/misc/mei/pxp/Kconfig" + source "drivers/misc/mei/gsc_proxy/Kconfig" ++source "drivers/misc/mei/iaf/Kconfig" + + endif +diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile +index 9de79da3d7027..e14c0cb5e5c89 100644 +--- a/drivers/misc/mei/Makefile ++++ b/drivers/misc/mei/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_INTEL_MEI_HDCP) += hdcp/ + obj-$(CONFIG_INTEL_MEI_PXP) += pxp/ + obj-$(CONFIG_INTEL_MEI_GSC_PROXY) += gsc_proxy/ + obj-$(CONFIG_INTEL_MEI_LB) += mei_lb.o ++obj-$(CONFIG_INTEL_MEI_IAF) += iaf/ + + obj-$(CONFIG_INTEL_MEI_VSC_HW) += mei-vsc-hw.o + mei-vsc-hw-y := vsc-tp.o +diff --git a/drivers/misc/mei/iaf/Kconfig b/drivers/misc/mei/iaf/Kconfig +new file mode 100644 +index 0000000000000..0690b96e2dd76 +--- /dev/null ++++ b/drivers/misc/mei/iaf/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (c) 2020-2021, Intel Corporation. All rights reserved. ++# ++config INTEL_MEI_IAF ++ tristate "Intel Accelerator Fabric services of ME Interface" ++ select INTEL_MEI_ME ++ depends on DRM_I915 ++ help ++ MEI Support for IAF Services on Intel graphics card. ++ ++ Enables the ME FW services required for IAF support through ++ I915 display driver of Intel. +diff --git a/drivers/misc/mei/iaf/Makefile b/drivers/misc/mei/iaf/Makefile +new file mode 100644 +index 0000000000000..8eb709cf536f5 +--- /dev/null ++++ b/drivers/misc/mei/iaf/Makefile +@@ -0,0 +1,9 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Copyright (c) 2020-2021, Intel Corporation. All rights reserved. ++# ++# Makefile - IAF client driver for Intel MEI Bus Driver. ++ ++subdir-ccflags-y += -I$(srctree)/drivers/misc/mei/ ++ ++obj-$(CONFIG_INTEL_MEI_IAF) += mei_iaf.o +diff --git a/drivers/misc/mei/iaf/mei_iaf.c b/drivers/misc/mei/iaf/mei_iaf.c +new file mode 100644 +index 0000000000000..e81280ea20a8c +--- /dev/null ++++ b/drivers/misc/mei/iaf/mei_iaf.c +@@ -0,0 +1,295 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright © 2020-2021 Intel Corporation ++ */ ++ ++/** ++ * DOC: MEI_IAF Client Driver ++ * ++ * The IAF (Intel Accelerator Fabric) component driver acts as an interface ++ * between IAF i915 driver and GSC. The only api this interface provides is ++ * the 'commit svn' call. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mkhi.h" ++ ++#define MCA_ARBSVN_COMMIT_COMMAND_ID 0x1B ++ ++enum arbsvn_nvar_usage { ++ ARBSVN_NVAR_USAGE_FW_MIN_VER = 0, ++ ARBSVN_NVAR_USAGE_MAX ++}; ++ ++struct mca_arbsvn_commit_req { ++ struct mkhi_msg_hdr mkhi_header; ++ u8 usage_id; ++ u8 reserved0; ++ u16 reserved1; ++} __packed; ++ ++struct mca_arbsvn_commit_resp { ++ struct mkhi_msg_hdr mkhi_header; ++}; ++ ++#define MCA_OK 0x0 /* on successful commit */ ++#define MCA_INVALID_INPUT 0xb /* if usage id is invalid */ ++/* if disabled in the file or any other error (generic, reading or writing file) */ ++#define MCA_ARB_SVN_DISABLED 0x20 ++/* SVN was not updated, same value */ ++#define MCA_ARB_SVN_SAME 0x28 ++/* SVN was not updated, older value */ ++#define MCA_ARB_SVN_SMALLER 0x29 ++ ++static int get_error_code(const struct device *dev, u8 result) ++{ ++ int ret; ++ ++ switch (result) { ++ case MCA_OK: ++ ret = 0; ++ break; ++ case MCA_ARB_SVN_DISABLED: ++ dev_dbg(dev, "Arb Svn disabled (error code 0x%x)\n", ++ MCA_ARB_SVN_DISABLED); ++ ret = -ENOENT; ++ break; ++ case MCA_INVALID_INPUT: ++ dev_err(dev, "Wrong usage id(error code 0x%x)\n", ++ MCA_INVALID_INPUT); ++ ret = -EINVAL; ++ break; ++ case MCA_ARB_SVN_SAME: ++ dev_dbg(dev, "SVN was not updated, same value(error code 0x%x)\n", ++ MCA_ARB_SVN_SAME); ++ ret = -EACCES; ++ break; ++ case MCA_ARB_SVN_SMALLER: ++ dev_dbg(dev, "SVN was not updated, older value(error code 0x%x)\n", ++ MCA_ARB_SVN_SMALLER); ++ ret = -EBADF; ++ break; ++ default: ++ dev_err(dev, "Unknown error code 0x%x\n", result); ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static int mei_iaf_check_response(const struct device *dev, ++ struct mkhi_msg_hdr *hdr) ++{ ++ if (hdr->group_id != MCHI_GROUP_ID) { ++ dev_err(dev, "Mismatch group id: 0x%x instead of 0x%x\n", ++ hdr->group_id, MCHI_GROUP_ID); ++ return -EINVAL; ++ } ++ ++ if (hdr->command != (MCA_ARBSVN_COMMIT_COMMAND_ID | 0x80)) { ++ dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", ++ hdr->command, MCA_ARBSVN_COMMIT_COMMAND_ID | 0x80); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * mei_iaf_commit_svn() - Commits current SVN. ++ * @dev: device corresponding to the mei_cl_device ++ * Return: 0 on Success ++ * * -EINVAL : Invalid usage id parameter ++ * * -ENOENT : ARB SVN is disabled in the file or any other error ++ * (generic, reading or writing file) ++ * * -EIO : Unknown I/O error ++ */ ++static int mei_iaf_commit_svn(const struct device *dev) ++{ ++ struct mei_cl_device *cldev; ++ struct mca_arbsvn_commit_req commit_req = { }; ++ struct mca_arbsvn_commit_resp commit_resp = { }; ++ int ret; ++ ++ dev_dbg(dev, "in %s\n", __func__); ++ ++ if (!dev) ++ return -EINVAL; ++ ++ cldev = to_mei_cl_device(dev); ++ ++ dev_dbg(dev, "after to_mei_cl_device cldev %p\n", cldev); ++ ++ ret = mei_cldev_enable(cldev); ++ if (ret < 0) { ++ dev_dbg(dev, "mei_cldev_enable Failed. %d\n", ret); ++ return -EBUSY; ++ } ++ ++ dev_dbg(dev, "after mei_cldev_enable, ret=%d\n", ret); ++ commit_req.mkhi_header.group_id = MCHI_GROUP_ID; ++ commit_req.mkhi_header.command = MCA_ARBSVN_COMMIT_COMMAND_ID; ++ commit_req.usage_id = ARBSVN_NVAR_USAGE_FW_MIN_VER; ++ ++ ret = mei_cldev_send(cldev, (u8 *)&commit_req, sizeof(commit_req)); ++ if (ret < 0) { ++ dev_err(dev, "mei_cldev_send failed. %d\n", ret); ++ goto end; ++ } ++ dev_dbg(dev, "after send, ret=%d\n", ret); ++ print_hex_dump_debug("sent svn commit message: ", DUMP_PREFIX_OFFSET, ++ 16, 1, (u8 *)&commit_req, ret, false); ++ ++ ret = mei_cldev_recv(cldev, (u8 *)&commit_resp, sizeof(commit_resp)); ++ if (ret < 0) { ++ dev_err(dev, "mei_cldev_recv failed. %d\n", ret); ++ goto end; ++ } ++ dev_dbg(dev, "after recv, ret=%d\n", ret); ++ print_hex_dump_debug("mei_iaf_commit_response ", DUMP_PREFIX_OFFSET, ++ 16, 1, (u8 *)&commit_resp, ret, false); ++ ++ ret = mei_iaf_check_response(dev, &commit_resp.mkhi_header); ++ if (ret) { ++ dev_err(dev, "bad result response from the firmware: 0x%x\n", ++ *(uint32_t *)&commit_resp.mkhi_header); ++ goto end; ++ } ++ dev_dbg(dev, "after check_response\n"); ++ ret = get_error_code(dev, commit_resp.mkhi_header.result); ++ ++end: ++ dev_dbg(dev, "returning with %d\n", ret); ++ mei_cldev_disable(cldev); ++ return ret; ++} ++ ++static const struct i915_iaf_component_ops mei_iaf_ops = { ++ .owner = THIS_MODULE, ++ .commit_svn = mei_iaf_commit_svn, ++}; ++ ++static int mei_component_master_bind(struct device *dev) ++{ ++ int ret; ++ ++ dev_dbg(dev, "mei_iaf_ops addr %p\n", &mei_iaf_ops); ++ ++ ret = component_bind_all(dev, (void *)&mei_iaf_ops); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static void mei_component_master_unbind(struct device *dev) ++{ ++ dev_dbg(dev, "in %s\n", __func__); ++ component_unbind_all(dev, (void *)&mei_iaf_ops); ++} ++ ++static const struct component_master_ops mei_component_master_ops = { ++ .bind = mei_component_master_bind, ++ .unbind = mei_component_master_unbind, ++}; ++ ++/** ++ * mei_iaf_component_match - compare function for matching mei iaf. ++ * ++ * The function checks if the driver is i915, the subcomponent is IAF ++ * and the parent of iaf and the grand parent of mei_if are the same ++ * i915 device. ++ * ++ * @dev: master device ++ * @subcomponent: subcomponent to match (I915_COMPONENT_IAF) ++ * @data: compare data (mei iaf device) ++ * ++ * Return: ++ * * 1 - if components match ++ * * 0 - otherwise ++ */ ++static int mei_iaf_component_match(struct device *dev, int subcomponent, ++ void *data) ++{ ++ struct device *base = data; ++ ++ dev_dbg(dev, "trying to match %s\n", dev->driver->name); ++ if (subcomponent != I915_COMPONENT_IAF) ++ return 0; ++ ++ if (strcmp(dev->driver->name, "iaf")) ++ return 0; ++ ++ base = base->parent; ++ if (!base) ++ return 0; ++ ++ base = base->parent; ++ dev = dev->parent; ++ ++ return (base && dev && dev == base); ++} ++ ++static int mei_iaf_probe(struct mei_cl_device *cldev, ++ const struct mei_cl_device_id *id) ++{ ++ struct component_match *master_match; ++ int ret; ++ ++ master_match = NULL; ++ component_match_add_typed(&cldev->dev, &master_match, ++ mei_iaf_component_match, &cldev->dev); ++ if (IS_ERR_OR_NULL(master_match)) { ++ ret = -ENOMEM; ++ goto err_exit; ++ } ++ ++ ret = component_master_add_with_match(&cldev->dev, ++ &mei_component_master_ops, ++ master_match); ++ if (ret < 0) { ++ dev_err(&cldev->dev, "Master comp add failed %d\n", ret); ++ goto err_exit; ++ } ++ ++ return 0; ++ ++err_exit: ++ return ret; ++} ++ ++static void mei_iaf_remove(struct mei_cl_device *cldev) ++{ ++ component_master_del(&cldev->dev, &mei_component_master_ops); ++} ++ ++/* fe2af7a6-ef22-4b45-872f-176b0bbc8b43: MCHIF GUID */ ++#define MEI_GUID_MCHIF UUID_LE(0xfe2af7a6, 0xef22, 0x4b45, \ ++ 0x87, 0x2f, 0x17, 0x6b, 0x0b, 0xbc, 0x8b, 0x43) ++ ++static struct mei_cl_device_id mei_iaf_tbl[] = { ++ { .uuid = MEI_GUID_MCHIF, .version = MEI_CL_VERSION_ANY }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mei, mei_iaf_tbl); ++ ++static struct mei_cl_driver mei_iaf_driver = { ++ .id_table = mei_iaf_tbl, ++ .name = KBUILD_MODNAME, ++ .probe = mei_iaf_probe, ++ .remove = mei_iaf_remove, ++}; ++ ++module_mei_cl_driver(mei_iaf_driver); ++ ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MEI IAF"); +diff --git a/drivers/misc/mei/mkhi.h b/drivers/misc/mei/mkhi.h +index 1473ea4896662..63d9f02e7341f 100644 +--- a/drivers/misc/mei/mkhi.h ++++ b/drivers/misc/mei/mkhi.h +@@ -16,6 +16,8 @@ + #define MKHI_GEN_GROUP_ID 0xFF + #define MKHI_GEN_GET_FW_VERSION_CMD 0x2 + ++#define MCHI_GROUP_ID 0xA ++ + #define MKHI_GROUP_ID_GFX 0x30 + #define MKHI_GFX_RESET_WARN_CMD_REQ 0x0 + #define MKHI_GFX_MEMORY_READY_CMD_REQ 0x1 +diff --git a/include/drm/i915_mei_iaf_interface.h b/include/drm/i915_mei_iaf_interface.h +new file mode 100644 +index 0000000000000..dde938dd0ea2c +--- /dev/null ++++ b/include/drm/i915_mei_iaf_interface.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: (GPL-2.0+) */ ++/* ++ * Copyright © 2020-2021 Intel Corporation ++ */ ++ ++#ifndef _I915_MEI_IAF_INTERFACE_H_ ++#define _I915_MEI_IAF_INTERFACE_H_ ++ ++#include ++ ++/** ++ * struct i915_iaf_component_ops- ops for IAF services. ++ * @owner: Module providing the ops ++ * @commit_svn: commits current FW SVN ++ */ ++struct i915_iaf_component_ops { ++ /** ++ * @owner: mei_iaf module ++ */ ++ struct module *owner; ++ ++ int (*commit_svn)(const struct device *dev); ++}; ++ ++#endif /* _I915_MEI_IAF_INTERFACE_H_ */ +diff --git a/include/drm/intel/i915_component.h b/include/drm/intel/i915_component.h +index 8082db222e00e..b598f34e6db28 100644 +--- a/include/drm/intel/i915_component.h ++++ b/include/drm/intel/i915_component.h +@@ -32,6 +32,7 @@ enum i915_component_type { + I915_COMPONENT_PXP, + I915_COMPONENT_GSC_PROXY, + INTEL_COMPONENT_LB, ++ I915_COMPONENT_IAF, + }; + + /* MAX_PORT is the number of port +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi b/SPECS/kernel-rt/0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi deleted file mode 100644 index 59fc03702..000000000 --- a/SPECS/kernel-rt/0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi +++ /dev/null @@ -1,197 +0,0 @@ -From bfc3da6b878cbec9bfe8034f8e817d0cab069ab5 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 31 Aug 2023 01:52:58 -0700 -Subject: [PATCH 06/44] KVM: VMX: Initialize VMCS FRED fields - -Initialize host VMCS FRED fields with host FRED MSRs' value and -guest VMCS FRED fields to 0. - -FRED CPU state is managed in 9 new FRED MSRs: - IA32_FRED_CONFIG, - IA32_FRED_STKLVLS, - IA32_FRED_RSP0, - IA32_FRED_RSP1, - IA32_FRED_RSP2, - IA32_FRED_RSP3, - IA32_FRED_SSP1, - IA32_FRED_SSP2, - IA32_FRED_SSP3, -as well as a few existing CPU registers and MSRs: - CR4.FRED, - IA32_STAR, - IA32_KERNEL_GS_BASE, - IA32_PL0_SSP (also known as IA32_FRED_SSP0). - -CR4, IA32_KERNEL_GS_BASE and IA32_STAR are already well managed. -Except IA32_FRED_RSP0 and IA32_FRED_SSP0, all other FRED CPU state -MSRs have corresponding VMCS fields in both the host-state and -guest-state areas. So KVM just needs to initialize them, and with -proper VM entry/exit FRED controls, a FRED CPU will keep tracking -host and guest FRED CPU state in VMCS automatically. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Initialize host SSP[1-3] to 0s in vmx_set_constant_host_state() - because Linux doesn't support kernel shadow stacks (Chao Gao). - -Change in v3: -* Use structure kvm_host_values to keep host fred config & stack levels - (Sean Christopherson). - -Changes in v2: -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() to decouple - KVM's capability to virtualize a feature and host's enabling of a - feature (Chao Gao). -* Move guest FRED state init into __vmx_vcpu_reset() (Chao Gao). ---- - arch/x86/include/asm/vmx.h | 32 ++++++++++++++++++++++++++++++ - arch/x86/kvm/vmx/vmx.c | 40 +++++++++++++++++++++++++++++++++++++- - arch/x86/kvm/x86.h | 3 +++ - 3 files changed, 74 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index ab70f52798d4..d222fd1517ce 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -294,12 +294,44 @@ enum vmcs_field { - GUEST_BNDCFGS_HIGH = 0x00002813, - GUEST_IA32_RTIT_CTL = 0x00002814, - GUEST_IA32_RTIT_CTL_HIGH = 0x00002815, -+ GUEST_IA32_FRED_CONFIG = 0x0000281a, -+ GUEST_IA32_FRED_CONFIG_HIGH = 0x0000281b, -+ GUEST_IA32_FRED_RSP1 = 0x0000281c, -+ GUEST_IA32_FRED_RSP1_HIGH = 0x0000281d, -+ GUEST_IA32_FRED_RSP2 = 0x0000281e, -+ GUEST_IA32_FRED_RSP2_HIGH = 0x0000281f, -+ GUEST_IA32_FRED_RSP3 = 0x00002820, -+ GUEST_IA32_FRED_RSP3_HIGH = 0x00002821, -+ GUEST_IA32_FRED_STKLVLS = 0x00002822, -+ GUEST_IA32_FRED_STKLVLS_HIGH = 0x00002823, -+ GUEST_IA32_FRED_SSP1 = 0x00002824, -+ GUEST_IA32_FRED_SSP1_HIGH = 0x00002825, -+ GUEST_IA32_FRED_SSP2 = 0x00002826, -+ GUEST_IA32_FRED_SSP2_HIGH = 0x00002827, -+ GUEST_IA32_FRED_SSP3 = 0x00002828, -+ GUEST_IA32_FRED_SSP3_HIGH = 0x00002829, - HOST_IA32_PAT = 0x00002c00, - HOST_IA32_PAT_HIGH = 0x00002c01, - HOST_IA32_EFER = 0x00002c02, - HOST_IA32_EFER_HIGH = 0x00002c03, - HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04, - HOST_IA32_PERF_GLOBAL_CTRL_HIGH = 0x00002c05, -+ HOST_IA32_FRED_CONFIG = 0x00002c08, -+ HOST_IA32_FRED_CONFIG_HIGH = 0x00002c09, -+ HOST_IA32_FRED_RSP1 = 0x00002c0a, -+ HOST_IA32_FRED_RSP1_HIGH = 0x00002c0b, -+ HOST_IA32_FRED_RSP2 = 0x00002c0c, -+ HOST_IA32_FRED_RSP2_HIGH = 0x00002c0d, -+ HOST_IA32_FRED_RSP3 = 0x00002c0e, -+ HOST_IA32_FRED_RSP3_HIGH = 0x00002c0f, -+ HOST_IA32_FRED_STKLVLS = 0x00002c10, -+ HOST_IA32_FRED_STKLVLS_HIGH = 0x00002c11, -+ HOST_IA32_FRED_SSP1 = 0x00002c12, -+ HOST_IA32_FRED_SSP1_HIGH = 0x00002c13, -+ HOST_IA32_FRED_SSP2 = 0x00002c14, -+ HOST_IA32_FRED_SSP2_HIGH = 0x00002c15, -+ HOST_IA32_FRED_SSP3 = 0x00002c16, -+ HOST_IA32_FRED_SSP3_HIGH = 0x00002c17, - PIN_BASED_VM_EXEC_CONTROL = 0x00004000, - CPU_BASED_VM_EXEC_CONTROL = 0x00004002, - EXCEPTION_BITMAP = 0x00004004, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 24c447935e9b..4a183ffb2154 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1462,6 +1462,15 @@ void vmx_vcpu_load_vmcs(struct kvm_vcpu *vcpu, int cpu) - (unsigned long)(cpu_entry_stack(cpu) + 1)); - } - -+ /* Per-CPU FRED MSRs */ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+#ifdef CONFIG_X86_64 -+ vmcs_write64(HOST_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); -+ vmcs_write64(HOST_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); -+ vmcs_write64(HOST_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); -+#endif -+ } -+ - vmx->loaded_vmcs->cpu = cpu; - } - } -@@ -4370,6 +4379,17 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) - */ - vmcs_write16(HOST_DS_SELECTOR, 0); - vmcs_write16(HOST_ES_SELECTOR, 0); -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+ /* FRED CONFIG and STKLVLS are the same on all CPUs */ -+ vmcs_write64(HOST_IA32_FRED_CONFIG, kvm_host.fred_config); -+ vmcs_write64(HOST_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); -+ -+ /* Linux doesn't support kernel shadow stacks, thus SSPs are 0s */ -+ vmcs_write64(HOST_IA32_FRED_SSP1, 0); -+ vmcs_write64(HOST_IA32_FRED_SSP2, 0); -+ vmcs_write64(HOST_IA32_FRED_SSP3, 0); -+ } - #else - vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS); /* 22.2.4 */ - vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS); /* 22.2.4 */ -@@ -4892,6 +4912,17 @@ static void init_vmcs(struct vcpu_vmx *vmx) - } - - vmx_setup_uret_msrs(vmx); -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, 0); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, 0); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, 0); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, 0); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, 0); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, 0); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, 0); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, 0); -+ } - } - - static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu) -@@ -8722,7 +8753,14 @@ __init int vmx_hardware_setup(void) - */ - if (!static_cpu_has(X86_FEATURE_SELFSNOOP)) - kvm_caps.supported_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; -- kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; -+ -+ kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+ rdmsrl(MSR_IA32_FRED_CONFIG, kvm_host.fred_config); -+ rdmsrl(MSR_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); -+ } -+ - return r; - } - -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 41ab002f6471..b12749a2a67c 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -52,6 +52,9 @@ struct kvm_host_values { - u64 xss; - u64 s_cet; - u64 arch_capabilities; -+ -+ u64 fred_config; -+ u64 fred_stklvls; - }; - - void kvm_spurious_fault(void); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet b/SPECS/kernel-rt/0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet deleted file mode 100644 index 191b608d9..000000000 --- a/SPECS/kernel-rt/0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet +++ /dev/null @@ -1,145 +0,0 @@ -From 02cbe8cd399a62d4bfc1621d081628a08cb4b136 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:36 -0700 -Subject: [PATCH 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support - -Enable KVM_{G,S}ET_ONE_REG uAPIs so that userspace can access HW MSR or -KVM synthetic MSR through it. - -In CET KVM series [1], KVM "steals" an MSR from PV MSR space and access -it via KVM_{G,S}ET_MSRs uAPIs, but the approach pollutes PV MSR space -and hides the difference of synthetic MSRs and normal HW defined MSRs. - -Now carve out a separate room in KVM-customized MSR address space for -synthetic MSRs. The synthetic MSRs are not exposed to userspace via -KVM_GET_MSR_INDEX_LIST, instead userspace complies with KVM's setup and -composes the uAPI params. KVM synthetic MSR indices start from 0 and -increase linearly. Userspace caller should tag MSR type correctly in -order to access intended HW or synthetic MSR. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Link: https://lore.kernel.org/all/20240219074733.122080-18-weijiang.yang@intel.com/ [1] -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/uapi/asm/kvm.h | 10 +++++ - arch/x86/kvm/x86.c | 66 +++++++++++++++++++++++++++++++++ - 2 files changed, 76 insertions(+) - -diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h -index 0f15d683817d..e72d9e6c1739 100644 ---- a/arch/x86/include/uapi/asm/kvm.h -+++ b/arch/x86/include/uapi/asm/kvm.h -@@ -411,6 +411,16 @@ struct kvm_xcrs { - __u64 padding[16]; - }; - -+#define KVM_X86_REG_MSR (1 << 2) -+#define KVM_X86_REG_SYNTHETIC (1 << 3) -+ -+struct kvm_x86_reg_id { -+ __u32 index; -+ __u8 type; -+ __u8 rsvd; -+ __u16 rsvd16; -+}; -+ - #define KVM_SYNC_X86_REGS (1UL << 0) - #define KVM_SYNC_X86_SREGS (1UL << 1) - #define KVM_SYNC_X86_EVENTS (1UL << 2) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a6765d1d1741..f2832a644226 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2218,6 +2218,31 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data) - return kvm_set_msr_ignored_check(vcpu, index, *data, true); - } - -+static int kvm_get_one_msr(struct kvm_vcpu *vcpu, u32 msr, u64 __user *value) -+{ -+ u64 val; -+ int r; -+ -+ r = do_get_msr(vcpu, msr, &val); -+ if (r) -+ return r; -+ -+ if (put_user(val, value)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static int kvm_set_one_msr(struct kvm_vcpu *vcpu, u32 msr, u64 __user *value) -+{ -+ u64 val; -+ -+ if (get_user(val, value)) -+ return -EFAULT; -+ -+ return do_set_msr(vcpu, msr, &val); -+} -+ - #ifdef CONFIG_X86_64 - struct pvclock_clock { - int vclock_mode; -@@ -5880,6 +5905,11 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, - } - } - -+static int kvm_translate_synthetic_msr(struct kvm_x86_reg_id *reg) -+{ -+ return -EINVAL; -+} -+ - long kvm_arch_vcpu_ioctl(struct file *filp, - unsigned int ioctl, unsigned long arg) - { -@@ -5996,6 +6026,42 @@ long kvm_arch_vcpu_ioctl(struct file *filp, - srcu_read_unlock(&vcpu->kvm->srcu, idx); - break; - } -+ case KVM_GET_ONE_REG: -+ case KVM_SET_ONE_REG: { -+ struct kvm_x86_reg_id *id; -+ struct kvm_one_reg reg; -+ u64 __user *value; -+ -+ r = -EFAULT; -+ if (copy_from_user(®, argp, sizeof(reg))) -+ break; -+ -+ r = -EINVAL; -+ id = (struct kvm_x86_reg_id *)®.id; -+ if (id->rsvd || id->rsvd16) -+ break; -+ -+ if (id->type != KVM_X86_REG_MSR && -+ id->type != KVM_X86_REG_SYNTHETIC) -+ break; -+ -+ if (id->type == KVM_X86_REG_SYNTHETIC) { -+ r = kvm_translate_synthetic_msr(id); -+ if (r) -+ break; -+ } -+ -+ r = -EINVAL; -+ if (id->type != KVM_X86_REG_MSR) -+ break; -+ -+ value = u64_to_user_ptr(reg.addr); -+ if (ioctl == KVM_GET_ONE_REG) -+ r = kvm_get_one_msr(vcpu, id->index, value); -+ else -+ r = kvm_set_one_msr(vcpu, id->index, value); -+ break; -+ } - case KVM_TPR_ACCESS_REPORTING: { - struct kvm_tpr_access_ctl tac; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-bfs-Reconstruct-file-type-when-loading-from-disk.patch b/SPECS/kernel-rt/0006-bfs-Reconstruct-file-type-when-loading-from-disk.patch deleted file mode 100644 index 63b432ed4..000000000 --- a/SPECS/kernel-rt/0006-bfs-Reconstruct-file-type-when-loading-from-disk.patch +++ /dev/null @@ -1,68 +0,0 @@ -From ee5d4d074c348bbdf9d0ab304ff12a66e74cfa35 Mon Sep 17 00:00:00 2001 -From: Tetsuo Handa -Date: Thu, 23 Oct 2025 22:25:49 +0900 -Subject: [PATCH 06/16] bfs: Reconstruct file type when loading from disk - -syzbot is reporting that S_IFMT bits of inode->i_mode can become bogus when -the S_IFMT bits of the 32bits "mode" field loaded from disk are corrupted -or when the 32bits "attributes" field loaded from disk are corrupted. - -A documentation says that BFS uses only lower 9 bits of the "mode" field. -But I can't find an explicit explanation that the unused upper 23 bits -(especially, the S_IFMT bits) are initialized with 0. - -Therefore, ignore the S_IFMT bits of the "mode" field loaded from disk. -Also, verify that the value of the "attributes" field loaded from disk is -either BFS_VREG or BFS_VDIR (because BFS supports only regular files and -the root directory). - -Reported-by: syzbot+895c23f6917da440ed0d@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=895c23f6917da440ed0d -Signed-off-by: Tetsuo Handa -Link: https://patch.msgid.link/fabce673-d5b9-4038-8287-0fd65d80203b@I-love.SAKURA.ne.jp -Reviewed-by: Tigran Aivazian -Signed-off-by: Christian Brauner ---- - fs/bfs/inode.c | 19 ++++++++++++++++++- - 1 file changed, 18 insertions(+), 1 deletion(-) - -diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c -index 1d41ce477df5..984b365df046 100644 ---- a/fs/bfs/inode.c -+++ b/fs/bfs/inode.c -@@ -61,7 +61,19 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino) - off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; - di = (struct bfs_inode *)bh->b_data + off; - -- inode->i_mode = 0x0000FFFF & le32_to_cpu(di->i_mode); -+ /* -+ * https://martin.hinner.info/fs/bfs/bfs-structure.html explains that -+ * BFS in SCO UnixWare environment used only lower 9 bits of di->i_mode -+ * value. This means that, although bfs_write_inode() saves whole -+ * inode->i_mode bits (which include S_IFMT bits and S_IS{UID,GID,VTX} -+ * bits), middle 7 bits of di->i_mode value can be garbage when these -+ * bits were not saved by bfs_write_inode(). -+ * Since we can't tell whether middle 7 bits are garbage, use only -+ * lower 12 bits (i.e. tolerate S_IS{UID,GID,VTX} bits possibly being -+ * garbage) and reconstruct S_IFMT bits for Linux environment from -+ * di->i_vtype value. -+ */ -+ inode->i_mode = 0x00000FFF & le32_to_cpu(di->i_mode); - if (le32_to_cpu(di->i_vtype) == BFS_VDIR) { - inode->i_mode |= S_IFDIR; - inode->i_op = &bfs_dir_inops; -@@ -71,6 +83,11 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino) - inode->i_op = &bfs_file_inops; - inode->i_fop = &bfs_file_operations; - inode->i_mapping->a_ops = &bfs_aops; -+ } else { -+ brelse(bh); -+ printf("Unknown vtype=%u %s:%08lx\n", -+ le32_to_cpu(di->i_vtype), inode->i_sb->s_id, ino); -+ goto error; - } - - BFS_I(inode)->i_sblock = le32_to_cpu(di->i_sblock); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl b/SPECS/kernel-rt/0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl new file mode 100644 index 000000000..5d6bbc4ac --- /dev/null +++ b/SPECS/kernel-rt/0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl @@ -0,0 +1,64 @@ +From 53c4a79d130e27d8bef41e702ef186c8e876cf9b Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Wed, 12 Nov 2025 17:23:24 +0100 +Subject: [PATCH 06/17] cpuidle: governors: teo: Drop redundant function + parameter + +The last no_poll parameter of teo_find_shallower_state() is always +false, so drop it. + +No intentional functional impact. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Tested-by: Christian Loehle +Link: https://patch.msgid.link/2253109.irdbgypaU6@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index cc74cecbea7f4..ada42e2ca7593 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -239,17 +239,15 @@ static bool teo_state_ok(int i, struct cpuidle_driver *drv) + * @dev: Target CPU. + * @state_idx: Index of the capping idle state. + * @duration_ns: Idle duration value to match. +- * @no_poll: Don't consider polling states. + */ + static int teo_find_shallower_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev, int state_idx, +- s64 duration_ns, bool no_poll) ++ s64 duration_ns) + { + int i; + + for (i = state_idx - 1; i >= 0; i--) { +- if (dev->states_usage[i].disable || +- (no_poll && drv->states[i].flags & CPUIDLE_FLAG_POLLING)) ++ if (dev->states_usage[i].disable) + continue; + + state_idx = i; +@@ -459,7 +457,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + * candidate state, a shallower one needs to be found. + */ + if (drv->states[idx].target_residency_ns > duration_ns) +- idx = teo_find_shallower_state(drv, dev, idx, duration_ns, false); ++ idx = teo_find_shallower_state(drv, dev, idx, duration_ns); + + /* + * If the selected state's target residency is below the tick length +@@ -487,7 +485,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + */ + if (idx > idx0 && + drv->states[idx].target_residency_ns > delta_tick) +- idx = teo_find_shallower_state(drv, dev, idx, delta_tick, false); ++ idx = teo_find_shallower_state(drv, dev, idx, delta_tick); + + out_tick: + *stop_tick = false; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-drm-i915-Drop-the-irqs_disabled-check.rt b/SPECS/kernel-rt/0006-drm-i915-Drop-the-irqs_disabled-check.rt deleted file mode 100644 index 2eb58e9c7..000000000 --- a/SPECS/kernel-rt/0006-drm-i915-Drop-the-irqs_disabled-check.rt +++ /dev/null @@ -1,45 +0,0 @@ -From 4b5149296b521b622cfce2240f87c0193c7866bb Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Fri, 1 Oct 2021 20:01:03 +0200 -Subject: [PATCH 6/9] drm/i915: Drop the irqs_disabled() check - -The !irqs_disabled() check triggers on PREEMPT_RT even with -i915_sched_engine::lock acquired. The reason is the lock is transformed -into a sleeping lock on PREEMPT_RT and does not disable interrupts. - -There is no need to check for disabled interrupts. The lockdep -annotation below already check if the lock has been acquired by the -caller and will yell if the interrupts are not disabled. - -Remove the !irqs_disabled() check. - -Reported-by: Maarten Lankhorst -Acked-by: Tvrtko Ursulin -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/i915_request.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c -index b9a2b2194c8f..e24798e4b44e 100644 ---- a/drivers/gpu/drm/i915/i915_request.c -+++ b/drivers/gpu/drm/i915/i915_request.c -@@ -608,7 +608,6 @@ bool __i915_request_submit(struct i915_request *request) - - RQ_TRACE(request, "\n"); - -- GEM_BUG_ON(!irqs_disabled()); - lockdep_assert_held(&engine->sched_engine->lock); - - /* -@@ -717,7 +716,6 @@ void __i915_request_unsubmit(struct i915_request *request) - */ - RQ_TRACE(request, "\n"); - -- GEM_BUG_ON(!irqs_disabled()); - lockdep_assert_held(&engine->sched_engine->lock); - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt b/SPECS/kernel-rt/0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt new file mode 100644 index 000000000..ec8285db2 --- /dev/null +++ b/SPECS/kernel-rt/0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt @@ -0,0 +1,35 @@ +From 7c2b752beb65a449e5bc914881ae5631168ca110 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Tue, 3 Oct 2023 21:37:21 +0200 +Subject: [PATCH 6/9] drm/i915/guc: Consider also RCU depth in busy loop. + +intel_guc_send_busy_loop() looks at in_atomic() and irqs_disabled() to +decide if it should busy-spin while waiting or if it may sleep. +Both checks will report false on PREEMPT_RT if sleeping spinlocks are +acquired leading to RCU splats while the function sleeps. + +Check also if RCU has been disabled. + +Reported-by: "John B. Wyatt IV" +Reviewed-by: Rodrigo Vivi +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h +index 053780f562c1a..b25fa8f4dc4bd 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h ++++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h +@@ -362,7 +362,7 @@ static inline int intel_guc_send_busy_loop(struct intel_guc *guc, + { + int err; + unsigned int sleep_period_ms = 1; +- bool not_atomic = !in_atomic() && !irqs_disabled(); ++ bool not_atomic = !in_atomic() && !irqs_disabled() && !rcu_preempt_depth(); + + /* + * FIXME: Have caller pass in if we are in an atomic context to avoid +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-i2c-atr-Remove-COMPILE_TEST-check.ipu b/SPECS/kernel-rt/0006-i2c-atr-Remove-COMPILE_TEST-check.ipu deleted file mode 100644 index d63a16b20..000000000 --- a/SPECS/kernel-rt/0006-i2c-atr-Remove-COMPILE_TEST-check.ipu +++ /dev/null @@ -1,34 +0,0 @@ -From 01ef232671beb365ed72d760c4f5aac0f1249cf8 Mon Sep 17 00:00:00 2001 -From: "Yew, Chang Ching" -Date: Wed, 21 Jan 2026 02:59:04 +0800 -Subject: [PATCH 6/6] i2c: atr: Remove COMPILE_TEST check - -The I2C Address Translator (ATR) driver was previously only visible when -COMPILE_TEST was enabled. Since the driver is functional and can be built -normally, drop the `if COMPILE_TEST` condition so that the ATR option is -always available in Kconfig. - -This allows platforms and drivers that actually use ATR hardware to enable -I2C ATR without relying on COMPILE_TEST. - -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/Kconfig | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig -index c232054fddd6..a7450ea9de41 100644 ---- a/drivers/i2c/Kconfig -+++ b/drivers/i2c/Kconfig -@@ -64,7 +64,7 @@ config I2C_MUX - source "drivers/i2c/muxes/Kconfig" - - config I2C_ATR -- tristate "I2C Address Translator (ATR) support" if COMPILE_TEST -+ tristate "I2C Address Translator (ATR) support" - help - Enable support for I2C Address Translator (ATR) chips. - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c b/SPECS/kernel-rt/0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c deleted file mode 100644 index 233c0ab93..000000000 --- a/SPECS/kernel-rt/0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c +++ /dev/null @@ -1,85 +0,0 @@ -From 780fd3c28a580c2657cfa58a430837220abf55e7 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:05 +0300 -Subject: [PATCH 06/11] i3c: mipi-i3c-hci: Change interrupt status prints to - dev_dbg() - -Change interrupt status prints from local DBG() macro to dev_dbg() in -order to make it easier to enable them without needing to recompile code -with DEBUG defined. - -While doing so, spell out the status register names as they are in the -specification to make it easier to differentiate between different -interrupt status registers. - -Since dynamic debug prints can include the line number remove the "(in)" -and "(out)" markers from the PIO interrupt status prints. - -Prefix the ring interrupt status print using "Ring %d" instead of "rh%d" -to make it uniform across all other prints showing the ring number. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250827103009.243771-2-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 2 +- - drivers/i3c/master/mipi-i3c-hci/dma.c | 3 ++- - drivers/i3c/master/mipi-i3c-hci/pio.c | 7 ++++--- - 3 files changed, 7 insertions(+), 5 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index 7a467ef65787..d532933ac7ab 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -553,7 +553,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) - - val = reg_read(INTR_STATUS); - reg_write(INTR_STATUS, val); -- DBG("INTR_STATUS = %#x", val); -+ dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); - - if (val) - result = IRQ_HANDLED; -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 09688ada4912..f5f5ab4db172 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -760,7 +760,8 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - - rh = &rings->headers[i]; - status = rh_reg_read(INTR_STATUS); -- DBG("rh%d status: %#x", i, status); -+ dev_dbg(&hci->master.dev, "Ring %d: RH_INTR_STATUS %#x", -+ i, status); - if (!status) - continue; - rh_reg_write(INTR_STATUS, status); -diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c -index 2fc71e696911..cde883137bc7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/pio.c -+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c -@@ -986,7 +986,8 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci) - - spin_lock(&pio->lock); - status = pio_reg_read(INTR_STATUS); -- DBG("(in) status: %#x/%#x", status, pio->enabled_irqs); -+ dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x", -+ status, pio->enabled_irqs); - status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS; - if (!status) { - spin_unlock(&pio->lock); -@@ -1023,8 +1024,8 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci) - pio->enabled_irqs &= ~STAT_CMD_QUEUE_READY; - - pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs); -- DBG("(out) status: %#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x", -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); - spin_unlock(&pio->lock); - return true; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-issei-host_client-add-dma-allocation-support.security b/SPECS/kernel-rt/0006-issei-host_client-add-dma-allocation-support.security new file mode 100644 index 000000000..adfea3980 --- /dev/null +++ b/SPECS/kernel-rt/0006-issei-host_client-add-dma-allocation-support.security @@ -0,0 +1,151 @@ +From 2b5fd680f307eaec54d036dc29e7d70316e82040 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Wed, 17 Dec 2025 09:31:31 +0200 +Subject: [PATCH 6/7] issei: host_client: add dma allocation support + +Add functions to allow dedicated DMA buffer allocated on current +hardware device per host client. + +Signed-off-by: Alexander Usyskin +--- + drivers/misc/issei/host_client.c | 55 ++++++++++++++++++++++++++++++++ + drivers/misc/issei/host_client.h | 13 ++++++++ + 2 files changed, 68 insertions(+) + +diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c +index 6b8dbe566f5f3..1c85d4ee05c0d 100644 +--- a/drivers/misc/issei/host_client.c ++++ b/drivers/misc/issei/host_client.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -12,6 +13,8 @@ + #include "host_client.h" + #include "fw_client.h" + ++#define ISSEI_HOST_DMA_MAX_SIZE SZ_32M ++ + static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) + { + return cl->fw_cl ? cl->fw_cl->id : 0; +@@ -104,6 +107,18 @@ static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_c + cl_dbg(idev, cl, "Disconnected\n"); + } + ++static void __issei_cl_dma_unmap(struct issei_host_client *cl) ++{ ++ if (!cl->dma_size) ++ return; ++ ++ dmam_free_coherent(cl->idev->parent, ++ cl->dma_size, cl->dma_vaddr, cl->dma_daddr); ++ cl->dma_size = 0; ++ cl->dma_vaddr = NULL; ++ cl->dma_daddr = 0; ++} ++ + static void issei_cl_init(struct issei_host_client *cl, struct issei_device *idev, + u16 id, struct file *fp) + { +@@ -179,6 +194,8 @@ void issei_cl_remove(struct issei_host_client *cl) + + __issei_cl_disconnect(idev, cl); + ++ __issei_cl_dma_unmap(cl); ++ + cl_dbg(idev, cl, "Removed\n"); + kfree(cl); + } +@@ -487,3 +504,41 @@ int issei_cl_check_write(struct issei_host_client *cl) + return 1; + return 0; + } ++ ++int issei_cl_dma_map(struct issei_host_client *cl, size_t size, ++ dma_addr_t *daddr, void **vaddr) ++{ ++ struct issei_device *idev = cl->idev; ++ ++ if (size == 0 || size > ISSEI_HOST_DMA_MAX_SIZE) { ++ cl_err(idev, cl, "The size is out of bounds."); ++ return -EINVAL; ++ } ++ ++ guard(mutex)(&idev->host_client_lock); ++ ++ if (cl->dma_size) { ++ cl_err(idev, cl, "DMA already allocated."); ++ return -EALREADY; ++ } ++ ++ cl->dma_vaddr = dmam_alloc_coherent(idev->parent, size, ++ &cl->dma_daddr, GFP_KERNEL); ++ if (!cl->dma_vaddr) ++ return -ENOMEM; ++ ++ cl->dma_size = size; ++ ++ *daddr = cl->dma_daddr; ++ *vaddr = cl->dma_vaddr; ++ return 0; ++} ++ ++void issei_cl_dma_unmap(struct issei_host_client *cl) ++{ ++ struct issei_device *idev = cl->idev; ++ ++ guard(mutex)(&idev->host_client_lock); ++ ++ __issei_cl_dma_unmap(cl); ++} +diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h +index 9f081bed190d2..ceee45b20143a 100644 +--- a/drivers/misc/issei/host_client.h ++++ b/drivers/misc/issei/host_client.h +@@ -3,6 +3,7 @@ + #ifndef _ISSEI_HOST_CLIENT_H_ + #define _ISSEI_HOST_CLIENT_H_ + ++#include + #include + #include + #include +@@ -32,6 +33,10 @@ enum issei_host_client_state { + * @read_wait: waitqueue for read object + * @read_data: received data pointer + * @read_data_size: received data size ++ * ++ * @dma_vaddr: allocated DMA buffer virtual address ++ * @dma_daddr: allocated DMA buffer physical address ++ * @dma_size: allocated DMA buffer size, zero if no buffer allocated + */ + struct issei_host_client { + struct list_head list; +@@ -48,6 +53,10 @@ struct issei_host_client { + wait_queue_head_t read_wait; + u8 *read_data; + size_t read_data_size; ++ ++ void *dma_vaddr; ++ dma_addr_t dma_daddr; ++ size_t dma_size; + }; + + struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp); +@@ -67,4 +76,8 @@ int issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t *buf_size); + int issei_cl_check_read(struct issei_host_client *cl); + int issei_cl_check_write(struct issei_host_client *cl); + ++int issei_cl_dma_map(struct issei_host_client *cl, size_t size, ++ dma_addr_t *daddr, void **vaddr); ++void issei_cl_dma_unmap(struct issei_host_client *cl); ++ + #endif /* ISSEI_HOST_CLIENT_H_ */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu b/SPECS/kernel-rt/0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu deleted file mode 100644 index c360df904..000000000 --- a/SPECS/kernel-rt/0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu +++ /dev/null @@ -1,43 +0,0 @@ -From 7d518c359a265f77443e1282c38fa576dc8a2fc2 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao -Date: Thu, 20 Mar 2025 16:41:37 +0800 -Subject: [PATCH 06/11] media: ipu: Dma sync at buffer_prepare callback as DMA - is non-coherent - -Test Platform: -PTLRVP -LNLRVP - -Signed-off-by: Bingbu Cao -Signed-off-by: zouxiaoh ---- - drivers/media/pci/intel/ipu7/ipu7-isys-queue.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -index 9cee0fb4440c..43955c6fa8cf 100644 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -@@ -92,7 +92,9 @@ static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, - static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - { - struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); - struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); - struct device *dev = &av->isys->adev->auxdev.dev; - u32 bytesperline = av->pix_fmt.bytesperline; - u32 height = av->pix_fmt.height; -@@ -107,6 +109,9 @@ static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - av->vdev.name, bytesperline, height); - vb2_set_plane_payload(vb, 0, bytesperline * height); - -+ /* assume IPU is not DMA coherent */ -+ ipu7_dma_sync_sgtable(isys->adev, sg); -+ - return 0; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss b/SPECS/kernel-rt/0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss deleted file mode 100644 index 51ec033aa..000000000 --- a/SPECS/kernel-rt/0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss +++ /dev/null @@ -1,45 +0,0 @@ -From 930d67dbe1c6fb97824f857ee19d81e7aea7a221 Mon Sep 17 00:00:00 2001 -From: Aapo Vienamo -Date: Thu, 7 Mar 2024 15:04:18 +0200 -Subject: [PATCH 06/16] mtd: core: Don't fail mtd_device_parse_register() if - OTP is unsupported - -Handle the case where -EOPNOTSUPP is returned from OTP driver. - -This addresses an issue that occurs with the Intel SPI flash controller, -which has a limited supported opcode set. Whilst the OTP functionality -is not available due to this restriction, other parts of the MTD -functionality of the device are intact. This change allows the driver -to gracefully handle the restriction by allowing the supported -functionality to remain available instead of failing the probe -altogether. - -Signed-off-by: Aapo Vienamo -Reviewed-by: Mika Westerberg ---- - drivers/mtd/mtdcore.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c -index 5ba9a741f5ac..2739d5a555c8 100644 ---- a/drivers/mtd/mtdcore.c -+++ b/drivers/mtd/mtdcore.c -@@ -1060,8 +1060,14 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, - - mtd_set_dev_defaults(mtd); - -+ /* -+ * Don't abort MTD init if OTP functionality is unsupported. The -+ * cleanup of the OTP init is contained within mtd_otp_nvmem_add(). -+ * Omitting goto out here is safe since the cleanup code there -+ * should be no-ops. -+ */ - ret = mtd_otp_nvmem_add(mtd); -- if (ret) -+ if (ret && ret != -EOPNOTSUPP) - goto out; - - if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet b/SPECS/kernel-rt/0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet new file mode 100644 index 000000000..469920aab --- /dev/null +++ b/SPECS/kernel-rt/0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet @@ -0,0 +1,68 @@ +From 7d8fc568184270a121d0aa1aab5d2efe706359d7 Mon Sep 17 00:00:00 2001 +From: Song Yoong Siang +Date: Tue, 2 Aug 2022 22:57:40 +0800 +Subject: [PATCH 06/18] net: stmmac: Adjust mac_capabilities for Intel mGbE + 2.5G mode + +In the case where kernel driver has no access to modify the clock rate +after it is increased by 2.5 times in the BIOS to support 2.5G mode, +link speeds other than 2.5Gbps are not supported. Therefore, this commit +remove 10 Mbps, 100 Mbps, and 1Gbps support from mac_capabilities list. + +Signed-off-by: Tan Tee Min +Signed-off-by: Song Yoong Siang +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 2 ++ + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++++ + include/linux/stmmac.h | 1 + + 3 files changed, 12 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index e74d00984b889..e2915815ee655 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -300,8 +300,10 @@ static void tgl_get_interfaces(struct stmmac_priv *priv, void *bsp_priv, + dev_info(priv->device, "Link Speed Mode: 2.5Gbps\n"); + priv->plat->mdio_bus_data->default_an_inband = false; + interface = PHY_INTERFACE_MODE_2500BASEX; ++ priv->plat->fixed_2G5_clock_rate = true; + } else { + interface = PHY_INTERFACE_MODE_SGMII; ++ priv->plat->fixed_2G5_clock_rate = false; + } + + __set_bit(interface, interfaces); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 2cd7548edd82f..f4b06854653fd 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1255,6 +1255,15 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) + if (!fwnode) + fwnode = dev_fwnode(priv->device); + ++ /* In the case where kernel driver has no access to modify the clock ++ * rate after it is increased by 2.5 times in the BIOS to support 2.5G ++ * mode, link speeds other than 2.5Gbps are not supported. Thus, remove ++ * them from mac_capabilities. ++ */ ++ if (priv->plat->fixed_2G5_clock_rate && priv->plat->max_speed == 2500) ++ priv->phylink_config.mac_capabilities &= ++ ~(MAC_10 | MAC_100 | MAC_1000); ++ + phylink = phylink_create(config, fwnode, priv->plat->phy_interface, + &stmmac_phylink_mac_ops); + if (IS_ERR(phylink)) +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index d7d6998fa5bd0..323021322a770 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -300,5 +300,6 @@ struct plat_stmmacenet_data { + const struct dwmac4_addrs *dwmac4_addrs; + unsigned int flags; + bool skip_reset; ++ bool fixed_2G5_clock_rate; + }; + #endif +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu b/SPECS/kernel-rt/0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu deleted file mode 100644 index 49ad47434..000000000 --- a/SPECS/kernel-rt/0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu +++ /dev/null @@ -1,112 +0,0 @@ -From ff009ddd8692d77eae757b9d5b593b3fdabdf524 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:38:44 +0800 -Subject: [PATCH 06/21] patch: staging add enable ENABLE_FW_OFFLINE_LOGGER - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-fw-isys.c | 59 +++++++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-fw-isys.h | 3 ++ - drivers/staging/media/ipu7/ipu7-isys.c | 4 ++ - 3 files changed, 66 insertions(+) - -diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c -index c98326bd9fee..2958e39a359e 100644 ---- a/drivers/staging/media/ipu7/ipu7-fw-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c -@@ -205,6 +205,65 @@ void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) - ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); - } - -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys) -+{ -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ void *token; -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ }; -+ -+ return 0; -+} -+ -+#endif - void ipu7_fw_isys_dump_stream_cfg(struct device *dev, - struct ipu7_insys_stream_cfg *cfg) - { -diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.h b/drivers/staging/media/ipu7/ipu7-fw-isys.h -index b556feda6b08..1235adc9694e 100644 ---- a/drivers/staging/media/ipu7/ipu7-fw-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.h -@@ -36,4 +36,7 @@ int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, - size_t size, u16 send_type); - struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); - void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys); -+#endif - #endif -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index 547d5fd6a036..dae8700dd7f5 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -1224,6 +1224,10 @@ int isys_isr_one(struct ipu7_bus_device *adev) - if (!isys->adev->syscom) - return 1; - -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_isys_get_log(isys); -+#endif -+ - resp = ipu7_fw_isys_get_resp(isys); - if (!resp) - return 1; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf b/SPECS/kernel-rt/0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf deleted file mode 100644 index e18272382..000000000 --- a/SPECS/kernel-rt/0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf +++ /dev/null @@ -1,78 +0,0 @@ -From 9ac64a4fcc7659344116d6b150578c42b75e032e Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 11 Aug 2025 17:00:32 +0800 -Subject: [PATCH 006/100] perf/x86: Add PERF_CAP_PEBS_TIMING_INFO flag - -IA32_PERF_CAPABILITIES.PEBS_TIMING_INFO[bit 17] is introduced to -indicate whether timed PEBS is supported. Timed PEBS adds a new "retired -latency" field in basic info group to show the timing info. Please find -detailed information about timed PEBS in section 8.4.1 "Timed Processor -Event Based Sampling" of "Intel Architecture Instruction Set Extensions -and Future Features". - -This patch adds PERF_CAP_PEBS_TIMING_INFO flag and KVM module leverages -this flag to expose timed PEBS feature to guest. - -Moreover, opportunistically refine the indents and make the macros -share consistent indents. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - arch/x86/include/asm/msr-index.h | 14 ++++++++------ - tools/arch/x86/include/asm/msr-index.h | 14 ++++++++------ - 2 files changed, 16 insertions(+), 12 deletions(-) - -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index b65c3ba5fa14..f627196eb796 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -315,12 +315,14 @@ - #define PERF_CAP_PT_IDX 16 - - #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 --#define PERF_CAP_PEBS_TRAP BIT_ULL(6) --#define PERF_CAP_ARCH_REG BIT_ULL(7) --#define PERF_CAP_PEBS_FORMAT 0xf00 --#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) --#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -- PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE) -+#define PERF_CAP_PEBS_TRAP BIT_ULL(6) -+#define PERF_CAP_ARCH_REG BIT_ULL(7) -+#define PERF_CAP_PEBS_FORMAT 0xf00 -+#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) -+#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17) -+#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -+ PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \ -+ PERF_CAP_PEBS_TIMING_INFO) - - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) -diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h -index 5cfb5d74dd5f..daebfd926f08 100644 ---- a/tools/arch/x86/include/asm/msr-index.h -+++ b/tools/arch/x86/include/asm/msr-index.h -@@ -315,12 +315,14 @@ - #define PERF_CAP_PT_IDX 16 - - #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 --#define PERF_CAP_PEBS_TRAP BIT_ULL(6) --#define PERF_CAP_ARCH_REG BIT_ULL(7) --#define PERF_CAP_PEBS_FORMAT 0xf00 --#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) --#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -- PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE) -+#define PERF_CAP_PEBS_TRAP BIT_ULL(6) -+#define PERF_CAP_ARCH_REG BIT_ULL(7) -+#define PERF_CAP_PEBS_FORMAT 0xf00 -+#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) -+#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17) -+#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -+ PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \ -+ PERF_CAP_PEBS_TIMING_INFO) - - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf b/SPECS/kernel-rt/0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf new file mode 100644 index 000000000..0b1b552c4 --- /dev/null +++ b/SPECS/kernel-rt/0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf @@ -0,0 +1,157 @@ +From 52e51ae23156389c49671eb14ab8a3cb224bd281 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 19 Nov 2025 12:54:50 -0800 +Subject: [PATCH 06/13] perf/x86/intel/uncore: Add CBB PMON support for Diamond + Rapids +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On DMR, PMON units inside the Core Building Block (CBB) are enumerated +separately from those in the Integrated Memory and I/O Hub (IMH). + +A new per-CBB MSR (0x710) is introduced for discovery table enumeration. + +For counter control registers, the tid_en bit (bit 16) exists on CBO, +SBO, and Santa, but it is not used by any events. Mark this bit as +reserved. + +Similarly, disallow extended umask (bits 32–63) on Santa and sNCU. + +Additionally, ignore broken SB2UCIE unit. + +Reviewed-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 2 + + arch/x86/events/intel/uncore.h | 1 + + arch/x86/events/intel/uncore_discovery.h | 2 + + arch/x86/events/intel/uncore_snbep.c | 52 ++++++++++++++++++++++-- + 4 files changed, 54 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index c07d2c88604ef..eb57eb7159959 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1837,6 +1837,8 @@ static const struct uncore_plat_init dmr_uncore_init __initconst = { + .domain[0].base_is_pci = true, + .domain[0].discovery_base = DMR_UNCORE_DISCOVERY_TABLE_DEVICE, + .domain[0].units_ignore = dmr_uncore_imh_units_ignore, ++ .domain[1].discovery_base = CBB_UNCORE_DISCOVERY_MSR, ++ .domain[1].units_ignore = dmr_uncore_cbb_units_ignore, + }; + + static const struct uncore_plat_init generic_uncore_init __initconst = { +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 1e4b3a22403c5..83d01a9cefc00 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -615,6 +615,7 @@ extern struct event_constraint uncore_constraint_empty; + extern int spr_uncore_units_ignore[]; + extern int gnr_uncore_units_ignore[]; + extern int dmr_uncore_imh_units_ignore[]; ++extern int dmr_uncore_cbb_units_ignore[]; + + /* uncore_snb.c */ + int snb_uncore_pci_init(void); +diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h +index 618788c30ac62..63b8f7634e42e 100644 +--- a/arch/x86/events/intel/uncore_discovery.h ++++ b/arch/x86/events/intel/uncore_discovery.h +@@ -2,6 +2,8 @@ + + /* Store the full address of the global discovery table */ + #define UNCORE_DISCOVERY_MSR 0x201e ++/* Base address of uncore perfmon discovery table for CBB domain */ ++#define CBB_UNCORE_DISCOVERY_MSR 0x710 + + /* Generic device ID of a discovery table device */ + #define UNCORE_DISCOVERY_TABLE_DEVICE 0x09a7 +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index 4b72560dc13ff..df173534637a6 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -6807,6 +6807,28 @@ static struct intel_uncore_type dmr_uncore_hamvf = { + .attr_update = uncore_alias_groups, + }; + ++static struct intel_uncore_type dmr_uncore_cbo = { ++ .name = "cbo", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_santa = { ++ .name = "santa", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cncu = { ++ .name = "cncu", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_sncu = { ++ .name = "sncu", ++ .attr_update = uncore_alias_groups, ++}; ++ + static struct intel_uncore_type dmr_uncore_ula = { + .name = "ula", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, +@@ -6814,6 +6836,20 @@ static struct intel_uncore_type dmr_uncore_ula = { + .attr_update = uncore_alias_groups, + }; + ++static struct intel_uncore_type dmr_uncore_dda = { ++ .name = "dda", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_sbo = { ++ .name = "sbo", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ + static struct intel_uncore_type dmr_uncore_ubr = { + .name = "ubr", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, +@@ -6902,10 +6938,15 @@ static struct intel_uncore_type *dmr_uncores[UNCORE_DMR_NUM_UNCORE_TYPES] = { + NULL, NULL, NULL, + NULL, NULL, + &dmr_uncore_hamvf, +- NULL, +- NULL, NULL, NULL, ++ &dmr_uncore_cbo, ++ &dmr_uncore_santa, ++ &dmr_uncore_cncu, ++ &dmr_uncore_sncu, + &dmr_uncore_ula, +- NULL, NULL, NULL, NULL, ++ &dmr_uncore_dda, ++ NULL, ++ &dmr_uncore_sbo, ++ NULL, + NULL, NULL, NULL, + &dmr_uncore_ubr, + NULL, +@@ -6923,6 +6964,11 @@ int dmr_uncore_imh_units_ignore[] = { + UNCORE_IGNORE_END + }; + ++int dmr_uncore_cbb_units_ignore[] = { ++ 0x25, /* SB2UCIE */ ++ UNCORE_IGNORE_END ++}; ++ + int dmr_uncore_pci_init(void) + { + uncore_pci_uncores = uncore_get_uncores(UNCORE_ACCESS_PCI, 0, NULL, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt b/SPECS/kernel-rt/0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt new file mode 100644 index 000000000..499e48f8b --- /dev/null +++ b/SPECS/kernel-rt/0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt @@ -0,0 +1,83 @@ +From cb1c72d420130019bdebc3528bc25a867b94f87a Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Wed, 5 Nov 2025 13:50:15 -0800 +Subject: [PATCH 6/6] platform/x86:intel/pmc: Enable SSRAM support for Wildcat + Lake +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Enable Wildcat Lake platforms to achieve PMC information from +Intel PMC SSRAM Telemetry driver and substate requirements data +from telemetry region. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251105215020.1984036-2-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/core.h | 2 ++ + drivers/platform/x86/intel/pmc/wcl.c | 18 ++++++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index 61c8d3c5faa0f..272fb4f57f346 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -304,6 +304,8 @@ enum ppfear_regs { + /* Wildcat Lake */ + #define WCL_PMC_LTR_RESERVED 0x1B64 + #define WCL_PCD_PMC_MMIO_REG_LEN 0x3178 ++#define WCL_NUM_S0IX_BLOCKER 94 ++#define WCL_BLK_REQ_OFFSET 50 + + /* SSRAM PMC Device ID */ + /* LNL */ +diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c +index 85e90a639e651..a45707e6364f2 100644 +--- a/drivers/platform/x86/intel/pmc/wcl.c ++++ b/drivers/platform/x86/intel/pmc/wcl.c +@@ -11,6 +11,9 @@ + + #include "core.h" + ++/* PMC SSRAM PMT Telemetry GUIDS */ ++#define PCDN_LPM_REQ_GUID 0x33747648 ++ + static const struct pmc_bit_map wcl_pcdn_pfear_map[] = { + {"PMC_0", BIT(0)}, + {"FUSE_OSSE", BIT(1)}, +@@ -453,6 +456,17 @@ static const struct pmc_reg_map wcl_pcdn_reg_map = { + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = wcl_pcdn_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, ++ .num_s0ix_blocker = WCL_NUM_S0IX_BLOCKER, ++ .blocker_req_offset = WCL_BLK_REQ_OFFSET, ++ .lpm_req_guid = PCDN_LPM_REQ_GUID, ++}; ++ ++static struct pmc_info wcl_pmc_info_list[] = { ++ { ++ .devid = PMC_DEVID_WCL_PCDN, ++ .map = &wcl_pcdn_reg_map, ++ }, ++ {} + }; + + #define WCL_NPU_PCI_DEV 0xfd3e +@@ -479,8 +493,12 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in + } + + struct pmc_dev_info wcl_pmc_dev = { ++ .pci_func = 2, ++ .regmap_list = wcl_pmc_info_list, + .map = &wcl_pcdn_reg_map, ++ .sub_req_show = &pmc_core_substate_blk_req_fops, + .suspend = cnl_suspend, + .resume = wcl_resume, + .init = wcl_core_init, ++ .sub_req = pmc_core_pmt_get_blk_sub_req, + }; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-rtnetlink-Add-return-value-check.ethernet b/SPECS/kernel-rt/0006-rtnetlink-Add-return-value-check.ethernet deleted file mode 100644 index d52d1b9b2..000000000 --- a/SPECS/kernel-rt/0006-rtnetlink-Add-return-value-check.ethernet +++ /dev/null @@ -1,36 +0,0 @@ -From d3e468e980806df6fb6e9e823ce703cdc3d5a65a Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Tue, 3 Aug 2021 08:49:34 +0800 -Subject: [PATCH 06/19] rtnetlink: Add return value check - -This patch add return value checking for both of the nla_put_u32() and -nla_put_u8() in rtnl_xdp_fill(). - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - net/core/rtnetlink.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -index e4716ed7643b..39d7c7c2ba26 100644 ---- a/net/core/rtnetlink.c -+++ b/net/core/rtnetlink.c -@@ -1771,8 +1771,12 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - if (!md_btf_id) - goto err_cancel; - -- nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); -- nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); -+ err = nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); -+ if (err) -+ goto err_cancel; -+ err = nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); -+ if (err) -+ goto err_cancel; - - nla_nest_end(skb, xdp); - return 0; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal b/SPECS/kernel-rt/0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal deleted file mode 100644 index 8f67d3762..000000000 --- a/SPECS/kernel-rt/0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal +++ /dev/null @@ -1,44 +0,0 @@ -From 46f7fde538aaa55443ee44c23e5fd9596f09cf6b Mon Sep 17 00:00:00 2001 -From: "Rafael J. Wysocki" -Date: Mon, 25 Aug 2025 15:27:46 +0200 -Subject: [PATCH 06/11] thermal: gov_step_wise: Clean up local variable - initialization - -Make the initialization of local variable throttle in -thermal_zone_trip_update() more straightforward. - -No intentional functional impact. - -Signed-off-by: Rafael J. Wysocki -Reviewed-by: Lukasz Luba -Link: https://patch.msgid.link/6203592.lOV4Wx5bFT@rafael.j.wysocki ---- - drivers/thermal/gov_step_wise.c | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c -index b7938bddd9a6..44945af3ce17 100644 ---- a/drivers/thermal/gov_step_wise.c -+++ b/drivers/thermal/gov_step_wise.c -@@ -82,16 +82,14 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip_desc *td, - int trip_threshold) - { -+ bool throttle = tz->temperature >= trip_threshold; - const struct thermal_trip *trip = &td->trip; - enum thermal_trend trend = get_tz_trend(tz, trip); - int trip_id = thermal_zone_trip_id(tz, trip); - struct thermal_instance *instance; -- bool throttle = false; - -- if (tz->temperature >= trip_threshold) { -- throttle = true; -+ if (throttle) - trace_thermal_zone_trip(tz, trip_id, trip->type); -- } - - dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", - trip_id, trip->type, trip_threshold, trend, throttle); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet b/SPECS/kernel-rt/0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet new file mode 100644 index 000000000..1c978d298 --- /dev/null +++ b/SPECS/kernel-rt/0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet @@ -0,0 +1,83 @@ +From 03189c50b96902c65b0a7f1bd39a3b720c0755ae Mon Sep 17 00:00:00 2001 +From: Saeed Mahameed +Date: Tue, 9 Apr 2019 14:52:02 -0700 +Subject: [PATCH 06/14] tools/bpf: Query XDP metadata BTF ID + +When dumping bpf net information, also query XDP MD BTF attributes: + +$ /usr/local/sbin/bpftool net +xdp: +mlx0(3) md_btf_id(1) md_btf_enabled(0) + +tc: + +flow_dissector: + +Signed-off-by: Saeed Mahameed +Signed-off-by: Aravindhan Gunasekaran +--- + tools/bpf/bpftool/netlink_dumper.c | 21 +++++++++++++++++---- + tools/include/uapi/linux/if_link.h | 2 ++ + 2 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/tools/bpf/bpftool/netlink_dumper.c b/tools/bpf/bpftool/netlink_dumper.c +index 0a3c7e96c797a..949779c3f9eeb 100644 +--- a/tools/bpf/bpftool/netlink_dumper.c ++++ b/tools/bpf/bpftool/netlink_dumper.c +@@ -29,23 +29,36 @@ static void xdp_dump_prog_id(struct nlattr **tb, int attr, + static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex, + const char *name) + { ++ unsigned char mode = XDP_ATTACHED_NONE; + struct nlattr *tb[IFLA_XDP_MAX + 1]; +- unsigned char mode; ++ unsigned char md_btf_enabled = 0; ++ unsigned int md_btf_id = 0; ++ bool attached; + + if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0) + return -1; + +- if (!tb[IFLA_XDP_ATTACHED]) ++ if (!tb[IFLA_XDP_ATTACHED] && !tb[IFLA_XDP_MD_BTF_ID]) + return 0; + +- mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); +- if (mode == XDP_ATTACHED_NONE) ++ if (tb[IFLA_XDP_ATTACHED]) ++ mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); ++ ++ if (tb[IFLA_XDP_MD_BTF_ID]) { ++ md_btf_id = libbpf_nla_getattr_u32(tb[IFLA_XDP_MD_BTF_ID]); ++ md_btf_enabled = libbpf_nla_getattr_u8(tb[IFLA_XDP_MD_BTF_STATE]); ++ } ++ ++ attached = (mode != XDP_ATTACHED_NONE); ++ if (!attached && !md_btf_id) + return 0; + + NET_START_OBJECT; + if (name) + NET_DUMP_STR("devname", "%s", name); + NET_DUMP_UINT("ifindex", "(%u)", ifindex); ++ NET_DUMP_UINT("md_btf_id", " md_btf_id(%d)", md_btf_id); ++ NET_DUMP_UINT("md_btf_enabled", " md_btf_enabled(%d)", md_btf_enabled); + + if (mode == XDP_ATTACHED_MULTI) { + if (json_output) { +diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h +index 7e46ca4cd31bb..4d5b143d48719 100644 +--- a/tools/include/uapi/linux/if_link.h ++++ b/tools/include/uapi/linux/if_link.h +@@ -1899,6 +1899,8 @@ enum { + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, ++ IFLA_XDP_MD_BTF_ID, ++ IFLA_XDP_MD_BTF_STATE, + __IFLA_XDP_MAX, + }; + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-tools-power-turbostat-Refactor-floating-point-printo.turbo b/SPECS/kernel-rt/0006-tools-power-turbostat-Refactor-floating-point-printo.turbo new file mode 100644 index 000000000..eef6544f3 --- /dev/null +++ b/SPECS/kernel-rt/0006-tools-power-turbostat-Refactor-floating-point-printo.turbo @@ -0,0 +1,161 @@ +From 3e9357c67083cec57193d01e1b154264e996177e Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 20:16:10 -0300 +Subject: [PATCH 06/21] tools/power turbostat: Refactor floating point printout + code + +Too many copies of (usually) the same printf code... + +Also, unify code for added-counter FORMAT_AVERAGE, +which was correct where it was tested, but neglected elsewhere. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 60 +++++++++++++-------------- + 1 file changed, 28 insertions(+), 32 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index f9b99940b2478..47cb723430389 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2736,6 +2736,10 @@ static inline int print_decimal_value(int width, int *printed, char *delim, unsi + else + return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + } ++static inline int print_float_value(int *printed, char *delim, double value) ++{ ++ return (sprintf(outp, "%s%0.2f", (*printed++ ? delim : ""), value)); ++} + + void print_header(char *delim) + { +@@ -3243,11 +3247,9 @@ int format_counters(PER_THREAD_PARAMS) + outp += print_decimal_value(mp->width, &printed, delim, t->counter[i]); + else if (mp->format == FORMAT_PERCENT) { + if (mp->type == COUNTER_USEC) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- t->counter[i] / interval_float / 10000); ++ outp += print_float_value(&printed, delim, t->counter[i] / interval_float / 10000); + else +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, 100.0 * t->counter[i] / tsc); + } + } + +@@ -3255,16 +3257,13 @@ int format_counters(PER_THREAD_PARAMS) + for (i = 0, pp = sys.perf_tp; pp; ++i, pp = pp->next) { + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, t->perf_counter[i]); +- else if (pp->format == FORMAT_DELTA) ++ else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, t->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) { + if (pp->type == COUNTER_USEC) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- t->perf_counter[i] / interval_float / 10000); ++ outp += print_float_value(&printed, delim, t->perf_counter[i] / interval_float / 10000); + else +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, 100.0 * t->perf_counter[i] / tsc); + } + } + +@@ -3320,20 +3319,18 @@ int format_counters(PER_THREAD_PARAMS) + outp += print_hex_value(mp->width, &printed, delim, c->counter[i]); + else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(mp->width, &printed, delim, c->counter[i]); +- else if (mp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->counter[i] / tsc); +- } ++ else if (mp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * c->counter[i] / tsc); + } + + /* Added perf Core counters */ + for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) { + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, c->perf_counter[i]); +- else if (pp->format == FORMAT_DELTA) ++ else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); +- else if (pp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->perf_counter[i] / tsc); +- } ++ else if (pp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * c->perf_counter[i] / tsc); + } + + /* Added PMT Core counters */ +@@ -3347,12 +3344,12 @@ int format_counters(PER_THREAD_PARAMS) + + case PMT_TYPE_XTAL_TIME: + value_converted = 100.0 * value_raw / crystal_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: + value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + } + } + +@@ -3500,26 +3497,25 @@ int format_counters(PER_THREAD_PARAMS) + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) + outp += print_hex_value(mp->width, &printed, delim, p->counter[i]); +- else if (mp->format == FORMAT_DELTA) +- outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); +- else if (mp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->counter[i] / tsc); +- } else if (mp->type == COUNTER_K2M) ++ else if (mp->type == COUNTER_K2M) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->counter[i] / 1000); ++ else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * p->counter[i] / tsc); + } + + /* Added perf Package Counters */ + for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) { + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); +- else if (pp->format == FORMAT_DELTA) { +- outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); +- } else if (pp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->perf_counter[i] / tsc); +- } else if (pp->type == COUNTER_K2M) { ++ else if (pp->type == COUNTER_K2M) + outp += + sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); +- } ++ else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); ++ else if (pp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * p->perf_counter[i] / tsc); + } + + /* Added PMT Package Counters */ +@@ -3533,12 +3529,12 @@ int format_counters(PER_THREAD_PARAMS) + + case PMT_TYPE_XTAL_TIME: + value_converted = 100.0 * value_raw / crystal_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: + value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi b/SPECS/kernel-rt/0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi new file mode 100644 index 000000000..9c1900661 --- /dev/null +++ b/SPECS/kernel-rt/0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi @@ -0,0 +1,45 @@ +From 543b5b749bd79a5cd3301151e3fa1aa95751014f Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Fri, 24 Oct 2025 11:52:32 -0700 +Subject: [PATCH 06/44] x86/cea: Export __this_cpu_ist_top_va() to KVM + +Export __this_cpu_ist_top_va() to allow KVM to retrieve the per-CPU +exception stack top. + +FRED introduced new fields in the host-state area of the VMCS for stack +levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively corresponding to +per-CPU exception stacks for #DB, NMI and #DF. KVM must populate these +fields each time a vCPU is loaded onto a CPU. + +Signed-off-by: Xin Li (Intel) +--- + arch/x86/mm/cpu_entry_area.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c +index b3d90f9cfbb11..e507621d5c209 100644 +--- a/arch/x86/mm/cpu_entry_area.c ++++ b/arch/x86/mm/cpu_entry_area.c +@@ -19,6 +19,11 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks); + DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks); + + /* ++ * FRED introduced new fields in the host-state area of the VMCS for ++ * stack levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively ++ * corresponding to per CPU stacks for #DB, NMI and #DF. KVM must ++ * populate these each time a vCPU is loaded onto a CPU. ++ * + * Typically invoked by entry code, so must be noinstr. + */ + noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack) +@@ -36,6 +41,7 @@ noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack) + { + return __this_cpu_ist_bottom_va(stack) + EXCEPTION_STKSZ; + } ++EXPORT_SYMBOL_FOR_MODULES(__this_cpu_ist_top_va, "kvm-intel"); + + static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac b/SPECS/kernel-rt/0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac deleted file mode 100644 index 50e959c82..000000000 --- a/SPECS/kernel-rt/0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac +++ /dev/null @@ -1,56 +0,0 @@ -From e35a77a69c52d16e62a1f6c041fd208bf985df4e Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Thu, 24 Jul 2025 14:17:23 +0800 -Subject: [PATCH 07/13] EDAC/i10nm: Reallocate skx_dev list if preconfigured - cnt != runtime cnt - -Ideally, read the present DDR memory controller count first and then -allocate the skx_dev list using this count. However, this approach -requires adding a significant amount of code similar to -skx_get_all_bus_mappings() to obtain the PCI bus mappings for the first -socket and use these mappings along with the related PCI register offset -to read the memory controller count. - -Given that the Granite Rapids CPU is the only one that can detect the -count of memory controllers at runtime (other CPUs use the count in the -configuration data), to reduce code complexity, reallocate the skx_dev -list only if the preconfigured count of DDR memory controllers differs -from the count read at runtime for Granite Rapids CPU. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/i10nm_base.c | 13 +++++++------ - 1 file changed, 7 insertions(+), 6 deletions(-) - -diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c -index 9d00f247f4e0..2010a47149f4 100644 ---- a/drivers/edac/i10nm_base.c -+++ b/drivers/edac/i10nm_base.c -@@ -468,17 +468,18 @@ static int i10nm_get_imc_num(struct res_config *cfg) - return -ENODEV; - } - -- if (imc_num > I10NM_NUM_DDR_IMC) { -- i10nm_printk(KERN_ERR, "Need to make I10NM_NUM_DDR_IMC >= %d\n", imc_num); -- return -EINVAL; -- } -- - if (cfg->ddr_imc_num != imc_num) { - /* -- * Store the number of present DDR memory controllers. -+ * Update the configuration data to reflect the number of -+ * present DDR memory controllers. - */ - cfg->ddr_imc_num = imc_num; - edac_dbg(2, "Set DDR MC number: %d", imc_num); -+ -+ /* Release and reallocate skx_dev list with the updated number. */ -+ skx_remove(); -+ if (skx_get_all_bus_mappings(cfg, &i10nm_edac_list) <= 0) -+ return -ENODEV; - } - - return 0; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security b/SPECS/kernel-rt/0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security new file mode 100644 index 000000000..fffdd320b --- /dev/null +++ b/SPECS/kernel-rt/0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security @@ -0,0 +1,462 @@ +From 3140756d9ae49d63b6a6f478a8a5451f5f17b5e4 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Wed, 19 Jan 2022 23:08:30 +0200 +Subject: [PATCH 7/8] INTEL_DII: mei: add check for offline bit in every + register access + +Added check for offline in every register access function. +When offline bit is set the driver should not access any mei hw. + +Co-developed-by: Tomas Winkler +Co-developed-by: Vitaly Lubart +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/hw-me.c | 151 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 144 insertions(+), 7 deletions(-) + +diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c +index 78b3050f2f801..803466460dbdb 100644 +--- a/drivers/misc/mei/hw-me.c ++++ b/drivers/misc/mei/hw-me.c +@@ -59,6 +59,9 @@ static inline void mei_me_reg_write(const struct mei_me_hw *hw, + */ + static inline u32 mei_me_mecbrw_read(const struct mei_device *dev) + { ++ if (dev->parent->offline) ++ return 0; ++ + return mei_me_reg_read(to_me_hw(dev), ME_CB_RW); + } + +@@ -70,6 +73,9 @@ static inline u32 mei_me_mecbrw_read(const struct mei_device *dev) + */ + static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data) + { ++ if (dev->parent->offline) ++ return; ++ + mei_me_reg_write(to_me_hw(dev), H_CB_WW, data); + } + +@@ -84,6 +90,9 @@ static inline u32 mei_me_mecsr_read(const struct mei_device *dev) + { + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA); + trace_mei_reg_read(&dev->dev, "ME_CSR_HA", ME_CSR_HA, reg); + +@@ -101,6 +110,9 @@ static inline u32 mei_hcsr_read(const struct mei_device *dev) + { + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_reg_read(to_me_hw(dev), H_CSR); + trace_mei_reg_read(&dev->dev, "H_CSR", H_CSR, reg); + +@@ -115,6 +127,9 @@ static inline u32 mei_hcsr_read(const struct mei_device *dev) + */ + static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) + { ++ if (dev->parent->offline) ++ return; ++ + trace_mei_reg_write(&dev->dev, "H_CSR", H_CSR, reg); + mei_me_reg_write(to_me_hw(dev), H_CSR, reg); + } +@@ -128,6 +143,9 @@ static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) + */ + static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) + { ++ if (dev->parent->offline) ++ return; ++ + reg &= ~H_CSR_IS_MASK; + mei_hcsr_write(dev, reg); + } +@@ -141,6 +159,9 @@ static inline void mei_hcsr_set_hig(struct mei_device *dev) + { + u32 hcsr; + ++ if (dev->parent->offline) ++ return; ++ + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); + } +@@ -156,6 +177,9 @@ static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) + { + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C); + trace_mei_reg_read(&dev->dev, "H_D0I3C", H_D0I3C, reg); + +@@ -170,6 +194,9 @@ static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) + */ + static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) + { ++ if (dev->parent->offline) ++ return; ++ + trace_mei_reg_write(&dev->dev, "H_D0I3C", H_D0I3C, reg); + mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg); + } +@@ -186,6 +213,9 @@ static int mei_me_trc_status(struct mei_device *dev, u32 *trc) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return -EOPNOTSUPP; ++ + if (!hw->cfg->hw_trc_supported) + return -EOPNOTSUPP; + +@@ -211,6 +241,9 @@ static int mei_me_fw_status(struct mei_device *dev, + int ret; + int i; + ++ if (dev->parent->offline) ++ return -EINVAL; ++ + if (!fw_status || !hw->read_fws) + return -EINVAL; + +@@ -243,6 +276,9 @@ static int mei_me_hw_config(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr, reg; + ++ if (dev->parent->offline) ++ return -EINVAL; ++ + if (WARN_ON(!hw->read_fws)) + return -EINVAL; + +@@ -295,6 +331,9 @@ static inline u32 me_intr_src(u32 hcsr) + */ + static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) + { ++ if (dev->parent->offline) ++ return; ++ + hcsr &= ~H_CSR_IE_MASK; + mei_hcsr_set(dev, hcsr); + } +@@ -307,6 +346,9 @@ static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) + */ + static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) + { ++ if (dev->parent->offline) ++ return; ++ + if (me_intr_src(hcsr)) + mei_hcsr_write(dev, hcsr); + } +@@ -318,7 +360,12 @@ static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) + */ + static void mei_me_intr_clear(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + + me_intr_clear(dev, hcsr); + } +@@ -331,6 +378,9 @@ static void mei_me_intr_enable(struct mei_device *dev) + { + u32 hcsr; + ++ if (dev->parent->offline) ++ return; ++ + if (mei_me_hw_use_polling(to_me_hw(dev))) + return; + +@@ -345,8 +395,12 @@ static void mei_me_intr_enable(struct mei_device *dev) + */ + static void mei_me_intr_disable(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; + ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + me_intr_disable(dev, hcsr); + } + +@@ -359,6 +413,9 @@ static void mei_me_synchronize_irq(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (mei_me_hw_use_polling(hw)) + return; + +@@ -372,7 +429,12 @@ static void mei_me_synchronize_irq(struct mei_device *dev) + */ + static void mei_me_hw_reset_release(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + + hcsr |= H_IG; + hcsr &= ~H_RST; +@@ -386,7 +448,12 @@ static void mei_me_hw_reset_release(struct mei_device *dev) + */ + static void mei_me_host_set_ready(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + + if (!mei_me_hw_use_polling(to_me_hw(dev))) + hcsr |= H_CSR_IE_MASK; +@@ -403,7 +470,12 @@ static void mei_me_host_set_ready(struct mei_device *dev) + */ + static bool mei_me_host_is_ready(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return true; ++ ++ hcsr = mei_hcsr_read(dev); + + return (hcsr & H_RDY) == H_RDY; + } +@@ -416,7 +488,12 @@ static bool mei_me_host_is_ready(struct mei_device *dev) + */ + static bool mei_me_hw_is_ready(struct mei_device *dev) + { +- u32 mecsr = mei_me_mecsr_read(dev); ++ u32 mecsr; ++ ++ if (dev->parent->offline) ++ return true; ++ ++ mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RDY_HRA) == ME_RDY_HRA; + } +@@ -429,7 +506,12 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) + */ + static bool mei_me_hw_is_resetting(struct mei_device *dev) + { +- u32 mecsr = mei_me_mecsr_read(dev); ++ u32 mecsr; ++ ++ if (dev->parent->offline) ++ return false; ++ ++ mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RST_HRA) == ME_RST_HRA; + } +@@ -444,6 +526,9 @@ static void mei_gsc_pxp_check(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 fwsts5 = 0; + ++ if (dev->parent->offline) ++ return; ++ + if (!kind_is_gsc(dev) && !kind_is_gscfi(dev)) + return; + +@@ -504,6 +589,9 @@ static int mei_me_hw_start(struct mei_device *dev) + { + int ret = mei_me_hw_ready_wait(dev); + ++ if (dev->parent->offline) ++ return 0; ++ + if ((kind_is_gsc(dev) || kind_is_gscfi(dev)) && + dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED) + dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE; +@@ -605,6 +693,9 @@ static int mei_me_hbuf_write(struct mei_device *dev, + u32 dw_cnt; + int empty_slots; + ++ if (dev->parent->offline) ++ return -EINVAL; ++ + if (WARN_ON(!hdr || hdr_len & 0x3)) + return -EINVAL; + +@@ -689,6 +780,9 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, + { + u32 *reg_buf = (u32 *)buffer; + ++ if (dev->parent->offline) ++ return 0; ++ + for (; buffer_length >= MEI_SLOT_SIZE; buffer_length -= MEI_SLOT_SIZE) + *reg_buf++ = mei_me_mecbrw_read(dev); + +@@ -712,6 +806,9 @@ static void mei_me_pg_set(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + ++ if (dev->parent->offline) ++ return; ++ + reg = mei_me_reg_read(hw, H_HPG_CSR); + trace_mei_reg_read(&dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); + +@@ -731,6 +828,9 @@ static void mei_me_pg_unset(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + ++ if (dev->parent->offline) ++ return; ++ + reg = mei_me_reg_read(hw, H_HPG_CSR); + trace_mei_reg_read(&dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); + +@@ -754,6 +854,9 @@ static int mei_me_pg_legacy_enter_sync(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + int ret; + ++ if (dev->parent->offline) ++ return 0; ++ + dev->pg_event = MEI_PG_EVENT_WAIT; + + ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD); +@@ -791,6 +894,9 @@ static int mei_me_pg_legacy_exit_sync(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + int ret; + ++ if (dev->parent->offline) ++ return 0; ++ + if (dev->pg_event == MEI_PG_EVENT_RECEIVED) + goto reply; + +@@ -936,6 +1042,9 @@ static int mei_me_d0i3_enter_sync(struct mei_device *dev) + int ret; + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + /* we are in d0i3, nothing to do */ +@@ -1011,6 +1120,9 @@ static int mei_me_d0i3_enter(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + /* we are in d0i3, nothing to do */ +@@ -1039,6 +1151,9 @@ static int mei_me_d0i3_exit_sync(struct mei_device *dev) + int ret; + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + dev->pg_event = MEI_PG_EVENT_INTR_WAIT; + + reg = mei_me_d0i3c_read(dev); +@@ -1090,6 +1205,9 @@ static void mei_me_pg_legacy_intr(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT) + return; + +@@ -1109,6 +1227,9 @@ static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && + (intr_source & H_D0I3C_IS)) { + dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; +@@ -1150,6 +1271,9 @@ static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (hw->d0i3_supported) + mei_me_d0i3_intr(dev, intr_source); + else +@@ -1167,6 +1291,9 @@ int mei_me_pg_enter_sync(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return 0; ++ + if (hw->d0i3_supported) + return mei_me_d0i3_enter_sync(dev); + else +@@ -1184,6 +1311,9 @@ int mei_me_pg_exit_sync(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return 0; ++ + if (hw->d0i3_supported) + return mei_me_d0i3_exit_sync(dev); + else +@@ -1274,6 +1404,9 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) + struct mei_device *dev = (struct mei_device *)dev_id; + u32 hcsr; + ++ if (dev->parent->offline) ++ return IRQ_HANDLED; ++ + hcsr = mei_hcsr_read(dev); + if (!me_intr_src(hcsr)) + return IRQ_NONE; +@@ -1306,6 +1439,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) + int rets = 0; + + dev_dbg(&dev->dev, "function called after ISR to handle the interrupt processing.\n"); ++ ++ if (dev->parent->offline) ++ return IRQ_HANDLED; ++ + /* initialize our complete list */ + mutex_lock(&dev->device_lock); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi b/SPECS/kernel-rt/0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi new file mode 100644 index 000000000..40b0a972d --- /dev/null +++ b/SPECS/kernel-rt/0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi @@ -0,0 +1,193 @@ +From 4e2ef01a100b0278869dd565367f3cb6c7c25d66 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 31 Aug 2023 01:52:58 -0700 +Subject: [PATCH 07/44] KVM: VMX: Initialize VMCS FRED fields + +Initialize host VMCS FRED fields with host FRED MSRs' value and +guest VMCS FRED fields to 0. + +FRED CPU state is managed in 9 new FRED MSRs: + IA32_FRED_CONFIG, + IA32_FRED_STKLVLS, + IA32_FRED_RSP0, + IA32_FRED_RSP1, + IA32_FRED_RSP2, + IA32_FRED_RSP3, + IA32_FRED_SSP1, + IA32_FRED_SSP2, + IA32_FRED_SSP3, +as well as a few existing CPU registers and MSRs: + CR4.FRED, + IA32_STAR, + IA32_KERNEL_GS_BASE, + IA32_PL0_SSP (also known as IA32_FRED_SSP0). + +CR4, IA32_KERNEL_GS_BASE and IA32_STAR are already well managed. +Except IA32_FRED_RSP0 and IA32_FRED_SSP0, all other FRED CPU state +MSRs have corresponding VMCS fields in both the host-state and +guest-state areas. So KVM just needs to initialize them, and with +proper VM entry/exit FRED controls, a FRED CPU will keep tracking +host and guest FRED CPU state in VMCS automatically. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Initialize host SSP[1-3] to 0s in vmx_set_constant_host_state() + because Linux doesn't support kernel shadow stacks (Chao Gao). + +Change in v3: +* Use structure kvm_host_values to keep host fred config & stack levels + (Sean Christopherson). + +Changes in v2: +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() to decouple + KVM's capability to virtualize a feature and host's enabling of a + feature (Chao Gao). +* Move guest FRED state init into __vmx_vcpu_reset() (Chao Gao). +--- + arch/x86/include/asm/vmx.h | 32 ++++++++++++++++++++++++++++++++ + arch/x86/kvm/vmx/vmx.c | 36 ++++++++++++++++++++++++++++++++++++ + arch/x86/kvm/x86.h | 3 +++ + 3 files changed, 71 insertions(+) + +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index dd79d027ea700..6f8b8947c60cd 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -293,12 +293,44 @@ enum vmcs_field { + GUEST_BNDCFGS_HIGH = 0x00002813, + GUEST_IA32_RTIT_CTL = 0x00002814, + GUEST_IA32_RTIT_CTL_HIGH = 0x00002815, ++ GUEST_IA32_FRED_CONFIG = 0x0000281a, ++ GUEST_IA32_FRED_CONFIG_HIGH = 0x0000281b, ++ GUEST_IA32_FRED_RSP1 = 0x0000281c, ++ GUEST_IA32_FRED_RSP1_HIGH = 0x0000281d, ++ GUEST_IA32_FRED_RSP2 = 0x0000281e, ++ GUEST_IA32_FRED_RSP2_HIGH = 0x0000281f, ++ GUEST_IA32_FRED_RSP3 = 0x00002820, ++ GUEST_IA32_FRED_RSP3_HIGH = 0x00002821, ++ GUEST_IA32_FRED_STKLVLS = 0x00002822, ++ GUEST_IA32_FRED_STKLVLS_HIGH = 0x00002823, ++ GUEST_IA32_FRED_SSP1 = 0x00002824, ++ GUEST_IA32_FRED_SSP1_HIGH = 0x00002825, ++ GUEST_IA32_FRED_SSP2 = 0x00002826, ++ GUEST_IA32_FRED_SSP2_HIGH = 0x00002827, ++ GUEST_IA32_FRED_SSP3 = 0x00002828, ++ GUEST_IA32_FRED_SSP3_HIGH = 0x00002829, + HOST_IA32_PAT = 0x00002c00, + HOST_IA32_PAT_HIGH = 0x00002c01, + HOST_IA32_EFER = 0x00002c02, + HOST_IA32_EFER_HIGH = 0x00002c03, + HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04, + HOST_IA32_PERF_GLOBAL_CTRL_HIGH = 0x00002c05, ++ HOST_IA32_FRED_CONFIG = 0x00002c08, ++ HOST_IA32_FRED_CONFIG_HIGH = 0x00002c09, ++ HOST_IA32_FRED_RSP1 = 0x00002c0a, ++ HOST_IA32_FRED_RSP1_HIGH = 0x00002c0b, ++ HOST_IA32_FRED_RSP2 = 0x00002c0c, ++ HOST_IA32_FRED_RSP2_HIGH = 0x00002c0d, ++ HOST_IA32_FRED_RSP3 = 0x00002c0e, ++ HOST_IA32_FRED_RSP3_HIGH = 0x00002c0f, ++ HOST_IA32_FRED_STKLVLS = 0x00002c10, ++ HOST_IA32_FRED_STKLVLS_HIGH = 0x00002c11, ++ HOST_IA32_FRED_SSP1 = 0x00002c12, ++ HOST_IA32_FRED_SSP1_HIGH = 0x00002c13, ++ HOST_IA32_FRED_SSP2 = 0x00002c14, ++ HOST_IA32_FRED_SSP2_HIGH = 0x00002c15, ++ HOST_IA32_FRED_SSP3 = 0x00002c16, ++ HOST_IA32_FRED_SSP3_HIGH = 0x00002c17, + PIN_BASED_VM_EXEC_CONTROL = 0x00004000, + CPU_BASED_VM_EXEC_CONTROL = 0x00004002, + EXCEPTION_BITMAP = 0x00004004, +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 90cbc73b21288..94f88bf8bb785 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1459,6 +1459,15 @@ void vmx_vcpu_load_vmcs(struct kvm_vcpu *vcpu, int cpu) + (unsigned long)(cpu_entry_stack(cpu) + 1)); + } + ++ /* Per-CPU FRED MSRs */ ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++#ifdef CONFIG_X86_64 ++ vmcs_write64(HOST_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); ++ vmcs_write64(HOST_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); ++ vmcs_write64(HOST_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); ++#endif ++ } ++ + vmx->loaded_vmcs->cpu = cpu; + } + } +@@ -4311,6 +4320,17 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) + */ + vmcs_write16(HOST_DS_SELECTOR, 0); + vmcs_write16(HOST_ES_SELECTOR, 0); ++ ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++ /* FRED CONFIG and STKLVLS are the same on all CPUs */ ++ vmcs_write64(HOST_IA32_FRED_CONFIG, kvm_host.fred_config); ++ vmcs_write64(HOST_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); ++ ++ /* Linux doesn't support kernel shadow stacks, thus SSPs are 0s */ ++ vmcs_write64(HOST_IA32_FRED_SSP1, 0); ++ vmcs_write64(HOST_IA32_FRED_SSP2, 0); ++ vmcs_write64(HOST_IA32_FRED_SSP3, 0); ++ } + #else + vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS); /* 22.2.4 */ + vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS); /* 22.2.4 */ +@@ -4822,6 +4842,17 @@ static void init_vmcs(struct vcpu_vmx *vmx) + } + + vmx_setup_uret_msrs(vmx); ++ ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, 0); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, 0); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, 0); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, 0); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, 0); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, 0); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, 0); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, 0); ++ } + } + + static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu) +@@ -8706,6 +8737,11 @@ __init int vmx_hardware_setup(void) + + kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; + ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++ rdmsrl(MSR_IA32_FRED_CONFIG, kvm_host.fred_config); ++ rdmsrl(MSR_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); ++ } ++ + return r; + } + +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index f3dc77f006f90..0c1fbf75442b7 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -52,6 +52,9 @@ struct kvm_host_values { + u64 xss; + u64 s_cet; + u64 arch_capabilities; ++ ++ u64 fred_config; ++ u64 fred_stklvls; + }; + + void kvm_spurious_fault(void); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi b/SPECS/kernel-rt/0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi deleted file mode 100644 index 2a02004cb..000000000 --- a/SPECS/kernel-rt/0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi +++ /dev/null @@ -1,95 +0,0 @@ -From cde72e614a686e09074d412ac5ebde282d14e944 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 31 Aug 2023 00:56:30 -0700 -Subject: [PATCH 07/44] KVM: VMX: Set FRED MSR intercepts - -On a userspace MSR filter change, set FRED MSR intercepts. - -8 FRED MSRs, i.e., MSR_IA32_FRED_RSP[123], MSR_IA32_FRED_STKLVLS, -MSR_IA32_FRED_SSP[123] and MSR_IA32_FRED_CONFIG, are all safe to -be passthrough, because they all have a pair of corresponding host -and guest VMCS fields. - -Both MSR_IA32_FRED_RSP0 and MSR_IA32_FRED_SSP0 are dedicated for -userspace event delivery only, IOW they are NOT used in any kernel -event delivery and the execution of ERETS. Thus KVM can run safely -with guest values in the two MSRs. As a result, save and restore of -their guest values are deferred until vCPU context switch and their -host values are restored upon host returning to userspace. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Skip execution of vmx_set_intercept_for_fred_msr() if FRED is - not available or enabled (Sean). -* Use 'intercept' as the variable name to indicate whether MSR - interception should be enabled (Sean). -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/vmx/vmx.c | 39 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 39 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 4a183ffb2154..2a40dfe9f034 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4177,6 +4177,43 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) - MSR_TYPE_RW, intercept); - } - -+static void vmx_set_intercept_for_fred_msr(struct kvm_vcpu *vcpu) -+{ -+ bool intercept = !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED); -+ -+ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) -+ return; -+ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP1, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP2, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP3, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_STKLVLS, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP1, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP2, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP3, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_CONFIG, MSR_TYPE_RW, intercept); -+ -+ /* -+ * MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) are -+ * designated for event delivery while executing in userspace. Since -+ * KVM operates exclusively in kernel mode (the CPL is always 0 after -+ * any VM exit), KVM can safely retain and operate with the guest-defined -+ * values for MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP. -+ * -+ * Therefore, interception of MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP -+ * is not required. -+ * -+ * Note, save and restore of MSR_IA32_PL0_SSP belong to CET supervisor -+ * context management. However the FRED SSP MSRs, including -+ * MSR_IA32_PL0_SSP, are supported by any processor that enumerates FRED. -+ * If such a processor does not support CET, FRED transitions will not -+ * use the MSRs, but the MSRs would still be accessible using MSR-access -+ * instructions (e.g., RDMSR, WRMSR). -+ */ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP0, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL0_SSP, MSR_TYPE_RW, intercept); -+} -+ - static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - bool set; -@@ -4245,6 +4282,8 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_U_CET, MSR_TYPE_RW, set); - vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, set); - } -+ -+ vmx_set_intercept_for_fred_msr(vcpu); - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be - * filtered by userspace. --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet b/SPECS/kernel-rt/0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet deleted file mode 100644 index e8c3c412a..000000000 --- a/SPECS/kernel-rt/0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet +++ /dev/null @@ -1,51 +0,0 @@ -From cc0952258b17a0cb7dfa675a97f736a2efc4d6ff Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 4 Jul 2025 01:49:37 -0700 -Subject: [PATCH 07/24] KVM: x86: Report XSS as to-be-saved if there are - supported features - -Add MSR_IA32_XSS to list of MSRs reported to userspace if supported_xss -is non-zero, i.e. KVM supports at least one XSS based feature. - -Before enabling CET virtualization series, guest IA32_MSR_XSS is -guaranteed to be 0, i.e., XSAVES/XRSTORS is executed in non-root mode -with XSS == 0, which equals to the effect of XSAVE/XRSTOR. - -Signed-off-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index f2832a644226..3445a446e4ba 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -339,7 +339,7 @@ static const u32 msrs_to_save_base[] = { - MSR_IA32_RTIT_ADDR3_A, MSR_IA32_RTIT_ADDR3_B, - MSR_IA32_UMWAIT_CONTROL, - -- MSR_IA32_XFD, MSR_IA32_XFD_ERR, -+ MSR_IA32_XFD, MSR_IA32_XFD_ERR, MSR_IA32_XSS, - }; - - static const u32 msrs_to_save_pmu[] = { -@@ -7426,6 +7426,10 @@ static void kvm_probe_msr_to_save(u32 msr_index) - if (!(kvm_get_arch_capabilities() & ARCH_CAP_TSX_CTRL_MSR)) - return; - break; -+ case MSR_IA32_XSS: -+ if (!kvm_caps.supported_xss) -+ return; -+ break; - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl b/SPECS/kernel-rt/0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl new file mode 100644 index 000000000..8ff55a8fa --- /dev/null +++ b/SPECS/kernel-rt/0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl @@ -0,0 +1,49 @@ +From 3ee8d1f2be3fb17d4775de5f7cf84f8c6f2ea086 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Wed, 12 Nov 2025 17:24:40 +0100 +Subject: [PATCH 07/17] cpuidle: governors: teo: Use s64 consistently in + teo_update() + +Two local variables in teo_update() are defined as u64, but their +values are then compared with s64 values, so it is more consistent +to use s64 as their data type. + +No intentional functional impact. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Tested-by: Christian Loehle +Link: https://patch.msgid.link/3026616.e9J7NaK4W3@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index ada42e2ca7593..88ed47e868b9c 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -157,8 +157,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + { + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + int i, idx_timer = 0, idx_duration = 0; +- s64 target_residency_ns; +- u64 measured_ns; ++ s64 target_residency_ns, measured_ns; + + cpu_data->short_idles -= cpu_data->short_idles >> DECAY_SHIFT; + +@@ -167,9 +166,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + * If one of the safety nets has triggered, assume that this + * might have been a long sleep. + */ +- measured_ns = U64_MAX; ++ measured_ns = S64_MAX; + } else { +- u64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns; ++ s64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns; + + measured_ns = dev->last_residency_ns; + /* +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-drm-i915-Consider-RCU-read-section-as-atomic.rt b/SPECS/kernel-rt/0007-drm-i915-Consider-RCU-read-section-as-atomic.rt new file mode 100644 index 000000000..7817c3940 --- /dev/null +++ b/SPECS/kernel-rt/0007-drm-i915-Consider-RCU-read-section-as-atomic.rt @@ -0,0 +1,37 @@ +From 19b951398ea5f6225cc8c67d2e5a63c7649c9b7c Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Mon, 14 Jul 2025 17:39:53 +0200 +Subject: [PATCH 7/9] drm/i915: Consider RCU read section as atomic. + +Locking and/ or running inside interrupt handler will not lead to an +atomic section on PREEMPT_RT. The RCU read section needs to be +considered because all locks, which become sleeping locks on +PREEMPT_RT, start a RCU read section. Scheduling/ sleeping while within +a RCU read section is invalid. + +Check for also for RCU read section in stop_timeout() to determine if it +is safe to sleep. + +Reviewed-by: Rodrigo Vivi +Link: https://lore.kernel.org/r/20250714153954.629393-9-bigeasy@linutronix.de +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/gt/intel_engine_cs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c +index b721bbd233567..f5a6143ea8a24 100644 +--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c ++++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c +@@ -1609,7 +1609,7 @@ u64 intel_engine_get_last_batch_head(const struct intel_engine_cs *engine) + + static unsigned long stop_timeout(const struct intel_engine_cs *engine) + { +- if (in_atomic() || irqs_disabled()) /* inside atomic preempt-reset? */ ++ if (in_atomic() || irqs_disabled() || rcu_preempt_depth()) /* inside atomic preempt-reset? */ + return 0; + + /* +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt b/SPECS/kernel-rt/0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt deleted file mode 100644 index af1d5d6ce..000000000 --- a/SPECS/kernel-rt/0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt +++ /dev/null @@ -1,35 +0,0 @@ -From 7f3224ea657c916f0414480c8411923478d6b9bb Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Tue, 3 Oct 2023 21:37:21 +0200 -Subject: [PATCH 7/9] drm/i915/guc: Consider also RCU depth in busy loop. - -intel_guc_send_busy_loop() looks at in_atomic() and irqs_disabled() to -decide if it should busy-spin while waiting or if it may sleep. -Both checks will report false on PREEMPT_RT if sleeping spinlocks are -acquired leading to RCU splats while the function sleeps. - -Check also if RCU has been disabled. - -Reported-by: "John B. Wyatt IV" -Reviewed-by: Rodrigo Vivi -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h -index 053780f562c1..b25fa8f4dc4b 100644 ---- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h -+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h -@@ -362,7 +362,7 @@ static inline int intel_guc_send_busy_loop(struct intel_guc *guc, - { - int err; - unsigned int sleep_period_ms = 1; -- bool not_atomic = !in_atomic() && !irqs_disabled(); -+ bool not_atomic = !in_atomic() && !irqs_disabled() && !rcu_preempt_depth(); - - /* - * FIXME: Have caller pass in if we are in an atomic context to avoid --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c b/SPECS/kernel-rt/0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c deleted file mode 100644 index a1cb90661..000000000 --- a/SPECS/kernel-rt/0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c +++ /dev/null @@ -1,51 +0,0 @@ -From f9cc3911f65a04a9524d8f49fd465323dbbf5f6f Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:06 +0300 -Subject: [PATCH 07/11] i3c: mipi-i3c-hci: Remove nonexistent ring interrupt - -Ring interrupt bit 7, INTR_WARN_INS_STOP_MODE was probably drafted at -some point but is marked as reserved in the MIPI I3C HCI specification -versions 1.1 and 1.2 that came out after the initial code and also in -the earlier specification versions so remove it. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250827103009.243771-3-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/dma.c | 5 ----- - 1 file changed, 5 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index f5f5ab4db172..8bc9de189543 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -77,7 +77,6 @@ - #define INTR_TRANSFER_COMPLETION BIT(11) - #define INTR_RING_OP BIT(10) - #define INTR_TRANSFER_ERR BIT(9) --#define INTR_WARN_INS_STOP_MODE BIT(7) - #define INTR_IBI_RING_FULL BIT(6) - #define INTR_TRANSFER_ABORT BIT(5) - -@@ -278,7 +277,6 @@ static int hci_dma_init(struct i3c_hci *hci) - INTR_TRANSFER_COMPLETION | - INTR_RING_OP | - INTR_TRANSFER_ERR | -- INTR_WARN_INS_STOP_MODE | - INTR_IBI_RING_FULL | - INTR_TRANSFER_ABORT); - -@@ -795,9 +793,6 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - RING_CTRL_RUN_STOP); - } - } -- if (status & INTR_WARN_INS_STOP_MODE) -- dev_warn_ratelimited(&hci->master.dev, -- "ring %d: Inserted Stop on Mode Change\n", i); - if (status & INTR_IBI_RING_FULL) - dev_err_ratelimited(&hci->master.dev, - "ring %d: IBI Ring Full Condition\n", i); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-issei-add-driver-to-driver-interface.security b/SPECS/kernel-rt/0007-issei-add-driver-to-driver-interface.security new file mode 100644 index 000000000..1d8874a97 --- /dev/null +++ b/SPECS/kernel-rt/0007-issei-add-driver-to-driver-interface.security @@ -0,0 +1,338 @@ +From ca7c252c19765c0eacda46f17c347c2318c4733b Mon Sep 17 00:00:00 2001 +From: "Abliyev, Reuven" +Date: Thu, 27 Mar 2025 17:55:25 +0200 +Subject: [PATCH 7/7] issei: add driver to driver interface + +This enables communication with ISSEI device from the kernel modules + +Signed-off-by: Abliyev, Reuven +Signed-off-by: Alexander Usyskin +--- + drivers/misc/issei/Makefile | 1 + + drivers/misc/issei/issei_device.c | 264 ++++++++++++++++++++++++++++++ + include/linux/issei_device.h | 30 ++++ + 3 files changed, 295 insertions(+) + create mode 100644 drivers/misc/issei/issei_device.c + create mode 100644 include/linux/issei_device.h + +diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile +index fb904fda39499..e4dc3599342f2 100644 +--- a/drivers/misc/issei/Makefile ++++ b/drivers/misc/issei/Makefile +@@ -9,6 +9,7 @@ issei-objs += fw_client.o + issei-objs += host_client.o + issei-objs += ham.o + issei-objs += main.o ++issei-objs += issei_device.o + + obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o + issei-heci-objs := pci_heci.o +diff --git a/drivers/misc/issei/issei_device.c b/drivers/misc/issei/issei_device.c +new file mode 100644 +index 0000000000000..33417bd160497 +--- /dev/null ++++ b/drivers/misc/issei/issei_device.c +@@ -0,0 +1,264 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2025 Intel Corporation */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "linux/uuid.h" ++#include "linux/wait.h" ++ ++#include "issei_dev.h" ++#include "host_client.h" ++ ++/** ++ * issei_device_open - find create issei device object. ++ * @match: pointer to callback that should return non-zero when matching device is found ++ * @data: user data to pass to match callback ++ * ++ * Return: pointer to opaque context on success, <0 on error ++ */ ++void *issei_device_open(int (*match)(struct device *, const void *), const void *data) ++{ ++ struct issei_host_client *cl; ++ struct issei_device *idev; ++ struct device *dev; ++ ++ dev = class_find_device(issei_class, NULL, data, match); ++ if (!dev) ++ return ERR_PTR(-ENODEV); ++ ++ idev = dev_get_drvdata(dev); ++ ++ cl = issei_cl_create(idev, NULL); ++ if (IS_ERR(cl)) ++ put_device(dev); ++ ++ return cl; ++} ++EXPORT_SYMBOL_GPL(issei_device_open); ++ ++/** ++ * issei_device_release - release issei device object. ++ * @ctx: opaque context ++ */ ++void issei_device_release(void *ctx) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ ++ issei_cl_remove(cl); ++ put_device(&idev->dev); ++} ++EXPORT_SYMBOL_GPL(issei_device_release); ++ ++/** ++ * issei_device_connect - connect to firmware client. ++ * @ctx: opaque context ++ * @client_uuid: UUID of firmware client to connect ++ * @conn: pointer for client data to fill on successful connection ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ int ret; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -ENODEV; ++ } ++ ret = issei_cl_connect(cl, client_uuid, &conn->max_msg_length, ++ &conn->protocol_version, &conn->flags); ++ if (ret) { ++ dev_info(&idev->dev, "failed to connect ret = %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(issei_device_connect); ++ ++/** ++ * issei_device_disconnect - disconnect from firmware client. ++ * @ctx: opaque context ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_disconnect(void *ctx) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ int ret; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -ENODEV; ++ } ++ ++ ret = issei_cl_disconnect(cl); ++ if (ret) { ++ dev_info(&idev->dev, "failed to disconnect ret = %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(issei_device_disconnect); ++ ++/** ++ * issei_device_write - send data to firmware client. ++ * @ctx: opaque context ++ * @ubuf: data to send ++ * @length: data length ++ * ++ * Return: >=0 data length on success, <0 on error ++ */ ++ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ const u8 *buf; ++ ssize_t ret; ++ ++ if (!length) ++ return 0; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -EBUSY; ++ } ++ ++ /* sanity check */ ++ if (length > idev->dma.length.h2f) { ++ dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", length, ++ idev->dma.length.h2f); ++ return -EFBIG; ++ } ++ ++ buf = kmemdup(ubuf, length, GFP_KERNEL); ++ if (!buf) { ++ dev_dbg(&idev->dev, "Failed to allocate buf size %zu\n", length); ++ return -ENOMEM; ++ } ++ ++ do { ++ ret = issei_cl_write(cl, buf, length); ++ if (ret < 0 && ret != -EAGAIN) { ++ kfree(buf); ++ dev_dbg(&idev->dev, "failed to write %zd\n", ret); ++ return ret; ++ } ++ if (wait_event_interruptible(cl->write_wait, ++ (issei_cl_check_write(cl) != 1))) { ++ if (signal_pending(current)) ++ return -EINTR; ++ return -ERESTARTSYS; ++ } ++ } while (ret == -EAGAIN); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(issei_device_write); ++ ++/** ++ * issei_device_read - receive data from firmware client. ++ * @ctx: opaque context ++ * @ubuf: buffer for data ++ * @length: buffer length ++ * ++ * Return: >=0 data length on success, <0 on error ++ */ ++ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ u8 *data = NULL; ++ size_t data_size = length; ++ ssize_t ret; ++ ++ if (!length) ++ return 0; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -EBUSY; ++ } ++ ++ /* sanity check */ ++ if (length > idev->dma.length.f2h) { ++ dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", length, ++ idev->dma.length.f2h); ++ return -EFBIG; ++ } ++ ++ ret = issei_cl_read(cl, &data, &data_size); ++ if (!ret) ++ goto copy; ++ if (ret != -ENOENT) ++ return ret; ++ ++ if (wait_event_interruptible(cl->read_wait, ++ (issei_cl_check_read(cl) != 0))) { ++ if (signal_pending(current)) ++ return -EINTR; ++ return -ERESTARTSYS; ++ } ++ ++ ret = issei_cl_read(cl, &data, &data_size); ++ if (ret) ++ return ret; ++ ++copy: ++ memcpy(ubuf, data, data_size); ++ ret = data_size; ++ kfree(data); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(issei_device_read); ++ ++/** ++ * issei_device_dma_map - allocate DMA buffer on parent device. ++ * @ctx: opaque context ++ * @size: buffer size ++ * @daddr: pointer for buffer physical address ++ * @vaddr: pointer for buffer virtual address ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ ++ return issei_cl_dma_map(cl, size, daddr, vaddr); ++} ++EXPORT_SYMBOL_GPL(issei_device_dma_map); ++ ++/** ++ * issei_device_dma_unmap - deallocate DMA buffer. ++ * @ctx: opaque context ++ */ ++void issei_device_dma_unmap(void *ctx) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ ++ issei_cl_dma_unmap(cl); ++} ++EXPORT_SYMBOL_GPL(issei_device_dma_unmap); ++ ++/** ++ * issei_device_register_interface - register class interface for issei class. ++ * @intf: interface structure ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_register_interface(struct class_interface *intf) ++{ ++ intf->class = issei_class; ++ ++ return class_interface_register(intf); ++} ++EXPORT_SYMBOL_GPL(issei_device_register_interface); +diff --git a/include/linux/issei_device.h b/include/linux/issei_device.h +new file mode 100644 +index 0000000000000..862abdb004199 +--- /dev/null ++++ b/include/linux/issei_device.h +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2025 Intel Corporation */ ++#ifndef _ISSEI_DEVICE_H_ ++#define _ISSEI_DEVICE_H_ ++ ++#include ++#include ++#include ++ ++struct class_interface; ++struct device; ++struct issei_client; ++ ++void *issei_device_open(int (*match)(struct device *, const void *), const void *data); ++void issei_device_release(void *ctx); ++ ++int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn); ++int issei_device_disconnect(void *ctx); ++ ++ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length); ++ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length); ++ ++int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr); ++void issei_device_dma_unmap(void *ctx); ++ ++int issei_device_register_interface(struct class_interface *intf); ++#define issei_device_unregister_interface(intf) \ ++ class_interface_unregister(intf) ++ ++#endif /* _ISSEI_DEVICE_H_ */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-ocfs2-relax-BUG-to-ocfs2_error-in-__ocfs2_move_exten.patch b/SPECS/kernel-rt/0007-ocfs2-relax-BUG-to-ocfs2_error-in-__ocfs2_move_exten.patch deleted file mode 100644 index 534712ce7..000000000 --- a/SPECS/kernel-rt/0007-ocfs2-relax-BUG-to-ocfs2_error-in-__ocfs2_move_exten.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 43cd5558c2039ad1b0864f366c4c592eaecfd93a Mon Sep 17 00:00:00 2001 -From: Dmitry Antipov -Date: Thu, 9 Oct 2025 13:23:49 +0300 -Subject: [PATCH 07/51] ocfs2: relax BUG() to ocfs2_error() in - __ocfs2_move_extent() - -In '__ocfs2_move_extent()', relax 'BUG()' to 'ocfs2_error()' just -to avoid crashing the whole kernel due to a filesystem corruption. - -Fixes: 8f603e567aa7 ("Ocfs2/move_extents: move a range of extent.") -Link: https://lkml.kernel.org/r/20251009102349.181126-2-dmantipov@yandex.ru -Signed-off-by: Dmitry Antipov -Closes: https://syzkaller.appspot.com/bug?extid=727d161855d11d81e411 -Reported-by: syzbot+727d161855d11d81e411@syzkaller.appspotmail.com -Reviewed-by: Joseph Qi -Cc: Mark Fasheh -Cc: Joel Becker -Cc: Junxiao Bi -Cc: Changwei Ge -Cc: Jun Piao -Signed-off-by: Andrew Morton ---- - fs/ocfs2/move_extents.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c -index 80ebb0b7265a..5a0228c51ec3 100644 ---- a/fs/ocfs2/move_extents.c -+++ b/fs/ocfs2/move_extents.c -@@ -98,7 +98,13 @@ static int __ocfs2_move_extent(handle_t *handle, - - rec = &el->l_recs[index]; - -- BUG_ON(ext_flags != rec->e_flags); -+ if (ext_flags != rec->e_flags) { -+ ret = ocfs2_error(inode->i_sb, -+ "Inode %llu has corrupted extent %d with flags 0x%x at cpos %u\n", -+ (unsigned long long)ino, index, rec->e_flags, cpos); -+ goto out; -+ } -+ - /* - * after moving/defraging to new location, the extent is not going - * to be refcounted anymore. --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu b/SPECS/kernel-rt/0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu deleted file mode 100644 index 3cc14c739..000000000 --- a/SPECS/kernel-rt/0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu +++ /dev/null @@ -1,33 +0,0 @@ -From ec8261555865cea6e5aaad3722e986e6dfefe44e Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:44:25 +0800 -Subject: [PATCH 07/21] patch: staging add patch for use DPHY as the default - phy mode - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index dae8700dd7f5..101f080df552 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -84,10 +84,10 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, - } - - isys->csi2[csi2->port].nlanes = csi2->nlanes; -- if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) -- isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; -- else -+ if (csi2->bus_type == V4L2_MBUS_CSI2_CPHY) - isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; -+ else -+ isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; - - return 0; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf b/SPECS/kernel-rt/0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf deleted file mode 100644 index c039cd150..000000000 --- a/SPECS/kernel-rt/0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf +++ /dev/null @@ -1,71 +0,0 @@ -From 57f44dd44c91cafad6a44c6c3650a28db51fa149 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Tue, 12 Aug 2025 15:58:20 +0800 -Subject: [PATCH 007/100] perf/x86/intel: Change macro - GLOBAL_CTRL_EN_PERF_METRICS to BIT_ULL(48) - -Macro GLOBAL_CTRL_EN_PERF_METRICS is defined to 48 instead of -BIT_ULL(48), it's inconsistent with other similar macros. This leads to -this macro is quite easily used wrongly since users thinks it's a -bit-mask just like other similar macros. - -Thus change GLOBAL_CTRL_EN_PERF_METRICS to BIT_ULL(48) and eliminate -this potential misuse. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - arch/x86/events/intel/core.c | 8 ++++---- - arch/x86/include/asm/perf_event.h | 2 +- - 2 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 602a7c33f711..3b51d8ffa5ac 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5468,9 +5468,9 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) - 0, x86_pmu_num_counters(&pmu->pmu), 0, 0); - - if (pmu->intel_cap.perf_metrics) -- pmu->intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS; -+ pmu->intel_ctrl |= GLOBAL_CTRL_EN_PERF_METRICS; - else -- pmu->intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS); -+ pmu->intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS; - - intel_pmu_check_event_constraints_all(&pmu->pmu); - -@@ -5602,7 +5602,7 @@ static void intel_pmu_cpu_starting(int cpu) - rdmsrq(MSR_IA32_PERF_CAPABILITIES, perf_cap.capabilities); - if (!perf_cap.perf_metrics) { - x86_pmu.intel_cap.perf_metrics = 0; -- x86_pmu.intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS); -+ x86_pmu.intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS; - } - } - -@@ -7934,7 +7934,7 @@ __init int intel_pmu_init(void) - } - - if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics) -- x86_pmu.intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS; -+ x86_pmu.intel_ctrl |= GLOBAL_CTRL_EN_PERF_METRICS; - - if (x86_pmu.intel_cap.pebs_timing_info) - x86_pmu.flags |= PMU_FL_RETIRE_LATENCY; -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 70d1d94aca7e..f8247ac276c4 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -430,7 +430,7 @@ static inline bool is_topdown_idx(int idx) - #define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(GLOBAL_STATUS_TRACE_TOPAPMI_BIT) - #define GLOBAL_STATUS_PERF_METRICS_OVF_BIT 48 - --#define GLOBAL_CTRL_EN_PERF_METRICS 48 -+#define GLOBAL_CTRL_EN_PERF_METRICS BIT_ULL(48) - /* - * We model guest LBR event tracing as another fixed-mode PMC like BTS. - * --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf b/SPECS/kernel-rt/0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf new file mode 100644 index 000000000..3863acc96 --- /dev/null +++ b/SPECS/kernel-rt/0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf @@ -0,0 +1,82 @@ +From dfc75ded70ed507b85d920b00ce199d2821c8b61 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Mon, 29 Dec 2025 11:41:02 -0800 +Subject: [PATCH 07/13] perf/x86/intel/uncore: Add domain global init callback + +In the Intel uncore self-describing mechanism, the Global Control +Register freeze_all bit is SoC-wide and propagates to all uncore PMUs. + +On Diamond Rapids, this bit is set at power-on, unlike some prior +platforms. Add a global_init callback to unfreeze all PMON units. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 16 ++++++++++++++++ + arch/x86/events/intel/uncore.h | 2 ++ + arch/x86/events/intel/uncore_discovery.c | 3 +++ + 3 files changed, 21 insertions(+) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index eb57eb7159959..34d0822dc0022 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1697,6 +1697,21 @@ static int __init uncore_mmio_init(void) + return ret; + } + ++static int uncore_mmio_global_init(u64 ctl) ++{ ++ void __iomem *io_addr; ++ ++ io_addr = ioremap(ctl, sizeof(ctl)); ++ if (!io_addr) ++ return -ENOMEM; ++ ++ /* Clear bit 0, all other bits are reserved */ ++ writel(0, io_addr); ++ ++ iounmap(io_addr); ++ return 0; ++} ++ + static const struct uncore_plat_init nhm_uncore_init __initconst = { + .cpu_init = nhm_uncore_cpu_init, + }; +@@ -1839,6 +1854,7 @@ static const struct uncore_plat_init dmr_uncore_init __initconst = { + .domain[0].units_ignore = dmr_uncore_imh_units_ignore, + .domain[1].discovery_base = CBB_UNCORE_DISCOVERY_MSR, + .domain[1].units_ignore = dmr_uncore_cbb_units_ignore, ++ .domain[1].global_init = uncore_mmio_global_init, + }; + + static const struct uncore_plat_init generic_uncore_init __initconst = { +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 83d01a9cefc00..55e3aebf4b5e0 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -51,6 +51,8 @@ struct uncore_discovery_domain { + /* MSR address or PCI device used as the discovery base */ + u32 discovery_base; + bool base_is_pci; ++ int (*global_init)(u64 ctl); ++ + /* The units in the discovery table should be ignored. */ + int *units_ignore; + }; +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index 24b8da18ee6c8..9779e38e272e0 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -287,6 +287,9 @@ static int __parse_discovery_table(char *type, + if (!io_addr) + return -ENOMEM; + ++ if (domain->global_init && domain->global_init(global.ctl)) ++ return -ENODEV; ++ + /* Parsing Unit Discovery State */ + for (i = 0; i < global.max_units; i++) { + memcpy_fromio(&unit, io_addr + (i + 1) * (global.stride * 8), +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss b/SPECS/kernel-rt/0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss deleted file mode 100644 index f34cab9a1..000000000 --- a/SPECS/kernel-rt/0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss +++ /dev/null @@ -1,29 +0,0 @@ -From 41e6ad2f14364ace8d33778371c0e438605eb6d1 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Thu, 17 Apr 2025 11:29:27 +0300 -Subject: [PATCH 07/16] spi: intel: Add support for Intel Wildcat Lake SPI - serial flash - -Add Intel Wildcat Lake SPI serial flash PCI ID to the list of supported -devices. - -Signed-off-by: Mika Westerberg ---- - drivers/spi/spi-intel-pci.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c -index 49b4d3061197..7765fb27c37c 100644 ---- a/drivers/spi/spi-intel-pci.c -+++ b/drivers/spi/spi-intel-pci.c -@@ -75,6 +75,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x38a4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, -+ { PCI_VDEVICE(INTEL, 0x4d23), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu b/SPECS/kernel-rt/0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu deleted file mode 100644 index d93aab2b4..000000000 --- a/SPECS/kernel-rt/0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu +++ /dev/null @@ -1,38 +0,0 @@ -From 3d690acb7efc7810580da8caa2a8aa0df7321882 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Thu, 11 Sep 2025 15:24:17 +0800 -Subject: [PATCH 07/11] staging: media: ipu7 remove from the Makefile & Kconfig - -Signed-off-by: linya14x ---- - drivers/staging/media/Kconfig | 2 +- - drivers/staging/media/Makefile | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig -index ab250c89cd4d..6da885037207 100644 ---- a/drivers/staging/media/Kconfig -+++ b/drivers/staging/media/Kconfig -@@ -28,7 +28,7 @@ source "drivers/staging/media/imx/Kconfig" - - source "drivers/staging/media/ipu3/Kconfig" - --source "drivers/staging/media/ipu7/Kconfig" -+#source "drivers/staging/media/ipu7/Kconfig" - - source "drivers/staging/media/max96712/Kconfig" - -diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile -index 4a073938b2b2..22d19580e91f 100644 ---- a/drivers/staging/media/Makefile -+++ b/drivers/staging/media/Makefile -@@ -8,5 +8,5 @@ obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ - obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ - obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ - obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ --obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -+#obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ - obj-$(CONFIG_DVB_AV7110) += av7110/ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet b/SPECS/kernel-rt/0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet new file mode 100644 index 000000000..eed26cacc --- /dev/null +++ b/SPECS/kernel-rt/0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet @@ -0,0 +1,38 @@ +From 4f6ac597346ccc5da9307428dd7e955ebe25329e Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Fri, 4 Jun 2021 11:03:50 +0800 +Subject: [PATCH 07/18] stmmac: intel: skip xpcs reset for 2.5Gbps on Intel + AlderLake + +Unlike any other platforms, Intel AlderLake has most of the SerDes +PLL clock configuration done in the BIOS. Hence, we need to avoid +performing a xPCS soft reset on driver load. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index e2915815ee655..bf6544ec769b2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -985,6 +985,7 @@ static int adls_sgmii_phy0_data(struct pci_dev *pdev, + struct plat_stmmacenet_data *plat) + { + plat->bus_id = 1; ++ plat->skip_reset = 1; + + /* SerDes power up and power down are done in BIOS for ADL */ + +@@ -999,6 +1000,7 @@ static int adls_sgmii_phy1_data(struct pci_dev *pdev, + struct plat_stmmacenet_data *plat) + { + plat->bus_id = 2; ++ plat->skip_reset = 1; + + /* SerDes power up and power down are done in BIOS for ADL */ + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal b/SPECS/kernel-rt/0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal deleted file mode 100644 index 323a3da61..000000000 --- a/SPECS/kernel-rt/0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal +++ /dev/null @@ -1,38 +0,0 @@ -From ee41dfa118a71f9a655f827d0bf98d69c2a39624 Mon Sep 17 00:00:00 2001 -From: "Rafael J. Wysocki" -Date: Mon, 25 Aug 2025 15:28:34 +0200 -Subject: [PATCH 07/11] thermal: gov_step_wise: Clarify cooling logic - description comment - -The cooling logic description comment next to the get_target_state() -definition is slightly ambiguous in what it means by "lower cooling -state", so clarify that by replacing the ambuguous phrase with "the -minimum applicable cooling state". - -No functional impact. - -Signed-off-by: Rafael J. Wysocki -Reviewed-by: Lukasz Luba -Link: https://patch.msgid.link/4690596.LvFx2qVVIh@rafael.j.wysocki ---- - drivers/thermal/gov_step_wise.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c -index 44945af3ce17..ea277c466d8d 100644 ---- a/drivers/thermal/gov_step_wise.c -+++ b/drivers/thermal/gov_step_wise.c -@@ -25,8 +25,8 @@ - * minimum - * If the temperature is lower than a trip point, - * a. if the trend is THERMAL_TREND_RAISING, do nothing -- * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling -- * state for this trip point, if the cooling state already -+ * b. if the trend is THERMAL_TREND_DROPPING, use the minimum applicable -+ * cooling state for this trip point, or if the cooling state already - * equals lower limit, deactivate the thermal instance - */ - static unsigned long get_target_state(struct thermal_instance *instance, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet b/SPECS/kernel-rt/0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet new file mode 100644 index 000000000..87cd1ed48 --- /dev/null +++ b/SPECS/kernel-rt/0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet @@ -0,0 +1,491 @@ +From da173fd67211ecc052baf182b880c5cc69122982 Mon Sep 17 00:00:00 2001 +From: Saeed Mahameed +Date: Tue, 9 Apr 2019 22:01:47 -0700 +Subject: [PATCH 07/14] tools/bpf: Add xdp set command for md btf + +Add new bpftool net subcommand and use it to report and set XDP attrs: + +$ /usr/local/sbin/bpftool net xdp help +Usage: /usr/local/sbin/bpftool net xdp + { show | list | set | md_btf} [dev ] + /usr/local/sbin/bpftool xdp help + +$ /usr/local/sbin/bpftool net xdp set dev mlx0 md_btf on + +$ /usr/local/sbin/bpftool net xdp show +xdp: +mlx0(3) md_btf_id(1) md_btf_enabled(1) + +Signed-off-by: Saeed Mahameed +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + tools/bpf/bpftool/main.h | 2 + + tools/bpf/bpftool/net.c | 7 +- + tools/bpf/bpftool/xdp.c | 308 +++++++++++++++++++++++++++++++++++++++ + tools/lib/bpf/libbpf.h | 1 + + tools/lib/bpf/libbpf.map | 1 + + tools/lib/bpf/netlink.c | 49 +++++++ + 6 files changed, 365 insertions(+), 3 deletions(-) + create mode 100644 tools/bpf/bpftool/xdp.c + +diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h +index 1130299cede0b..64cd05b1072e3 100644 +--- a/tools/bpf/bpftool/main.h ++++ b/tools/bpf/bpftool/main.h +@@ -165,6 +165,7 @@ int do_btf(int argc, char **argv); + + /* non-bootstrap only commands */ + int do_prog(int argc, char **arg) __weak; ++int do_xdp(int argc, char **argv); + int do_map(int argc, char **arg) __weak; + int do_link(int argc, char **arg) __weak; + int do_event_pipe(int argc, char **argv) __weak; +@@ -252,6 +253,7 @@ struct tcmsg; + int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb); + int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind, + const char *devname, int ifindex); ++int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb); + + int print_all_levels(__maybe_unused enum libbpf_print_level level, + const char *format, va_list args); +diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c +index cfc6f944f7c33..f313f5be0960c 100644 +--- a/tools/bpf/bpftool/net.c ++++ b/tools/bpf/bpftool/net.c +@@ -362,7 +362,7 @@ static int netlink_get_link(int sock, unsigned int nl_pid, + dump_link_nlmsg, cookie); + } + +-static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) ++int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) + { + struct bpf_netdev_t *netinfo = cookie; + struct ifinfomsg *ifinfo = msg; +@@ -937,7 +937,7 @@ static int do_show(int argc, char **argv) + jsonw_start_array(json_wtr); + NET_START_OBJECT; + NET_START_ARRAY("xdp", "%s:\n"); +- ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); ++ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); + NET_END_ARRAY("\n"); + + if (!ret) { +@@ -984,7 +984,7 @@ static int do_help(int argc, char **argv) + } + + fprintf(stderr, +- "Usage: %1$s %2$s { show | list } [dev ]\n" ++ "Usage: %1$s %2$s { show | list | xdp } [dev ]\n" + " %1$s %2$s attach ATTACH_TYPE PROG dev [ overwrite ]\n" + " %1$s %2$s detach ATTACH_TYPE dev \n" + " %1$s %2$s help\n" +@@ -1011,6 +1011,7 @@ static const struct cmd cmds[] = { + { "list", do_show }, + { "attach", do_attach }, + { "detach", do_detach }, ++ { "xdp", do_xdp }, + { "help", do_help }, + { 0 } + }; +diff --git a/tools/bpf/bpftool/xdp.c b/tools/bpf/bpftool/xdp.c +new file mode 100644 +index 0000000000000..d33671de877b8 +--- /dev/null ++++ b/tools/bpf/bpftool/xdp.c +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++// Copyright (C) 2019 Mellanox. ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bpf/nlattr.h" ++#include "main.h" ++#include "netlink_dumper.h" ++ ++/* TODO: reuse form net.c */ ++#ifndef SOL_NETLINK ++#define SOL_NETLINK 270 ++#endif ++ ++static int netlink_open(__u32 *nl_pid) ++{ ++ struct sockaddr_nl sa; ++ socklen_t addrlen; ++ int one = 1, ret; ++ int sock; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.nl_family = AF_NETLINK; ++ ++ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if (sock < 0) ++ return -errno; ++ ++ if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, ++ &one, sizeof(one)) < 0) { ++ p_err("Netlink error reporting not supported"); ++ } ++ ++ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { ++ ret = -errno; ++ goto cleanup; ++ } ++ ++ addrlen = sizeof(sa); ++ if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { ++ ret = -errno; ++ goto cleanup; ++ } ++ ++ if (addrlen != sizeof(sa)) { ++ ret = -LIBBPF_ERRNO__INTERNAL; ++ goto cleanup; ++ } ++ ++ *nl_pid = sa.nl_pid; ++ return sock; ++ ++cleanup: ++ close(sock); ++ return ret; ++} ++ ++typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); ++ ++typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie); ++ ++static int netlink_recv(int sock, __u32 nl_pid, __u32 seq, ++ __dump_nlmsg_t _fn, dump_nlmsg_t fn, ++ void *cookie) ++{ ++ bool multipart = true; ++ struct nlmsgerr *err; ++ struct nlmsghdr *nh; ++ char buf[4096]; ++ int len, ret; ++ ++ while (multipart) { ++ multipart = false; ++ len = recv(sock, buf, sizeof(buf), 0); ++ if (len < 0) { ++ ret = -errno; ++ goto done; ++ } ++ ++ if (len == 0) ++ break; ++ ++ for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); ++ nh = NLMSG_NEXT(nh, len)) { ++ if (nh->nlmsg_pid != nl_pid) { ++ ret = -LIBBPF_ERRNO__WRNGPID; ++ goto done; ++ } ++ if (nh->nlmsg_seq != seq) { ++ ret = -LIBBPF_ERRNO__INVSEQ; ++ goto done; ++ } ++ if (nh->nlmsg_flags & NLM_F_MULTI) ++ multipart = true; ++ switch (nh->nlmsg_type) { ++ case NLMSG_ERROR: ++ err = (struct nlmsgerr *)NLMSG_DATA(nh); ++ if (!err->error) ++ continue; ++ ret = err->error; ++ libbpf_nla_dump_errormsg(nh); ++ goto done; ++ case NLMSG_DONE: ++ return 0; ++ default: ++ break; ++ } ++ if (_fn) { ++ ret = _fn(nh, fn, cookie); ++ if (ret) ++ return ret; ++ } ++ } ++ } ++ ret = 0; ++done: ++ return ret; ++} ++ ++static int __dump_link_nlmsg(struct nlmsghdr *nlh, ++ dump_nlmsg_t dump_link_nlmsg, void *cookie) ++{ ++ struct nlattr *tb[IFLA_MAX + 1], *attr; ++ struct ifinfomsg *ifi = NLMSG_DATA(nlh); ++ int len; ++ ++ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); ++ attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); ++ if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) ++ return -LIBBPF_ERRNO__NLPARSE; ++ ++ return dump_link_nlmsg(cookie, ifi, tb); ++} ++ ++static int netlink_get_link(int sock, unsigned int nl_pid, ++ dump_nlmsg_t dump_link_nlmsg, void *cookie) ++{ ++ struct { ++ struct nlmsghdr nlh; ++ struct ifinfomsg ifm; ++ } req = { ++ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), ++ .nlh.nlmsg_type = RTM_GETLINK, ++ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, ++ .ifm.ifi_family = AF_PACKET, ++ }; ++ int seq = time(NULL); ++ ++ req.nlh.nlmsg_seq = seq; ++ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) ++ return -errno; ++ ++ return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, ++ dump_link_nlmsg, cookie); ++} ++ ++struct ip_devname_ifindex { ++ char devname[64]; ++ int ifindex; ++}; ++ ++struct bpf_netdev_t { ++ struct ip_devname_ifindex *devices; ++ int used_len; ++ int array_len; ++ int filter_idx; ++}; ++ ++static int do_show(int argc, char **argv) ++{ ++ int sock, ret, filter_idx = -1; ++ struct bpf_netdev_t dev_array; ++ unsigned int nl_pid = 0; ++ char err_buf[256]; ++ ++ if (argc == 2) { ++ if (strcmp(argv[0], "dev") != 0) ++ usage(); ++ filter_idx = if_nametoindex(argv[1]); ++ if (filter_idx == 0) { ++ fprintf(stderr, "invalid dev name %s\n", argv[1]); ++ return -1; ++ } ++ } else if (argc != 0) { ++ usage(); ++ } ++ ++ sock = netlink_open(&nl_pid); ++ if (sock < 0) { ++ fprintf(stderr, "failed to open netlink sock\n"); ++ return -1; ++ } ++ ++ dev_array.devices = NULL; ++ dev_array.used_len = 0; ++ dev_array.array_len = 0; ++ dev_array.filter_idx = filter_idx; ++ ++ if (json_output) ++ jsonw_start_array(json_wtr); ++ NET_START_OBJECT; ++ NET_START_ARRAY("xdp", "%s:\n"); ++ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); ++ NET_END_ARRAY("\n"); ++ ++ NET_END_OBJECT; ++ if (json_output) ++ jsonw_end_array(json_wtr); ++ ++ if (ret) { ++ if (json_output) ++ jsonw_null(json_wtr); ++ libbpf_strerror(ret, err_buf, sizeof(err_buf)); ++ fprintf(stderr, "Error: %s\n", err_buf); ++ } ++ free(dev_array.devices); ++ close(sock); ++ return ret; ++} ++ ++static int set_usage(void) ++{ ++ fprintf(stderr, ++ "Usage: %s net xdp set dev {md_btf {on|off}}\n" ++ " %s net xdp set help\n" ++ " md_btf {on|off}: enable/disable meta data btf\n", ++ bin_name, bin_name); ++ ++ return -1; ++} ++ ++static int xdp_set_md_btf(int ifindex, char *arg) ++{ ++ __u8 enable = (strcmp(arg, "on") == 0) ? 1 : 0; ++ int ret; ++ ++ ret = bpf_set_link_xdp_md_btf(ifindex, enable); ++ if (ret) ++ fprintf(stderr, "Failed to setup xdp md, err=%d\n", ret); ++ ++ return -ret; ++} ++ ++static int do_set(int argc, char **argv) ++{ ++ char *set_cmd, *set_arg; ++ int dev_idx = -1; ++ ++ if (argc < 4) ++ return set_usage(); ++ ++ if (strcmp(argv[0], "dev") != 0) ++ return set_usage(); ++ ++ dev_idx = if_nametoindex(argv[1]); ++ if (dev_idx == 0) { ++ fprintf(stderr, "invalid dev name %s\n", argv[1]); ++ return -1; ++ } ++ ++ set_cmd = argv[2]; ++ set_arg = argv[3]; ++ ++ if (strcmp(set_cmd, "md_btf") != 0) ++ return set_usage(); ++ ++ if (strcmp(set_arg, "on") != 0 && strcmp(set_arg, "off") != 0) ++ return set_usage(); ++ ++ return xdp_set_md_btf(dev_idx, set_arg); ++} ++ ++static int do_help(int argc, char **argv) ++{ ++ if (json_output) { ++ jsonw_null(json_wtr); ++ return 0; ++ } ++ ++ fprintf(stderr, ++ "Usage: %s %s xdp { show | list | set } [dev ]\n" ++ " %s %s help\n", ++ bin_name, argv[-2], bin_name, argv[-2]); ++ ++ return 0; ++} ++ ++static const struct cmd cmds[] = { ++ { "show", do_show }, ++ { "list", do_show }, ++ { "set", do_set }, ++ { "help", do_help }, ++ { 0 } ++}; ++ ++int do_xdp(int argc, char **argv) ++{ ++ return cmd_select(cmds, argc, argv, do_help); ++} +diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h +index 5118d0a90e243..846309ddcb246 100644 +--- a/tools/lib/bpf/libbpf.h ++++ b/tools/lib/bpf/libbpf.h +@@ -1644,6 +1644,7 @@ LIBBPF_API struct perf_buffer * + perf_buffer__new(int map_fd, size_t page_cnt, + perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx, + const struct perf_buffer_opts *opts); ++LIBBPF_API int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable); + + enum bpf_perf_event_ret { + LIBBPF_PERF_EVENT_DONE = 0, +diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map +index 8ed8749907d47..c20311e4ce122 100644 +--- a/tools/lib/bpf/libbpf.map ++++ b/tools/lib/bpf/libbpf.map +@@ -73,6 +73,7 @@ LIBBPF_0.0.2 { + bpf_map_lookup_elem_flags; + bpf_object__btf; + bpf_object__find_map_fd_by_name; ++ bpf_set_link_xdp_md_btf; + btf__get_raw_data; + btf_ext__free; + btf_ext__get_raw_data; +diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c +index c997e69d507fe..204827a4380bf 100644 +--- a/tools/lib/bpf/netlink.c ++++ b/tools/lib/bpf/netlink.c +@@ -341,6 +341,55 @@ int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *o + return bpf_xdp_attach(ifindex, -1, flags, opts); + } + ++int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable) ++{ ++ struct nlattr *nla, *nla_xdp; ++ int sock, seq = 0, ret; ++ __u32 nl_pid; ++ struct { ++ struct nlmsghdr nh; ++ struct ifinfomsg ifinfo; ++ char attrbuf[64]; ++ } req; ++ ++ sock = libbpf_netlink_open(&nl_pid, NETLINK_ROUTE); ++ if (sock < 0) ++ return sock; ++ ++ memset(&req, 0, sizeof(req)); ++ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); ++ req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; ++ req.nh.nlmsg_type = RTM_SETLINK; ++ req.nh.nlmsg_pid = 0; ++ req.nh.nlmsg_seq = ++seq; ++ req.ifinfo.ifi_family = AF_UNSPEC; ++ req.ifinfo.ifi_index = ifindex; ++ ++ /* started nested attribute for XDP */ ++ nla = (struct nlattr *)(((char *)&req) ++ + NLMSG_ALIGN(req.nh.nlmsg_len)); ++ nla->nla_type = NLA_F_NESTED | IFLA_XDP; ++ nla->nla_len = NLA_HDRLEN; ++ /* add XDP MD setup */ ++ nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); ++ nla_xdp->nla_type = IFLA_XDP_MD_BTF_STATE; ++ nla_xdp->nla_len = NLA_HDRLEN + sizeof(__u8); ++ memcpy((char *)nla_xdp + NLA_HDRLEN, &enable, sizeof(__u8)); ++ nla->nla_len += nla_xdp->nla_len; ++ ++ req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); ++ ++ if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { ++ ret = -errno; ++ goto cleanup; ++ } ++ ret = libbpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); ++ ++cleanup: ++ close(sock); ++ return ret; ++} ++ + static int __dump_link_nlmsg(struct nlmsghdr *nlh, + libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) + { +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet b/SPECS/kernel-rt/0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet deleted file mode 100644 index b41a6629e..000000000 --- a/SPECS/kernel-rt/0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet +++ /dev/null @@ -1,83 +0,0 @@ -From 0b9a15dbd24a0f814fa8862eb7c889b705fa8918 Mon Sep 17 00:00:00 2001 -From: Saeed Mahameed -Date: Tue, 9 Apr 2019 14:52:02 -0700 -Subject: [PATCH 07/19] tools/bpf: Query XDP metadata BTF ID - -When dumping bpf net information, also query XDP MD BTF attributes: - -$ /usr/local/sbin/bpftool net -xdp: -mlx0(3) md_btf_id(1) md_btf_enabled(0) - -tc: - -flow_dissector: - -Signed-off-by: Saeed Mahameed -Signed-off-by: Aravindhan Gunasekaran ---- - tools/bpf/bpftool/netlink_dumper.c | 21 +++++++++++++++++---- - tools/include/uapi/linux/if_link.h | 2 ++ - 2 files changed, 19 insertions(+), 4 deletions(-) - -diff --git a/tools/bpf/bpftool/netlink_dumper.c b/tools/bpf/bpftool/netlink_dumper.c -index 0a3c7e96c797..949779c3f9ee 100644 ---- a/tools/bpf/bpftool/netlink_dumper.c -+++ b/tools/bpf/bpftool/netlink_dumper.c -@@ -29,23 +29,36 @@ static void xdp_dump_prog_id(struct nlattr **tb, int attr, - static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex, - const char *name) - { -+ unsigned char mode = XDP_ATTACHED_NONE; - struct nlattr *tb[IFLA_XDP_MAX + 1]; -- unsigned char mode; -+ unsigned char md_btf_enabled = 0; -+ unsigned int md_btf_id = 0; -+ bool attached; - - if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0) - return -1; - -- if (!tb[IFLA_XDP_ATTACHED]) -+ if (!tb[IFLA_XDP_ATTACHED] && !tb[IFLA_XDP_MD_BTF_ID]) - return 0; - -- mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); -- if (mode == XDP_ATTACHED_NONE) -+ if (tb[IFLA_XDP_ATTACHED]) -+ mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); -+ -+ if (tb[IFLA_XDP_MD_BTF_ID]) { -+ md_btf_id = libbpf_nla_getattr_u32(tb[IFLA_XDP_MD_BTF_ID]); -+ md_btf_enabled = libbpf_nla_getattr_u8(tb[IFLA_XDP_MD_BTF_STATE]); -+ } -+ -+ attached = (mode != XDP_ATTACHED_NONE); -+ if (!attached && !md_btf_id) - return 0; - - NET_START_OBJECT; - if (name) - NET_DUMP_STR("devname", "%s", name); - NET_DUMP_UINT("ifindex", "(%u)", ifindex); -+ NET_DUMP_UINT("md_btf_id", " md_btf_id(%d)", md_btf_id); -+ NET_DUMP_UINT("md_btf_enabled", " md_btf_enabled(%d)", md_btf_enabled); - - if (mode == XDP_ATTACHED_MULTI) { - if (json_output) { -diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h -index 7e46ca4cd31b..4d5b143d4871 100644 ---- a/tools/include/uapi/linux/if_link.h -+++ b/tools/include/uapi/linux/if_link.h -@@ -1899,6 +1899,8 @@ enum { - IFLA_XDP_SKB_PROG_ID, - IFLA_XDP_HW_PROG_ID, - IFLA_XDP_EXPECTED_FD, -+ IFLA_XDP_MD_BTF_ID, -+ IFLA_XDP_MD_BTF_STATE, - __IFLA_XDP_MAX, - }; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0007-tools-power-turbostat-Remove-dead-code.turbo b/SPECS/kernel-rt/0007-tools-power-turbostat-Remove-dead-code.turbo new file mode 100644 index 000000000..d1b11729b --- /dev/null +++ b/SPECS/kernel-rt/0007-tools-power-turbostat-Remove-dead-code.turbo @@ -0,0 +1,40 @@ +From 286a9f326584d99b206a35959e027ae8f2424b24 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Wed, 22 Oct 2025 20:26:33 -0300 +Subject: [PATCH 07/21] tools/power turbostat: Remove dead code + +amperf_group_fd is never used. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 47cb723430389..f63525a1877c7 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -466,8 +466,6 @@ static void bic_groups_init(void) + #define PCL_10 14 /* PC10 */ + #define PCLUNL 15 /* Unlimited */ + +-struct amperf_group_fd; +- + char *proc_stat = "/proc/stat"; + FILE *outf; + int *fd_percpu; +@@ -4418,11 +4416,6 @@ int get_core_throt_cnt(int cpu, unsigned long long *cnt) + return 0; + } + +-struct amperf_group_fd { +- int aperf; /* Also the group descriptor */ +- int mperf; +-}; +- + static int read_perf_counter_info(const char *const path, const char *const parse_format, void *value_ptr) + { + int fdmt; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac b/SPECS/kernel-rt/0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac deleted file mode 100644 index e0e724d8a..000000000 --- a/SPECS/kernel-rt/0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac +++ /dev/null @@ -1,43 +0,0 @@ -From 243c1b5ae04cd5b3358dc03d33cb37deaf5a7b9c Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Tue, 29 Jul 2025 16:14:39 +0800 -Subject: [PATCH 08/13] EDAC/skx_common: Remove unused *NUM*_IMC macros - -There are no references to the *NUM*_IMC macros, so remove them. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.h | 5 ----- - 1 file changed, 5 deletions(-) - -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index e7038fd45d06..73ba89786cdf 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -29,23 +29,18 @@ - #define GET_BITFIELD(v, lo, hi) \ - (((v) & GENMASK_ULL((hi), (lo))) >> (lo)) - --#define SKX_NUM_IMC 2 /* Memory controllers per socket */ - #define SKX_NUM_CHANNELS 3 /* Channels per memory controller */ - #define SKX_NUM_DIMMS 2 /* Max DIMMS per channel */ - --#define I10NM_NUM_DDR_IMC 12 - #define I10NM_NUM_DDR_CHANNELS 2 - #define I10NM_NUM_DDR_DIMMS 2 - --#define I10NM_NUM_HBM_IMC 16 - #define I10NM_NUM_HBM_CHANNELS 2 - #define I10NM_NUM_HBM_DIMMS 1 - --#define I10NM_NUM_IMC (I10NM_NUM_DDR_IMC + I10NM_NUM_HBM_IMC) - #define I10NM_NUM_CHANNELS MAX(I10NM_NUM_DDR_CHANNELS, I10NM_NUM_HBM_CHANNELS) - #define I10NM_NUM_DIMMS MAX(I10NM_NUM_DDR_DIMMS, I10NM_NUM_HBM_DIMMS) - --#define NUM_IMC MAX(SKX_NUM_IMC, I10NM_NUM_IMC) - #define NUM_CHANNELS MAX(SKX_NUM_CHANNELS, I10NM_NUM_CHANNELS) - #define NUM_DIMMS MAX(SKX_NUM_DIMMS, I10NM_NUM_DIMMS) - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security b/SPECS/kernel-rt/0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security new file mode 100644 index 000000000..07e4fecb7 --- /dev/null +++ b/SPECS/kernel-rt/0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security @@ -0,0 +1,338 @@ +From d500ed859e1abc17c8f388f8e98632d8d48ada1e Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Thu, 20 Jan 2022 10:33:17 +0200 +Subject: [PATCH 8/8] INTEL_DII: mei: add empty handlers for ops functions + +When the offline bit is set and the device is being removed +we should prevent the driver from accessing the hardware because +at this stage the hardware may be no longer available. +Replace the operation handlers with the empty ones to ensure +no hardware registers are being accessed by the driver. + +Co-developed-by: Tomas Winkler +Co-developed-by: Vitaly Lubart +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/gsc-me.c | 292 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 292 insertions(+) + +diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c +index 72e1d195dce9a..4e84f75a7efd0 100644 +--- a/drivers/misc/mei/gsc-me.c ++++ b/drivers/misc/mei/gsc-me.c +@@ -23,6 +23,11 @@ + + #define MEI_GSC_RPM_TIMEOUT 2000 + ++static inline bool mei_gsc_hw_is_unavailable(const struct device *dev) ++{ ++ return dev->offline; ++} ++ + static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) + { + struct mei_me_hw *hw = to_me_hw(dev); +@@ -43,6 +48,291 @@ static void mei_gsc_set_ext_op_mem(const struct mei_me_hw *hw, struct resource * + iowrite32(limit, hw->mem_addr + H_GSC_EXT_OP_MEM_LIMIT_REG); + } + ++/** ++ * mei_gsc_mecbrw_read_null - read 32bit data from ME circular buffer (empty implementation) ++ * read window register ++ * ++ * @dev: the device structure ++ * ++ * Return: always 0 ++ */ ++static inline u32 mei_gsc_mecbrw_read_null(const struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_trc_status_null - read trc status register (empty implementation) ++ * ++ * @dev: mei device ++ * @trc: trc status register value ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_trc_status_null(struct mei_device *dev, u32 *trc) ++{ ++ *trc = 0; ++ return 0; ++} ++ ++/** ++ * mei_gsc_fw_status_null - read fw status register from pci config space (empty implementation) ++ * ++ * @dev: mei device ++ * @fw_status: fw status register values ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_fw_status_null(struct mei_device *dev, ++ struct mei_fw_status *fw_status) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_hw_config_null - configure hw dependent settings (empty implementation) ++ * ++ * @dev: mei device ++ * ++ * Return: always 0 ++ * ++ */ ++static int mei_gsc_hw_config_null(struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_pg_state_null - translate internal pg state (empty implementation) ++ * to the mei power gating state ++ * ++ * @dev: mei device ++ * ++ * Return: always MEI_PG_OFF ++ */ ++static inline enum mei_pg_state mei_gsc_pg_state_null(struct mei_device *dev) ++{ ++ return MEI_PG_OFF; ++} ++ ++/** ++ * mei_gsc_intr_clear_null - clear and stop interrupts (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_intr_clear_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_intr_enable_null - enables mei device interrupts (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_intr_enable_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_intr_disable_null - disables mei device interrupts (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_intr_disable_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_synchronize_irq_null - wait for pending IRQ handlers (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_synchronize_irq_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_host_is_ready_null - check whether the host has turned ready (empty implementation) ++ * ++ * @dev: mei device ++ * Return: always true ++ */ ++static bool mei_gsc_host_is_ready_null(struct mei_device *dev) ++{ ++ return true; ++} ++ ++/** ++ * mei_gsc_hw_start_null - hw start routine (empty implementation) ++ * ++ * @dev: mei device ++ * Return: always 0 ++ */ ++static int mei_gsc_hw_start_null(struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_hbuf_is_empty_null - checks if host buffer is empty (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always true ++ */ ++static bool mei_gsc_hbuf_is_empty_null(struct mei_device *dev) ++{ ++ return true; ++} ++ ++/** ++ * mei_gsc_hbuf_empty_slots_null - counts write empty slots (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always -EOVERFLOW ++ */ ++static int mei_gsc_hbuf_empty_slots_null(struct mei_device *dev) ++{ ++ return -EOVERFLOW; ++} ++ ++/** ++ * mei_gsc_hbuf_depth_null - returns depth of the hw buffer (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always 1 ++ */ ++static u32 mei_gsc_hbuf_depth_null(const struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_hbuf_write_null - writes a message to host hw buffer (empty implementation) ++ * ++ * @dev: the device structure ++ * @hdr: header of message ++ * @hdr_len: header length in bytes: must be multiplication of a slot (4bytes) ++ * @data: payload ++ * @data_len: payload length in bytes ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_hbuf_write_null(struct mei_device *dev, ++ const void *hdr, size_t hdr_len, ++ const void *data, size_t data_len) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_count_full_read_slots_null - counts read full slots (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always -EOVERFLOW ++ */ ++static int mei_gsc_count_full_read_slots_null(struct mei_device *dev) ++{ ++ return -EOVERFLOW; ++} ++ ++/** ++ * mei_gsc_read_slots_null - reads a message from mei device (empty implementation) ++ * ++ * @dev: the device structure ++ * @buffer: message buffer will be written ++ * @buffer_length: message size will be read ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_read_slots_null(struct mei_device *dev, unsigned char *buffer, ++ unsigned long buffer_length) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_pg_in_transition_null - is device now in pg transition (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always false ++ */ ++static bool mei_gsc_pg_in_transition_null(struct mei_device *dev) ++{ ++ return false; ++} ++ ++/** ++ * mei_gsc_pg_is_enabled_null - detect if PG is supported by HW (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always false ++ */ ++static bool mei_gsc_pg_is_enabled_null(struct mei_device *dev) ++{ ++ return false; ++} ++ ++/** ++ * mei_gsc_hw_is_ready_null - check whether the me(hw) has turned ready (empty implementation) ++ * ++ * @dev: mei device ++ * Return: always true ++ */ ++static bool mei_gsc_hw_is_ready_null(struct mei_device *dev) ++{ ++ return true; ++} ++ ++/** ++ * mei_gsc_hw_reset_null - resets fw via mei csr register (empty implementation) ++ * ++ * @dev: the device structure ++ * @intr_enable: if interrupt should be enabled after reset. ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_hw_reset_null(struct mei_device *dev, bool intr_enable) ++{ ++ return 0; ++} ++ ++static const struct mei_hw_ops mei_gsc_hw_ops_null = { ++ .trc_status = mei_gsc_trc_status_null, ++ .fw_status = mei_gsc_fw_status_null, ++ .pg_state = mei_gsc_pg_state_null, ++ ++ .host_is_ready = mei_gsc_host_is_ready_null, ++ ++ .hw_is_ready = mei_gsc_hw_is_ready_null, ++ .hw_reset = mei_gsc_hw_reset_null, ++ .hw_config = mei_gsc_hw_config_null, ++ .hw_start = mei_gsc_hw_start_null, ++ ++ .pg_in_transition = mei_gsc_pg_in_transition_null, ++ .pg_is_enabled = mei_gsc_pg_is_enabled_null, ++ ++ .intr_clear = mei_gsc_intr_clear_null, ++ .intr_enable = mei_gsc_intr_enable_null, ++ .intr_disable = mei_gsc_intr_disable_null, ++ .synchronize_irq = mei_gsc_synchronize_irq_null, ++ ++ .hbuf_free_slots = mei_gsc_hbuf_empty_slots_null, ++ .hbuf_is_ready = mei_gsc_hbuf_is_empty_null, ++ .hbuf_depth = mei_gsc_hbuf_depth_null, ++ ++ .write = mei_gsc_hbuf_write_null, ++ ++ .rdbuf_full_slots = mei_gsc_count_full_read_slots_null, ++ .read_hdr = mei_gsc_mecbrw_read_null, ++ .read = mei_gsc_read_slots_null ++}; ++ + static int mei_gsc_probe(struct auxiliary_device *aux_dev, + const struct auxiliary_device_id *aux_dev_id) + { +@@ -143,6 +433,8 @@ static void mei_gsc_remove(struct auxiliary_device *aux_dev) + + dev = dev_get_drvdata(&aux_dev->dev); + hw = to_me_hw(dev); ++ if (mei_gsc_hw_is_unavailable(&aux_dev->dev)) ++ dev->ops = &mei_gsc_hw_ops_null; + + mei_stop(dev); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi b/SPECS/kernel-rt/0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi deleted file mode 100644 index 16a00f006..000000000 --- a/SPECS/kernel-rt/0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi +++ /dev/null @@ -1,86 +0,0 @@ -From f2415943450c73de269f65d31e4fb685167447e5 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 31 Aug 2023 01:23:00 -0700 -Subject: [PATCH 08/44] KVM: VMX: Save/restore guest FRED RSP0 - -Save guest FRED RSP0 in vmx_prepare_switch_to_host() and restore it -in vmx_prepare_switch_to_guest() because MSR_IA32_FRED_RSP0 is passed -through to the guest, thus is volatile/unknown. - -Note, host FRED RSP0 is restored in arch_exit_to_user_mode_prepare(), -regardless of whether it is modified in KVM. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Remove the cpu_feature_enabled() check when set/get guest - MSR_IA32_FRED_RSP0, as guest_cpu_cap_has() should suffice (Sean). -* Add a comment when synchronizing current MSR_IA32_FRED_RSP0 MSR to - the kernel's local cache, because its handling is different from - the MSR_KERNEL_GS_BASE handling (Sean). -* Add TB from Xuelian Guo. - -Changes in v3: -* KVM only needs to save/restore guest FRED RSP0 now as host FRED RSP0 - is restored in arch_exit_to_user_mode_prepare() (Sean Christopherson). - -Changes in v2: -* Don't use guest_cpuid_has() in vmx_prepare_switch_to_{host,guest}(), - which are called from IRQ-disabled context (Chao Gao). -* Reset msr_guest_fred_rsp0 in __vmx_vcpu_reset() (Chao Gao). ---- - arch/x86/kvm/vmx/vmx.c | 14 ++++++++++++++ - arch/x86/kvm/vmx/vmx.h | 1 + - 2 files changed, 15 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 2a40dfe9f034..30322c4897cb 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1295,6 +1295,10 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) - } - - wrmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ wrmsrns(MSR_IA32_FRED_RSP0, vmx->msr_guest_fred_rsp0); -+ - #else - savesegment(fs, fs_sel); - savesegment(gs, gs_sel); -@@ -1339,6 +1343,16 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx) - invalidate_tss_limit(); - #ifdef CONFIG_X86_64 - wrmsrq(MSR_KERNEL_GS_BASE, vmx->vt.msr_host_kernel_gs_base); -+ -+ if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED)) { -+ vmx->msr_guest_fred_rsp0 = read_msr(MSR_IA32_FRED_RSP0); -+ /* -+ * Synchronize the current value in hardware to the kernel's -+ * local cache. The desired host RSP0 will be set when the -+ * CPU exits to userspace (RSP0 is a per-task value). -+ */ -+ fred_sync_rsp0(vmx->msr_guest_fred_rsp0); -+ } - #endif - load_fixmap_gdt(raw_smp_processor_id()); - vmx->vt.guest_state_loaded = false; -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 50656aa3f57a..05fa52a7581c 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -227,6 +227,7 @@ struct vcpu_vmx { - bool guest_uret_msrs_loaded; - #ifdef CONFIG_X86_64 - u64 msr_guest_kernel_gs_base; -+ u64 msr_guest_fred_rsp0; - #endif - - u64 spec_ctrl; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi b/SPECS/kernel-rt/0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi new file mode 100644 index 000000000..872a4c8a3 --- /dev/null +++ b/SPECS/kernel-rt/0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi @@ -0,0 +1,120 @@ +From d88c4e614915e2887af1eaf55bd4d49cda3ba8d6 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 31 Aug 2023 00:56:30 -0700 +Subject: [PATCH 08/44] KVM: VMX: Set FRED MSR intercepts + +On a userspace MSR filter change, set FRED MSR intercepts. + +The eight FRED MSRs, MSR_IA32_FRED_RSP[123], MSR_IA32_FRED_STKLVLS, +MSR_IA32_FRED_SSP[123] and MSR_IA32_FRED_CONFIG, are all safe to +passthrough, because each has a corresponding host and guest field +in VMCS. + +Both MSR_IA32_FRED_RSP0 and MSR_IA32_FRED_SSP0 (aka MSR_IA32_PL0_SSP) +are dedicated for userspace event delivery, IOW they are NOT used in +any kernel event delivery and the execution of ERETS. Thus KVM can +run safely with guest values in the two MSRs. As a result, save and +restore of their guest values are deferred until vCPU context switch, +Host MSR_IA32_FRED_RSP0 is restored upon returning to userspace, and +Host MSR_IA32_PL0_SSP is managed with XRSTORS/XSAVES. + +Note, FRED SSP MSRs, including MSR_IA32_PL0_SSP, are available on +any processor that enumerates FRED. On processors that support FRED +but not CET, FRED transitions do not use these MSRs, but they remain +accessible via MSR instructions such as RDMSR and WRMSR. + +Intercept MSR_IA32_PL0_SSP when CET shadow stack is not supported, +regardless of FRED support. This ensures the guest value remains +fully virtual and does not modify the hardware FRED SSP0 MSR. + +This behavior is consistent with the current setup in +vmx_recalc_msr_intercepts(), so no change is needed to the interception +logic for MSR_IA32_PL0_SSP. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v7: +* Rewrite the changelog and comment, majorly for MSR_IA32_PL0_SSP. + +Changes in v5: +* Skip execution of vmx_set_intercept_for_fred_msr() if FRED is + not available or enabled (Sean). +* Use 'intercept' as the variable name to indicate whether MSR + interception should be enabled (Sean). +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/vmx/vmx.c | 47 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 94f88bf8bb785..1c639496a7562 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -4127,6 +4127,51 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) + } + } + ++static void vmx_set_intercept_for_fred_msr(struct kvm_vcpu *vcpu) ++{ ++ bool intercept = !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED); ++ ++ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) ++ return; ++ ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP1, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP2, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP3, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_STKLVLS, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP1, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP2, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP3, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_CONFIG, MSR_TYPE_RW, intercept); ++ ++ /* ++ * MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) are ++ * designed for event delivery while executing in userspace. Since KVM ++ * operates entirely in kernel mode (CPL is always 0 after any VM exit), ++ * it can safely retain and operate with guest-defined values for these ++ * MSRs. ++ * ++ * As a result, interception of MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP ++ * is unnecessary. ++ * ++ * Note: Saving and restoring MSR_IA32_PL0_SSP is part of CET supervisor ++ * context management. However, FRED SSP MSRs, including MSR_IA32_PL0_SSP, ++ * are available on any processor that enumerates FRED. ++ * ++ * On processors that support FRED but not CET, FRED transitions do not ++ * use these MSRs, but they remain accessible via MSR instructions such ++ * as RDMSR and WRMSR. ++ * ++ * Intercept MSR_IA32_PL0_SSP when CET shadow stack is not supported, ++ * regardless of FRED support. This ensures the guest value remains ++ * fully virtual and does not modify the hardware FRED SSP0 MSR. ++ * ++ * This behavior is consistent with the current setup in ++ * vmx_recalc_msr_intercepts(), so no change is needed to the interception ++ * logic for MSR_IA32_PL0_SSP. ++ */ ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP0, MSR_TYPE_RW, intercept); ++} ++ + static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) + { + bool intercept; +@@ -4193,6 +4238,8 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) + vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, intercept); + } + ++ vmx_set_intercept_for_fred_msr(vcpu); ++ + /* + * x2APIC and LBR MSR intercepts are modified on-demand and cannot be + * filtered by userspace. +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet b/SPECS/kernel-rt/0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet deleted file mode 100644 index afc7673c6..000000000 --- a/SPECS/kernel-rt/0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet +++ /dev/null @@ -1,123 +0,0 @@ -From aa07693d9d006cf602f6fc1140a5d93b76f874b7 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:38 -0700 -Subject: [PATCH 08/24] KVM: x86: Refresh CPUID on write to guest MSR_IA32_XSS - -Update CPUID.(EAX=0DH,ECX=1).EBX to reflect current required xstate size -due to XSS MSR modification. -CPUID(EAX=0DH,ECX=1).EBX reports the required storage size of all enabled -xstate features in (XCR0 | IA32_XSS). The CPUID value can be used by guest -before allocate sufficient xsave buffer. - -Note, KVM does not yet support any XSS based features, i.e. supported_xss -is guaranteed to be zero at this time. - -Opportunistically return KVM_MSR_RET_UNSUPPORTED if guest CPUID doesn't -enumerate it. Since KVM_MSR_RET_UNSUPPORTED takes care of host_initiated -cases, drop the host_initiated check. - -Suggested-by: Sean Christopherson -Co-developed-by: Zhang Yi Z -Signed-off-by: Zhang Yi Z -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 3 ++- - arch/x86/kvm/cpuid.c | 15 ++++++++++++++- - arch/x86/kvm/x86.c | 9 +++++---- - 3 files changed, 21 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 475a81620e88..6b567512984a 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -816,7 +816,6 @@ struct kvm_vcpu_arch { - bool at_instruction_boundary; - bool tpr_access_reporting; - bool xfd_no_write_intercept; -- u64 ia32_xss; - u64 microcode_version; - u64 arch_capabilities; - u64 perf_capabilities; -@@ -877,6 +876,8 @@ struct kvm_vcpu_arch { - - u64 xcr0; - u64 guest_supported_xcr0; -+ u64 guest_supported_xss; -+ u64 ia32_xss; - - struct kvm_pio_request pio; - void *pio_data; -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index 5e43a4999a4b..b9f278efb907 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -263,6 +263,17 @@ static u64 cpuid_get_supported_xcr0(struct kvm_vcpu *vcpu) - return (best->eax | ((u64)best->edx << 32)) & kvm_caps.supported_xcr0; - } - -+static u64 cpuid_get_supported_xss(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_cpuid_entry2 *best; -+ -+ best = kvm_find_cpuid_entry_index(vcpu, 0xd, 1); -+ if (!best) -+ return 0; -+ -+ return (best->ecx | ((u64)best->edx << 32)) & kvm_caps.supported_xss; -+} -+ - static __always_inline void kvm_update_feature_runtime(struct kvm_vcpu *vcpu, - struct kvm_cpuid_entry2 *entry, - unsigned int x86_feature, -@@ -305,7 +316,8 @@ static void kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu) - best = kvm_find_cpuid_entry_index(vcpu, 0xD, 1); - if (best && (cpuid_entry_has(best, X86_FEATURE_XSAVES) || - cpuid_entry_has(best, X86_FEATURE_XSAVEC))) -- best->ebx = xstate_required_size(vcpu->arch.xcr0, true); -+ best->ebx = xstate_required_size(vcpu->arch.xcr0 | -+ vcpu->arch.ia32_xss, true); - } - - static bool kvm_cpuid_has_hyperv(struct kvm_vcpu *vcpu) -@@ -424,6 +436,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - } - - vcpu->arch.guest_supported_xcr0 = cpuid_get_supported_xcr0(vcpu); -+ vcpu->arch.guest_supported_xss = cpuid_get_supported_xss(vcpu); - - vcpu->arch.pv_cpuid.features = kvm_apply_cpuid_pv_features_quirk(vcpu); - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 3445a446e4ba..3057ae88b6ba 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -3976,16 +3976,17 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - } - break; - case MSR_IA32_XSS: -- if (!msr_info->host_initiated && -- !guest_cpuid_has(vcpu, X86_FEATURE_XSAVES)) -- return 1; -+ if (!guest_cpuid_has(vcpu, X86_FEATURE_XSAVES)) -+ return KVM_MSR_RET_UNSUPPORTED; - /* - * KVM supports exposing PT to the guest, but does not support - * IA32_XSS[bit 8]. Guests have to use RDMSR/WRMSR rather than - * XSAVES/XRSTORS to save/restore PT MSRs. - */ -- if (data & ~kvm_caps.supported_xss) -+ if (data & ~vcpu->arch.guest_supported_xss) - return 1; -+ if (vcpu->arch.ia32_xss == data) -+ break; - vcpu->arch.ia32_xss = data; - vcpu->arch.cpuid_dynamic_bits_dirty = true; - break; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt b/SPECS/kernel-rt/0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt new file mode 100644 index 000000000..1c26af588 --- /dev/null +++ b/SPECS/kernel-rt/0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt @@ -0,0 +1,29 @@ +From 38d8a801c66aa0397b4503490dc3a092650b6a0c Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Mon, 21 Feb 2022 17:59:14 +0100 +Subject: [PATCH 8/9] Revert "drm/i915: Depend on !PREEMPT_RT." + +Once the known issues are addressed, it should be safe to enable the +driver. + +Acked-by: Tvrtko Ursulin +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig +index 5e939004b6463..40a9234e6e5dc 100644 +--- a/drivers/gpu/drm/i915/Kconfig ++++ b/drivers/gpu/drm/i915/Kconfig +@@ -3,7 +3,6 @@ config DRM_I915 + tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" + depends on DRM + depends on X86 && PCI +- depends on !PREEMPT_RT + select INTEL_GTT if X86 + select INTERVAL_TREE + # we need shmfs for the swappable backing store, and in particular +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl b/SPECS/kernel-rt/0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl new file mode 100644 index 000000000..9dde435ad --- /dev/null +++ b/SPECS/kernel-rt/0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl @@ -0,0 +1,91 @@ +From aabb3d25c197c2cc2d8b7d0234e9ce73a0725125 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Wed, 12 Nov 2025 19:03:08 +0100 +Subject: [PATCH 08/17] cpuidle: governors: teo: Decay metrics below + DECAY_SHIFT threshold + +If a given governor metric falls below a certain value (8 for +DECAY_SHIFT equal to 3), it will not decay any more due to the +simplistic decay implementation. This may in some cases lead to +subtle inconsistencies in the governor behavior, so change the +decay implementation to take it into account and set the metric +at hand to 0 in that case. + +Suggested-by: Christian Loehle +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Tested-by: Christian Loehle +Link: https://patch.msgid.link/2819353.mvXUDI8C0e@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 88ed47e868b9c..8b80d73e518ed 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -148,6 +148,16 @@ struct teo_cpu { + + static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); + ++static void teo_decay(unsigned int *metric) ++{ ++ unsigned int delta = *metric >> DECAY_SHIFT; ++ ++ if (delta) ++ *metric -= delta; ++ else ++ *metric = 0; ++} ++ + /** + * teo_update - Update CPU metrics after wakeup. + * @drv: cpuidle driver containing state data. +@@ -158,8 +168,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + int i, idx_timer = 0, idx_duration = 0; + s64 target_residency_ns, measured_ns; ++ unsigned int total = 0; + +- cpu_data->short_idles -= cpu_data->short_idles >> DECAY_SHIFT; ++ teo_decay(&cpu_data->short_idles); + + if (cpu_data->artificial_wakeup) { + /* +@@ -195,8 +206,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + for (i = 0; i < drv->state_count; i++) { + struct teo_bin *bin = &cpu_data->state_bins[i]; + +- bin->hits -= bin->hits >> DECAY_SHIFT; +- bin->intercepts -= bin->intercepts >> DECAY_SHIFT; ++ teo_decay(&bin->hits); ++ total += bin->hits; ++ teo_decay(&bin->intercepts); ++ total += bin->intercepts; + + target_residency_ns = drv->states[i].target_residency_ns; + +@@ -207,7 +220,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + } + } + +- cpu_data->tick_intercepts -= cpu_data->tick_intercepts >> DECAY_SHIFT; ++ cpu_data->total = total + PULSE; ++ ++ teo_decay(&cpu_data->tick_intercepts); + /* + * If the measured idle duration falls into the same bin as the sleep + * length, this is a "hit", so update the "hits" metric for that bin. +@@ -221,9 +236,6 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + if (TICK_NSEC <= measured_ns) + cpu_data->tick_intercepts += PULSE; + } +- +- cpu_data->total -= cpu_data->total >> DECAY_SHIFT; +- cpu_data->total += PULSE; + } + + static bool teo_state_ok(int i, struct cpuidle_driver *drv) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-drm-i915-Consider-RCU-read-section-as-atomic.rt b/SPECS/kernel-rt/0008-drm-i915-Consider-RCU-read-section-as-atomic.rt deleted file mode 100644 index 8de405be5..000000000 --- a/SPECS/kernel-rt/0008-drm-i915-Consider-RCU-read-section-as-atomic.rt +++ /dev/null @@ -1,37 +0,0 @@ -From 71b2435a33f7ac8a2b1ae42a0d7730fc246fcb82 Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Mon, 14 Jul 2025 17:39:53 +0200 -Subject: [PATCH 8/9] drm/i915: Consider RCU read section as atomic. - -Locking and/ or running inside interrupt handler will not lead to an -atomic section on PREEMPT_RT. The RCU read section needs to be -considered because all locks, which become sleeping locks on -PREEMPT_RT, start a RCU read section. Scheduling/ sleeping while within -a RCU read section is invalid. - -Check for also for RCU read section in stop_timeout() to determine if it -is safe to sleep. - -Reviewed-by: Rodrigo Vivi -Link: https://lore.kernel.org/r/20250714153954.629393-9-bigeasy@linutronix.de -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/gt/intel_engine_cs.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c -index b721bbd23356..f5a6143ea8a2 100644 ---- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c -+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c -@@ -1609,7 +1609,7 @@ u64 intel_engine_get_last_batch_head(const struct intel_engine_cs *engine) - - static unsigned long stop_timeout(const struct intel_engine_cs *engine) - { -- if (in_atomic() || irqs_disabled()) /* inside atomic preempt-reset? */ -+ if (in_atomic() || irqs_disabled() || rcu_preempt_depth()) /* inside atomic preempt-reset? */ - return 0; - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-erofs-limit-the-level-of-fs-stacking-for-file-backed.patch b/SPECS/kernel-rt/0008-erofs-limit-the-level-of-fs-stacking-for-file-backed.patch deleted file mode 100644 index a1df1facd..000000000 --- a/SPECS/kernel-rt/0008-erofs-limit-the-level-of-fs-stacking-for-file-backed.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 9faaff36c08f369350f515943ad8ceb47bbfd8c7 Mon Sep 17 00:00:00 2001 -From: Gao Xiang -Date: Sat, 22 Nov 2025 14:23:32 +0800 -Subject: [PATCH 08/51] erofs: limit the level of fs stacking for file-backed - mounts - -Otherwise, it could cause potential kernel stack overflow (e.g., EROFS -mounting itself). - -Reviewed-by: Sheng Yong -Fixes: fb176750266a ("erofs: add file-backed mount support") -Reviewed-by: Chao Yu -Reviewed-by: Hongbo Li -Signed-off-by: Gao Xiang ---- - fs/erofs/super.c | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/fs/erofs/super.c b/fs/erofs/super.c -index db13b40a78e0..09807699b15c 100644 ---- a/fs/erofs/super.c -+++ b/fs/erofs/super.c -@@ -632,6 +632,22 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) - - sbi->blkszbits = PAGE_SHIFT; - if (!sb->s_bdev) { -+ /* -+ * (File-backed mounts) EROFS claims it's safe to nest other -+ * fs contexts (including its own) due to self-controlled RO -+ * accesses/contexts and no side-effect changes that need to -+ * context save & restore so it can reuse the current thread -+ * context. However, it still needs to bump `s_stack_depth` to -+ * avoid kernel stack overflow from nested filesystems. -+ */ -+ if (erofs_is_fileio_mode(sbi)) { -+ sb->s_stack_depth = -+ file_inode(sbi->dif0.file)->i_sb->s_stack_depth + 1; -+ if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { -+ erofs_err(sb, "maximum fs stacking depth exceeded"); -+ return -ENOTBLK; -+ } -+ } - sb->s_blocksize = PAGE_SIZE; - sb->s_blocksize_bits = PAGE_SHIFT; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c b/SPECS/kernel-rt/0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c deleted file mode 100644 index 8e201c8b7..000000000 --- a/SPECS/kernel-rt/0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c +++ /dev/null @@ -1,40 +0,0 @@ -From 1d4ae1aec57b03cebaa41feda361f1ed93357373 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:07 +0300 -Subject: [PATCH 08/11] i3c: mipi-i3c-hci: Uniform ring number printouts - -Use the same "Ring" prefix in all prints that print out the ring number. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250827103009.243771-4-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/dma.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 8bc9de189543..3fadacbda582 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -775,7 +775,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - u32 ring_status; - - dev_notice_ratelimited(&hci->master.dev, -- "ring %d: Transfer Aborted\n", i); -+ "Ring %d: Transfer Aborted\n", i); - mipi_i3c_hci_resume(hci); - ring_status = rh_reg_read(RING_STATUS); - if (!(ring_status & RING_STATUS_RUNNING) && -@@ -795,7 +795,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - } - if (status & INTR_IBI_RING_FULL) - dev_err_ratelimited(&hci->master.dev, -- "ring %d: IBI Ring Full Condition\n", i); -+ "Ring %d: IBI Ring Full Condition\n", i); - - handled = true; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-igc-Add-BTF-based-metadata-for-XDP.ethernet b/SPECS/kernel-rt/0008-igc-Add-BTF-based-metadata-for-XDP.ethernet new file mode 100644 index 000000000..a583b6144 --- /dev/null +++ b/SPECS/kernel-rt/0008-igc-Add-BTF-based-metadata-for-XDP.ethernet @@ -0,0 +1,235 @@ +From 9a080a8e0dd97fb5f78348640863abc59ba8c448 Mon Sep 17 00:00:00 2001 +From: Vedang Patel +Date: Mon, 14 Jun 2021 00:35:20 +0800 +Subject: [PATCH 08/14] igc: Add BTF based metadata for XDP + +This commit adds support for BTF based metadata for XDP. Currently, the +support has only been tested on receive side. Following is the struct +describing the metadata: + +struct xdp_md_desc { + u64 timestamp; +}; + +Note that only a single member is added to the struct. More members will +be added in the future. + +Signed-off-by: Vedang Patel +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc.h | 2 + + drivers/net/ethernet/intel/igc/igc_main.c | 12 +++ + drivers/net/ethernet/intel/igc/igc_xdp.c | 114 ++++++++++++++++++++++ + drivers/net/ethernet/intel/igc/igc_xdp.h | 11 +++ + 4 files changed, 139 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index b9f846389e75e..64d22481be91e 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -331,6 +331,8 @@ struct igc_adapter { + char fw_version[32]; + + struct bpf_prog *xdp_prog; ++ struct btf *btf; ++ u8 btf_enabled; + + bool pps_sys_wrap_on; + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 9b52d60ca315c..526b98b603c05 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -14,6 +14,7 @@ + #include + #include + ++#include + #include + + #include "igc.h" +@@ -6880,6 +6881,12 @@ static int igc_bpf(struct net_device *dev, struct netdev_bpf *bpf) + case XDP_SETUP_XSK_POOL: + return igc_xdp_setup_pool(adapter, bpf->xsk.pool, + bpf->xsk.queue_id); ++ case XDP_SETUP_MD_BTF: ++ return igc_xdp_set_btf_md(dev, bpf->btf_enable); ++ case XDP_QUERY_MD_BTF: ++ bpf->btf_id = igc_xdp_query_btf(dev, &bpf->btf_enable); ++ return 0; ++ + default: + return -EOPNOTSUPP; + } +@@ -7420,6 +7427,11 @@ static void igc_remove(struct pci_dev *pdev) + if (IS_ENABLED(CONFIG_IGC_LEDS) && adapter->leds_available) + igc_led_free(adapter); + ++ if (adapter->btf) { ++ adapter->btf_enabled = 0; ++ btf_unregister(adapter->btf); ++ } ++ + /* Release control of h/w to f/w. If f/w is AMT enabled, this + * would have already happened in close and is redundant. + */ +diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.c b/drivers/net/ethernet/intel/igc/igc_xdp.c +index 9eb47b4beb062..c23e0385fc74a 100644 +--- a/drivers/net/ethernet/intel/igc/igc_xdp.c ++++ b/drivers/net/ethernet/intel/igc/igc_xdp.c +@@ -3,10 +3,124 @@ + + #include + #include ++#include + + #include "igc.h" + #include "igc_xdp.h" + ++#define BTF_INFO_ENC(kind, kind_flag, vlen) \ ++ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) ++ ++#define BTF_TYPE_ENC(name, info, size_or_type) \ ++ (name), (info), (size_or_type) ++ ++#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \ ++ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits)) ++ ++#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \ ++ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \ ++ BTF_INT_ENC(encoding, bits_offset, bits) ++ ++#define BTF_STRUCT_ENC(name, nr_elems, sz) \ ++ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, nr_elems), sz) ++ ++#define BTF_MEMBER_ENC(name, type, bits_offset) \ ++ (name), (type), (bits_offset) ++ ++/* struct xdp_md_desc { ++ * u64 timestamp; ++ * }; ++ */ ++#define IGC_MD_NUM_MMBRS 1 ++static const char names_str[] = "\0xdp_md_desc\0timestamp\0"; ++ ++/* Must match struct xdp_md_desc */ ++static const u32 igc_md_raw_types[] = { ++ /* #define u64 */ ++ BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* type [1] */ ++ /* struct xdp_md_desc { */ ++ BTF_STRUCT_ENC(1, IGC_MD_NUM_MMBRS, 8), ++ BTF_MEMBER_ENC(13, 1, 0), /* u64 timestamp; */ ++ /* } */ ++}; ++ ++static int igc_xdp_register_btf(struct igc_adapter *priv) ++{ ++ unsigned int type_sec_sz, str_sec_sz; ++ char *types_sec, *str_sec; ++ struct btf_header *hdr; ++ unsigned int btf_size; ++ void *raw_btf = NULL; ++ int err = 0; ++ ++ type_sec_sz = sizeof(igc_md_raw_types); ++ str_sec_sz = sizeof(names_str); ++ ++ btf_size = sizeof(*hdr) + type_sec_sz + str_sec_sz; ++ raw_btf = kzalloc(btf_size, GFP_KERNEL); ++ if (!raw_btf) ++ return -ENOMEM; ++ ++ hdr = raw_btf; ++ hdr->magic = BTF_MAGIC; ++ hdr->version = BTF_VERSION; ++ hdr->hdr_len = sizeof(*hdr); ++ hdr->type_off = 0; ++ hdr->type_len = type_sec_sz; ++ hdr->str_off = type_sec_sz; ++ hdr->str_len = str_sec_sz; ++ ++ types_sec = raw_btf + sizeof(*hdr); ++ str_sec = types_sec + type_sec_sz; ++ memcpy(types_sec, igc_md_raw_types, type_sec_sz); ++ memcpy(str_sec, names_str, str_sec_sz); ++ ++ priv->btf = btf_register(priv->netdev->name, raw_btf, btf_size); ++ if (IS_ERR(priv->btf)) { ++ err = PTR_ERR(priv->btf); ++ priv->btf = NULL; ++ netdev_err(priv->netdev, "failed to register BTF MD, err (%d)\n", err); ++ } ++ ++ kfree(raw_btf); ++ return err; ++} ++ ++int igc_xdp_query_btf(struct net_device *dev, u8 *enabled) ++{ ++ struct igc_adapter *priv = netdev_priv(dev); ++ u32 md_btf_id = 0; ++ ++ if (!IS_ENABLED(CONFIG_BPF_SYSCALL)) ++ return md_btf_id; ++ ++ if (!priv->btf) ++ igc_xdp_register_btf(priv); ++ ++ *enabled = !!priv->btf_enabled; ++ md_btf_id = priv->btf ? btf_obj_id(priv->btf) : 0; ++ ++ return md_btf_id; ++} ++ ++int igc_xdp_set_btf_md(struct net_device *dev, u8 enable) ++{ ++ struct igc_adapter *priv = netdev_priv(dev); ++ int err = 0; ++ ++ if (enable && !priv->btf) { ++ igc_xdp_register_btf(priv); ++ if (!priv->btf) { ++ err = -EINVAL; ++ goto unlock; ++ } ++ } ++ ++ priv->btf_enabled = enable; ++unlock: ++ return err; ++} ++ + int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, + struct netlink_ext_ack *extack) + { +diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.h b/drivers/net/ethernet/intel/igc/igc_xdp.h +index a74e5487d1998..644dd8a49a3a6 100644 +--- a/drivers/net/ethernet/intel/igc/igc_xdp.h ++++ b/drivers/net/ethernet/intel/igc/igc_xdp.h +@@ -4,6 +4,12 @@ + #ifndef _IGC_XDP_H_ + #define _IGC_XDP_H_ + ++#include ++ ++struct igc_md_desc { ++ u64 timestamp; ++}; ++ + int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, + struct netlink_ext_ack *extack); + int igc_xdp_setup_pool(struct igc_adapter *adapter, struct xsk_buff_pool *pool, +@@ -14,4 +20,9 @@ static inline bool igc_xdp_is_enabled(struct igc_adapter *adapter) + return !!adapter->xdp_prog; + } + ++int igc_xdp_register_rxq_info(struct igc_ring *ring); ++void igc_xdp_unregister_rxq_info(struct igc_ring *ring); ++int igc_xdp_query_btf(struct net_device *dev, u8 *enabled); ++int igc_xdp_set_btf_md(struct net_device *dev, u8 enable); ++ + #endif /* _IGC_XDP_H_ */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu b/SPECS/kernel-rt/0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu deleted file mode 100644 index 8e5ed354d..000000000 --- a/SPECS/kernel-rt/0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu +++ /dev/null @@ -1,34 +0,0 @@ -From 643f25a3e375b8e907fc17630159b06ee9ad4fc9 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Thu, 4 Dec 2025 11:19:32 +0800 -Subject: [PATCH 08/21] media: ipu: invalidate MMU TLB in dma buffers creation - -This patch ensures that the MMU TLB is properly invalidated during -the creation and mapping of DMA buffers for IPU devices. Without -explicit invalidation, stale or incorrect entries in the TLB can cause -invalid memory access in hardware. - -Test Platform: -PTL CRB FAB B B0 silicon - -Signed-off-by: hepengpx ---- - drivers/staging/media/ipu7/ipu7-dma.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c -index a118b41b2f34..a0bdf6c37f96 100644 ---- a/drivers/staging/media/ipu7/ipu7-dma.c -+++ b/drivers/staging/media/ipu7/ipu7-dma.c -@@ -206,6 +206,8 @@ void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, - } - } - -+ mmu->tlb_invalidate(mmu); -+ - info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); - if (!info->vaddr) - goto out_unmap; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu b/SPECS/kernel-rt/0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu deleted file mode 100644 index 884b2e6d5..000000000 --- a/SPECS/kernel-rt/0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu +++ /dev/null @@ -1,35 +0,0 @@ -From c31079215991ef56964aa96acfd8516590c263da Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 6 Oct 2025 08:13:16 +0800 -Subject: [PATCH 08/11] media: pci: Enable IPU7 in Makefile & Kconfig - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/Kconfig | 1 + - drivers/media/pci/intel/Makefile | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index d9fcddce028b..948cda08fff5 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -2,6 +2,7 @@ - - source "drivers/media/pci/intel/ipu3/Kconfig" - source "drivers/media/pci/intel/ipu6/Kconfig" -+source "drivers/media/pci/intel/ipu7/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - - config IPU_BRIDGE -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index 3a2cc6567159..ff0fea13422d 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ - obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet b/SPECS/kernel-rt/0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet new file mode 100644 index 000000000..62f828446 --- /dev/null +++ b/SPECS/kernel-rt/0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet @@ -0,0 +1,32 @@ +From 674b9153b1c57fa9deac9efb6b0debaf52b746a5 Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Tue, 23 Aug 2022 12:05:21 +0800 +Subject: [PATCH 08/18] net: stmmac: add check for 2.5G mode to prevent MAC + capability update + +Checking the 2.5G capabilities to prevent other capabilities +from turning on, since we cannot switch from 2.5G to other +speeds dynamically. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +index d85bc0bb5c3c0..07357df01a1d2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -69,7 +69,8 @@ static void dwmac4_core_init(struct mac_device_info *hw, + + static void dwmac4_update_caps(struct stmmac_priv *priv) + { +- if (priv->plat->tx_queues_to_use > 1) ++ if (priv->plat->tx_queues_to_use > 1 && ++ !priv->plat->fixed_2G5_clock_rate) + priv->hw->link.caps &= ~(MAC_10HD | MAC_100HD | MAC_1000HD); + else + priv->hw->link.caps |= (MAC_10HD | MAC_100HD | MAC_1000HD); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf b/SPECS/kernel-rt/0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf new file mode 100644 index 000000000..45a58242c --- /dev/null +++ b/SPECS/kernel-rt/0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf @@ -0,0 +1,156 @@ +From 8dfdb82882eb7e7783c54be3085ceb1b810f0f45 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Thu, 11 Dec 2025 10:04:29 -0800 +Subject: [PATCH 08/13] perf/x86/intel/uncore: Add freerunning event descriptor + helper macro + +Freerunning counter events are repetitive: the event code is fixed to +0xff, the unit is always "MiB", and the scale is identical across all +counters on a given PMON unit. + +Introduce a new helper macro, INTEL_UNCORE_FR_EVENT_DESC(), to populate +the event, scale, and unit descriptor triplet. This reduces duplicated +lines and improves readability. + +No functional change intended. + +Reviewed-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore_snbep.c | 95 ++++++++-------------------- + 1 file changed, 28 insertions(+), 67 deletions(-) + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index df173534637a6..09a3bdbd188aa 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -4068,34 +4068,24 @@ static struct freerunning_counters skx_iio_freerunning[] = { + [SKX_IIO_MSR_UTIL] = { 0xb08, 0x1, 0x10, 8, 36 }, + }; + ++#define INTEL_UNCORE_FR_EVENT_DESC(name, umask, scl) \ ++ INTEL_UNCORE_EVENT_DESC(name, \ ++ "event=0xff,umask=" __stringify(umask)), \ ++ INTEL_UNCORE_EVENT_DESC(name.scale, __stringify(scl)), \ ++ INTEL_UNCORE_EVENT_DESC(name.unit, "MiB") ++ + static struct uncore_event_desc skx_uncore_iio_freerunning_events[] = { + /* Free-Running IO CLOCKS Counter */ + INTEL_UNCORE_EVENT_DESC(ioclk, "event=0xff,umask=0x10"), + /* Free-Running IIO BANDWIDTH Counters */ +- INTEL_UNCORE_EVENT_DESC(bw_in_port0, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2, "event=0xff,umask=0x22"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3, "event=0xff,umask=0x23"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port0, "event=0xff,umask=0x24"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port0.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port0.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port1, "event=0xff,umask=0x25"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port1.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port1.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port2, "event=0xff,umask=0x26"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port2.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port2.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port3, "event=0xff,umask=0x27"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port3.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port3.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port0, 0x24, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port1, 0x25, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port2, 0x26, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port3, 0x27, 3.814697266e-6), + /* Free-running IIO UTILIZATION Counters */ + INTEL_UNCORE_EVENT_DESC(util_in_port0, "event=0xff,umask=0x30"), + INTEL_UNCORE_EVENT_DESC(util_out_port0, "event=0xff,umask=0x31"), +@@ -4910,30 +4900,14 @@ static struct uncore_event_desc snr_uncore_iio_freerunning_events[] = { + /* Free-Running IIO CLOCKS Counter */ + INTEL_UNCORE_EVENT_DESC(ioclk, "event=0xff,umask=0x10"), + /* Free-Running IIO BANDWIDTH IN Counters */ +- INTEL_UNCORE_EVENT_DESC(bw_in_port0, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2, "event=0xff,umask=0x22"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3, "event=0xff,umask=0x23"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port4, "event=0xff,umask=0x24"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port4.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port4.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port5, "event=0xff,umask=0x25"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port5.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port5.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port6, "event=0xff,umask=0x26"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port6.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port6.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port7, "event=0xff,umask=0x27"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port7.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port7.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port4, 0x24, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port5, 0x25, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port6, 0x26, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port7, 0x27, 3.0517578125e-5), + { /* end: all zeroes */ }, + }; + +@@ -5266,12 +5240,8 @@ static struct freerunning_counters snr_imc_freerunning[] = { + static struct uncore_event_desc snr_uncore_imc_freerunning_events[] = { + INTEL_UNCORE_EVENT_DESC(dclk, "event=0xff,umask=0x10"), + +- INTEL_UNCORE_EVENT_DESC(read, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(read.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(read.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(write, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(write.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(write.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(read, 0x20, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(write, 0x21, 6.103515625e-5), + { /* end: all zeroes */ }, + }; + +@@ -5836,19 +5806,10 @@ static struct freerunning_counters icx_imc_freerunning[] = { + static struct uncore_event_desc icx_uncore_imc_freerunning_events[] = { + INTEL_UNCORE_EVENT_DESC(dclk, "event=0xff,umask=0x10"), + +- INTEL_UNCORE_EVENT_DESC(read, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(read.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(read.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(write, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(write.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(write.unit, "MiB"), +- +- INTEL_UNCORE_EVENT_DESC(ddrt_read, "event=0xff,umask=0x30"), +- INTEL_UNCORE_EVENT_DESC(ddrt_read.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(ddrt_read.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(ddrt_write, "event=0xff,umask=0x31"), +- INTEL_UNCORE_EVENT_DESC(ddrt_write.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(ddrt_write.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(read, 0x20, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(write, 0x21, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(ddrt_read, 0x30, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(ddrt_write, 0x31, 6.103515625e-5), + { /* end: all zeroes */ }, + }; + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0008-spi-intel-Add-support-for-128M-component-density.lpss b/SPECS/kernel-rt/0008-spi-intel-Add-support-for-128M-component-density.lpss deleted file mode 100644 index a79bb01d4..000000000 --- a/SPECS/kernel-rt/0008-spi-intel-Add-support-for-128M-component-density.lpss +++ /dev/null @@ -1,42 +0,0 @@ -From d68c39a40f5b4504b044183ff28a8bad5912c655 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Wed, 28 May 2025 11:07:28 +0300 -Subject: [PATCH 08/16] spi: intel: Add support for 128M component density - -With the recent hardware the flash component density can be increased to -128M. Update the driver to support this. While there log a warning if we -encounter a unsupported value in this field. - -Signed-off-by: Mika Westerberg ---- - drivers/spi/spi-intel.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c -index 13bbb2133507..1775ad39e633 100644 ---- a/drivers/spi/spi-intel.c -+++ b/drivers/spi/spi-intel.c -@@ -132,6 +132,7 @@ - #define FLCOMP_C0DEN_16M 0x05 - #define FLCOMP_C0DEN_32M 0x06 - #define FLCOMP_C0DEN_64M 0x07 -+#define FLCOMP_C0DEN_128M 0x08 - - #define INTEL_SPI_TIMEOUT 5000 /* ms */ - #define INTEL_SPI_FIFO_SZ 64 -@@ -1347,7 +1348,12 @@ static int intel_spi_read_desc(struct intel_spi *ispi) - case FLCOMP_C0DEN_64M: - ispi->chip0_size = SZ_64M; - break; -+ case FLCOMP_C0DEN_128M: -+ ispi->chip0_size = SZ_128M; -+ break; - default: -+ dev_warn(ispi->dev, "unsupported C0DEN: %#lx\n", -+ flcomp & FLCOMP_C0DEN_MASK); - return -EINVAL; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-thermal-testing-Rearrange-variable-declarations-in.thermal b/SPECS/kernel-rt/0008-thermal-testing-Rearrange-variable-declarations-in.thermal deleted file mode 100644 index 53d16bbbf..000000000 --- a/SPECS/kernel-rt/0008-thermal-testing-Rearrange-variable-declarations-in.thermal +++ /dev/null @@ -1,125 +0,0 @@ -From f355872ec97e33a9d2ac43196c8c24304e5f5220 Mon Sep 17 00:00:00 2001 -From: "Rafael J. Wysocki" -Date: Wed, 3 Sep 2025 16:52:45 +0200 -Subject: [PATCH 08/11] thermal: testing: Rearrange variable declarations - involving __free() - -Follow cleanup.h recommendations and always define and assign variables -in one statement when __free() is used. - -No intentional functional impact. - -Signed-off-by: Rafael J. Wysocki -Reviewed-by: Krzysztof Kozlowski -Link: https://patch.msgid.link/5934556.DvuYhMxLoT@rafael.j.wysocki ---- - drivers/thermal/testing/zone.c | 31 +++++++++++-------------------- - 1 file changed, 11 insertions(+), 20 deletions(-) - -diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c -index 4257d813d572..c12c405225bb 100644 ---- a/drivers/thermal/testing/zone.c -+++ b/drivers/thermal/testing/zone.c -@@ -184,15 +184,14 @@ static void tt_add_tz_work_fn(struct work_struct *work) - - int tt_add_tz(void) - { -- struct tt_thermal_zone *tt_zone __free(kfree); -- struct tt_work *tt_work __free(kfree) = NULL; - int ret; - -- tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); -+ struct tt_thermal_zone *tt_zone __free(kfree) = kzalloc(sizeof(*tt_zone), -+ GFP_KERNEL); - if (!tt_zone) - return -ENOMEM; - -- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); -+ struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL); - if (!tt_work) - return -ENOMEM; - -@@ -237,7 +236,6 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) - - int tt_del_tz(const char *arg) - { -- struct tt_work *tt_work __free(kfree) = NULL; - struct tt_thermal_zone *tt_zone, *aux; - int ret; - int id; -@@ -246,7 +244,7 @@ int tt_del_tz(const char *arg) - if (ret != 1) - return -EINVAL; - -- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); -+ struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL); - if (!tt_work) - return -ENOMEM; - -@@ -330,20 +328,17 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work) - - int tt_zone_add_trip(const char *arg) - { -- struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL; -- struct tt_trip *tt_trip __free(kfree) = NULL; -- struct tt_work *tt_work __free(kfree); - int id; - -- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); -+ struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL); - if (!tt_work) - return -ENOMEM; - -- tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL); -+ struct tt_trip *tt_trip __free(kfree) = kzalloc(sizeof(*tt_trip), GFP_KERNEL); - if (!tt_trip) - return -ENOMEM; - -- tt_zone = tt_get_tt_zone(arg); -+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg); - if (IS_ERR(tt_zone)) - return PTR_ERR(tt_zone); - -@@ -387,7 +382,6 @@ static const struct thermal_zone_device_ops tt_zone_ops = { - - static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) - { -- struct thermal_trip *trips __free(kfree) = NULL; - struct thermal_zone_device *tz; - struct tt_trip *tt_trip; - int i; -@@ -397,7 +391,8 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) - if (tt_zone->tz) - return -EINVAL; - -- trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL); -+ struct thermal_trip *trips __free(kfree) = kcalloc(tt_zone->num_trips, -+ sizeof(*trips), GFP_KERNEL); - if (!trips) - return -ENOMEM; - -@@ -421,9 +416,7 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) - - int tt_zone_reg(const char *arg) - { -- struct tt_thermal_zone *tt_zone __free(put_tt_zone); -- -- tt_zone = tt_get_tt_zone(arg); -+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg); - if (IS_ERR(tt_zone)) - return PTR_ERR(tt_zone); - -@@ -432,9 +425,7 @@ int tt_zone_reg(const char *arg) - - int tt_zone_unreg(const char *arg) - { -- struct tt_thermal_zone *tt_zone __free(put_tt_zone); -- -- tt_zone = tt_get_tt_zone(arg); -+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg); - if (IS_ERR(tt_zone)) - return PTR_ERR(tt_zone); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet b/SPECS/kernel-rt/0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet deleted file mode 100644 index 44974924d..000000000 --- a/SPECS/kernel-rt/0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet +++ /dev/null @@ -1,491 +0,0 @@ -From 4138882641dc56cd30b13d410a01f9463a7bb2ff Mon Sep 17 00:00:00 2001 -From: Saeed Mahameed -Date: Tue, 9 Apr 2019 22:01:47 -0700 -Subject: [PATCH 08/19] tools/bpf: Add xdp set command for md btf - -Add new bpftool net subcommand and use it to report and set XDP attrs: - -$ /usr/local/sbin/bpftool net xdp help -Usage: /usr/local/sbin/bpftool net xdp - { show | list | set | md_btf} [dev ] - /usr/local/sbin/bpftool xdp help - -$ /usr/local/sbin/bpftool net xdp set dev mlx0 md_btf on - -$ /usr/local/sbin/bpftool net xdp show -xdp: -mlx0(3) md_btf_id(1) md_btf_enabled(1) - -Signed-off-by: Saeed Mahameed -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - tools/bpf/bpftool/main.h | 2 + - tools/bpf/bpftool/net.c | 7 +- - tools/bpf/bpftool/xdp.c | 308 +++++++++++++++++++++++++++++++++++++++ - tools/lib/bpf/libbpf.h | 1 + - tools/lib/bpf/libbpf.map | 1 + - tools/lib/bpf/netlink.c | 49 +++++++ - 6 files changed, 365 insertions(+), 3 deletions(-) - create mode 100644 tools/bpf/bpftool/xdp.c - -diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h -index 6db704fda5c0..56afb1e81b42 100644 ---- a/tools/bpf/bpftool/main.h -+++ b/tools/bpf/bpftool/main.h -@@ -156,6 +156,7 @@ int do_btf(int argc, char **argv); - - /* non-bootstrap only commands */ - int do_prog(int argc, char **arg) __weak; -+int do_xdp(int argc, char **argv); - int do_map(int argc, char **arg) __weak; - int do_link(int argc, char **arg) __weak; - int do_event_pipe(int argc, char **argv) __weak; -@@ -242,6 +243,7 @@ struct tcmsg; - int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb); - int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind, - const char *devname, int ifindex); -+int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb); - - int print_all_levels(__maybe_unused enum libbpf_print_level level, - const char *format, va_list args); -diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c -index cfc6f944f7c3..f313f5be0960 100644 ---- a/tools/bpf/bpftool/net.c -+++ b/tools/bpf/bpftool/net.c -@@ -362,7 +362,7 @@ static int netlink_get_link(int sock, unsigned int nl_pid, - dump_link_nlmsg, cookie); - } - --static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) -+int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) - { - struct bpf_netdev_t *netinfo = cookie; - struct ifinfomsg *ifinfo = msg; -@@ -937,7 +937,7 @@ static int do_show(int argc, char **argv) - jsonw_start_array(json_wtr); - NET_START_OBJECT; - NET_START_ARRAY("xdp", "%s:\n"); -- ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); -+ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); - NET_END_ARRAY("\n"); - - if (!ret) { -@@ -984,7 +984,7 @@ static int do_help(int argc, char **argv) - } - - fprintf(stderr, -- "Usage: %1$s %2$s { show | list } [dev ]\n" -+ "Usage: %1$s %2$s { show | list | xdp } [dev ]\n" - " %1$s %2$s attach ATTACH_TYPE PROG dev [ overwrite ]\n" - " %1$s %2$s detach ATTACH_TYPE dev \n" - " %1$s %2$s help\n" -@@ -1011,6 +1011,7 @@ static const struct cmd cmds[] = { - { "list", do_show }, - { "attach", do_attach }, - { "detach", do_detach }, -+ { "xdp", do_xdp }, - { "help", do_help }, - { 0 } - }; -diff --git a/tools/bpf/bpftool/xdp.c b/tools/bpf/bpftool/xdp.c -new file mode 100644 -index 000000000000..d33671de877b ---- /dev/null -+++ b/tools/bpf/bpftool/xdp.c -@@ -0,0 +1,308 @@ -+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+// Copyright (C) 2019 Mellanox. -+ -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "bpf/nlattr.h" -+#include "main.h" -+#include "netlink_dumper.h" -+ -+/* TODO: reuse form net.c */ -+#ifndef SOL_NETLINK -+#define SOL_NETLINK 270 -+#endif -+ -+static int netlink_open(__u32 *nl_pid) -+{ -+ struct sockaddr_nl sa; -+ socklen_t addrlen; -+ int one = 1, ret; -+ int sock; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.nl_family = AF_NETLINK; -+ -+ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); -+ if (sock < 0) -+ return -errno; -+ -+ if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, -+ &one, sizeof(one)) < 0) { -+ p_err("Netlink error reporting not supported"); -+ } -+ -+ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { -+ ret = -errno; -+ goto cleanup; -+ } -+ -+ addrlen = sizeof(sa); -+ if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { -+ ret = -errno; -+ goto cleanup; -+ } -+ -+ if (addrlen != sizeof(sa)) { -+ ret = -LIBBPF_ERRNO__INTERNAL; -+ goto cleanup; -+ } -+ -+ *nl_pid = sa.nl_pid; -+ return sock; -+ -+cleanup: -+ close(sock); -+ return ret; -+} -+ -+typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); -+ -+typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie); -+ -+static int netlink_recv(int sock, __u32 nl_pid, __u32 seq, -+ __dump_nlmsg_t _fn, dump_nlmsg_t fn, -+ void *cookie) -+{ -+ bool multipart = true; -+ struct nlmsgerr *err; -+ struct nlmsghdr *nh; -+ char buf[4096]; -+ int len, ret; -+ -+ while (multipart) { -+ multipart = false; -+ len = recv(sock, buf, sizeof(buf), 0); -+ if (len < 0) { -+ ret = -errno; -+ goto done; -+ } -+ -+ if (len == 0) -+ break; -+ -+ for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); -+ nh = NLMSG_NEXT(nh, len)) { -+ if (nh->nlmsg_pid != nl_pid) { -+ ret = -LIBBPF_ERRNO__WRNGPID; -+ goto done; -+ } -+ if (nh->nlmsg_seq != seq) { -+ ret = -LIBBPF_ERRNO__INVSEQ; -+ goto done; -+ } -+ if (nh->nlmsg_flags & NLM_F_MULTI) -+ multipart = true; -+ switch (nh->nlmsg_type) { -+ case NLMSG_ERROR: -+ err = (struct nlmsgerr *)NLMSG_DATA(nh); -+ if (!err->error) -+ continue; -+ ret = err->error; -+ libbpf_nla_dump_errormsg(nh); -+ goto done; -+ case NLMSG_DONE: -+ return 0; -+ default: -+ break; -+ } -+ if (_fn) { -+ ret = _fn(nh, fn, cookie); -+ if (ret) -+ return ret; -+ } -+ } -+ } -+ ret = 0; -+done: -+ return ret; -+} -+ -+static int __dump_link_nlmsg(struct nlmsghdr *nlh, -+ dump_nlmsg_t dump_link_nlmsg, void *cookie) -+{ -+ struct nlattr *tb[IFLA_MAX + 1], *attr; -+ struct ifinfomsg *ifi = NLMSG_DATA(nlh); -+ int len; -+ -+ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); -+ attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); -+ if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) -+ return -LIBBPF_ERRNO__NLPARSE; -+ -+ return dump_link_nlmsg(cookie, ifi, tb); -+} -+ -+static int netlink_get_link(int sock, unsigned int nl_pid, -+ dump_nlmsg_t dump_link_nlmsg, void *cookie) -+{ -+ struct { -+ struct nlmsghdr nlh; -+ struct ifinfomsg ifm; -+ } req = { -+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), -+ .nlh.nlmsg_type = RTM_GETLINK, -+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, -+ .ifm.ifi_family = AF_PACKET, -+ }; -+ int seq = time(NULL); -+ -+ req.nlh.nlmsg_seq = seq; -+ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) -+ return -errno; -+ -+ return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, -+ dump_link_nlmsg, cookie); -+} -+ -+struct ip_devname_ifindex { -+ char devname[64]; -+ int ifindex; -+}; -+ -+struct bpf_netdev_t { -+ struct ip_devname_ifindex *devices; -+ int used_len; -+ int array_len; -+ int filter_idx; -+}; -+ -+static int do_show(int argc, char **argv) -+{ -+ int sock, ret, filter_idx = -1; -+ struct bpf_netdev_t dev_array; -+ unsigned int nl_pid = 0; -+ char err_buf[256]; -+ -+ if (argc == 2) { -+ if (strcmp(argv[0], "dev") != 0) -+ usage(); -+ filter_idx = if_nametoindex(argv[1]); -+ if (filter_idx == 0) { -+ fprintf(stderr, "invalid dev name %s\n", argv[1]); -+ return -1; -+ } -+ } else if (argc != 0) { -+ usage(); -+ } -+ -+ sock = netlink_open(&nl_pid); -+ if (sock < 0) { -+ fprintf(stderr, "failed to open netlink sock\n"); -+ return -1; -+ } -+ -+ dev_array.devices = NULL; -+ dev_array.used_len = 0; -+ dev_array.array_len = 0; -+ dev_array.filter_idx = filter_idx; -+ -+ if (json_output) -+ jsonw_start_array(json_wtr); -+ NET_START_OBJECT; -+ NET_START_ARRAY("xdp", "%s:\n"); -+ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); -+ NET_END_ARRAY("\n"); -+ -+ NET_END_OBJECT; -+ if (json_output) -+ jsonw_end_array(json_wtr); -+ -+ if (ret) { -+ if (json_output) -+ jsonw_null(json_wtr); -+ libbpf_strerror(ret, err_buf, sizeof(err_buf)); -+ fprintf(stderr, "Error: %s\n", err_buf); -+ } -+ free(dev_array.devices); -+ close(sock); -+ return ret; -+} -+ -+static int set_usage(void) -+{ -+ fprintf(stderr, -+ "Usage: %s net xdp set dev {md_btf {on|off}}\n" -+ " %s net xdp set help\n" -+ " md_btf {on|off}: enable/disable meta data btf\n", -+ bin_name, bin_name); -+ -+ return -1; -+} -+ -+static int xdp_set_md_btf(int ifindex, char *arg) -+{ -+ __u8 enable = (strcmp(arg, "on") == 0) ? 1 : 0; -+ int ret; -+ -+ ret = bpf_set_link_xdp_md_btf(ifindex, enable); -+ if (ret) -+ fprintf(stderr, "Failed to setup xdp md, err=%d\n", ret); -+ -+ return -ret; -+} -+ -+static int do_set(int argc, char **argv) -+{ -+ char *set_cmd, *set_arg; -+ int dev_idx = -1; -+ -+ if (argc < 4) -+ return set_usage(); -+ -+ if (strcmp(argv[0], "dev") != 0) -+ return set_usage(); -+ -+ dev_idx = if_nametoindex(argv[1]); -+ if (dev_idx == 0) { -+ fprintf(stderr, "invalid dev name %s\n", argv[1]); -+ return -1; -+ } -+ -+ set_cmd = argv[2]; -+ set_arg = argv[3]; -+ -+ if (strcmp(set_cmd, "md_btf") != 0) -+ return set_usage(); -+ -+ if (strcmp(set_arg, "on") != 0 && strcmp(set_arg, "off") != 0) -+ return set_usage(); -+ -+ return xdp_set_md_btf(dev_idx, set_arg); -+} -+ -+static int do_help(int argc, char **argv) -+{ -+ if (json_output) { -+ jsonw_null(json_wtr); -+ return 0; -+ } -+ -+ fprintf(stderr, -+ "Usage: %s %s xdp { show | list | set } [dev ]\n" -+ " %s %s help\n", -+ bin_name, argv[-2], bin_name, argv[-2]); -+ -+ return 0; -+} -+ -+static const struct cmd cmds[] = { -+ { "show", do_show }, -+ { "list", do_show }, -+ { "set", do_set }, -+ { "help", do_help }, -+ { 0 } -+}; -+ -+int do_xdp(int argc, char **argv) -+{ -+ return cmd_select(cmds, argc, argv, do_help); -+} -diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h -index 455a957cb702..987e3e0ab681 100644 ---- a/tools/lib/bpf/libbpf.h -+++ b/tools/lib/bpf/libbpf.h -@@ -1597,6 +1597,7 @@ LIBBPF_API struct perf_buffer * - perf_buffer__new(int map_fd, size_t page_cnt, - perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx, - const struct perf_buffer_opts *opts); -+LIBBPF_API int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable); - - enum bpf_perf_event_ret { - LIBBPF_PERF_EVENT_DONE = 0, -diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map -index d7bd463e7017..ee2851c7fb3d 100644 ---- a/tools/lib/bpf/libbpf.map -+++ b/tools/lib/bpf/libbpf.map -@@ -73,6 +73,7 @@ LIBBPF_0.0.2 { - bpf_map_lookup_elem_flags; - bpf_object__btf; - bpf_object__find_map_fd_by_name; -+ bpf_set_link_xdp_md_btf; - btf__get_raw_data; - btf_ext__free; - btf_ext__get_raw_data; -diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c -index c997e69d507f..204827a4380b 100644 ---- a/tools/lib/bpf/netlink.c -+++ b/tools/lib/bpf/netlink.c -@@ -341,6 +341,55 @@ int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *o - return bpf_xdp_attach(ifindex, -1, flags, opts); - } - -+int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable) -+{ -+ struct nlattr *nla, *nla_xdp; -+ int sock, seq = 0, ret; -+ __u32 nl_pid; -+ struct { -+ struct nlmsghdr nh; -+ struct ifinfomsg ifinfo; -+ char attrbuf[64]; -+ } req; -+ -+ sock = libbpf_netlink_open(&nl_pid, NETLINK_ROUTE); -+ if (sock < 0) -+ return sock; -+ -+ memset(&req, 0, sizeof(req)); -+ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); -+ req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; -+ req.nh.nlmsg_type = RTM_SETLINK; -+ req.nh.nlmsg_pid = 0; -+ req.nh.nlmsg_seq = ++seq; -+ req.ifinfo.ifi_family = AF_UNSPEC; -+ req.ifinfo.ifi_index = ifindex; -+ -+ /* started nested attribute for XDP */ -+ nla = (struct nlattr *)(((char *)&req) -+ + NLMSG_ALIGN(req.nh.nlmsg_len)); -+ nla->nla_type = NLA_F_NESTED | IFLA_XDP; -+ nla->nla_len = NLA_HDRLEN; -+ /* add XDP MD setup */ -+ nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); -+ nla_xdp->nla_type = IFLA_XDP_MD_BTF_STATE; -+ nla_xdp->nla_len = NLA_HDRLEN + sizeof(__u8); -+ memcpy((char *)nla_xdp + NLA_HDRLEN, &enable, sizeof(__u8)); -+ nla->nla_len += nla_xdp->nla_len; -+ -+ req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); -+ -+ if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { -+ ret = -errno; -+ goto cleanup; -+ } -+ ret = libbpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); -+ -+cleanup: -+ close(sock); -+ return ret; -+} -+ - static int __dump_link_nlmsg(struct nlmsghdr *nlh, - libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) - { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0008-tools-power-turbostat-Add-LLC-stats.turbo b/SPECS/kernel-rt/0008-tools-power-turbostat-Add-LLC-stats.turbo new file mode 100644 index 000000000..b7159f16a --- /dev/null +++ b/SPECS/kernel-rt/0008-tools-power-turbostat-Add-LLC-stats.turbo @@ -0,0 +1,551 @@ +From 190569df9b9508257304a7ce06fce2238b88c82a Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 21 Oct 2025 20:23:49 -0300 +Subject: [PATCH 08/21] tools/power turbostat: Add LLC stats + +LLCkRPS = Last Level Cache Thousands of References Per Second +LLC%hit = Last Level Cache Hit % + +These columns are enabled by-default. +They can be controlled with the --show/--hide options +by individual column names above, +or together using the "llc" or "cache" groups. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.8 | 6 +- + tools/power/x86/turbostat/turbostat.c | 186 ++++++++++++++++++++++---- + 2 files changed, 164 insertions(+), 28 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 +index ad3fc201552f7..1551fcdbfd8a0 100644 +--- a/tools/power/x86/turbostat/turbostat.8 ++++ b/tools/power/x86/turbostat/turbostat.8 +@@ -101,7 +101,7 @@ The column name "all" can be used to enable all disabled-by-default built-in cou + .PP + \fB--show column\fP show only the specified built-in columns. May be invoked multiple times, or with a comma-separated list of column names. + .PP +-\fB--show CATEGORY --hide CATEGORY\fP Show and hide also accept a single CATEGORY of columns: "all", "topology", "idle", "frequency", "power", "cpuidle", "hwidle", "swidle", "other". "idle" (enabled by default), includes "hwidle" and "pct_idle". "cpuidle" (default disabled) includes cpuidle software invocation counters. "swidle" includes "cpuidle" plus "pct_idle". "hwidle" includes only hardware based idle residency counters. Older versions of turbostat used the term "sysfs" for what is now "swidle". ++\fB--show CATEGORY --hide CATEGORY\fP Show and hide also accept a comma-separated-list of CATEGORIES of columns: "all", "topology", "idle", "frequency", "power", "cpuidle", "hwidle", "swidle", "cache", "llc", "other". "idle" (enabled by default), includes "hwidle" and "pct_idle". "cpuidle" (default disabled) includes cpuidle software invocation counters. "swidle" includes "cpuidle" plus "pct_idle". "hwidle" includes only hardware based idle residency counters. Older versions of turbostat used the term "sysfs" for what is now "swidle". + .PP + \fB--Dump\fP displays the raw counter values. + .PP +@@ -159,6 +159,10 @@ The system configuration dump (if --quiet is not used) is followed by statistics + .PP + \fBSMI\fP The number of System Management Interrupts serviced CPU during the measurement interval. While this counter is actually per-CPU, SMI are triggered on all processors, so the number should be the same for all CPUs. + .PP ++\fBLLCkRPS\fP Last Level Cache Thousands of References Per Second. For CPUs with an L3 LLC, this is the number of references that CPU made to the L3 (and the number of misses that CPU made to it's L2). For CPUs with an L2 LLC, this is the number of references to the L2 (and the number of misses to the CPU's L1). The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1000 in the interest of usually fitting into 8 columns. ++.PP ++\fBLLC%hit\fP Last Level Cache Hit Rate %. Hit Rate Percent = 100.0 * (References - Misses)/References. The system summary row shows the weighted average for all CPUs (100.0 * (Sum_References - Sum_Misses)/Sum_References). ++.PP + \fBC1, C2, C3...\fP The number times Linux requested the C1, C2, C3 idle state during the measurement interval. The system summary line shows the sum for all CPUs. These are C-state names as exported in /sys/devices/system/cpu/cpu*/cpuidle/state*/name. While their names are generic, their attributes are processor specific. They the system description section of output shows what MWAIT sub-states they are mapped to on each system. These counters are in the "cpuidle" group, which is disabled, by default. + .PP + \fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Inidcates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default. +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index f63525a1877c7..2854b66eb7480 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -209,6 +209,8 @@ struct msr_counter bic[] = { + { 0x0, "NMI", NULL, 0, 0, 0, NULL, 0 }, + { 0x0, "CPU%c1e", NULL, 0, 0, 0, NULL, 0 }, + { 0x0, "pct_idle", NULL, 0, 0, 0, NULL, 0 }, ++ { 0x0, "LLCkRPS", NULL, 0, 0, 0, NULL, 0 }, ++ { 0x0, "LLC%hit", NULL, 0, 0, 0, NULL, 0 }, + }; + + /* n.b. bic_names must match the order in bic[], above */ +@@ -278,6 +280,8 @@ enum bic_names { + BIC_NMI, + BIC_CPU_c1e, + BIC_pct_idle, ++ BIC_LLC_RPS, ++ BIC_LLC_HIT, + MAX_BIC + }; + +@@ -305,6 +309,7 @@ static cpu_set_t bic_group_frequency; + static cpu_set_t bic_group_hw_idle; + static cpu_set_t bic_group_sw_idle; + static cpu_set_t bic_group_idle; ++static cpu_set_t bic_group_cache; + static cpu_set_t bic_group_other; + static cpu_set_t bic_group_disabled_by_default; + static cpu_set_t bic_enabled; +@@ -413,9 +418,14 @@ static void bic_groups_init(void) + SET_BIC(BIC_pct_idle, &bic_group_sw_idle); + + BIC_INIT(&bic_group_idle); ++ + CPU_OR(&bic_group_idle, &bic_group_idle, &bic_group_hw_idle); + SET_BIC(BIC_pct_idle, &bic_group_idle); + ++ BIC_INIT(&bic_group_cache); ++ SET_BIC(BIC_LLC_RPS, &bic_group_cache); ++ SET_BIC(BIC_LLC_HIT, &bic_group_cache); ++ + BIC_INIT(&bic_group_other); + SET_BIC(BIC_IRQ, &bic_group_other); + SET_BIC(BIC_NMI, &bic_group_other); +@@ -470,6 +480,7 @@ char *proc_stat = "/proc/stat"; + FILE *outf; + int *fd_percpu; + int *fd_instr_count_percpu; ++int *fd_llc_percpu; + struct timeval interval_tv = { 5, 0 }; + struct timespec interval_ts = { 5, 0 }; + +@@ -1992,6 +2003,10 @@ void pmt_counter_resize(struct pmt_counter *pcounter, unsigned int new_size) + pmt_counter_resize_(pcounter, new_size); + } + ++struct llc_stats { ++ unsigned long long references; ++ unsigned long long misses; ++}; + struct thread_data { + struct timeval tv_begin; + struct timeval tv_end; +@@ -2004,6 +2019,7 @@ struct thread_data { + unsigned long long irq_count; + unsigned long long nmi_count; + unsigned int smi_count; ++ struct llc_stats llc; + unsigned int cpu_id; + unsigned int apic_id; + unsigned int x2apic_id; +@@ -2363,23 +2379,19 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk + return retval; + } + +-int is_cpu_first_thread_in_core(PER_THREAD_PARAMS) ++int is_cpu_first_thread_in_core(struct thread_data *t, struct core_data *c) + { +- UNUSED(p); +- + return ((int)t->cpu_id == c->base_cpu || c->base_cpu < 0); + } + +-int is_cpu_first_core_in_package(PER_THREAD_PARAMS) ++int is_cpu_first_core_in_package(struct thread_data *t, struct pkg_data *p) + { +- UNUSED(c); +- + return ((int)t->cpu_id == p->base_cpu || p->base_cpu < 0); + } + +-int is_cpu_first_thread_in_package(PER_THREAD_PARAMS) ++int is_cpu_first_thread_in_package(struct thread_data *t, struct core_data *c, struct pkg_data *p) + { +- return is_cpu_first_thread_in_core(t, c, p) && is_cpu_first_core_in_package(t, c, p); ++ return is_cpu_first_thread_in_core(t, c) && is_cpu_first_core_in_package(t, p); + } + + int cpu_migrate(int cpu) +@@ -2657,6 +2669,12 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + } else if (!strcmp(name_list, "idle")) { + CPU_OR(ret_set, ret_set, &bic_group_idle); + break; ++ } else if (!strcmp(name_list, "cache")) { ++ CPU_OR(ret_set, ret_set, &bic_group_cache); ++ break; ++ } else if (!strcmp(name_list, "llc")) { ++ CPU_OR(ret_set, ret_set, &bic_group_cache); ++ break; + } else if (!strcmp(name_list, "swidle")) { + CPU_OR(ret_set, ret_set, &bic_group_sw_idle); + break; +@@ -2794,6 +2812,12 @@ void print_header(char *delim) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%sSMI", (printed++ ? delim : "")); + ++ if (DO_BIC(BIC_LLC_RPS)) ++ outp += sprintf(outp, "%sLLCkRPS", (printed++ ? delim : "")); ++ ++ if (DO_BIC(BIC_LLC_HIT)) ++ outp += sprintf(outp, "%sLLC%%hit", (printed++ ? delim : "")); ++ + for (mp = sys.tp; mp; mp = mp->next) + outp += print_name(mp->width, &printed, delim, mp->name); + +@@ -2864,7 +2888,6 @@ void print_header(char *delim) + + ppmt = ppmt->next; + } +- + if (DO_BIC(BIC_PkgTmp)) + outp += sprintf(outp, "%sPkgTmp", (printed++ ? delim : "")); + +@@ -3001,6 +3024,10 @@ int dump_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "SMI: %d\n", t->smi_count); + ++ outp += sprintf(outp, "LLC refs: %lld", t->llc.references); ++ outp += sprintf(outp, "LLC miss: %lld", t->llc.misses); ++ outp += sprintf(outp, "LLC Hit%%: %.2f", 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + outp += + sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +@@ -3008,7 +3035,7 @@ int dump_counters(PER_THREAD_PARAMS) + } + } + +- if (c && is_cpu_first_thread_in_core(t, c, p)) { ++ if (c && is_cpu_first_thread_in_core(t, c)) { + outp += sprintf(outp, "core: %d\n", c->core_id); + outp += sprintf(outp, "c3: %016llX\n", c->c3); + outp += sprintf(outp, "c6: %016llX\n", c->c6); +@@ -3030,7 +3057,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "mc6_us: %016llX\n", c->mc6_us); + } + +- if (p && is_cpu_first_core_in_package(t, c, p)) { ++ if (p && is_cpu_first_core_in_package(t, p)) { + outp += sprintf(outp, "package: %d\n", p->package_id); + + outp += sprintf(outp, "Weighted cores: %016llX\n", p->pkg_wtd_core_c0); +@@ -3088,6 +3115,26 @@ double rapl_counter_get_value(const struct rapl_counter *c, enum rapl_unit desir + return scaled; + } + ++void get_perf_llc_stats(int cpu, struct llc_stats *llc) ++{ ++ struct read_format { ++ unsigned long long num_read; ++ struct llc_stats llc; ++ } r; ++ const ssize_t expected_read_size = sizeof(r); ++ ssize_t actual_read_size; ++ ++ actual_read_size = read(fd_llc_percpu[cpu], &r, expected_read_size); ++ ++ if (actual_read_size == -1) ++ err(-1, "%s(cpu%d,) %d,,%ld\n", __func__, cpu, fd_llc_percpu[cpu], expected_read_size); ++ ++ llc->references = r.llc.references; ++ llc->misses = r.llc.misses; ++ if (actual_read_size != expected_read_size) ++ warn("%s: failed to read perf_data (req %zu act %zu)", __func__, expected_read_size, actual_read_size); ++} ++ + /* + * column formatting convention & formats + */ +@@ -3097,7 +3144,8 @@ int format_counters(PER_THREAD_PARAMS) + + struct platform_counters *pplat_cnt = NULL; + double interval_float, tsc; +- char *fmt8; ++ char *fmt8 = "%s%.2f"; ++ + int i; + struct msr_counter *mp; + struct perf_counter_info *pp; +@@ -3111,11 +3159,11 @@ int format_counters(PER_THREAD_PARAMS) + } + + /* if showing only 1st thread in core and this isn't one, bail out */ +- if (show_core_only && !is_cpu_first_thread_in_core(t, c, p)) ++ if (show_core_only && !is_cpu_first_thread_in_core(t, c)) + return 0; + + /* if showing only 1st thread in pkg and this isn't one, bail out */ +- if (show_pkg_only && !is_cpu_first_core_in_package(t, c, p)) ++ if (show_pkg_only && !is_cpu_first_core_in_package(t, p)) + return 0; + + /*if not summary line and --cpu is used */ +@@ -3237,6 +3285,16 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->smi_count); + ++ /* LLC Stats */ ++ if (DO_BIC(BIC_LLC_RPS) || DO_BIC(BIC_LLC_HIT)) { ++ if (DO_BIC(BIC_LLC_RPS)) ++ outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), t->llc.references / interval_float / 1000); ++ ++ if (DO_BIC(BIC_LLC_HIT)) ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ } ++ ++ + /* Added Thread Counters */ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) +@@ -3290,7 +3348,7 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->c1 / tsc); + + /* print per-core data only for 1st thread in core */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + goto done; + + if (DO_BIC(BIC_CPU_c3)) +@@ -3351,8 +3409,6 @@ int format_counters(PER_THREAD_PARAMS) + } + } + +- fmt8 = "%s%.2f"; +- + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) + outp += + sprintf(outp, fmt8, (printed++ ? delim : ""), +@@ -3362,7 +3418,7 @@ int format_counters(PER_THREAD_PARAMS) + rapl_counter_get_value(&c->core_energy, RAPL_UNIT_JOULES, interval_float)); + + /* print per-package data only for 1st core in package */ +- if (!is_cpu_first_core_in_package(t, c, p)) ++ if (!is_cpu_first_core_in_package(t, p)) + goto done; + + /* PkgTmp */ +@@ -3808,6 +3864,12 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + if (DO_BIC(BIC_SMI)) + old->smi_count = new->smi_count - old->smi_count; + ++ if (DO_BIC(BIC_LLC_RPS)) ++ old->llc.references = new->llc.references - old->llc.references; ++ ++ if (DO_BIC(BIC_LLC_HIT)) ++ old->llc.misses = new->llc.misses - old->llc.misses; ++ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) + old->counter[i] = new->counter[i]; +@@ -3838,14 +3900,14 @@ int delta_cpu(struct thread_data *t, struct core_data *c, + int retval = 0; + + /* calculate core delta only for 1st thread in core */ +- if (is_cpu_first_thread_in_core(t, c, p)) ++ if (is_cpu_first_thread_in_core(t, c)) + delta_core(c, c2); + + /* always calculate thread delta */ + retval = delta_thread(t, t2, c2); /* c2 is core delta */ + + /* calculate package delta only for 1st core in package */ +- if (is_cpu_first_core_in_package(t, c, p)) ++ if (is_cpu_first_core_in_package(t, p)) + retval |= delta_package(p, p2); + + return retval; +@@ -3886,6 +3948,9 @@ void clear_counters(PER_THREAD_PARAMS) + t->nmi_count = 0; + t->smi_count = 0; + ++ t->llc.references = 0; ++ t->llc.misses = 0; ++ + c->c3 = 0; + c->c6 = 0; + c->c7 = 0; +@@ -3894,6 +3959,9 @@ void clear_counters(PER_THREAD_PARAMS) + rapl_counter_clear(&c->core_energy); + c->core_throt_cnt = 0; + ++ t->llc.references = 0; ++ t->llc.misses = 0; ++ + p->pkg_wtd_core_c0 = 0; + p->pkg_any_core_c0 = 0; + p->pkg_any_gfxe_c0 = 0; +@@ -3991,6 +4059,9 @@ int sum_counters(PER_THREAD_PARAMS) + average.threads.nmi_count += t->nmi_count; + average.threads.smi_count += t->smi_count; + ++ average.threads.llc.references += t->llc.references; ++ average.threads.llc.misses += t->llc.misses; ++ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) + continue; +@@ -4008,7 +4079,7 @@ int sum_counters(PER_THREAD_PARAMS) + } + + /* sum per-core values only for 1st thread in core */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + return 0; + + average.cores.c3 += c->c3; +@@ -4038,7 +4109,7 @@ int sum_counters(PER_THREAD_PARAMS) + } + + /* sum per-pkg values only for 1st core in pkg */ +- if (!is_cpu_first_core_in_package(t, c, p)) ++ if (!is_cpu_first_core_in_package(t, p)) + return 0; + + if (DO_BIC(BIC_Totl_c0)) +@@ -5009,6 +5080,9 @@ int get_counters(PER_THREAD_PARAMS) + + get_smi_aperf_mperf(cpu, t); + ++ if (DO_BIC(BIC_LLC_RPS) || DO_BIC(BIC_LLC_HIT)) ++ get_perf_llc_stats(cpu, &t->llc); ++ + if (DO_BIC(BIC_IPC)) + if (read(get_instr_count_fd(cpu), &t->instr_count, sizeof(long long)) != sizeof(long long)) + return -4; +@@ -5032,7 +5106,7 @@ int get_counters(PER_THREAD_PARAMS) + t->pmt_counter[i] = pmt_read_counter(pp, t->cpu_id); + + /* collect core counters only for 1st thread in core */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + goto done; + + if (platform->has_per_core_rapl) { +@@ -5076,7 +5150,7 @@ int get_counters(PER_THREAD_PARAMS) + c->pmt_counter[i] = pmt_read_counter(pp, c->core_id); + + /* collect package counters only for 1st core in package */ +- if (!is_cpu_first_core_in_package(t, c, p)) ++ if (!is_cpu_first_core_in_package(t, p)) + goto done; + + if (DO_BIC(BIC_Totl_c0)) { +@@ -5631,6 +5705,20 @@ void free_fd_instr_count_percpu(void) + fd_instr_count_percpu = NULL; + } + ++void free_fd_llc_percpu(void) ++{ ++ if (!fd_llc_percpu) ++ return; ++ ++ for (int i = 0; i < topo.max_cpu_num + 1; ++i) { ++ if (fd_llc_percpu[i] != 0) ++ close(fd_llc_percpu[i]); ++ } ++ ++ free(fd_llc_percpu); ++ fd_llc_percpu = NULL; ++} ++ + void free_fd_cstate(void) + { + if (!ccstate_counter_info) +@@ -5755,6 +5843,7 @@ void free_all_buffers(void) + + free_fd_percpu(); + free_fd_instr_count_percpu(); ++ free_fd_llc_percpu(); + free_fd_msr(); + free_fd_rapl_percpu(); + free_fd_cstate(); +@@ -6101,6 +6190,7 @@ void linux_perf_init(void); + void msr_perf_init(void); + void rapl_perf_init(void); + void cstate_perf_init(void); ++void perf_llc_init(void); + void added_perf_counters_init(void); + void pmt_init(void); + +@@ -6112,6 +6202,7 @@ void re_initialize(void) + msr_perf_init(); + rapl_perf_init(); + cstate_perf_init(); ++ perf_llc_init(); + added_perf_counters_init(); + pmt_init(); + fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, +@@ -7976,7 +8067,7 @@ int print_thermal(PER_THREAD_PARAMS) + cpu = t->cpu_id; + + /* DTS is per-core, no need to print for each thread */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + return 0; + + if (cpu_migrate(cpu)) { +@@ -7984,7 +8075,7 @@ int print_thermal(PER_THREAD_PARAMS) + return -1; + } + +- if (do_ptm && is_cpu_first_core_in_package(t, c, p)) { ++ if (do_ptm && is_cpu_first_core_in_package(t, p)) { + if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_STATUS, &msr)) + return 0; + +@@ -8263,6 +8354,11 @@ void linux_perf_init(void) + if (fd_instr_count_percpu == NULL) + err(-1, "calloc fd_instr_count_percpu"); + } ++ if (BIC_IS_ENABLED(BIC_LLC_RPS)) { ++ fd_llc_percpu = calloc(topo.max_cpu_num + 1, sizeof(int)); ++ if (fd_llc_percpu == NULL) ++ err(-1, "calloc fd_llc_percpu"); ++ } + } + + void rapl_perf_init(void) +@@ -8933,6 +9029,40 @@ void probe_pm_features(void) + decode_misc_feature_control(); + } + ++void perf_llc_init(void) ++{ ++ int cpu; ++ int retval; ++ ++ if (no_perf) ++ return; ++ if (!(BIC_IS_ENABLED(BIC_LLC_RPS) && BIC_IS_ENABLED(BIC_LLC_HIT))) ++ return; ++ ++ for (cpu = 0; cpu <= topo.max_cpu_num; ++cpu) { ++ ++ if (cpu_is_not_allowed(cpu)) ++ continue; ++ ++ assert(fd_llc_percpu != 0); ++ fd_llc_percpu[cpu] = open_perf_counter(cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, -1, PERF_FORMAT_GROUP); ++ if (fd_llc_percpu[cpu] == -1) { ++ warnx("%s: perf REFS: failed to open counter on cpu%d", __func__, cpu); ++ free_fd_llc_percpu(); ++ return; ++ } ++ assert(fd_llc_percpu != 0); ++ retval = open_perf_counter(cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, fd_llc_percpu[cpu], PERF_FORMAT_GROUP); ++ if (retval == -1) { ++ warnx("%s: perf MISS: failed to open counter on cpu%d", __func__, cpu); ++ free_fd_llc_percpu(); ++ return; ++ } ++ } ++ BIC_PRESENT(BIC_LLC_RPS); ++ BIC_PRESENT(BIC_LLC_HIT); ++} ++ + /* + * in /dev/cpu/ return success for names that are numbers + * ie. filter out ".", "..", "microcode". +@@ -9239,6 +9369,7 @@ void init_counter(struct thread_data *thread_base, struct core_data *core_base, + + t->cpu_id = cpu_id; + if (!cpu_is_not_allowed(cpu_id)) { ++ + if (c->base_cpu < 0) + c->base_cpu = t->cpu_id; + if (pkg_base[pkg_id].base_cpu < 0) +@@ -9909,6 +10040,7 @@ void turbostat_init() + linux_perf_init(); + rapl_perf_init(); + cstate_perf_init(); ++ perf_llc_init(); + added_perf_counters_init(); + pmt_init(); + +@@ -10014,7 +10146,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.10.18 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.10.24 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi b/SPECS/kernel-rt/0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi deleted file mode 100644 index 148023976..000000000 --- a/SPECS/kernel-rt/0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi +++ /dev/null @@ -1,242 +0,0 @@ -From 42342460efa55247a870c163fbcea60501dfb4a4 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 13:22:02 -0700 -Subject: [PATCH 09/44] KVM: VMX: Add support for FRED context save/restore - -Handle FRED MSR access requests, allowing FRED context to be set/get -from both host and guest. - -During VM save/restore and live migration, FRED context needs to be -saved/restored, which requires FRED MSRs to be accessed from userspace, -e.g., Qemu. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v6: -* Return KVM_MSR_RET_UNSUPPORTED instead of 1 when FRED is not available - (Chao Gao) -* Handle MSR_IA32_PL0_SSP when FRED is enumerated but CET not. - -Change in v5: -* Use the newly added guest MSR read/write helpers (Sean). -* Check the size of fred_msr_vmcs_fields[] using static_assert() (Sean). -* Rewrite setting FRED MSRs to make it much easier to read (Sean). -* Add TB from Xuelian Guo. - -Changes since v2: -* Add a helper to convert FRED MSR index to VMCS field encoding to - make the code more compact (Chao Gao). -* Get rid of the "host_initiated" check because userspace has to set - CPUID before MSRs (Chao Gao & Sean Christopherson). -* Address a few cleanup comments (Sean Christopherson). - -Changes since v1: -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). -* Fail host requested FRED MSRs access if KVM cannot virtualize FRED - (Chao Gao). -* Handle the case FRED MSRs are valid but KVM cannot virtualize FRED - (Chao Gao). -* Add sanity checks when writing to FRED MSRs. ---- - arch/x86/kvm/vmx/vmx.c | 45 +++++++++++++++++++++++++++ - arch/x86/kvm/x86.c | 69 ++++++++++++++++++++++++++++++++++++++++-- - 2 files changed, 111 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 30322c4897cb..ba0177a97417 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1390,6 +1390,18 @@ static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) - vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data, - &vmx->msr_guest_kernel_gs_base); - } -+ -+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) -+{ -+ return vmx_read_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, -+ &vmx->msr_guest_fred_rsp0); -+} -+ -+static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) -+{ -+ vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, -+ &vmx->msr_guest_fred_rsp0); -+} - #endif - - static void grow_ple_window(struct kvm_vcpu *vcpu) -@@ -1991,6 +2003,27 @@ int vmx_get_feature_msr(u32 msr, u64 *data) - } - } - -+#ifdef CONFIG_X86_64 -+static const u32 fred_msr_vmcs_fields[] = { -+ GUEST_IA32_FRED_RSP1, -+ GUEST_IA32_FRED_RSP2, -+ GUEST_IA32_FRED_RSP3, -+ GUEST_IA32_FRED_STKLVLS, -+ GUEST_IA32_FRED_SSP1, -+ GUEST_IA32_FRED_SSP2, -+ GUEST_IA32_FRED_SSP3, -+ GUEST_IA32_FRED_CONFIG, -+}; -+ -+static_assert(MSR_IA32_FRED_CONFIG - MSR_IA32_FRED_RSP1 == -+ ARRAY_SIZE(fred_msr_vmcs_fields) - 1); -+ -+static u32 fred_msr_to_vmcs(u32 msr) -+{ -+ return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1]; -+} -+#endif -+ - /* - * Reads an msr value (of 'msr_info->index') into 'msr_info->data'. - * Returns 0 on success, non-0 otherwise. -@@ -2013,6 +2046,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - case MSR_KERNEL_GS_BASE: - msr_info->data = vmx_read_guest_kernel_gs_base(vmx); - break; -+ case MSR_IA32_FRED_RSP0: -+ msr_info->data = vmx_read_guest_fred_rsp0(vmx); -+ break; -+ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: -+ msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index)); -+ break; - #endif - case MSR_EFER: - return kvm_get_msr_common(vcpu, msr_info); -@@ -2245,6 +2284,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - vmx_update_exception_bitmap(vcpu); - } - break; -+ case MSR_IA32_FRED_RSP0: -+ vmx_write_guest_fred_rsp0(vmx, data); -+ break; -+ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: -+ vmcs_write64(fred_msr_to_vmcs(msr_index), data); -+ break; - #endif - case MSR_IA32_SYSENTER_CS: - if (is_guest_mode(vcpu)) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index da04331a4200..bd1c2eb7c538 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -333,6 +333,9 @@ static const u32 msrs_to_save_base[] = { - MSR_STAR, - #ifdef CONFIG_X86_64 - MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR, -+ MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2, -+ MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1, -+ MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG, - #endif - MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA, - MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX, -@@ -1902,7 +1905,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, - if (!host_initiated) - return 1; - fallthrough; -- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: - if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) - return KVM_MSR_RET_UNSUPPORTED; - if (is_noncanonical_msr_address(data, vcpu)) -@@ -1911,6 +1914,48 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, - if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4)) - return 1; - break; -+ case MSR_IA32_FRED_STKLVLS: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: -+ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_CONFIG: -+ u64 reserved_bits = 0; -+ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ -+ if (is_noncanonical_msr_address(data, vcpu)) -+ return 1; -+ -+ switch (index) { -+ case MSR_IA32_FRED_CONFIG: -+ reserved_bits = BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2); -+ break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: -+ reserved_bits = GENMASK_ULL(5, 0); -+ break; -+ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_SSP3: -+ reserved_bits = GENMASK_ULL(2, 0); -+ break; -+ default: -+ WARN_ON_ONCE(1); -+ return 1; -+ } -+ if (data & reserved_bits) -+ return 1; -+ break; -+ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ -+ if (is_noncanonical_msr_address(data, vcpu)) -+ return 1; -+ -+ if (!IS_ALIGNED(data, 4)) -+ return 1; -+ break; - } - - msr.data = data; -@@ -1965,10 +2010,19 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, - if (!host_initiated) - return 1; - fallthrough; -- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: - if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) - return KVM_MSR_RET_UNSUPPORTED; - break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; -+ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; - } - - msr.index = index; -@@ -7538,10 +7592,19 @@ static void kvm_probe_msr_to_save(u32 msr_index) - if (!kvm_cpu_cap_has(X86_FEATURE_LM)) - return; - fallthrough; -- case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ case MSR_IA32_PL1_SSP ... MSR_IA32_PL3_SSP: - if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK)) - return; - break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: -+ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) -+ return; -+ break; -+ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && -+ !kvm_cpu_cap_has(X86_FEATURE_FRED)) -+ return; -+ break; - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi b/SPECS/kernel-rt/0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi new file mode 100644 index 000000000..43a3841e2 --- /dev/null +++ b/SPECS/kernel-rt/0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi @@ -0,0 +1,85 @@ +From adf1a7131298285f5f222362b94a0b9f37a4a1c1 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 31 Aug 2023 01:23:00 -0700 +Subject: [PATCH 09/44] KVM: VMX: Save/restore guest FRED RSP0 + +Save guest FRED RSP0 in vmx_prepare_switch_to_host() and restore it +in vmx_prepare_switch_to_guest() because MSR_IA32_FRED_RSP0 is passed +through to the guest, thus is volatile/unknown. + +Note, host FRED RSP0 is restored in arch_exit_to_user_mode_prepare(), +regardless of whether it is modified in KVM. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v5: +* Remove the cpu_feature_enabled() check when set/get guest + MSR_IA32_FRED_RSP0, as guest_cpu_cap_has() should suffice (Sean). +* Add a comment when synchronizing current MSR_IA32_FRED_RSP0 MSR to + the kernel's local cache, because its handling is different from + the MSR_KERNEL_GS_BASE handling (Sean). +* Add TB from Xuelian Guo. + +Changes in v3: +* KVM only needs to save/restore guest FRED RSP0 now as host FRED RSP0 + is restored in arch_exit_to_user_mode_prepare() (Sean Christopherson). + +Changes in v2: +* Don't use guest_cpuid_has() in vmx_prepare_switch_to_{host,guest}(), + which are called from IRQ-disabled context (Chao Gao). +* Reset msr_guest_fred_rsp0 in __vmx_vcpu_reset() (Chao Gao). +--- + arch/x86/kvm/vmx/vmx.c | 13 +++++++++++++ + arch/x86/kvm/vmx/vmx.h | 1 + + 2 files changed, 14 insertions(+) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 1c639496a7562..3ac89ccf47210 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1292,6 +1292,9 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) + } + + wrmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base); ++ ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ wrmsrns(MSR_IA32_FRED_RSP0, vmx->msr_guest_fred_rsp0); + #else + savesegment(fs, fs_sel); + savesegment(gs, gs_sel); +@@ -1336,6 +1339,16 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx) + invalidate_tss_limit(); + #ifdef CONFIG_X86_64 + wrmsrq(MSR_KERNEL_GS_BASE, vmx->vt.msr_host_kernel_gs_base); ++ ++ if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED)) { ++ vmx->msr_guest_fred_rsp0 = read_msr(MSR_IA32_FRED_RSP0); ++ /* ++ * Synchronize the current value in hardware to the kernel's ++ * local cache. The desired host RSP0 will be set when the ++ * CPU exits to userspace (RSP0 is a per-task value). ++ */ ++ fred_sync_rsp0(vmx->msr_guest_fred_rsp0); ++ } + #endif + load_fixmap_gdt(raw_smp_processor_id()); + vmx->vt.guest_state_loaded = false; +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index 2cf599211ab30..c4a3b28553fbd 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -227,6 +227,7 @@ struct vcpu_vmx { + bool guest_uret_msrs_loaded; + #ifdef CONFIG_X86_64 + u64 msr_guest_kernel_gs_base; ++ u64 msr_guest_fred_rsp0; + #endif + + u64 spec_ctrl; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet b/SPECS/kernel-rt/0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet deleted file mode 100644 index 9c0adb9b4..000000000 --- a/SPECS/kernel-rt/0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet +++ /dev/null @@ -1,59 +0,0 @@ -From 2ee63e38a5bc5418e85af793c33ff0cb36d8b546 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:39 -0700 -Subject: [PATCH 09/24] KVM: x86: Initialize kvm_caps.supported_xss - -Set original kvm_caps.supported_xss to (host_xss & KVM_SUPPORTED_XSS) if -XSAVES is supported. host_xss contains the host supported xstate feature -bits for thread FPU context switch, KVM_SUPPORTED_XSS includes all KVM -enabled XSS feature bits, the resulting value represents the supervisor -xstates that are available to guest and are backed by host FPU framework -for swapping {guest,host} XSAVE-managed registers/MSRs. - -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 11 ++++++++--- - 1 file changed, 8 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 3057ae88b6ba..46b45eeadf47 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -224,6 +224,8 @@ static struct kvm_user_return_msrs __percpu *user_return_msrs; - | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 \ - | XFEATURE_MASK_PKRU | XFEATURE_MASK_XTILE) - -+#define KVM_SUPPORTED_XSS 0 -+ - bool __read_mostly allow_smaller_maxphyaddr = 0; - EXPORT_SYMBOL_GPL(allow_smaller_maxphyaddr); - -@@ -9744,14 +9746,17 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - kvm_host.xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); - kvm_caps.supported_xcr0 = kvm_host.xcr0 & KVM_SUPPORTED_XCR0; - } -+ -+ if (boot_cpu_has(X86_FEATURE_XSAVES)) { -+ rdmsrq(MSR_IA32_XSS, kvm_host.xss); -+ kvm_caps.supported_xss = kvm_host.xss & KVM_SUPPORTED_XSS; -+ } -+ - kvm_caps.supported_quirks = KVM_X86_VALID_QUIRKS; - kvm_caps.inapplicable_quirks = KVM_X86_CONDITIONAL_QUIRKS; - - rdmsrq_safe(MSR_EFER, &kvm_host.efer); - -- if (boot_cpu_has(X86_FEATURE_XSAVES)) -- rdmsrq(MSR_IA32_XSS, kvm_host.xss); -- - kvm_init_pmu_capability(ops->pmu_ops); - - if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt b/SPECS/kernel-rt/0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt deleted file mode 100644 index aeaf00e4a..000000000 --- a/SPECS/kernel-rt/0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt +++ /dev/null @@ -1,29 +0,0 @@ -From 17f438779c1aa42aefb4f97da991a1a1bc72722c Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Mon, 21 Feb 2022 17:59:14 +0100 -Subject: [PATCH 9/9] Revert "drm/i915: Depend on !PREEMPT_RT." - -Once the known issues are addressed, it should be safe to enable the -driver. - -Acked-by: Tvrtko Ursulin -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/Kconfig | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig -index 5e939004b646..40a9234e6e5d 100644 ---- a/drivers/gpu/drm/i915/Kconfig -+++ b/drivers/gpu/drm/i915/Kconfig -@@ -3,7 +3,6 @@ config DRM_I915 - tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" - depends on DRM - depends on X86 && PCI -- depends on !PREEMPT_RT - select INTEL_GTT if X86 - select INTERVAL_TREE - # we need shmfs for the swappable backing store, and in particular --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c b/SPECS/kernel-rt/0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c deleted file mode 100644 index bc62e501d..000000000 --- a/SPECS/kernel-rt/0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c +++ /dev/null @@ -1,97 +0,0 @@ -From 87e39c8a0d700c78c5467465551cf812ed4123dc Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:08 +0300 -Subject: [PATCH 09/11] i3c: mipi-i3c-hci: Remove function enter DBG() - printouts - -These function enter DBG("") printouts are not very useful in error -report point of view because they require code recompile. In which case -they can be replaced with more informative debug prints if needed so -remove them for now. - -Signed-off-by: Jarkko Nikula -Link: https://lore.kernel.org/r/20250827103009.243771-5-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 16 ---------------- - 1 file changed, 16 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index d532933ac7ab..9932945ecf06 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -121,8 +121,6 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) - struct i3c_device_info info; - int ret; - -- DBG(""); -- - if (hci->cmd == &mipi_i3c_hci_cmd_v1) { - ret = mipi_i3c_hci_dat_v1.init(hci); - if (ret) -@@ -159,8 +157,6 @@ static void i3c_hci_bus_cleanup(struct i3c_master_controller *m) - struct i3c_hci *hci = to_i3c_hci(m); - struct platform_device *pdev = to_platform_device(m->dev.parent); - -- DBG(""); -- - reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE); - synchronize_irq(platform_get_irq(pdev, 0)); - hci->io->cleanup(hci); -@@ -267,8 +263,6 @@ static int i3c_hci_daa(struct i3c_master_controller *m) - { - struct i3c_hci *hci = to_i3c_hci(m); - -- DBG(""); -- - return hci->cmd->perform_daa(hci); - } - -@@ -385,8 +379,6 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev) - struct i3c_hci_dev_data *dev_data; - int ret; - -- DBG(""); -- - dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); - if (!dev_data) - return -ENOMEM; -@@ -410,8 +402,6 @@ static int i3c_hci_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr) - struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - -- DBG(""); -- - if (hci->cmd == &mipi_i3c_hci_cmd_v1) - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dev_data->dat_idx, - dev->info.dyn_addr); -@@ -424,8 +414,6 @@ static void i3c_hci_detach_i3c_dev(struct i3c_dev_desc *dev) - struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - -- DBG(""); -- - i3c_dev_set_master_data(dev, NULL); - if (hci->cmd == &mipi_i3c_hci_cmd_v1) - mipi_i3c_hci_dat_v1.free_entry(hci, dev_data->dat_idx); -@@ -439,8 +427,6 @@ static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *dev) - struct i3c_hci_dev_data *dev_data; - int ret; - -- DBG(""); -- - if (hci->cmd != &mipi_i3c_hci_cmd_v1) - return 0; - dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); -@@ -464,8 +450,6 @@ static void i3c_hci_detach_i2c_dev(struct i2c_dev_desc *dev) - struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i2c_dev_get_master_data(dev); - -- DBG(""); -- - if (dev_data) { - i2c_dev_set_master_data(dev, NULL); - if (hci->cmd == &mipi_i3c_hci_cmd_v1) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-igc-Add-BTF-based-metadata-for-XDP.ethernet b/SPECS/kernel-rt/0009-igc-Add-BTF-based-metadata-for-XDP.ethernet deleted file mode 100644 index e2cca1fdf..000000000 --- a/SPECS/kernel-rt/0009-igc-Add-BTF-based-metadata-for-XDP.ethernet +++ /dev/null @@ -1,235 +0,0 @@ -From fcd22d765ef29817abe0958e372bb4825e5a39ef Mon Sep 17 00:00:00 2001 -From: Vedang Patel -Date: Mon, 14 Jun 2021 00:35:20 +0800 -Subject: [PATCH 09/19] igc: Add BTF based metadata for XDP - -This commit adds support for BTF based metadata for XDP. Currently, the -support has only been tested on receive side. Following is the struct -describing the metadata: - -struct xdp_md_desc { - u64 timestamp; -}; - -Note that only a single member is added to the struct. More members will -be added in the future. - -Signed-off-by: Vedang Patel -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc.h | 2 + - drivers/net/ethernet/intel/igc/igc_main.c | 12 +++ - drivers/net/ethernet/intel/igc/igc_xdp.c | 114 ++++++++++++++++++++++ - drivers/net/ethernet/intel/igc/igc_xdp.h | 11 +++ - 4 files changed, 139 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h -index b9f846389e75..64d22481be91 100644 ---- a/drivers/net/ethernet/intel/igc/igc.h -+++ b/drivers/net/ethernet/intel/igc/igc.h -@@ -331,6 +331,8 @@ struct igc_adapter { - char fw_version[32]; - - struct bpf_prog *xdp_prog; -+ struct btf *btf; -+ u8 btf_enabled; - - bool pps_sys_wrap_on; - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index caf7bed4c8ae..b581e96d0735 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -14,6 +14,7 @@ - #include - #include - -+#include - #include - - #include "igc.h" -@@ -6880,6 +6881,12 @@ static int igc_bpf(struct net_device *dev, struct netdev_bpf *bpf) - case XDP_SETUP_XSK_POOL: - return igc_xdp_setup_pool(adapter, bpf->xsk.pool, - bpf->xsk.queue_id); -+ case XDP_SETUP_MD_BTF: -+ return igc_xdp_set_btf_md(dev, bpf->btf_enable); -+ case XDP_QUERY_MD_BTF: -+ bpf->btf_id = igc_xdp_query_btf(dev, &bpf->btf_enable); -+ return 0; -+ - default: - return -EOPNOTSUPP; - } -@@ -7420,6 +7427,11 @@ static void igc_remove(struct pci_dev *pdev) - if (IS_ENABLED(CONFIG_IGC_LEDS) && adapter->leds_available) - igc_led_free(adapter); - -+ if (adapter->btf) { -+ adapter->btf_enabled = 0; -+ btf_unregister(adapter->btf); -+ } -+ - /* Release control of h/w to f/w. If f/w is AMT enabled, this - * would have already happened in close and is redundant. - */ -diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.c b/drivers/net/ethernet/intel/igc/igc_xdp.c -index 9eb47b4beb06..c23e0385fc74 100644 ---- a/drivers/net/ethernet/intel/igc/igc_xdp.c -+++ b/drivers/net/ethernet/intel/igc/igc_xdp.c -@@ -3,10 +3,124 @@ - - #include - #include -+#include - - #include "igc.h" - #include "igc_xdp.h" - -+#define BTF_INFO_ENC(kind, kind_flag, vlen) \ -+ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) -+ -+#define BTF_TYPE_ENC(name, info, size_or_type) \ -+ (name), (info), (size_or_type) -+ -+#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \ -+ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits)) -+ -+#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \ -+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \ -+ BTF_INT_ENC(encoding, bits_offset, bits) -+ -+#define BTF_STRUCT_ENC(name, nr_elems, sz) \ -+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, nr_elems), sz) -+ -+#define BTF_MEMBER_ENC(name, type, bits_offset) \ -+ (name), (type), (bits_offset) -+ -+/* struct xdp_md_desc { -+ * u64 timestamp; -+ * }; -+ */ -+#define IGC_MD_NUM_MMBRS 1 -+static const char names_str[] = "\0xdp_md_desc\0timestamp\0"; -+ -+/* Must match struct xdp_md_desc */ -+static const u32 igc_md_raw_types[] = { -+ /* #define u64 */ -+ BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* type [1] */ -+ /* struct xdp_md_desc { */ -+ BTF_STRUCT_ENC(1, IGC_MD_NUM_MMBRS, 8), -+ BTF_MEMBER_ENC(13, 1, 0), /* u64 timestamp; */ -+ /* } */ -+}; -+ -+static int igc_xdp_register_btf(struct igc_adapter *priv) -+{ -+ unsigned int type_sec_sz, str_sec_sz; -+ char *types_sec, *str_sec; -+ struct btf_header *hdr; -+ unsigned int btf_size; -+ void *raw_btf = NULL; -+ int err = 0; -+ -+ type_sec_sz = sizeof(igc_md_raw_types); -+ str_sec_sz = sizeof(names_str); -+ -+ btf_size = sizeof(*hdr) + type_sec_sz + str_sec_sz; -+ raw_btf = kzalloc(btf_size, GFP_KERNEL); -+ if (!raw_btf) -+ return -ENOMEM; -+ -+ hdr = raw_btf; -+ hdr->magic = BTF_MAGIC; -+ hdr->version = BTF_VERSION; -+ hdr->hdr_len = sizeof(*hdr); -+ hdr->type_off = 0; -+ hdr->type_len = type_sec_sz; -+ hdr->str_off = type_sec_sz; -+ hdr->str_len = str_sec_sz; -+ -+ types_sec = raw_btf + sizeof(*hdr); -+ str_sec = types_sec + type_sec_sz; -+ memcpy(types_sec, igc_md_raw_types, type_sec_sz); -+ memcpy(str_sec, names_str, str_sec_sz); -+ -+ priv->btf = btf_register(priv->netdev->name, raw_btf, btf_size); -+ if (IS_ERR(priv->btf)) { -+ err = PTR_ERR(priv->btf); -+ priv->btf = NULL; -+ netdev_err(priv->netdev, "failed to register BTF MD, err (%d)\n", err); -+ } -+ -+ kfree(raw_btf); -+ return err; -+} -+ -+int igc_xdp_query_btf(struct net_device *dev, u8 *enabled) -+{ -+ struct igc_adapter *priv = netdev_priv(dev); -+ u32 md_btf_id = 0; -+ -+ if (!IS_ENABLED(CONFIG_BPF_SYSCALL)) -+ return md_btf_id; -+ -+ if (!priv->btf) -+ igc_xdp_register_btf(priv); -+ -+ *enabled = !!priv->btf_enabled; -+ md_btf_id = priv->btf ? btf_obj_id(priv->btf) : 0; -+ -+ return md_btf_id; -+} -+ -+int igc_xdp_set_btf_md(struct net_device *dev, u8 enable) -+{ -+ struct igc_adapter *priv = netdev_priv(dev); -+ int err = 0; -+ -+ if (enable && !priv->btf) { -+ igc_xdp_register_btf(priv); -+ if (!priv->btf) { -+ err = -EINVAL; -+ goto unlock; -+ } -+ } -+ -+ priv->btf_enabled = enable; -+unlock: -+ return err; -+} -+ - int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, - struct netlink_ext_ack *extack) - { -diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.h b/drivers/net/ethernet/intel/igc/igc_xdp.h -index a74e5487d199..644dd8a49a3a 100644 ---- a/drivers/net/ethernet/intel/igc/igc_xdp.h -+++ b/drivers/net/ethernet/intel/igc/igc_xdp.h -@@ -4,6 +4,12 @@ - #ifndef _IGC_XDP_H_ - #define _IGC_XDP_H_ - -+#include -+ -+struct igc_md_desc { -+ u64 timestamp; -+}; -+ - int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, - struct netlink_ext_ack *extack); - int igc_xdp_setup_pool(struct igc_adapter *adapter, struct xsk_buff_pool *pool, -@@ -14,4 +20,9 @@ static inline bool igc_xdp_is_enabled(struct igc_adapter *adapter) - return !!adapter->xdp_prog; - } - -+int igc_xdp_register_rxq_info(struct igc_ring *ring); -+void igc_xdp_unregister_rxq_info(struct igc_ring *ring); -+int igc_xdp_query_btf(struct net_device *dev, u8 *enabled); -+int igc_xdp_set_btf_md(struct net_device *dev, u8 enable); -+ - #endif /* _IGC_XDP_H_ */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel-rt/0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet new file mode 100644 index 000000000..b6db57848 --- /dev/null +++ b/SPECS/kernel-rt/0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet @@ -0,0 +1,43 @@ +From 84f1e9b417eb559e6b2b65280e5f656d0f9a1eea Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Wed, 9 Jun 2021 01:56:30 +0800 +Subject: [PATCH 09/14] igc: Enable HW RX Timestamp for AF_XDP ZC + +Enable the RX HW Timestamp using meta data to userspace. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc_main.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 526b98b603c05..5b797a0bd870d 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -2815,6 +2815,7 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) + u16 cleaned_count = igc_desc_unused(ring); + int total_bytes = 0, total_packets = 0; + u16 ntc = ring->next_to_clean; ++ struct igc_md_desc *md; + struct bpf_prog *prog; + bool failure = false; + int xdp_status = 0; +@@ -2863,6 +2864,14 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) + bi->xdp->data_end = bi->xdp->data + size; + xsk_buff_dma_sync_for_cpu(bi->xdp); + ++ if (adapter->btf_enabled) { ++ md = bi->xdp->data - sizeof(*md); ++ md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); ++ bi->xdp->data_meta = md; ++ } else { ++ xdp_set_data_meta_invalid(bi->xdp); ++ } ++ + res = __igc_xdp_run_prog(adapter, prog, bi->xdp); + switch (res) { + case IGC_XDP_PASS: +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-max9x-add-config-in-makefile-kconfig.ipu b/SPECS/kernel-rt/0009-max9x-add-config-in-makefile-kconfig.ipu deleted file mode 100644 index 75642a5a7..000000000 --- a/SPECS/kernel-rt/0009-max9x-add-config-in-makefile-kconfig.ipu +++ /dev/null @@ -1,53 +0,0 @@ -From ee759f8dcd8fe7bc5720ddfe2a5e55bf6da57a16 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Wed, 27 Aug 2025 11:09:54 +0800 -Subject: [PATCH 09/11] max9x: add config in makefile & kconfig - -Signed-off-by: hepengpx ---- - drivers/media/i2c/Kconfig | 15 +++++++++++++++ - drivers/media/i2c/Makefile | 2 ++ - 2 files changed, 17 insertions(+) - -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index 7ec788adeec1..f65a2f6118ea 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -278,6 +278,21 @@ config VIDEO_LT6911GXD - To compile this driver as a module, choose M here: the - module will be called lt6911gxd. - -+config VIDEO_ISX031 -+ tristate "ISX031 sensor support" -+ depends on VIDEO_DEV && I2C -+ select VIDEO_V4L2_SUBDEV_API -+ depends on MEDIA_CAMERA_SUPPORT -+ help -+ This is a Video4Linux2 sensor-level driver for ISX031 camera. -+ -+config VIDEO_MAX9X -+ tristate "MAX9X serdes support" -+ depends on VIDEO_DEV && I2C -+ select VIDEO_V4L2_SUBDEV_API -+ depends on MEDIA_CAMERA_SUPPORT -+ help -+ This is a Video4Linux2 sensor-level driver for MAX9X serdes. - - config VIDEO_MAX9271_LIB - tristate -diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile -index 2fbdd4c181d1..085c0e0e15dd 100644 ---- a/drivers/media/i2c/Makefile -+++ b/drivers/media/i2c/Makefile -@@ -61,6 +61,8 @@ obj-$(CONFIG_VIDEO_IMX412) += imx412.o - obj-$(CONFIG_VIDEO_IMX415) += imx415.o - obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o - obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o -+obj-$(CONFIG_VIDEO_MAX9X) += max9x/ -+obj-$(CONFIG_VIDEO_ISX031) += isx031.o - obj-$(CONFIG_VIDEO_KS0127) += ks0127.o - obj-$(CONFIG_VIDEO_LM3560) += lm3560.o - obj-$(CONFIG_VIDEO_LM3646) += lm3646.o --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu b/SPECS/kernel-rt/0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu deleted file mode 100644 index 6ec1374c0..000000000 --- a/SPECS/kernel-rt/0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu +++ /dev/null @@ -1,30 +0,0 @@ -From ed2fcce3d2b80e56411b1b7216885402fadac835 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 14:58:47 +0800 -Subject: [PATCH 09/21] patch: staging add fixup some PCI probe and release - issues - -Update IPU7 on pci functions on IPU7 driver. - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 06e5f08bed4d..3a6c129015e7 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -2300,7 +2300,7 @@ static void ipu7_remove_debugfs(struct ipu7_device *isp) - } - #endif /* CONFIG_DEBUG_FS */ - --static int ipu7_pci_config_setup(struct pci_dev *dev) -+static void ipu7_pci_config_setup(struct pci_dev *dev) - { - u16 pci_command; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf b/SPECS/kernel-rt/0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf deleted file mode 100644 index 05679822a..000000000 --- a/SPECS/kernel-rt/0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf +++ /dev/null @@ -1,41 +0,0 @@ -From 70cd5c863e317ca281602b5399395eda5b79dfbe Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Wed, 6 Mar 2024 06:58:31 +0800 -Subject: [PATCH 009/100] perf/x86: Remove helper perf_events_lapic_init() from - x86_pmu_enable() - -The helper perf_events_lapic_init() mainly writes APIC_LVTPC MSR to -configure PMI to NMI and clear MASK bit, but it seems unnecessary to -call it in x86_pmu_enable(). perf_events_lapic_init() is already called -by init_hw_perf_events() and MASK bit would be always cleared in PMI -interrupt handler. - -Since x86_pmu_enable() could be called very frequently in some high -context-switch cases, the performance overhead from APIC_LVTPC write -could not be ignored especially in Guest environment. - -For example, the Geekbench performance in perf-stat multiplexing case -increases 1% and perf-sched benchmark performance increases 7% in Guest -environment after removing perf_events_lapic_init() from -x86_pmu_enable(). - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index f0a3bc57157d..2007cfe1a0f5 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1368,7 +1368,6 @@ static void x86_pmu_enable(struct pmu *pmu) - x86_pmu_start(event, PERF_EF_RELOAD); - } - cpuc->n_added = 0; -- perf_events_lapic_init(); - } - - cpuc->enabled = 1; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf b/SPECS/kernel-rt/0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf new file mode 100644 index 000000000..8669ac358 --- /dev/null +++ b/SPECS/kernel-rt/0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf @@ -0,0 +1,181 @@ +From 516488d4800dc8a9a24a51861e0534aa89c071ff Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 8 Oct 2025 09:57:48 -0700 +Subject: [PATCH 09/13] perf/x86/intel/uncore: Support IIO free-running + counters on DMR + +The free-running counters for IIO uncore blocks on Diamond Rapids are +similar to Sapphire Rapids IMC freecounters, with the following +differences: + +- The counters are MMIO based. +- Only a subset of IP blocks implement free-running counters: + HIOP0 (IP Base Addr: 2E7000h) + HIOP1 (IP Base Addr: 2EF000h) + HIOP3 (IP Base Addr: 2FF000h) + HIOP4 (IP Base Addr: 307000h) +- IMH2 (Secondary IMH) does not provide free-running counters. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore_snbep.c | 118 +++++++++++++++++++++++++-- + 1 file changed, 113 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index 09a3bdbd188aa..28bcccf5cdfe9 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -472,10 +472,14 @@ + #define SPR_C0_MSR_PMON_BOX_FILTER0 0x200e + + /* DMR */ ++#define DMR_IMH1_HIOP_MMIO_BASE 0x1ffff6ae7000 ++#define DMR_HIOP_MMIO_SIZE 0x8000 + #define DMR_CXLCM_EVENT_MASK_EXT 0xf + #define DMR_HAMVF_EVENT_MASK_EXT 0xffffffff + #define DMR_PCIE4_EVENT_MASK_EXT 0xffffff + ++#define UNCORE_DMR_ITC 0x30 ++ + #define DMR_IMC_PMON_FIXED_CTR 0x18 + #define DMR_IMC_PMON_FIXED_CTL 0x10 + +@@ -6442,7 +6446,11 @@ static int uncore_type_max_boxes(struct intel_uncore_type **types, + for (node = rb_first(type->boxes); node; node = rb_next(node)) { + unit = rb_entry(node, struct intel_uncore_discovery_unit, node); + +- if (unit->id > max) ++ /* ++ * on DMR IMH2, the unit id starts from 0x8000, ++ * and we don't need to count it. ++ */ ++ if ((unit->id > max) && (unit->id < 0x8000)) + max = unit->id; + } + return max + 1; +@@ -6930,6 +6938,101 @@ int dmr_uncore_cbb_units_ignore[] = { + UNCORE_IGNORE_END + }; + ++static unsigned int dmr_iio_freerunning_box_offsets[] = { ++ 0x0, 0x8000, 0x18000, 0x20000 ++}; ++ ++static void dmr_uncore_freerunning_init_box(struct intel_uncore_box *box) ++{ ++ struct intel_uncore_type *type = box->pmu->type; ++ u64 mmio_base; ++ ++ if (box->pmu->pmu_idx >= type->num_boxes) ++ return; ++ ++ mmio_base = DMR_IMH1_HIOP_MMIO_BASE; ++ mmio_base += dmr_iio_freerunning_box_offsets[box->pmu->pmu_idx]; ++ ++ box->io_addr = ioremap(mmio_base, type->mmio_map_size); ++ if (!box->io_addr) ++ pr_warn("perf uncore: Failed to ioremap for %s.\n", type->name); ++} ++ ++static struct intel_uncore_ops dmr_uncore_freerunning_ops = { ++ .init_box = dmr_uncore_freerunning_init_box, ++ .exit_box = uncore_mmio_exit_box, ++ .read_counter = uncore_mmio_read_counter, ++ .hw_config = uncore_freerunning_hw_config, ++}; ++ ++enum perf_uncore_dmr_iio_freerunning_type_id { ++ DMR_ITC_INB_DATA_BW, ++ DMR_ITC_BW_IN, ++ DMR_OTC_BW_OUT, ++ DMR_OTC_CLOCK_TICKS, ++ ++ DMR_IIO_FREERUNNING_TYPE_MAX, ++}; ++ ++static struct freerunning_counters dmr_iio_freerunning[] = { ++ [DMR_ITC_INB_DATA_BW] = { 0x4d40, 0x8, 0, 8, 48}, ++ [DMR_ITC_BW_IN] = { 0x6b00, 0x8, 0, 8, 48}, ++ [DMR_OTC_BW_OUT] = { 0x6b60, 0x8, 0, 8, 48}, ++ [DMR_OTC_CLOCK_TICKS] = { 0x6bb0, 0x8, 0, 1, 48}, ++}; ++ ++static struct uncore_event_desc dmr_uncore_iio_freerunning_events[] = { ++ /* ITC Free Running Data BW counter for inbound traffic */ ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port0, 0x10, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port1, 0x11, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port2, 0x12, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port3, 0x13, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port4, 0x14, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port5, 0x15, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port6, 0x16, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port7, 0x17, "3.814697266e-6"), ++ ++ /* ITC Free Running BW IN counters */ ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port4, 0x24, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port5, 0x25, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port6, 0x26, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port7, 0x27, "3.814697266e-6"), ++ ++ /* ITC Free Running BW OUT counters */ ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port0, 0x30, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port1, 0x31, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port2, 0x32, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port3, 0x33, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port4, 0x34, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port5, 0x35, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port6, 0x36, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port7, 0x37, "3.814697266e-6"), ++ ++ /* Free Running Clock Counter */ ++ INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x40"), ++ { /* end: all zeroes */ }, ++}; ++ ++static struct intel_uncore_type dmr_uncore_iio_free_running = { ++ .name = "iio_free_running", ++ .num_counters = 25, ++ .mmio_map_size = DMR_HIOP_MMIO_SIZE, ++ .num_freerunning_types = DMR_IIO_FREERUNNING_TYPE_MAX, ++ .freerunning = dmr_iio_freerunning, ++ .ops = &dmr_uncore_freerunning_ops, ++ .event_descs = dmr_uncore_iio_freerunning_events, ++ .format_group = &skx_uncore_iio_freerunning_format_group, ++}; ++ ++#define UNCORE_DMR_MMIO_EXTRA_UNCORES 1 ++static struct intel_uncore_type *dmr_mmio_uncores[UNCORE_DMR_MMIO_EXTRA_UNCORES] = { ++ &dmr_uncore_iio_free_running, ++}; ++ + int dmr_uncore_pci_init(void) + { + uncore_pci_uncores = uncore_get_uncores(UNCORE_ACCESS_PCI, 0, NULL, +@@ -6937,11 +7040,16 @@ int dmr_uncore_pci_init(void) + dmr_uncores); + return 0; + } ++ + void dmr_uncore_mmio_init(void) + { +- uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, 0, NULL, +- UNCORE_DMR_NUM_UNCORE_TYPES, +- dmr_uncores); +-} ++ uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, ++ UNCORE_DMR_MMIO_EXTRA_UNCORES, ++ dmr_mmio_uncores, ++ UNCORE_DMR_NUM_UNCORE_TYPES, ++ dmr_uncores); + ++ dmr_uncore_iio_free_running.num_boxes = ++ uncore_type_max_boxes(uncore_mmio_uncores, UNCORE_DMR_ITC); ++} + /* end of DMR uncore support */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet b/SPECS/kernel-rt/0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet new file mode 100644 index 000000000..e63fef9b7 --- /dev/null +++ b/SPECS/kernel-rt/0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet @@ -0,0 +1,27 @@ +From 60ecf14a41389646b286adc9987e0042380bc217 Mon Sep 17 00:00:00 2001 +From: Gan Yi Fang +Date: Thu, 22 Jun 2023 21:27:11 -0400 +Subject: [PATCH 09/18] stmmac: intel: Enable PHY WoL in ADL-N + +Enable PHY Wake On LAN in ADL-N Intel platform. + +Signed-off-by: Gan Yi Fang +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index bf6544ec769b2..ca16568374d5a 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -1019,6 +1019,7 @@ static int adln_common_data(struct pci_dev *pdev, + plat->rx_queues_to_use = 6; + plat->tx_queues_to_use = 4; + plat->clk_ptp_rate = 204800000; ++ plat->flags |= STMMAC_FLAG_USE_PHY_WOL; + + plat->safety_feat_cfg->tsoee = 1; + plat->safety_feat_cfg->mrxpee = 0; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-sysfs-Add-sys-kernel-realtime-entry.rt b/SPECS/kernel-rt/0009-sysfs-Add-sys-kernel-realtime-entry.rt new file mode 100644 index 000000000..4a9dec296 --- /dev/null +++ b/SPECS/kernel-rt/0009-sysfs-Add-sys-kernel-realtime-entry.rt @@ -0,0 +1,54 @@ +From 80ad4b089250eab455aa32fbd3755dafe16d78a2 Mon Sep 17 00:00:00 2001 +From: Clark Williams +Date: Sat, 30 Jul 2011 21:55:53 -0500 +Subject: [PATCH 9/9] sysfs: Add /sys/kernel/realtime entry + +Add a /sys/kernel entry to indicate that the kernel is a +realtime kernel. + +Clark says that he needs this for udev rules, udev needs to evaluate +if its a PREEMPT_RT kernel a few thousand times and parsing uname +output is too slow or so. + +Are there better solutions? Should it exist and return 0 on !-rt? + +Signed-off-by: Clark Williams +Signed-off-by: Peter Zijlstra +Signed-off-by: Thomas Gleixner +--- + kernel/ksysfs.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c +index eefb67d9883c2..20ac8a5ac27d7 100644 +--- a/kernel/ksysfs.c ++++ b/kernel/ksysfs.c +@@ -188,6 +188,15 @@ KERNEL_ATTR_RO(crash_elfcorehdr_size); + + #endif /* CONFIG_VMCORE_INFO */ + ++#if defined(CONFIG_PREEMPT_RT) ++static ssize_t realtime_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", 1); ++} ++KERNEL_ATTR_RO(realtime); ++#endif ++ + /* whether file capabilities are enabled */ + static ssize_t fscaps_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +@@ -271,6 +280,9 @@ static struct attribute * kernel_attrs[] = { + #ifndef CONFIG_TINY_RCU + &rcu_expedited_attr.attr, + &rcu_normal_attr.attr, ++#endif ++#ifdef CONFIG_PREEMPT_RT ++ &realtime_attr.attr, + #endif + NULL + }; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal b/SPECS/kernel-rt/0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal deleted file mode 100644 index 530a1baf7..000000000 --- a/SPECS/kernel-rt/0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal +++ /dev/null @@ -1,35 +0,0 @@ -From 48c98aeb5aaf5b357d8ab559ccf6b36103539c3b Mon Sep 17 00:00:00 2001 -From: Osama Abdelkader -Date: Wed, 3 Sep 2025 21:20:59 +0200 -Subject: [PATCH 09/11] thermal: hwmon: replace deprecated strcpy() with - strscpy() - -Since strcpy() is deprecated and the last user of it in the thermal -subsystem is thermal_hwmon_lookup_by_type(), replace strcpy() in that -function with strscpy(). - -Signed-off-by: Osama Abdelkader -Reviewed-by: Lukasz Luba -Link: https://patch.msgid.link/20250903192059.11353-1-osama.abdelkader@gmail.com -[ rjw: Changelog rewrite ] -Signed-off-by: Rafael J. Wysocki ---- - drivers/thermal/thermal_hwmon.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c -index 0ecccd4d8556..64cc3ab949fe 100644 ---- a/drivers/thermal/thermal_hwmon.c -+++ b/drivers/thermal/thermal_hwmon.c -@@ -96,7 +96,7 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) - - mutex_lock(&thermal_hwmon_list_lock); - list_for_each_entry(hwmon, &thermal_hwmon_list, node) { -- strcpy(type, tz->type); -+ strscpy(type, tz->type); - strreplace(type, '-', '_'); - if (!strcmp(hwmon->type, type)) { - mutex_unlock(&thermal_hwmon_list_lock); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo b/SPECS/kernel-rt/0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo new file mode 100644 index 000000000..12edbae0c --- /dev/null +++ b/SPECS/kernel-rt/0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo @@ -0,0 +1,30 @@ +From 1b1e446c16021a5bc12c9ff3dc6f49d1d8d5e531 Mon Sep 17 00:00:00 2001 +From: Emily Ehlert +Date: Thu, 13 Nov 2025 19:16:08 +0000 +Subject: [PATCH 09/21] tools/power turbostat: Set per_cpu_msr_sum to NULL + after free + +Set per_cpu_msr_sum to NULL after freeing it in the error path +of msr_sum_record() to prevent potential use-after-free issues. + +Signed-off-by: Emily Ehlert +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 2854b66eb7480..8154d110dd07d 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -6652,6 +6652,7 @@ void msr_sum_record(void) + timer_delete(timerid); + release_msr: + free(per_cpu_msr_sum); ++ per_cpu_msr_sum = NULL; + } + + /* +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac b/SPECS/kernel-rt/0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac deleted file mode 100644 index 6b52477d8..000000000 --- a/SPECS/kernel-rt/0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac +++ /dev/null @@ -1,44 +0,0 @@ -From c217243dd29072f467eadbbf01719bfb4a5453b2 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Sun, 27 Oct 2019 09:23:32 +0800 -Subject: [PATCH 09/13] x86/mce: Add MCACOD code for generic I/O error - -Errors of some I/O devices can be signaled by MCE and logged in -IOMCA bank. Add MCACOD code of generic I/O error and related macros -for MCi_MISC to support IOMCA logging. - -See Intel Software Developers' Manual, version 071, volume 3B, -section "IOMCA". - -Signed-off-by: Qiuxu Zhuo ---- - arch/x86/include/asm/mce.h | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h -index 6c77c03139f7..fcb369287dd2 100644 ---- a/arch/x86/include/asm/mce.h -+++ b/arch/x86/include/asm/mce.h -@@ -81,6 +81,7 @@ - #define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */ - #define MCACOD_DATA 0x0134 /* Data Load */ - #define MCACOD_INSTR 0x0150 /* Instruction Fetch */ -+#define MCACOD_IOERR 0x0e0b /* Generic I/O error */ - - /* MCi_MISC register defines */ - #define MCI_MISC_ADDR_LSB(m) ((m) & 0x3f) -@@ -94,6 +95,11 @@ - /* MCi_ADDR register defines */ - #define MCI_ADDR_PHYSADDR GENMASK_ULL(boot_cpu_data.x86_phys_bits - 1, 0) - -+#define MCI_MISC_PCISEG_MASK GENMASK_ULL(39, 32) -+#define MCI_MISC_PCISEG(m) (((m) & MCI_MISC_PCISEG_MASK) >> 32) -+#define MCI_MISC_PCIRID_MASK GENMASK_ULL(31, 16) -+#define MCI_MISC_PCIRID(m) (((m) & MCI_MISC_PCIRID_MASK) >> 16) -+ - /* CTL2 register defines */ - #define MCI_CTL2_CMCI_EN BIT_ULL(30) - #define MCI_CTL2_CMCI_THRESHOLD_MASK 0x7fffULL --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac b/SPECS/kernel-rt/0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac deleted file mode 100644 index 9eade3b33..000000000 --- a/SPECS/kernel-rt/0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac +++ /dev/null @@ -1,850 +0,0 @@ -From 244e4b1754eececf2398814b99a7c1392bc46d5a Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Sun, 27 Oct 2019 12:50:01 +0800 -Subject: [PATCH 10/13] EDAC/ieh: Add I/O device EDAC driver for Intel CPUs - with IEH - -Integrated Error Handlers (IEHs) are PCIe devices which aggregate and -report error events of different severities (correctable, non-fatal -uncorrectable, and fatal uncorrectable) from various I/O devices, e.g., -PCIe devices, legacy PCI devices. Each error severity is notified by -one of {SMI, NMI, MCE} which is configured by BIOS/platform firmware. - -The first IEH-supported platform is Intel Tiger Lake-U CPU. The driver -reads/prints the error severity and error source (bus/device/function) -logged in the IEH(s) and restarts the system on fatal I/O device error. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/Kconfig | 10 + - drivers/edac/Makefile | 1 + - drivers/edac/ieh_edac.c | 784 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 795 insertions(+) - create mode 100644 drivers/edac/ieh_edac.c - -diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig -index 19ad3c3b675d..ca22dd8bf253 100644 ---- a/drivers/edac/Kconfig -+++ b/drivers/edac/Kconfig -@@ -311,6 +311,16 @@ config EDAC_IGEN6 - This In-Band ECC is first used on the Elkhart Lake SoC but - may appear on others in the future. - -+config EDAC_IEH -+ tristate "Intel Integrated Error Handler" -+ depends on PCI && X86_64 -+ help -+ Support for error detection and correction on the Intel -+ CPU using I/O IEH (Integrated Error Handler). IEHs are PCIe -+ devices which aggregate and report error events of different -+ severities from various I/O devices, e.g., PCIe devices and -+ legacy PCI devices. -+ - config EDAC_MPC85XX - bool "Freescale MPC83xx / MPC85xx" - depends on FSL_SOC && EDAC=y -diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile -index a8f2d8f6c894..9ca5ae143a6b 100644 ---- a/drivers/edac/Makefile -+++ b/drivers/edac/Makefile -@@ -36,6 +36,7 @@ obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o - obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o - obj-$(CONFIG_EDAC_PND2) += pnd2_edac.o - obj-$(CONFIG_EDAC_IGEN6) += igen6_edac.o -+obj-$(CONFIG_EDAC_IEH) += ieh_edac.o - obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o - obj-$(CONFIG_EDAC_E752X) += e752x_edac.o - obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o -diff --git a/drivers/edac/ieh_edac.c b/drivers/edac/ieh_edac.c -new file mode 100644 -index 000000000000..81bba5aa775e ---- /dev/null -+++ b/drivers/edac/ieh_edac.c -@@ -0,0 +1,784 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Driver for Intel Integrated Error Handler (IEH) -+ * -+ * Copyright (C) 2020 Intel Corporation -+ * -+ * IEH centralizes and standardizes how I/O device errors are reported. -+ * They are PCIe devices which aggregate and report error events of different -+ * severities (correctable, non-fatal uncorrectable, and fatal uncorrectable) -+ * from various I/O devices, e.g., PCIe devices, legacy PCI devices. -+ * -+ * There is a global IEH and optional north/south satellite IEH(s) logically -+ * connected to global IEH. The global IEH is the root to process all incoming -+ * error messages from satellite IEH(s) and local devices (if some devices -+ * are connected directly to the global IEH) and generate interrupts(SMI/NMI/MCE -+ * configured by BIOS/platform firmware). The first IEH-supported platform is -+ * Tiger Lake-U. This driver reads/prints the error severity and error source -+ * (bus/device/function) logged in the IEH(s) and reboots the system on fatal -+ * IEH errors. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "edac_mc.h" -+ -+#define IEH_REVISION "v1.7" -+ -+#define EDAC_MOD_STR "ieh_edac" -+#define IEH_NMI_NAME "ieh" -+ -+#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) -+ -+/* Global correctable error status */ -+#define GCOERRSTS_OFFSET 0x200 -+/* Global non-fatal error status */ -+#define GNFERRSTS_OFFSET 0x210 -+/* Global fatal error status */ -+#define GFAERRSTS_OFFSET 0x220 -+ -+/* Global correctable error mask */ -+#define GCOERRMSK_OFFSET 0x230 -+#define GCOERRMSK 0xffffffff -+/* Global nonfatal error mask */ -+#define GNFERRMSK_OFFSET 0x234 -+#define GNFERRMSK 0xffffffff -+/* Global fatal error mask */ -+#define GFAERRMSK_OFFSET 0x238 -+#define GFAERRMSK 0xffffffff -+ -+/* Global system event status */ -+#define GSYSEVTSTS_OFFSET 0x260 -+ -+/* Global system event mask */ -+#define GSYSEVTMSK_OFFSET 0x264 -+#define GSYSEVTMSK 0x7 -+#define GSYSEVTMSK_CORR BIT(0) -+#define GSYSEVTMSK_NONFATAL BIT(1) -+#define GSYSEVTMSK_FATAL BIT(2) -+ -+/* Global system event map */ -+#define GSYSEVTMAP_OFFSET 0x268 -+#define GSYSEVTMAP_CORR(m) GET_BITFIELD(m, 0, 1) -+#define GSYSEVTMAP_NONFATAL(m) GET_BITFIELD(m, 2, 3) -+#define GSYSEVTMAP_FATAL(m) GET_BITFIELD(m, 4, 5) -+#define GSYSEVTMAP_MCE 0x3f -+ -+/* IEH type and version */ -+#define IEHTYPEVER_OFFSET 0x26c -+#define IEHTYPEVER_TYPE(t) GET_BITFIELD(t, 0, 3) -+#define IEHTYPEVER_VER(t) GET_BITFIELD(t, 4, 7) -+#define IEHTYPEVER_BUS(t) GET_BITFIELD(t, 8, 15) -+ -+/* Bitmap field of satellite IEH */ -+#define BITMAP_OFFSET 0x27c -+#define BITMAP(m) GET_BITFIELD(m, 0, 4) -+ -+/* Local uncorrectable error mask */ -+#define LERRUNCMSK_OFFSET 0x298 -+#define LERRUNCMSK 0xffffffff -+/* Local correctable error mask */ -+#define LERRCORMSK_OFFSET 0x2c0 -+#define LERRCORMSK 0xffffffff -+ -+/* Device number and function number of the device reporting to IEH */ -+#define DEVFUN_OFFSET 0x300 -+#define DEVFUN_FUN(d) GET_BITFIELD(d, 0, 2) -+#define DEVFUN_DEV(d) GET_BITFIELD(d, 3, 7) -+ -+#define ieh_printk(level, fmt, arg...) \ -+ edac_printk(level, "ieh", fmt, ##arg) -+ -+#define PCI_ADDR(sbdf) (sbdf)->seg, (sbdf)->bus, (sbdf)->dev, (sbdf)->fun -+ -+/* Error notification methods */ -+enum evt_map { -+ IEH_IGN, -+ IEH_SMI, -+ IEH_NMI, -+ IEH_MCE, -+}; -+ -+enum severity_level { -+ IEH_CORR_ERR, -+ IEH_NONFATAL_ERR, -+ IEH_FATAL_ERR, -+}; -+ -+enum ieh_type { -+ /* Global IEH */ -+ IEH_GLOBAL, -+ /* North satellite IEH logically connected to global IEH */ -+ IEH_NORTH, -+ /* South satellite IEH logically connected to north IEH */ -+ IEH_SOUTH, -+ /* -+ * Superset south satellite IEH with physical ERR[2:0] signals output. -+ * It's used as a global IEH (when it present, system has only one IEH). -+ */ -+ IEH_SUPERSET, -+}; -+ -+enum action_on_fatal_err { -+ NOP, -+ RESTART, -+ POWER_OFF, -+}; -+ -+struct pci_sbdf { -+ u32 seg : 16; -+ u32 bus : 8; -+ u32 dev : 5; -+ u32 fun : 3; -+}; -+ -+struct ieh_dev { -+ struct list_head list; -+ struct pci_dev *pdev; -+ struct pci_sbdf sbdf; -+ enum ieh_type type; -+ u8 ver; -+ /* Global IEH fields */ -+ enum evt_map corr_map; -+ enum evt_map nonfatal_map; -+ enum evt_map fatal_map; -+}; -+ -+static struct ieh_config { -+ u16 did; -+ enum action_on_fatal_err action; -+} *ieh_cfg; -+ -+struct decoded_res { -+ enum severity_level sev; -+ struct pci_sbdf sbdf; -+}; -+ -+static LIST_HEAD(global_ieh_list); -+static LIST_HEAD(north_ieh_list); -+static LIST_HEAD(south_ieh_list); -+ -+/* Tiger Lake-U SoC */ -+#define IEH_DID_TGL_U 0xa0af -+ -+static struct ieh_config tgl_u_cfg = { -+ .did = IEH_DID_TGL_U, -+ .action = RESTART, -+}; -+ -+static const char * const severities[] = { -+ [IEH_CORR_ERR] = "correctable", -+ [IEH_NONFATAL_ERR] = "non-fatal uncorrectable", -+ [IEH_FATAL_ERR] = "fatal uncorrectable", -+}; -+ -+static struct irq_work ieh_irq_work; -+ -+static int dev_idx(u32 status, int start) -+{ -+ int i; -+ -+ for (i = start; i < 32; i++) { -+ if (status & (1 << i)) -+ return i; -+ } -+ -+ return -1; -+} -+ -+static inline bool has_notification_by(enum evt_map map) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ if (ieh->corr_map == map || ieh->nonfatal_map == map || -+ ieh->fatal_map == map) -+ return true; -+ } -+ -+ return false; -+} -+ -+static void ieh_output_error(struct decoded_res *res) -+{ -+ struct pci_sbdf *p = &res->sbdf; -+ -+ ieh_printk(KERN_ERR, "Device %04x:%02x:%02x.%x - %s error\n", -+ p->seg, p->bus, p->dev, -+ p->fun, severities[res->sev]); -+ -+ if (res->sev != IEH_FATAL_ERR) -+ return; -+ -+ switch (ieh_cfg->action) { -+ case RESTART: -+ ieh_printk(KERN_EMERG, "Restart system on device fatal error!\n"); -+ kernel_restart(NULL); -+ break; -+ -+ case POWER_OFF: -+ ieh_printk(KERN_EMERG, "Power off system on device fatal error!\n"); -+ kernel_power_off(); -+ break; -+ default: -+ break; -+ } -+ -+ /* TODO: Further report error information from the error source */ -+} -+ -+static bool is_same_pdev(struct pci_sbdf *p, struct pci_sbdf *q) -+{ -+ return (p->seg == q->seg && p->bus == q->bus && -+ p->dev == q->dev && p->fun == q->fun); -+} -+ -+static struct ieh_dev *__get_ieh(struct list_head *ieh_list, -+ struct pci_sbdf *sbdf) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, ieh_list, list) { -+ if (is_same_pdev(sbdf, &ieh->sbdf)) -+ return ieh; -+ } -+ -+ return NULL; -+} -+ -+static struct ieh_dev *get_global_ieh(struct pci_sbdf *sbdf) -+{ -+ return __get_ieh(&global_ieh_list, sbdf); -+} -+ -+static inline struct ieh_dev *get_north_sat_ieh(struct pci_sbdf *sbdf) -+{ -+ return __get_ieh(&north_ieh_list, sbdf); -+} -+ -+static inline struct ieh_dev *get_south_sat_ieh(struct pci_sbdf *sbdf) -+{ -+ return __get_ieh(&south_ieh_list, sbdf); -+} -+ -+static int read_and_clear(struct pci_dev *pdev, int offset, u32 *val) -+{ -+ if (pci_read_config_dword(pdev, offset, val)) { -+ ieh_printk(KERN_ERR, "Failed to read 0x%x\n", offset); -+ return -ENODEV; -+ } -+ -+ /* Write 1s to clear status */ -+ if (pci_write_config_dword(pdev, offset, *val)) { -+ ieh_printk(KERN_ERR, "Failed to write 0x%x\n", offset); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+#define UNMASK_ERR_EVENT(ieh, name) \ -+ do { \ -+ u32 val; \ -+ if (pci_read_config_dword(ieh->pdev, name##MSK_OFFSET, &val)) \ -+ return -ENODEV; \ -+ val &= ~name##MSK; \ -+ if (pci_write_config_dword(ieh->pdev, name##MSK_OFFSET, val)) \ -+ return -ENODEV; \ -+ } while (0) -+ -+static int unmask_all_err_events(void) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ UNMASK_ERR_EVENT(ieh, GFAERR); -+ UNMASK_ERR_EVENT(ieh, GNFERR); -+ UNMASK_ERR_EVENT(ieh, GCOERR); -+ UNMASK_ERR_EVENT(ieh, LERRUNC); -+ UNMASK_ERR_EVENT(ieh, LERRCOR); -+ UNMASK_ERR_EVENT(ieh, GSYSEVT); -+ } -+ -+ return 0; -+} -+ -+#define MASK_ERR_EVENT(ieh, name) \ -+ do { \ -+ u32 val; \ -+ if (pci_read_config_dword(ieh->pdev, name##MSK_OFFSET, &val)) \ -+ return -ENODEV; \ -+ val |= name##MSK; \ -+ if (pci_write_config_dword(ieh->pdev, name##MSK_OFFSET, val)) \ -+ return -ENODEV; \ -+ } while (0) -+ -+static int mask_all_err_events(void) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ MASK_ERR_EVENT(ieh, GFAERR); -+ MASK_ERR_EVENT(ieh, GNFERR); -+ MASK_ERR_EVENT(ieh, GCOERR); -+ MASK_ERR_EVENT(ieh, LERRUNC); -+ MASK_ERR_EVENT(ieh, LERRCOR); -+ MASK_ERR_EVENT(ieh, GSYSEVT); -+ } -+ -+ return 0; -+} -+ -+static int ieh_handle_error(struct ieh_dev *d, enum severity_level sev) -+{ -+ struct decoded_res res; -+ struct pci_sbdf *sbdf = &res.sbdf; -+ struct ieh_dev *ieh; -+ int i, start = 0; -+ u32 sts, reg; -+ -+ switch (sev) { -+ case IEH_CORR_ERR: -+ if (read_and_clear(d->pdev, GCOERRSTS_OFFSET, &sts)) -+ return -ENODEV; -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GCOERRSTS: 0x%x\n", -+ PCI_ADDR(&d->sbdf), sts); -+ break; -+ case IEH_NONFATAL_ERR: -+ if (read_and_clear(d->pdev, GNFERRSTS_OFFSET, &sts)) -+ return -ENODEV; -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GNFERRSTS: 0x%x\n", -+ PCI_ADDR(&d->sbdf), sts); -+ break; -+ case IEH_FATAL_ERR: -+ if (read_and_clear(d->pdev, GFAERRSTS_OFFSET, &sts)) -+ return -ENODEV; -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GFAERRSTS: 0x%x\n", -+ PCI_ADDR(&d->sbdf), sts); -+ break; -+ } -+ -+ while ((i = dev_idx(sts, start)) != -1) { -+ if (pci_read_config_dword(d->pdev, DEVFUN_OFFSET + i * 4, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read DEVFUN %d\n", i); -+ return -ENODEV; -+ } -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x DEVFUN %d: 0x%x\n", -+ PCI_ADDR(&d->sbdf), i, reg); -+ -+ memset(&res, 0, sizeof(res)); -+ res.sev = sev; -+ sbdf->seg = d->sbdf.seg; -+ sbdf->bus = d->sbdf.bus; -+ sbdf->dev = DEVFUN_DEV(reg); -+ sbdf->fun = DEVFUN_FUN(reg); -+ -+ switch (d->type) { -+ case IEH_GLOBAL: -+ ieh = get_north_sat_ieh(sbdf); -+ if (!ieh) -+ ieh_output_error(&res); -+ else if (ieh->type == IEH_NORTH) -+ ieh_handle_error(ieh, sev); -+ else -+ ieh_printk(KERN_ERR, "Invalid global IEH\n"); -+ break; -+ case IEH_NORTH: -+ ieh = get_south_sat_ieh(sbdf); -+ if (!ieh) -+ ieh_output_error(&res); -+ else if (ieh->type == IEH_SOUTH) -+ ieh_handle_error(ieh, sev); -+ else -+ ieh_printk(KERN_ERR, "Invalid north IEH\n"); -+ break; -+ case IEH_SOUTH: -+ case IEH_SUPERSET: -+ ieh_output_error(&res); -+ break; -+ } -+ -+ start = i + 1; -+ } -+ -+ return 0; -+} -+ -+static void __ieh_check_error(struct ieh_dev *ieh) -+{ -+ struct pci_dev *pdev = ieh->pdev; -+ u32 sts; -+ -+ if (pci_read_config_dword(pdev, GSYSEVTSTS_OFFSET, &sts)) { -+ ieh_printk(KERN_ERR, "Failed to read GSYSEVTSTS\n"); -+ return; -+ } -+ -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GSYSEVTSTS: 0x%x\n", -+ PCI_ADDR(&ieh->sbdf), sts); -+ -+ if ((sts & (1 << IEH_FATAL_ERR)) && ieh->fatal_map == IEH_NMI) -+ ieh_handle_error(ieh, IEH_FATAL_ERR); -+ -+ if ((sts & (1 << IEH_NONFATAL_ERR)) && ieh->nonfatal_map == IEH_NMI) -+ ieh_handle_error(ieh, IEH_NONFATAL_ERR); -+ -+ if ((sts & (1 << IEH_CORR_ERR)) && ieh->corr_map == IEH_NMI) -+ ieh_handle_error(ieh, IEH_CORR_ERR); -+} -+ -+static void ieh_check_error(void) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ __ieh_check_error(ieh); -+ } -+} -+ -+static void ieh_irq_work_cb(struct irq_work *irq_work) -+{ -+ ieh_check_error(); -+} -+ -+static int ieh_nmi_handler(unsigned int cmd, struct pt_regs *regs) -+{ -+ irq_work_queue(&ieh_irq_work); -+ return 0; -+} -+ -+static int mce_check_error(struct notifier_block *nb, unsigned long val, -+ void *data) -+{ -+ struct mce *mce = (struct mce *)data; -+ struct decoded_res res; -+ struct pci_sbdf *sbdf = &res.sbdf; -+ struct ieh_dev *ieh; -+ u64 rid; -+ -+ /* TODO: For debug only. Remove them later. */ -+ ieh_printk(KERN_DEBUG, "MCi_STATUS 0x%llx\n", mce->status); -+ ieh_printk(KERN_DEBUG, "MCi_MISC 0x%llx\n", mce->misc); -+ ieh_printk(KERN_DEBUG, "MCi_ADDR 0x%llx\n", mce->addr); -+ ieh_printk(KERN_DEBUG, "MCGSTATUS 0x%llx\n", mce->mcgstatus); -+ ieh_printk(KERN_DEBUG, "MCGSCAP 0x%llx\n", mce->mcgcap); -+ ieh_printk(KERN_DEBUG, "IP 0x%llx\n", mce->ip); -+ ieh_printk(KERN_DEBUG, "MC bank 0x%x\n", mce->bank); -+ -+ if ((mce->status & MCACOD) != MCACOD_IOERR) -+ return NOTIFY_DONE; -+ -+ if (!(mce->status & MCI_STATUS_MISCV)) -+ return NOTIFY_DONE; -+ -+ memset(&res, 0, sizeof(res)); -+ rid = MCI_MISC_PCIRID(mce->misc); -+ sbdf->seg = MCI_MISC_PCISEG(mce->misc); -+ sbdf->bus = GET_BITFIELD(rid, 8, 15); -+ sbdf->dev = GET_BITFIELD(rid, 3, 7); -+ sbdf->fun = GET_BITFIELD(rid, 0, 2); -+ -+ if (mce->status & MCI_STATUS_PCC) -+ res.sev = IEH_FATAL_ERR; -+ else if (mce->status & MCI_STATUS_UC) -+ res.sev = IEH_NONFATAL_ERR; -+ else -+ res.sev = IEH_CORR_ERR; -+ -+ ieh = get_global_ieh(sbdf); -+ if (ieh) -+ goto handle; -+ -+ ieh = get_north_sat_ieh(sbdf); -+ if (ieh) -+ goto handle; -+ -+ ieh = get_south_sat_ieh(sbdf); -+ if (ieh) -+ goto handle; -+ -+ goto output; -+ -+handle: -+ ieh_handle_error(ieh, res.sev); -+ mce->kflags |= MCE_HANDLED_EDAC; -+ return NOTIFY_DONE; -+ -+output: -+ ieh_output_error(&res); -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block ieh_mce_dec = { -+ .notifier_call = mce_check_error, -+ .priority = MCE_PRIO_EDAC, -+}; -+ -+static const struct x86_cpu_id ieh_cpuids[] = { -+ X86_MATCH_VFM(INTEL_TIGERLAKE_L, &tgl_u_cfg), -+ {} -+}; -+MODULE_DEVICE_TABLE(x86cpu, ieh_cpuids); -+ -+static void __put_ieh(struct ieh_dev *ieh) -+{ -+ if (!ieh) -+ return; -+ if (ieh->pdev) { -+ pci_disable_device(ieh->pdev); -+ pci_dev_put(ieh->pdev); -+ } -+ kfree(ieh); -+} -+ -+static void __put_iehs(struct list_head *ieh_list) -+{ -+ struct ieh_dev *ieh, *tmp; -+ -+ edac_dbg(0, "\n"); -+ -+ list_for_each_entry_safe(ieh, tmp, ieh_list, list) { -+ list_del(&ieh->list); -+ __put_ieh(ieh); -+ } -+} -+ -+static void put_all_iehs(void) -+{ -+ __put_iehs(&global_ieh_list); -+ __put_iehs(&north_ieh_list); -+ __put_iehs(&south_ieh_list); -+} -+ -+static int __get_all_iehs(u16 did) -+{ -+ struct pci_dev *pdev, *prev = NULL; -+ int rc = -ENODEV, n = 0; -+ struct pci_sbdf *sbdf; -+ struct ieh_dev *ieh; -+ u32 reg; -+ -+ edac_dbg(0, "\n"); -+ -+ for (;;) { -+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, prev); -+ if (!pdev) -+ break; -+ -+ if (pci_enable_device(pdev)) { -+ ieh_printk(KERN_ERR, "Failed to enable %04x:%04x\n", -+ pdev->vendor, pdev->device); -+ goto fail; -+ } -+ -+ ieh = kzalloc(sizeof(*ieh), GFP_KERNEL); -+ if (!ieh) { -+ rc = -ENOMEM; -+ goto fail2; -+ } -+ -+ if (pci_read_config_dword(pdev, IEHTYPEVER_OFFSET, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read IEHTYPEVER\n"); -+ return -ENODEV; -+ } -+ -+ ieh->pdev = pdev; -+ ieh->ver = IEHTYPEVER_VER(reg); -+ ieh->type = IEHTYPEVER_TYPE(reg); -+ sbdf = &ieh->sbdf; -+ sbdf->seg = pci_domain_nr(pdev->bus); -+ sbdf->bus = IEHTYPEVER_BUS(reg); -+ sbdf->dev = PCI_SLOT(pdev->devfn); -+ sbdf->fun = PCI_FUNC(pdev->devfn); -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x IEHTYPEVER: 0x%x\n", -+ PCI_ADDR(sbdf), reg); -+ -+ if (sbdf->bus != pdev->bus->number) { -+ ieh_printk(KERN_ERR, "Mismatched IEH bus\n"); -+ rc = -EINVAL; -+ goto fail3; -+ } -+ -+ switch (ieh->type) { -+ case IEH_SUPERSET: -+ case IEH_GLOBAL: -+ /* Set notification to MCE */ -+ if (pci_read_config_dword(pdev, GSYSEVTMAP_OFFSET, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read old GSYSEVTMAP\n"); -+ return -ENODEV; -+ } -+ -+ reg |= GSYSEVTMAP_MCE; -+ if (pci_write_config_dword(pdev, GSYSEVTMAP_OFFSET, reg)) { -+ ieh_printk(KERN_ERR, "Failed to write GSYSEVTMAP\n"); -+ return -ENODEV; -+ } -+ -+ if (pci_read_config_dword(pdev, GSYSEVTMAP_OFFSET, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read new GSYSEVTMAP\n"); -+ return -ENODEV; -+ } -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GSYSEVTMAP: 0x%x\n", -+ PCI_ADDR(sbdf), reg); -+ -+ ieh->corr_map = GSYSEVTMAP_CORR(reg); -+ ieh->nonfatal_map = GSYSEVTMAP_NONFATAL(reg); -+ ieh->fatal_map = GSYSEVTMAP_FATAL(reg); -+ list_add_tail(&ieh->list, &global_ieh_list); -+ ieh_printk(KERN_DEBUG, "Global/Superset IEH %04x:%02x:%02x.%x\n", -+ PCI_ADDR(sbdf)); -+ break; -+ case IEH_NORTH: -+ list_add_tail(&ieh->list, &north_ieh_list); -+ ieh_printk(KERN_DEBUG, "North IEH %04x:%02x:%02x.%x\n", -+ PCI_ADDR(sbdf)); -+ break; -+ case IEH_SOUTH: -+ list_add_tail(&ieh->list, &south_ieh_list); -+ ieh_printk(KERN_DEBUG, "South IEH %04x:%02x:%02x.%x\n", -+ PCI_ADDR(sbdf)); -+ break; -+ } -+ -+ pci_dev_get(pdev); -+ prev = pdev; -+ n++; -+ } -+ -+ return n; -+fail3: -+ kfree(ieh); -+fail2: -+ pci_disable_device(pdev); -+fail: -+ pci_dev_put(pdev); -+ put_all_iehs(); -+ return rc; -+} -+ -+static int get_all_iehs(u16 did) -+{ -+ int rc; -+ -+ rc = __get_all_iehs(did); -+ if (rc < 0) -+ return rc; -+ -+ if (rc == 0) { -+ ieh_printk(KERN_DEBUG, "No IEHs found\n"); -+ return -ENODEV; -+ } -+ -+ if (list_empty(&global_ieh_list)) { -+ ieh_printk(KERN_ERR, "No global IEH found\n"); -+ put_all_iehs(); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static int register_err_handler(void) -+{ -+ bool os_visible = false; -+ int rc; -+ -+ if (has_notification_by(IEH_NMI)) { -+ init_irq_work(&ieh_irq_work, ieh_irq_work_cb); -+ rc = register_nmi_handler(NMI_SERR, ieh_nmi_handler, -+ 0, IEH_NMI_NAME); -+ if (rc) { -+ ieh_printk(KERN_ERR, "Can't register NMI handler\n"); -+ return rc; -+ } -+ -+ os_visible = true; -+ } -+ -+ if (has_notification_by(IEH_MCE)) { -+ mce_register_decode_chain(&ieh_mce_dec); -+ os_visible = true; -+ } -+ -+ if (!os_visible) { -+ ieh_printk(KERN_INFO, "No OS-visible IEH events\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static void unregister_err_handler(void) -+{ -+ if (has_notification_by(IEH_NMI)) { -+ unregister_nmi_handler(NMI_SERR, IEH_NMI_NAME); -+ irq_work_sync(&ieh_irq_work); -+ } -+ -+ if (has_notification_by(IEH_MCE)) -+ mce_unregister_decode_chain(&ieh_mce_dec); -+} -+ -+static int __init ieh_init(void) -+{ -+ const struct x86_cpu_id *id; -+ struct ieh_dev *ieh; -+ int rc; -+ -+ edac_dbg(2, "\n"); -+ -+ id = x86_match_cpu(ieh_cpuids); -+ if (!id) -+ return -ENODEV; -+ ieh_cfg = (struct ieh_config *)id->driver_data; -+ -+ rc = get_all_iehs(ieh_cfg->did); -+ if (rc) -+ return rc; -+ -+ rc = register_err_handler(); -+ if (rc) -+ goto fail; -+ -+ rc = unmask_all_err_events(); -+ if (rc) -+ goto fail2; -+ -+ ieh = list_first_entry(&global_ieh_list, struct ieh_dev, list); -+ ieh_printk(KERN_INFO, "hw v%d, drv %s\n", ieh->ver, IEH_REVISION); -+ -+ return 0; -+fail2: -+ unregister_err_handler(); -+fail: -+ put_all_iehs(); -+ return rc; -+} -+ -+static void __exit ieh_exit(void) -+{ -+ edac_dbg(2, "\n"); -+ mask_all_err_events(); -+ unregister_err_handler(); -+ put_all_iehs(); -+} -+ -+module_init(ieh_init); -+module_exit(ieh_exit); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Qiuxu Zhuo"); -+MODULE_DESCRIPTION("IEH Driver for Intel CPU using I/O IEH"); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi b/SPECS/kernel-rt/0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi new file mode 100644 index 000000000..3d920ecda --- /dev/null +++ b/SPECS/kernel-rt/0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi @@ -0,0 +1,297 @@ +From 72fdaf6f276eef1443e29b45b03a78b1fe48bce8 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 13:22:02 -0700 +Subject: [PATCH 10/44] KVM: VMX: Add support for saving and restoring FRED + MSRs + +Introduce support for handling FRED MSR access requests, enabling both +host and guest to read and write FRED MSRs, which is essential for VM +save/restore and live migration, and allows userspace tools such as QEMU +to access the relevant MSRs. + +Specially, intercept accesses to the FRED SSP0 MSR (IA32_PL0_SSP), which +remains accessible when FRED is enumerated even if CET is not. This +ensures the guest value is fully virtual and does not alter the hardware +FRED SSP0 MSR. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v7: +* Intercept accesses to FRED SSP0, i.e., IA32_PL0_SSP, which remains + accessible when FRED but !CET (Sean). + +Change in v6: +* Return KVM_MSR_RET_UNSUPPORTED instead of 1 when FRED is not available + (Chao Gao) +* Handle MSR_IA32_PL0_SSP when FRED is enumerated but CET not. + +Change in v5: +* Use the newly added guest MSR read/write helpers (Sean). +* Check the size of fred_msr_vmcs_fields[] using static_assert() (Sean). +* Rewrite setting FRED MSRs to make it much easier to read (Sean). +* Add TB from Xuelian Guo. + +Changes since v2: +* Add a helper to convert FRED MSR index to VMCS field encoding to + make the code more compact (Chao Gao). +* Get rid of the "host_initiated" check because userspace has to set + CPUID before MSRs (Chao Gao & Sean Christopherson). +* Address a few cleanup comments (Sean Christopherson). + +Changes since v1: +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). +* Fail host requested FRED MSRs access if KVM cannot virtualize FRED + (Chao Gao). +* Handle the case FRED MSRs are valid but KVM cannot virtualize FRED + (Chao Gao). +* Add sanity checks when writing to FRED MSRs. +--- + arch/x86/include/asm/kvm_host.h | 5 ++ + arch/x86/kvm/vmx/vmx.c | 45 +++++++++++++++++ + arch/x86/kvm/x86.c | 85 +++++++++++++++++++++++++++++++-- + 3 files changed, 132 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 48598d017d6f3..43a18e265289b 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -1092,6 +1092,11 @@ struct kvm_vcpu_arch { + #if IS_ENABLED(CONFIG_HYPERV) + hpa_t hv_root_tdp; + #endif ++ /* ++ * Stores the FRED SSP0 MSR when CET is not supported, prompting KVM ++ * to intercept its accesses. ++ */ ++ u64 fred_ssp0_fallback; + }; + + struct kvm_lpage_info { +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 3ac89ccf47210..b51f2c4fcdb8c 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1386,6 +1386,18 @@ static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) + vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data, + &vmx->msr_guest_kernel_gs_base); + } ++ ++static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) ++{ ++ return vmx_read_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, ++ &vmx->msr_guest_fred_rsp0); ++} ++ ++static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) ++{ ++ vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, ++ &vmx->msr_guest_fred_rsp0); ++} + #endif + + static void grow_ple_window(struct kvm_vcpu *vcpu) +@@ -1987,6 +1999,27 @@ int vmx_get_feature_msr(u32 msr, u64 *data) + } + } + ++#ifdef CONFIG_X86_64 ++static const u32 fred_msr_vmcs_fields[] = { ++ GUEST_IA32_FRED_RSP1, ++ GUEST_IA32_FRED_RSP2, ++ GUEST_IA32_FRED_RSP3, ++ GUEST_IA32_FRED_STKLVLS, ++ GUEST_IA32_FRED_SSP1, ++ GUEST_IA32_FRED_SSP2, ++ GUEST_IA32_FRED_SSP3, ++ GUEST_IA32_FRED_CONFIG, ++}; ++ ++static_assert(MSR_IA32_FRED_CONFIG - MSR_IA32_FRED_RSP1 == ++ ARRAY_SIZE(fred_msr_vmcs_fields) - 1); ++ ++static u32 fred_msr_to_vmcs(u32 msr) ++{ ++ return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1]; ++} ++#endif ++ + /* + * Reads an msr value (of 'msr_info->index') into 'msr_info->data'. + * Returns 0 on success, non-0 otherwise. +@@ -2009,6 +2042,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + case MSR_KERNEL_GS_BASE: + msr_info->data = vmx_read_guest_kernel_gs_base(vmx); + break; ++ case MSR_IA32_FRED_RSP0: ++ msr_info->data = vmx_read_guest_fred_rsp0(vmx); ++ break; ++ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: ++ msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index)); ++ break; + #endif + case MSR_EFER: + return kvm_get_msr_common(vcpu, msr_info); +@@ -2241,6 +2280,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + vmx_update_exception_bitmap(vcpu); + } + break; ++ case MSR_IA32_FRED_RSP0: ++ vmx_write_guest_fred_rsp0(vmx, data); ++ break; ++ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: ++ vmcs_write64(fred_msr_to_vmcs(msr_index), data); ++ break; + #endif + case MSR_IA32_SYSENTER_CS: + if (is_guest_mode(vcpu)) +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index c9c2aa6f4705e..af7543e7c8063 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -331,6 +331,9 @@ static const u32 msrs_to_save_base[] = { + MSR_STAR, + #ifdef CONFIG_X86_64 + MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR, ++ MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2, ++ MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1, ++ MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG, + #endif + MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA, + MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX, +@@ -1919,7 +1922,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, + * architecture. Intercepting XRSTORS/XSAVES for this + * special case isn't deemed worthwhile. + */ +- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: ++ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) + return KVM_MSR_RET_UNSUPPORTED; + /* +@@ -1934,6 +1937,52 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, + if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4)) + return 1; + break; ++ case MSR_IA32_FRED_STKLVLS: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: ++ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_CONFIG: { ++ u64 reserved_bits = 0; ++ ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ ++ if (is_noncanonical_msr_address(data, vcpu)) ++ return 1; ++ ++ switch (index) { ++ case MSR_IA32_FRED_CONFIG: ++ reserved_bits = BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2); ++ break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: ++ reserved_bits = GENMASK_ULL(5, 0); ++ break; ++ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_SSP3: ++ reserved_bits = GENMASK_ULL(2, 0); ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return 1; ++ } ++ ++ if (data & reserved_bits) ++ return 1; ++ ++ break; ++ } ++ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && ++ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ ++ if (is_noncanonical_msr_address(data, vcpu)) ++ return 1; ++ ++ if (!IS_ALIGNED(data, 4)) ++ return 1; ++ ++ break; + } + + msr.data = data; +@@ -1988,10 +2037,19 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, + if (!host_initiated) + return 1; + fallthrough; +- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: ++ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) + return KVM_MSR_RET_UNSUPPORTED; + break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ break; ++ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && ++ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ break; + } + + msr.index = index; +@@ -4315,6 +4373,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + #endif + case MSR_IA32_U_CET: + case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { ++ WARN_ON_ONCE(msr != MSR_IA32_FRED_SSP0); ++ vcpu->arch.fred_ssp0_fallback = data; ++ break; ++ } ++ + kvm_set_xstate_msr(vcpu, msr_info); + break; + default: +@@ -4668,6 +4732,12 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + #endif + case MSR_IA32_U_CET: + case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { ++ WARN_ON_ONCE(msr_info->index != MSR_IA32_FRED_SSP0); ++ msr_info->data = vcpu->arch.fred_ssp0_fallback; ++ break; ++ } ++ + kvm_get_xstate_msr(vcpu, msr_info); + break; + default: +@@ -7711,10 +7781,19 @@ static void kvm_probe_msr_to_save(u32 msr_index) + if (!kvm_cpu_cap_has(X86_FEATURE_LM)) + return; + fallthrough; +- case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: ++ case MSR_IA32_PL1_SSP ... MSR_IA32_PL3_SSP: + if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK)) + return; + break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: ++ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) ++ return; ++ break; ++ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ ++ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && ++ !kvm_cpu_cap_has(X86_FEATURE_FRED)) ++ return; ++ break; + default: + break; + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi b/SPECS/kernel-rt/0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi deleted file mode 100644 index 63e132654..000000000 --- a/SPECS/kernel-rt/0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi +++ /dev/null @@ -1,49 +0,0 @@ -From 4d54ce3cf01e889088b73b3c67f75ce5161061f9 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Wed, 7 Feb 2024 09:26:31 -0800 -Subject: [PATCH 10/44] KVM: x86: Add a helper to detect if FRED is enabled for - a vCPU - -Signed-off-by: Xin Li -[ Sean: removed the "kvm_" prefix from the function name ] -Signed-off-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/kvm_cache_regs.h | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - -diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h -index 36a8786db291..31b446b6cbd7 100644 ---- a/arch/x86/kvm/kvm_cache_regs.h -+++ b/arch/x86/kvm/kvm_cache_regs.h -@@ -204,6 +204,21 @@ static __always_inline bool kvm_is_cr4_bit_set(struct kvm_vcpu *vcpu, - return !!kvm_read_cr4_bits(vcpu, cr4_bit); - } - -+/* -+ * It's enough to check just CR4.FRED (X86_CR4_FRED) to tell if -+ * a vCPU is running with FRED enabled, because: -+ * 1) CR4.FRED can be set to 1 only _after_ IA32_EFER.LMA = 1. -+ * 2) To leave IA-32e mode, CR4.FRED must be cleared first. -+ */ -+static inline bool is_fred_enabled(struct kvm_vcpu *vcpu) -+{ -+#ifdef CONFIG_X86_64 -+ return kvm_is_cr4_bit_set(vcpu, X86_CR4_FRED); -+#else -+ return false; -+#endif -+} -+ - static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu) - { - if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet b/SPECS/kernel-rt/0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet deleted file mode 100644 index 74615669d..000000000 --- a/SPECS/kernel-rt/0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet +++ /dev/null @@ -1,139 +0,0 @@ -From 72c45e1f7d755c5b002da53053179f8931a5a599 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 4 Jul 2025 01:49:40 -0700 -Subject: [PATCH 10/24] KVM: x86: Load guest FPU state when access - XSAVE-managed MSRs - -Load the guest's FPU state if userspace is accessing MSRs whose values -are managed by XSAVES. Introduce two helpers, kvm_{get,set}_xstate_msr(), -to facilitate access to such kind of MSRs. - -If MSRs supported in kvm_caps.supported_xss are passed through to guest, -the guest MSRs are swapped with host's before vCPU exits to userspace and -after it reenters kernel before next VM-entry. - -Because the modified code is also used for the KVM_GET_MSRS device ioctl(), -explicitly check @vcpu is non-null before attempting to load guest state. -The XSAVE-managed MSRs cannot be retrieved via the device ioctl() without -loading guest FPU state (which doesn't exist). - -Note that guest_cpuid_has() is not queried as host userspace is allowed to -access MSRs that have not been exposed to the guest, e.g. it might do -KVM_SET_MSRS prior to KVM_SET_CPUID2. - -The two helpers are put here in order to manifest accessing xsave-managed -MSRs requires special check and handling to guarantee the correctness of -read/write to the MSRs. - -Signed-off-by: Sean Christopherson -Co-developed-by: Yang Weijiang -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 35 ++++++++++++++++++++++++++++++++++- - arch/x86/kvm/x86.h | 24 ++++++++++++++++++++++++ - 2 files changed, 58 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 46b45eeadf47..0b0468348ee0 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -136,6 +136,9 @@ static int __set_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2); - static void __get_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2); - - static DEFINE_MUTEX(vendor_module_lock); -+static void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); -+static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); -+ - struct kvm_x86_ops kvm_x86_ops __read_mostly; - - #define KVM_X86_OP(func) \ -@@ -4531,6 +4534,21 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - } - EXPORT_SYMBOL_GPL(kvm_get_msr_common); - -+/* -+ * Returns true if the MSR in question is managed via XSTATE, i.e. is context -+ * switched with the rest of guest FPU state. -+ */ -+static bool is_xstate_managed_msr(u32 index) -+{ -+ switch (index) { -+ case MSR_IA32_U_CET: -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ return true; -+ default: -+ return false; -+ } -+} -+ - /* - * Read or write a bunch of msrs. All parameters are kernel addresses. - * -@@ -4541,11 +4559,26 @@ static int __msr_io(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs, - int (*do_msr)(struct kvm_vcpu *vcpu, - unsigned index, u64 *data)) - { -+ bool fpu_loaded = false; - int i; - -- for (i = 0; i < msrs->nmsrs; ++i) -+ for (i = 0; i < msrs->nmsrs; ++i) { -+ /* -+ * If userspace is accessing one or more XSTATE-managed MSRs, -+ * temporarily load the guest's FPU state so that the guest's -+ * MSR value(s) is resident in hardware, i.e. so that KVM can -+ * get/set the MSR via RDMSR/WRMSR. -+ */ -+ if (vcpu && !fpu_loaded && kvm_caps.supported_xss && -+ is_xstate_managed_msr(entries[i].index)) { -+ kvm_load_guest_fpu(vcpu); -+ fpu_loaded = true; -+ } - if (do_msr(vcpu, entries[i].index, &entries[i].data)) - break; -+ } -+ if (fpu_loaded) -+ kvm_put_guest_fpu(vcpu); - - return i; - } -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index bd1149768acc..97e258a3e88e 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -701,4 +701,28 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, - - int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); - -+/* -+ * Lock and/or reload guest FPU and access xstate MSRs. For accesses initiated -+ * by host, guest FPU is loaded in __msr_io(). For accesses initiated by guest, -+ * guest FPU should have been loaded already. -+ */ -+ -+static inline void kvm_get_xstate_msr(struct kvm_vcpu *vcpu, -+ struct msr_data *msr_info) -+{ -+ KVM_BUG_ON(!vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm); -+ kvm_fpu_get(); -+ rdmsrl(msr_info->index, msr_info->data); -+ kvm_fpu_put(); -+} -+ -+static inline void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, -+ struct msr_data *msr_info) -+{ -+ KVM_BUG_ON(!vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm); -+ kvm_fpu_get(); -+ wrmsrl(msr_info->index, msr_info->data); -+ kvm_fpu_put(); -+} -+ - #endif --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl b/SPECS/kernel-rt/0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl new file mode 100644 index 000000000..cd7678e2c --- /dev/null +++ b/SPECS/kernel-rt/0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl @@ -0,0 +1,129 @@ +From 387b4220e1ad94cbc517a4180af34b3ac1b023a0 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Thu, 13 Nov 2025 17:56:27 +0100 +Subject: [PATCH 10/17] cpuidle: governors: teo: Rework the handling of tick + wakeups + +If the wakeup pattern is clearly dominated by tick wakeups, count those +wakeups as hits on the deepest available idle state to increase the +likelihood of stopping the tick, especially on systems where there are +only 2 usable idle states and the tick can only be stopped when the +deeper state is selected. + +This change is expected to reduce power on some systems where state 0 is +selected relatively often even though they are almost idle. Without it, +the governor may end up selecting the shallowest idle state all the time +even if the system is almost completely idle due all tick wakeups being +counted as hits on that state and preventing the tick from being stopped +at all. + +Fixes: 4b20b07ce72f ("cpuidle: teo: Don't count non-existent intercepts") +Reported-by: Reka Norman +Closes: https://lore.kernel.org/linux-pm/CAEmPcwsNMNnNXuxgvHTQ93Mx-q3Oz9U57THQsU_qdcCx1m4w5g@mail.gmail.com/ +Tested-by: Reka Norman +Tested-by: Christian Loehle +Cc: 6.11+ # 6.11+: 92ce5c07b7a1: cpuidle: teo: Reorder candidate state index checks +Cc: 6.11+ # 6.11+: ea185406d1ed: cpuidle: teo: Combine candidate state index checks against 0 +Cc: 6.11+ # 6.11+: b9a6af26bd83: cpuidle: teo: Drop local variable prev_intercept_idx +Cc: 6.11+ # 6.11+: e24f8a55de50: cpuidle: teo: Clarify two code comments +Cc: 6.11+ # 6.11+: d619b5cc6780: cpuidle: teo: Simplify counting events used for tick management +Cc: 6.11+ # 6.11+: 13ed5c4a6d9c: cpuidle: teo: Skip getting the sleep length if wakeups are very frequent +Cc: 6.11+ # 6.11+: ddcfa7964677: cpuidle: teo: Simplify handling of total events count +Cc: 6.11+ # 6.11+: 65e18e654475: cpuidle: teo: Replace time_span_ns with a flag +Cc: 6.11+ # 6.11+: 0796ddf4a7f0: cpuidle: teo: Use this_cpu_ptr() where possible +Cc: 6.11+ # 6.11+: 8f3f01082d7a: cpuidle: governors: teo: Use s64 consistently in teo_update() +Cc: 6.11+ # 6.11+: b54df61c7428: cpuidle: governors: teo: Decay metrics below DECAY_SHIFT threshold +Cc: 6.11+ # 6.11+ +Signed-off-by: Rafael J. Wysocki +[ rjw: Rebase on commit 0796ddf4a7f0, changelog update ] +Link: https://patch.msgid.link/6228387.lOV4Wx5bFT@rafael.j.wysocki +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/governors/teo.c | 39 ++++++++++++++++++++------------- + 1 file changed, 24 insertions(+), 15 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 8b80d73e518ed..94ba00b7617d7 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -133,17 +133,19 @@ struct teo_bin { + * @sleep_length_ns: Time till the closest timer event (at the selection time). + * @state_bins: Idle state data bins for this CPU. + * @total: Grand total of the "intercepts" and "hits" metrics for all bins. ++ * @total_tick: Wakeups by the scheduler tick. + * @tick_intercepts: "Intercepts" before TICK_NSEC. + * @short_idles: Wakeups after short idle periods. +- * @artificial_wakeup: Set if the wakeup has been triggered by a safety net. ++ * @tick_wakeup: Set if the last wakeup was by the scheduler tick. + */ + struct teo_cpu { + s64 sleep_length_ns; + struct teo_bin state_bins[CPUIDLE_STATE_MAX]; + unsigned int total; ++ unsigned int total_tick; + unsigned int tick_intercepts; + unsigned int short_idles; +- bool artificial_wakeup; ++ bool tick_wakeup; + }; + + static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); +@@ -172,9 +174,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + + teo_decay(&cpu_data->short_idles); + +- if (cpu_data->artificial_wakeup) { ++ if (dev->poll_time_limit) { ++ dev->poll_time_limit = false; + /* +- * If one of the safety nets has triggered, assume that this ++ * Polling state timeout has triggered, so assume that this + * might have been a long sleep. + */ + measured_ns = S64_MAX; +@@ -223,6 +226,21 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + cpu_data->total = total + PULSE; + + teo_decay(&cpu_data->tick_intercepts); ++ ++ teo_decay(&cpu_data->total_tick); ++ if (cpu_data->tick_wakeup) { ++ cpu_data->total_tick += PULSE; ++ /* ++ * If tick wakeups dominate the wakeup pattern, count this one ++ * as a hit on the deepest available idle state to increase the ++ * likelihood of stopping the tick. ++ */ ++ if (3 * cpu_data->total_tick > 2 * cpu_data->total) { ++ cpu_data->state_bins[drv->state_count-1].hits += PULSE; ++ return; ++ } ++ } ++ + /* + * If the measured idle duration falls into the same bin as the sleep + * length, this is a "hit", so update the "hits" metric for that bin. +@@ -512,18 +530,9 @@ static void teo_reflect(struct cpuidle_device *dev, int state) + { + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + ++ cpu_data->tick_wakeup = tick_nohz_idle_got_tick(); ++ + dev->last_state_idx = state; +- if (dev->poll_time_limit || +- (tick_nohz_idle_got_tick() && cpu_data->sleep_length_ns > TICK_NSEC)) { +- /* +- * The wakeup was not "genuine", but triggered by one of the +- * safety nets. +- */ +- dev->poll_time_limit = false; +- cpu_data->artificial_wakeup = true; +- } else { +- cpu_data->artificial_wakeup = false; +- } + } + + /** +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu b/SPECS/kernel-rt/0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu deleted file mode 100644 index 97f0c6255..000000000 --- a/SPECS/kernel-rt/0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu +++ /dev/null @@ -1,27 +0,0 @@ -From 02c24ef6b67f79e73b6db09af3e253738869b273 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Wed, 27 Aug 2025 11:10:06 +0800 -Subject: [PATCH 10/11] drivers: media: set v4l2_subdev_enable_streams_api=true - for WA - -Signed-off-by: hepengpx ---- - drivers/media/v4l2-core/v4l2-subdev.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c -index 4fd25fea3b58..60a05561f305 100644 ---- a/drivers/media/v4l2-core/v4l2-subdev.c -+++ b/drivers/media/v4l2-core/v4l2-subdev.c -@@ -32,7 +32,7 @@ - * 'v4l2_subdev_enable_streams_api' to 1 below. - */ - --static bool v4l2_subdev_enable_streams_api; -+static bool v4l2_subdev_enable_streams_api = true; - #endif - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss b/SPECS/kernel-rt/0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss deleted file mode 100644 index 66e5296a4..000000000 --- a/SPECS/kernel-rt/0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss +++ /dev/null @@ -1,69 +0,0 @@ -From 643efa49322625dad779c5b02473d1e4ccc11951 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 8 Aug 2025 16:05:50 +0300 -Subject: [PATCH 10/16] i2c: i801: Add support for Intel Wildcat Lake-U - -Add SMBus IDs on Intel Wildcat Lake-U. - -Signed-off-by: Jarkko Nikula ---- - Documentation/i2c/busses/i2c-i801.rst | 1 + - drivers/i2c/busses/Kconfig | 1 + - drivers/i2c/busses/i2c-i801.c | 3 +++ - 3 files changed, 5 insertions(+) - -diff --git a/Documentation/i2c/busses/i2c-i801.rst b/Documentation/i2c/busses/i2c-i801.rst -index 47e8ac5b7099..36c563ad3f06 100644 ---- a/Documentation/i2c/busses/i2c-i801.rst -+++ b/Documentation/i2c/busses/i2c-i801.rst -@@ -50,6 +50,7 @@ Supported adapters: - * Intel Birch Stream (SOC) - * Intel Arrow Lake (SOC) - * Intel Panther Lake (SOC) -+ * Intel Wildcat Lake (SOC) - - Datasheets: Publicly available at the Intel website - -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 070d014fdc5d..0c77b1d4c260 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -165,6 +165,7 @@ config I2C_I801 - Birch Stream (SOC) - Arrow Lake (SOC) - Panther Lake (SOC) -+ Wildcat Lake (SOC) - - This driver can also be built as a module. If so, the module - will be called i2c-i801. -diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c -index a7f89946dad4..1e54fed4cac1 100644 ---- a/drivers/i2c/busses/i2c-i801.c -+++ b/drivers/i2c/busses/i2c-i801.c -@@ -83,6 +83,7 @@ - * Arrow Lake-H (SOC) 0x7722 32 hard yes yes yes - * Panther Lake-H (SOC) 0xe322 32 hard yes yes yes - * Panther Lake-P (SOC) 0xe422 32 hard yes yes yes -+ * Wildcat Lake-U (SOC) 0x4d22 32 hard yes yes yes - * - * Features supported by this driver: - * Software PEC no -@@ -236,6 +237,7 @@ - #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 - #define PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS 0x43a3 - #define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23 -+#define PCI_DEVICE_ID_INTEL_WILDCAT_LAKE_U_SMBUS 0x4d22 - #define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3 - #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3 - #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3 -@@ -1056,6 +1058,7 @@ static const struct pci_device_id i801_ids[] = { - { PCI_DEVICE_DATA(INTEL, ARROW_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, - { PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, - { PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, -+ { PCI_DEVICE_DATA(INTEL, WILDCAT_LAKE_U_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, - { 0, } - }; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c b/SPECS/kernel-rt/0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c deleted file mode 100644 index db242a097..000000000 --- a/SPECS/kernel-rt/0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c +++ /dev/null @@ -1,445 +0,0 @@ -From e90cfbe370ad12807a9c16f0313ce6fec37ab8da Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:09 +0300 -Subject: [PATCH 10/11] i3c: mipi-i3c-hci: Convert remaining DBG() prints to - dev_dbg() - -Get rid of local DBG() macro and convert remaining debug prints to -dev_dbg() which can be controlled without code recompile when kernel is -built with dynamic debug support. - -Signed-off-by: Jarkko Nikula -Link: https://lore.kernel.org/r/20250827103009.243771-6-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 9 ++- - drivers/i3c/master/mipi-i3c-hci/cmd_v2.c | 7 ++- - drivers/i3c/master/mipi-i3c-hci/core.c | 16 ++--- - drivers/i3c/master/mipi-i3c-hci/dma.c | 15 +++-- - drivers/i3c/master/mipi-i3c-hci/ext_caps.c | 11 ++-- - drivers/i3c/master/mipi-i3c-hci/hci.h | 3 - - drivers/i3c/master/mipi-i3c-hci/pio.c | 68 +++++++++++++--------- - 7 files changed, 73 insertions(+), 56 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c -index dd636094b07f..eb8a3ae2990d 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c -+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c -@@ -317,7 +317,9 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) - break; - next_addr = ret; - -- DBG("next_addr = 0x%02x, DAA using DAT %d", next_addr, dat_idx); -+ dev_dbg(&hci->master.dev, -+ "next_addr = 0x%02x, DAA using DAT %d", -+ next_addr, dat_idx); - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx, next_addr); - mipi_i3c_hci_dct_index_reset(hci); - -@@ -349,8 +351,9 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) - } - - i3c_hci_dct_get_val(hci, 0, &pid, &dcr, &bcr); -- DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -- next_addr, pid, dcr, bcr); -+ dev_dbg(&hci->master.dev, -+ "assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -+ next_addr, pid, dcr, bcr); - - mipi_i3c_hci_dat_v1.free_entry(hci, dat_idx); - dat_idx = -1; -diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c -index 4493b2b067cb..efb4326a25b7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c -+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c -@@ -261,7 +261,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci) - if (ret < 0) - break; - next_addr = ret; -- DBG("next_addr = 0x%02x", next_addr); -+ dev_dbg(&hci->master.dev, "next_addr = 0x%02x", next_addr); - xfer[0].cmd_tid = hci_get_tid(); - xfer[0].cmd_desc[0] = - CMD_0_ATTR_A | -@@ -293,8 +293,9 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci) - pid = (pid << 32) | device_id[0]; - bcr = FIELD_GET(W1_MASK(55, 48), device_id[1]); - dcr = FIELD_GET(W1_MASK(63, 56), device_id[1]); -- DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -- next_addr, pid, dcr, bcr); -+ dev_dbg(&hci->master.dev, -+ "assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -+ next_addr, pid, dcr, bcr); - /* - * TODO: Extend the subsystem layer to allow for registering - * new device and provide BCR/DCR/PID at the same time. -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index 9932945ecf06..47e42cb4dbe7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -147,7 +147,7 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) - amd_set_resp_buf_thld(hci); - - reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE); -- DBG("HC_CONTROL = %#x", reg_read(HC_CONTROL)); -+ dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL)); - - return 0; - } -@@ -192,8 +192,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, - DECLARE_COMPLETION_ONSTACK(done); - int i, last, ret = 0; - -- DBG("cmd=%#x rnw=%d ndests=%d data[0].len=%d", -- ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len); -+ dev_dbg(&hci->master.dev, "cmd=%#x rnw=%d ndests=%d data[0].len=%d", -+ ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len); - - xfer = hci_alloc_xfer(nxfers); - if (!xfer) -@@ -251,8 +251,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, - } - - if (ccc->rnw) -- DBG("got: %*ph", -- ccc->dests[0].payload.len, ccc->dests[0].payload.data); -+ dev_dbg(&hci->master.dev, "got: %*ph", -+ ccc->dests[0].payload.len, ccc->dests[0].payload.data); - - out: - hci_free_xfer(xfer, nxfers); -@@ -277,7 +277,7 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - unsigned int size_limit; - int i, last, ret = 0; - -- DBG("nxfers = %d", nxfers); -+ dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); - - xfer = hci_alloc_xfer(nxfers); - if (!xfer) -@@ -335,7 +335,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - DECLARE_COMPLETION_ONSTACK(done); - int i, last, ret = 0; - -- DBG("nxfers = %d", nxfers); -+ dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); - - xfer = hci_alloc_xfer(nxfers); - if (!xfer) -@@ -587,7 +587,7 @@ static int i3c_hci_init(struct i3c_hci *hci) - } - - hci->caps = reg_read(HC_CAPABILITIES); -- DBG("caps = %#x", hci->caps); -+ dev_dbg(&hci->master.dev, "caps = %#x", hci->caps); - - size_in_dwords = hci->version_major < 1 || - (hci->version_major == 1 && hci->version_minor < 1); -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 3fadacbda582..c401a9425cdc 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -248,8 +248,9 @@ static int hci_dma_init(struct i3c_hci *hci) - regval = rh_reg_read(CR_SETUP); - rh->xfer_struct_sz = FIELD_GET(CR_XFER_STRUCT_SIZE, regval); - rh->resp_struct_sz = FIELD_GET(CR_RESP_STRUCT_SIZE, regval); -- DBG("xfer_struct_sz = %d, resp_struct_sz = %d", -- rh->xfer_struct_sz, rh->resp_struct_sz); -+ dev_dbg(&hci->master.dev, -+ "xfer_struct_sz = %d, resp_struct_sz = %d", -+ rh->xfer_struct_sz, rh->resp_struct_sz); - xfers_sz = rh->xfer_struct_sz * rh->xfer_entries; - resps_sz = rh->resp_struct_sz * rh->xfer_entries; - -@@ -523,11 +524,11 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) - ring_resp = rh->resp + rh->resp_struct_sz * done_ptr; - resp = *ring_resp; - tid = RESP_TID(resp); -- DBG("resp = 0x%08x", resp); -+ dev_dbg(&hci->master.dev, "resp = 0x%08x", resp); - - xfer = rh->src_xfers[done_ptr]; - if (!xfer) { -- DBG("orphaned ring entry"); -+ dev_dbg(&hci->master.dev, "orphaned ring entry"); - } else { - hci_dma_unmap_xfer(hci, xfer, 1); - xfer->ring_entry = -1; -@@ -630,7 +631,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - - ring_ibi_status = rh->ibi_status + rh->ibi_status_sz * ptr; - ibi_status = *ring_ibi_status; -- DBG("status = %#x", ibi_status); -+ dev_dbg(&hci->master.dev, "status = %#x", ibi_status); - - if (ibi_status_error) { - /* we no longer care */ -@@ -658,7 +659,9 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - - if (last_ptr == -1) { - /* this IBI sequence is not yet complete */ -- DBG("no LAST_STATUS available (e=%d d=%d)", enq_ptr, deq_ptr); -+ dev_dbg(&hci->master.dev, -+ "no LAST_STATUS available (e=%d d=%d)", -+ enq_ptr, deq_ptr); - return; - } - deq_ptr = last_ptr + 1; -diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c -index 2e9b23efdc45..7714f00ea9cc 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c -+++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c -@@ -35,7 +35,7 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base) - switch (hci->vendor_mipi_id) { - case MIPI_VENDOR_NXP: - hci->quirks |= HCI_QUIRK_RAW_CCC; -- DBG("raw CCC quirks set"); -+ dev_dbg(&hci->master.dev, "raw CCC quirks set"); - break; - } - -@@ -77,7 +77,8 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base) - for (index = 0; index < entries; index++) { - u32 mode_entry = readl(base); - -- DBG("mode %d: 0x%08x", index, mode_entry); -+ dev_dbg(&hci->master.dev, "mode %d: 0x%08x", -+ index, mode_entry); - /* TODO: will be needed when I3C core does more than SDR */ - base += 4; - } -@@ -97,7 +98,8 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base) - dev_info(&hci->master.dev, "available data rates:\n"); - for (index = 0; index < entries; index++) { - rate_entry = readl(base); -- DBG("entry %d: 0x%08x", index, rate_entry); -+ dev_dbg(&hci->master.dev, "entry %d: 0x%08x", -+ index, rate_entry); - rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry); - rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry); - mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry); -@@ -268,7 +270,8 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci) - cap_header = readl(curr_cap); - cap_id = FIELD_GET(CAP_HEADER_ID, cap_header); - cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header); -- DBG("id=0x%02x length=%d", cap_id, cap_length); -+ dev_dbg(&hci->master.dev, "id=0x%02x length=%d", -+ cap_id, cap_length); - if (!cap_length) - break; - if (curr_cap + cap_length * 4 >= end) { -diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h -index 33bc4906df1f..249ccb13c909 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/hci.h -+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h -@@ -12,9 +12,6 @@ - - #include - --/* Handy logging macro to save on line length */ --#define DBG(x, ...) pr_devel("%s: " x "\n", __func__, ##__VA_ARGS__) -- - /* 32-bit word aware bit and mask macros */ - #define W0_MASK(h, l) GENMASK((h) - 0, (l) - 0) - #define W1_MASK(h, l) GENMASK((h) - 32, (l) - 32) -diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c -index cde883137bc7..710faa46a00f 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/pio.c -+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c -@@ -213,8 +213,8 @@ static void hci_pio_cleanup(struct i3c_hci *hci) - pio_reg_write(INTR_SIGNAL_ENABLE, 0x0); - - if (pio) { -- DBG("status = %#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "status = %#x/%#x", -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); - BUG_ON(pio->curr_xfer); - BUG_ON(pio->curr_rx); - BUG_ON(pio->curr_tx); -@@ -226,13 +226,17 @@ static void hci_pio_cleanup(struct i3c_hci *hci) - - static void hci_pio_write_cmd(struct i3c_hci *hci, struct hci_xfer *xfer) - { -- DBG("cmd_desc[%d] = 0x%08x", 0, xfer->cmd_desc[0]); -- DBG("cmd_desc[%d] = 0x%08x", 1, xfer->cmd_desc[1]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 0, xfer->cmd_desc[0]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 1, xfer->cmd_desc[1]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[0]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[1]); - if (hci->cmd == &mipi_i3c_hci_cmd_v2) { -- DBG("cmd_desc[%d] = 0x%08x", 2, xfer->cmd_desc[2]); -- DBG("cmd_desc[%d] = 0x%08x", 3, xfer->cmd_desc[3]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 2, xfer->cmd_desc[2]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 3, xfer->cmd_desc[3]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[2]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[3]); - } -@@ -254,7 +258,8 @@ static bool hci_pio_do_rx(struct i3c_hci *hci, struct hci_pio_data *pio) - nr_words = min(xfer->data_left / 4, pio->rx_thresh_size); - /* extract data from FIFO */ - xfer->data_left -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, xfer->data_left); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, xfer->data_left); - while (nr_words--) - *p++ = pio_reg_read(XFER_DATA_PORT); - } -@@ -269,7 +274,7 @@ static void hci_pio_do_trailing_rx(struct i3c_hci *hci, - struct hci_xfer *xfer = pio->curr_rx; - u32 *p; - -- DBG("%d remaining", count); -+ dev_dbg(&hci->master.dev, "%d remaining", count); - - p = xfer->data; - p += (xfer->data_len - xfer->data_left) / 4; -@@ -278,7 +283,8 @@ static void hci_pio_do_trailing_rx(struct i3c_hci *hci, - unsigned int nr_words = count / 4; - /* extract data from FIFO */ - xfer->data_left -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, xfer->data_left); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, xfer->data_left); - while (nr_words--) - *p++ = pio_reg_read(XFER_DATA_PORT); - } -@@ -321,7 +327,8 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio) - nr_words = min(xfer->data_left / 4, pio->tx_thresh_size); - /* push data into the FIFO */ - xfer->data_left -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, xfer->data_left); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, xfer->data_left); - while (nr_words--) - pio_reg_write(XFER_DATA_PORT, *p++); - } -@@ -336,7 +343,7 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio) - */ - if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD)) - return false; -- DBG("trailing %d", xfer->data_left); -+ dev_dbg(&hci->master.dev, "trailing %d", xfer->data_left); - pio_reg_write(XFER_DATA_PORT, *p); - xfer->data_left = 0; - } -@@ -481,7 +488,7 @@ static bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio) - u32 resp = pio_reg_read(RESPONSE_QUEUE_PORT); - unsigned int tid = RESP_TID(resp); - -- DBG("resp = 0x%08x", resp); -+ dev_dbg(&hci->master.dev, "resp = 0x%08x", resp); - if (tid != xfer->cmd_tid) { - dev_err(&hci->master.dev, - "response tid=%d when expecting %d\n", -@@ -522,14 +529,15 @@ static bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio) - * still exists. - */ - if (pio->curr_rx == xfer) { -- DBG("short RX ?"); -+ dev_dbg(&hci->master.dev, "short RX ?"); - pio->curr_rx = pio->curr_rx->next_data; - } else if (pio->curr_tx == xfer) { -- DBG("short TX ?"); -+ dev_dbg(&hci->master.dev, "short TX ?"); - pio->curr_tx = pio->curr_tx->next_data; - } else if (xfer->data_left) { -- DBG("PIO xfer count = %d after response", -- xfer->data_left); -+ dev_dbg(&hci->master.dev, -+ "PIO xfer count = %d after response", -+ xfer->data_left); - } - - pio->curr_resp = xfer->next_resp; -@@ -591,7 +599,7 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n) - struct hci_xfer *prev_queue_tail; - int i; - -- DBG("n = %d", n); -+ dev_dbg(&hci->master.dev, "n = %d", n); - - /* link xfer instances together and initialize data count */ - for (i = 0; i < n; i++) { -@@ -611,8 +619,9 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n) - if (!hci_pio_process_cmd(hci, pio)) - pio->enabled_irqs |= STAT_CMD_QUEUE_READY; - pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs); -- DBG("status = %#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "status = %#x/%#x", -+ pio_reg_read(INTR_STATUS), -+ pio_reg_read(INTR_SIGNAL_ENABLE)); - } - spin_unlock_irq(&pio->lock); - return 0; -@@ -686,10 +695,10 @@ static bool hci_pio_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int - int ret; - - spin_lock_irq(&pio->lock); -- DBG("n=%d status=%#x/%#x", n, -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -- DBG("main_status = %#x/%#x", -- readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28)); -+ dev_dbg(&hci->master.dev, "n=%d status=%#x/%#x", n, -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "main_status = %#x/%#x", -+ readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28)); - - ret = hci_pio_dequeue_xfer_common(hci, pio, xfer, n); - spin_unlock_irq(&pio->lock); -@@ -733,8 +742,8 @@ static void hci_pio_err(struct i3c_hci *hci, struct hci_pio_data *pio, - mipi_i3c_hci_pio_reset(hci); - mipi_i3c_hci_resume(hci); - -- DBG("status=%#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "status=%#x/%#x", -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); - } - - static void hci_pio_set_ibi_thresh(struct i3c_hci *hci, -@@ -749,7 +758,7 @@ static void hci_pio_set_ibi_thresh(struct i3c_hci *hci, - if (regval != pio->reg_queue_thresh) { - pio_reg_write(QUEUE_THLD_CTRL, regval); - pio->reg_queue_thresh = regval; -- DBG("%d", thresh_val); -+ dev_dbg(&hci->master.dev, "%d", thresh_val); - } - } - -@@ -773,7 +782,8 @@ static bool hci_pio_get_ibi_segment(struct i3c_hci *hci, - /* extract the data from the IBI port */ - nr_words = thresh_val; - ibi->seg_cnt -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, ibi->seg_cnt); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, ibi->seg_cnt); - while (nr_words--) - *p++ = pio_reg_read(IBI_PORT); - } -@@ -791,7 +801,7 @@ static bool hci_pio_get_ibi_segment(struct i3c_hci *hci, - hci_pio_set_ibi_thresh(hci, pio, 1); - if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD)) - return false; -- DBG("trailing %d", ibi->seg_cnt); -+ dev_dbg(&hci->master.dev, "trailing %d", ibi->seg_cnt); - data = pio_reg_read(IBI_PORT); - data = (__force u32) cpu_to_le32(data); - while (ibi->seg_cnt--) { -@@ -820,7 +830,7 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio) - */ - - ibi_status = pio_reg_read(IBI_PORT); -- DBG("status = %#x", ibi_status); -+ dev_dbg(&hci->master.dev, "status = %#x", ibi_status); - ibi->addr = FIELD_GET(IBI_TARGET_ADDR, ibi_status); - if (ibi_status & IBI_ERROR) { - dev_err(&hci->master.dev, "IBI error from %#x\n", ibi->addr); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel-rt/0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet deleted file mode 100644 index e85563962..000000000 --- a/SPECS/kernel-rt/0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet +++ /dev/null @@ -1,43 +0,0 @@ -From c2697c3fa88d42d534846cfe97afc44ef236f4a8 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Wed, 9 Jun 2021 01:56:30 +0800 -Subject: [PATCH 10/19] igc: Enable HW RX Timestamp for AF_XDP ZC - -Enable the RX HW Timestamp using meta data to userspace. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc_main.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index b581e96d0735..8df729810af5 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -2815,6 +2815,7 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) - u16 cleaned_count = igc_desc_unused(ring); - int total_bytes = 0, total_packets = 0; - u16 ntc = ring->next_to_clean; -+ struct igc_md_desc *md; - struct bpf_prog *prog; - bool failure = false; - int xdp_status = 0; -@@ -2863,6 +2864,14 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) - bi->xdp->data_end = bi->xdp->data + size; - xsk_buff_dma_sync_for_cpu(bi->xdp); - -+ if (adapter->btf_enabled) { -+ md = bi->xdp->data - sizeof(*md); -+ md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); -+ bi->xdp->data_meta = md; -+ } else { -+ xdp_set_data_meta_invalid(bi->xdp); -+ } -+ - res = __igc_xdp_run_prog(adapter, prog, bi->xdp); - switch (res) { - case IGC_XDP_PASS: --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet b/SPECS/kernel-rt/0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet new file mode 100644 index 000000000..c5812252d --- /dev/null +++ b/SPECS/kernel-rt/0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet @@ -0,0 +1,34 @@ +From af7b03b8fad0f872c89d775d77545d4fe9ab61f4 Mon Sep 17 00:00:00 2001 +From: Aravindhan Gunasekaran +Date: Tue, 3 Aug 2021 17:32:26 +0000 +Subject: [PATCH 10/14] igc: Take care of DMA timestamp rollover + +This patch is to fix the spike in driver Tx Path when measuring between +two timestamp of TX HW Timestamp during profiling stage. + +Rollover is identified by checking the 32-bit SYSTIM_L(say, present-time) +value which should be greater that LS 32bits from DMA WB(time in past). + +Signed-off-by: Aravindhan Gunasekaran +Signed-off-by: Muhammad Husaini Zulkifli +--- + drivers/net/ethernet/intel/igc/igc_ptp.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c +index c1af5edae7aae..ad786c816144f 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ptp.c ++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c +@@ -459,6 +459,9 @@ static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, + nsec = rd32(IGC_SYSTIML); + sec = rd32(IGC_SYSTIMH); + ++ if (unlikely(nsec < (systim & 0xFFFFFFFF))) ++ --sec; ++ + switch (adapter->hw.mac.type) { + case igc_i225: + memset(hwtstamps, 0, sizeof(*hwtstamps)); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet b/SPECS/kernel-rt/0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet new file mode 100644 index 000000000..f5042a29b --- /dev/null +++ b/SPECS/kernel-rt/0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet @@ -0,0 +1,121 @@ +From 43595d6f6842ca5bcb642a947af5cc5f48fc9e98 Mon Sep 17 00:00:00 2001 +From: Gan Yi Fang +Date: Mon, 24 Jul 2023 03:10:06 -0400 +Subject: [PATCH 10/18] net: phy: reconfigure PHY WoL when WoL option is + enabled + +This patch reconfigures the PHY WoL event from two scenarios. +They are needed for the PHY that operated in PHY_POLL mode +where there is no ISR available to handle the WoL event. + +1. PHY WoL status will only be clear after hard reboot. PHY WoL +reconfiguration is needed in init_phy for the WoL to set properly +after soft reboot. +2. After the PHY enables the WoL event, arm the WoL INT bit +before suspending to ensure the WoL is set properly. + +Below are additional background. +In order for the WoL to work, the interrupt header needs to be +connected to the PMC for the platform to wake up when there is a +rising edge from the WoL INT bit. + +Ideal case to trigger the WoL: +1. User enables WoL (WoL INT bit: 0) +2. Sleep the platform +3. Platform receives magic packet (WoL INT bit: change from 0 to 1) +4. Platform wakes up + +Issue might occur when: +1. User enables WoL (WoL INT bit: 0) +2. Platform receives 1st magic packet (WoL INT bit: change from 0 to 1) +3. Without handling the INT, sleeps the platform (WoL INT bit: 1) +4. Platform receives second magic packet (There is no change in +WoL INT bit) +5. Platform cannot wake up + +Check and set the WoL INT bit to 0 will reset the PHY interrupt +before suspending so the issue can be avoided. + +Signed-off-by: Gan Yi Fang +--- + .../net/ethernet/stmicro/stmmac/dwmac-intel.c | 5 ++++ + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 25 ++++++++++++++++++- + 3 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index ca16568374d5a..932432846800d 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -1239,8 +1239,13 @@ static int stmmac_config_multi_msi(struct pci_dev *pdev, + static int intel_eth_pci_suspend(struct device *dev, void *bsp_priv) + { + struct pci_dev *pdev = to_pci_dev(dev); ++ struct net_device *ndev = dev_get_drvdata(&pdev->dev); + int ret; + ++ rtnl_lock(); ++ stmmac_rearm_wol(ndev); ++ rtnl_unlock(); ++ + ret = pci_save_state(pdev); + if (ret) + return ret; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index 7ca5477be390b..56acc67bd6044 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -396,6 +396,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv); + void stmmac_ptp_unregister(struct stmmac_priv *priv); + int stmmac_xdp_open(struct net_device *dev); + void stmmac_xdp_release(struct net_device *dev); ++void stmmac_rearm_wol(struct net_device *dev); + int stmmac_resume(struct device *dev); + int stmmac_suspend(struct device *dev); + void stmmac_dvr_remove(struct device *dev); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index f4b06854653fd..361b29c2c9854 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -4096,6 +4096,29 @@ static int stmmac_release(struct net_device *dev) + return 0; + } + ++void stmmac_rearm_wol(struct net_device *dev) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ ++ if (priv->plat->flags & STMMAC_FLAG_USE_PHY_WOL) { ++ struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; ++ ++ phylink_ethtool_get_wol(priv->phylink, &wol); ++ ++ if (wol.wolopts) { ++ phylink_ethtool_set_wol(priv->phylink, &wol); ++ device_set_wakeup_enable(priv->device, !!wol.wolopts); ++ } ++ } ++} ++EXPORT_SYMBOL_GPL(stmmac_rearm_wol); ++ ++static int stmmac_stop(struct net_device *dev) ++{ ++ stmmac_rearm_wol(dev); ++ return stmmac_release(dev); ++} ++ + static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb, + struct stmmac_tx_queue *tx_q) + { +@@ -7099,7 +7122,7 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 + static const struct net_device_ops stmmac_netdev_ops = { + .ndo_open = stmmac_open, + .ndo_start_xmit = stmmac_xmit, +- .ndo_stop = stmmac_release, ++ .ndo_stop = stmmac_stop, + .ndo_change_mtu = stmmac_change_mtu, + .ndo_fix_features = stmmac_fix_features, + .ndo_set_features = stmmac_set_features, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0010-patch-staging-add-IPU8_PCI_ID-support.ipu b/SPECS/kernel-rt/0010-patch-staging-add-IPU8_PCI_ID-support.ipu deleted file mode 100644 index 2096b513e..000000000 --- a/SPECS/kernel-rt/0010-patch-staging-add-IPU8_PCI_ID-support.ipu +++ /dev/null @@ -1,26 +0,0 @@ -From 9d9778e7b94ab66297f5d87aa7028520654c39be Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 15:01:40 +0800 -Subject: [PATCH 10/21] patch: staging add IPU8_PCI_ID support - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 3a6c129015e7..8448c5e2161b 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -2818,6 +2818,7 @@ static const struct dev_pm_ops ipu7_pm_ops = { - static const struct pci_device_id ipu7_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, - {0,} - }; - MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf b/SPECS/kernel-rt/0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf deleted file mode 100644 index 6a08d5e83..000000000 --- a/SPECS/kernel-rt/0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf +++ /dev/null @@ -1,29 +0,0 @@ -From aeb93d23991eff302911cc7331b7acb8d1d734b1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 26 Jan 2024 13:47:26 +0800 -Subject: [PATCH 010/100] perf/x86/intel: Fix typo in comments of - intel_put_event_constraints() - -As title said. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 465f740cc187..0b57a9146b82 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3922,7 +3922,7 @@ static void intel_put_event_constraints(struct cpu_hw_events *cpuc, - intel_put_shared_regs_event_constraints(cpuc, event); - - /* -- * is PMU has exclusive counter restrictions, then -+ * If PMU has exclusive counter restrictions, then - * all events are subject to and must call the - * put_excl_constraints() routine - */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf b/SPECS/kernel-rt/0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf new file mode 100644 index 000000000..ef141e859 --- /dev/null +++ b/SPECS/kernel-rt/0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf @@ -0,0 +1,329 @@ +From 2eceb42aa55ab2f8fe6302a18e673a5fb91fdc5b Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 24 Dec 2025 13:30:40 -0800 +Subject: [PATCH 10/13] perf/x86/intel/uncore: Support uncore constraint ranges + +Add UNCORE_EVENT_CONSTRAINT_RANGE macro for uncore constraints, +similar to INTEL_EVENT_CONSTRAINT_RANGE, to reduce duplication when +defining consecutive uncore event constraints. + +No functional change intended. + +Suggested-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 2 +- + arch/x86/events/intel/uncore.h | 2 + + arch/x86/events/intel/uncore_snbep.c | 183 ++++++--------------------- + 3 files changed, 44 insertions(+), 143 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index 34d0822dc0022..e47483d1207d4 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -436,7 +436,7 @@ uncore_get_event_constraint(struct intel_uncore_box *box, struct perf_event *eve + + if (type->constraints) { + for_each_event_constraint(c, type->constraints) { +- if ((event->hw.config & c->cmask) == c->code) ++ if (constraint_match(c, event->hw.config)) + return c; + } + } +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 55e3aebf4b5e0..564cb26c44680 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -33,6 +33,8 @@ + #define UNCORE_EXTRA_PCI_DEV_MAX 4 + + #define UNCORE_EVENT_CONSTRAINT(c, n) EVENT_CONSTRAINT(c, n, 0xff) ++#define UNCORE_EVENT_CONSTRAINT_RANGE(c, e, n) \ ++ EVENT_CONSTRAINT_RANGE(c, e, n, 0xff) + + #define UNCORE_IGNORE_END -1 + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index 28bcccf5cdfe9..fac2be780276d 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -836,76 +836,37 @@ static struct intel_uncore_ops snbep_uncore_pci_ops = { + static struct event_constraint snbep_uncore_cbox_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x1), + UNCORE_EVENT_CONSTRAINT(0x02, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x04, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x05, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x04, 0x5, 0x3), + UNCORE_EVENT_CONSTRAINT(0x07, 0x3), + UNCORE_EVENT_CONSTRAINT(0x09, 0x3), + UNCORE_EVENT_CONSTRAINT(0x11, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x12, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x13, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x1b, 0xc), +- UNCORE_EVENT_CONSTRAINT(0x1c, 0xc), +- UNCORE_EVENT_CONSTRAINT(0x1d, 0xc), +- UNCORE_EVENT_CONSTRAINT(0x1e, 0xc), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x12, 0x13, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1b, 0x1e, 0xc), + UNCORE_EVENT_CONSTRAINT(0x1f, 0xe), + UNCORE_EVENT_CONSTRAINT(0x21, 0x3), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x31, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x35, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x31, 0x35, 0x3), + UNCORE_EVENT_CONSTRAINT(0x36, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x37, 0x39, 0x3), + UNCORE_EVENT_CONSTRAINT(0x3b, 0x1), + EVENT_CONSTRAINT_END + }; + + static struct event_constraint snbep_uncore_r2pcie_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x12, 0x1), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x24, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x24, 0x26, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x32, 0x34, 0x3), + EVENT_CONSTRAINT_END + }; + + static struct event_constraint snbep_uncore_r3qpi_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x12, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x12, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x20, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x21, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x22, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x24, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2a, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2b, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x30, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x31, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x36, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x20, 0x26, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x36, 0x39, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3034,24 +2995,15 @@ static struct intel_uncore_type hswep_uncore_qpi = { + }; + + static struct event_constraint hswep_uncore_r2pcie_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x24, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x23, 0x25, 0x1), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x27, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x29, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2a, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x2b, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x35, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2b, 0x2d, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x32, 0x35, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3066,38 +3018,17 @@ static struct intel_uncore_type hswep_uncore_r2pcie = { + + static struct event_constraint hswep_uncore_r3qpi_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x07, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x08, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x09, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x0a, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x7, 0x0a, 0x7), + UNCORE_EVENT_CONSTRAINT(0x0e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x12, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x12, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x14, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x15, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x1f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x20, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x21, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x22, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x31, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x36, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x14, 0x15, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1f, 0x23, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x25, 0x26, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x29, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2c, 0x2f, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x31, 0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x36, 0x39, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3371,8 +3302,7 @@ static struct event_constraint bdx_uncore_r2pcie_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x25, 0x1), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2c, 0x2d, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3387,35 +3317,18 @@ static struct intel_uncore_type bdx_uncore_r2pcie = { + + static struct event_constraint bdx_uncore_r3qpi_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x07, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x08, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x09, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x0a, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x07, 0x0a, 0x7), + UNCORE_EVENT_CONSTRAINT(0x0e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x14, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x15, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x1f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x20, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x21, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x22, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x14, 0x15, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1f, 0x23, 0x3), + UNCORE_EVENT_CONSTRAINT(0x25, 0x3), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x36, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x29, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2c, 0x2f, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x33, 0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x36, 0x39, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3722,8 +3635,7 @@ static struct event_constraint skx_uncore_iio_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x95, 0xc), + UNCORE_EVENT_CONSTRAINT(0xc0, 0xc), + UNCORE_EVENT_CONSTRAINT(0xc5, 0xc), +- UNCORE_EVENT_CONSTRAINT(0xd4, 0xc), +- UNCORE_EVENT_CONSTRAINT(0xd5, 0xc), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0xd4, 0xd5, 0xc), + EVENT_CONSTRAINT_END + }; + +@@ -4479,14 +4391,9 @@ static struct intel_uncore_type skx_uncore_m2pcie = { + }; + + static struct event_constraint skx_uncore_m3upi_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x1d, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1e, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1d, 0x1e, 0x1), + UNCORE_EVENT_CONSTRAINT(0x40, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4f, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x50, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x51, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x52, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x4e, 0x52, 0x7), + EVENT_CONSTRAINT_END + }; + +@@ -5652,14 +5559,9 @@ static struct intel_uncore_type icx_uncore_upi = { + }; + + static struct event_constraint icx_uncore_m3upi_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x1c, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1d, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1e, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1f, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1c, 0x1f, 0x1), + UNCORE_EVENT_CONSTRAINT(0x40, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4f, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x50, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x4e, 0x50, 0x7), + EVENT_CONSTRAINT_END + }; + +@@ -6142,10 +6044,7 @@ static struct intel_uncore_ops spr_uncore_mmio_offs8_ops = { + static struct event_constraint spr_uncore_cxlcm_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x02, 0x0f), + UNCORE_EVENT_CONSTRAINT(0x05, 0x0f), +- UNCORE_EVENT_CONSTRAINT(0x40, 0xf0), +- UNCORE_EVENT_CONSTRAINT(0x41, 0xf0), +- UNCORE_EVENT_CONSTRAINT(0x42, 0xf0), +- UNCORE_EVENT_CONSTRAINT(0x43, 0xf0), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x40, 0x43, 0xf0), + UNCORE_EVENT_CONSTRAINT(0x4b, 0xf0), + UNCORE_EVENT_CONSTRAINT(0x52, 0xf0), + EVENT_CONSTRAINT_END +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal b/SPECS/kernel-rt/0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal deleted file mode 100644 index 0b56f019f..000000000 --- a/SPECS/kernel-rt/0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal +++ /dev/null @@ -1,48 +0,0 @@ -From fa14de5297eb00d640f1b87ae2522142a525a60e Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Tue, 23 Sep 2025 13:56:31 -0700 -Subject: [PATCH 10/11] thermal: intel: int340x: Power Slider: Validate - slider_balance range - -When the module parameter slider_balance is set to the performance -slider value of 0, the SoC slider profile switches to the performance -mode. - -This can cause the Linux power-profiles-daemon to change the system -power mode to performance from balanced mode. This happens when there -is only one platform profile registered as there will be no conflict -with other platform profiles. - -Same issue occurs when the slider_balance is set to the power-saver -slider value. - -Prevent module parameter slider_balance from overlapping with -performance and power-saver slider values by adding range validation. - -Return an error when an invalid value is provided. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250923205631.3056590-1-srinivas.pandruvada@linux.intel.com -[ rjw: Changelog edits ] -Signed-off-by: Rafael J. Wysocki ---- - .../intel/int340x_thermal/processor_thermal_soc_slider.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -index 20d70cb01542..49ff3bae7271 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -67,7 +67,8 @@ static int slider_def_balance_set(const char *arg, const struct kernel_param *kp - - ret = kstrtou8(arg, 16, &slider_val); - if (!ret) { -- if (slider_val > SOC_SLIDER_VALUE_MAXIMUM) -+ if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] || -+ slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE]) - return -EINVAL; - - slider_balanced_param = slider_val; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo b/SPECS/kernel-rt/0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo new file mode 100644 index 000000000..7355353b6 --- /dev/null +++ b/SPECS/kernel-rt/0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo @@ -0,0 +1,138 @@ +From 968520318c7c179db8729828b9d934b1d1dd7cfa Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Sat, 29 Nov 2025 22:57:50 -0500 +Subject: [PATCH 10/21] tools/power turbostat: Add run-time MSR driver probe + +Rather than starting down the conditional-compile road... + +Probe the location of the MSR files at run-time. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 68 +++++++++++++++------------ + 1 file changed, 39 insertions(+), 29 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 8154d110dd07d..e85bdb00f24a5 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -142,6 +142,7 @@ struct msr_counter { + #define FLAGS_SHOW (1 << 1) + #define SYSFS_PERCPU (1 << 1) + }; ++static int use_android_msr_path; + + struct msr_counter bic[] = { + { 0x0, "usec", NULL, 0, 0, 0, NULL, 0 }, +@@ -2413,20 +2414,11 @@ int get_msr_fd(int cpu) + + if (fd) + return fd; +-#if defined(ANDROID) +- sprintf(pathname, "/dev/msr%d", cpu); +-#else +- sprintf(pathname, "/dev/cpu/%d/msr", cpu); +-#endif ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDONLY); + if (fd < 0) +-#if defined(ANDROID) +- err(-1, "%s open failed, try chown or chmod +r /dev/msr*, " +- "or run with --no-msr, or run as root", pathname); +-#else +- err(-1, "%s open failed, try chown or chmod +r /dev/cpu/*/msr, " +- "or run with --no-msr, or run as root", pathname); +-#endif ++ err(-1, "%s open failed, try chown or chmod +r %s, " ++ "or run with --no-msr, or run as root", pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); + fd_percpu[cpu] = fd; + + return fd; +@@ -6777,21 +6769,43 @@ void turbostat_loop() + } + } + +-void check_dev_msr() ++int probe_dev_msr(void) ++{ ++ struct stat sb; ++ char pathname[32]; ++ ++ sprintf(pathname, "/dev/msr%d", base_cpu); ++ return !stat(pathname, &sb); ++} ++ ++int probe_dev_cpu_msr(void) + { + struct stat sb; + char pathname[32]; + +- if (no_msr) +- return; +-#if defined(ANDROID) +- sprintf(pathname, "/dev/msr%d", base_cpu); +-#else + sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); +-#endif +- if (stat(pathname, &sb)) +- if (system("/sbin/modprobe msr > /dev/null 2>&1")) +- no_msr = 1; ++ return !stat(pathname, &sb); ++} ++ ++int probe_msr_driver(void) ++{ ++ if (probe_dev_msr()) { ++ use_android_msr_path = 1; ++ return 1; ++ } ++ return probe_dev_cpu_msr(); ++} ++ ++void check_msr_driver(void) ++{ ++ if (probe_msr_driver()) ++ return; ++ ++ if (system("/sbin/modprobe msr > /dev/null 2>&1")) ++ no_msr = 1; ++ ++ if (!probe_msr_driver()) ++ no_msr = 1; + } + + /* +@@ -6846,11 +6860,7 @@ void check_msr_permission(void) + failed += check_for_cap_sys_rawio(); + + /* test file permissions */ +-#if defined(ANDROID) +- sprintf(pathname, "/dev/msr%d", base_cpu); +-#else +- sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); +-#endif ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu); + if (euidaccess(pathname, R_OK)) { + failed++; + } +@@ -9476,7 +9486,7 @@ bool has_added_counters(void) + + void check_msr_access(void) + { +- check_dev_msr(); ++ check_msr_driver(); + check_msr_permission(); + + if (no_msr) +@@ -10147,7 +10157,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.10.24 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.11.29 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac b/SPECS/kernel-rt/0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac deleted file mode 100644 index f6e6232fe..000000000 --- a/SPECS/kernel-rt/0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac +++ /dev/null @@ -1,55 +0,0 @@ -From f63777b3f954d064c2f2230d8def8b88615f2645 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Fri, 20 Mar 2020 20:19:48 +0800 -Subject: [PATCH 11/13] EDAC/ieh: Add I/O device EDAC support for Intel Tiger - Lake-H SoC - -Tiger Lake-H SoC shares the same Integrated Error Handler(IEH) architecture -with Tiger Lake-U, so can use the same ieh_edac driver. - -Add Tiger Lake-H IEH device ID for I/O device EDAC support. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/ieh_edac.c | 11 ++++++++++- - 1 file changed, 10 insertions(+), 1 deletion(-) - -diff --git a/drivers/edac/ieh_edac.c b/drivers/edac/ieh_edac.c -index 81bba5aa775e..46eca4bddea8 100644 ---- a/drivers/edac/ieh_edac.c -+++ b/drivers/edac/ieh_edac.c -@@ -36,7 +36,7 @@ - - #include "edac_mc.h" - --#define IEH_REVISION "v1.7" -+#define IEH_REVISION "v1.8" - - #define EDAC_MOD_STR "ieh_edac" - #define IEH_NMI_NAME "ieh" -@@ -179,6 +179,14 @@ static struct ieh_config tgl_u_cfg = { - .action = RESTART, - }; - -+/* Tiger Lake-H SoC */ -+#define IEH_DID_TGL_H 0x43af -+ -+static struct ieh_config tgl_h_cfg = { -+ .did = IEH_DID_TGL_H, -+ .action = RESTART, -+}; -+ - static const char * const severities[] = { - [IEH_CORR_ERR] = "correctable", - [IEH_NONFATAL_ERR] = "non-fatal uncorrectable", -@@ -529,6 +537,7 @@ static struct notifier_block ieh_mce_dec = { - - static const struct x86_cpu_id ieh_cpuids[] = { - X86_MATCH_VFM(INTEL_TIGERLAKE_L, &tgl_u_cfg), -+ X86_MATCH_VFM(INTEL_TIGERLAKE, &tgl_h_cfg), - {} - }; - MODULE_DEVICE_TABLE(x86cpu, ieh_cpuids); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-KVM-VMX-Virtualize-FRED-event_data.nmi b/SPECS/kernel-rt/0011-KVM-VMX-Virtualize-FRED-event_data.nmi deleted file mode 100644 index 7716dbdc2..000000000 --- a/SPECS/kernel-rt/0011-KVM-VMX-Virtualize-FRED-event_data.nmi +++ /dev/null @@ -1,224 +0,0 @@ -From 2fb61e63bcd7ac26da234d4be9b1474391f68dab Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 13 Jun 2024 09:07:46 -0700 -Subject: [PATCH 11/44] KVM: VMX: Virtualize FRED event_data - -Set injected-event data when injecting a #PF, #DB, or #NM caused -by extended feature disable using FRED event delivery, and save -original-event data for being used as injected-event data. - -Unlike IDT using some extra CPU register as part of an event -context, e.g., %cr2 for #PF, FRED saves a complete event context -in its stack frame, e.g., FRED saves the faulting linear address -of a #PF into the event data field defined in its stack frame. - -Thus a new VMX control field called injected-event data is added -to provide the event data that will be pushed into a FRED stack -frame for VM entries that inject an event using FRED event delivery. -In addition, a new VM exit information field called original-event -data is added to store the event data that would have saved into a -FRED stack frame for VM exits that occur during FRED event delivery. -After such a VM exit is handled to allow the original-event to be -delivered, the data in the original-event data VMCS field needs to -be set into the injected-event data VMCS field for the injection of -the original event. - -Signed-off-by: Xin Li -[ Sean: reworked event data injection for nested ] -Signed-off-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v3: -* Rework event data injection for nested (Chao Gao & Sean Christopherson). - -Changes in v2: -* Document event data should be equal to CR2/DR6/IA32_XFD_ERR instead - of using WARN_ON() (Chao Gao). -* Zero event data if a #NM was not caused by extended feature disable - (Chao Gao). ---- - arch/x86/include/asm/kvm_host.h | 3 ++- - arch/x86/include/asm/vmx.h | 4 ++++ - arch/x86/kvm/svm/svm.c | 2 +- - arch/x86/kvm/vmx/vmx.c | 22 ++++++++++++++++++---- - arch/x86/kvm/x86.c | 16 +++++++++++++++- - 5 files changed, 40 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 3b64414a6730..40d2c5e1b7e7 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -761,6 +761,7 @@ struct kvm_queued_exception { - u32 error_code; - unsigned long payload; - bool has_payload; -+ u64 event_data; - }; - - /* -@@ -2208,7 +2209,7 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); - void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); - void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code); -+ bool has_error_code, u32 error_code, u64 event_data); - void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); - void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault); -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index d222fd1517ce..613deb8cfb78 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -270,8 +270,12 @@ enum vmcs_field { - PID_POINTER_TABLE_HIGH = 0x00002043, - SECONDARY_VM_EXIT_CONTROLS = 0x00002044, - SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, -+ INJECTED_EVENT_DATA = 0x00002052, -+ INJECTED_EVENT_DATA_HIGH = 0x00002053, - GUEST_PHYSICAL_ADDRESS = 0x00002400, - GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, -+ ORIGINAL_EVENT_DATA = 0x00002404, -+ ORIGINAL_EVENT_DATA_HIGH = 0x00002405, - VMCS_LINK_POINTER = 0x00002800, - VMCS_LINK_POINTER_HIGH = 0x00002801, - GUEST_IA32_DEBUGCTL = 0x00002802, -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 239436ae074b..8ee9f66a34f8 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4187,7 +4187,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) - - kvm_requeue_exception(vcpu, vector, - exitintinfo & SVM_EXITINTINFO_VALID_ERR, -- error_code); -+ error_code, 0); - break; - } - case SVM_EXITINTINFO_TYPE_INTR: -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index ba0177a97417..89841d2f3e45 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1864,6 +1864,9 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); - -+ if (is_fred_enabled(vcpu)) -+ vmcs_write64(INJECTED_EVENT_DATA, ex->event_data); -+ - vmx_clear_hlt(vcpu); - } - -@@ -7309,7 +7312,8 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) - static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - u32 idt_vectoring_info, - int instr_len_field, -- int error_code_field) -+ int error_code_field, -+ int event_data_field) - { - u8 vector; - int type; -@@ -7344,13 +7348,17 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - fallthrough; - case INTR_TYPE_HARD_EXCEPTION: { - u32 error_code = 0; -+ u64 event_data = 0; - - if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK) - error_code = vmcs_read32(error_code_field); -+ if (is_fred_enabled(vcpu)) -+ event_data = vmcs_read64(event_data_field); - - kvm_requeue_exception(vcpu, vector, - idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, -- error_code); -+ error_code, -+ event_data); - break; - } - case INTR_TYPE_SOFT_INTR: -@@ -7368,7 +7376,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx) - { - __vmx_complete_interrupts(&vmx->vcpu, vmx->idt_vectoring_info, - VM_EXIT_INSTRUCTION_LEN, -- IDT_VECTORING_ERROR_CODE); -+ IDT_VECTORING_ERROR_CODE, -+ ORIGINAL_EVENT_DATA); - } - - void vmx_cancel_injection(struct kvm_vcpu *vcpu) -@@ -7376,7 +7385,8 @@ void vmx_cancel_injection(struct kvm_vcpu *vcpu) - __vmx_complete_interrupts(vcpu, - vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), - VM_ENTRY_INSTRUCTION_LEN, -- VM_ENTRY_EXCEPTION_ERROR_CODE); -+ VM_ENTRY_EXCEPTION_ERROR_CODE, -+ INJECTED_EVENT_DATA); - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); - } -@@ -7530,6 +7540,10 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, - - vmx_disable_fb_clear(vmx); - -+ /* -+ * Note, even though FRED delivers the faulting linear address via the -+ * event data field on the stack, CR2 is still updated. -+ */ - if (vcpu->arch.cr2 != native_read_cr2()) - native_write_cr2(vcpu->arch.cr2); - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index bd1c2eb7c538..090f9fc0363f 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -810,9 +810,22 @@ void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu, - * breakpoint), it is reserved and must be zero in DR6. - */ - vcpu->arch.dr6 &= ~BIT(12); -+ -+ /* -+ * FRED #DB event data matches DR6, but follows the polarity of -+ * VMX's pending debug exceptions, not DR6. -+ */ -+ ex->event_data = ex->payload & ~BIT(12); -+ break; -+ case NM_VECTOR: -+ ex->event_data = ex->payload; - break; - case PF_VECTOR: - vcpu->arch.cr2 = ex->payload; -+ ex->event_data = ex->payload; -+ break; -+ default: -+ ex->event_data = 0; - break; - } - -@@ -920,7 +933,7 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, - } - - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code) -+ bool has_error_code, u32 error_code, u64 event_data) - { - - /* -@@ -945,6 +958,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.error_code = error_code; - vcpu->arch.exception.has_payload = false; - vcpu->arch.exception.payload = 0; -+ vcpu->arch.exception.event_data = event_data; - } - EXPORT_SYMBOL_GPL(kvm_requeue_exception); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi b/SPECS/kernel-rt/0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi new file mode 100644 index 000000000..0c65a8924 --- /dev/null +++ b/SPECS/kernel-rt/0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi @@ -0,0 +1,49 @@ +From 01246fe551a7415adaae6d94f89bf6353859f793 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Wed, 7 Feb 2024 09:26:31 -0800 +Subject: [PATCH 11/44] KVM: x86: Add a helper to detect if FRED is enabled for + a vCPU + +Signed-off-by: Xin Li +[ Sean: removed the "kvm_" prefix from the function name ] +Signed-off-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/kvm_cache_regs.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h +index 8ddb01191d6f6..3c8dbb77d7d48 100644 +--- a/arch/x86/kvm/kvm_cache_regs.h ++++ b/arch/x86/kvm/kvm_cache_regs.h +@@ -205,6 +205,21 @@ static __always_inline bool kvm_is_cr4_bit_set(struct kvm_vcpu *vcpu, + return !!kvm_read_cr4_bits(vcpu, cr4_bit); + } + ++/* ++ * It's enough to check just CR4.FRED (X86_CR4_FRED) to tell if ++ * a vCPU is running with FRED enabled, because: ++ * 1) CR4.FRED can be set to 1 only _after_ IA32_EFER.LMA = 1. ++ * 2) To leave IA-32e mode, CR4.FRED must be cleared first. ++ */ ++static inline bool is_fred_enabled(struct kvm_vcpu *vcpu) ++{ ++#ifdef CONFIG_X86_64 ++ return kvm_is_cr4_bit_set(vcpu, X86_CR4_FRED); ++#else ++ return false; ++#endif ++} ++ + static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu) + { + if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet b/SPECS/kernel-rt/0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet deleted file mode 100644 index 0a64ba2db..000000000 --- a/SPECS/kernel-rt/0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet +++ /dev/null @@ -1,48 +0,0 @@ -From 19e43999ba3d8d5a8f08ce836ea23f306adce236 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:41 -0700 -Subject: [PATCH 11/24] KVM: x86: Add fault checks for guest CR4.CET setting - -Check potential faults for CR4.CET setting per Intel SDM requirements. -CET can be enabled if and only if CR0.WP == 1, i.e. setting CR4.CET == -1 faults if CR0.WP == 0 and setting CR0.WP == 0 fails if CR4.CET == 1. - -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Reviewed-by: Chao Gao -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 0b0468348ee0..fbe3d1bea657 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1176,6 +1176,9 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) - (is_64_bit_mode(vcpu) || kvm_is_cr4_bit_set(vcpu, X86_CR4_PCIDE))) - return 1; - -+ if (!(cr0 & X86_CR0_WP) && kvm_is_cr4_bit_set(vcpu, X86_CR4_CET)) -+ return 1; -+ - kvm_x86_call(set_cr0)(vcpu, cr0); - - kvm_post_set_cr0(vcpu, old_cr0, cr0); -@@ -1375,6 +1378,9 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) - return 1; - } - -+ if ((cr4 & X86_CR4_CET) && !kvm_is_cr0_bit_set(vcpu, X86_CR0_WP)) -+ return 1; -+ - kvm_x86_call(set_cr4)(vcpu, cr4); - - kvm_post_set_cr4(vcpu, old_cr4, cr4); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl b/SPECS/kernel-rt/0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl new file mode 100644 index 000000000..83a459ecf --- /dev/null +++ b/SPECS/kernel-rt/0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl @@ -0,0 +1,38 @@ +From bbea5bdc46ea316869d78e041c5ec340ae5383f9 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Sun, 16 Nov 2025 13:34:29 +0100 +Subject: [PATCH 11/17] cpuidle: governors: teo: Fix tick_intercepts handling + in teo_update() + +The condition deciding whether or not to increase cpu_data->tick_intercepts +in teo_update() is reverse, so fix it. + +Fixes: d619b5cc6780 ("cpuidle: teo: Simplify counting events used for tick management") +Cc: 6.14+ # 6.14+: 0796ddf4a7f0: cpuidle: teo: Use this_cpu_ptr() where possible +Cc: 6.14+ # 6.14+: 8f3f01082d7a: cpuidle: governors: teo: Use s64 consistently in teo_update() +Cc: 6.14+ # 6.14+: b54df61c7428: cpuidle: governors: teo: Decay metrics below DECAY_SHIFT threshold +Cc: 6.14+ 6.14+: 083654ded547: cpuidle: governors: teo: Rework the handling of tick wakeups +Cc: 6.14+ +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Link: https://patch.msgid.link/5085160.31r3eYUQgx@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 94ba00b7617d7..85b5517067d16 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -251,7 +251,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + cpu_data->state_bins[idx_timer].hits += PULSE; + } else { + cpu_data->state_bins[idx_duration].intercepts += PULSE; +- if (TICK_NSEC <= measured_ns) ++ if (measured_ns <= TICK_NSEC) + cpu_data->tick_intercepts += PULSE; + } + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0011-i2c-designware-Preliminary-SMBus-support.lpss b/SPECS/kernel-rt/0011-i2c-designware-Preliminary-SMBus-support.lpss deleted file mode 100644 index 74cd75789..000000000 --- a/SPECS/kernel-rt/0011-i2c-designware-Preliminary-SMBus-support.lpss +++ /dev/null @@ -1,258 +0,0 @@ -From 3830284c347eb6ba17227cc9dcd2a769fe962247 Mon Sep 17 00:00:00 2001 -From: Heikki Krogerus -Date: Tue, 17 Dec 2024 16:15:44 +0200 -Subject: [PATCH 11/16] i2c: designware: Preliminary SMBus support - -This adds only the bare minimum handling for SMBus Alert -Signal, and only in host mode. - -Signed-off-by: Heikki Krogerus ---- - drivers/i2c/busses/Kconfig | 1 + - drivers/i2c/busses/Makefile | 1 + - drivers/i2c/busses/i2c-designware-core.h | 25 ++++++ - drivers/i2c/busses/i2c-designware-master.c | 5 ++ - drivers/i2c/busses/i2c-designware-pcidrv.c | 1 + - drivers/i2c/busses/i2c-designware-platdrv.c | 4 +- - drivers/i2c/busses/i2c-designware-smbus.c | 84 +++++++++++++++++++++ - 7 files changed, 120 insertions(+), 1 deletion(-) - create mode 100644 drivers/i2c/busses/i2c-designware-smbus.c - -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 0c77b1d4c260..bafb2794d06b 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -564,6 +564,7 @@ config I2C_DAVINCI - config I2C_DESIGNWARE_CORE - tristate "Synopsys DesignWare I2C adapter" - select REGMAP -+ select I2C_SMBUS - help - This option enables support for the Synopsys DesignWare I2C adapter. - This driver includes support for the I2C host on the Synopsys -diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile -index 04db855fdfd6..b362e7250ae7 100644 ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -53,6 +53,7 @@ obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o - obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o - i2c-designware-core-y := i2c-designware-common.o - i2c-designware-core-y += i2c-designware-master.o -+i2c-designware-core-y += i2c-designware-smbus.o - i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o - obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o - i2c-designware-platform-y := i2c-designware-platdrv.o -diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h -index 347843b4f5dd..5cfc909713d9 100644 ---- a/drivers/i2c/busses/i2c-designware-core.h -+++ b/drivers/i2c/busses/i2c-designware-core.h -@@ -78,9 +78,14 @@ - #define DW_IC_TX_ABRT_SOURCE 0x80 - #define DW_IC_ENABLE_STATUS 0x9c - #define DW_IC_CLR_RESTART_DET 0xa8 -+#define DW_IC_SMBUS_INTR_STAT 0xc8 -+#define DW_IC_SMBUS_INTR_MASK 0xcc -+#define DW_IC_SMBUS_RAW_INTR_STAT 0xd0 -+#define DW_IC_CLR_SMBUS_INTR 0xd4 - #define DW_IC_COMP_PARAM_1 0xf4 - #define DW_IC_COMP_VERSION 0xf8 - #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ -+#define DW_IC_SMBUS_MIN_VER 0x3230302a /* "200*" == v2.00* */ - #define DW_IC_COMP_TYPE 0xfc - #define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ - -@@ -118,6 +123,8 @@ - #define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) - #define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) - -+#define DW_IC_SMBUS_INTR_ALERT BIT(10) -+ - #define DW_IC_SDA_HOLD_RX_SHIFT 16 - #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16) - -@@ -262,6 +269,7 @@ struct dw_i2c_dev { - struct clk *pclk; - struct reset_control *rst; - struct i2c_client *slave; -+ struct i2c_client *smbus_alert; - u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); - int cmd_err; - struct i2c_msg *msgs; -@@ -311,6 +319,7 @@ struct dw_i2c_dev { - #define ACCESS_NO_IRQ_SUSPEND BIT(1) - #define ARBITRATION_SEMAPHORE BIT(2) - #define ACCESS_POLLING BIT(3) -+#define IS_SMBUS BIT(4) - - #define MODEL_MSCC_OCELOT BIT(8) - #define MODEL_BAIKAL_BT1 BIT(9) -@@ -413,3 +422,19 @@ int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev); - #endif - - int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev); -+ -+irqreturn_t i2c_dw_smbus_isr(struct dw_i2c_dev *dev); -+int i2c_dw_smbus_host_register(struct dw_i2c_dev *dev); -+ -+/** -+ * i2c_dw_smbus_unregister - Helper to remove SMBus resources -+ * @dev: handle to the controller -+ * -+ * This function removes the SMBus alert device if it exists. -+ */ -+static inline void i2c_dw_smbus_unregister(struct dw_i2c_dev *dev) -+{ -+ regmap_set_bits(dev->map, DW_IC_SMBUS_INTR_MASK, DW_IC_SMBUS_INTR_ALERT); -+ i2c_unregister_device(dev->smbus_alert); -+ dev->smbus_alert = NULL; -+} -diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c -index cbd88ffa5610..ffe41e1c9855 100644 ---- a/drivers/i2c/busses/i2c-designware-master.c -+++ b/drivers/i2c/busses/i2c-designware-master.c -@@ -747,6 +747,9 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) - struct dw_i2c_dev *dev = dev_id; - unsigned int stat, enabled; - -+ if (i2c_dw_smbus_isr(dev)) -+ return IRQ_HANDLED; -+ - regmap_read(dev->map, DW_IC_ENABLE, &enabled); - regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat); - if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY)) -@@ -1089,6 +1092,8 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) - ret = i2c_add_numbered_adapter(adap); - if (ret) - dev_err(dev->dev, "failure adding adapter: %d\n", ret); -+ else if (dev->flags & IS_SMBUS) -+ ret = i2c_dw_smbus_host_register(dev); - pm_runtime_put_noidle(dev->dev); - - return ret; -diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c -index f21f9877c040..1e7b4bd20964 100644 ---- a/drivers/i2c/busses/i2c-designware-pcidrv.c -+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c -@@ -303,6 +303,7 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev) - pm_runtime_forbid(device); - pm_runtime_get_noresume(device); - -+ i2c_dw_smbus_unregister(dev); - i2c_del_adapter(&dev->adapter); - } - -diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c -index a35e4c64a1d4..dc9fd89e9f32 100644 ---- a/drivers/i2c/busses/i2c-designware-platdrv.c -+++ b/drivers/i2c/busses/i2c-designware-platdrv.c -@@ -326,6 +326,8 @@ static void dw_i2c_plat_remove(struct platform_device *pdev) - - pm_runtime_get_sync(device); - -+ i2c_dw_smbus_unregister(dev); -+ - i2c_del_adapter(&dev->adapter); - - i2c_dw_disable(dev); -@@ -365,7 +367,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { - { "INT33C3", 0 }, - { "INT3432", 0 }, - { "INT3433", 0 }, -- { "INTC10EF", 0 }, -+ { "INTC10EF", IS_SMBUS }, - {} - }; - MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); -diff --git a/drivers/i2c/busses/i2c-designware-smbus.c b/drivers/i2c/busses/i2c-designware-smbus.c -new file mode 100644 -index 000000000000..2e01d070a913 ---- /dev/null -+++ b/drivers/i2c/busses/i2c-designware-smbus.c -@@ -0,0 +1,84 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Synopsys DesignWare SMBus driver. -+ * -+ * Copyright (C) 2024 Intel Corporation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "i2c-designware-core.h" -+ -+static struct i2c_smbus_alert_setup i2c_dw_smbus_setup; -+ -+/** -+ * i2c_dw_smbus_isr - Interrupt service routine for SMBus interrupts -+ * @dev: handle to the controller -+ * -+ * This function currently only handles the SMBus Alert signal. -+ * -+ * Return: IRQ_HANDLED if the interrupt was caused by the SMBUS Alert, -+ * otherwise IRQ_NONE. -+ */ -+irqreturn_t i2c_dw_smbus_isr(struct dw_i2c_dev *dev) -+{ -+ u32 stat; -+ -+ if (!dev->smbus_alert) -+ return IRQ_NONE; -+ -+ regmap_read(dev->map, DW_IC_SMBUS_INTR_STAT, &stat); -+ if (!stat) -+ return IRQ_NONE; -+ -+ regmap_write(dev->map, DW_IC_CLR_SMBUS_INTR, stat); -+ -+ if (stat & DW_IC_SMBUS_INTR_ALERT) -+ i2c_handle_smbus_alert(dev->smbus_alert); -+ -+ return IRQ_HANDLED; -+} -+ -+/** -+ * i2c_dw_smbus_host_register - Register the SMBus alert device for the host -+ * @dev: handle to the controller -+ * -+ * This function checks is the SMBus feature available, and then registers the -+ * alert device if it is. If the SMBus feature is not available the function -+ * returns silently with a success. -+ * -+ * The SMBus alert device needs to be unregistered by calling -+ * i2c_dw_smbus_unregister(). -+ * -+ * Return: 0 on success, errno on error. -+ */ -+int i2c_dw_smbus_host_register(struct dw_i2c_dev *dev) -+{ -+ struct i2c_client *alert; -+ u32 ic_version; -+ int ret; -+ -+ ret = regmap_read(dev->map, DW_IC_COMP_VERSION, &ic_version); -+ if (ret) -+ return ret; -+ -+ if (ic_version < DW_IC_SMBUS_MIN_VER) -+ return 0; -+ -+ alert = i2c_new_smbus_alert_device(&dev->adapter, &i2c_dw_smbus_setup); -+ if (IS_ERR(alert)) -+ return PTR_ERR(alert); -+ -+ dev->smbus_alert = alert; -+ -+ ret = regmap_clear_bits(dev->map, DW_IC_SMBUS_INTR_MASK, DW_IC_SMBUS_INTR_ALERT); -+ if (ret) -+ i2c_dw_smbus_unregister(dev); -+ -+ return ret; -+} --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel-rt/0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet new file mode 100644 index 000000000..7c391c49c --- /dev/null +++ b/SPECS/kernel-rt/0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet @@ -0,0 +1,97 @@ +From e3ca62b0f8e5880890ac1134c29d50e9081f226a Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Thu, 10 Jun 2021 12:44:46 +0800 +Subject: [PATCH 11/14] igc: Enable HW TX Timestamp for AF_XDP ZC + +Enable HW TX Timestamp per-packet using dma write back descriptor. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc.h | 1 + + drivers/net/ethernet/intel/igc/igc_main.c | 7 ++++++- + drivers/net/ethernet/intel/igc/igc_ptp.c | 24 +++++++++++++++++++++++ + 3 files changed, 31 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index 64d22481be91e..3f8969c73a6d2 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -788,6 +788,7 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, + void igc_ptp_tx_hang(struct igc_adapter *adapter); + void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); + void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); ++ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp); + + int igc_led_setup(struct igc_adapter *adapter); + void igc_led_free(struct igc_adapter *adapter); +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 5b797a0bd870d..cabdcacc4c923 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -3119,6 +3119,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) + tx_desc->read.buffer_addr = cpu_to_le64(dma); + + bi->type = IGC_TX_BUFFER_TYPE_XSK; ++ bi->tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; + bi->protocol = 0; + bi->bytecount = xdp_desc.len; + bi->gso_segs = 1; +@@ -3159,6 +3160,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + unsigned int i = tx_ring->next_to_clean; + struct igc_tx_buffer *tx_buffer; + union igc_adv_tx_desc *tx_desc; ++ ktime_t timestamp = 0; + u32 xsk_frames = 0; + + if (test_bit(__IGC_DOWN, &adapter->state)) +@@ -3197,7 +3199,10 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { + u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); + +- igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); ++ if (tx_ring->xsk_pool && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) ++ timestamp = igc_tx_dma_hw_tstamp(adapter, tstamp); ++ else ++ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); + } + + /* clear next_to_watch to prevent false hangs */ +diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c +index ad786c816144f..23a55ca30677b 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ptp.c ++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c +@@ -916,6 +916,30 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, + skb_tstamp_tx(skb, &shhwtstamps); + } + ++ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp) ++{ ++ struct skb_shared_hwtstamps shhwtstamps; ++ int adjust = 0; ++ ++ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); ++ ++ switch (adapter->link_speed) { ++ case SPEED_10: ++ adjust = IGC_I225_TX_LATENCY_10; ++ break; ++ case SPEED_100: ++ adjust = IGC_I225_TX_LATENCY_100; ++ break; ++ case SPEED_1000: ++ adjust = IGC_I225_TX_LATENCY_1000; ++ break; ++ case SPEED_2500: ++ adjust = IGC_I225_TX_LATENCY_2500; ++ break; ++ } ++ return ktime_add_ns(shhwtstamps.hwtstamp, adjust); ++} ++ + /** + * igc_ptp_tx_tstamp_event + * @adapter: board private structure +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet b/SPECS/kernel-rt/0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet deleted file mode 100644 index 37413f6e4..000000000 --- a/SPECS/kernel-rt/0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet +++ /dev/null @@ -1,34 +0,0 @@ -From df31134af3bc30f219b7f0a0414c852b551cc081 Mon Sep 17 00:00:00 2001 -From: Aravindhan Gunasekaran -Date: Tue, 3 Aug 2021 17:32:26 +0000 -Subject: [PATCH 11/19] igc: Take care of DMA timestamp rollover - -This patch is to fix the spike in driver Tx Path when measuring between -two timestamp of TX HW Timestamp during profiling stage. - -Rollover is identified by checking the 32-bit SYSTIM_L(say, present-time) -value which should be greater that LS 32bits from DMA WB(time in past). - -Signed-off-by: Aravindhan Gunasekaran -Signed-off-by: Muhammad Husaini Zulkifli ---- - drivers/net/ethernet/intel/igc/igc_ptp.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index 61fe47c2a5d3..7c220a6f601f 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -459,6 +459,9 @@ static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, - nsec = rd32(IGC_SYSTIML); - sec = rd32(IGC_SYSTIMH); - -+ if (unlikely(nsec < (systim & 0xFFFFFFFF))) -+ --sec; -+ - switch (adapter->hw.mac.type) { - case igc_i225: - memset(hwtstamps, 0, sizeof(*hwtstamps)); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu b/SPECS/kernel-rt/0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu deleted file mode 100644 index fcaf4fe87..000000000 --- a/SPECS/kernel-rt/0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu +++ /dev/null @@ -1,317 +0,0 @@ -From 2a9fc94ed5cd0f8d8f54e7537337bdc1567cf4e9 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 6 Oct 2025 10:01:19 +0800 -Subject: [PATCH 11/11] ipu7: media: Fix allyesconfig & allmodconfig - -Remove IPU6 config in Kconfig & Makefile. Remove declared but -unused functions. Update Kconfig to block arm64 build. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/Kconfig | 1 + - drivers/media/i2c/isx031.c | 4 +- - drivers/media/i2c/max9x/max96724.c | 192 ----------------------- - drivers/media/pci/intel/Kconfig | 1 - - drivers/media/pci/intel/Makefile | 1 - - drivers/media/pci/intel/ipu7/ipu7-isys.c | 3 +- - drivers/media/platform/intel/ipu-acpi.c | 1 - - 7 files changed, 5 insertions(+), 198 deletions(-) - -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index f65a2f6118ea..d7e8a0b986a3 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -289,6 +289,7 @@ config VIDEO_ISX031 - config VIDEO_MAX9X - tristate "MAX9X serdes support" - depends on VIDEO_DEV && I2C -+ depends on VIDEO_INTEL_IPU7 && VIDEO_INTEL_IPU_USE_PLATFORMDATA - select VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - help -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index e7d9e417745a..ddc3e512efa4 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -769,11 +769,11 @@ static int isx031_probe(struct i2c_client *client) - return ret; - } - -- if (isx031->platform_data && isx031->platform_data->suffix) -+ if (isx031->platform_data) - snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %s", - isx031->platform_data->suffix); - -- if (isx031->platform_data && isx031->platform_data->lanes) -+ if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - - mutex_init(&isx031->mutex); -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index 1c1059c0c9de..b214e1176963 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -64,8 +64,6 @@ static int max96724_enable_serial_link(struct max9x_common *common, unsigned int - static int max96724_set_remote_control_channel_enabled(struct max9x_common *common, unsigned int link_id, bool enabled); - static int max96724_select_serial_link(struct max9x_common *common, unsigned int link); - static int max96724_deselect_serial_link(struct max9x_common *common, unsigned int link); --static int max96724_enable_native_frame_sync(struct max9x_common *common); --static int max96724_enable_gpio_frame_sync(struct max9x_common *common); - static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line); - static int max96724_enable_line_fault(struct max9x_common *common, unsigned int line); - static int max96724_set_line_fault(struct max9x_common *common, unsigned int line, bool enable); -@@ -877,196 +875,6 @@ static struct max9x_serial_link_ops max96724_serial_link_ops = { - .get_locked = max96724_get_serial_link_lock, - }; - --static int max96724_enable_native_frame_sync(struct max9x_common *common) --{ -- struct device_node *node = common->dev->of_node; -- struct device *dev = common->dev; -- struct regmap *map = common->map; -- int ret, i; -- unsigned int val; -- enum max96724_fsync_pin pin; -- unsigned int fsync_freq; -- unsigned int pclk_freq; -- unsigned int fsync_period; -- unsigned int fsync_tx_id; -- bool fsync_master; -- -- if (!of_property_read_bool(node, "frame-sync-enable")) { -- dev_info(dev, "Native frame sync not enabled"); -- return regmap_write(map, MAX96724_FSYNC_0, -- MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -- MAX96724_FSYNC_GEN_OFF_GPIO_OFF)); -- } -- -- fsync_master = of_property_read_bool(node, "frame-sync-master"); -- if (fsync_master) -- dev_dbg(dev, "Frame sync master mode"); -- else -- dev_dbg(dev, "Frame sync slave mode"); -- -- ret = of_property_read_u32(node, "frame-sync-pin", &val); -- if (ret) { -- dev_err(dev, "Missing property: frame-sync-pin"); -- return ret; -- } -- -- // check value of pin -- switch (val) { -- case MAX96724_FSYNC_PIN_MFP0: -- case MAX96724_FSYNC_PIN_MFP7: -- pin = val; -- break; -- -- default: -- dev_err(dev, "Invalid frame-sync-pin"); -- return -EINVAL; -- }; -- -- ret = of_property_read_u32(node, "frame-sync-tx-id", &val); -- if (ret) { -- dev_err(dev, "Missing property: frame-sync-tx-id"); -- return -EINVAL; -- } -- -- // check value of frame-sync-tx-id -- fsync_tx_id = val & 0x1F; -- if (fsync_tx_id != val) -- dev_warn(dev, "Truncated frame-sync-tx-id to 5 bits!"); -- -- ret = of_property_read_u32(node, "pclk-freq", &pclk_freq); -- if (ret) { -- dev_err(dev, "Missing property: pclk-freq"); -- return -EINVAL; -- } -- -- ret = of_property_read_u32(node, "frame-sync-freq", &fsync_freq); -- if (ret) { -- dev_err(dev, "Missing property: frame-sync-freq;"); -- return -EINVAL; -- } -- -- // Reset register to known state -- ret = regmap_write(map, MAX96724_FSYNC_15, 0xDF); -- if (ret) { -- dev_dbg(dev, "Failed to reset FSYNC state"); -- return ret; -- } -- -- // Disable AUTO FS links -- val = MAX9X_FIELD_PREP(MAX96724_FS_GPIO_TYPE_FIELD, MAX96724_FS_GPIO_TYPE_GMSL2) | -- MAX9X_FIELD_PREP(MAX96724_FS_USE_XTAL_FIELD, true) | -- MAX9X_FIELD_PREP(MAX96724_AUTO_FS_LINKS_FIELD, 0); -- // Enable all FS links manually -- for (i = 0; i < 4; ++i) -- val |= MAX9X_FIELD_PREP(MAX96724_FS_LINK_FIELD(i), 1); -- -- ret = regmap_write(map, MAX96724_FSYNC_15, val); -- if (ret) { -- dev_dbg(dev, "Failed to write FSYNC_15"); -- return ret; -- } -- -- // Calculate value of FSYNC_PERIOD registers -- // FSYNC_PERIOD = number of pclk cycles per fsync period -- fsync_period = pclk_freq / fsync_freq; -- dev_dbg(dev, "Calculated FSYNC_PERIOD: 0x%06x", fsync_period); -- -- for (val = MAX96724_FSYNC_5; val <= MAX96724_FSYNC_7; ++val) { -- ret = regmap_write(map, val, (uint8_t) fsync_period); -- if (ret) { -- dev_err(dev, "Failed to write FSYNC_PERIOD registers to 0x%03x", val); -- return ret; -- } -- -- fsync_period = fsync_period >> 8; -- } -- -- ret = regmap_write(map, MAX96724_FSYNC_17, -- MAX9X_FIELD_PREP(MAX96724_FSYNC_TX_ID_FIELD, fsync_tx_id) | -- MAX9X_FIELD_PREP(MAX96724_FSYNC_ERR_THR_FIELD, 0)); -- if (ret) { -- dev_err(dev, "Failed to set FSYNC_17"); -- return ret; -- } -- -- ret = regmap_write(map, MAX96724_FSYNC_0, -- MAX9X_FIELD_PREP(MAX96724_FSYNC_OUT_PIN_FIELD, pin) | -- MAX9X_FIELD_PREP(MAX96724_EN_VS_GEN_FIELD, 0) | -- MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -- (fsync_master ? MAX96724_FSYNC_GEN_ON_GPIO_OUTPUT : MAX96724_FSYNC_GEN_OFF_GPIO_INPUT)) | -- MAX9X_FIELD_PREP(MAX96724_FSYNC_METH_FIELD, MAX96724_FSYNC_METHOD_MANUAL)); -- -- return 0; --} -- --static int max96724_enable_gpio_frame_sync(struct max9x_common *common) --{ -- struct device_node *node = common->dev->of_node; -- struct device *dev = common->dev; -- struct regmap *map = common->map; -- -- u32 fsync_gpios[MAX96724_NUM_GPIOS]; -- int num_fsync_gpios; -- int i, gpio, gpio_tx_val, ret; -- -- // Clean up any previous values in the event the chip was not reset -- // or GPIO forwarding needs to be toggled off -- dev_dbg(dev, "Setting GPIO registers to default value"); -- for (i = 0; i < MAX96724_NUM_GPIOS; i++) { -- // Default values per the datasheet -- TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), (BIT(7) | BIT(0)))); -- -- // Link A register has different fields from Links B, C, D -- TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), (BIT(7) | BIT(5) | i))); -- TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), i)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), i)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), i)); -- } -- -- // Read DT to find fsync GPIOs -- ret = of_property_read_variable_u32_array(node, "frame-sync-ports", -- fsync_gpios, 0, MAX96724_NUM_GPIOS); -- -- if (ret == -ENODATA || ret == -EINVAL) { -- dev_dbg(dev, "No frame sync GPIOs specified in DT"); -- return 0; -- } -- -- if (ret < 0) { -- dev_err(dev, "Failed to parse DT frame-sync-ports, error %d", ret); -- return ret; -- } -- -- num_fsync_gpios = ret; -- dev_info(dev, "Enabling %d frame sync GPIOs", num_fsync_gpios); -- -- // Configure MAX96724 to forward specified GPIOs -- for (i = 0; i < num_fsync_gpios; i++) { -- gpio = fsync_gpios[i]; -- -- if (gpio >= MAX96724_NUM_GPIOS) { -- dev_warn(dev, "Skipping invalid GPIO %d in DT", gpio); -- continue; -- } -- -- // See: MAX96724 Users Guide "Configuring GPIO forwarding" -- -- // Enable GPIO for transmission -- TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), -- MAX96724_GPIO_RES_CFG | MAX96724_GPIO_TX_ENABLE | MAX96724_GPIO_OUTDRV_DISABLE)); -- -- // Configure transmission registers on Links A-D. -- gpio_tx_val = MAX96724_GPIO_PUSH_PULL | gpio; -- -- TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), gpio_tx_val)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), gpio_tx_val)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), gpio_tx_val)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), gpio_tx_val)); -- } -- -- return 0; --} -- - static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line) - { - return max96724_set_line_fault(common, line, false); -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index 948cda08fff5..a92a7fabeac9 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -1,7 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0-only - - source "drivers/media/pci/intel/ipu3/Kconfig" --source "drivers/media/pci/intel/ipu6/Kconfig" - source "drivers/media/pci/intel/ipu7/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index ff0fea13422d..b022340db8ba 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -5,5 +5,4 @@ - obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ --obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ - obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c -index e5ba50142a1a..44e860d3db24 100644 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys.c -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys.c -@@ -517,6 +517,7 @@ static void isys_tpg_unregister_subdevices(struct ipu7_isys *isys) - isys->tpg = NULL; - } - -+/* - static int isys_tpg_register_subdevices(struct ipu7_isys *isys) - { - const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -@@ -577,7 +578,7 @@ static int isys_tpg_create_media_links(struct ipu7_isys *isys) - - return 0; - } -- -+*/ - #endif - - #if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c -index deed3bba52bb..c254c8299930 100644 ---- a/drivers/media/platform/intel/ipu-acpi.c -+++ b/drivers/media/platform/intel/ipu-acpi.c -@@ -191,7 +191,6 @@ static int ipu_acpi_test(struct device *dev, void *priv) - - int ipu_get_acpi_devices(void **spdata) - { -- struct ipu_i2c_helper helper = {0}; - int rval; - struct ipu7_isys_subdev_pdata *ptr = NULL; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet b/SPECS/kernel-rt/0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet new file mode 100644 index 000000000..e2983f559 --- /dev/null +++ b/SPECS/kernel-rt/0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet @@ -0,0 +1,117 @@ +From 14e35c66b1dbba600374e06ad117fd1d5f0368e9 Mon Sep 17 00:00:00 2001 +From: Choong Yong Liang +Date: Fri, 15 Dec 2023 09:09:21 +0800 +Subject: [PATCH 11/18] net: stmmac: Set mac_managed_pm flag from stmmac to + resolve race condition + +When WoL is set to 'g' (e.g., using the command +'ethtool -s enp0s30f4 wol g'), waking up from hibernation will result +in the error messages 'PM: failed to quiesce: error -16' and +'PM: hibernation: Failed to load image, recovering.' + +During 'hibernation_restore()', it will eventually call the +'mdio_bus_phy_suspend()' function, and the function will check the +'mac_managed_pm' flag. If the flag is disabled, it will proceed to the +'phy_suspend()' function and return a -16 error. + +For 'stmmac', the 'mac_managed_pm' flag is always set to 'true' for the +'phylink', and 'phylink' will set the 'mac_managed_pm' flag for the 'phy' +during 'phylink_bringup_phy()'. The process of setting the 'mac_managed_pm' +flag from 'stmmac' -> 'phylink' -> 'phy' takes a while to complete. + +During wake-up from hibernation, there is a race condition that depends on +whether 'mac_managed_pm' was set for the 'phy' first or 'phy_suspend()' +function is called first. + +To address the race condition, 'stmmac' directly setting the +'mac_managed_pm' during 'stmmac_dvr_probe()' will resolve the issue. + +Fixes: f151c147b3af ("net: stmmac: Enable mac_managed_pm phylink config") +Signed-off-by: Choong Yong Liang +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 40 +++++++++++++------ + 1 file changed, 27 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 361b29c2c9854..92a8075ef9399 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1102,6 +1102,24 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) + } + } + ++static int stmmac_get_phydev(struct stmmac_priv *priv, struct phy_device **phydev) ++{ ++ int addr = priv->plat->phy_addr; ++ ++ if (addr < 0) { ++ netdev_err(priv->dev, "no phy found\n"); ++ return -ENODEV; ++ } ++ ++ *phydev = mdiobus_get_phy(priv->mii, addr); ++ if (!*phydev) { ++ netdev_err(priv->dev, "no phy at addr %d\n", addr); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ + /** + * stmmac_init_phy - PHY initialization + * @dev: net device structure +@@ -1139,19 +1157,10 @@ static int stmmac_init_phy(struct net_device *dev) + * manually parse it + */ + if (!phy_fwnode || IS_ERR(phy_fwnode)) { +- int addr = priv->plat->phy_addr; + struct phy_device *phydev; +- +- if (addr < 0) { +- netdev_err(priv->dev, "no phy found\n"); +- return -ENODEV; +- } +- +- phydev = mdiobus_get_phy(priv->mii, addr); +- if (!phydev) { +- netdev_err(priv->dev, "no phy at addr %d\n", addr); +- return -ENODEV; +- } ++ ret = stmmac_get_phydev(priv, &phydev); ++ if (ret) ++ return ret; + + ret = phylink_connect_phy(priv->phylink, phydev); + } else { +@@ -1198,7 +1207,6 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) + + config->dev = &priv->dev->dev; + config->type = PHYLINK_NETDEV; +- config->mac_managed_pm = true; + + /* Stmmac always requires an RX clock for hardware initialization */ + config->mac_requires_rxc = true; +@@ -7446,6 +7454,7 @@ int stmmac_dvr_probe(struct device *device, + { + struct net_device *ndev = NULL; + struct stmmac_priv *priv; ++ struct phy_device *phydev; + u32 rxq; + int i, ret = 0; + +@@ -7694,6 +7703,11 @@ int stmmac_dvr_probe(struct device *device, + if (ret) + goto error_pcs_setup; + ++ ret = stmmac_get_phydev(priv, &phydev); ++ if (ret) ++ return ret; ++ phydev->mac_managed_pm = true; ++ + ret = stmmac_phy_setup(priv); + if (ret) { + netdev_err(ndev, "failed to setup phy (%d)\n", ret); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu b/SPECS/kernel-rt/0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu deleted file mode 100644 index cb63d9e11..000000000 --- a/SPECS/kernel-rt/0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu +++ /dev/null @@ -1,78 +0,0 @@ -From d18ecf4f258ad566e3e85b194e61047c12bdb56b Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 15:39:28 +0800 -Subject: [PATCH 11/21] patch: staging add patch for ipu7 Kconfig Makefile - -Support kernel v6.10 - -Due to change "kbuild: use $(src) instead of $(srctree)/$(src) for -source directory" at https://lore.kernel.org/lkml/20240416121838. -95427-5-masahiroy@kernel.org/, the old include paths in Makefile -can't work on kernel v6.10. To keep compatible with < v6.10 kernel, -add another check in Makefile. - -Signed-off-by: linya14x -Signed-off-by: Hao Yao ---- - drivers/staging/media/ipu7/Kconfig | 11 ++++++++--- - drivers/staging/media/ipu7/Makefile | 11 +++++++++++ - 2 files changed, 19 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig -index c4eee7c3e6d7..0a6e68c7632d 100644 ---- a/drivers/staging/media/ipu7/Kconfig -+++ b/drivers/staging/media/ipu7/Kconfig -@@ -4,7 +4,12 @@ config VIDEO_INTEL_IPU7 - depends on VIDEO_DEV - depends on X86 && HAS_DMA - depends on IPU_BRIDGE || !IPU_BRIDGE -- depends on PCI -+ # -+ # This driver incorrectly tries to override the dma_ops. It should -+ # never have done that, but for now keep it working on architectures -+ # that use dma ops -+ # -+ depends on ARCH_HAS_DMA_OPS - select AUXILIARY_BUS - select IOMMU_IOVA - select VIDEO_V4L2_SUBDEV_API -@@ -15,8 +20,8 @@ config VIDEO_INTEL_IPU7 - This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs - and used for capturing images and video from camera sensors. - -- To compile this driver, say Y here! It contains 2 modules - -- intel_ipu7 and intel_ipu7_isys. -+ To compile this driver, say Y here! It contains 3 modules - -+ intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. - - config VIDEO_INTEL_IPU7_ISYS_RESET - bool "IPU7 ISYS RESET" -diff --git a/drivers/staging/media/ipu7/Makefile b/drivers/staging/media/ipu7/Makefile -index 6d2aec219e65..3c35ca566473 100644 ---- a/drivers/staging/media/ipu7/Makefile -+++ b/drivers/staging/media/ipu7/Makefile -@@ -1,6 +1,13 @@ - # SPDX-License-Identifier: GPL-2.0 - # Copyright (c) 2017 - 2025 Intel Corporation. - -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ - intel-ipu7-objs += ipu7.o \ - ipu7-bus.o \ - ipu7-dma.o \ -@@ -21,3 +28,7 @@ intel-ipu7-isys-objs += ipu7-isys.o \ - ipu7-isys-subdev.o - - obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ -+ -+ccflags-y += -I$(src)/ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf b/SPECS/kernel-rt/0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf deleted file mode 100644 index 327902cf0..000000000 --- a/SPECS/kernel-rt/0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf +++ /dev/null @@ -1,40 +0,0 @@ -From ac8185284e5f69dfb3652c262cd9c7729886012e Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 21 Jul 2023 20:31:26 +0800 -Subject: [PATCH 011/100] perf/x86: Fix typos and inconsistent indents in - perf_event header - -There is one typo and some inconsistent indents in perf_event.h header -file. Fix them. - -Signed-off-by: Dapeng Mi ---- - arch/x86/include/asm/perf_event.h | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 49a4d442f3fc..1eb31cd9fceb 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -444,15 +444,15 @@ static inline bool is_topdown_idx(int idx) - * - * With this fake counter assigned, the guest LBR event user (such as KVM), - * can program the LBR registers on its own, and we don't actually do anything -- * with then in the host context. -+ * with them in the host context. - */ --#define INTEL_PMC_IDX_FIXED_VLBR (GLOBAL_STATUS_LBRS_FROZEN_BIT) -+#define INTEL_PMC_IDX_FIXED_VLBR (GLOBAL_STATUS_LBRS_FROZEN_BIT) - - /* - * Pseudo-encoding the guest LBR event as event=0x00,umask=0x1b, - * since it would claim bit 58 which is effectively Fixed26. - */ --#define INTEL_FIXED_VLBR_EVENT 0x1b00 -+#define INTEL_FIXED_VLBR_EVENT 0x1b00 - - /* - * Adaptive PEBS v4 --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf b/SPECS/kernel-rt/0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf new file mode 100644 index 000000000..f6b9501ae --- /dev/null +++ b/SPECS/kernel-rt/0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf @@ -0,0 +1,78 @@ +From f14332ef9553da5ad187f29c0a4f49bd73622dbb Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Fri, 21 Nov 2025 14:47:18 -0800 +Subject: [PATCH 11/13] perf/x86/intel/uncore: Update DMR uncore constraints + preliminarily + +Update event constraints base on the latest DMR uncore event list. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore_snbep.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index fac2be780276d..3f96fcc8562b7 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -6660,10 +6660,19 @@ static const struct attribute_group dmr_cxlcm_uncore_format_group = { + .attrs = dmr_cxlcm_uncore_formats_attr, + }; + ++static struct event_constraint dmr_uncore_cxlcm_constraints[] = { ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1, 0x24, 0x0f), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x41, 0x41, 0xf0), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x50, 0x5e, 0xf0), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x60, 0x61, 0xf0), ++ EVENT_CONSTRAINT_END ++}; ++ + static struct intel_uncore_type dmr_uncore_cxlcm = { + .name = "cxlcm", + .event_mask = GENERIC_PMON_RAW_EVENT_MASK, + .event_mask_ext = DMR_CXLCM_EVENT_MASK_EXT, ++ .constraints = dmr_uncore_cxlcm_constraints, + .format_group = &dmr_cxlcm_uncore_format_group, + .attr_update = uncore_alias_groups, + }; +@@ -6675,9 +6684,20 @@ static struct intel_uncore_type dmr_uncore_hamvf = { + .attr_update = uncore_alias_groups, + }; + ++static struct event_constraint dmr_uncore_cbo_constraints[] = { ++ UNCORE_EVENT_CONSTRAINT(0x11, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x19, 0x1a, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x1f, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x21, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x25, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x36, 0x1), ++ EVENT_CONSTRAINT_END ++}; ++ + static struct intel_uncore_type dmr_uncore_cbo = { + .name = "cbo", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .constraints = dmr_uncore_cbo_constraints, + .format_group = &dmr_sca_uncore_format_group, + .attr_update = uncore_alias_groups, + }; +@@ -6711,9 +6731,16 @@ static struct intel_uncore_type dmr_uncore_dda = { + .attr_update = uncore_alias_groups, + }; + ++static struct event_constraint dmr_uncore_sbo_constraints[] = { ++ UNCORE_EVENT_CONSTRAINT(0x1f, 0x01), ++ UNCORE_EVENT_CONSTRAINT(0x25, 0x01), ++ EVENT_CONSTRAINT_END ++}; ++ + static struct intel_uncore_type dmr_uncore_sbo = { + .name = "sbo", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .constraints = dmr_uncore_sbo_constraints, + .format_group = &dmr_sca_uncore_format_group, + .attr_update = uncore_alias_groups, + }; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal b/SPECS/kernel-rt/0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal deleted file mode 100644 index d6af217b1..000000000 --- a/SPECS/kernel-rt/0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal +++ /dev/null @@ -1,33 +0,0 @@ -From 3c8cf98b0c1d91cbe3df0c34fa640ab5a03977da Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 10 Nov 2025 15:50:41 -0800 -Subject: [PATCH 11/11] platform/x86/intel/hid: Add Nova Lake support -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add ACPI ID for Nova Lake. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20251110235041.123685-1-srinivas.pandruvada@linux.intel.com -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - drivers/platform/x86/intel/hid.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c -index f25a427cccda..9c07a7faf18f 100644 ---- a/drivers/platform/x86/intel/hid.c -+++ b/drivers/platform/x86/intel/hid.c -@@ -55,6 +55,7 @@ static const struct acpi_device_id intel_hid_ids[] = { - { "INTC10CB" }, - { "INTC10CC" }, - { "INTC10F1" }, -+ { "INTC10F2" }, - { } - }; - MODULE_DEVICE_TABLE(acpi, intel_hid_ids); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo b/SPECS/kernel-rt/0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo new file mode 100644 index 000000000..c81f3896c --- /dev/null +++ b/SPECS/kernel-rt/0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo @@ -0,0 +1,126 @@ +From 585bad8fa06a1be2c4016ed6ea7cfdba37a4f69a Mon Sep 17 00:00:00 2001 +From: Kaushlendra Kumar +Date: Fri, 3 Oct 2025 16:33:19 +0530 +Subject: [PATCH 11/21] tools/power x86_energy_perf_policy: Add Android MSR + device support + +Add support for Android MSR device paths which use /dev/msrN format +instead of the standard Linux /dev/cpu/N/msr format. The tool now +probes both path formats at startup and uses the appropriate one. + +This enables x86_energy_perf_policy to work on Android systems where +MSR devices follow a different naming convention while maintaining +full compatibility with standard Linux systems. + +Signed-off-by: Kaushlendra Kumar +Signed-off-by: Len Brown +--- + .../x86_energy_perf_policy.c | 54 ++++++++++++++++--- + 1 file changed, 46 insertions(+), 8 deletions(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index 884a4c746f32e..5301efc741cee 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -95,6 +95,9 @@ unsigned int bdx_highest_ratio; + #define PATH_TO_CPU "/sys/devices/system/cpu/" + #define SYSFS_PATH_MAX 255 + ++/* keep Default as a linux path */ ++static int use_android_msr_path; ++ + /* + * maintain compatibility with original implementation, but don't document it: + */ +@@ -678,16 +681,41 @@ void err_on_hypervisor(void) + "not supported on this virtual machine"); + } + ++static void probe_msr_path_format(void) ++{ ++ struct stat sb; ++ char test_path[32]; ++ ++ /* Test standard Linux path */ ++ sprintf(test_path, "/dev/cpu/%d/msr", base_cpu); ++ if (stat(test_path, &sb) == 0) { ++ use_android_msr_path = 0; ++ return; ++ } ++ ++ /* Test Android-style path */ ++ sprintf(test_path, "/dev/msr%d", base_cpu); ++ if (stat(test_path, &sb) == 0) { ++ use_android_msr_path = 1; ++ return; ++ } ++ ++ /* If neither exists, keep the default Linux format */ ++ use_android_msr_path = 0; ++} ++ + int get_msr(int cpu, int offset, unsigned long long *msr) + { + int retval; + char pathname[32]; + int fd; + +- sprintf(pathname, "/dev/cpu/%d/msr", cpu); ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDONLY); + if (fd < 0) +- err(-1, "%s open failed, try chown or chmod +r /dev/cpu/*/msr, or run as root", pathname); ++ err(-1, "%s open failed, try chown or chmod +r %s, or run as root", ++ pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); ++ + + retval = pread(fd, msr, sizeof(*msr), offset); + if (retval != sizeof(*msr)) { +@@ -708,10 +736,11 @@ int put_msr(int cpu, int offset, unsigned long long new_msr) + int retval; + int fd; + +- sprintf(pathname, "/dev/cpu/%d/msr", cpu); ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDWR); + if (fd < 0) +- err(-1, "%s open failed, try chown or chmod +r /dev/cpu/*/msr, or run as root", pathname); ++ err(-1, "%s open failed, try chown or chmod +r %s, or run as root", ++ pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); + + retval = pwrite(fd, &new_msr, sizeof(new_msr), offset); + if (retval != sizeof(new_msr)) +@@ -1427,10 +1456,15 @@ void probe_dev_msr(void) + struct stat sb; + char pathname[32]; + +- sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); +- if (stat(pathname, &sb)) +- if (system("/sbin/modprobe msr > /dev/null 2>&1")) +- err(-5, "no /dev/cpu/0/msr, Try \"# modprobe msr\" "); ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu); ++ if (stat(pathname, &sb)) { ++ if (system("/sbin/modprobe msr > /dev/null 2>&1")) { ++ if (use_android_msr_path) ++ err(-5, "no /dev/msr0, Try \"# modprobe msr\" "); ++ else ++ err(-5, "no /dev/cpu/0/msr, Try \"# modprobe msr\" "); ++ } ++ } + } + + static void get_cpuid_or_exit(unsigned int leaf, +@@ -1547,6 +1581,10 @@ void parse_cpuid(void) + int main(int argc, char **argv) + { + set_base_cpu(); ++ ++ /* probe MSR path */ ++ probe_msr_path_format(); ++ + probe_dev_msr(); + init_data_structures(); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac b/SPECS/kernel-rt/0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac deleted file mode 100644 index 039571548..000000000 --- a/SPECS/kernel-rt/0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac +++ /dev/null @@ -1,104 +0,0 @@ -From 1a38a5e8463321de4e625950e55003cd06e20082 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Fri, 10 May 2019 23:04:28 +0800 -Subject: [PATCH 12/13] EDAC/igen6: Add registration APIs for In-Band ECC error - notification - -The igen6_edac driver is the root to capture the In-Band ECC error -event. There are some external modules which want to be notified about -the In-Band ECC errors for specific error handling. So add the -registration APIs for those external modules for the In-Band ECC errors. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/igen6_edac.c | 23 +++++++++++++++++++++++ - drivers/edac/igen6_edac.h | 22 ++++++++++++++++++++++ - 2 files changed, 45 insertions(+) - create mode 100644 drivers/edac/igen6_edac.h - -diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c -index 2fc59f9eed69..3ec58cae8d2b 100644 ---- a/drivers/edac/igen6_edac.c -+++ b/drivers/edac/igen6_edac.c -@@ -26,6 +26,7 @@ - - #include "edac_mc.h" - #include "edac_module.h" -+#include "igen6_edac.h" - - #define IGEN6_REVISION "v2.5.1" - -@@ -641,6 +642,20 @@ static struct pci_device_id igen6_pci_tbl[] = { - }; - MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); - -+static BLOCKING_NOTIFIER_HEAD(ibecc_err_handler_chain); -+ -+int ibecc_err_register_notifer(struct notifier_block *nb) -+{ -+ return blocking_notifier_chain_register(&ibecc_err_handler_chain, nb); -+} -+EXPORT_SYMBOL_GPL(ibecc_err_register_notifer); -+ -+int ibecc_err_unregister_notifer(struct notifier_block *nb) -+{ -+ return blocking_notifier_chain_unregister(&ibecc_err_handler_chain, nb); -+} -+EXPORT_SYMBOL_GPL(ibecc_err_unregister_notifer); -+ - static enum dev_type get_width(int dimm_l, u32 mad_dimm) - { - u32 w = dimm_l ? MAD_DIMM_CH_DLW(mad_dimm) : -@@ -758,6 +773,7 @@ static void igen6_output_error(struct decoded_addr *res, - enum hw_event_mc_err_type type = ecclog & ECC_ERROR_LOG_UE ? - HW_EVENT_ERR_UNCORRECTED : - HW_EVENT_ERR_CORRECTED; -+ struct ibecc_err_info e; - - edac_mc_handle_error(type, mci, 1, - res->sys_addr >> PAGE_SHIFT, -@@ -765,6 +781,13 @@ static void igen6_output_error(struct decoded_addr *res, - ECC_ERROR_LOG_SYND(ecclog), - res->channel_idx, res->sub_channel_idx, - -1, "", ""); -+ -+ /* Notify other handlers for further IBECC error handling */ -+ memset(&e, 0, sizeof(e)); -+ e.type = type; -+ e.sys_addr = res->sys_addr; -+ e.ecc_log = ecclog; -+ blocking_notifier_call_chain(&ibecc_err_handler_chain, 0, &e); - } - - static struct gen_pool *ecclog_gen_pool_create(void) -diff --git a/drivers/edac/igen6_edac.h b/drivers/edac/igen6_edac.h -new file mode 100644 -index 000000000000..ca447593bdf8 ---- /dev/null -+++ b/drivers/edac/igen6_edac.h -@@ -0,0 +1,22 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Registration for IBECC error notification -+ * Copyright (C) 2020 Intel Corporation -+ */ -+ -+#ifndef _IGEN6_EDAC_H -+#define _IGEN6_EDAC_H -+ -+#include -+#include -+ -+struct ibecc_err_info { -+ enum hw_event_mc_err_type type; -+ u64 sys_addr; -+ u64 ecc_log; -+}; -+ -+int ibecc_err_register_notifer(struct notifier_block *nb); -+int ibecc_err_unregister_notifer(struct notifier_block *nb); -+ -+#endif /* _IGEN6_EDAC_H */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0012-KVM-VMX-Virtualize-FRED-event_data.nmi b/SPECS/kernel-rt/0012-KVM-VMX-Virtualize-FRED-event_data.nmi new file mode 100644 index 000000000..cd0a36a54 --- /dev/null +++ b/SPECS/kernel-rt/0012-KVM-VMX-Virtualize-FRED-event_data.nmi @@ -0,0 +1,224 @@ +From dc37448492c27bcfa1f0e6c2924988e5795a948c Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 13 Jun 2024 09:07:46 -0700 +Subject: [PATCH 12/44] KVM: VMX: Virtualize FRED event_data + +Set injected-event data when injecting a #PF, #DB, or #NM caused +by extended feature disable using FRED event delivery, and save +original-event data for being used as injected-event data. + +Unlike IDT using some extra CPU register as part of an event +context, e.g., %cr2 for #PF, FRED saves a complete event context +in its stack frame, e.g., FRED saves the faulting linear address +of a #PF into the event data field defined in its stack frame. + +Thus a new VMX control field called injected-event data is added +to provide the event data that will be pushed into a FRED stack +frame for VM entries that inject an event using FRED event delivery. +In addition, a new VM exit information field called original-event +data is added to store the event data that would have saved into a +FRED stack frame for VM exits that occur during FRED event delivery. +After such a VM exit is handled to allow the original-event to be +delivered, the data in the original-event data VMCS field needs to +be set into the injected-event data VMCS field for the injection of +the original event. + +Signed-off-by: Xin Li +[ Sean: reworked event data injection for nested ] +Signed-off-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v3: +* Rework event data injection for nested (Chao Gao & Sean Christopherson). + +Changes in v2: +* Document event data should be equal to CR2/DR6/IA32_XFD_ERR instead + of using WARN_ON() (Chao Gao). +* Zero event data if a #NM was not caused by extended feature disable + (Chao Gao). +--- + arch/x86/include/asm/kvm_host.h | 3 ++- + arch/x86/include/asm/vmx.h | 4 ++++ + arch/x86/kvm/svm/svm.c | 2 +- + arch/x86/kvm/vmx/vmx.c | 22 ++++++++++++++++++---- + arch/x86/kvm/x86.c | 16 +++++++++++++++- + 5 files changed, 40 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 43a18e265289b..550a8716a2272 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -760,6 +760,7 @@ struct kvm_queued_exception { + u32 error_code; + unsigned long payload; + bool has_payload; ++ u64 event_data; + }; + + /* +@@ -2230,7 +2231,7 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); + void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); + void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code); ++ bool has_error_code, u32 error_code, u64 event_data); + void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); + void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault); +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index 6f8b8947c60cd..539af190ad3e5 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -269,8 +269,12 @@ enum vmcs_field { + PID_POINTER_TABLE_HIGH = 0x00002043, + SECONDARY_VM_EXIT_CONTROLS = 0x00002044, + SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, ++ INJECTED_EVENT_DATA = 0x00002052, ++ INJECTED_EVENT_DATA_HIGH = 0x00002053, + GUEST_PHYSICAL_ADDRESS = 0x00002400, + GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, ++ ORIGINAL_EVENT_DATA = 0x00002404, ++ ORIGINAL_EVENT_DATA_HIGH = 0x00002405, + VMCS_LINK_POINTER = 0x00002800, + VMCS_LINK_POINTER_HIGH = 0x00002801, + GUEST_IA32_DEBUGCTL = 0x00002802, +diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c +index 9d29b2e7e855d..147b644488f21 100644 +--- a/arch/x86/kvm/svm/svm.c ++++ b/arch/x86/kvm/svm/svm.c +@@ -4095,7 +4095,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) + + kvm_requeue_exception(vcpu, vector, + exitintinfo & SVM_EXITINTINFO_VALID_ERR, +- error_code); ++ error_code, 0); + break; + } + case SVM_EXITINTINFO_TYPE_INTR: +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index b51f2c4fcdb8c..5cefe7024ac6e 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1860,6 +1860,9 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) + + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); + ++ if (is_fred_enabled(vcpu)) ++ vmcs_write64(INJECTED_EVENT_DATA, ex->event_data); ++ + vmx_clear_hlt(vcpu); + } + +@@ -7288,7 +7291,8 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) + static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + u32 idt_vectoring_info, + int instr_len_field, +- int error_code_field) ++ int error_code_field, ++ int event_data_field) + { + u8 vector; + int type; +@@ -7323,13 +7327,17 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + fallthrough; + case INTR_TYPE_HARD_EXCEPTION: { + u32 error_code = 0; ++ u64 event_data = 0; + + if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK) + error_code = vmcs_read32(error_code_field); ++ if (is_fred_enabled(vcpu)) ++ event_data = vmcs_read64(event_data_field); + + kvm_requeue_exception(vcpu, vector, + idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, +- error_code); ++ error_code, ++ event_data); + break; + } + case INTR_TYPE_SOFT_INTR: +@@ -7347,7 +7355,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx) + { + __vmx_complete_interrupts(&vmx->vcpu, vmx->idt_vectoring_info, + VM_EXIT_INSTRUCTION_LEN, +- IDT_VECTORING_ERROR_CODE); ++ IDT_VECTORING_ERROR_CODE, ++ ORIGINAL_EVENT_DATA); + } + + void vmx_cancel_injection(struct kvm_vcpu *vcpu) +@@ -7355,7 +7364,8 @@ void vmx_cancel_injection(struct kvm_vcpu *vcpu) + __vmx_complete_interrupts(vcpu, + vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), + VM_ENTRY_INSTRUCTION_LEN, +- VM_ENTRY_EXCEPTION_ERROR_CODE); ++ VM_ENTRY_EXCEPTION_ERROR_CODE, ++ INJECTED_EVENT_DATA); + + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); + } +@@ -7509,6 +7519,10 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, + + vmx_disable_fb_clear(vmx); + ++ /* ++ * Note, even though FRED delivers the faulting linear address via the ++ * event data field on the stack, CR2 is still updated. ++ */ + if (vcpu->arch.cr2 != native_read_cr2()) + native_write_cr2(vcpu->arch.cr2); + +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index af7543e7c8063..71b651d4567ff 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -815,9 +815,22 @@ void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu, + * breakpoint), it is reserved and must be zero in DR6. + */ + vcpu->arch.dr6 &= ~BIT(12); ++ ++ /* ++ * FRED #DB event data matches DR6, but follows the polarity of ++ * VMX's pending debug exceptions, not DR6. ++ */ ++ ex->event_data = ex->payload & ~BIT(12); ++ break; ++ case NM_VECTOR: ++ ex->event_data = ex->payload; + break; + case PF_VECTOR: + vcpu->arch.cr2 = ex->payload; ++ ex->event_data = ex->payload; ++ break; ++ default: ++ ex->event_data = 0; + break; + } + +@@ -925,7 +938,7 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, + } + + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code) ++ bool has_error_code, u32 error_code, u64 event_data) + { + + /* +@@ -950,6 +963,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.error_code = error_code; + vcpu->arch.exception.has_payload = false; + vcpu->arch.exception.payload = 0; ++ vcpu->arch.exception.event_data = event_data; + } + EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi b/SPECS/kernel-rt/0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi deleted file mode 100644 index 917bc7561..000000000 --- a/SPECS/kernel-rt/0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi +++ /dev/null @@ -1,206 +0,0 @@ -From 45ca6a4965644bb66e47eaad8c677d32d69aec6e Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 13 Jun 2024 09:48:43 -0700 -Subject: [PATCH 12/44] KVM: VMX: Virtualize FRED nested exception tracking - -Set the VMX nested exception bit in VM-entry interruption information -field when injecting a nested exception using FRED event delivery to -ensure: - 1) A nested exception is injected on a correct stack level. - 2) The nested bit defined in FRED stack frame is set. - -The event stack level used by FRED event delivery depends on whether -the event was a nested exception encountered during delivery of an -earlier event, because a nested exception is "regarded" as happening -on ring 0. E.g., when #PF is configured to use stack level 1 in -IA32_FRED_STKLVLS MSR: - - nested #PF will be delivered on the stack pointed by IA32_FRED_RSP1 - MSR when encountered in ring 3 and ring 0. - - normal #PF will be delivered on the stack pointed by IA32_FRED_RSP0 - MSR when encountered in ring 3. - -The VMX nested-exception support ensures a correct event stack level is -chosen when a VM entry injects a nested exception. - -Signed-off-by: Xin Li -[ Sean: reworked kvm_requeue_exception() to simply the code changes ] -Signed-off-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Move the check is_fred_enable() from kvm_multiple_exception() to - vmx_inject_exception() thus avoid bleeding FRED details into - kvm_multiple_exception() (Chao Gao). - -Change in v3: -* Rework kvm_requeue_exception() to simply the code changes (Sean - Christopherson). - -Change in v2: -* Set the nested flag when there is an original interrupt (Chao Gao). ---- - arch/x86/include/asm/kvm_host.h | 4 +++- - arch/x86/include/asm/vmx.h | 5 ++++- - arch/x86/kvm/svm/svm.c | 2 +- - arch/x86/kvm/vmx/vmx.c | 6 +++++- - arch/x86/kvm/x86.c | 13 ++++++++++++- - arch/x86/kvm/x86.h | 1 + - 6 files changed, 26 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 40d2c5e1b7e7..0d4cdb704f97 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -761,6 +761,7 @@ struct kvm_queued_exception { - u32 error_code; - unsigned long payload; - bool has_payload; -+ bool nested; - u64 event_data; - }; - -@@ -2209,7 +2210,8 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); - void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); - void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code, u64 event_data); -+ bool has_error_code, u32 error_code, bool nested, -+ u64 event_data); - void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); - void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault); -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index 613deb8cfb78..f766791367d2 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -141,6 +141,7 @@ - #define VMX_BASIC_INOUT BIT_ULL(54) - #define VMX_BASIC_TRUE_CTLS BIT_ULL(55) - #define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56) -+#define VMX_BASIC_NESTED_EXCEPTION BIT_ULL(58) - - static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic) - { -@@ -443,13 +444,15 @@ enum vmcs_field { - #define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */ - #define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */ - #define INTR_INFO_UNBLOCK_NMI 0x1000 /* 12 */ -+#define INTR_INFO_NESTED_EXCEPTION_MASK 0x2000 /* 13 */ - #define INTR_INFO_VALID_MASK 0x80000000 /* 31 */ --#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000 -+#define INTR_INFO_RESVD_BITS_MASK 0x7fffd000 - - #define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK - #define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK - #define VECTORING_INFO_DELIVER_CODE_MASK INTR_INFO_DELIVER_CODE_MASK - #define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK -+#define VECTORING_INFO_NESTED_EXCEPTION_MASK INTR_INFO_NESTED_EXCEPTION_MASK - - #define INTR_TYPE_EXT_INTR (EVENT_TYPE_EXTINT << 8) /* external interrupt */ - #define INTR_TYPE_RESERVED (EVENT_TYPE_RESERVED << 8) /* reserved */ -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 8ee9f66a34f8..5408f84fa263 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4187,7 +4187,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) - - kvm_requeue_exception(vcpu, vector, - exitintinfo & SVM_EXITINTINFO_VALID_ERR, -- error_code, 0); -+ error_code, false, 0); - break; - } - case SVM_EXITINTINFO_TYPE_INTR: -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 89841d2f3e45..f225778115dc 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1859,8 +1859,11 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) - vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, - vmx->vcpu.arch.event_exit_inst_len); - intr_info |= INTR_TYPE_SOFT_EXCEPTION; -- } else -+ } else { - intr_info |= INTR_TYPE_HARD_EXCEPTION; -+ if (ex->nested && is_fred_enabled(vcpu)) -+ intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; -+ } - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); - -@@ -7358,6 +7361,7 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - kvm_requeue_exception(vcpu, vector, - idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, - error_code, -+ idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK, - event_data); - break; - } -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 090f9fc0363f..6029125a518d 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -874,6 +874,10 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.pending = true; - vcpu->arch.exception.injected = false; - -+ vcpu->arch.exception.nested = vcpu->arch.exception.nested || -+ vcpu->arch.nmi_injected || -+ vcpu->arch.interrupt.injected; -+ - vcpu->arch.exception.has_error_code = has_error; - vcpu->arch.exception.vector = nr; - vcpu->arch.exception.error_code = error_code; -@@ -903,8 +907,13 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.injected = false; - vcpu->arch.exception.pending = false; - -+ /* #DF is NOT a nested event, per its definition. */ -+ vcpu->arch.exception.nested = false; -+ - kvm_queue_exception_e(vcpu, DF_VECTOR, 0); - } else { -+ vcpu->arch.exception.nested = true; -+ - /* replace previous exception with a new one in a hope - that instruction re-execution will regenerate lost - exception */ -@@ -933,7 +942,8 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, - } - - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code, u64 event_data) -+ bool has_error_code, u32 error_code, bool nested, -+ u64 event_data) - { - - /* -@@ -958,6 +968,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.error_code = error_code; - vcpu->arch.exception.has_payload = false; - vcpu->arch.exception.payload = 0; -+ vcpu->arch.exception.nested = nested; - vcpu->arch.exception.event_data = event_data; - } - EXPORT_SYMBOL_GPL(kvm_requeue_exception); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index b12749a2a67c..25c2d7173b89 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -198,6 +198,7 @@ static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu) - { - vcpu->arch.exception.pending = false; - vcpu->arch.exception.injected = false; -+ vcpu->arch.exception.nested = false; - vcpu->arch.exception_vmexit.pending = false; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet b/SPECS/kernel-rt/0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet deleted file mode 100644 index 17f5254b6..000000000 --- a/SPECS/kernel-rt/0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet +++ /dev/null @@ -1,56 +0,0 @@ -From 2bc8dfbeccd96bc388c97ea050a6c4d524293f6e Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:42 -0700 -Subject: [PATCH 12/24] KVM: x86: Report KVM supported CET MSRs as to-be-saved - -Add CET MSRs to the list of MSRs reported to userspace if the feature, -i.e. IBT or SHSTK, associated with the MSRs is supported by KVM. - -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index fbe3d1bea657..cbefc0228ba5 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -345,6 +345,10 @@ static const u32 msrs_to_save_base[] = { - MSR_IA32_UMWAIT_CONTROL, - - MSR_IA32_XFD, MSR_IA32_XFD_ERR, MSR_IA32_XSS, -+ -+ MSR_IA32_U_CET, MSR_IA32_S_CET, -+ MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP, -+ MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB, - }; - - static const u32 msrs_to_save_pmu[] = { -@@ -7472,6 +7476,20 @@ static void kvm_probe_msr_to_save(u32 msr_index) - if (!kvm_caps.supported_xss) - return; - break; -+ case MSR_IA32_U_CET: -+ case MSR_IA32_S_CET: -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && -+ !kvm_cpu_cap_has(X86_FEATURE_IBT)) -+ return; -+ break; -+ case MSR_IA32_INT_SSP_TAB: -+ if (!kvm_cpu_cap_has(X86_FEATURE_LM)) -+ return; -+ fallthrough; -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK)) -+ return; -+ break; - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0012-comedi-check-device-s-attached-status-in-compat-ioct.patch b/SPECS/kernel-rt/0012-comedi-check-device-s-attached-status-in-compat-ioct.patch deleted file mode 100644 index 107d3287d..000000000 --- a/SPECS/kernel-rt/0012-comedi-check-device-s-attached-status-in-compat-ioct.patch +++ /dev/null @@ -1,147 +0,0 @@ -From b6d371e15f5bc91054b76f685309c33bfb5c7335 Mon Sep 17 00:00:00 2001 -From: Nikita Zhandarovich -Date: Thu, 23 Oct 2025 16:22:32 +0300 -Subject: [PATCH 12/16] comedi: check device's attached status in compat ioctls - -Syzbot identified an issue [1] that crashes kernel, seemingly due to -unexistent callback dev->get_valid_routes(). By all means, this should -not occur as said callback must always be set to -get_zero_valid_routes() in __comedi_device_postconfig(). - -As the crash seems to appear exclusively in i386 kernels, at least, -judging from [1] reports, the blame lies with compat versions -of standard IOCTL handlers. Several of them are modified and -do not use comedi_unlocked_ioctl(). While functionality of these -ioctls essentially copy their original versions, they do not -have required sanity check for device's attached status. This, -in turn, leads to a possibility of calling select IOCTLs on a -device that has not been properly setup, even via COMEDI_DEVCONFIG. - -Doing so on unconfigured devices means that several crucial steps -are missed, for instance, specifying dev->get_valid_routes() -callback. - -Fix this somewhat crudely by ensuring device's attached status before -performing any ioctls, improving logic consistency between modern -and compat functions. - -[1] Syzbot report: -BUG: kernel NULL pointer dereference, address: 0000000000000000 -... -CR2: ffffffffffffffd6 CR3: 000000006c717000 CR4: 0000000000352ef0 -Call Trace: - - get_valid_routes drivers/comedi/comedi_fops.c:1322 [inline] - parse_insn+0x78c/0x1970 drivers/comedi/comedi_fops.c:1401 - do_insnlist_ioctl+0x272/0x700 drivers/comedi/comedi_fops.c:1594 - compat_insnlist drivers/comedi/comedi_fops.c:3208 [inline] - comedi_compat_ioctl+0x810/0x990 drivers/comedi/comedi_fops.c:3273 - __do_compat_sys_ioctl fs/ioctl.c:695 [inline] - __se_compat_sys_ioctl fs/ioctl.c:638 [inline] - __ia32_compat_sys_ioctl+0x242/0x370 fs/ioctl.c:638 - do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline] -... - -Reported-by: syzbot+ab8008c24e84adee93ff@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=ab8008c24e84adee93ff -Fixes: 3fbfd2223a27 ("comedi: get rid of compat_alloc_user_space() mess in COMEDI_CHANINFO compat") -Cc: stable -Reviewed-by: Ian Abbott -Signed-off-by: Nikita Zhandarovich -Link: https://patch.msgid.link/20251023132234.395794-1-n.zhandarovich@fintech.ru -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/comedi_fops.c | 42 ++++++++++++++++++++++++++++++------ - 1 file changed, 36 insertions(+), 6 deletions(-) - -diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c -index 7e2f2b1a1c36..b2e62e04afd9 100644 ---- a/drivers/comedi/comedi_fops.c -+++ b/drivers/comedi/comedi_fops.c -@@ -3023,7 +3023,12 @@ static int compat_chaninfo(struct file *file, unsigned long arg) - chaninfo.rangelist = compat_ptr(chaninfo32.rangelist); - - mutex_lock(&dev->mutex); -- err = do_chaninfo_ioctl(dev, &chaninfo); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ err = -ENODEV; -+ } else { -+ err = do_chaninfo_ioctl(dev, &chaninfo); -+ } - mutex_unlock(&dev->mutex); - return err; - } -@@ -3044,7 +3049,12 @@ static int compat_rangeinfo(struct file *file, unsigned long arg) - rangeinfo.range_ptr = compat_ptr(rangeinfo32.range_ptr); - - mutex_lock(&dev->mutex); -- err = do_rangeinfo_ioctl(dev, &rangeinfo); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ err = -ENODEV; -+ } else { -+ err = do_rangeinfo_ioctl(dev, &rangeinfo); -+ } - mutex_unlock(&dev->mutex); - return err; - } -@@ -3120,7 +3130,12 @@ static int compat_cmd(struct file *file, unsigned long arg) - return rc; - - mutex_lock(&dev->mutex); -- rc = do_cmd_ioctl(dev, &cmd, ©, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_cmd_ioctl(dev, &cmd, ©, file); -+ } - mutex_unlock(&dev->mutex); - if (copy) { - /* Special case: copy cmd back to user. */ -@@ -3145,7 +3160,12 @@ static int compat_cmdtest(struct file *file, unsigned long arg) - return rc; - - mutex_lock(&dev->mutex); -- rc = do_cmdtest_ioctl(dev, &cmd, ©, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_cmdtest_ioctl(dev, &cmd, ©, file); -+ } - mutex_unlock(&dev->mutex); - if (copy) { - err = put_compat_cmd(compat_ptr(arg), &cmd); -@@ -3205,7 +3225,12 @@ static int compat_insnlist(struct file *file, unsigned long arg) - } - - mutex_lock(&dev->mutex); -- rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file); -+ } - mutex_unlock(&dev->mutex); - kfree(insns); - return rc; -@@ -3224,7 +3249,12 @@ static int compat_insn(struct file *file, unsigned long arg) - return rc; - - mutex_lock(&dev->mutex); -- rc = do_insn_ioctl(dev, &insn, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_insn_ioctl(dev, &insn, file); -+ } - mutex_unlock(&dev->mutex); - return rc; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl b/SPECS/kernel-rt/0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl new file mode 100644 index 000000000..c09bae034 --- /dev/null +++ b/SPECS/kernel-rt/0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl @@ -0,0 +1,127 @@ +From 5e1834e3a4a2efe85b595f96a462230bf54c772d Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Sun, 16 Nov 2025 13:35:14 +0100 +Subject: [PATCH 12/17] cpuidle: governors: teo: Simplify intercepts-based + state lookup + +Simplify the loop looking up a candidate idle state in the case when an +intercept is likely to occur by adding a search for the state index limit +if the tick is stopped before it. + +First, call tick_nohz_tick_stopped() just once and if it returns true, +look for the shallowest state index below the current candidate one with +target residency at least equal to the tick period length. + +Next, simply look for a state that is not shallower than the one found +in the previous step and satisfies the intercepts majority condition (if +there are no such states, the shallowest state that is not shallower +than the one found in the previous step becomes the new candidate). + +Since teo_state_ok() has no callers any more after the above changes, +drop it. + +No intentional functional impact. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +[ rjw: Changelog clarification and code comment edit ] +Link: https://patch.msgid.link/2418792.ElGaqSPkdT@rafael.j.wysocki +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/governors/teo.c | 62 +++++++++------------------------ + 1 file changed, 16 insertions(+), 46 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 85b5517067d16..bab186336bf4a 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -256,12 +256,6 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + } + } + +-static bool teo_state_ok(int i, struct cpuidle_driver *drv) +-{ +- return !tick_nohz_tick_stopped() || +- drv->states[i].target_residency_ns >= TICK_NSEC; +-} +- + /** + * teo_find_shallower_state - Find shallower idle state matching given duration. + * @drv: cpuidle driver containing state data. +@@ -383,7 +377,18 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + * better choice. + */ + if (2 * idx_intercept_sum > cpu_data->total - idx_hit_sum) { +- int first_suitable_idx = idx; ++ int min_idx = idx0; ++ ++ if (tick_nohz_tick_stopped()) { ++ /* ++ * Look for the shallowest idle state below the current ++ * candidate one whose target residency is at least ++ * equal to the tick period length. ++ */ ++ while (min_idx < idx && ++ drv->states[min_idx].target_residency_ns < TICK_NSEC) ++ min_idx++; ++ } + + /* + * Look for the deepest idle state whose target residency had +@@ -393,49 +398,14 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + * Take the possible duration limitation present if the tick + * has been stopped already into account. + */ +- intercept_sum = 0; +- +- for (i = idx - 1; i >= 0; i--) { +- struct teo_bin *bin = &cpu_data->state_bins[i]; +- +- intercept_sum += bin->intercepts; +- +- if (2 * intercept_sum > idx_intercept_sum) { +- /* +- * Use the current state unless it is too +- * shallow or disabled, in which case take the +- * first enabled state that is deep enough. +- */ +- if (teo_state_ok(i, drv) && +- !dev->states_usage[i].disable) { +- idx = i; +- break; +- } +- idx = first_suitable_idx; +- break; +- } ++ for (i = idx - 1, intercept_sum = 0; i >= min_idx; i--) { ++ intercept_sum += cpu_data->state_bins[i].intercepts; + + if (dev->states_usage[i].disable) + continue; + +- if (teo_state_ok(i, drv)) { +- /* +- * The current state is deep enough, but still +- * there may be a better one. +- */ +- first_suitable_idx = i; +- continue; +- } +- +- /* +- * The current state is too shallow, so if no suitable +- * states other than the initial candidate have been +- * found, give up (the remaining states to check are +- * shallower still), but otherwise the first suitable +- * state other than the initial candidate may turn out +- * to be preferable. +- */ +- if (first_suitable_idx == idx) ++ idx = i; ++ if (2 * intercept_sum > idx_intercept_sum) + break; + } + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet b/SPECS/kernel-rt/0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet new file mode 100644 index 000000000..7629ce038 --- /dev/null +++ b/SPECS/kernel-rt/0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet @@ -0,0 +1,36 @@ +From cea39eca48e8b06d14799ad9d289adc27e03a144 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Fri, 23 Jul 2021 21:27:36 +0800 +Subject: [PATCH 12/14] igc: Enable trace for HW TX Timestamp AF_XDP ZC + +This is a temporary solution as it uses trace_printk as a means to +log tx timestamps. + +Future implementation should use xdp_frame's data_meta to let user +applications retrieve it directly. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc_main.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index cabdcacc4c923..571cc380de5ad 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -3214,6 +3214,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + + switch (tx_buffer->type) { + case IGC_TX_BUFFER_TYPE_XSK: ++#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) ++ /* Only use for RTCP KPI Measurement on Q2 */ ++ if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) ++ trace_printk("TX HW TS %lld\n", timestamp); ++#endif + xsk_frames++; + break; + case IGC_TX_BUFFER_TYPE_XDP: +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu b/SPECS/kernel-rt/0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu deleted file mode 100644 index 2499a2c40..000000000 --- a/SPECS/kernel-rt/0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu +++ /dev/null @@ -1,87 +0,0 @@ -From e2baf64f19d5e0b8d76986e1647ff0c894f570ee Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 14 Nov 2025 18:41:08 +0800 -Subject: [PATCH 12/21] media: ipu: Update firmware ABI version to - 1.2.1.20251215_224531 - -Signed-off-by: Hao Yao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h | 1 + - drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 11 +++++++---- - drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h | 2 +- - 3 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h -index a1519c4fe661..4ce304f54e4b 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h -@@ -153,6 +153,7 @@ enum ia_gofo_boot_state { - IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, - IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, - IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_FAILURE = 0xDEAD1016U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, - IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -index 45db85eb13ec..dc63449e3bc1 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -@@ -47,7 +47,6 @@ enum ipu7_insys_resp_type { - IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, - IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, - IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, -- IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, - N_IPU_INSYS_RESP_TYPE - }; - -@@ -201,7 +200,8 @@ enum ipu7_insys_mipi_dt_rename_mode { - enum ipu7_insys_output_link_dest { - IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, - IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, -- IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 -+ IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2, -+ N_IPU_INSYS_OUTPUT_LINK_DEST - }; - - enum ipu7_insys_dpcm_type { -@@ -220,9 +220,12 @@ enum ipu7_insys_dpcm_predictor { - - enum ipu7_insys_send_queue_token_flag { - IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, -- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 -+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1, -+ N_IPU_INSYS_SEND_QUEUE_TOKEN_FLAG - }; - -+#define IPU_INSYS_MIPI_FRAME_NUMBER_DONT_CARE UINT16_MAX -+ - #pragma pack(push, 1) - struct ipu7_insys_resolution { - u32 width; -@@ -359,7 +362,7 @@ struct ipu7_insys_resp { - u8 pin_id; - u8 frame_id; - u8 skip_frame; -- u8 pad[2]; -+ u16 mipi_fn; - }; - - struct ipu7_insys_resp_queue_token { -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h -index 8a78dd0936df..1319f0eb6319 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h -@@ -217,7 +217,7 @@ struct ipu7_msg_task { - u8 frag_id; - u8 req_done_msg; - u8 req_done_irq; -- u8 reserved[1]; -+ u8 disable_save; - ipu7_msg_teb_t payload_reuse_bm; - ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; - }; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0012-net-phylink-Add-module_exit.ethernet b/SPECS/kernel-rt/0012-net-phylink-Add-module_exit.ethernet new file mode 100644 index 000000000..f16496408 --- /dev/null +++ b/SPECS/kernel-rt/0012-net-phylink-Add-module_exit.ethernet @@ -0,0 +1,39 @@ +From 096687e863b9acf6eeccc25e85c7d8527991eb0c Mon Sep 17 00:00:00 2001 +From: Choong Yong Liang +Date: Wed, 3 Jan 2024 14:59:29 +0800 +Subject: [PATCH 12/18] net: phylink: Add module_exit() + +In free_module(), if mod->init callback is defined but mod->exit callback +is not defined, it will assume the module cannot be removed and return +EBUSY. The module_exit() is missing from current phylink module drive +causing failure while unloading it. + +This patch introduces phylink_exit() for phylink module removal. + +Fixes: eca68a3c7d05 ("net: phylink: pass supported host PHY interface modes to phylib for SFP's PHYs") +Signed-off-by: Lai Peter Jun Ann +Signed-off-by: Gan, Yi Fang +Signed-off-by: Choong Yong Liang +--- + drivers/net/phy/phylink.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 9182443082158..e9d0dcfa4c7b1 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -4285,5 +4285,11 @@ static int __init phylink_init(void) + + module_init(phylink_init); + ++static void __exit phylink_exit(void) ++{ ++} ++ ++module_exit(phylink_exit); ++ + MODULE_LICENSE("GPL v2"); + MODULE_DESCRIPTION("phylink models the MAC to optional PHY connection"); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf b/SPECS/kernel-rt/0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf new file mode 100644 index 000000000..d8c3a17c6 --- /dev/null +++ b/SPECS/kernel-rt/0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf @@ -0,0 +1,60 @@ +From 7302a5d52e119f352b20bff0ea104d1c5f97aecb Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Mon, 17 Nov 2025 15:31:01 -0800 +Subject: [PATCH 12/13] perf pmu: Relax uncore wildcard matching to allow + numeric suffix + +Diamond Rapids introduces two types of PCIe related uncore PMUs: +"uncore_pcie4_*" and "uncore_pcie6_*". + +To ensure that generic PCIe events (e.g., UNC_PCIE_CLOCKTICKS) can match +and collect events from both PMU types, slightly relax the wildcard +matching logic in perf_pmu__match_wildcard(). + +This change allows a wildcard such as "pcie" to match PMU names that +include a numeric suffix, such as "pcie4_*" and "pcie6_*". + +Co-developed-by: Dapeng Mi +Signed-off-by: Dapeng Mi +Reviewed-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + tools/perf/util/pmu.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c +index 3d1f975e8db9f..00cb72615621d 100644 +--- a/tools/perf/util/pmu.c ++++ b/tools/perf/util/pmu.c +@@ -905,6 +905,7 @@ static bool perf_pmu__match_wildcard(const char *pmu_name, const char *tok) + { + const char *p, *suffix; + bool has_hex = false; ++ bool has_underscore = false; + size_t tok_len = strlen(tok); + + /* Check start of pmu_name for equality. */ +@@ -915,13 +916,14 @@ static bool perf_pmu__match_wildcard(const char *pmu_name, const char *tok) + if (*p == 0) + return true; + +- if (*p == '_') { +- ++p; +- ++suffix; +- } +- +- /* Ensure we end in a number */ ++ /* Ensure we end in a number or a mix of number and "_". */ + while (1) { ++ if (!has_underscore && (*p == '_')) { ++ has_underscore = true; ++ ++p; ++ ++suffix; ++ } ++ + if (!isxdigit(*p)) + return false; + if (!has_hex) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf b/SPECS/kernel-rt/0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf deleted file mode 100644 index 5333e3779..000000000 --- a/SPECS/kernel-rt/0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf +++ /dev/null @@ -1,44 +0,0 @@ -From 11bad8db8b8652abe061f50c7973691d58763802 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 25 Jan 2024 11:31:22 +0000 -Subject: [PATCH 012/100] perf/x86/intel: Print more information in - x86_pmu_show_pmu_cap() - -Adjust the PMU message output sequence and print more PMU information in -helper x86_pmu_show_pmu_cap(). - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 16 +++++++++------- - 1 file changed, 9 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 2007cfe1a0f5..24fae86f2682 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -2071,13 +2071,15 @@ static void _x86_pmu_read(struct perf_event *event) - - void x86_pmu_show_pmu_cap(struct pmu *pmu) - { -- pr_info("... version: %d\n", x86_pmu.version); -- pr_info("... bit width: %d\n", x86_pmu.cntval_bits); -- pr_info("... generic registers: %d\n", x86_pmu_num_counters(pmu)); -- pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask); -- pr_info("... max period: %016Lx\n", x86_pmu.max_period); -- pr_info("... fixed-purpose events: %d\n", x86_pmu_num_counters_fixed(pmu)); -- pr_info("... event mask: %016Lx\n", hybrid(pmu, intel_ctrl)); -+ pr_info("... version: %d\n", x86_pmu.version); -+ pr_info("... bit width: %d\n", x86_pmu.cntval_bits); -+ pr_info("... generic counters: %d\n", x86_pmu_num_counters(pmu)); -+ pr_info("... generic bitmap: %016Lx\n", hybrid(pmu, cntr_mask64)); -+ pr_info("... fixed-purpose counters: %d\n", x86_pmu_num_counters_fixed(pmu)); -+ pr_info("... fixed-purpose bitmap: %016Lx\n", hybrid(pmu, fixed_cntr_mask64)); -+ pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask); -+ pr_info("... max period: %016Lx\n", x86_pmu.max_period); -+ pr_info("... global_ctrl mask: %016Lx\n", hybrid(pmu, intel_ctrl)); - } - - static int __init init_hw_perf_events(void) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo b/SPECS/kernel-rt/0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo new file mode 100644 index 000000000..0d3d0dbad --- /dev/null +++ b/SPECS/kernel-rt/0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo @@ -0,0 +1,92 @@ +From 74b6b483cba4e31ffce21e1c5364a14d55dcc8f6 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 25 Nov 2025 11:47:04 -0500 +Subject: [PATCH 12/21] tools/power x86_energy_perf_policy: Simplify Android + MSR probe + +no functional change + +Signed-off-by: Len Brown +--- + .../x86_energy_perf_policy.c | 38 ++++++------------- + 1 file changed, 11 insertions(+), 27 deletions(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index 5301efc741cee..e68eaa9f7cd4d 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -95,7 +95,6 @@ unsigned int bdx_highest_ratio; + #define PATH_TO_CPU "/sys/devices/system/cpu/" + #define SYSFS_PATH_MAX 255 + +-/* keep Default as a linux path */ + static int use_android_msr_path; + + /* +@@ -681,29 +680,6 @@ void err_on_hypervisor(void) + "not supported on this virtual machine"); + } + +-static void probe_msr_path_format(void) +-{ +- struct stat sb; +- char test_path[32]; +- +- /* Test standard Linux path */ +- sprintf(test_path, "/dev/cpu/%d/msr", base_cpu); +- if (stat(test_path, &sb) == 0) { +- use_android_msr_path = 0; +- return; +- } +- +- /* Test Android-style path */ +- sprintf(test_path, "/dev/msr%d", base_cpu); +- if (stat(test_path, &sb) == 0) { +- use_android_msr_path = 1; +- return; +- } +- +- /* If neither exists, keep the default Linux format */ +- use_android_msr_path = 0; +-} +- + int get_msr(int cpu, int offset, unsigned long long *msr) + { + int retval; +@@ -1450,12 +1426,23 @@ void set_base_cpu(void) + err(-ENODEV, "No valid cpus found"); + } + ++static void probe_android_msr_path(void) ++{ ++ struct stat sb; ++ char test_path[32]; ++ ++ sprintf(test_path, "/dev/msr%d", base_cpu); ++ if (stat(test_path, &sb) == 0) ++ use_android_msr_path = 1; ++} + + void probe_dev_msr(void) + { + struct stat sb; + char pathname[32]; + ++ probe_android_msr_path(); ++ + sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu); + if (stat(pathname, &sb)) { + if (system("/sbin/modprobe msr > /dev/null 2>&1")) { +@@ -1582,9 +1569,6 @@ int main(int argc, char **argv) + { + set_base_cpu(); + +- /* probe MSR path */ +- probe_msr_path_format(); +- + probe_dev_msr(); + init_data_structures(); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet b/SPECS/kernel-rt/0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet deleted file mode 100644 index 1bcc082b8..000000000 --- a/SPECS/kernel-rt/0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet +++ /dev/null @@ -1,111 +0,0 @@ -From ed3c1c22e1d76f87fdbc728415b59c472e7baab5 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:43 -0700 -Subject: [PATCH 13/24] KVM: VMX: Introduce CET VMCS fields and control bits - -Control-flow Enforcement Technology (CET) is a kind of CPU feature used -to prevent Return/CALL/Jump-Oriented Programming (ROP/COP/JOP) attacks. -It provides two sub-features(SHSTK,IBT) to defend against ROP/COP/JOP -style control-flow subversion attacks. - -Shadow Stack (SHSTK): - A shadow stack is a second stack used exclusively for control transfer - operations. The shadow stack is separate from the data/normal stack and - can be enabled individually in user and kernel mode. When shadow stack - is enabled, CALL pushes the return address on both the data and shadow - stack. RET pops the return address from both stacks and compares them. - If the return addresses from the two stacks do not match, the processor - generates a #CP. - -Indirect Branch Tracking (IBT): - IBT introduces instruction(ENDBRANCH)to mark valid target addresses of - indirect branches (CALL, JMP etc...). If an indirect branch is executed - and the next instruction is _not_ an ENDBRANCH, the processor generates - a #CP. These instruction behaves as a NOP on platforms that have no CET. - -Several new CET MSRs are defined to support CET: - MSR_IA32_{U,S}_CET: CET settings for {user,supervisor} CET respectively. - - MSR_IA32_PL{0,1,2,3}_SSP: SHSTK pointer linear address for CPL{0,1,2,3}. - - MSR_IA32_INT_SSP_TAB: Linear address of SHSTK pointer table, whose entry - is indexed by IST of interrupt gate desc. - -Two XSAVES state bits are introduced for CET: - IA32_XSS:[bit 11]: Control saving/restoring user mode CET states - IA32_XSS:[bit 12]: Control saving/restoring supervisor mode CET states. - -Six VMCS fields are introduced for CET: - {HOST,GUEST}_S_CET: Stores CET settings for kernel mode. - {HOST,GUEST}_SSP: Stores current active SSP. - {HOST,GUEST}_INTR_SSP_TABLE: Stores current active MSR_IA32_INT_SSP_TAB. - -On Intel platforms, two additional bits are defined in VM_EXIT and VM_ENTRY -control fields: -If VM_EXIT_LOAD_CET_STATE = 1, host CET states are loaded from following -VMCS fields at VM-Exit: - HOST_S_CET - HOST_SSP - HOST_INTR_SSP_TABLE - -If VM_ENTRY_LOAD_CET_STATE = 1, guest CET states are loaded from following -VMCS fields at VM-Entry: - GUEST_S_CET - GUEST_SSP - GUEST_INTR_SSP_TABLE - -Co-developed-by: Zhang Yi Z -Signed-off-by: Zhang Yi Z -Signed-off-by: Yang Weijiang -Reviewed-by: Chao Gao -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/vmx.h | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index af71666c3a37..6a3fb81e852a 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -106,6 +106,7 @@ - #define VM_EXIT_CLEAR_BNDCFGS 0x00800000 - #define VM_EXIT_PT_CONCEAL_PIP 0x01000000 - #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 -+#define VM_EXIT_LOAD_CET_STATE 0x10000000 - #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 - - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff -@@ -120,6 +121,7 @@ - #define VM_ENTRY_LOAD_BNDCFGS 0x00010000 - #define VM_ENTRY_PT_CONCEAL_PIP 0x00020000 - #define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 -+#define VM_ENTRY_LOAD_CET_STATE 0x00100000 - - #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff - -@@ -370,6 +372,9 @@ enum vmcs_field { - GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822, - GUEST_SYSENTER_ESP = 0x00006824, - GUEST_SYSENTER_EIP = 0x00006826, -+ GUEST_S_CET = 0x00006828, -+ GUEST_SSP = 0x0000682a, -+ GUEST_INTR_SSP_TABLE = 0x0000682c, - HOST_CR0 = 0x00006c00, - HOST_CR3 = 0x00006c02, - HOST_CR4 = 0x00006c04, -@@ -382,6 +387,9 @@ enum vmcs_field { - HOST_IA32_SYSENTER_EIP = 0x00006c12, - HOST_RSP = 0x00006c14, - HOST_RIP = 0x00006c16, -+ HOST_S_CET = 0x00006c18, -+ HOST_SSP = 0x00006c1a, -+ HOST_INTR_SSP_TABLE = 0x00006c1c - }; - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi b/SPECS/kernel-rt/0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi new file mode 100644 index 000000000..e41090c24 --- /dev/null +++ b/SPECS/kernel-rt/0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi @@ -0,0 +1,206 @@ +From 7fd175dd0952e80fd3d7f04d10a4926b52780c77 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 13 Jun 2024 09:48:43 -0700 +Subject: [PATCH 13/44] KVM: VMX: Virtualize FRED nested exception tracking + +Set the VMX nested exception bit in VM-entry interruption information +field when injecting a nested exception using FRED event delivery to +ensure: + 1) A nested exception is injected on a correct stack level. + 2) The nested bit defined in FRED stack frame is set. + +The event stack level used by FRED event delivery depends on whether +the event was a nested exception encountered during delivery of an +earlier event, because a nested exception is "regarded" as happening +on ring 0. E.g., when #PF is configured to use stack level 1 in +IA32_FRED_STKLVLS MSR: + - nested #PF will be delivered on the stack pointed by IA32_FRED_RSP1 + MSR when encountered in ring 3 and ring 0. + - normal #PF will be delivered on the stack pointed by IA32_FRED_RSP0 + MSR when encountered in ring 3. + +The VMX nested-exception support ensures a correct event stack level is +chosen when a VM entry injects a nested exception. + +Signed-off-by: Xin Li +[ Sean: reworked kvm_requeue_exception() to simply the code changes ] +Signed-off-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Move the check is_fred_enable() from kvm_multiple_exception() to + vmx_inject_exception() thus avoid bleeding FRED details into + kvm_multiple_exception() (Chao Gao). + +Change in v3: +* Rework kvm_requeue_exception() to simply the code changes (Sean + Christopherson). + +Change in v2: +* Set the nested flag when there is an original interrupt (Chao Gao). +--- + arch/x86/include/asm/kvm_host.h | 4 +++- + arch/x86/include/asm/vmx.h | 5 ++++- + arch/x86/kvm/svm/svm.c | 2 +- + arch/x86/kvm/vmx/vmx.c | 6 +++++- + arch/x86/kvm/x86.c | 13 ++++++++++++- + arch/x86/kvm/x86.h | 1 + + 6 files changed, 26 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 550a8716a2272..3b6dadf368eb6 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -760,6 +760,7 @@ struct kvm_queued_exception { + u32 error_code; + unsigned long payload; + bool has_payload; ++ bool nested; + u64 event_data; + }; + +@@ -2231,7 +2232,8 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); + void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); + void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code, u64 event_data); ++ bool has_error_code, u32 error_code, bool nested, ++ u64 event_data); + void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); + void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault); +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index 539af190ad3e5..7b34a9357b288 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -140,6 +140,7 @@ + #define VMX_BASIC_INOUT BIT_ULL(54) + #define VMX_BASIC_TRUE_CTLS BIT_ULL(55) + #define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56) ++#define VMX_BASIC_NESTED_EXCEPTION BIT_ULL(58) + + static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic) + { +@@ -442,13 +443,15 @@ enum vmcs_field { + #define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */ + #define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */ + #define INTR_INFO_UNBLOCK_NMI 0x1000 /* 12 */ ++#define INTR_INFO_NESTED_EXCEPTION_MASK 0x2000 /* 13 */ + #define INTR_INFO_VALID_MASK 0x80000000 /* 31 */ +-#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000 ++#define INTR_INFO_RESVD_BITS_MASK 0x7fffd000 + + #define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK + #define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK + #define VECTORING_INFO_DELIVER_CODE_MASK INTR_INFO_DELIVER_CODE_MASK + #define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK ++#define VECTORING_INFO_NESTED_EXCEPTION_MASK INTR_INFO_NESTED_EXCEPTION_MASK + + #define INTR_TYPE_EXT_INTR (EVENT_TYPE_EXTINT << 8) /* external interrupt */ + #define INTR_TYPE_RESERVED (EVENT_TYPE_RESERVED << 8) /* reserved */ +diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c +index 147b644488f21..f4ccb3e666355 100644 +--- a/arch/x86/kvm/svm/svm.c ++++ b/arch/x86/kvm/svm/svm.c +@@ -4095,7 +4095,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) + + kvm_requeue_exception(vcpu, vector, + exitintinfo & SVM_EXITINTINFO_VALID_ERR, +- error_code, 0); ++ error_code, false, 0); + break; + } + case SVM_EXITINTINFO_TYPE_INTR: +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 5cefe7024ac6e..9d12c4a01ab2f 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1855,8 +1855,11 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) + vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, + vmx->vcpu.arch.event_exit_inst_len); + intr_info |= INTR_TYPE_SOFT_EXCEPTION; +- } else ++ } else { + intr_info |= INTR_TYPE_HARD_EXCEPTION; ++ if (ex->nested && is_fred_enabled(vcpu)) ++ intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; ++ } + + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); + +@@ -7337,6 +7340,7 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + kvm_requeue_exception(vcpu, vector, + idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, + error_code, ++ idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK, + event_data); + break; + } +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 71b651d4567ff..881c5543a77a5 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -879,6 +879,10 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.pending = true; + vcpu->arch.exception.injected = false; + ++ vcpu->arch.exception.nested = vcpu->arch.exception.nested || ++ vcpu->arch.nmi_injected || ++ vcpu->arch.interrupt.injected; ++ + vcpu->arch.exception.has_error_code = has_error; + vcpu->arch.exception.vector = nr; + vcpu->arch.exception.error_code = error_code; +@@ -908,8 +912,13 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.injected = false; + vcpu->arch.exception.pending = false; + ++ /* #DF is NOT a nested event, per its definition. */ ++ vcpu->arch.exception.nested = false; ++ + kvm_queue_exception_e(vcpu, DF_VECTOR, 0); + } else { ++ vcpu->arch.exception.nested = true; ++ + /* replace previous exception with a new one in a hope + that instruction re-execution will regenerate lost + exception */ +@@ -938,7 +947,8 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, + } + + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code, u64 event_data) ++ bool has_error_code, u32 error_code, bool nested, ++ u64 event_data) + { + + /* +@@ -963,6 +973,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.error_code = error_code; + vcpu->arch.exception.has_payload = false; + vcpu->arch.exception.payload = 0; ++ vcpu->arch.exception.nested = nested; + vcpu->arch.exception.event_data = event_data; + } + EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception); +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index 0c1fbf75442b7..4f5d12d7136ee 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -198,6 +198,7 @@ static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu) + { + vcpu->arch.exception.pending = false; + vcpu->arch.exception.injected = false; ++ vcpu->arch.exception.nested = false; + vcpu->arch.exception_vmexit.pending = false; + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi b/SPECS/kernel-rt/0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi deleted file mode 100644 index bca2d873f..000000000 --- a/SPECS/kernel-rt/0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi +++ /dev/null @@ -1,200 +0,0 @@ -From aa3e2401705ede04e2e4d8e54149942d6408a5e2 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Thu, 13 Jun 2024 09:48:43 -0700 -Subject: [PATCH 13/44] KVM: x86: Save/restore the nested flag of an exception - -Save/restore the nested flag of an exception during VM save/restore -and live migration to ensure a correct event stack level is chosen -when a nested exception is injected through FRED event delivery. - -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Add live migration support for exception nested flag (Chao Gao). ---- - Documentation/virt/kvm/api.rst | 21 ++++++++++++++++++++- - arch/x86/include/asm/kvm_host.h | 1 + - arch/x86/include/uapi/asm/kvm.h | 4 +++- - arch/x86/kvm/x86.c | 19 ++++++++++++++++++- - include/uapi/linux/kvm.h | 1 + - 5 files changed, 43 insertions(+), 3 deletions(-) - -diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst -index 6aa40ee05a4a..c496b0883a7f 100644 ---- a/Documentation/virt/kvm/api.rst -+++ b/Documentation/virt/kvm/api.rst -@@ -1184,6 +1184,10 @@ The following bits are defined in the flags field: - fields contain a valid state. This bit will be set whenever - KVM_CAP_EXCEPTION_PAYLOAD is enabled. - -+- KVM_VCPUEVENT_VALID_NESTED_FLAG may be set to inform that the -+ exception is a nested exception. This bit will be set whenever -+ KVM_CAP_EXCEPTION_NESTED_FLAG is enabled. -+ - - KVM_VCPUEVENT_VALID_TRIPLE_FAULT may be set to signal that the - triple_fault_pending field contains a valid state. This bit will - be set whenever KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled. -@@ -1283,6 +1287,10 @@ can be set in the flags field to signal that the - exception_has_payload, exception_payload, and exception.pending fields - contain a valid state and shall be written into the VCPU. - -+If KVM_CAP_EXCEPTION_NESTED_FLAG is enabled, KVM_VCPUEVENT_VALID_NESTED_FLAG -+can be set in the flags field to inform that the exception is a nested -+exception and exception_is_nested shall be written into the VCPU. -+ - If KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled, KVM_VCPUEVENT_VALID_TRIPLE_FAULT - can be set in flags field to signal that the triple_fault field contains - a valid state and shall be written into the VCPU. -@@ -8651,7 +8659,7 @@ given VM. - When this capability is enabled, KVM resets the VCPU when setting - MP_STATE_INIT_RECEIVED through IOCTL. The original MP_STATE is preserved. - --7.43 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED -+7.44 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED - ------------------------------------------- - - :Architectures: arm64 -@@ -8662,6 +8670,17 @@ This capability indicate to the userspace whether a PFNMAP memory region - can be safely mapped as cacheable. This relies on the presence of - force write back (FWB) feature support on the hardware. - -+7.45 KVM_CAP_EXCEPTION_NESTED_FLAG -+---------------------------------- -+ -+:Architectures: x86 -+:Parameters: args[0] whether feature should be enabled or not -+ -+With this capability enabled, an exception is save/restored with the -+additional information of whether it was nested or not. FRED event -+delivery uses this information to ensure a correct event stack level -+is chosen when a VM entry injects a nested exception. -+ - 8. Other capabilities. - ====================== - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 0d4cdb704f97..3b38c82b4c1c 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -1471,6 +1471,7 @@ struct kvm_arch { - bool has_mapped_host_mmio; - bool guest_can_read_msr_platform_info; - bool exception_payload_enabled; -+ bool exception_nested_flag_enabled; - - bool triple_fault_event; - -diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h -index a4870d9c9279..7b6b0491b157 100644 ---- a/arch/x86/include/uapi/asm/kvm.h -+++ b/arch/x86/include/uapi/asm/kvm.h -@@ -326,6 +326,7 @@ struct kvm_reinject_control { - #define KVM_VCPUEVENT_VALID_SMM 0x00000008 - #define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 - #define KVM_VCPUEVENT_VALID_TRIPLE_FAULT 0x00000020 -+#define KVM_VCPUEVENT_VALID_NESTED_FLAG 0x00000040 - - /* Interrupt shadow states */ - #define KVM_X86_SHADOW_INT_MOV_SS 0x01 -@@ -363,7 +364,8 @@ struct kvm_vcpu_events { - struct { - __u8 pending; - } triple_fault; -- __u8 reserved[26]; -+ __u8 reserved[25]; -+ __u8 exception_is_nested; - __u8 exception_has_payload; - __u64 exception_payload; - }; -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 6029125a518d..eb0a1e537a88 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -4875,6 +4875,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) - case KVM_CAP_GET_MSR_FEATURES: - case KVM_CAP_MSR_PLATFORM_INFO: - case KVM_CAP_EXCEPTION_PAYLOAD: -+ case KVM_CAP_EXCEPTION_NESTED_FLAG: - case KVM_CAP_X86_TRIPLE_FAULT_EVENT: - case KVM_CAP_SET_GUEST_DEBUG: - case KVM_CAP_LAST_CPU: -@@ -5619,6 +5620,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, - events->exception.error_code = ex->error_code; - events->exception_has_payload = ex->has_payload; - events->exception_payload = ex->payload; -+ events->exception_is_nested = ex->nested; - - events->interrupt.injected = - vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft; -@@ -5644,6 +5646,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, - | KVM_VCPUEVENT_VALID_SMM); - if (vcpu->kvm->arch.exception_payload_enabled) - events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD; -+ if (vcpu->kvm->arch.exception_nested_flag_enabled) -+ events->flags |= KVM_VCPUEVENT_VALID_NESTED_FLAG; - if (vcpu->kvm->arch.triple_fault_event) { - events->triple_fault.pending = kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu); - events->flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; -@@ -5658,7 +5662,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, - | KVM_VCPUEVENT_VALID_SHADOW - | KVM_VCPUEVENT_VALID_SMM - | KVM_VCPUEVENT_VALID_PAYLOAD -- | KVM_VCPUEVENT_VALID_TRIPLE_FAULT)) -+ | KVM_VCPUEVENT_VALID_TRIPLE_FAULT -+ | KVM_VCPUEVENT_VALID_NESTED_FLAG)) - return -EINVAL; - - if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) { -@@ -5673,6 +5678,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, - events->exception_has_payload = 0; - } - -+ if (events->flags & KVM_VCPUEVENT_VALID_NESTED_FLAG) { -+ if (!vcpu->kvm->arch.exception_nested_flag_enabled) -+ return -EINVAL; -+ } else { -+ events->exception_is_nested = 0; -+ } -+ - if ((events->exception.injected || events->exception.pending) && - (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR)) - return -EINVAL; -@@ -5698,6 +5710,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, - vcpu->arch.exception.error_code = events->exception.error_code; - vcpu->arch.exception.has_payload = events->exception_has_payload; - vcpu->arch.exception.payload = events->exception_payload; -+ vcpu->arch.exception.nested = events->exception_is_nested; - - vcpu->arch.interrupt.injected = events->interrupt.injected; - vcpu->arch.interrupt.nr = events->interrupt.nr; -@@ -6732,6 +6745,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, - kvm->arch.exception_payload_enabled = cap->args[0]; - r = 0; - break; -+ case KVM_CAP_EXCEPTION_NESTED_FLAG: -+ kvm->arch.exception_nested_flag_enabled = cap->args[0]; -+ r = 0; -+ break; - case KVM_CAP_X86_TRIPLE_FAULT_EVENT: - kvm->arch.triple_fault_event = cap->args[0]; - r = 0; -diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h -index f0f0d49d2544..fe4a822b3c09 100644 ---- a/include/uapi/linux/kvm.h -+++ b/include/uapi/linux/kvm.h -@@ -962,6 +962,7 @@ struct kvm_enable_cap { - #define KVM_CAP_ARM_EL2_E2H0 241 - #define KVM_CAP_RISCV_MP_STATE_RESET 242 - #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 -+#define KVM_CAP_EXCEPTION_NESTED_FLAG 244 - - struct kvm_irq_routing_irqchip { - __u32 irqchip; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet b/SPECS/kernel-rt/0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet new file mode 100644 index 000000000..6144f79bf --- /dev/null +++ b/SPECS/kernel-rt/0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet @@ -0,0 +1,31 @@ +From 9dd63a8bddee2d398483bb8996a30b26f6f209b8 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Fri, 30 Jul 2021 10:17:52 +0800 +Subject: [PATCH 13/14] igc: Remove the CONFIG_DEBUG_MISC condition for trace + +This patch is to remove the CONFIG_DEBUG_MISC for trace_printk. +CONFIG_DEBUG_MISC was enabled in ER89 config but not enable in +latest config. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 571cc380de5ad..bfef9555a12ea 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -3214,7 +3214,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + + switch (tx_buffer->type) { + case IGC_TX_BUFFER_TYPE_XSK: +-#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) ++#if defined(CONFIG_TRACING) + /* Only use for RTCP KPI Measurement on Q2 */ + if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) + trace_printk("TX HW TS %lld\n", timestamp); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet b/SPECS/kernel-rt/0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet new file mode 100644 index 000000000..60a51e9cc --- /dev/null +++ b/SPECS/kernel-rt/0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet @@ -0,0 +1,152 @@ +From 8cf346c678cf84a00c374a398d4b39742d98d584 Mon Sep 17 00:00:00 2001 +From: "Song, Yoong Siang" +Date: Sun, 11 Apr 2021 16:19:28 +0800 +Subject: [PATCH 13/18] net: stmmac: restructure Rx & Tx hardware timestamping + functions + +We rearrange the functions for getting Rx & Tx time-stampings for +skb so that these functions can also be reused for XDP later. + +Signed-off-by: Ong Boon Leong +Signed-off-by: Song, Yoong Siang +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 46 ++++++++++--------- + 1 file changed, 24 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 92a8075ef9399..641b011035087 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -388,25 +388,20 @@ static void stmmac_eee_ctrl_timer(struct timer_list *t) + /* stmmac_get_tx_hwtstamp - get HW TX timestamps + * @priv: driver private structure + * @p : descriptor pointer +- * @skb : the socket buffer ++ * @hwtstamp: hardware timestamp + * Description : + * This function will read timestamp from the descriptor & pass it to stack. + * and also perform some sanity checks. + */ + static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, +- struct dma_desc *p, struct sk_buff *skb) ++ struct dma_desc *p, ktime_t *hwtstamp) + { +- struct skb_shared_hwtstamps shhwtstamp; + bool found = false; + u64 ns = 0; + + if (!priv->hwts_tx_en) + return; + +- /* exit if skb doesn't support hw tstamp */ +- if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))) +- return; +- + /* check tx tstamp status */ + if (stmmac_get_tx_timestamp_status(priv, p)) { + stmmac_get_timestamp(priv, p, priv->adv_ts, &ns); +@@ -418,12 +413,8 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, + if (found) { + ns -= priv->plat->cdc_error_adj; + +- memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); +- shhwtstamp.hwtstamp = ns_to_ktime(ns); +- ++ *hwtstamp = ns_to_ktime(ns); + netdev_dbg(priv->dev, "get valid TX hw timestamp %llu\n", ns); +- /* pass tstamp to stack */ +- skb_tstamp_tx(skb, &shhwtstamp); + } + } + +@@ -431,15 +422,14 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, + * @priv: driver private structure + * @p : descriptor pointer + * @np : next descriptor pointer +- * @skb : the socket buffer ++ * @hwtstamp: hardware timestamp + * Description : + * This function will read received packet's timestamp from the descriptor + * and pass it to stack. It also perform some sanity checks. + */ + static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, +- struct dma_desc *np, struct sk_buff *skb) ++ struct dma_desc *np, ktime_t *hwtstamp) + { +- struct skb_shared_hwtstamps *shhwtstamp = NULL; + struct dma_desc *desc = p; + u64 ns = 0; + +@@ -456,11 +446,10 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, + ns -= priv->plat->cdc_error_adj; + + netdev_dbg(priv->dev, "get valid RX hw timestamp %llu\n", ns); +- shhwtstamp = skb_hwtstamps(skb); +- memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); +- shhwtstamp->hwtstamp = ns_to_ktime(ns); ++ *hwtstamp = ns_to_ktime(ns); + } else { + netdev_dbg(priv->dev, "cannot get RX hw timestamp\n"); ++ *hwtstamp = 0; + } + } + +@@ -2729,8 +2718,15 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, + } else { + tx_packets++; + } +- if (skb) { +- stmmac_get_tx_hwtstamp(priv, p, skb); ++ if (skb && ++ skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) { ++ struct skb_shared_hwtstamps shhwtstamp; ++ ++ memset(&shhwtstamp, 0, ++ sizeof(struct skb_shared_hwtstamps)); ++ stmmac_get_tx_hwtstamp(priv, p, ++ &shhwtstamp.hwtstamp); ++ skb_tstamp_tx(skb, &shhwtstamp); + } else if (tx_q->xsk_pool && + xp_tx_metadata_enabled(tx_q->xsk_pool)) { + struct stmmac_xsk_tx_complete tx_compl = { +@@ -5157,6 +5153,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, + { + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; ++ struct skb_shared_hwtstamps *shhwtstamp = NULL; + unsigned int len = xdp->data_end - xdp->data; + enum pkt_hash_types hash_type; + int coe = priv->hw->rx_csum; +@@ -5169,7 +5166,9 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, + return; + } + +- stmmac_get_rx_hwtstamp(priv, p, np, skb); ++ shhwtstamp = skb_hwtstamps(skb); ++ memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); ++ stmmac_get_rx_hwtstamp(priv, p, np, &shhwtstamp->hwtstamp); + if (priv->hw->hw_vlan_en) + /* MAC level stripping. */ + stmmac_rx_hw_vlan(priv, priv->hw, p, skb); +@@ -5493,6 +5492,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) + rx_q->dma_rx_phy, desc_size); + } + while (count < limit) { ++ struct skb_shared_hwtstamps *shhwtstamp = NULL; + unsigned int buf1_len = 0, buf2_len = 0; + enum pkt_hash_types hash_type; + struct stmmac_rx_buffer *buf; +@@ -5686,7 +5686,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) + + /* Got entire packet into SKB. Finish it. */ + +- stmmac_get_rx_hwtstamp(priv, p, np, skb); ++ shhwtstamp = skb_hwtstamps(skb); ++ memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); ++ stmmac_get_rx_hwtstamp(priv, p, np, &shhwtstamp->hwtstamp); + + if (priv->hw->hw_vlan_en) + /* MAC level stripping. */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu b/SPECS/kernel-rt/0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu deleted file mode 100644 index 94feea209..000000000 --- a/SPECS/kernel-rt/0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu +++ /dev/null @@ -1,814 +0,0 @@ -From 0dee21c66fcb4ee0f58535f368f9fe5e81c857f2 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 16:47:40 +0800 -Subject: [PATCH 13/21] patch: staging add ipu7 isys tpg and MGC config - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/Kconfig | 11 + - drivers/staging/media/ipu7/ipu7-isys-tpg.c | 693 +++++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-isys-tpg.h | 70 +++ - 3 files changed, 774 insertions(+) - create mode 100644 drivers/staging/media/ipu7/ipu7-isys-tpg.c - create mode 100644 drivers/staging/media/ipu7/ipu7-isys-tpg.h - -diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig -index 0a6e68c7632d..91954fdadc8b 100644 ---- a/drivers/staging/media/ipu7/Kconfig -+++ b/drivers/staging/media/ipu7/Kconfig -@@ -23,6 +23,17 @@ config VIDEO_INTEL_IPU7 - To compile this driver, say Y here! It contains 3 modules - - intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. - -+config VIDEO_INTEL_IPU7_MGC -+ bool "Compile for IPU7 MGC driver" -+ depends on VIDEO_INTEL_IPU7 -+ help -+ If selected, MGC device nodes would be created. -+ -+ Recommended for driver developers only. -+ -+ If you want to the MGC devices exposed to user as media entity, -+ you must select this option, otherwise no. -+ - config VIDEO_INTEL_IPU7_ISYS_RESET - bool "IPU7 ISYS RESET" - depends on VIDEO_INTEL_IPU7 -diff --git a/drivers/staging/media/ipu7/ipu7-isys-tpg.c b/drivers/staging/media/ipu7/ipu7-isys-tpg.c -new file mode 100644 -index 000000000000..35b6298e4f8c ---- /dev/null -+++ b/drivers/staging/media/ipu7/ipu7-isys-tpg.c -@@ -0,0 +1,693 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-tpg.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-platform-regs.h" -+ -+static const u32 tpg_supported_codes[] = { -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ 0, -+}; -+ -+#define IPU_ISYS_FREQ 533000000UL -+ -+static u32 isys_mbus_code_to_bpp(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return 24; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return 20; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return 16; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return 12; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return 10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return 8; -+ default: -+ WARN_ON(1); -+ return 0; -+ } -+} -+ -+static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { -+ .s_stream = tpg_set_stream, -+}; -+ -+static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, -+ struct -+ ipu7_isys_subdev, -+ ctrl_handler), -+ struct ipu7_isys_tpg, asd); -+ switch (ctrl->id) { -+ case V4L2_CID_HBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_HBLANK); -+ break; -+ case V4L2_CID_VBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_VBLANK); -+ break; -+ case V4L2_CID_TEST_PATTERN: -+ writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); -+ break; -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { -+ .s_ctrl = ipu7_isys_tpg_s_ctrl, -+}; -+ -+static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) -+{ -+ return MGC_PPC * IPU_ISYS_FREQ / bpp; -+} -+ -+static const char *const tpg_mode_items[] = { -+ "Ramp", -+ "Checkerboard", -+ "Monochrome per frame", -+ "Color palette", -+}; -+ -+static struct v4l2_ctrl_config tpg_mode = { -+ .ops = &ipu7_isys_tpg_ctrl_ops, -+ .id = V4L2_CID_TEST_PATTERN, -+ .name = "Test Pattern", -+ .type = V4L2_CTRL_TYPE_MENU, -+ .min = TPG_MODE_RAMP, -+ .max = ARRAY_SIZE(tpg_mode_items) - 1, -+ .def = TPG_MODE_COLOR_PALETTE, -+ .menu_skip_mask = 0x2, -+ .qmenu = tpg_mode_items, -+}; -+ -+static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ int hblank; -+ u64 default_pixel_rate; -+ -+ hblank = 1024; -+ -+ tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_HBLANK, 8, 65535, 1, hblank); -+ -+ tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_VBLANK, 8, 65535, 1, 1024); -+ -+ default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); -+ tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_PIXEL_RATE, -+ default_pixel_rate, -+ default_pixel_rate, -+ 1, default_pixel_rate); -+ if (tpg->pixel_rate) { -+ tpg->pixel_rate->cur.val = default_pixel_rate; -+ tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ } -+ -+ v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); -+} -+ -+static int tpg_sd_init_cfg(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route routes[] = { -+ { -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ } -+ }; -+ -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = routes, -+ }; -+ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_SBGGR10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); -+} -+ -+static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { -+ .init_state = tpg_sd_init_cfg, -+}; -+ -+static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu7_isys_subdev_set_fmt, -+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -+}; -+ -+static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+}; -+ -+/* V4L2 subdev core operations */ -+static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { -+ .subscribe_event = subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static const struct v4l2_subdev_ops tpg_sd_ops = { -+ .core = &tpg_sd_core_ops, -+ .video = &tpg_sd_video_ops, -+ .pad = &tpg_sd_pad_ops, -+}; -+ -+static struct media_entity_operations tpg_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ struct video_device *vdev = tpg->asd.sd.devnode; -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "sof_event::tpg-%i sequence: %i\n", tpg->index, -+ ev.u.frame_sync.frame_sequence); -+} -+ -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "eof_event::tpg-%i sequence: %i\n", -+ tpg->index, frame_sequence); -+} -+ -+#define DEFAULT_VC_ID 0 -+static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) -+{ -+ return false; -+} -+ -+static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, -+ void __iomem *mg_base) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "---------MGC REG DUMP START----------"); -+ -+ dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", -+ MGC_MG_CSI_ADAPT_LAYER_TYPE, -+ readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); -+ dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); -+ dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); -+ dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); -+ dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MULTI_DTYPES_MODE, -+ readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); -+ dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", -+ MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); -+ dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", -+ MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); -+ dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", -+ MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); -+ dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", -+ MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); -+ dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); -+ dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", -+ readl(mg_base + MGC_MG_TPG_R0), -+ readl(mg_base + MGC_MG_TPG_G0), -+ readl(mg_base + MGC_MG_TPG_B0)); -+ dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", -+ readl(mg_base + MGC_MG_TPG_R1), -+ readl(mg_base + MGC_MG_TPG_G1), -+ readl(mg_base + MGC_MG_TPG_B1)); -+ dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); -+ dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", -+ MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); -+ dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", -+ MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_EN, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_INCR_VAL, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); -+ dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", -+ MGC_MG_FRAME_NUM_STTS, -+ readl(mg_base + MGC_MG_FRAME_NUM_STTS)); -+ -+ dev_dbg(dev, "---------MGC REG DUMP END----------"); -+} -+ -+#define TPG_STOP_TIMEOUT 500000 -+static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ unsigned int port; -+ u32 status; -+ void __iomem *reg; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ -+ port = 1 << tpg->index; -+ -+ dev_dbg(dev, "MG%d generated %u frames", tpg->index, -+ readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); -+ writel(port, mgc_base + MGC_ASYNC_STOP); -+ -+ dev_dbg(dev, "wait for MG%d stop", tpg->index); -+ -+ reg = mg_base + MGC_MG_STOPPED_STTS; -+ ret = readl_poll_timeout(reg, status, status & 0x1, 200, -+ TPG_STOP_TIMEOUT); -+ if (ret < 0) { -+ dev_err(dev, "mgc stop timeout"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "MG%d STOPPED", tpg->index); -+ -+ return 0; -+} -+ -+#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) -+#define TPG_FRAME_RATE 30 -+#define TPG_BLANK_RATIO (4 / 3) -+static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, -+ u32 *hblank_cycles, u32 *vblank_cycles) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 width, height; -+ u32 code; -+ u32 bpp; -+ u32 bits_per_line; -+ u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; -+ u64 vblank_us, hblank_us; -+ u32 ref_clk; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ u32 dto_incr_val = 0x100; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return; -+ -+ dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); -+ bits_per_line = width * bpp * TPG_BLANK_RATIO; -+ -+ cycles = div_u64(bits_per_line, 64); -+ dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, -+ bits_per_line, cycles); -+ -+ do { -+ dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, -+ dto_incr_val); -+ rate = div_u64(1 << 16, dto_incr_val); -+ ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); -+ dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, -+ ns_per_cycle); -+ -+ line_time_ns = cycles * ns_per_cycle; -+ frame_time_us = line_time_ns * height / 1000; -+ dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", -+ tpg->index, line_time_ns, frame_time_us); -+ -+ if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) -+ break; -+ -+ /* dto incr val step 0x100 */ -+ dto_incr_val += 0x100; -+ } while (dto_incr_val < (1 << 16)); -+ -+ if (dto_incr_val >= (1 << 16)) { -+ dev_warn(dev, "No DTO_INCR_VAL found\n"); -+ hblank_us = 10; /* 10us */ -+ vblank_us = 10000; /* 10ms */ -+ dto_incr_val = 0x1000; -+ } else { -+ hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; -+ vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; -+ } -+ -+ dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", -+ hblank_us, vblank_us, dto_incr_val); -+ -+ ref_clk = tpg->isys->adev->isp->buttress.ref_clk; -+ -+ *dto = dto_incr_val; -+ *hblank_cycles = hblank_us * ref_clk / 10; -+ *vblank_cycles = vblank_us * ref_clk / 10; -+ dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", -+ *hblank_cycles, *vblank_cycles); -+} -+ -+static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 port_map; -+ u32 csi_port; -+ u32 code, bpp; -+ u32 width, height; -+ u32 dto, hblank, vblank; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return ret; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", -+ tpg->index, code, width, height); -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return -EINVAL; -+ -+ csi_port = tpg->index; -+ if (csi_port >= 4) -+ dev_err(dev, "invalid tpg index %u\n", tpg->index); -+ -+ dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", -+ DEFAULT_VC_ID, csi_port); -+ -+ /* config port map -+ * TODO: add VC support and TPG with multiple -+ * source pads. Currently, for simplicity, only map 1 mg to 1 csi port -+ */ -+ port_map = 1 << tpg->index; -+ writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); -+ -+ /* configure adapt layer type */ -+ writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); -+ -+ /* configure MGC mode -+ * 0 - Disable MGC -+ * 1 - Enable PRBS -+ * 2 - Enable TPG -+ * 3 - Reserved [Write phase: SW/FW debug] -+ */ -+ writel(2, mg_base + MGC_MG_MODE); -+ -+ /* config mg init counter */ -+ writel(0, mg_base + MGC_MG_INIT_COUNTER); -+ -+ /* -+ * configure virtual channel -+ * TODO: VC support if need -+ * currently each MGC just uses 1 virtual channel -+ */ -+ writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); -+ -+ /* -+ * configure data type and multi dtypes mode -+ * TODO: it needs to add the metedata flow. -+ */ -+ if (is_metadata_enabled(tpg)) { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } else { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } -+ -+ /* -+ * configure frame information -+ */ -+ writel(0, mg_base + MGC_MG_NOF_FRAMES); -+ writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); -+ -+ tpg_get_timing(tpg, &dto, &hblank, &vblank); -+ writel(hblank, mg_base + MGC_MG_HBLANK); -+ writel(vblank, mg_base + MGC_MG_VBLANK); -+ -+ /* -+ * configure tpg mode, colors, mask, tile dimension -+ * Mode was set by user configuration -+ * 0 - Ramp mode -+ * 1 - Checkerboard -+ * 2 - Monochrome per frame -+ * 3 - Color palette -+ */ -+ writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); -+ -+ /* red and green for checkerboard, n/a for other modes */ -+ writel(58, mg_base + MGC_MG_TPG_R0); -+ writel(122, mg_base + MGC_MG_TPG_G0); -+ writel(46, mg_base + MGC_MG_TPG_B0); -+ writel(123, mg_base + MGC_MG_TPG_R1); -+ writel(85, mg_base + MGC_MG_TPG_G1); -+ writel(67, mg_base + MGC_MG_TPG_B1); -+ -+ writel(0x0, mg_base + MGC_MG_TPG_FACTORS); -+ -+ /* hor_mask [15:0] ver_mask [31:16] */ -+ writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); -+ /* xy_mask [11:0] */ -+ writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); -+ writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), -+ mg_base + MGC_MG_TPG_TILE_DIM); -+ -+ writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); -+ writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); -+ -+ /* disable err_injection */ -+ writel(0, mg_base + MGC_MG_ERR_INJECT); -+ writel(0, mg_base + MGC_MG_ERR_LOCATION); -+ -+ ipu7_mipigen_regdump(tpg, mg_base); -+ -+ dev_dbg(dev, "starting MG%d streaming...\n", csi_port); -+ -+ /* kick and start */ -+ writel(port_map, mgc_base + MGC_KICK); -+ -+ return 0; -+} -+ -+static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct ipu7_isys_csi2 *csi2; -+ u32 offset; -+ struct ipu7_isys *isys = tpg->isys; -+ -+ csi2 = &isys->csi2[tpg->index]; -+ offset = IS_IO_GPREGS_BASE; -+ -+ /* MGC is in use by SW or not */ -+ if (enable) -+ writel(1, csi2->base + offset + MGC_CLK_GATE); -+ else -+ writel(0, csi2->base + offset + MGC_CLK_GATE); -+} -+ -+static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ struct ipu7_isys *isys = tpg->isys; -+ struct ipu7_isys_csi2 *csi2; -+ u32 port, offset, val; -+ void __iomem *isys_base = isys->pdata->base; -+ -+ port = tpg->index; -+ csi2 = &isys->csi2[port]; -+ -+ offset = IS_IO_GPREGS_BASE; -+ val = readl(isys_base + offset + CSI_PORT_CLK_GATE); -+ dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); -+ -+ if (!enable) { -+ writel(~(1 << port) & val, -+ isys_base + offset + CSI_PORT_CLK_GATE); -+ return; -+ } -+ -+ /* set csi port is using by SW */ -+ writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); -+ /* input is coming from MGC */ -+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -+ writel(CSI_MIPIGEN_INPUT, -+ csi2->base + offset + CSI2_ADPL_INPUT_MODE); -+} -+ -+/* TODO: add the processing of vc */ -+int tpg_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ struct ipu7_isys_stream *stream = tpg->av->stream; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ -+ if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { -+ dev_err(dev, "invalid MGC index %d\n", tpg->index); -+ return -EINVAL; -+ } -+ -+ if (!enable) { -+ /* Stop MGC */ -+ stream->asd->is_tpg = false; -+ stream->asd = NULL; -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ ret = tpg_stop_stream(tpg); -+ ipu7_isys_ungate_mgc(tpg, enable); -+ -+ return ret; -+ } -+ -+ stream->asd = &tpg->asd; -+ /* ungate the MGC clock to program */ -+ ipu7_isys_ungate_mgc(tpg, enable); -+ /* Start MGC */ -+ ret = tpg_start_stream(tpg); -+ v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ -+ return ret; -+} -+ -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) -+{ -+ v4l2_device_unregister_subdev(&tpg->asd.sd); -+ ipu7_isys_subdev_cleanup(&tpg->asd); -+} -+ -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ tpg->isys = isys; -+ tpg->base = base; -+ tpg->sel = sel; -+ tpg->index = index; -+ -+ tpg->asd.sd.entity.ops = &tpg_entity_ops; -+ tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; -+ tpg->asd.isys = isys; -+ -+ ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, -+ NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); -+ if (ret) -+ return ret; -+ -+ tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; -+ tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -+ -+ tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; -+ tpg->asd.supported_codes = tpg_supported_codes; -+ tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; -+ -+ snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), -+ IPU_ISYS_ENTITY_PREFIX " TPG %u", index); -+ v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); -+ -+ ret = v4l2_subdev_init_finalize(&tpg->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to finalize subdev (%d)\n", ret); -+ goto fail; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); -+ if (ret) { -+ dev_info(dev, "can't register v4l2 subdev\n"); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ ipu7_isys_tpg_cleanup(tpg); -+ -+ return ret; -+} -diff --git a/drivers/staging/media/ipu7/ipu7-isys-tpg.h b/drivers/staging/media/ipu7/ipu7-isys-tpg.h -new file mode 100644 -index 000000000000..e2542a6472e3 ---- /dev/null -+++ b/drivers/staging/media/ipu7/ipu7-isys-tpg.h -@@ -0,0 +1,70 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_TPG_H -+#define IPU7_ISYS_TPG_H -+ -+#include -+#include -+#include -+ -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-queue.h" -+ -+struct ipu7_isys_tpg_pdata; -+struct ipu7_isys; -+ -+#define TPG_PAD_SOURCE 0 -+#define NR_OF_TPG_PADS 1 -+#define NR_OF_TPG_SOURCE_PADS 1 -+#define NR_OF_TPG_SINK_PADS 0 -+#define NR_OF_TPG_STREAMS 1 -+ -+enum isys_tpg_mode { -+ TPG_MODE_RAMP = 0, -+ TPG_MODE_CHECKERBOARD = 1, -+ TPG_MODE_MONO = 2, -+ TPG_MODE_COLOR_PALETTE = 3, -+}; -+ -+/* -+ * struct ipu7_isys_tpg -+ * -+ * @nlanes: number of lanes in the receiver -+ */ -+struct ipu7_isys_tpg { -+ struct ipu7_isys_subdev asd; -+ struct ipu7_isys_tpg_pdata *pdata; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_video *av; -+ -+ /* MG base not MGC */ -+ void __iomem *base; -+ void __iomem *sel; -+ unsigned int index; -+ -+ struct v4l2_ctrl *hblank; -+ struct v4l2_ctrl *vblank; -+ struct v4l2_ctrl *pixel_rate; -+}; -+ -+#define ipu7_isys_subdev_to_tpg(__sd) \ -+ container_of(__sd, struct ipu7_isys_tpg, asd) -+ -+#define to_ipu7_isys_tpg(sd) \ -+ container_of(to_ipu7_isys_subdev(sd), \ -+ struct ipu7_isys_tpg, asd) -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, -+ struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index); -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); -+int tpg_set_stream(struct v4l2_subdev *sd, int enable); -+ -+#endif /* IPU7_ISYS_TPG_H */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0013-perf-x86-intel-Initialize-architectural-PEBS.perf b/SPECS/kernel-rt/0013-perf-x86-intel-Initialize-architectural-PEBS.perf deleted file mode 100644 index f4c7bb59a..000000000 --- a/SPECS/kernel-rt/0013-perf-x86-intel-Initialize-architectural-PEBS.perf +++ /dev/null @@ -1,381 +0,0 @@ -From b4743cb44b027e0b485dfe784b53a0b2c95cfda6 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 17 Feb 2025 11:01:45 +0000 -Subject: [PATCH 013/100] perf/x86/intel: Initialize architectural PEBS - -arch-PEBS leverages CPUID.23H.4/5 sub-leaves enumerate arch-PEBS -supported capabilities and counters bitmap. This patch parses these 2 -sub-leaves and initializes arch-PEBS capabilities and corresponding -structures. - -Since IA32_PEBS_ENABLE and MSR_PEBS_DATA_CFG MSRs are no longer existed -for arch-PEBS, arch-PEBS doesn't need to manipulate these MSRs. Thus add -a simple pair of __intel_pmu_pebs_enable/disable() callbacks for -arch-PEBS. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 21 ++++++++++--- - arch/x86/events/intel/core.c | 46 ++++++++++++++++++--------- - arch/x86/events/intel/ds.c | 52 ++++++++++++++++++++++++++----- - arch/x86/events/perf_event.h | 25 +++++++++++++-- - arch/x86/include/asm/perf_event.h | 7 ++++- - 5 files changed, 120 insertions(+), 31 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 24fae86f2682..94a8ea4389e4 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -554,14 +554,22 @@ static inline int precise_br_compat(struct perf_event *event) - return m == b; - } - --int x86_pmu_max_precise(void) -+int x86_pmu_max_precise(struct pmu *pmu) - { - int precise = 0; - -- /* Support for constant skid */ - if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) { -- precise++; -+ /* arch PEBS */ -+ if (x86_pmu.arch_pebs) { -+ precise = 2; -+ if (hybrid(pmu, arch_pebs_cap).pdists) -+ precise++; -+ -+ return precise; -+ } - -+ /* legacy PEBS - support for constant skid */ -+ precise++; - /* Support for IP fixup */ - if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2) - precise++; -@@ -569,13 +577,14 @@ int x86_pmu_max_precise(void) - if (x86_pmu.pebs_prec_dist) - precise++; - } -+ - return precise; - } - - int x86_pmu_hw_config(struct perf_event *event) - { - if (event->attr.precise_ip) { -- int precise = x86_pmu_max_precise(); -+ int precise = x86_pmu_max_precise(event->pmu); - - if (event->attr.precise_ip > precise) - return -EOPNOTSUPP; -@@ -2631,7 +2640,9 @@ static ssize_t max_precise_show(struct device *cdev, - struct device_attribute *attr, - char *buf) - { -- return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise()); -+ struct pmu *pmu = dev_get_drvdata(cdev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise(pmu)); - } - - static DEVICE_ATTR_RO(max_precise); -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 0b57a9146b82..dcc796d00f05 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5420,34 +5420,49 @@ static inline bool intel_pmu_broken_perf_cap(void) - - static void update_pmu_cap(struct pmu *pmu) - { -- unsigned int cntr, fixed_cntr, ecx, edx; -- union cpuid35_eax eax; -- union cpuid35_ebx ebx; -+ unsigned int eax, ebx, ecx, edx; -+ union cpuid35_eax eax_0; -+ union cpuid35_ebx ebx_0; - -- cpuid(ARCH_PERFMON_EXT_LEAF, &eax.full, &ebx.full, &ecx, &edx); -+ cpuid(ARCH_PERFMON_EXT_LEAF, &eax_0.full, &ebx_0.full, &ecx, &edx); - -- if (ebx.split.umask2) -+ if (ebx_0.split.umask2) - hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_UMASK2; -- if (ebx.split.eq) -+ if (ebx_0.split.eq) - hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_EQ; - -- if (eax.split.cntr_subleaf) { -+ if (eax_0.split.cntr_subleaf) { - cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_NUM_COUNTER_LEAF, -- &cntr, &fixed_cntr, &ecx, &edx); -- hybrid(pmu, cntr_mask64) = cntr; -- hybrid(pmu, fixed_cntr_mask64) = fixed_cntr; -+ &eax, &ebx, &ecx, &edx); -+ hybrid(pmu, cntr_mask64) = eax; -+ hybrid(pmu, fixed_cntr_mask64) = ebx; - } - -- if (eax.split.acr_subleaf) { -+ if (eax_0.split.acr_subleaf) { - cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_ACR_LEAF, -- &cntr, &fixed_cntr, &ecx, &edx); -+ &eax, &ebx, &ecx, &edx); - /* The mask of the counters which can be reloaded */ -- hybrid(pmu, acr_cntr_mask64) = cntr | ((u64)fixed_cntr << INTEL_PMC_IDX_FIXED); -+ hybrid(pmu, acr_cntr_mask64) = eax | ((u64)ebx << INTEL_PMC_IDX_FIXED); - - /* The mask of the counters which can cause a reload of reloadable counters */ - hybrid(pmu, acr_cause_mask64) = ecx | ((u64)edx << INTEL_PMC_IDX_FIXED); - } - -+ /* Bits[5:4] should be set simultaneously if arch-PEBS is supported */ -+ if (eax_0.split.pebs_caps_subleaf && eax_0.split.pebs_cnts_subleaf) { -+ cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_CAP_LEAF, -+ &eax, &ebx, &ecx, &edx); -+ hybrid(pmu, arch_pebs_cap).caps = (u64)ebx << 32; -+ -+ cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_COUNTER_LEAF, -+ &eax, &ebx, &ecx, &edx); -+ hybrid(pmu, arch_pebs_cap).counters = ((u64)ecx << 32) | eax; -+ hybrid(pmu, arch_pebs_cap).pdists = ((u64)edx << 32) | ebx; -+ } else { -+ WARN_ON(x86_pmu.arch_pebs == 1); -+ x86_pmu.arch_pebs = 0; -+ } -+ - if (!intel_pmu_broken_perf_cap()) { - /* Perf Metric (Bit 15) and PEBS via PT (Bit 16) are hybrid enumeration */ - rdmsrq(MSR_IA32_PERF_CAPABILITIES, hybrid(pmu, intel_cap).capabilities); -@@ -6396,7 +6411,7 @@ tsx_is_visible(struct kobject *kobj, struct attribute *attr, int i) - static umode_t - pebs_is_visible(struct kobject *kobj, struct attribute *attr, int i) - { -- return x86_pmu.ds_pebs ? attr->mode : 0; -+ return intel_pmu_has_pebs() ? attr->mode : 0; - } - - static umode_t -@@ -7872,6 +7887,9 @@ __init int intel_pmu_init(void) - if (!is_hybrid() && boot_cpu_has(X86_FEATURE_ARCH_PERFMON_EXT)) - update_pmu_cap(NULL); - -+ if (x86_pmu.arch_pebs) -+ pr_cont("Architectural PEBS, "); -+ - intel_pmu_check_counters_mask(&x86_pmu.cntr_mask64, - &x86_pmu.fixed_cntr_mask64, - &x86_pmu.intel_ctrl); -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index b23c49e2e06f..19d707933d61 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1531,6 +1531,15 @@ static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc) - intel_pmu_drain_pebs_buffer(); - } - -+static void __intel_pmu_pebs_enable(struct perf_event *event) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct hw_perf_event *hwc = &event->hw; -+ -+ hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; -+ cpuc->pebs_enabled |= 1ULL << hwc->idx; -+} -+ - void intel_pmu_pebs_enable(struct perf_event *event) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -@@ -1539,9 +1548,7 @@ void intel_pmu_pebs_enable(struct perf_event *event) - struct debug_store *ds = cpuc->ds; - unsigned int idx = hwc->idx; - -- hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; -- -- cpuc->pebs_enabled |= 1ULL << hwc->idx; -+ __intel_pmu_pebs_enable(event); - - if ((event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) && (x86_pmu.version < 5)) - cpuc->pebs_enabled |= 1ULL << (hwc->idx + 32); -@@ -1603,14 +1610,22 @@ void intel_pmu_pebs_del(struct perf_event *event) - pebs_update_state(needed_cb, cpuc, event, false); - } - --void intel_pmu_pebs_disable(struct perf_event *event) -+static void __intel_pmu_pebs_disable(struct perf_event *event) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - - intel_pmu_drain_large_pebs(cpuc); -- - cpuc->pebs_enabled &= ~(1ULL << hwc->idx); -+ hwc->config |= ARCH_PERFMON_EVENTSEL_INT; -+} -+ -+void intel_pmu_pebs_disable(struct perf_event *event) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct hw_perf_event *hwc = &event->hw; -+ -+ __intel_pmu_pebs_disable(event); - - if ((event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) && - (x86_pmu.version < 5)) -@@ -1622,8 +1637,6 @@ void intel_pmu_pebs_disable(struct perf_event *event) - - if (cpuc->enabled) - wrmsrq(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); -- -- hwc->config |= ARCH_PERFMON_EVENTSEL_INT; - } - - void intel_pmu_pebs_enable_all(void) -@@ -2668,11 +2681,26 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - } - } - -+static void __init intel_arch_pebs_init(void) -+{ -+ /* -+ * Current hybrid platforms always both support arch-PEBS or not -+ * on all kinds of cores. So directly set x86_pmu.arch_pebs flag -+ * if boot cpu supports arch-PEBS. -+ */ -+ x86_pmu.arch_pebs = 1; -+ x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; -+ x86_pmu.pebs_capable = ~0ULL; -+ -+ x86_pmu.pebs_enable = __intel_pmu_pebs_enable; -+ x86_pmu.pebs_disable = __intel_pmu_pebs_disable; -+} -+ - /* - * PEBS probe and setup - */ - --void __init intel_pebs_init(void) -+static void __init intel_ds_pebs_init(void) - { - /* - * No support for 32bit formats -@@ -2787,6 +2815,14 @@ void __init intel_pebs_init(void) - } - } - -+void __init intel_pebs_init(void) -+{ -+ if (x86_pmu.intel_cap.pebs_format == 0xf) -+ intel_arch_pebs_init(); -+ else -+ intel_ds_pebs_init(); -+} -+ - void perf_restore_debug_store(void) - { - struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds); -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index 2b969386dcdd..a5145e8f1ddb 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -708,6 +708,12 @@ enum hybrid_pmu_type { - hybrid_big_small_tiny = hybrid_big | hybrid_small_tiny, - }; - -+struct arch_pebs_cap { -+ u64 caps; -+ u64 counters; -+ u64 pdists; -+}; -+ - struct x86_hybrid_pmu { - struct pmu pmu; - const char *name; -@@ -752,6 +758,8 @@ struct x86_hybrid_pmu { - mid_ack :1, - enabled_ack :1; - -+ struct arch_pebs_cap arch_pebs_cap; -+ - u64 pebs_data_source[PERF_PEBS_DATA_SOURCE_MAX]; - }; - -@@ -906,7 +914,7 @@ struct x86_pmu { - union perf_capabilities intel_cap; - - /* -- * Intel DebugStore bits -+ * Intel DebugStore and PEBS bits - */ - unsigned int bts :1, - bts_active :1, -@@ -917,7 +925,8 @@ struct x86_pmu { - pebs_no_tlb :1, - pebs_no_isolation :1, - pebs_block :1, -- pebs_ept :1; -+ pebs_ept :1, -+ arch_pebs :1; - int pebs_record_size; - int pebs_buffer_size; - u64 pebs_events_mask; -@@ -929,6 +938,11 @@ struct x86_pmu { - u64 rtm_abort_event; - u64 pebs_capable; - -+ /* -+ * Intel Architectural PEBS -+ */ -+ struct arch_pebs_cap arch_pebs_cap; -+ - /* - * Intel LBR - */ -@@ -1217,7 +1231,7 @@ int x86_reserve_hardware(void); - - void x86_release_hardware(void); - --int x86_pmu_max_precise(void); -+int x86_pmu_max_precise(struct pmu *pmu); - - void hw_perf_lbr_event_destroy(struct perf_event *event); - -@@ -1792,6 +1806,11 @@ static inline int intel_pmu_max_num_pebs(struct pmu *pmu) - return fls((u32)hybrid(pmu, pebs_events_mask)); - } - -+static inline bool intel_pmu_has_pebs(void) -+{ -+ return x86_pmu.ds_pebs || x86_pmu.arch_pebs; -+} -+ - #else /* CONFIG_CPU_SUP_INTEL */ - - static inline void reserve_ds_buffers(void) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 1eb31cd9fceb..a81084f58de4 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -200,6 +200,8 @@ union cpuid10_edx { - #define ARCH_PERFMON_EXT_LEAF 0x00000023 - #define ARCH_PERFMON_NUM_COUNTER_LEAF 0x1 - #define ARCH_PERFMON_ACR_LEAF 0x2 -+#define ARCH_PERFMON_PEBS_CAP_LEAF 0x4 -+#define ARCH_PERFMON_PEBS_COUNTER_LEAF 0x5 - - union cpuid35_eax { - struct { -@@ -210,7 +212,10 @@ union cpuid35_eax { - unsigned int acr_subleaf:1; - /* Events Sub-Leaf */ - unsigned int events_subleaf:1; -- unsigned int reserved:28; -+ /* arch-PEBS Sub-Leaves */ -+ unsigned int pebs_caps_subleaf:1; -+ unsigned int pebs_cnts_subleaf:1; -+ unsigned int reserved:26; - } split; - unsigned int full; - }; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf b/SPECS/kernel-rt/0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf new file mode 100644 index 000000000..a723eaeb8 --- /dev/null +++ b/SPECS/kernel-rt/0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf @@ -0,0 +1,110 @@ +From 77f8e1175fdfcf2f6df88260a7a3cc7721c764f4 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 17 Dec 2025 13:22:20 -0800 +Subject: [PATCH 13/13] perf/x86/intel/uncore: Add missing PMON units for + Panther Lake + +Besides CBOX, Panther Lake includes several legacy uncore PMON units +not enumerated via discovery tables, including cNCU, SANTA, and +ia_core_bridge. + +The cNCU PMON is similar to Meteor Lake but has two boxes with two +counters each. SANTA and IA Core Bridge PMON units follow the legacy +model used on Lunar Lake, Meteor Lake, and others. + +Panther Lake implements the Global Control Register; the freeze_all bit +must be cleared before programming counters. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 1 + + arch/x86/events/intel/uncore_snb.c | 45 ++++++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index e47483d1207d4..efdaa0d111d2a 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1814,6 +1814,7 @@ static const struct uncore_plat_init ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, + .domain[0].discovery_base = UNCORE_DISCOVERY_MSR, ++ .domain[0].global_init = uncore_mmio_global_init, + }; + + static const struct uncore_plat_init icx_uncore_init __initconst = { +diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c +index 807e582b8f17d..c663b00b68fe6 100644 +--- a/arch/x86/events/intel/uncore_snb.c ++++ b/arch/x86/events/intel/uncore_snb.c +@@ -245,6 +245,17 @@ + #define MTL_UNC_HBO_CTR 0x2048 + #define MTL_UNC_HBO_CTRL 0x2042 + ++/* PTL Low Power Bridge register */ ++#define PTL_UNC_IA_CORE_BRIDGE_PER_CTR0 0x2028 ++#define PTL_UNC_IA_CORE_BRIDGE_PERFEVTSEL0 0x2022 ++ ++/* PTL Santa register */ ++#define PTL_UNC_SANTA_CTR0 0x2418 ++#define PTL_UNC_SANTA_CTRL0 0x2412 ++ ++/* PTL cNCU register */ ++#define PTL_UNC_CNCU_MSR_OFFSET 0x140 ++ + DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); + DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15"); + DEFINE_UNCORE_FORMAT_ATTR(chmask, chmask, "config:8-11"); +@@ -1921,8 +1932,36 @@ void ptl_uncore_mmio_init(void) + ptl_uncores); + } + ++static struct intel_uncore_type ptl_uncore_ia_core_bridge = { ++ .name = "ia_core_bridge", ++ .num_counters = 2, ++ .num_boxes = 1, ++ .perf_ctr_bits = 48, ++ .perf_ctr = PTL_UNC_IA_CORE_BRIDGE_PER_CTR0, ++ .event_ctl = PTL_UNC_IA_CORE_BRIDGE_PERFEVTSEL0, ++ .event_mask = ADL_UNC_RAW_EVENT_MASK, ++ .ops = &icl_uncore_msr_ops, ++ .format_group = &adl_uncore_format_group, ++}; ++ ++static struct intel_uncore_type ptl_uncore_santa = { ++ .name = "santa", ++ .num_counters = 2, ++ .num_boxes = 2, ++ .perf_ctr_bits = 48, ++ .perf_ctr = PTL_UNC_SANTA_CTR0, ++ .event_ctl = PTL_UNC_SANTA_CTRL0, ++ .event_mask = ADL_UNC_RAW_EVENT_MASK, ++ .msr_offset = SNB_UNC_CBO_MSR_OFFSET, ++ .ops = &icl_uncore_msr_ops, ++ .format_group = &adl_uncore_format_group, ++}; ++ + static struct intel_uncore_type *ptl_msr_uncores[] = { + &mtl_uncore_cbox, ++ &ptl_uncore_ia_core_bridge, ++ &ptl_uncore_santa, ++ &mtl_uncore_cncu, + NULL + }; + +@@ -1930,6 +1969,12 @@ void ptl_uncore_cpu_init(void) + { + mtl_uncore_cbox.num_boxes = 6; + mtl_uncore_cbox.ops = &lnl_uncore_msr_ops; ++ ++ mtl_uncore_cncu.num_counters = 2; ++ mtl_uncore_cncu.num_boxes = 2; ++ mtl_uncore_cncu.msr_offset = PTL_UNC_CNCU_MSR_OFFSET; ++ mtl_uncore_cncu.single_fixed = 0; ++ + uncore_msr_uncores = ptl_msr_uncores; + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl b/SPECS/kernel-rt/0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl new file mode 100644 index 000000000..7370634a1 --- /dev/null +++ b/SPECS/kernel-rt/0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl @@ -0,0 +1,222 @@ +From 89288056b82c56b3e09188f93a98d52319fda6bf Mon Sep 17 00:00:00 2001 +From: Kuppuswamy Sathyanarayanan +Date: Thu, 20 Nov 2025 16:05:38 -0800 +Subject: [PATCH 13/17] powercap: intel_rapl: Prepare read_raw() interface for + atomic-context callers + +The current read_raw() implementation of the TPMI, MMIO and MSR +interfaces does not distinguish between atomic and non-atomic callers. + +rapl_msr_read_raw() uses rdmsrq_safe_on_cpu(), which can sleep and +issue cross CPU calls. When MSR-based RAPL PMU support is enabled, PMU +event handlers can invoke this function from atomic context where +sleeping or rescheduling is not allowed. In atomic context, the caller +is already executing on the target CPU, so a direct rdmsrq() is +sufficient. + +To support such usage, introduce an atomic flag to the read_raw() +interface to allow callers pass the context information. Modify the +common RAPL code to propagate this flag, and set the flag to reflect +the calling contexts. + +Utilize the atomic flag in rapl_msr_read_raw() to perform direct MSR +read with rdmsrq() when running in atomic context, and a sanity check +to ensure target CPU matches the current CPU for such use cases. + +The TPMI and MMIO implementations do not require special atomic +handling, so the flag is ignored in those paths. + +This is a preparatory patch for adding MSR-based RAPL PMU support. + +Signed-off-by: Kuppuswamy Sathyanarayanan +Reviewed-by: Srinivas Pandruvada +[ rjw: Subject tweak ] +Link: https://patch.msgid.link/20251121000539.386069-2-sathyanarayanan.kuppuswamy@linux.intel.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/powercap/intel_rapl_common.c | 24 ++++++++++--------- + drivers/powercap/intel_rapl_msr.c | 16 ++++++++++++- + drivers/powercap/intel_rapl_tpmi.c | 2 +- + .../int340x_thermal/processor_thermal_rapl.c | 2 +- + include/linux/intel_rapl.h | 2 +- + 5 files changed, 31 insertions(+), 15 deletions(-) + +diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c +index 57bebd07c7d0d..47ec34d4c0997 100644 +--- a/drivers/powercap/intel_rapl_common.c ++++ b/drivers/powercap/intel_rapl_common.c +@@ -253,7 +253,8 @@ struct rapl_primitive_info { + static void rapl_init_domains(struct rapl_package *rp); + static int rapl_read_data_raw(struct rapl_domain *rd, + enum rapl_primitives prim, +- bool xlate, u64 *data); ++ bool xlate, u64 *data, ++ bool atomic); + static int rapl_write_data_raw(struct rapl_domain *rd, + enum rapl_primitives prim, + unsigned long long value); +@@ -289,7 +290,7 @@ static int get_energy_counter(struct powercap_zone *power_zone, + cpus_read_lock(); + rd = power_zone_to_rapl_domain(power_zone); + +- if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now)) { ++ if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now, false)) { + *energy_raw = energy_now; + cpus_read_unlock(); + +@@ -830,7 +831,8 @@ prim_fixups(struct rapl_domain *rd, enum rapl_primitives prim) + * 63-------------------------- 31--------------------------- 0 + */ + static int rapl_read_data_raw(struct rapl_domain *rd, +- enum rapl_primitives prim, bool xlate, u64 *data) ++ enum rapl_primitives prim, bool xlate, u64 *data, ++ bool atomic) + { + u64 value; + enum rapl_primitives prim_fixed = prim_fixups(rd, prim); +@@ -852,7 +854,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, + + ra.mask = rpi->mask; + +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, atomic)) { + pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg.val, rd->rp->name, rd->name); + return -EIO; + } +@@ -904,7 +906,7 @@ static int rapl_read_pl_data(struct rapl_domain *rd, int pl, + if (!is_pl_valid(rd, pl)) + return -EINVAL; + +- return rapl_read_data_raw(rd, prim, xlate, data); ++ return rapl_read_data_raw(rd, prim, xlate, data, false); + } + + static int rapl_write_pl_data(struct rapl_domain *rd, int pl, +@@ -941,7 +943,7 @@ static int rapl_check_unit_core(struct rapl_domain *rd) + + ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; + ra.mask = ~0; +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, false)) { + pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", + ra.reg.val, rd->rp->name, rd->name); + return -ENODEV; +@@ -969,7 +971,7 @@ static int rapl_check_unit_atom(struct rapl_domain *rd) + + ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; + ra.mask = ~0; +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, false)) { + pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", + ra.reg.val, rd->rp->name, rd->name); + return -ENODEV; +@@ -1156,7 +1158,7 @@ static int rapl_check_unit_tpmi(struct rapl_domain *rd) + + ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; + ra.mask = ~0; +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, false)) { + pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", + ra.reg.val, rd->rp->name, rd->name); + return -ENODEV; +@@ -1328,7 +1330,7 @@ static void rapl_update_domain_data(struct rapl_package *rp) + struct rapl_primitive_info *rpi = get_rpi(rp, prim); + + if (!rapl_read_data_raw(&rp->domains[dmn], prim, +- rpi->unit, &val)) ++ rpi->unit, &val, false)) + rp->domains[dmn].rdd.primitives[prim] = val; + } + } +@@ -1428,7 +1430,7 @@ static int rapl_check_domain(int domain, struct rapl_package *rp) + */ + + ra.mask = ENERGY_STATUS_MASK; +- if (rp->priv->read_raw(get_rid(rp), &ra) || !ra.value) ++ if (rp->priv->read_raw(get_rid(rp), &ra, false) || !ra.value) + return -ENODEV; + + return 0; +@@ -1639,7 +1641,7 @@ static u64 event_read_counter(struct perf_event *event) + if (event->hw.idx < 0) + return 0; + +- ret = rapl_read_data_raw(&rp->domains[event->hw.idx], ENERGY_COUNTER, false, &val); ++ ret = rapl_read_data_raw(&rp->domains[event->hw.idx], ENERGY_COUNTER, false, &val, true); + + /* Return 0 for failed read */ + if (ret) +diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c +index c6b9a7debc354..6e3c50af09128 100644 +--- a/drivers/powercap/intel_rapl_msr.c ++++ b/drivers/powercap/intel_rapl_msr.c +@@ -102,12 +102,26 @@ static int rapl_cpu_down_prep(unsigned int cpu) + return 0; + } + +-static int rapl_msr_read_raw(int cpu, struct reg_action *ra) ++static int rapl_msr_read_raw(int cpu, struct reg_action *ra, bool atomic) + { ++ /* ++ * When called from atomic-context (eg PMU event handler) ++ * perform MSR read directly using rdmsrq(). ++ */ ++ if (atomic) { ++ if (unlikely(smp_processor_id() != cpu)) ++ return -EIO; ++ ++ rdmsrq(ra->reg.msr, ra->value); ++ goto out; ++ } ++ + if (rdmsrq_safe_on_cpu(cpu, ra->reg.msr, &ra->value)) { + pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg.msr, cpu); + return -EIO; + } ++ ++out: + ra->value &= ra->mask; + return 0; + } +diff --git a/drivers/powercap/intel_rapl_tpmi.c b/drivers/powercap/intel_rapl_tpmi.c +index 82201bf4685d4..0a0b85f4528b1 100644 +--- a/drivers/powercap/intel_rapl_tpmi.c ++++ b/drivers/powercap/intel_rapl_tpmi.c +@@ -60,7 +60,7 @@ static DEFINE_MUTEX(tpmi_rapl_lock); + + static struct powercap_control_type *tpmi_control_type; + +-static int tpmi_rapl_read_raw(int id, struct reg_action *ra) ++static int tpmi_rapl_read_raw(int id, struct reg_action *ra, bool atomic) + { + if (!ra->reg.mmio) + return -EINVAL; +diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +index bde2cc386afdd..bf51a17c5be61 100644 +--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c ++++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +@@ -19,7 +19,7 @@ static const struct rapl_mmio_regs rapl_mmio_default = { + .limits[RAPL_DOMAIN_DRAM] = BIT(POWER_LIMIT2), + }; + +-static int rapl_mmio_read_raw(int cpu, struct reg_action *ra) ++static int rapl_mmio_read_raw(int cpu, struct reg_action *ra, bool atomic) + { + if (!ra->reg.mmio) + return -EINVAL; +diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h +index c0397423d3a89..e9ade2ff4af66 100644 +--- a/include/linux/intel_rapl.h ++++ b/include/linux/intel_rapl.h +@@ -152,7 +152,7 @@ struct rapl_if_priv { + union rapl_reg reg_unit; + union rapl_reg regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX]; + int limits[RAPL_DOMAIN_MAX]; +- int (*read_raw)(int id, struct reg_action *ra); ++ int (*read_raw)(int id, struct reg_action *ra, bool atomic); + int (*write_raw)(int id, struct reg_action *ra); + void *defaults; + void *rpi; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo b/SPECS/kernel-rt/0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo new file mode 100644 index 000000000..4a90d5a58 --- /dev/null +++ b/SPECS/kernel-rt/0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo @@ -0,0 +1,35 @@ +From 18949ad92e8c5aef6f621b1ffe77d1fbc619685c Mon Sep 17 00:00:00 2001 +From: Malaya Kumar Rout +Date: Sat, 22 Nov 2025 19:14:54 +0530 +Subject: [PATCH 13/21] tools/power x86_energy_perf_policy: Fix format string + in error message + +The error message in validate_cpu_selected_set() uses an incomplete +format specifier "cpu%" instead of "cpu%d", resulting in the error +message printing "Requested cpu% is not present" rather than +showing the actual CPU number. + +Fix the format string to properly display the CPU number. + +Signed-off-by: Malaya Kumar Rout +Signed-off-by: Len Brown +--- + tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index e68eaa9f7cd4d..b2125275c69e0 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -372,7 +372,7 @@ void validate_cpu_selected_set(void) + for (cpu = 0; cpu <= max_cpu_num; ++cpu) { + if (CPU_ISSET_S(cpu, cpu_setsize, cpu_selected_set)) + if (!CPU_ISSET_S(cpu, cpu_setsize, cpu_present_set)) +- errx(1, "Requested cpu% is not present", cpu); ++ errx(1, "Requested cpu%d is not present", cpu); + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet b/SPECS/kernel-rt/0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet deleted file mode 100644 index 70750dc1c..000000000 --- a/SPECS/kernel-rt/0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet +++ /dev/null @@ -1,85 +0,0 @@ -From a77961bfab3a3364a7a63fb67d1ee0be53392b47 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:44 -0700 -Subject: [PATCH 14/24] KVM: x86: Enable guest SSP read/write interface with - new uAPIs - -Enable guest shadow stack pointer(SSP) access interface with new uAPIs. -CET guest SSP is HW register which has corresponding VMCS field to save -/restore guest values when VM-{Exit,Entry} happens. KVM handles SSP as -a synthetic MSR for userspace access. - -Use a translation helper to set up mapping for SSP synthetic index and -KVM-internal MSR index so that userspace doesn't need to take care of -KVM's management for synthetic MSRs and avoid conflicts. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/uapi/asm/kvm.h | 3 +++ - arch/x86/kvm/x86.c | 10 +++++++++- - arch/x86/kvm/x86.h | 10 ++++++++++ - 3 files changed, 22 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h -index e72d9e6c1739..a4870d9c9279 100644 ---- a/arch/x86/include/uapi/asm/kvm.h -+++ b/arch/x86/include/uapi/asm/kvm.h -@@ -421,6 +421,9 @@ struct kvm_x86_reg_id { - __u16 rsvd16; - }; - -+/* KVM synthetic MSR index staring from 0 */ -+#define KVM_SYNTHETIC_GUEST_SSP 0 -+ - #define KVM_SYNC_X86_REGS (1UL << 0) - #define KVM_SYNC_X86_SREGS (1UL << 1) - #define KVM_SYNC_X86_EVENTS (1UL << 2) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index cbefc0228ba5..1a437aad26f9 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -5953,7 +5953,15 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, - - static int kvm_translate_synthetic_msr(struct kvm_x86_reg_id *reg) - { -- return -EINVAL; -+ switch (reg->index) { -+ case KVM_SYNTHETIC_GUEST_SSP: -+ reg->type = KVM_X86_REG_MSR; -+ reg->index = MSR_KVM_INTERNAL_GUEST_SSP; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; - } - - long kvm_arch_vcpu_ioctl(struct file *filp, -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 97e258a3e88e..cf74d877737a 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -101,6 +101,16 @@ do { \ - #define KVM_SVM_DEFAULT_PLE_WINDOW_MAX USHRT_MAX - #define KVM_SVM_DEFAULT_PLE_WINDOW 3000 - -+/* -+ * KVM's internal, non-ABI indices for synthetic MSRs. The values themselves -+ * are arbitrary and have no meaning, the only requirement is that they don't -+ * conflict with "real" MSRs that KVM supports. Use values at the upper end -+ * of KVM's reserved paravirtual MSR range to minimize churn, i.e. these values -+ * will be usable until KVM exhausts its supply of paravirtual MSR indices. -+ */ -+ -+#define MSR_KVM_INTERNAL_GUEST_SSP 0x4b564dff -+ - static inline unsigned int __grow_ple_window(unsigned int val, - unsigned int base, unsigned int modifier, unsigned int max) - { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi b/SPECS/kernel-rt/0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi deleted file mode 100644 index 180b99a87..000000000 --- a/SPECS/kernel-rt/0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi +++ /dev/null @@ -1,61 +0,0 @@ -From b638a94b8dd192801ea2f76e1b7261caf2cbf927 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 13:26:01 -0700 -Subject: [PATCH 14/44] KVM: x86: Mark CR4.FRED as not reserved - -The CR4.FRED bit, i.e., CR4[32], is no longer a reserved bit when -guest cpu cap has FRED, i.e., - 1) All of FRED KVM support is in place. - 2) Guest enumerates FRED. - -Otherwise it is still a reserved bit. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Rebase on top of "guest_cpu_cap". - -Change in v3: -* Don't allow CR4.FRED=1 before all of FRED KVM support is in place - (Sean Christopherson). ---- - arch/x86/include/asm/kvm_host.h | 2 +- - arch/x86/kvm/x86.h | 2 ++ - 2 files changed, 3 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 3b38c82b4c1c..2634c16b09c4 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -142,7 +142,7 @@ - | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ - | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ - | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ -- | X86_CR4_LAM_SUP | X86_CR4_CET)) -+ | X86_CR4_LAM_SUP | X86_CR4_CET | X86_CR4_FRED)) - - #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR) - -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 25c2d7173b89..5f2279e11f87 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -688,6 +688,8 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) - if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ - !__cpu_has(__c, X86_FEATURE_IBT)) \ - __reserved_bits |= X86_CR4_CET; \ -+ if (!__cpu_has(__c, X86_FEATURE_FRED)) \ -+ __reserved_bits |= X86_CR4_FRED; \ - __reserved_bits; \ - }) - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi b/SPECS/kernel-rt/0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi new file mode 100644 index 000000000..c85247f89 --- /dev/null +++ b/SPECS/kernel-rt/0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi @@ -0,0 +1,204 @@ +From 068fe8908d3439277254912358e677dd6f5e68d0 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Thu, 13 Jun 2024 09:48:43 -0700 +Subject: [PATCH 14/44] KVM: x86: Save/restore the nested flag of an exception + +Save/restore the nested flag of an exception during VM save/restore +and live migration to ensure a correct event stack level is chosen +when a nested exception is injected through FRED event delivery. + +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v8: +* Update KVM_CAP_EXCEPTION_NESTED_FLAG, as the number in v7 is used + by another new cap. + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Add live migration support for exception nested flag (Chao Gao). +--- + Documentation/virt/kvm/api.rst | 21 ++++++++++++++++++++- + arch/x86/include/asm/kvm_host.h | 1 + + arch/x86/include/uapi/asm/kvm.h | 4 +++- + arch/x86/kvm/x86.c | 19 ++++++++++++++++++- + include/uapi/linux/kvm.h | 1 + + 5 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst +index 57061fa29e6a0..88dbd80dcd3c8 100644 +--- a/Documentation/virt/kvm/api.rst ++++ b/Documentation/virt/kvm/api.rst +@@ -1184,6 +1184,10 @@ The following bits are defined in the flags field: + fields contain a valid state. This bit will be set whenever + KVM_CAP_EXCEPTION_PAYLOAD is enabled. + ++- KVM_VCPUEVENT_VALID_NESTED_FLAG may be set to inform that the ++ exception is a nested exception. This bit will be set whenever ++ KVM_CAP_EXCEPTION_NESTED_FLAG is enabled. ++ + - KVM_VCPUEVENT_VALID_TRIPLE_FAULT may be set to signal that the + triple_fault_pending field contains a valid state. This bit will + be set whenever KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled. +@@ -1286,6 +1290,10 @@ can be set in the flags field to signal that the + exception_has_payload, exception_payload, and exception.pending fields + contain a valid state and shall be written into the VCPU. + ++If KVM_CAP_EXCEPTION_NESTED_FLAG is enabled, KVM_VCPUEVENT_VALID_NESTED_FLAG ++can be set in the flags field to inform that the exception is a nested ++exception and exception_is_nested shall be written into the VCPU. ++ + If KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled, KVM_VCPUEVENT_VALID_TRIPLE_FAULT + can be set in flags field to signal that the triple_fault field contains + a valid state and shall be written into the VCPU. +@@ -8692,7 +8700,7 @@ given VM. + When this capability is enabled, KVM resets the VCPU when setting + MP_STATE_INIT_RECEIVED through IOCTL. The original MP_STATE is preserved. + +-7.43 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED ++7.44 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED + ------------------------------------------- + + :Architectures: arm64 +@@ -8703,6 +8711,17 @@ This capability indicate to the userspace whether a PFNMAP memory region + can be safely mapped as cacheable. This relies on the presence of + force write back (FWB) feature support on the hardware. + ++7.45 KVM_CAP_EXCEPTION_NESTED_FLAG ++---------------------------------- ++ ++:Architectures: x86 ++:Parameters: args[0] whether feature should be enabled or not ++ ++With this capability enabled, an exception is save/restored with the ++additional information of whether it was nested or not. FRED event ++delivery uses this information to ensure a correct event stack level ++is chosen when a VM entry injects a nested exception. ++ + 8. Other capabilities. + ====================== + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 3b6dadf368eb6..5fff22d837aa0 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -1491,6 +1491,7 @@ struct kvm_arch { + bool has_mapped_host_mmio; + bool guest_can_read_msr_platform_info; + bool exception_payload_enabled; ++ bool exception_nested_flag_enabled; + + bool triple_fault_event; + +diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h +index d420c9c066d48..fbeeea236fc21 100644 +--- a/arch/x86/include/uapi/asm/kvm.h ++++ b/arch/x86/include/uapi/asm/kvm.h +@@ -331,6 +331,7 @@ struct kvm_reinject_control { + #define KVM_VCPUEVENT_VALID_SMM 0x00000008 + #define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 + #define KVM_VCPUEVENT_VALID_TRIPLE_FAULT 0x00000020 ++#define KVM_VCPUEVENT_VALID_NESTED_FLAG 0x00000040 + + /* Interrupt shadow states */ + #define KVM_X86_SHADOW_INT_MOV_SS 0x01 +@@ -368,7 +369,8 @@ struct kvm_vcpu_events { + struct { + __u8 pending; + } triple_fault; +- __u8 reserved[26]; ++ __u8 reserved[25]; ++ __u8 exception_is_nested; + __u8 exception_has_payload; + __u64 exception_payload; + }; +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 881c5543a77a5..2f364c4e35879 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -4967,6 +4967,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) + case KVM_CAP_GET_MSR_FEATURES: + case KVM_CAP_MSR_PLATFORM_INFO: + case KVM_CAP_EXCEPTION_PAYLOAD: ++ case KVM_CAP_EXCEPTION_NESTED_FLAG: + case KVM_CAP_X86_TRIPLE_FAULT_EVENT: + case KVM_CAP_SET_GUEST_DEBUG: + case KVM_CAP_LAST_CPU: +@@ -5712,6 +5713,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, + events->exception.error_code = ex->error_code; + events->exception_has_payload = ex->has_payload; + events->exception_payload = ex->payload; ++ events->exception_is_nested = ex->nested; + + events->interrupt.injected = + vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft; +@@ -5737,6 +5739,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, + | KVM_VCPUEVENT_VALID_SMM); + if (vcpu->kvm->arch.exception_payload_enabled) + events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD; ++ if (vcpu->kvm->arch.exception_nested_flag_enabled) ++ events->flags |= KVM_VCPUEVENT_VALID_NESTED_FLAG; + if (vcpu->kvm->arch.triple_fault_event) { + events->triple_fault.pending = kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu); + events->flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; +@@ -5751,7 +5755,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, + | KVM_VCPUEVENT_VALID_SHADOW + | KVM_VCPUEVENT_VALID_SMM + | KVM_VCPUEVENT_VALID_PAYLOAD +- | KVM_VCPUEVENT_VALID_TRIPLE_FAULT)) ++ | KVM_VCPUEVENT_VALID_TRIPLE_FAULT ++ | KVM_VCPUEVENT_VALID_NESTED_FLAG)) + return -EINVAL; + + if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) { +@@ -5766,6 +5771,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, + events->exception_has_payload = 0; + } + ++ if (events->flags & KVM_VCPUEVENT_VALID_NESTED_FLAG) { ++ if (!vcpu->kvm->arch.exception_nested_flag_enabled) ++ return -EINVAL; ++ } else { ++ events->exception_is_nested = 0; ++ } ++ + if ((events->exception.injected || events->exception.pending) && + (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR)) + return -EINVAL; +@@ -5791,6 +5803,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, + vcpu->arch.exception.error_code = events->exception.error_code; + vcpu->arch.exception.has_payload = events->exception_has_payload; + vcpu->arch.exception.payload = events->exception_payload; ++ vcpu->arch.exception.nested = events->exception_is_nested; + + vcpu->arch.interrupt.injected = events->interrupt.injected; + vcpu->arch.interrupt.nr = events->interrupt.nr; +@@ -6911,6 +6924,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, + kvm->arch.exception_payload_enabled = cap->args[0]; + r = 0; + break; ++ case KVM_CAP_EXCEPTION_NESTED_FLAG: ++ kvm->arch.exception_nested_flag_enabled = cap->args[0]; ++ r = 0; ++ break; + case KVM_CAP_X86_TRIPLE_FAULT_EVENT: + kvm->arch.triple_fault_event = cap->args[0]; + r = 0; +diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h +index 52f6000ab0208..ec3cc37b93739 100644 +--- a/include/uapi/linux/kvm.h ++++ b/include/uapi/linux/kvm.h +@@ -963,6 +963,7 @@ struct kvm_enable_cap { + #define KVM_CAP_RISCV_MP_STATE_RESET 242 + #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 + #define KVM_CAP_GUEST_MEMFD_FLAGS 244 ++#define KVM_CAP_EXCEPTION_NESTED_FLAG 245 + + struct kvm_irq_routing_irqchip { + __u32 irqchip; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel-rt/0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet deleted file mode 100644 index b339486cf..000000000 --- a/SPECS/kernel-rt/0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet +++ /dev/null @@ -1,97 +0,0 @@ -From 235954e7641a45928f8038d9e0f8f6cbfb57d1cb Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Thu, 10 Jun 2021 12:44:46 +0800 -Subject: [PATCH 14/19] igc: Enable HW TX Timestamp for AF_XDP ZC - -Enable HW TX Timestamp per-packet using dma write back descriptor. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc.h | 1 + - drivers/net/ethernet/intel/igc/igc_main.c | 7 ++++++- - drivers/net/ethernet/intel/igc/igc_ptp.c | 24 +++++++++++++++++++++++ - 3 files changed, 31 insertions(+), 1 deletion(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h -index 64d22481be91..3f8969c73a6d 100644 ---- a/drivers/net/ethernet/intel/igc/igc.h -+++ b/drivers/net/ethernet/intel/igc/igc.h -@@ -788,6 +788,7 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, - void igc_ptp_tx_hang(struct igc_adapter *adapter); - void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); - void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); -+ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp); - - int igc_led_setup(struct igc_adapter *adapter); - void igc_led_free(struct igc_adapter *adapter); -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index e4a8dfaf1b02..8b5f747db38b 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -3160,6 +3160,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) - tx_desc->read.buffer_addr = cpu_to_le64(dma); - - bi->type = IGC_TX_BUFFER_TYPE_XSK; -+ bi->tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; - bi->protocol = 0; - bi->bytecount = xdp_desc.len; - bi->gso_segs = 1; -@@ -3200,6 +3201,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - unsigned int i = tx_ring->next_to_clean; - struct igc_tx_buffer *tx_buffer; - union igc_adv_tx_desc *tx_desc; -+ ktime_t timestamp = 0; - u32 xsk_frames = 0; - - if (test_bit(__IGC_DOWN, &adapter->state)) -@@ -3238,7 +3240,10 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { - u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); - -- igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); -+ if (tx_ring->xsk_pool && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) -+ timestamp = igc_tx_dma_hw_tstamp(adapter, tstamp); -+ else -+ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); - } - - /* clear next_to_watch to prevent false hangs */ -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index 7c220a6f601f..4fef3f1abe29 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -909,6 +909,30 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, - skb_tstamp_tx(skb, &shhwtstamps); - } - -+ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp) -+{ -+ struct skb_shared_hwtstamps shhwtstamps; -+ int adjust = 0; -+ -+ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); -+ -+ switch (adapter->link_speed) { -+ case SPEED_10: -+ adjust = IGC_I225_TX_LATENCY_10; -+ break; -+ case SPEED_100: -+ adjust = IGC_I225_TX_LATENCY_100; -+ break; -+ case SPEED_1000: -+ adjust = IGC_I225_TX_LATENCY_1000; -+ break; -+ case SPEED_2500: -+ adjust = IGC_I225_TX_LATENCY_2500; -+ break; -+ } -+ return ktime_add_ns(shhwtstamps.hwtstamp, adjust); -+} -+ - /** - * igc_ptp_tx_tstamp_event - * @adapter: board private structure --- -2.43.0 - diff --git a/SPECS/kernel-rt/0014-igc-Remove-XDP-metadata-invalidation.ethernet b/SPECS/kernel-rt/0014-igc-Remove-XDP-metadata-invalidation.ethernet new file mode 100644 index 000000000..85fc57f6f --- /dev/null +++ b/SPECS/kernel-rt/0014-igc-Remove-XDP-metadata-invalidation.ethernet @@ -0,0 +1,32 @@ +From ed83e3fc4986d1af74f20d37b3fcd9f4f82edffa Mon Sep 17 00:00:00 2001 +From: KhaiWenTan +Date: Wed, 24 Dec 2025 15:13:12 +0800 +Subject: [PATCH 14/14] igc: Remove XDP metadata invalidation + +Delete the else branch that invalidates XDP metadata when RX +timestamping is off. Only set metadata when hardware RX timestamping +is enabled, simplifying the receive path logic. + +Fixes: c2697c3 ("igc: Enable HW RX Timestamp for AF_XDP ZC") +Signed-off-by: KhaiWenTan +Signed-off-by: Song Yoong Siang +--- + drivers/net/ethernet/intel/igc/igc_main.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index bfef9555a12ea..08d924f77e99e 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -2868,8 +2868,6 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) + md = bi->xdp->data - sizeof(*md); + md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); + bi->xdp->data_meta = md; +- } else { +- xdp_set_data_meta_invalid(bi->xdp); + } + + res = __igc_xdp_run_prog(adapter, prog, bi->xdp); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu b/SPECS/kernel-rt/0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu deleted file mode 100644 index f2ac7adbb..000000000 --- a/SPECS/kernel-rt/0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu +++ /dev/null @@ -1,43 +0,0 @@ -From 9cb2621f1ca767b50ed3aa7517aaed767301406d Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Sat, 25 Oct 2025 16:42:45 +0800 -Subject: [PATCH 14/21] media: ipu: Dma sync at buffer_prepare callback as DMA - is non-coherent - -Test Platform: -PTLRVP -LNLRVP - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys-queue.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c -index 527fdfe2f11c..3ae832c1909c 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c -@@ -91,7 +91,9 @@ static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, - static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - { - struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); - struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); - struct device *dev = &av->isys->adev->auxdev.dev; - u32 bytesperline = av->pix_fmt.bytesperline; - u32 height = av->pix_fmt.height; -@@ -106,6 +108,9 @@ static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - av->vdev.name, bytesperline, height); - vb2_set_plane_payload(vb, 0, bytesperline * height); - -+ /* assume IPU is not DMA coherent */ -+ ipu7_dma_sync_sgtable(isys->adev, sg); -+ - return 0; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet b/SPECS/kernel-rt/0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet new file mode 100644 index 000000000..2940af489 --- /dev/null +++ b/SPECS/kernel-rt/0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet @@ -0,0 +1,69 @@ +From cb8806ed837b76bcbca199e1ec2d945d9fecbb8e Mon Sep 17 00:00:00 2001 +From: "Song, Yoong Siang" +Date: Sun, 11 Apr 2021 22:49:37 +0800 +Subject: [PATCH 14/18] net: stmmac: introduce AF_XDP ZC RX HW timestamps + +Users can requests for timestamps by requesting HWTSTAMP_FILTER_ALL. +The timestamp is passed up the stack via xdp_buff's data_meta field. +This is applicable to AF_XDP ZC only for now. + +Signed-off-by: Voon Weifeng +Signed-off-by: Song, Yoong Siang +--- + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 13 ++++++++++++- + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index 56acc67bd6044..48c5907c97ffb 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -264,6 +264,7 @@ struct stmmac_priv { + struct mac_device_info *hw; + int (*hwif_quirks)(struct stmmac_priv *priv); + struct mutex lock; ++ int hwts_all; + + struct stmmac_dma_conf dma_conf; + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 641b011035087..056a91165df82 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -616,6 +616,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, + case HWTSTAMP_FILTER_ALL: + /* time stamp any incoming packet */ + config->rx_filter = HWTSTAMP_FILTER_ALL; ++ priv->hwts_all = HWTSTAMP_FILTER_ALL; + tstamp_all = PTP_TCR_TSENALL; + break; + +@@ -5357,7 +5358,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) + buf->xdp = NULL; + dirty++; + error = 1; +- if (!priv->hwts_rx_en) ++ if (!priv->hwts_rx_en || !priv->hwts_all) + rx_errors++; + } + +@@ -5382,6 +5383,16 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) + ctx->desc = p; + ctx->ndesc = np; + ++ if (unlikely(priv->hwts_all)) { ++ /* We use XDP meta data to store T/S */ ++ buf->xdp->data_meta = buf->xdp->data - sizeof(ktime_t); ++ ++ stmmac_get_rx_hwtstamp(priv, p, np, ++ (ktime_t *)buf->xdp->data_meta); ++ } else { ++ buf->xdp->data_meta = buf->xdp->data; ++ } ++ + /* XDP ZC Frame only support primary buffers for now */ + buf1_len = stmmac_rx_buf1_len(priv, p, status, len); + len += buf1_len; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf b/SPECS/kernel-rt/0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf deleted file mode 100644 index 873c674a8..000000000 --- a/SPECS/kernel-rt/0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf +++ /dev/null @@ -1,136 +0,0 @@ -From af6b9db282d3fd4d7cb4bb6bf09810fcd3b39ec2 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 05:27:22 +0000 -Subject: [PATCH 014/100] perf/x86/intel/ds: Factor out PEBS record processing - code to functions - -Beside some PEBS record layout difference, arch-PEBS can share most of -PEBS record processing code with adaptive PEBS. Thus, factor out these -common processing code to independent inline functions, so they can be -reused by subsequent arch-PEBS handler. - -Suggested-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/ds.c | 81 ++++++++++++++++++++++++++------------ - 1 file changed, 56 insertions(+), 25 deletions(-) - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 19d707933d61..db40dc76eb20 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2613,6 +2613,55 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d - } - } - -+static inline void __intel_pmu_handle_pebs_record(struct pt_regs *iregs, -+ struct pt_regs *regs, -+ struct perf_sample_data *data, -+ void *at, u64 pebs_status, -+ short *counts, void **last, -+ setup_fn setup_sample) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct perf_event *event; -+ int bit; -+ -+ for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) { -+ event = cpuc->events[bit]; -+ -+ if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) -+ continue; -+ -+ if (counts[bit]++) -+ __intel_pmu_pebs_event(event, iregs, regs, data, -+ last[bit], setup_sample); -+ -+ last[bit] = at; -+ } -+} -+ -+static inline void -+__intel_pmu_handle_last_pebs_record(struct pt_regs *iregs, struct pt_regs *regs, -+ struct perf_sample_data *data, u64 mask, -+ short *counts, void **last, -+ setup_fn setup_sample) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct perf_event *event; -+ int bit; -+ -+ for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) { -+ if (!counts[bit]) -+ continue; -+ -+ event = cpuc->events[bit]; -+ if (!event) -+ continue; -+ -+ __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit], -+ counts[bit], setup_sample); -+ } -+ -+} -+ - static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_data *data) - { - short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {}; -@@ -2622,9 +2671,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - struct x86_perf_regs perf_regs; - struct pt_regs *regs = &perf_regs.regs; - struct pebs_basic *basic; -- struct perf_event *event; - void *base, *at, *top; -- int bit; - u64 mask; - - if (!x86_pmu.pebs_active) -@@ -2637,6 +2684,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - - mask = hybrid(cpuc->pmu, pebs_events_mask) | - (hybrid(cpuc->pmu, fixed_cntr_mask64) << INTEL_PMC_IDX_FIXED); -+ mask &= cpuc->pebs_enabled; - - if (unlikely(base >= top)) { - intel_pmu_pebs_event_update_no_drain(cpuc, mask); -@@ -2654,31 +2702,14 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - if (basic->format_size != cpuc->pebs_record_size) - continue; - -- pebs_status = basic->applicable_counters & cpuc->pebs_enabled & mask; -- for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) { -- event = cpuc->events[bit]; -- if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) -- continue; -- -- if (counts[bit]++) { -- __intel_pmu_pebs_event(event, iregs, regs, data, last[bit], -- setup_pebs_adaptive_sample_data); -- } -- last[bit] = at; -- } -+ pebs_status = mask & basic->applicable_counters; -+ __intel_pmu_handle_pebs_record(iregs, regs, data, at, -+ pebs_status, counts, last, -+ setup_pebs_adaptive_sample_data); - } - -- for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) { -- if (!counts[bit]) -- continue; -- -- event = cpuc->events[bit]; -- if (!event) -- continue; -- -- __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit], -- counts[bit], setup_pebs_adaptive_sample_data); -- } -+ __intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, last, -+ setup_pebs_adaptive_sample_data); - } - - static void __init intel_arch_pebs_init(void) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl b/SPECS/kernel-rt/0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl new file mode 100644 index 000000000..50077e267 --- /dev/null +++ b/SPECS/kernel-rt/0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl @@ -0,0 +1,147 @@ +From 4bd64bb8656d5c02a470af121b196d928344e3cc Mon Sep 17 00:00:00 2001 +From: Kuppuswamy Sathyanarayanan +Date: Thu, 20 Nov 2025 16:05:39 -0800 +Subject: [PATCH 14/17] powercap: intel_rapl: Enable MSR-based RAPL PMU support + +Currently, RAPL PMU support requires adding CPU model entries to +arch/x86/events/rapl.c for each new generation. However, RAPL MSRs are +not architectural and require platform-specific customization, making +arch/x86 an inappropriate location for this functionality. + +The powercap subsystem already handles RAPL functionality and is the +natural place to consolidate all RAPL features. The powercap RAPL +driver already includes PMU support for TPMI-based RAPL interfaces, +making it straightforward to extend this support to MSR-based RAPL +interfaces as well. + +This consolidation eliminates the need to maintain RAPL support in +multiple subsystems and provides a unified approach for both TPMI and +MSR-based RAPL implementations. + +The MSR-based PMU support includes the following updates: + + 1. Register MSR-based PMU support for the supported platforms + and unregister it when no online CPUs remain in the package. + + 2. Remove existing checks that restrict RAPL PMU support to TPMI-based + interfaces and extend the logic to allow MSR-based RAPL interfaces. + + 3. Define a CPU model list to determine which processors should + register RAPL PMU interface through the powercap driver for + MSR-based RAPL, excluding those that support TPMI interface. + This list prevents conflicts with existing arch/x86 PMU code + that already registers RAPL PMU for some processors. Add + Panther Lake & Wildcat Lake to the CPU models list. + +Signed-off-by: Kuppuswamy Sathyanarayanan +Reviewed-by: Srinivas Pandruvada +[ rjw: Changelog edits ] +Link: https://patch.msgid.link/20251121000539.386069-3-sathyanarayanan.kuppuswamy@linux.intel.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/powercap/intel_rapl_common.c | 12 ++++++------ + drivers/powercap/intel_rapl_msr.c | 24 ++++++++++++++++++++++-- + 2 files changed, 28 insertions(+), 8 deletions(-) + +diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c +index 47ec34d4c0997..b9d87e56cbbc8 100644 +--- a/drivers/powercap/intel_rapl_common.c ++++ b/drivers/powercap/intel_rapl_common.c +@@ -1597,11 +1597,11 @@ static int get_pmu_cpu(struct rapl_package *rp) + if (!rp->has_pmu) + return nr_cpu_ids; + +- /* Only TPMI RAPL is supported for now */ +- if (rp->priv->type != RAPL_IF_TPMI) ++ /* Only TPMI & MSR RAPL are supported for now */ ++ if (rp->priv->type != RAPL_IF_TPMI && rp->priv->type != RAPL_IF_MSR) + return nr_cpu_ids; + +- /* TPMI RAPL uses any CPU in the package for PMU */ ++ /* TPMI/MSR RAPL uses any CPU in the package for PMU */ + for_each_online_cpu(cpu) + if (topology_physical_package_id(cpu) == rp->id) + return cpu; +@@ -1614,11 +1614,11 @@ static bool is_rp_pmu_cpu(struct rapl_package *rp, int cpu) + if (!rp->has_pmu) + return false; + +- /* Only TPMI RAPL is supported for now */ +- if (rp->priv->type != RAPL_IF_TPMI) ++ /* Only TPMI & MSR RAPL are supported for now */ ++ if (rp->priv->type != RAPL_IF_TPMI && rp->priv->type != RAPL_IF_MSR) + return false; + +- /* TPMI RAPL uses any CPU in the package for PMU */ ++ /* TPMI/MSR RAPL uses any CPU in the package for PMU */ + return topology_physical_package_id(cpu) == rp->id; + } + +diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c +index 6e3c50af09128..0ce1096b63145 100644 +--- a/drivers/powercap/intel_rapl_msr.c ++++ b/drivers/powercap/intel_rapl_msr.c +@@ -33,6 +33,8 @@ + /* private data for RAPL MSR Interface */ + static struct rapl_if_priv *rapl_msr_priv; + ++static bool rapl_msr_pmu __ro_after_init; ++ + static struct rapl_if_priv rapl_msr_priv_intel = { + .type = RAPL_IF_MSR, + .reg_unit.msr = MSR_RAPL_POWER_UNIT, +@@ -79,6 +81,8 @@ static int rapl_cpu_online(unsigned int cpu) + rp = rapl_add_package_cpuslocked(cpu, rapl_msr_priv, true); + if (IS_ERR(rp)) + return PTR_ERR(rp); ++ if (rapl_msr_pmu) ++ rapl_package_add_pmu(rp); + } + cpumask_set_cpu(cpu, &rp->cpumask); + return 0; +@@ -95,10 +99,14 @@ static int rapl_cpu_down_prep(unsigned int cpu) + + cpumask_clear_cpu(cpu, &rp->cpumask); + lead_cpu = cpumask_first(&rp->cpumask); +- if (lead_cpu >= nr_cpu_ids) ++ if (lead_cpu >= nr_cpu_ids) { ++ if (rapl_msr_pmu) ++ rapl_package_remove_pmu(rp); + rapl_remove_package_cpuslocked(rp); +- else if (rp->lead_cpu == cpu) ++ } else if (rp->lead_cpu == cpu) { + rp->lead_cpu = lead_cpu; ++ } ++ + return 0; + } + +@@ -171,6 +179,13 @@ static const struct x86_cpu_id pl4_support_ids[] = { + {} + }; + ++/* List of MSR-based RAPL PMU support CPUs */ ++static const struct x86_cpu_id pmu_support_ids[] = { ++ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL), ++ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL), ++ {} ++}; ++ + static int rapl_msr_probe(struct platform_device *pdev) + { + const struct x86_cpu_id *id = x86_match_cpu(pl4_support_ids); +@@ -198,6 +213,11 @@ static int rapl_msr_probe(struct platform_device *pdev) + pr_info("PL4 support detected.\n"); + } + ++ if (x86_match_cpu(pmu_support_ids)) { ++ rapl_msr_pmu = true; ++ pr_info("MSR-based RAPL PMU support enabled\n"); ++ } ++ + rapl_msr_priv->control_type = powercap_register_control_type(NULL, "intel-rapl", NULL); + if (IS_ERR(rapl_msr_priv->control_type)) { + pr_debug("failed to register powercap control_type.\n"); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo b/SPECS/kernel-rt/0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo new file mode 100644 index 000000000..b381aa377 --- /dev/null +++ b/SPECS/kernel-rt/0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo @@ -0,0 +1,49 @@ +From 037577d50b92f1e879887a8736fdfa0af3c23b39 Mon Sep 17 00:00:00 2001 +From: Malaya Kumar Rout +Date: Sat, 22 Nov 2025 20:46:52 +0530 +Subject: [PATCH 14/21] tools/power x86_energy_perf_policy: Fix potential NULL + pointer dereference + +In err_on_hypervisor(), strstr() is called to search for "flags" in the +buffer, but the return value is not checked before being used in pointer +arithmetic (flags - buffer). If strstr() returns NULL because "flags" is +not found in /proc/cpuinfo, this will cause undefined behavior and likely +a crash. + +Add a NULL check after the strstr() call and handle the error appropriately +by cleaning up resources and reporting a meaningful error message. + +Signed-off-by: Malaya Kumar Rout +Signed-off-by: Len Brown +--- + .../x86/x86_energy_perf_policy/x86_energy_perf_policy.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index b2125275c69e0..ac37132207a47 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -520,7 +520,7 @@ void for_packages(unsigned long long pkg_set, int (func)(int)) + + void print_version(void) + { +- printf("x86_energy_perf_policy 2025.9.19 Len Brown \n"); ++ printf("x86_energy_perf_policy 2025.11.22 Len Brown \n"); + } + + void cmdline(int argc, char **argv) +@@ -662,6 +662,11 @@ void err_on_hypervisor(void) + } + + flags = strstr(buffer, "flags"); ++ if (!flags) { ++ fclose(cpuinfo); ++ free(buffer); ++ err(1, "Failed to find 'flags' in /proc/cpuinfo"); ++ } + rewind(cpuinfo); + fseek(cpuinfo, flags - buffer, SEEK_SET); + if (!fgets(buffer, 4096, cpuinfo)) { +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi b/SPECS/kernel-rt/0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi deleted file mode 100644 index 5316044d8..000000000 --- a/SPECS/kernel-rt/0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi +++ /dev/null @@ -1,133 +0,0 @@ -From b6f80792b264fea9e081fd691885e1915a1048b0 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 28 Feb 2023 13:06:07 -0800 -Subject: [PATCH 15/44] KVM: VMX: Dump FRED context in dump_vmcs() - -Add FRED related VMCS fields to dump_vmcs() to dump FRED context. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Read guest FRED RSP0 with vmx_read_guest_fred_rsp0() (Sean). -* Add TB from Xuelian Guo. - -Change in v3: -* Use (vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED) instead of is_fred_enabled() - (Chao Gao). - -Changes in v2: -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). -* Dump guest FRED states only if guest has FRED enabled (Nikolay Borisov). ---- - arch/x86/kvm/vmx/vmx.c | 43 +++++++++++++++++++++++++++++++++++------- - 1 file changed, 36 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index f225778115dc..96edf88dd924 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1402,6 +1402,9 @@ static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) - vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, - &vmx->msr_guest_fred_rsp0); - } -+#else -+/* To make sure dump_vmcs() compile on 32-bit */ -+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) { return 0; } - #endif - - static void grow_ple_window(struct kvm_vcpu *vcpu) -@@ -6478,7 +6481,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - struct vcpu_vmx *vmx = to_vmx(vcpu); - u32 vmentry_ctl, vmexit_ctl; - u32 cpu_based_exec_ctrl, pin_based_exec_ctrl, secondary_exec_control; -- u64 tertiary_exec_control; -+ u64 tertiary_exec_control, secondary_vmexit_ctl; - unsigned long cr4; - int efer_slot; - -@@ -6489,6 +6492,8 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - - vmentry_ctl = vmcs_read32(VM_ENTRY_CONTROLS); - vmexit_ctl = vmcs_read32(VM_EXIT_CONTROLS); -+ secondary_vmexit_ctl = cpu_has_secondary_vmexit_ctrls() ? -+ vmcs_read64(SECONDARY_VM_EXIT_CONTROLS) : 0; - cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); - pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL); - cr4 = vmcs_readl(GUEST_CR4); -@@ -6535,6 +6540,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - vmx_dump_sel("LDTR:", GUEST_LDTR_SELECTOR); - vmx_dump_dtsel("IDTR:", GUEST_IDTR_LIMIT); - vmx_dump_sel("TR: ", GUEST_TR_SELECTOR); -+ if (vmentry_ctl & VM_ENTRY_LOAD_IA32_FRED) -+ pr_err("FRED guest: config=0x%016llx, stack_levels=0x%016llx\n" -+ "RSP0=0x%016llx, RSP1=0x%016llx\n" -+ "RSP2=0x%016llx, RSP3=0x%016llx\n", -+ vmcs_read64(GUEST_IA32_FRED_CONFIG), -+ vmcs_read64(GUEST_IA32_FRED_STKLVLS), -+ vmx_read_guest_fred_rsp0(vmx), -+ vmcs_read64(GUEST_IA32_FRED_RSP1), -+ vmcs_read64(GUEST_IA32_FRED_RSP2), -+ vmcs_read64(GUEST_IA32_FRED_RSP3)); - efer_slot = vmx_find_loadstore_msr_slot(&vmx->msr_autoload.guest, MSR_EFER); - if (vmentry_ctl & VM_ENTRY_LOAD_IA32_EFER) - pr_err("EFER= 0x%016llx\n", vmcs_read64(GUEST_IA32_EFER)); -@@ -6586,6 +6601,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - vmcs_readl(HOST_TR_BASE)); - pr_err("GDTBase=%016lx IDTBase=%016lx\n", - vmcs_readl(HOST_GDTR_BASE), vmcs_readl(HOST_IDTR_BASE)); -+ if (vmexit_ctl & SECONDARY_VM_EXIT_LOAD_IA32_FRED) -+ pr_err("FRED host: config=0x%016llx, stack_levels=0x%016llx\n" -+ "RSP0=0x%016lx, RSP1=0x%016llx\n" -+ "RSP2=0x%016llx, RSP3=0x%016llx\n", -+ vmcs_read64(HOST_IA32_FRED_CONFIG), -+ vmcs_read64(HOST_IA32_FRED_STKLVLS), -+ (unsigned long)task_stack_page(current) + THREAD_SIZE, -+ vmcs_read64(HOST_IA32_FRED_RSP1), -+ vmcs_read64(HOST_IA32_FRED_RSP2), -+ vmcs_read64(HOST_IA32_FRED_RSP3)); - pr_err("CR0=%016lx CR3=%016lx CR4=%016lx\n", - vmcs_readl(HOST_CR0), vmcs_readl(HOST_CR3), - vmcs_readl(HOST_CR4)); -@@ -6611,25 +6636,29 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - pr_err("*** Control State ***\n"); - pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", - cpu_based_exec_ctrl, secondary_exec_control, tertiary_exec_control); -- pr_err("PinBased=0x%08x EntryControls=%08x ExitControls=%08x\n", -- pin_based_exec_ctrl, vmentry_ctl, vmexit_ctl); -+ pr_err("PinBased=0x%08x EntryControls=0x%08x\n", -+ pin_based_exec_ctrl, vmentry_ctl); -+ pr_err("ExitControls=0x%08x SecondaryExitControls=0x%016llx\n", -+ vmexit_ctl, secondary_vmexit_ctl); - pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n", - vmcs_read32(EXCEPTION_BITMAP), - vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK), - vmcs_read32(PAGE_FAULT_ERROR_CODE_MATCH)); -- pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x\n", -+ pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x event_data=%016llx\n", - vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), - vmcs_read32(VM_ENTRY_EXCEPTION_ERROR_CODE), -- vmcs_read32(VM_ENTRY_INSTRUCTION_LEN)); -+ vmcs_read32(VM_ENTRY_INSTRUCTION_LEN), -+ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(INJECTED_EVENT_DATA) : 0); - pr_err("VMExit: intr_info=%08x errcode=%08x ilen=%08x\n", - vmcs_read32(VM_EXIT_INTR_INFO), - vmcs_read32(VM_EXIT_INTR_ERROR_CODE), - vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); - pr_err(" reason=%08x qualification=%016lx\n", - vmcs_read32(VM_EXIT_REASON), vmcs_readl(EXIT_QUALIFICATION)); -- pr_err("IDTVectoring: info=%08x errcode=%08x\n", -+ pr_err("IDTVectoring: info=%08x errcode=%08x event_data=%016llx\n", - vmcs_read32(IDT_VECTORING_INFO_FIELD), -- vmcs_read32(IDT_VECTORING_ERROR_CODE)); -+ vmcs_read32(IDT_VECTORING_ERROR_CODE), -+ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(ORIGINAL_EVENT_DATA) : 0); - pr_err("TSC Offset = 0x%016llx\n", vmcs_read64(TSC_OFFSET)); - if (secondary_exec_control & SECONDARY_EXEC_TSC_SCALING) - pr_err("TSC Multiplier = 0x%016llx\n", --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet b/SPECS/kernel-rt/0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet deleted file mode 100644 index 358f488dd..000000000 --- a/SPECS/kernel-rt/0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet +++ /dev/null @@ -1,172 +0,0 @@ -From 0adbce1831ce117f956f1e8b1b28df73bd66e61a Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:45 -0700 -Subject: [PATCH 15/24] KVM: VMX: Emulate read and write to CET MSRs - -Add emulation interface for CET MSR access. The emulation code is split -into common part and vendor specific part. The former does common checks -for MSRs, e.g., accessibility, data validity etc., then passes operation -to either XSAVE-managed MSRs via the helpers or CET VMCS fields. - -SSP can only be read via RDSSP. Writing even requires destructive and -potentially faulting operations such as SAVEPREVSSP/RSTORSSP or -SETSSBSY/CLRSSBSY. Let the host use a pseudo-MSR that is just a wrapper -for the GUEST_SSP field of the VMCS. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/vmx.c | 18 ++++++++++++++++++ - arch/x86/kvm/x86.c | 43 ++++++++++++++++++++++++++++++++++++++++++ - arch/x86/kvm/x86.h | 23 ++++++++++++++++++++++ - 3 files changed, 84 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 4a4691beba55..9098d4ee4f79 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2095,6 +2095,15 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - else - msr_info->data = vmx->pt_desc.guest.addr_a[index / 2]; - break; -+ case MSR_IA32_S_CET: -+ msr_info->data = vmcs_readl(GUEST_S_CET); -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ msr_info->data = vmcs_readl(GUEST_SSP); -+ break; -+ case MSR_IA32_INT_SSP_TAB: -+ msr_info->data = vmcs_readl(GUEST_INTR_SSP_TABLE); -+ break; - case MSR_IA32_DEBUGCTLMSR: - msr_info->data = vmx_guest_debugctl_read(); - break; -@@ -2413,6 +2422,15 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - else - vmx->pt_desc.guest.addr_a[index / 2] = data; - break; -+ case MSR_IA32_S_CET: -+ vmcs_writel(GUEST_S_CET, data); -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ vmcs_writel(GUEST_SSP, data); -+ break; -+ case MSR_IA32_INT_SSP_TAB: -+ vmcs_writel(GUEST_INTR_SSP_TABLE, data); -+ break; - case MSR_IA32_PERF_CAPABILITIES: - if (data & PERF_CAP_LBR_FMT) { - if ((data & PERF_CAP_LBR_FMT) != -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 1a437aad26f9..42e4fac999de 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1889,6 +1889,27 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, - - data = (u32)data; - break; -+ case MSR_IA32_U_CET: -+ case MSR_IA32_S_CET: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ if (!is_cet_msr_valid(vcpu, data)) -+ return 1; -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ if (!host_initiated) -+ return 1; -+ fallthrough; -+ case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ if (is_noncanonical_msr_address(data, vcpu)) -+ return 1; -+ /* All SSP MSRs except MSR_IA32_INT_SSP_TAB must be 4-byte aligned */ -+ if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4)) -+ return 1; -+ break; - } - - msr.data = data; -@@ -1933,6 +1954,20 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, - !guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID)) - return 1; - break; -+ case MSR_IA32_U_CET: -+ case MSR_IA32_S_CET: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ if (!host_initiated) -+ return 1; -+ fallthrough; -+ case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; - } - - msr.index = index; -@@ -4185,6 +4220,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - vcpu->arch.guest_fpu.xfd_err = data; - break; - #endif -+ case MSR_IA32_U_CET: -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ kvm_set_xstate_msr(vcpu, msr_info); -+ break; - default: - if (kvm_pmu_is_valid_msr(vcpu, msr)) - return kvm_pmu_set_msr(vcpu, msr_info); -@@ -4534,6 +4573,10 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - msr_info->data = vcpu->arch.guest_fpu.xfd_err; - break; - #endif -+ case MSR_IA32_U_CET: -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ kvm_get_xstate_msr(vcpu, msr_info); -+ break; - default: - if (kvm_pmu_is_valid_msr(vcpu, msr_info->index)) - return kvm_pmu_get_msr(vcpu, msr_info); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index cf74d877737a..b54ec7d37f1f 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -735,4 +735,27 @@ static inline void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, - kvm_fpu_put(); - } - -+#define CET_US_RESERVED_BITS GENMASK(9, 6) -+#define CET_US_SHSTK_MASK_BITS GENMASK(1, 0) -+#define CET_US_IBT_MASK_BITS (GENMASK_ULL(5, 2) | GENMASK_ULL(63, 10)) -+#define CET_US_LEGACY_BITMAP_BASE(data) ((data) >> 12) -+ -+static inline bool is_cet_msr_valid(struct kvm_vcpu *vcpu, u64 data) -+{ -+ if (data & CET_US_RESERVED_BITS) -+ return false; -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ (data & CET_US_SHSTK_MASK_BITS)) -+ return false; -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) && -+ (data & CET_US_IBT_MASK_BITS)) -+ return false; -+ if (!IS_ALIGNED(CET_US_LEGACY_BITMAP_BASE(data), 4)) -+ return false; -+ /* IBT can be suppressed iff the TRACKER isn't WAIT_ENDBR. */ -+ if ((data & CET_SUPPRESS) && (data & CET_WAIT_ENDBR)) -+ return false; -+ -+ return true; -+} - #endif --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi b/SPECS/kernel-rt/0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi new file mode 100644 index 000000000..b37f870d1 --- /dev/null +++ b/SPECS/kernel-rt/0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi @@ -0,0 +1,61 @@ +From 3d0fa57e44432f0f0f1a92c1cef082c4a2c95410 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 13:26:01 -0700 +Subject: [PATCH 15/44] KVM: x86: Mark CR4.FRED as not reserved + +The CR4.FRED bit, i.e., CR4[32], is no longer a reserved bit when +guest cpu cap has FRED, i.e., + 1) All of FRED KVM support is in place. + 2) Guest enumerates FRED. + +Otherwise it is still a reserved bit. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Rebase on top of "guest_cpu_cap". + +Change in v3: +* Don't allow CR4.FRED=1 before all of FRED KVM support is in place + (Sean Christopherson). +--- + arch/x86/include/asm/kvm_host.h | 2 +- + arch/x86/kvm/x86.h | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 5fff22d837aa0..558f260a1afd7 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -142,7 +142,7 @@ + | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ + | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ + | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ +- | X86_CR4_LAM_SUP | X86_CR4_CET)) ++ | X86_CR4_LAM_SUP | X86_CR4_CET | X86_CR4_FRED)) + + #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR) + +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index 4f5d12d7136ee..e9c6f304b02e4 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -687,6 +687,8 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) + if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ + !__cpu_has(__c, X86_FEATURE_IBT)) \ + __reserved_bits |= X86_CR4_CET; \ ++ if (!__cpu_has(__c, X86_FEATURE_FRED)) \ ++ __reserved_bits |= X86_CR4_FRED; \ + __reserved_bits; \ + }) + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0015-comedi-c6xdigio-Fix-invalid-PNP-driver-unregistratio.patch b/SPECS/kernel-rt/0015-comedi-c6xdigio-Fix-invalid-PNP-driver-unregistratio.patch deleted file mode 100644 index 32ea0bd90..000000000 --- a/SPECS/kernel-rt/0015-comedi-c6xdigio-Fix-invalid-PNP-driver-unregistratio.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 2d5318767a2c0249af5485810fe8c162835db1bd Mon Sep 17 00:00:00 2001 -From: Ian Abbott -Date: Thu, 23 Oct 2025 13:31:41 +0100 -Subject: [PATCH 15/51] comedi: c6xdigio: Fix invalid PNP driver unregistration - -The Comedi low-level driver "c6xdigio" seems to be for a parallel port -connected device. When the Comedi core calls the driver's Comedi -"attach" handler `c6xdigio_attach()` to configure a Comedi to use this -driver, it tries to enable the parallel port PNP resources by -registering a PNP driver with `pnp_register_driver()`, but ignores the -return value. (The `struct pnp_driver` it uses has only the `name` and -`id_table` members filled in.) The driver's Comedi "detach" handler -`c6xdigio_detach()` unconditionally unregisters the PNP driver with -`pnp_unregister_driver()`. - -It is possible for `c6xdigio_attach()` to return an error before it -calls `pnp_register_driver()` and it is possible for the call to -`pnp_register_driver()` to return an error (that is ignored). In both -cases, the driver should not be calling `pnp_unregister_driver()` as it -does in `c6xdigio_detach()`. (Note that `c6xdigio_detach()` will be -called by the Comedi core if `c6xdigio_attach()` returns an error, or if -the Comedi core decides to detach the Comedi device from the driver for -some other reason.) - -The unconditional call to `pnp_unregister_driver()` without a previous -successful call to `pnp_register_driver()` will cause -`driver_unregister()` to issue a warning "Unexpected driver -unregister!". This was detected by Syzbot [1]. - -Also, the PNP driver registration and unregistration should be done at -module init and exit time, respectively, not when attaching or detaching -Comedi devices to the driver. (There might be more than one Comedi -device being attached to the driver, although that is unlikely.) - -Change the driver to do the PNP driver registration at module init time, -and the unregistration at module exit time. Since `c6xdigio_detach()` -now only calls `comedi_legacy_detach()`, remove the function and change -the Comedi driver "detach" handler to `comedi_legacy_detach`. - -------------------------------------------- -[1] Syzbot sample crash report: -Unexpected driver unregister! -WARNING: CPU: 0 PID: 5970 at drivers/base/driver.c:273 driver_unregister drivers/base/driver.c:273 [inline] -WARNING: CPU: 0 PID: 5970 at drivers/base/driver.c:273 driver_unregister+0x90/0xb0 drivers/base/driver.c:270 -Modules linked in: -CPU: 0 UID: 0 PID: 5970 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) -Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 10/02/2025 -RIP: 0010:driver_unregister drivers/base/driver.c:273 [inline] -RIP: 0010:driver_unregister+0x90/0xb0 drivers/base/driver.c:270 -Code: 48 89 ef e8 c2 e6 82 fc 48 89 df e8 3a 93 ff ff 5b 5d e9 c3 6d d9 fb e8 be 6d d9 fb 90 48 c7 c7 e0 f8 1f 8c e8 51 a2 97 fb 90 <0f> 0b 90 90 5b 5d e9 a5 6d d9 fb e8 e0 f4 41 fc eb 94 e8 d9 f4 41 -RSP: 0018:ffffc9000373f9a0 EFLAGS: 00010282 -RAX: 0000000000000000 RBX: ffffffff8ff24720 RCX: ffffffff817b6ee8 -RDX: ffff88807c932480 RSI: ffffffff817b6ef5 RDI: 0000000000000001 -RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000 -R10: 0000000000000001 R11: 0000000000000001 R12: ffffffff8ff24660 -R13: dffffc0000000000 R14: 0000000000000000 R15: ffff88814cca0000 -FS: 000055556dab1500(0000) GS:ffff8881249d9000(0000) knlGS:0000000000000000 -CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 -CR2: 000055f77f285cd0 CR3: 000000007d871000 CR4: 00000000003526f0 -Call Trace: - - comedi_device_detach_locked+0x12f/0xa50 drivers/comedi/drivers.c:207 - comedi_device_detach+0x67/0xb0 drivers/comedi/drivers.c:215 - comedi_device_attach+0x43d/0x900 drivers/comedi/drivers.c:1011 - do_devconfig_ioctl+0x1b1/0x710 drivers/comedi/comedi_fops.c:872 - comedi_unlocked_ioctl+0x165d/0x2f00 drivers/comedi/comedi_fops.c:2178 - vfs_ioctl fs/ioctl.c:51 [inline] - __do_sys_ioctl fs/ioctl.c:597 [inline] - __se_sys_ioctl fs/ioctl.c:583 [inline] - __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583 - do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] - do_syscall_64+0xcd/0xfa0 arch/x86/entry/syscall_64.c:94 - entry_SYSCALL_64_after_hwframe+0x77/0x7f -RIP: 0033:0x7fc05798eec9 -Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48 -RSP: 002b:00007ffcf8184238 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 -RAX: ffffffffffffffda RBX: 00007fc057be5fa0 RCX: 00007fc05798eec9 -RDX: 0000200000000080 RSI: 0000000040946400 RDI: 0000000000000003 -RBP: 00007fc057a11f91 R08: 0000000000000000 R09: 0000000000000000 -R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 -R13: 00007fc057be5fa0 R14: 00007fc057be5fa0 R15: 0000000000000003 - -------------------------------------------- - -Reported-by: syzbot+6616bba359cec7a1def1@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=6616bba359cec7a1def1 -Fixes: 2c89e159cd2f ("Staging: comedi: add c6xdigio driver") -Cc: stable@kernel.org -Signed-off-by: Ian Abbott -Link: https://patch.msgid.link/20251023123141.6537-1-abbotti@mev.co.uk -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/drivers/c6xdigio.c | 46 +++++++++++++++++++++++-------- - 1 file changed, 35 insertions(+), 11 deletions(-) - -diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c -index 14b90d1c64dc..8da653c8c1ca 100644 ---- a/drivers/comedi/drivers/c6xdigio.c -+++ b/drivers/comedi/drivers/c6xdigio.c -@@ -249,9 +249,6 @@ static int c6xdigio_attach(struct comedi_device *dev, - if (ret) - return ret; - -- /* Make sure that PnP ports get activated */ -- pnp_register_driver(&c6xdigio_pnp_driver); -- - s = &dev->subdevices[0]; - /* pwm output subdevice */ - s->type = COMEDI_SUBD_PWM; -@@ -278,19 +275,46 @@ static int c6xdigio_attach(struct comedi_device *dev, - return 0; - } - --static void c6xdigio_detach(struct comedi_device *dev) --{ -- comedi_legacy_detach(dev); -- pnp_unregister_driver(&c6xdigio_pnp_driver); --} -- - static struct comedi_driver c6xdigio_driver = { - .driver_name = "c6xdigio", - .module = THIS_MODULE, - .attach = c6xdigio_attach, -- .detach = c6xdigio_detach, -+ .detach = comedi_legacy_detach, - }; --module_comedi_driver(c6xdigio_driver); -+ -+static bool c6xdigio_pnp_registered; -+ -+static int __init c6xdigio_module_init(void) -+{ -+ int ret; -+ -+ ret = comedi_driver_register(&c6xdigio_driver); -+ if (ret) -+ return ret; -+ -+ if (IS_ENABLED(CONFIG_PNP)) { -+ /* Try to activate the PnP ports */ -+ ret = pnp_register_driver(&c6xdigio_pnp_driver); -+ if (ret) { -+ pr_warn("failed to register pnp driver - err %d\n", -+ ret); -+ ret = 0; /* ignore the error. */ -+ } else { -+ c6xdigio_pnp_registered = true; -+ } -+ } -+ -+ return 0; -+} -+module_init(c6xdigio_module_init); -+ -+static void __exit c6xdigio_module_exit(void) -+{ -+ if (c6xdigio_pnp_registered) -+ pnp_unregister_driver(&c6xdigio_pnp_driver); -+ comedi_driver_unregister(&c6xdigio_driver); -+} -+module_exit(c6xdigio_module_exit); - - MODULE_AUTHOR("Comedi https://www.comedi.org"); - MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card"); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl b/SPECS/kernel-rt/0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl new file mode 100644 index 000000000..281fb0582 --- /dev/null +++ b/SPECS/kernel-rt/0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl @@ -0,0 +1,32 @@ +From 108a0dab67c8cf7584162c87a656b75c192f4294 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Fri, 21 Nov 2025 21:11:16 +0100 +Subject: [PATCH 15/17] cpuidle: governors: teo: Add missing space to the + description + +There is a missing space in the governor description comment, so add it. + +No functional impact. + +Signed-off-by: Rafael J. Wysocki +Link: https://patch.msgid.link/5059034.31r3eYUQgx@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index bab186336bf4a..81ac5fd58a1c6 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -76,7 +76,7 @@ + * likely woken up by a non-timer wakeup source). + * + * 2. If the second sum computed in step 1 is greater than a half of the sum of +- * both metrics for the candidate state bin and all subsequent bins(if any), ++ * both metrics for the candidate state bin and all subsequent bins (if any), + * a shallower idle state is likely to be more suitable, so look for it. + * + * - Traverse the enabled idle states shallower than the candidate one in the +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet b/SPECS/kernel-rt/0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet deleted file mode 100644 index c051c20cb..000000000 --- a/SPECS/kernel-rt/0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet +++ /dev/null @@ -1,36 +0,0 @@ -From cb41485c3b08e1a27d2ce3e25c6b75e6f416e418 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Fri, 23 Jul 2021 21:27:36 +0800 -Subject: [PATCH 15/19] igc: Enable trace for HW TX Timestamp AF_XDP ZC - -This is a temporary solution as it uses trace_printk as a means to -log tx timestamps. - -Future implementation should use xdp_frame's data_meta to let user -applications retrieve it directly. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc_main.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 8b5f747db38b..8fdeea791b1d 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -3255,6 +3255,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - - switch (tx_buffer->type) { - case IGC_TX_BUFFER_TYPE_XSK: -+#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) -+ /* Only use for RTCP KPI Measurement on Q2 */ -+ if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) -+ trace_printk("TX HW TS %lld\n", timestamp); -+#endif - xsk_frames++; - break; - case IGC_TX_BUFFER_TYPE_XDP: --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-media-ipu7-update-CDPHY-register-settings.ipu b/SPECS/kernel-rt/0015-media-ipu7-update-CDPHY-register-settings.ipu deleted file mode 100644 index c7c610e79..000000000 --- a/SPECS/kernel-rt/0015-media-ipu7-update-CDPHY-register-settings.ipu +++ /dev/null @@ -1,81 +0,0 @@ -From 7612f6deb2488e07a6c38a73655a83b45762cb6c Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Fri, 19 Dec 2025 15:29:00 +0800 -Subject: [PATCH 15/21] media: ipu7: update CDPHY register settings - -Some CPHY settings are copied from Wins code, but some of -them aren't correct and need to be fixed. - -Program 45ohm for tuning resistance to fix CPHY problem and -update the ITMINRX and GMODE for CPHY high data rate. - -Test Platform: -PTLRVP -LNLRVP - -Signed-off-by: Bingbu Cao -Signed-off-by: hepengpx ---- - drivers/staging/media/ipu7/ipu7-isys-csi-phy.c | 15 +++++++++++---- - 1 file changed, 11 insertions(+), 4 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c -index b8c5db7ae300..97cd47daf614 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c -@@ -124,6 +124,7 @@ static const struct cdr_fbk_cap_prog_params table7[] = { - { 1350, 1589, 4 }, - { 1590, 1949, 5 }, - { 1950, 2499, 6 }, -+ { 2500, 3500, 7 }, - { } - }; - -@@ -730,7 +731,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - u16 deass_thresh; - u16 delay_thresh; - u16 reset_thresh; -- u16 cap_prog = 6U; -+ u16 cap_prog; - u16 reg; - u16 val; - u32 i; -@@ -838,9 +839,10 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - dwc_phy_write_mask(isys, id, reg + 0x400 * i, - reset_thresh, 9, 11); - -+ /* Tuning ITMINRX to 2 for CPHY */ - reg = CORE_DIG_CLANE_0_RW_LP_0; - for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); - - reg = CORE_DIG_CLANE_0_RW_LP_2; - for (i = 0; i < trios; i++) -@@ -860,7 +862,11 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - for (i = 0; i < (lanes + 1); i++) { - reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; - dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); -- dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); -+ /* Set GMODE to 2 when CPHY >= 1.5Gsps */ -+ if (mbps >= 1500) -+ dwc_phy_write_mask(isys, id, reg, 2U, 3, 4); -+ else -+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); - - reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; - dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); -@@ -930,8 +936,9 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - 7, 12, 14); - dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, - 0, 8, 10); -+ /* resistance tuning: 1 for 45ohm, 0 for 50ohm */ - dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, -- 0, 8, 8); -+ 1, 8, 8); - - if (aggregation) - phy_mode = isys->csi2[0].phy_mode; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet b/SPECS/kernel-rt/0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet new file mode 100644 index 000000000..5b27f5af7 --- /dev/null +++ b/SPECS/kernel-rt/0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet @@ -0,0 +1,97 @@ +From ed293eeac928fcf3dcaf9b7e3481f4e5c346bd6f Mon Sep 17 00:00:00 2001 +From: Tan Tee Min +Date: Tue, 12 Apr 2022 15:14:07 +0800 +Subject: [PATCH 15/18] net: stmmac: add fsleep() in HW Rx timestamp checking + loop + +There is a possibility that the context descriptor still owned by the DMA +even the previous normal descriptor own bit is already cleared. Checking +the context descriptor readiness without delay might be not enough time +for the DMA to update the descriptor field, which causing failure in +getting HW Rx timestamp. + +This patch introduces a 1ms fsleep() in HW Rx timestamp checking loop +to give time for DMA to update/complete the context descriptor. + +ptp4l Timestamp log without this patch: +----------------------------------------------------------- +$ echo 10000 > /sys/class/net/enp0s30f4/gro_flush_timeout +$ echo 10000 > /sys/class/net/enp0s30f4/napi_defer_hard_irqs +$ ptp4l -P2Hi enp0s30f4 --step_threshold=1 -m +ptp4l: selected /dev/ptp2 as PTP clock +ptp4l: port 1: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l: selected local clock 901210.fffe.b57df7 as best master +ptp4l: port 1: new foreign master 22bb22.fffe.bb22bb-1 +ptp4l: selected best master clock 22bb22.fffe.bb22bb +ptp4l: port 1: LISTENING to UNCALIBRATED on RS_SLAVE +ptp4l: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED +ptp4l: port 1: received SYNC without timestamp +ptp4l: rms 49 max 63 freq -9573 +/- 34 delay 71 +/- 1 +ptp4l: rms 15 max 25 freq -9553 +/- 20 delay 72 +/- 0 +ptp4l: port 1: received SYNC without timestamp +ptp4l: rms 9 max 18 freq -9540 +/- 11 delay 70 +/- 0 +ptp4l: port 1: received PDELAY_REQ without timestamp +ptp4l: rms 16 max 29 freq -9519 +/- 12 delay 72 +/- 0 +ptp4l: port 1: received PDELAY_REQ without timestamp +ptp4l: rms 9 max 18 freq -9527 +/- 12 delay 72 +/- 0 +ptp4l: rms 5 max 9 freq -9530 +/- 7 delay 70 +/- 0 +ptp4l: rms 11 max 20 freq -9530 +/- 16 delay 72 +/- 0 +ptp4l: rms 5 max 11 freq -9530 +/- 7 delay 74 +/- 0 +ptp4l: rms 6 max 9 freq -9522 +/- 7 delay 72 +/- 0 +ptp4l: port 1: received PDELAY_REQ without timestamp +----------------------------------------------------------- + +ptp4l Timestamp log with this patch: +----------------------------------------------------------- +$ echo 10000 > /sys/class/net/enp0s30f4/gro_flush_timeout +$ echo 10000 > /sys/class/net/enp0s30f4/napi_defer_hard_irqs +$ ptp4l -P2Hi enp0s30f4 --step_threshold=1 -m +ptp4l: selected /dev/ptp2 as PTP clock +ptp4l: port 1: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l: selected local clock 901210.fffe.b57df7 as best master +ptp4l: port 1: new foreign master 22bb22.fffe.bb22bb-1 +ptp4l: selected best master clock 22bb22.fffe.bb22bb +ptp4l: port 1: LISTENING to UNCALIBRATED on RS_SLAVE +ptp4l: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED +ptp4l: rms 30 max 45 freq -9400 +/- 23 delay 72 +/- 0 +ptp4l: rms 7 max 16 freq -9414 +/- 10 delay 70 +/- 0 +ptp4l: rms 6 max 9 freq -9422 +/- 6 delay 72 +/- 0 +ptp4l: rms 13 max 20 freq -9436 +/- 13 delay 74 +/- 0 +ptp4l: rms 12 max 27 freq -9446 +/- 11 delay 72 +/- 0 +ptp4l: rms 9 max 12 freq -9453 +/- 6 delay 74 +/- 0 +ptp4l: rms 9 max 15 freq -9438 +/- 11 delay 74 +/- 0 +ptp4l: rms 10 max 16 freq -9435 +/- 12 delay 74 +/- 0 +ptp4l: rms 8 max 18 freq -9428 +/- 8 delay 72 +/- 0 +ptp4l: rms 8 max 18 freq -9423 +/- 8 delay 72 +/- 0 +ptp4l: rms 9 max 16 freq -9431 +/- 12 delay 70 +/- 0 +ptp4l: rms 9 max 18 freq -9441 +/- 9 delay 72 +/- 0 +----------------------------------------------------------- + +Fixes: ba1ffd74df74 ("stmmac: fix PTP support for GMAC4") +Cc: # 5.4.x +Signed-off-by: Song Yoong Siang +Signed-off-by: Tan Tee Min +--- + drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +index aac68dc28dc19..615d5764e52b3 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +@@ -292,10 +292,11 @@ static int dwmac4_wrback_get_rx_timestamp_status(void *desc, void *next_desc, + /* Check if timestamp is OK from context descriptor */ + do { + ret = dwmac4_rx_check_timestamp(next_desc); +- if (ret < 0) ++ if (ret <= 0) + goto exit; + i++; + ++ fsleep(1); + } while ((ret == 1) && (i < 10)); + + if (i == 10) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf b/SPECS/kernel-rt/0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf deleted file mode 100644 index 5de4385b1..000000000 --- a/SPECS/kernel-rt/0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf +++ /dev/null @@ -1,233 +0,0 @@ -From 999573cf9a51cb35df1ecf5a3a014dd5fcb980f1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 28 Nov 2024 06:45:17 +0000 -Subject: [PATCH 015/100] perf/x86/intel/ds: Factor out PEBS group processing - code to functions - -Adaptive PEBS and arch-PEBS share lots of same code to process these -PEBS groups, like basic, GPR and meminfo groups. Extract these shared -code to generic functions to avoid duplicated code. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/ds.c | 170 +++++++++++++++++++++++-------------- - 1 file changed, 104 insertions(+), 66 deletions(-) - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index db40dc76eb20..f1d79b9e5f50 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2072,6 +2072,90 @@ static inline void __setup_pebs_counter_group(struct cpu_hw_events *cpuc, - - #define PEBS_LATENCY_MASK 0xffff - -+static inline void __setup_perf_sample_data(struct perf_event *event, -+ struct pt_regs *iregs, -+ struct perf_sample_data *data) -+{ -+ perf_sample_data_init(data, 0, event->hw.last_period); -+ -+ /* -+ * We must however always use iregs for the unwinder to stay sane; the -+ * record BP,SP,IP can point into thin air when the record is from a -+ * previous PMI context or an (I)RET happened between the record and -+ * PMI. -+ */ -+ perf_sample_save_callchain(data, event, iregs); -+} -+ -+static inline void __setup_pebs_basic_group(struct perf_event *event, -+ struct pt_regs *regs, -+ struct perf_sample_data *data, -+ u64 sample_type, u64 ip, -+ u64 tsc, u16 retire) -+{ -+ /* The ip in basic is EventingIP */ -+ set_linear_ip(regs, ip); -+ regs->flags = PERF_EFLAGS_EXACT; -+ setup_pebs_time(event, data, tsc); -+ -+ if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) -+ data->weight.var3_w = retire; -+} -+ -+static inline void __setup_pebs_gpr_group(struct perf_event *event, -+ struct pt_regs *regs, -+ struct pebs_gprs *gprs, -+ u64 sample_type) -+{ -+ if (event->attr.precise_ip < 2) { -+ set_linear_ip(regs, gprs->ip); -+ regs->flags &= ~PERF_EFLAGS_EXACT; -+ } -+ -+ if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) -+ adaptive_pebs_save_regs(regs, gprs); -+} -+ -+static inline void __setup_pebs_meminfo_group(struct perf_event *event, -+ struct perf_sample_data *data, -+ u64 sample_type, u64 latency, -+ u16 instr_latency, u64 address, -+ u64 aux, u64 tsx_tuning, u64 ax) -+{ -+ if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) { -+ u64 tsx_latency = intel_get_tsx_weight(tsx_tuning); -+ -+ data->weight.var2_w = instr_latency; -+ -+ /* -+ * Although meminfo::latency is defined as a u64, -+ * only the lower 32 bits include the valid data -+ * in practice on Ice Lake and earlier platforms. -+ */ -+ if (sample_type & PERF_SAMPLE_WEIGHT) -+ data->weight.full = latency ?: tsx_latency; -+ else -+ data->weight.var1_dw = (u32)latency ?: tsx_latency; -+ -+ data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE; -+ } -+ -+ if (sample_type & PERF_SAMPLE_DATA_SRC) { -+ data->data_src.val = get_data_src(event, aux); -+ data->sample_flags |= PERF_SAMPLE_DATA_SRC; -+ } -+ -+ if (sample_type & PERF_SAMPLE_ADDR_TYPE) { -+ data->addr = address; -+ data->sample_flags |= PERF_SAMPLE_ADDR; -+ } -+ -+ if (sample_type & PERF_SAMPLE_TRANSACTION) { -+ data->txn = intel_get_tsx_transaction(tsx_tuning, ax); -+ data->sample_flags |= PERF_SAMPLE_TRANSACTION; -+ } -+} -+ - /* - * With adaptive PEBS the layout depends on what fields are configured. - */ -@@ -2081,12 +2165,14 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - struct pt_regs *regs) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ u64 sample_type = event->attr.sample_type; - struct pebs_basic *basic = __pebs; - void *next_record = basic + 1; -- u64 sample_type, format_group; - struct pebs_meminfo *meminfo = NULL; - struct pebs_gprs *gprs = NULL; - struct x86_perf_regs *perf_regs; -+ u64 format_group; -+ u16 retire; - - if (basic == NULL) - return; -@@ -2094,31 +2180,17 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; - -- sample_type = event->attr.sample_type; - format_group = basic->format_group; -- perf_sample_data_init(data, 0, event->hw.last_period); - -- setup_pebs_time(event, data, basic->tsc); -- -- /* -- * We must however always use iregs for the unwinder to stay sane; the -- * record BP,SP,IP can point into thin air when the record is from a -- * previous PMI context or an (I)RET happened between the record and -- * PMI. -- */ -- perf_sample_save_callchain(data, event, iregs); -+ __setup_perf_sample_data(event, iregs, data); - - *regs = *iregs; -- /* The ip in basic is EventingIP */ -- set_linear_ip(regs, basic->ip); -- regs->flags = PERF_EFLAGS_EXACT; - -- if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) { -- if (x86_pmu.flags & PMU_FL_RETIRE_LATENCY) -- data->weight.var3_w = basic->retire_latency; -- else -- data->weight.var3_w = 0; -- } -+ /* basic group */ -+ retire = x86_pmu.flags & PMU_FL_RETIRE_LATENCY ? -+ basic->retire_latency : 0; -+ __setup_pebs_basic_group(event, regs, data, sample_type, -+ basic->ip, basic->tsc, retire); - - /* - * The record for MEMINFO is in front of GP -@@ -2134,54 +2206,20 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - gprs = next_record; - next_record = gprs + 1; - -- if (event->attr.precise_ip < 2) { -- set_linear_ip(regs, gprs->ip); -- regs->flags &= ~PERF_EFLAGS_EXACT; -- } -- -- if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) -- adaptive_pebs_save_regs(regs, gprs); -+ __setup_pebs_gpr_group(event, regs, gprs, sample_type); - } - - if (format_group & PEBS_DATACFG_MEMINFO) { -- if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) { -- u64 latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ? -- meminfo->cache_latency : meminfo->mem_latency; -- -- if (x86_pmu.flags & PMU_FL_INSTR_LATENCY) -- data->weight.var2_w = meminfo->instr_latency; -- -- /* -- * Although meminfo::latency is defined as a u64, -- * only the lower 32 bits include the valid data -- * in practice on Ice Lake and earlier platforms. -- */ -- if (sample_type & PERF_SAMPLE_WEIGHT) { -- data->weight.full = latency ?: -- intel_get_tsx_weight(meminfo->tsx_tuning); -- } else { -- data->weight.var1_dw = (u32)latency ?: -- intel_get_tsx_weight(meminfo->tsx_tuning); -- } -- -- data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE; -- } -- -- if (sample_type & PERF_SAMPLE_DATA_SRC) { -- data->data_src.val = get_data_src(event, meminfo->aux); -- data->sample_flags |= PERF_SAMPLE_DATA_SRC; -- } -- -- if (sample_type & PERF_SAMPLE_ADDR_TYPE) { -- data->addr = meminfo->address; -- data->sample_flags |= PERF_SAMPLE_ADDR; -- } -- -- if (sample_type & PERF_SAMPLE_TRANSACTION) { -- data->txn = intel_get_tsx_transaction(meminfo->tsx_tuning, -- gprs ? gprs->ax : 0); -- data->sample_flags |= PERF_SAMPLE_TRANSACTION; -- } -+ u64 latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ? -+ meminfo->cache_latency : meminfo->mem_latency; -+ u64 instr_latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ? -+ meminfo->instr_latency : 0; -+ u64 ax = gprs ? gprs->ax : 0; -+ -+ __setup_pebs_meminfo_group(event, data, sample_type, latency, -+ instr_latency, meminfo->address, -+ meminfo->aux, meminfo->tsx_tuning, -+ ax); - } - - if (format_group & PEBS_DATACFG_XMMS) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-scsi-imm-Fix-use-after-free-bug-caused-by-unfinished.patch b/SPECS/kernel-rt/0015-scsi-imm-Fix-use-after-free-bug-caused-by-unfinished.patch deleted file mode 100644 index da2964be8..000000000 --- a/SPECS/kernel-rt/0015-scsi-imm-Fix-use-after-free-bug-caused-by-unfinished.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 3cb9169b624d34394440ce655a9b8bf5efc706e6 Mon Sep 17 00:00:00 2001 -From: Duoming Zhou -Date: Tue, 28 Oct 2025 18:01:49 +0800 -Subject: [PATCH 15/16] scsi: imm: Fix use-after-free bug caused by unfinished - delayed work - -The delayed work item 'imm_tq' is initialized in imm_attach() and -scheduled via imm_queuecommand() for processing SCSI commands. When the -IMM parallel port SCSI host adapter is detached through imm_detach(), -the imm_struct device instance is deallocated. - -However, the delayed work might still be pending or executing -when imm_detach() is called, leading to use-after-free bugs -when the work function imm_interrupt() accesses the already -freed imm_struct memory. - -The race condition can occur as follows: - -CPU 0(detach thread) | CPU 1 - | imm_queuecommand() - | imm_queuecommand_lck() -imm_detach() | schedule_delayed_work() - kfree(dev) //FREE | imm_interrupt() - | dev = container_of(...) //USE - dev-> //USE - -Add disable_delayed_work_sync() in imm_detach() to guarantee proper -cancellation of the delayed work item before imm_struct is deallocated. - -Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") -Signed-off-by: Duoming Zhou -Link: https://patch.msgid.link/20251028100149.40721-1-duoming@zju.edu.cn -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/imm.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c -index 0821cf994b98..8a099bc27e06 100644 ---- a/drivers/scsi/imm.c -+++ b/drivers/scsi/imm.c -@@ -1260,6 +1260,7 @@ static void imm_detach(struct parport *pb) - imm_struct *dev; - list_for_each_entry(dev, &imm_hosts, list) { - if (dev->dev->port == pb) { -+ disable_delayed_work_sync(&dev->imm_tq); - list_del_init(&dev->list); - scsi_remove_host(dev->host); - scsi_host_put(dev->host); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo b/SPECS/kernel-rt/0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo new file mode 100644 index 000000000..97b682568 --- /dev/null +++ b/SPECS/kernel-rt/0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo @@ -0,0 +1,554 @@ +From 1dd87cc0d4d7d7b0cb124ec1ee101dba9de3da8e Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Sun, 30 Nov 2025 00:11:22 -0500 +Subject: [PATCH 15/21] tools/power turbostat: Validate RAPL MSRs for AWS Nitro + Hypervisor + +Even though the platform->plat_rapl_msrs enumeration may be accurate, +a VM, such as AWS Nitro Hypervisor, may deny access to the underlying MSRs. + +Probe if PKG_ENERGY is readable and non-zero. +If no, ignore all RAPL MSRs. + +Reported-by: Emily Ehlert +Tested-by: Emily Ehlert +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 156 ++++++++++++++++---------- + 1 file changed, 98 insertions(+), 58 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index e85bdb00f24a5..4411ef44294f4 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -492,6 +492,7 @@ unsigned int quiet; + unsigned int shown; + unsigned int sums_need_wide_columns; + unsigned int rapl_joules; ++unsigned int valid_rapl_msrs; + unsigned int summary_only; + unsigned int list_header_only; + unsigned int dump_only; +@@ -588,7 +589,7 @@ struct platform_features { + bool has_cst_prewake_bit; /* Cstate prewake bit in MSR_IA32_POWER_CTL */ + int trl_msrs; /* MSR_TURBO_RATIO_LIMIT/LIMIT1/LIMIT2/SECONDARY, Atom TRL MSRs */ + int plr_msrs; /* MSR_CORE/GFX/RING_PERF_LIMIT_REASONS */ +- int rapl_msrs; /* RAPL PKG/DRAM/CORE/GFX MSRs, AMD RAPL MSRs */ ++ int plat_rapl_msrs; /* RAPL PKG/DRAM/CORE/GFX MSRs, AMD RAPL MSRs */ + bool has_per_core_rapl; /* Indicates cores energy collection is per-core, not per-package. AMD specific for now */ + bool has_rapl_divisor; /* Divisor for Energy unit raw value from MSR_RAPL_POWER_UNIT */ + bool has_fixed_rapl_unit; /* Fixed Energy Unit used for DRAM RAPL Domain */ +@@ -743,7 +744,7 @@ static const struct platform_features snb_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features snx_features = { +@@ -755,7 +756,7 @@ static const struct platform_features snx_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, + }; + + static const struct platform_features ivb_features = { +@@ -768,7 +769,7 @@ static const struct platform_features ivb_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features ivx_features = { +@@ -780,7 +781,7 @@ static const struct platform_features ivx_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_LIMIT1, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, + }; + + static const struct platform_features hsw_features = { +@@ -794,7 +795,7 @@ static const struct platform_features hsw_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features hsx_features = { +@@ -808,7 +809,7 @@ static const struct platform_features hsx_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_LIMIT1 | TRL_LIMIT2, + .plr_msrs = PLR_CORE | PLR_RING, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + }; + +@@ -823,7 +824,7 @@ static const struct platform_features hswl_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features hswg_features = { +@@ -837,7 +838,7 @@ static const struct platform_features hswg_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features bdw_features = { +@@ -850,7 +851,7 @@ static const struct platform_features bdw_features = { + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features bdwg_features = { +@@ -863,7 +864,7 @@ static const struct platform_features bdwg_features = { + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features bdx_features = { +@@ -877,7 +878,7 @@ static const struct platform_features bdx_features = { + .has_irtl_msrs = 1, + .has_cst_auto_convension = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + }; + +@@ -894,7 +895,7 @@ static const struct platform_features skl_features = { + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, + .enable_tsc_tweak = 1, + }; + +@@ -911,7 +912,7 @@ static const struct platform_features cnl_features = { + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, + .enable_tsc_tweak = 1, + }; + +@@ -929,7 +930,7 @@ static const struct platform_features adl_features = { + .has_ext_cst_msrs = cnl_features.has_ext_cst_msrs, + .trl_msrs = cnl_features.trl_msrs, + .tcc_offset_bits = cnl_features.tcc_offset_bits, +- .rapl_msrs = cnl_features.rapl_msrs, ++ .plat_rapl_msrs = cnl_features.plat_rapl_msrs, + .enable_tsc_tweak = cnl_features.enable_tsc_tweak, + }; + +@@ -947,7 +948,7 @@ static const struct platform_features lnl_features = { + .has_ext_cst_msrs = adl_features.has_ext_cst_msrs, + .trl_msrs = adl_features.trl_msrs, + .tcc_offset_bits = adl_features.tcc_offset_bits, +- .rapl_msrs = adl_features.rapl_msrs, ++ .plat_rapl_msrs = adl_features.plat_rapl_msrs, + .enable_tsc_tweak = adl_features.enable_tsc_tweak, + }; + +@@ -962,7 +963,7 @@ static const struct platform_features skx_features = { + .has_irtl_msrs = 1, + .has_cst_auto_convension = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + }; + +@@ -978,7 +979,7 @@ static const struct platform_features icx_features = { + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + .has_fixed_rapl_unit = 1, + }; + +@@ -995,7 +996,7 @@ static const struct platform_features spr_features = { + .has_cst_prewake_bit = 1, + .has_fixed_rapl_psys_unit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + }; + + static const struct platform_features dmr_features = { +@@ -1010,7 +1011,7 @@ static const struct platform_features dmr_features = { + .has_fixed_rapl_psys_unit = spr_features.has_fixed_rapl_psys_unit, + .trl_msrs = spr_features.trl_msrs, + .has_msr_module_c6_res_ms = 1, /* DMR has Dual-Core-Module and MC6 MSR */ +- .rapl_msrs = 0, /* DMR does not have RAPL MSRs */ ++ .plat_rapl_msrs = 0, /* DMR does not have RAPL MSRs */ + .plr_msrs = 0, /* DMR does not have PLR MSRs */ + .has_irtl_msrs = 0, /* DMR does not have IRTL MSRs */ + .has_config_tdp = 0, /* DMR does not have CTDP MSRs */ +@@ -1029,7 +1030,7 @@ static const struct platform_features srf_features = { + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + }; + + static const struct platform_features grr_features = { +@@ -1045,7 +1046,7 @@ static const struct platform_features grr_features = { + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + }; + + static const struct platform_features slv_features = { +@@ -1058,7 +1059,7 @@ static const struct platform_features slv_features = { + .has_msr_c6_demotion_policy_config = 1, + .has_msr_atom_pkg_c6_residency = 1, + .trl_msrs = TRL_ATOM, +- .rapl_msrs = RAPL_PKG | RAPL_CORE, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE, + .has_rapl_divisor = 1, + .rapl_quirk_tdp = 30, + }; +@@ -1071,7 +1072,7 @@ static const struct platform_features slvd_features = { + .cst_limit = CST_LIMIT_SLV, + .has_msr_atom_pkg_c6_residency = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE, + .rapl_quirk_tdp = 30, + }; + +@@ -1092,7 +1093,7 @@ static const struct platform_features gmt_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features gmtd_features = { +@@ -1105,7 +1106,7 @@ static const struct platform_features gmtd_features = { + .has_irtl_msrs = 1, + .has_msr_core_c1_res = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_CORE_ENERGY_STATUS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_CORE_ENERGY_STATUS, + }; + + static const struct platform_features gmtp_features = { +@@ -1117,7 +1118,7 @@ static const struct platform_features gmtp_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features tmt_features = { +@@ -1128,7 +1129,7 @@ static const struct platform_features tmt_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, + .enable_tsc_tweak = 1, + }; + +@@ -1140,7 +1141,7 @@ static const struct platform_features tmtd_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL, + }; + + static const struct platform_features knl_features = { +@@ -1152,7 +1153,7 @@ static const struct platform_features knl_features = { + .cst_limit = CST_LIMIT_KNL, + .has_msr_knl_core_c6_residency = 1, + .trl_msrs = TRL_KNL, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + .need_perf_multiplier = 1, + }; +@@ -1161,7 +1162,7 @@ static const struct platform_features default_features = { + }; + + static const struct platform_features amd_features_with_rapl = { +- .rapl_msrs = RAPL_AMD_F17H, ++ .plat_rapl_msrs = RAPL_AMD_F17H, + .has_per_core_rapl = 1, + .rapl_quirk_tdp = 280, /* This is the max stock TDP of HEDT/Server Fam17h+ chips */ + }; +@@ -2136,7 +2137,7 @@ off_t idx_to_offset(int idx) + + switch (idx) { + case IDX_PKG_ENERGY: +- if (platform->rapl_msrs & RAPL_AMD_F17H) ++ if (valid_rapl_msrs & RAPL_AMD_F17H) + offset = MSR_PKG_ENERGY_STAT; + else + offset = MSR_PKG_ENERGY_STATUS; +@@ -2202,19 +2203,19 @@ int idx_valid(int idx) + { + switch (idx) { + case IDX_PKG_ENERGY: +- return platform->rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H); ++ return valid_rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H); + case IDX_DRAM_ENERGY: +- return platform->rapl_msrs & RAPL_DRAM; ++ return valid_rapl_msrs & RAPL_DRAM; + case IDX_PP0_ENERGY: +- return platform->rapl_msrs & RAPL_CORE_ENERGY_STATUS; ++ return valid_rapl_msrs & RAPL_CORE_ENERGY_STATUS; + case IDX_PP1_ENERGY: +- return platform->rapl_msrs & RAPL_GFX; ++ return valid_rapl_msrs & RAPL_GFX; + case IDX_PKG_PERF: +- return platform->rapl_msrs & RAPL_PKG_PERF_STATUS; ++ return valid_rapl_msrs & RAPL_PKG_PERF_STATUS; + case IDX_DRAM_PERF: +- return platform->rapl_msrs & RAPL_DRAM_PERF_STATUS; ++ return valid_rapl_msrs & RAPL_DRAM_PERF_STATUS; + case IDX_PSYS_ENERGY: +- return platform->rapl_msrs & RAPL_PSYS; ++ return valid_rapl_msrs & RAPL_PSYS; + default: + return 0; + } +@@ -2517,7 +2518,7 @@ int add_rapl_msr_counter(int cpu, const struct rapl_counter_arch_info *cai) + { + int ret; + +- if (!(platform->rapl_msrs & cai->feature_mask)) ++ if (!(valid_rapl_msrs & cai->feature_mask)) + return -1; + + ret = add_msr_counter(cpu, cai->msr); +@@ -2850,10 +2851,10 @@ void print_header(char *delim) + if (DO_BIC(BIC_CORE_THROT_CNT)) + outp += sprintf(outp, "%sCoreThr", (printed++ ? delim : "")); + +- if (platform->rapl_msrs && !rapl_joules) { ++ if (valid_rapl_msrs && !rapl_joules) { + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) + outp += sprintf(outp, "%sCorWatt", (printed++ ? delim : "")); +- } else if (platform->rapl_msrs && rapl_joules) { ++ } else if (valid_rapl_msrs && rapl_joules) { + if (DO_BIC(BIC_Cor_J) && platform->has_per_core_rapl) + outp += sprintf(outp, "%sCor_J", (printed++ ? delim : "")); + } +@@ -7572,7 +7573,7 @@ double get_tdp_intel(void) + { + unsigned long long msr; + +- if (platform->rapl_msrs & RAPL_PKG_POWER_INFO) ++ if (valid_rapl_msrs & RAPL_PKG_POWER_INFO) + if (!get_msr(base_cpu, MSR_PKG_POWER_INFO, &msr)) + return ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units; + return get_quirk_tdp(); +@@ -7603,12 +7604,12 @@ void rapl_probe_intel(void) + CLR_BIC(BIC_GFX_J, &bic_enabled); + } + +- if (!platform->rapl_msrs || no_msr) ++ if (!valid_rapl_msrs || no_msr) + return; + +- if (!(platform->rapl_msrs & RAPL_PKG_PERF_STATUS)) ++ if (!(valid_rapl_msrs & RAPL_PKG_PERF_STATUS)) + CLR_BIC(BIC_PKG__, &bic_enabled); +- if (!(platform->rapl_msrs & RAPL_DRAM_PERF_STATUS)) ++ if (!(valid_rapl_msrs & RAPL_DRAM_PERF_STATUS)) + CLR_BIC(BIC_RAM__, &bic_enabled); + + /* units on package 0, verify later other packages match */ +@@ -7657,7 +7658,7 @@ void rapl_probe_amd(void) + CLR_BIC(BIC_Cor_J, &bic_enabled); + } + +- if (!platform->rapl_msrs || no_msr) ++ if (!valid_rapl_msrs || no_msr) + return; + + if (get_msr(base_cpu, MSR_RAPL_PWR_UNIT, &msr)) +@@ -7847,7 +7848,7 @@ int print_rapl(PER_THREAD_PARAMS) + UNUSED(c); + UNUSED(p); + +- if (!platform->rapl_msrs) ++ if (!valid_rapl_msrs) + return 0; + + /* RAPL counters are per package, so print only for 1st thread/package */ +@@ -7860,7 +7861,7 @@ int print_rapl(PER_THREAD_PARAMS) + return -1; + } + +- if (platform->rapl_msrs & RAPL_AMD_F17H) { ++ if (valid_rapl_msrs & RAPL_AMD_F17H) { + msr_name = "MSR_RAPL_PWR_UNIT"; + if (get_msr(cpu, MSR_RAPL_PWR_UNIT, &msr)) + return -1; +@@ -7873,7 +7874,7 @@ int print_rapl(PER_THREAD_PARAMS) + fprintf(outf, "cpu%d: %s: 0x%08llx (%f Watts, %f Joules, %f sec.)\n", cpu, msr_name, msr, + rapl_power_units, rapl_energy_units, rapl_time_units); + +- if (platform->rapl_msrs & RAPL_PKG_POWER_INFO) { ++ if (valid_rapl_msrs & RAPL_PKG_POWER_INFO) { + + if (get_msr(cpu, MSR_PKG_POWER_INFO, &msr)) + return -5; +@@ -7886,7 +7887,7 @@ int print_rapl(PER_THREAD_PARAMS) + ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + + } +- if (platform->rapl_msrs & RAPL_PKG) { ++ if (valid_rapl_msrs & RAPL_PKG) { + + if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr)) + return -9; +@@ -7910,7 +7911,7 @@ int print_rapl(PER_THREAD_PARAMS) + cpu, ((msr >> 0) & 0x1FFF) * rapl_power_units, (msr >> 31) & 1 ? "" : "UN"); + } + +- if (platform->rapl_msrs & RAPL_DRAM_POWER_INFO) { ++ if (valid_rapl_msrs & RAPL_DRAM_POWER_INFO) { + if (get_msr(cpu, MSR_DRAM_POWER_INFO, &msr)) + return -6; + +@@ -7921,7 +7922,7 @@ int print_rapl(PER_THREAD_PARAMS) + ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, + ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + } +- if (platform->rapl_msrs & RAPL_DRAM) { ++ if (valid_rapl_msrs & RAPL_DRAM) { + if (get_msr(cpu, MSR_DRAM_POWER_LIMIT, &msr)) + return -9; + fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", +@@ -7929,20 +7930,20 @@ int print_rapl(PER_THREAD_PARAMS) + + print_power_limit_msr(cpu, msr, "DRAM Limit"); + } +- if (platform->rapl_msrs & RAPL_CORE_POLICY) { ++ if (valid_rapl_msrs & RAPL_CORE_POLICY) { + if (get_msr(cpu, MSR_PP0_POLICY, &msr)) + return -7; + + fprintf(outf, "cpu%d: MSR_PP0_POLICY: %lld\n", cpu, msr & 0xF); + } +- if (platform->rapl_msrs & RAPL_CORE_POWER_LIMIT) { ++ if (valid_rapl_msrs & RAPL_CORE_POWER_LIMIT) { + if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr)) + return -9; + fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", + cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + print_power_limit_msr(cpu, msr, "Cores Limit"); + } +- if (platform->rapl_msrs & RAPL_GFX) { ++ if (valid_rapl_msrs & RAPL_GFX) { + if (get_msr(cpu, MSR_PP1_POLICY, &msr)) + return -8; + +@@ -7957,6 +7958,43 @@ int print_rapl(PER_THREAD_PARAMS) + return 0; + } + ++/* ++ * probe_rapl_msrs ++ * ++ * initialize global valid_rapl_msrs to platform->plat_rapl_msrs ++ * only if PKG_ENERGY counter is enumerated and reads non-zero ++ */ ++void probe_rapl_msrs(void) ++{ ++ int ret; ++ off_t offset; ++ unsigned long long msr_value; ++ ++ if (no_msr) ++ return; ++ ++ if ((platform->plat_rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H)) == 0) ++ return; ++ ++ offset = idx_to_offset(IDX_PKG_ENERGY); ++ if (offset < 0) ++ return; ++ ++ ret = get_msr(base_cpu, offset, &msr_value); ++ if (ret) { ++ if (debug) ++ fprintf(outf, "Can not read RAPL_PKG_ENERGY MSR(0x%llx)\n", (unsigned long long)offset); ++ return; ++ } ++ if (msr_value == 0) { ++ if (debug) ++ fprintf(outf, "RAPL_PKG_ENERGY MSR(0x%llx) == ZERO: disabling all RAPL MSRs\n", (unsigned long long)offset); ++ return; ++ } ++ ++ valid_rapl_msrs = platform->plat_rapl_msrs; /* success */ ++} ++ + /* + * probe_rapl() + * +@@ -7964,6 +8002,8 @@ int print_rapl(PER_THREAD_PARAMS) + */ + void probe_rapl(void) + { ++ probe_rapl_msrs(); ++ + if (genuine_intel) + rapl_probe_intel(); + if (authentic_amd || hygon_genuine) +@@ -7974,7 +8014,7 @@ void probe_rapl(void) + + print_rapl_sysfs(); + +- if (!platform->rapl_msrs || no_msr) ++ if (!valid_rapl_msrs || no_msr) + return; + + for_all_cpus(print_rapl, ODD_COUNTERS); +@@ -10157,7 +10197,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.11.29 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.12.01 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi b/SPECS/kernel-rt/0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi new file mode 100644 index 000000000..5d0cb3931 --- /dev/null +++ b/SPECS/kernel-rt/0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi @@ -0,0 +1,133 @@ +From 2a578d27be4741b1275fb893d00be8442ca0a48a Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 28 Feb 2023 13:06:07 -0800 +Subject: [PATCH 16/44] KVM: VMX: Dump FRED context in dump_vmcs() + +Add FRED related VMCS fields to dump_vmcs() to dump FRED context. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v5: +* Read guest FRED RSP0 with vmx_read_guest_fred_rsp0() (Sean). +* Add TB from Xuelian Guo. + +Change in v3: +* Use (vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED) instead of is_fred_enabled() + (Chao Gao). + +Changes in v2: +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). +* Dump guest FRED states only if guest has FRED enabled (Nikolay Borisov). +--- + arch/x86/kvm/vmx/vmx.c | 43 +++++++++++++++++++++++++++++++++++------- + 1 file changed, 36 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 9d12c4a01ab2f..4fdf80d02dadd 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1398,6 +1398,9 @@ static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) + vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, + &vmx->msr_guest_fred_rsp0); + } ++#else ++/* Make sure it builds on 32-bit */ ++static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) { return 0; } + #endif + + static void grow_ple_window(struct kvm_vcpu *vcpu) +@@ -6449,7 +6452,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + struct vcpu_vmx *vmx = to_vmx(vcpu); + u32 vmentry_ctl, vmexit_ctl; + u32 cpu_based_exec_ctrl, pin_based_exec_ctrl, secondary_exec_control; +- u64 tertiary_exec_control; ++ u64 tertiary_exec_control, secondary_vmexit_ctl; + unsigned long cr4; + int efer_slot; + +@@ -6460,6 +6463,8 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + + vmentry_ctl = vmcs_read32(VM_ENTRY_CONTROLS); + vmexit_ctl = vmcs_read32(VM_EXIT_CONTROLS); ++ secondary_vmexit_ctl = cpu_has_secondary_vmexit_ctrls() ? ++ vmcs_read64(SECONDARY_VM_EXIT_CONTROLS) : 0; + cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); + pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL); + cr4 = vmcs_readl(GUEST_CR4); +@@ -6506,6 +6511,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + vmx_dump_sel("LDTR:", GUEST_LDTR_SELECTOR); + vmx_dump_dtsel("IDTR:", GUEST_IDTR_LIMIT); + vmx_dump_sel("TR: ", GUEST_TR_SELECTOR); ++ if (vmentry_ctl & VM_ENTRY_LOAD_IA32_FRED) ++ pr_err("FRED guest: config=0x%016llx, stack_levels=0x%016llx\n" ++ "RSP0=0x%016llx, RSP1=0x%016llx\n" ++ "RSP2=0x%016llx, RSP3=0x%016llx\n", ++ vmcs_read64(GUEST_IA32_FRED_CONFIG), ++ vmcs_read64(GUEST_IA32_FRED_STKLVLS), ++ vmx_read_guest_fred_rsp0(vmx), ++ vmcs_read64(GUEST_IA32_FRED_RSP1), ++ vmcs_read64(GUEST_IA32_FRED_RSP2), ++ vmcs_read64(GUEST_IA32_FRED_RSP3)); + efer_slot = vmx_find_loadstore_msr_slot(&vmx->msr_autoload.guest, MSR_EFER); + if (vmentry_ctl & VM_ENTRY_LOAD_IA32_EFER) + pr_err("EFER= 0x%016llx\n", vmcs_read64(GUEST_IA32_EFER)); +@@ -6557,6 +6572,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + vmcs_readl(HOST_TR_BASE)); + pr_err("GDTBase=%016lx IDTBase=%016lx\n", + vmcs_readl(HOST_GDTR_BASE), vmcs_readl(HOST_IDTR_BASE)); ++ if (vmexit_ctl & SECONDARY_VM_EXIT_LOAD_IA32_FRED) ++ pr_err("FRED host: config=0x%016llx, stack_levels=0x%016llx\n" ++ "RSP0=0x%016lx, RSP1=0x%016llx\n" ++ "RSP2=0x%016llx, RSP3=0x%016llx\n", ++ vmcs_read64(HOST_IA32_FRED_CONFIG), ++ vmcs_read64(HOST_IA32_FRED_STKLVLS), ++ (unsigned long)task_stack_page(current) + THREAD_SIZE, ++ vmcs_read64(HOST_IA32_FRED_RSP1), ++ vmcs_read64(HOST_IA32_FRED_RSP2), ++ vmcs_read64(HOST_IA32_FRED_RSP3)); + pr_err("CR0=%016lx CR3=%016lx CR4=%016lx\n", + vmcs_readl(HOST_CR0), vmcs_readl(HOST_CR3), + vmcs_readl(HOST_CR4)); +@@ -6582,25 +6607,29 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + pr_err("*** Control State ***\n"); + pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", + cpu_based_exec_ctrl, secondary_exec_control, tertiary_exec_control); +- pr_err("PinBased=0x%08x EntryControls=%08x ExitControls=%08x\n", +- pin_based_exec_ctrl, vmentry_ctl, vmexit_ctl); ++ pr_err("PinBased=0x%08x EntryControls=0x%08x\n", ++ pin_based_exec_ctrl, vmentry_ctl); ++ pr_err("ExitControls=0x%08x SecondaryExitControls=0x%016llx\n", ++ vmexit_ctl, secondary_vmexit_ctl); + pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n", + vmcs_read32(EXCEPTION_BITMAP), + vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK), + vmcs_read32(PAGE_FAULT_ERROR_CODE_MATCH)); +- pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x\n", ++ pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x event_data=%016llx\n", + vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), + vmcs_read32(VM_ENTRY_EXCEPTION_ERROR_CODE), +- vmcs_read32(VM_ENTRY_INSTRUCTION_LEN)); ++ vmcs_read32(VM_ENTRY_INSTRUCTION_LEN), ++ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(INJECTED_EVENT_DATA) : 0); + pr_err("VMExit: intr_info=%08x errcode=%08x ilen=%08x\n", + vmcs_read32(VM_EXIT_INTR_INFO), + vmcs_read32(VM_EXIT_INTR_ERROR_CODE), + vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); + pr_err(" reason=%08x qualification=%016lx\n", + vmcs_read32(VM_EXIT_REASON), vmcs_readl(EXIT_QUALIFICATION)); +- pr_err("IDTVectoring: info=%08x errcode=%08x\n", ++ pr_err("IDTVectoring: info=%08x errcode=%08x event_data=%016llx\n", + vmcs_read32(IDT_VECTORING_INFO_FIELD), +- vmcs_read32(IDT_VECTORING_ERROR_CODE)); ++ vmcs_read32(IDT_VECTORING_ERROR_CODE), ++ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(ORIGINAL_EVENT_DATA) : 0); + pr_err("TSC Offset = 0x%016llx\n", vmcs_read64(TSC_OFFSET)); + if (secondary_exec_control & SECONDARY_EXEC_TSC_SCALING) + pr_err("TSC Multiplier = 0x%016llx\n", +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0016-KVM-x86-Advertise-support-for-FRED.nmi b/SPECS/kernel-rt/0016-KVM-x86-Advertise-support-for-FRED.nmi deleted file mode 100644 index 965f69f75..000000000 --- a/SPECS/kernel-rt/0016-KVM-x86-Advertise-support-for-FRED.nmi +++ /dev/null @@ -1,37 +0,0 @@ -From d8890903db72a044940523acbf532c7807141723 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Wed, 11 May 2022 17:53:27 -0700 -Subject: [PATCH 16/44] KVM: x86: Advertise support for FRED - -Advertise support for FRED to userspace after changes required to enable -FRED in a KVM guest are in place. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Don't advertise FRED/LKGS together, LKGS can be advertised as an - independent feature (Sean). -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/cpuid.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index 5f76dd7a7620..15217805c007 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -996,6 +996,7 @@ void kvm_set_cpu_caps(void) - F(FSRS), - F(FSRC), - F(WRMSRNS), -+ X86_64_F(FRED), - X86_64_F(LKGS), - F(AMX_FP16), - F(AVX_IFMA), --- -2.43.0 - diff --git a/SPECS/kernel-rt/0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet b/SPECS/kernel-rt/0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet deleted file mode 100644 index ca07fbf34..000000000 --- a/SPECS/kernel-rt/0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet +++ /dev/null @@ -1,63 +0,0 @@ -From a1ccfaa33793cad7f90e4523bc33708cf05e6111 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:46 -0700 -Subject: [PATCH 16/24] KVM: x86: Save and reload SSP to/from SMRAM - -Save CET SSP to SMRAM on SMI and reload it on RSM. KVM emulates HW arch -behavior when guest enters/leaves SMM mode,i.e., save registers to SMRAM -at the entry of SMM and reload them at the exit to SMM. Per SDM, SSP is -one of such registers on 64-bit Arch, and add the support for SSP. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/smm.c | 8 ++++++++ - arch/x86/kvm/smm.h | 2 +- - 2 files changed, 9 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c -index 5dd8a1646800..b0b14ba37f9a 100644 ---- a/arch/x86/kvm/smm.c -+++ b/arch/x86/kvm/smm.c -@@ -269,6 +269,10 @@ static void enter_smm_save_state_64(struct kvm_vcpu *vcpu, - enter_smm_save_seg_64(vcpu, &smram->gs, VCPU_SREG_GS); - - smram->int_shadow = kvm_x86_call(get_interrupt_shadow)(vcpu); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ kvm_msr_read(vcpu, MSR_KVM_INTERNAL_GUEST_SSP, &smram->ssp)) -+ kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); - } - #endif - -@@ -558,6 +562,10 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, - kvm_x86_call(set_interrupt_shadow)(vcpu, 0); - ctxt->interruptibility = (u8)smstate->int_shadow; - -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ kvm_msr_write(vcpu, MSR_KVM_INTERNAL_GUEST_SSP, smstate->ssp)) -+ return X86EMUL_UNHANDLEABLE; -+ - return X86EMUL_CONTINUE; - } - #endif -diff --git a/arch/x86/kvm/smm.h b/arch/x86/kvm/smm.h -index 551703fbe200..db3c88f16138 100644 ---- a/arch/x86/kvm/smm.h -+++ b/arch/x86/kvm/smm.h -@@ -116,8 +116,8 @@ struct kvm_smram_state_64 { - u32 smbase; - u32 reserved4[5]; - -- /* ssp and svm_* fields below are not implemented by KVM */ - u64 ssp; -+ /* svm_* fields below are not implemented by KVM */ - u64 svm_guest_pat; - u64 svm_host_efer; - u64 svm_host_cr4; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu b/SPECS/kernel-rt/0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu deleted file mode 100644 index 955681991..000000000 --- a/SPECS/kernel-rt/0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu +++ /dev/null @@ -1,806 +0,0 @@ -From c5b019c970132fd87f0d8c9477ec6620c1404c60 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 26 Jan 2026 16:00:15 +0800 -Subject: [PATCH 16/21] Port over IPU ACPI drivers changes from VTG github repo - -Removing Linux version macro from upstream code. -This macro removal is to align with innersource -kernel PR submission standard. - -These are the ACPI drivers copied over from ipu6-drivers repo: -- drivers/media/platform/intel/Kconfig -- drivers/media/platform/intel/Makefile -- drivers/media/platform/intel/ipu-acpi-common.c -- drivers/media/platform/intel/ipu-acpi-pdata.c -- drivers/media/platform/intel/ipu-acpi.c - -This is the ACPI drivers copied over from ipu7-drivers repo: -- drivers/media/platform/intel/ipu7-fpga-pdata.c - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/platform/intel/Kconfig | 3 +- - drivers/media/platform/intel/Makefile | 8 +- - .../media/platform/intel/ipu-acpi-common.c | 8 +- - drivers/media/platform/intel/ipu-acpi-pdata.c | 394 ++++++++++++++++-- - drivers/media/platform/intel/ipu-acpi.c | 68 ++- - .../media/platform/intel/ipu7-fpga-pdata.c | 3 + - 6 files changed, 414 insertions(+), 70 deletions(-) - -diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig -index f8affa636c5c..4ec9c70b80cf 100644 ---- a/drivers/media/platform/intel/Kconfig -+++ b/drivers/media/platform/intel/Kconfig -@@ -8,9 +8,8 @@ config INTEL_IPU7_FPGA_PDATA - development and mainly used for pixter or sensor enablement - without ACPI support. - --config INTEL_IPU7_ACPI -+config INTEL_IPU_ACPI - tristate "Enable IPU ACPI driver" -- default VIDEO_INTEL_IPU7 - depends on I2C - depends on ACPI - help -diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile -index d0c41b731794..64ecc8c1fff3 100644 ---- a/drivers/media/platform/intel/Makefile -+++ b/drivers/media/platform/intel/Makefile -@@ -1,5 +1,5 @@ - # SPDX-License-Identifier: GPL-2.0 --# Copyright (c) 2010 - 2022 Intel Corporation. -+# Copyright (c) 2010 - 2025 Intel Corporation. - - is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) - ifeq ($(is_kernel_lt_6_10), 1) -@@ -8,10 +8,10 @@ src := $(srctree)/$(src) - endif - endif - --ccflags-y += -I$(src)/../../pci/intel/ipu7/ -+ccflags-y += -I$(src)/../../../staging/media/ipu7/ - --ifneq ($(filter y m, $(CONFIG_INTEL_IPU7_ACPI)),) --obj-$(CONFIG_INTEL_IPU7_ACPI) += ipu-acpi.o \ -+ifneq ($(filter y m, $(CONFIG_INTEL_IPU_ACPI)),) -+obj-$(CONFIG_INTEL_IPU_ACPI) += ipu-acpi.o \ - ipu-acpi-pdata.o \ - ipu-acpi-common.o - else -diff --git a/drivers/media/platform/intel/ipu-acpi-common.c b/drivers/media/platform/intel/ipu-acpi-common.c -index ee7bd6c93f9a..f846cb574fca 100644 ---- a/drivers/media/platform/intel/ipu-acpi-common.c -+++ b/drivers/media/platform/intel/ipu-acpi-common.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0 - /* -- * Copyright (c) 2016-2024 Intel Corporation. -+ * Copyright (c) 2016-2025 Intel Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version -@@ -12,8 +12,12 @@ - * GNU General Public License for more details. - * - */ --#include -+ - #include -+#include -+#include -+#include -+ - #include - #include - -diff --git a/drivers/media/platform/intel/ipu-acpi-pdata.c b/drivers/media/platform/intel/ipu-acpi-pdata.c -index 0a84dccf980c..8488693f0068 100644 ---- a/drivers/media/platform/intel/ipu-acpi-pdata.c -+++ b/drivers/media/platform/intel/ipu-acpi-pdata.c -@@ -12,18 +12,32 @@ - * GNU General Public License for more details. - * - */ -- - #include - #include - - #define MIN_SENSOR_I2C 1 - #define MIN_SERDES_I2C 3 -+#define SENSOR_2X_I2C 5 - #define SUFFIX_BASE 97 -+#define MSG_LEN 128 - --struct ipu7_isys_subdev_pdata acpi_subdev_pdata = { -- .subdevs = (struct ipu7_isys_subdev_info *[]) { -- NULL, -- } -+static struct ipu_isys_subdev_pdata *ptr_built_in_pdata; -+ -+void set_built_in_pdata(struct ipu_isys_subdev_pdata *pdata) -+{ -+ ptr_built_in_pdata = pdata; -+}; -+EXPORT_SYMBOL(set_built_in_pdata); -+ -+static struct ipu_isys_clk_mapping clk_mapping[] = { -+ { CLKDEV_INIT(NULL, NULL, NULL), NULL } -+}; -+ -+struct ipu_isys_subdev_pdata acpi_subdev_pdata = { -+ .subdevs = (struct ipu_isys_subdev_info *[]) { -+ NULL, NULL, NULL, NULL, NULL, -+ }, -+ .clk_map = clk_mapping, - }; - - struct serdes_local serdes_info; -@@ -74,7 +88,8 @@ static int get_i2c_bus_id(int adapter_id, char *adapter_bdf, int bdf_len) - */ - static void update_i2c_bus_id(void) - { -- struct ipu7_isys_subdev_info **subdevs = acpi_subdev_pdata.subdevs; -+ struct ipu_isys_subdev_info **subdevs = acpi_subdev_pdata.subdevs; -+ - for (int i = 0; subdevs[i] != NULL; i++) { - subdevs[i]->i2c.i2c_adapter_id = - get_i2c_bus_id(subdevs[i]->i2c.i2c_adapter_id, -@@ -83,9 +98,9 @@ static void update_i2c_bus_id(void) - } - } - --struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void) -+struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void) - { -- struct ipu7_isys_subdev_pdata *ptr; -+ struct ipu_isys_subdev_pdata *ptr; - - update_i2c_bus_id(); - ptr = &acpi_subdev_pdata; -@@ -122,7 +137,7 @@ static void print_serdes_sdinfo(struct serdes_subdev_info *sdinfo) - (int)sd_mpdata->gpio_powerup_seq[i]); - } - --static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) -+static void print_serdes_subdev(struct ipu_isys_subdev_info *sd) - { - struct serdes_platform_data *sd_pdata = sd->i2c.board_info.platform_data; - int i; -@@ -153,6 +168,7 @@ static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) - pr_debug("\t\tlink_freq_mbps \t\t= %d", sd_pdata->link_freq_mbps); - pr_debug("\t\tdeser_nlanes \t\t= %d", sd_pdata->deser_nlanes); - pr_debug("\t\tser_nlanes \t\t= %d", sd_pdata->ser_nlanes); -+ pr_debug("\t\tser_name \t\t= %s", sd_pdata->ser_name); - - for (i = 0; i < serdes_info.rx_port; i++) { - sd_sdinfo = &sd_pdata->subdev_info[i]; -@@ -167,7 +183,7 @@ static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) - - } - --static void print_subdev(struct ipu7_isys_subdev_info *sd) -+static void print_subdev(struct ipu_isys_subdev_info *sd) - { - struct sensor_platform_data *spdata = sd->i2c.board_info.platform_data; - int i; -@@ -198,10 +214,316 @@ static void print_subdev(struct ipu7_isys_subdev_info *sd) - pr_debug("\t\treset_pin \t\t= %d", spdata->reset_pin); - pr_debug("\t\tdetect_pin \t\t= %d", spdata->detect_pin); - -- for (i = 0; i < IPU7_SPDATA_GPIO_NUM; i++) -+ for (i = 0; i < IPU_SPDATA_GPIO_NUM; i++) - pr_debug("\t\tgpios[%d] \t\t= %d", i, spdata->gpios[i]); - } - -+static void add_local_subdevs(struct ipu_isys_subdev_info *new_subdev_info) -+{ -+ struct ipu_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -+ int i = 0; -+ -+ while (i < MAX_ACPI_SENSOR_NUM) { -+ if (!ptr_acpi_subdev_pdata->subdevs[i]) { -+ ptr_acpi_subdev_pdata->subdevs[i] = new_subdev_info; -+ ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -+ break; -+ } -+ i++; -+ } -+} -+ -+static void update_short(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned short *old_short, -+ unsigned int new_short) -+{ -+ if (*old_short != new_short) { -+ dev_info(dev, "%s 0x%x -> 0x%x", msg, *old_short, new_short); -+ *old_short = new_short; -+ } -+} -+ -+static void update_hex(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned int *old_hex, -+ unsigned int new_hex) -+{ -+ if (*old_hex != new_hex) { -+ dev_info(dev, "%s 0x%x -> 0x%x", msg, *old_hex, new_hex); -+ *old_hex = new_hex; -+ } -+} -+ -+static void update_int(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned int *old_int, -+ unsigned int new_int) -+{ -+ if (*old_int != new_int) { -+ dev_info(dev, "%s %d -> %d", msg, *old_int, new_int); -+ *old_int = new_int; -+ } -+} -+ -+static void update_inta(struct device *dev, -+ char msg[MSG_LEN], -+ int old_int[MSG_LEN], -+ int new_int[MSG_LEN], -+ size_t size) -+{ -+ int i; -+ -+ for (i = 0; i < size; i++) { -+ if (old_int[i] != new_int[i]) { -+ dev_info(dev, "%s %d -> %d", msg, old_int[i], new_int[i]); -+ old_int[i] = new_int[i]; -+ } -+ } -+} -+ -+static void update_str(struct device *dev, -+ char msg[MSG_LEN], -+ char old_str[MSG_LEN], -+ char new_str[MSG_LEN]) -+{ -+ if (strcmp(old_str, new_str) != 0) { -+ dev_info(dev, "%s %s -> %s", msg, old_str, new_str); -+ strscpy(old_str, new_str, strlen(new_str)+1); -+ } -+} -+ -+static void update_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_sd, -+ struct ipu_isys_subdev_info **old_sd) -+{ -+ struct sensor_platform_data *old_pdata = -+ (*old_sd)->i2c.board_info.platform_data; -+ -+ struct sensor_platform_data *new_pdata = -+ new_sd->i2c.board_info.platform_data; -+ -+ /* csi2 */ -+ update_int(dev, "CSI2 port", &(*old_sd)->csi2->port, new_sd->csi2->port); -+ update_int(dev, "CSI2 nlanes", &(*old_sd)->csi2->nlanes, new_sd->csi2->nlanes); -+ -+ /* i2c */ -+ update_short(dev, "I2C board_info addr", &(*old_sd)->i2c.board_info.addr, -+ new_sd->i2c.board_info.addr); -+ update_str(dev, "I2C i2c_adapter_bdf", (*old_sd)->i2c.i2c_adapter_bdf, -+ new_sd->i2c.i2c_adapter_bdf); -+ -+ /* platform data */ -+ update_int(dev, "pdata port", &(old_pdata)->port, new_pdata->port); -+ update_int(dev, "pdata lanes", &(old_pdata)->lanes, new_pdata->lanes); -+ update_hex(dev, "pdata I2C slave addr", &(old_pdata)->i2c_slave_address, -+ new_pdata->i2c_slave_address); -+ update_int(dev, "pdata irq_pin", &(old_pdata)->irq_pin, new_pdata->irq_pin); -+ update_str(dev, "pdata irq_pin_name", old_pdata->irq_pin_name, new_pdata->irq_pin_name); -+ update_int(dev, "pdata reset_pin", &(old_pdata)->reset_pin, new_pdata->reset_pin); -+ update_int(dev, "pdata detect_pin", &(old_pdata)->detect_pin, new_pdata->detect_pin); -+ update_inta(dev, "pdata gpios", old_pdata->gpios, new_pdata->gpios, IPU_SPDATA_GPIO_NUM); -+} -+ -+static void update_serdes_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_sd, -+ struct ipu_isys_subdev_info **old_sd) -+{ -+ struct serdes_platform_data *old_pdata = -+ (*old_sd)->i2c.board_info.platform_data; -+ -+ struct serdes_platform_data *new_pdata = -+ new_sd->i2c.board_info.platform_data; -+ -+ int i; -+ struct serdes_subdev_info *old_sdinfo, *new_sdinfo; -+ struct serdes_module_pdata *old_mpdata, *new_mpdata; -+ -+ /* csi2 */ -+ update_int(dev, "CSI2 port", &(*old_sd)->csi2->port, new_sd->csi2->port); -+ update_int(dev, "CSI2 nlanes", &(*old_sd)->csi2->nlanes, new_sd->csi2->nlanes); -+ -+ /* i2c */ -+ update_short(dev, "I2C board_info addr", &(*old_sd)->i2c.board_info.addr, -+ new_sd->i2c.board_info.addr); -+ update_str(dev, "I2C i2c_adapter_bdf", (*old_sd)->i2c.i2c_adapter_bdf, -+ new_sd->i2c.i2c_adapter_bdf); -+ -+ update_int(dev, "I2C Pdata reset_gpio", &old_pdata->reset_gpio, -+ new_pdata->reset_gpio); -+ update_int(dev, "I2C Pdata FPD_gpio", &old_pdata->FPD_gpio, new_pdata->FPD_gpio); -+ -+ /* platform data */ -+ for (i = 0; i < SERDES_MAX_PORT; i++) { -+ old_sdinfo = &old_pdata->subdev_info[i]; -+ old_mpdata = old_sdinfo->board_info.platform_data; -+ -+ new_sdinfo = &new_pdata->subdev_info[i]; -+ new_mpdata = new_sdinfo->board_info.platform_data; -+ -+ if (!strcmp(old_sdinfo->board_info.type, new_sdinfo->board_info.type) && -+ old_sdinfo->suffix == new_sdinfo->suffix) { -+ update_short(dev, "SdInfo port", &old_sdinfo->rx_port, -+ new_sdinfo->rx_port); -+ update_short(dev, "SdInfo ser_alias", &old_sdinfo->ser_alias, -+ new_sdinfo->ser_alias); -+ update_short(dev, "SdInfo board_info.addr", &old_sdinfo->board_info.addr, -+ new_sdinfo->board_info.addr); -+ -+ if (!strcmp(old_mpdata->module_name, new_mpdata->module_name)) { -+ update_int(dev, "mPdata lanes", &old_mpdata->lanes, -+ new_mpdata->lanes); -+ update_int(dev, "mPdata fsin", &old_mpdata->fsin, -+ new_mpdata->fsin); -+ update_inta(dev, "mPdata gpio_powerup_seq", -+ (int *)old_mpdata->gpio_powerup_seq, -+ (int *)new_mpdata->gpio_powerup_seq, -+ SERDES_MAX_GPIO_POWERUP_SEQ); -+ } -+ } -+ } -+} -+ -+static int compare_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_subdev, -+ struct ipu_isys_subdev_info *old_subdev, -+ enum connection_type connect) -+{ -+ /* check for ACPI HID in existing pdata */ -+ if (old_subdev->acpi_hid) { -+ /* compare with HID for User Custom */ -+ if (!strcmp(old_subdev->acpi_hid, dev_name(dev))) { -+ dev_info(dev, "Found matching sensor : %s", dev_name(dev)); -+ return 0; -+ } -+ } -+ /* compare sensor type */ -+ if (!strcmp(old_subdev->i2c.board_info.type, -+ new_subdev->i2c.board_info.type)) { -+ -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *old_pdata, *new_pdata; -+ -+ old_pdata = (struct sensor_platform_data *) -+ old_subdev->i2c.board_info.platform_data; -+ -+ new_pdata = (struct sensor_platform_data *) -+ new_subdev->i2c.board_info.platform_data; -+ -+ if (old_pdata->suffix == new_pdata->suffix) { -+ dev_info(dev, "Found matching sensor : %s %c", -+ old_subdev->i2c.board_info.type, -+ old_pdata->suffix); -+ return 0; -+ } -+ } else if (connect == TYPE_SERDES) { -+ struct serdes_platform_data *old_pdata, *new_pdata; -+ -+ old_pdata = (struct serdes_platform_data *) -+ old_subdev->i2c.board_info.platform_data; -+ -+ new_pdata = (struct serdes_platform_data *) -+ new_subdev->i2c.board_info.platform_data; -+ -+ if (old_pdata->suffix == new_pdata->suffix) { -+ dev_info(dev, "Found matching sensor : %s %c", -+ old_subdev->i2c.board_info.type, -+ old_pdata->suffix); -+ return 0; -+ } -+ } -+ } -+ return -1; -+} -+ -+static void update_pdata(struct device *dev, -+ struct ipu_isys_subdev_info *new_subdev, -+ enum connection_type connect) -+{ -+ struct ipu_isys_subdev_info *acpi_subdev; -+ bool found = false; -+ -+ acpi_subdev = new_subdev; -+ -+ /* update local ipu_isys_subdev_pdata */ -+ add_local_subdevs(acpi_subdev); -+ -+ /* found existing pdata */ -+ if (ptr_built_in_pdata) { -+ struct ipu_isys_subdev_info **subdevs, *sd_info; -+ -+ for (subdevs = ptr_built_in_pdata->subdevs; *subdevs; subdevs++) { -+ sd_info = *subdevs; -+ -+ /* found similar subdev in existing pdata*/ -+ if (!compare_subdev(dev, acpi_subdev, sd_info, connect)) { -+ /* print and update old subdev */ -+ if (connect == TYPE_DIRECT) { -+ dev_dbg(dev, "Old sensor subdev\n"); -+ print_subdev(sd_info); -+ update_subdev(dev, acpi_subdev, &sd_info); -+ dev_dbg(dev, "Updated subdev\n"); -+ print_subdev(sd_info); -+ } else if (connect == TYPE_SERDES) { -+ dev_dbg(dev, "Old serdes subdev\n"); -+ print_serdes_subdev(sd_info); -+ update_serdes_subdev(dev, acpi_subdev, &sd_info); -+ dev_dbg(dev, "Updated subdev\n"); -+ print_serdes_subdev(sd_info); -+ } -+ -+ /* stop once similar subdev updated */ -+ found = true; -+ break; -+ } -+ } -+ -+ /* no similar subdev found */ -+ if (!found) { -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *acpi_pdata; -+ -+ acpi_pdata = (struct sensor_platform_data *) -+ acpi_subdev->i2c.board_info.platform_data; -+ -+ dev_err(dev, "Pdata does not contain %s %c\n", -+ acpi_subdev->i2c.board_info.type, -+ acpi_pdata->suffix); -+ -+ /* print new subdev */ -+ print_subdev(acpi_subdev); -+ -+ } else { -+ struct serdes_platform_data *acpi_pdata; -+ -+ acpi_pdata = (struct serdes_platform_data *) -+ acpi_subdev->i2c.board_info.platform_data; -+ -+ dev_err(dev, "Pdata does not contain %s %c\n", -+ acpi_subdev->i2c.board_info.type, -+ acpi_pdata->suffix); -+ -+ print_serdes_subdev(acpi_subdev); -+ } -+ } -+ } -+ /* does not have existing pdata */ -+ else { -+ /* print new subdev */ -+ if (connect == TYPE_DIRECT) { -+ pr_debug("New sensor subdev\n"); -+ print_subdev(acpi_subdev); -+ } else { -+ pr_debug("New serdes subdev\n"); -+ print_serdes_subdev(acpi_subdev); -+ } -+ } -+ -+ /* update total num of sensor connected */ -+ if (connect == TYPE_SERDES) -+ serdes_info.deser_num++; -+} -+ - static void set_common_gpio(struct control_logic_data *ctl_data, - struct sensor_platform_data **pdata) - { -@@ -226,11 +548,11 @@ static void set_common_gpio(struct control_logic_data *ctl_data, - ctl_data->gpio[i].func); - } - --static int set_csi2(struct ipu7_isys_subdev_info **sensor_sd, -+static int set_csi2(struct ipu_isys_subdev_info **sensor_sd, - unsigned int lanes, unsigned int port, - unsigned int bus_type) - { -- struct ipu7_isys_csi2_config *csi2_config; -+ struct ipu_isys_csi2_config *csi2_config; - - csi2_config = kzalloc(sizeof(*csi2_config), GFP_KERNEL); - if (!csi2_config) -@@ -250,7 +572,7 @@ static int set_csi2(struct ipu7_isys_subdev_info **sensor_sd, - return 0; - } - --static void set_i2c(struct ipu7_isys_subdev_info **sensor_sd, -+static void set_i2c(struct ipu_isys_subdev_info **sensor_sd, - struct device *dev, - const char *sensor_name, - unsigned int addr, -@@ -274,7 +596,7 @@ static void set_serdes_sd_pdata(struct serdes_module_pdata **module_pdata, - - #define PORT_NR 8 - --static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, -+static int set_serdes_subdev(struct ipu_isys_subdev_info **serdes_sd, - struct device *dev, - struct serdes_platform_data **pdata, - const char *sensor_name, -@@ -304,12 +626,13 @@ static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, - - /* board info */ - strscpy(serdes_sdinfo[i].board_info.type, sensor_name, I2C_NAME_SIZE); -- serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ - serdes_sdinfo[i].board_info.platform_data = module_pdata[i]; - - /* serdes_subdev_info */ - serdes_sdinfo[i].rx_port = i; -- serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; -+ serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; - - serdes_sdinfo[i].phy_i2c_addr = serdes_info.phy_i2c_addr; - snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", -@@ -326,7 +649,7 @@ static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, - return 0; - } - --static int set_pdata(struct ipu7_isys_subdev_info **sensor_sd, -+static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, - struct device *dev, - const char *sensor_name, - const char *hid_name, -@@ -363,7 +686,7 @@ static int set_pdata(struct ipu7_isys_subdev_info **sensor_sd, - pdata->i2c_slave_address = addr; - - /* gpio */ -- set_common_gpio(ctl_data, &pdata); -+ set_common_gpio(ctl_data, &pdata); - - (*sensor_sd)->i2c.board_info.platform_data = pdata; - } else if (connect == TYPE_SERDES) { -@@ -423,7 +746,7 @@ static void set_serdes_info(struct device *dev, const char *sensor_name, - } - - static int populate_sensor_pdata(struct device *dev, -- struct ipu7_isys_subdev_info **sensor_sd, -+ struct ipu_isys_subdev_info **sensor_sd, - struct sensor_bios_data *cam_data, - struct control_logic_data *ctl_data, - enum connection_type connect, -@@ -433,8 +756,6 @@ static int populate_sensor_pdata(struct device *dev, - int sensor_physical_addr, - int link_freq) - { -- struct ipu7_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -- int i = 0; - int ret; - - if (connect == TYPE_DIRECT) { -@@ -453,7 +774,7 @@ static int populate_sensor_pdata(struct device *dev, - cam_data->i2c_num); - return -1; - } -- /* Others use DISCRETE Control Logic */ -+ - if (ctl_data->type != CL_DISCRETE) { - dev_err(dev, "IPU ACPI: Control Logic Type\n"); - dev_err(dev, "for %s: %d is Incorrect\n", -@@ -495,28 +816,9 @@ static int populate_sensor_pdata(struct device *dev, - if (ret) - return ret; - -- /* update local ipu7_isys_subdev_pdata */ -- while (i <= MAX_ACPI_SENSOR_NUM) { -- if (!ptr_acpi_subdev_pdata->subdevs[i]) { -- ptr_acpi_subdev_pdata->subdevs[i] = *sensor_sd; -- ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -- break; -- } -- i++; -- } -- -- /* print new subdev */ -- if (connect == TYPE_DIRECT) { -- pr_debug("New sensor subdev\n"); -- print_subdev(*sensor_sd); -- } else { -- pr_debug("New serdes subdev\n"); -- print_serdes_subdev(*sensor_sd); -- } -+ update_pdata(dev, *sensor_sd, connect); - -- /* update total num of sensor connected */ -- if (connect == TYPE_SERDES) -- serdes_info.deser_num++; -+ /* Lontium specific */ - - return 0; - } -@@ -530,12 +832,13 @@ int get_sensor_pdata(struct device *dev, - { - struct sensor_bios_data *cam_data; - struct control_logic_data *ctl_data; -- struct ipu7_isys_subdev_info *sensor_sd; -+ struct ipu_isys_subdev_info *sensor_sd; - int rval; - - cam_data = kzalloc(sizeof(*cam_data), GFP_KERNEL); - if (!cam_data) - return -ENOMEM; -+ - cam_data->dev = dev; - - ctl_data = kzalloc(sizeof(*ctl_data), GFP_KERNEL); -@@ -543,6 +846,7 @@ int get_sensor_pdata(struct device *dev, - kfree(cam_data); - return -ENOMEM; - } -+ - ctl_data->dev = dev; - - sensor_sd = kzalloc(sizeof(*sensor_sd), GFP_KERNEL); -diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c -index c254c8299930..8b94dc307735 100644 ---- a/drivers/media/platform/intel/ipu-acpi.c -+++ b/drivers/media/platform/intel/ipu-acpi.c -@@ -32,15 +32,10 @@ - #include - #include - #include -+ - #include - #include -- --#if IS_ENABLED(CONFIG_VIDEO_ISX031) --#include --#endif -- --#include "ipu7.h" --#include "ipu7-isys.h" -+#include - - static LIST_HEAD(devices); - -@@ -73,7 +68,7 @@ static const struct ipu_acpi_devices supported_devices[] = { - - static int get_table_index(const char *acpi_name) - { -- unsigned int i; -+ int i; - - for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { - if (!strncmp(supported_devices[i].hid_name, acpi_name, -@@ -143,8 +138,8 @@ static int ipu_acpi_test(struct device *dev, void *priv) - - if (acpi_idx < 0) - return 0; -- else -- dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); -+ -+ dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); - - const char *target_hid = supported_devices[acpi_idx].hid_name; - -@@ -161,10 +156,10 @@ static int ipu_acpi_test(struct device *dev, void *priv) - if (!adev) { - dev_dbg(dev, "No ACPI device found for %s\n", target_hid); - return 0; -- } else { -- set_primary_fwnode(dev, &adev->fwnode); -- dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); - } -+ -+ set_primary_fwnode(dev, &adev->fwnode); -+ dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); - } - - if (ACPI_COMPANION(dev) != adev) { -@@ -185,14 +180,52 @@ static int ipu_acpi_test(struct device *dev, void *priv) - return 0; /* Continue iteration */ - } - -+/* Scan all i2c devices and pick ones which we can handle */ -+ - /* Try to get all IPU related devices mentioned in BIOS and all related information -- * return a new generated existing pdata -+ * If there is existing ipu_isys_subdev_pdata, update the existing pdata -+ * If not, return a new generated existing pdata - */ - --int ipu_get_acpi_devices(void **spdata) -+int ipu_get_acpi_devices(void *driver_data, -+ struct device *dev, -+ void **spdata, -+ void **built_in_pdata, -+ int (*fn) -+ (struct device *, void *, -+ void *csi2, -+ bool reprobe)) - { - int rval; -- struct ipu7_isys_subdev_pdata *ptr = NULL; -+ -+ if (!built_in_pdata) -+ dev_dbg(dev, "Built-in pdata not found"); -+ else { -+ dev_dbg(dev, "Built-in pdata found"); -+ set_built_in_pdata(*built_in_pdata); -+ } -+ -+ if ((!fn) || (!driver_data)) -+ return -ENODEV; -+ -+ rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); -+ if (rval < 0) -+ return rval; -+ -+ if (!built_in_pdata) { -+ dev_dbg(dev, "Return ACPI generated pdata"); -+ *spdata = get_acpi_subdev_pdata(); -+ } else -+ dev_dbg(dev, "Return updated built-in pdata"); -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_get_acpi_devices); -+ -+int ipu_get_acpi_devices_new(void **spdata) -+{ -+ int rval; -+ struct ipu_isys_subdev_pdata *ptr = NULL; - - rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); - if (rval < 0) -@@ -204,10 +237,11 @@ int ipu_get_acpi_devices(void **spdata) - - return 0; - } --EXPORT_SYMBOL(ipu_get_acpi_devices); -+EXPORT_SYMBOL(ipu_get_acpi_devices_new); - - static int __init ipu_acpi_init(void) - { -+ set_built_in_pdata(NULL); - return 0; - } - -diff --git a/drivers/media/platform/intel/ipu7-fpga-pdata.c b/drivers/media/platform/intel/ipu7-fpga-pdata.c -index 9f60a38d1536..753e2f5c23ad 100644 ---- a/drivers/media/platform/intel/ipu7-fpga-pdata.c -+++ b/drivers/media/platform/intel/ipu7-fpga-pdata.c -@@ -3,6 +3,7 @@ - #include - #include - #include -+#include - - #include "ipu7.h" - #include "ipu7-isys.h" -@@ -14,6 +15,7 @@ - static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_0 = { - .nlanes = OV13B10_LANES, - .port = 0, -+ .bus_type = V4L2_MBUS_CSI2_DPHY, - }; - - static struct ipu7_isys_subdev_info ov13b10_sd_0 = { -@@ -29,6 +31,7 @@ static struct ipu7_isys_subdev_info ov13b10_sd_0 = { - static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_1 = { - .nlanes = OV13B10_2LANES, - .port = 2, -+ .bus_type = V4L2_MBUS_CSI2_DPHY, - }; - - static struct ipu7_isys_subdev_info ov13b10_sd_1 = { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0016-cpuidle-Update-header-inclusion.rapl b/SPECS/kernel-rt/0016-cpuidle-Update-header-inclusion.rapl new file mode 100644 index 000000000..f4e0a168f --- /dev/null +++ b/SPECS/kernel-rt/0016-cpuidle-Update-header-inclusion.rapl @@ -0,0 +1,40 @@ +From 135d26f20b612def7d998378fce999adfa9df715 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Mon, 24 Nov 2025 21:57:52 +0100 +Subject: [PATCH 16/17] cpuidle: Update header inclusion + +While cleaning up some headers, I got a build error on this file: + +drivers/cpuidle/poll_state.c:52:2: error: call to undeclared library function 'snprintf' with type 'int (char *restrict, unsigned long, const char *restrict, ...)'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] + +Update header inclusions to follow IWYU (Include What You Use) +principle. + +Signed-off-by: Andy Shevchenko +Link: https://patch.msgid.link/20251124205752.1328701-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/poll_state.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c +index 9b6d90a726019..c7524e4c522a2 100644 +--- a/drivers/cpuidle/poll_state.c ++++ b/drivers/cpuidle/poll_state.c +@@ -4,9 +4,13 @@ + */ + + #include ++#include ++#include + #include + #include + #include ++#include ++#include + + #define POLL_IDLE_RELAX_COUNT 200 + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet b/SPECS/kernel-rt/0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet deleted file mode 100644 index 6b689a7a6..000000000 --- a/SPECS/kernel-rt/0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet +++ /dev/null @@ -1,31 +0,0 @@ -From be0eb7643ae076c4c70fdf2cb0f75fb053345394 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Fri, 30 Jul 2021 10:17:52 +0800 -Subject: [PATCH 16/19] igc: Remove the CONFIG_DEBUG_MISC condition for trace - -This patch is to remove the CONFIG_DEBUG_MISC for trace_printk. -CONFIG_DEBUG_MISC was enabled in ER89 config but not enable in -latest config. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc_main.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 8fdeea791b1d..ace39bc52667 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -3255,7 +3255,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - - switch (tx_buffer->type) { - case IGC_TX_BUFFER_TYPE_XSK: --#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) -+#if defined(CONFIG_TRACING) - /* Only use for RTCP KPI Measurement on Q2 */ - if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) - trace_printk("TX HW TS %lld\n", timestamp); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet b/SPECS/kernel-rt/0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet new file mode 100644 index 000000000..db7550541 --- /dev/null +++ b/SPECS/kernel-rt/0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet @@ -0,0 +1,50 @@ +From e691b36dc2dbe1ef38095372c13bd7aa4c9ff256 Mon Sep 17 00:00:00 2001 +From: "Song, Yoong Siang" +Date: Sun, 11 Apr 2021 23:19:13 +0800 +Subject: [PATCH 16/18] net: stmmac: introduce AF_XDP ZC TX HW timestamps + +This is a temporary solution as it uses trace_printk as a means to +log tx timestamps. Future implementation should use xdp_frame's +data_meta to let user applications retrieve it directly. + +Signed-off-by: Voon Weifeng +Signed-off-by: Tan, Tee Min +Signed-off-by: Song, Yoong Siang +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 056a91165df82..5397996dfb261 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2582,7 +2582,10 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + + tx_q->tx_count_frames++; + +- if (!priv->tx_coal_frames[queue]) ++ if (unlikely(priv->hwts_all)) { ++ stmmac_enable_tx_timestamp(priv, tx_desc); ++ set_ic = true; ++ } else if (!priv->tx_coal_frames[queue]) + set_ic = false; + else if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0) + set_ic = true; +@@ -2738,6 +2741,14 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, + xsk_tx_metadata_complete(&tx_q->tx_skbuff_dma[entry].xsk_meta, + &stmmac_xsk_tx_metadata_ops, + &tx_compl); ++ } else if (unlikely(priv->hwts_all) && ++ tx_q->tx_skbuff_dma[entry].buf_type == ++ STMMAC_TXBUF_T_XSK_TX) { ++ ktime_t tx_hwtstamp = { 0 }; ++ ++ stmmac_get_tx_hwtstamp(priv, p, &tx_hwtstamp); ++ trace_printk("XDP TX HW TS %llu\n", ++ tx_hwtstamp); + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf b/SPECS/kernel-rt/0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf deleted file mode 100644 index c85ba3007..000000000 --- a/SPECS/kernel-rt/0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf +++ /dev/null @@ -1,401 +0,0 @@ -From ad152a9e55f0029be873b8571880c7e558e02c28 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 05:43:33 +0000 -Subject: [PATCH 016/100] perf/x86/intel: Process arch-PEBS records or record - fragments - -A significant difference with adaptive PEBS is that arch-PEBS record -supports fragments which means an arch-PEBS record could be split into -several independent fragments which have its own arch-PEBS header in -each fragment. - -This patch defines architectural PEBS record layout structures and add -helpers to process arch-PEBS records or fragments. Only legacy PEBS -groups like basic, GPR, XMM and LBR groups are supported in this patch, -the new added YMM/ZMM/OPMASK vector registers capturing would be -supported in subsequent patches. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 15 ++- - arch/x86/events/intel/ds.c | 180 ++++++++++++++++++++++++++++++ - arch/x86/include/asm/msr-index.h | 6 + - arch/x86/include/asm/perf_event.h | 100 +++++++++++++++++ - 4 files changed, 300 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index dcc796d00f05..a1e91cc4bfdc 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3219,6 +3219,19 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) - status &= ~GLOBAL_STATUS_PERF_METRICS_OVF_BIT; - } - -+ /* -+ * Arch PEBS sets bit 54 in the global status register -+ */ -+ if (__test_and_clear_bit(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT, -+ (unsigned long *)&status)) { -+ handled++; -+ static_call(x86_pmu_drain_pebs)(regs, &data); -+ -+ if (cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS] && -+ is_pebs_counter_event_group(cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS])) -+ status &= ~GLOBAL_STATUS_PERF_METRICS_OVF_BIT; -+ } -+ - /* - * Intel PT - */ -@@ -3273,7 +3286,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) - * The PEBS buffer has to be drained before handling the A-PMI - */ - if (is_pebs_counter_event_group(event)) -- x86_pmu.drain_pebs(regs, &data); -+ static_call(x86_pmu_drain_pebs)(regs, &data); - - last_period = event->hw.last_period; - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index f1d79b9e5f50..e5f88c48bbd6 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2270,6 +2270,114 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - format_group); - } - -+static inline bool arch_pebs_record_continued(struct arch_pebs_header *header) -+{ -+ /* Continue bit or null PEBS record indicates fragment follows. */ -+ return header->cont || !(header->format & GENMASK_ULL(63, 16)); -+} -+ -+static void setup_arch_pebs_sample_data(struct perf_event *event, -+ struct pt_regs *iregs, void *__pebs, -+ struct perf_sample_data *data, -+ struct pt_regs *regs) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ u64 sample_type = event->attr.sample_type; -+ struct arch_pebs_header *header = NULL; -+ struct arch_pebs_aux *meminfo = NULL; -+ struct arch_pebs_gprs *gprs = NULL; -+ struct x86_perf_regs *perf_regs; -+ void *next_record; -+ void *at = __pebs; -+ -+ if (at == NULL) -+ return; -+ -+ perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ perf_regs->xmm_regs = NULL; -+ -+ __setup_perf_sample_data(event, iregs, data); -+ -+ *regs = *iregs; -+ -+again: -+ header = at; -+ next_record = at + sizeof(struct arch_pebs_header); -+ if (header->basic) { -+ struct arch_pebs_basic *basic = next_record; -+ u16 retire = 0; -+ -+ next_record = basic + 1; -+ -+ if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) -+ retire = basic->valid ? basic->retire : 0; -+ __setup_pebs_basic_group(event, regs, data, sample_type, -+ basic->ip, basic->tsc, retire); -+ } -+ -+ /* -+ * The record for MEMINFO is in front of GP -+ * But PERF_SAMPLE_TRANSACTION needs gprs->ax. -+ * Save the pointer here but process later. -+ */ -+ if (header->aux) { -+ meminfo = next_record; -+ next_record = meminfo + 1; -+ } -+ -+ if (header->gpr) { -+ gprs = next_record; -+ next_record = gprs + 1; -+ -+ __setup_pebs_gpr_group(event, regs, (struct pebs_gprs *)gprs, -+ sample_type); -+ } -+ -+ if (header->aux) { -+ u64 ax = gprs ? gprs->ax : 0; -+ -+ __setup_pebs_meminfo_group(event, data, sample_type, -+ meminfo->cache_latency, -+ meminfo->instr_latency, -+ meminfo->address, meminfo->aux, -+ meminfo->tsx_tuning, ax); -+ } -+ -+ if (header->xmm) { -+ struct arch_pebs_xmm *xmm; -+ -+ next_record += sizeof(struct arch_pebs_xer_header); -+ -+ xmm = next_record; -+ perf_regs->xmm_regs = xmm->xmm; -+ next_record = xmm + 1; -+ } -+ -+ if (header->lbr) { -+ struct arch_pebs_lbr_header *lbr_header = next_record; -+ struct lbr_entry *lbr; -+ int num_lbr; -+ -+ next_record = lbr_header + 1; -+ lbr = next_record; -+ -+ num_lbr = header->lbr == ARCH_PEBS_LBR_NUM_VAR ? lbr_header->depth : -+ header->lbr * ARCH_PEBS_BASE_LBR_ENTRIES; -+ next_record += num_lbr * sizeof(struct lbr_entry); -+ -+ if (has_branch_stack(event)) { -+ intel_pmu_store_pebs_lbrs(lbr); -+ intel_pmu_lbr_save_brstack(data, cpuc, event); -+ } -+ } -+ -+ /* Parse followed fragments if there are. */ -+ if (arch_pebs_record_continued(header)) { -+ at = at + header->size; -+ goto again; -+ } -+} -+ - static inline void * - get_next_pebs_record_by_bit(void *base, void *top, int bit) - { -@@ -2750,6 +2858,77 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - setup_pebs_adaptive_sample_data); - } - -+static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs, -+ struct perf_sample_data *data) -+{ -+ short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {}; -+ void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS]; -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ union arch_pebs_index index; -+ struct x86_perf_regs perf_regs; -+ struct pt_regs *regs = &perf_regs.regs; -+ void *base, *at, *top; -+ u64 mask; -+ -+ rdmsrq(MSR_IA32_PEBS_INDEX, index.full); -+ -+ if (unlikely(!index.split.wr)) { -+ intel_pmu_pebs_event_update_no_drain(cpuc, X86_PMC_IDX_MAX); -+ return; -+ } -+ -+ base = cpuc->ds_pebs_vaddr; -+ top = (void *)((u64)cpuc->ds_pebs_vaddr + -+ (index.split.wr << ARCH_PEBS_INDEX_WR_SHIFT)); -+ -+ mask = hybrid(cpuc->pmu, arch_pebs_cap).counters & cpuc->pebs_enabled; -+ -+ if (!iregs) -+ iregs = &dummy_iregs; -+ -+ /* Process all but the last event for each counter. */ -+ for (at = base; at < top;) { -+ struct arch_pebs_header *header; -+ struct arch_pebs_basic *basic; -+ u64 pebs_status; -+ -+ header = at; -+ -+ if (WARN_ON_ONCE(!header->size)) -+ break; -+ -+ /* 1st fragment or single record must have basic group */ -+ if (!header->basic) { -+ at += header->size; -+ continue; -+ } -+ -+ basic = at + sizeof(struct arch_pebs_header); -+ pebs_status = mask & basic->applicable_counters; -+ __intel_pmu_handle_pebs_record(iregs, regs, data, at, -+ pebs_status, counts, last, -+ setup_arch_pebs_sample_data); -+ -+ /* Skip non-last fragments */ -+ while (arch_pebs_record_continued(header)) { -+ if (!header->size) -+ break; -+ at += header->size; -+ header = at; -+ } -+ -+ /* Skip last fragment or the single record */ -+ at += header->size; -+ } -+ -+ __intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, -+ last, setup_arch_pebs_sample_data); -+ -+ index.split.wr = 0; -+ index.split.full = 0; -+ wrmsrq(MSR_IA32_PEBS_INDEX, index.full); -+} -+ - static void __init intel_arch_pebs_init(void) - { - /* -@@ -2759,6 +2938,7 @@ static void __init intel_arch_pebs_init(void) - */ - x86_pmu.arch_pebs = 1; - x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; -+ x86_pmu.drain_pebs = intel_pmu_drain_arch_pebs; - x86_pmu.pebs_capable = ~0ULL; - - x86_pmu.pebs_enable = __intel_pmu_pebs_enable; -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index f627196eb796..7c306dd0713c 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -324,6 +324,12 @@ - PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \ - PERF_CAP_PEBS_TIMING_INFO) - -+/* Arch PEBS */ -+#define MSR_IA32_PEBS_BASE 0x000003f4 -+#define MSR_IA32_PEBS_INDEX 0x000003f5 -+#define ARCH_PEBS_OFFSET_MASK 0x7fffff -+#define ARCH_PEBS_INDEX_WR_SHIFT 4 -+ - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) - #define RTIT_CTL_CYCLEACC BIT(1) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index a81084f58de4..1f62efe3fce2 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -437,6 +437,8 @@ static inline bool is_topdown_idx(int idx) - #define GLOBAL_STATUS_LBRS_FROZEN BIT_ULL(GLOBAL_STATUS_LBRS_FROZEN_BIT) - #define GLOBAL_STATUS_TRACE_TOPAPMI_BIT 55 - #define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(GLOBAL_STATUS_TRACE_TOPAPMI_BIT) -+#define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT 54 -+#define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD BIT_ULL(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT) - #define GLOBAL_STATUS_PERF_METRICS_OVF_BIT 48 - - #define GLOBAL_CTRL_EN_PERF_METRICS BIT_ULL(48) -@@ -507,6 +509,104 @@ struct pebs_cntr_header { - - #define INTEL_CNTR_METRICS 0x3 - -+/* -+ * Arch PEBS -+ */ -+union arch_pebs_index { -+ struct { -+ u64 rsvd:4, -+ wr:23, -+ rsvd2:4, -+ full:1, -+ en:1, -+ rsvd3:3, -+ thresh:23, -+ rsvd4:5; -+ } split; -+ u64 full; -+}; -+ -+struct arch_pebs_header { -+ union { -+ u64 format; -+ struct { -+ u64 size:16, /* Record size */ -+ rsvd:14, -+ mode:1, /* 64BIT_MODE */ -+ cont:1, -+ rsvd2:3, -+ cntr:5, -+ lbr:2, -+ rsvd3:7, -+ xmm:1, -+ ymmh:1, -+ rsvd4:2, -+ opmask:1, -+ zmmh:1, -+ h16zmm:1, -+ rsvd5:5, -+ gpr:1, -+ aux:1, -+ basic:1; -+ }; -+ }; -+ u64 rsvd6; -+}; -+ -+struct arch_pebs_basic { -+ u64 ip; -+ u64 applicable_counters; -+ u64 tsc; -+ u64 retire :16, /* Retire Latency */ -+ valid :1, -+ rsvd :47; -+ u64 rsvd2; -+ u64 rsvd3; -+}; -+ -+struct arch_pebs_aux { -+ u64 address; -+ u64 rsvd; -+ u64 rsvd2; -+ u64 rsvd3; -+ u64 rsvd4; -+ u64 aux; -+ u64 instr_latency :16, -+ pad2 :16, -+ cache_latency :16, -+ pad3 :16; -+ u64 tsx_tuning; -+}; -+ -+struct arch_pebs_gprs { -+ u64 flags, ip, ax, cx, dx, bx, sp, bp, si, di; -+ u64 r8, r9, r10, r11, r12, r13, r14, r15, ssp; -+ u64 rsvd; -+}; -+ -+struct arch_pebs_xer_header { -+ u64 xstate; -+ u64 rsvd; -+}; -+ -+struct arch_pebs_xmm { -+ u64 xmm[16*2]; /* two entries for each register */ -+}; -+ -+#define ARCH_PEBS_LBR_NAN 0x0 -+#define ARCH_PEBS_LBR_NUM_8 0x1 -+#define ARCH_PEBS_LBR_NUM_16 0x2 -+#define ARCH_PEBS_LBR_NUM_VAR 0x3 -+#define ARCH_PEBS_BASE_LBR_ENTRIES 8 -+struct arch_pebs_lbr_header { -+ u64 rsvd; -+ u64 ctl; -+ u64 depth; -+ u64 ler_from; -+ u64 ler_to; -+ u64 ler_info; -+}; -+ - /* - * AMD Extended Performance Monitoring and Debug cpuid feature detection - */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0016-tools-power-turbostat-Enhance-perf-probe.turbo b/SPECS/kernel-rt/0016-tools-power-turbostat-Enhance-perf-probe.turbo new file mode 100644 index 000000000..1a99f351a --- /dev/null +++ b/SPECS/kernel-rt/0016-tools-power-turbostat-Enhance-perf-probe.turbo @@ -0,0 +1,115 @@ +From bdd2267861f4539de190f281ce2202f5da1865ed Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 15:15:32 -0500 +Subject: [PATCH 16/21] tools/power turbostat: Enhance perf probe + +check_perf_access() will now check both IPC and LLC perf counters +if they are enabled. If any fail, it now disables perf +and all perf counters. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 54 ++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 4411ef44294f4..ab28ac6e74b63 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2438,6 +2438,13 @@ static void bic_disable_msr_access(void) + free_sys_msr_counters(); + } + ++static void bic_disable_perf_access(void) ++{ ++ CLR_BIC(BIC_IPC, &bic_enabled); ++ CLR_BIC(BIC_LLC_RPS, &bic_enabled); ++ CLR_BIC(BIC_LLC_HIT, &bic_enabled); ++} ++ + static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) + { + assert(!no_perf); +@@ -8327,26 +8334,23 @@ void print_dev_latency(void) + close(fd); + } + +-static int has_instr_count_access(void) ++static int has_perf_instr_count_access(void) + { + int fd; +- int has_access; + + if (no_perf) + return 0; + + fd = open_perf_counter(base_cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, -1, 0); +- has_access = fd != -1; +- + if (fd != -1) + close(fd); + +- if (!has_access) ++ if (fd == -1) + warnx("Failed to access %s. Some of the counters may not be available\n" +- "\tRun as root to enable them or use %s to disable the access explicitly", +- "instructions retired perf counter", "--no-perf"); ++ "\tRun as root to enable them or use %s to disable the access explicitly", "perf instructions retired counter", ++ "'--hide IPC' or '--no-perf'"); + +- return has_access; ++ return (fd != -1); + } + + int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai, +@@ -9080,6 +9084,28 @@ void probe_pm_features(void) + decode_misc_feature_control(); + } + ++/* perf_llc_probe ++ * ++ * return 1 on success, else 0 ++ */ ++int has_perf_llc_access(void) ++{ ++ int fd; ++ ++ if (no_perf) ++ return 0; ++ ++ fd = open_perf_counter(base_cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, -1, PERF_FORMAT_GROUP); ++ if (fd != -1) ++ close(fd); ++ ++ if (fd == -1) ++ warnx("Failed to access %s. Some of the counters may not be available\n" ++ "\tRun as root to enable them or use %s to disable the access explicitly", "perf LLC counters", "'--hide LLC' or '--no-perf'"); ++ ++ return (fd != -1); ++} ++ + void perf_llc_init(void) + { + int cpu; +@@ -9535,8 +9561,16 @@ void check_msr_access(void) + + void check_perf_access(void) + { +- if (no_perf || !BIC_IS_ENABLED(BIC_IPC) || !has_instr_count_access()) +- CLR_BIC(BIC_IPC, &bic_enabled); ++ if (BIC_IS_ENABLED(BIC_IPC)) ++ if (!has_perf_instr_count_access()) ++ no_perf = 1; ++ ++ if (BIC_IS_ENABLED(BIC_LLC_RPS) || BIC_IS_ENABLED(BIC_LLC_HIT)) ++ if (!has_perf_llc_access()) ++ no_perf = 1; ++ ++ if (no_perf) ++ bic_disable_perf_access(); + } + + bool perf_has_hybrid_devices(void) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu b/SPECS/kernel-rt/0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu deleted file mode 100644 index aa8871213..000000000 --- a/SPECS/kernel-rt/0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu +++ /dev/null @@ -1,295 +0,0 @@ -From 58cb4ba66d58380ce189e67303d6b908e11e1567 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Thu, 15 Jan 2026 15:22:42 +0800 -Subject: [PATCH 17/21] Copy ACPI header files from VTG IPU7 & IPU6 repo - -These are the header files copied over from ipu6-drivers: -- include/media/ipu-acpi-pdata.h -- include/media/ipu-acpi.h -- include/media/ipu-get-acpi.h -- include/media/serdes-pdata.h - -Linux kernel macro need to be removed in ipu-acpi.h -to meet innersource kernel standards. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - include/media/ipu-acpi-pdata.h | 38 +++--------------- - include/media/ipu-acpi.h | 73 +++++++++++++++++++++++++++++----- - include/media/ipu-get-acpi.h | 30 ++++++++++++++ - include/media/serdes-pdata.h | 37 +++++++++++++++++ - 4 files changed, 137 insertions(+), 41 deletions(-) - create mode 100644 include/media/ipu-get-acpi.h - create mode 100644 include/media/serdes-pdata.h - -diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h -index e2074419dd88..4c754e98ded6 100644 ---- a/include/media/ipu-acpi-pdata.h -+++ b/include/media/ipu-acpi-pdata.h -@@ -3,12 +3,12 @@ - #include - #include - #include --#if IS_ENABLED(CONFIG_VIDEO_ISX031) --#include --#endif -+#include - - #define CL_EMPTY 0 - #define CL_DISCRETE 1 -+#define CL_LT 5 -+#define SERDES_MAX_PORT 4 - #define SERDES_MAX_GPIO_POWERUP_SEQ 4 - #define LOOP_SIZE 10 - -@@ -26,7 +26,7 @@ int get_sensor_pdata(struct device *dev, - int sensor_physical_addr, - int link_freq); - --struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void); -+struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void); - - struct sensor_platform_data { - unsigned int port; -@@ -34,37 +34,11 @@ struct sensor_platform_data { - uint32_t i2c_slave_address; - int irq_pin; - unsigned int irq_pin_flags; -- char irq_pin_name[IPU7_SPDATA_IRQ_PIN_NAME_LEN]; -+ char irq_pin_name[IPU_SPDATA_IRQ_PIN_NAME_LEN]; - int reset_pin; - int detect_pin; - char suffix; -- int gpios[IPU7_SPDATA_GPIO_NUM]; --}; -- --struct serdes_platform_data { -- unsigned int subdev_num; -- struct serdes_subdev_info *subdev_info; -- unsigned int reset_gpio; -- unsigned int FPD_gpio; -- char suffix; -- unsigned int link_freq_mbps; -- enum v4l2_mbus_type bus_type; -- unsigned int deser_nlanes; -- unsigned int ser_nlanes; -- unsigned int des_port; -- char ser_name[I2C_NAME_SIZE]; -- struct i2c_board_info *deser_board_info; --}; -- --struct serdes_subdev_info { -- struct i2c_board_info board_info; -- int i2c_adapter_id; -- unsigned short rx_port; -- unsigned short phy_i2c_addr; -- unsigned short ser_alias; -- char suffix[5]; /* suffix for subdevs */ -- unsigned short ser_phys_addr; -- unsigned int sensor_dt; -+ int gpios[IPU_SPDATA_GPIO_NUM]; - }; - - struct serdes_module_pdata { -diff --git a/include/media/ipu-acpi.h b/include/media/ipu-acpi.h -index bc63240d7c24..f06b6861eb24 100644 ---- a/include/media/ipu-acpi.h -+++ b/include/media/ipu-acpi.h -@@ -16,16 +16,56 @@ - #ifndef MEDIA_INTEL_IPU_ACPI_H - #define MEDIA_INTEL_IPU_ACPI_H - --#include "ipu7-isys.h" -+#include -+ -+#include -+#include -+ -+#include -+ -+struct ipu_isys_csi2_config { -+ unsigned int nlanes; -+ unsigned int port; -+ enum v4l2_mbus_type bus_type; -+}; -+ -+struct ipu_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu_isys_subdev_info { -+ struct ipu_isys_csi2_config *csi2; -+ struct ipu_isys_subdev_i2c_info i2c; -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ char *acpi_hid; -+#endif -+}; -+ -+struct ipu_isys_clk_mapping { -+ struct clk_lookup clkdev_data; -+ char *platform_clock_name; -+}; -+ -+struct ipu_isys_subdev_pdata { -+ struct ipu_isys_subdev_info **subdevs; -+ struct ipu_isys_clk_mapping *clk_map; -+}; - - #define MAX_ACPI_SENSOR_NUM 4 - #define MAX_ACPI_I2C_NUM 12 - #define MAX_ACPI_GPIO_NUM 12 - - #define GPIO_RESET 0x0 -+#define GPIO_POWER_EN 0xb -+#define GPIO_READY_STAT 0x13 -+#define GPIO_HDMI_DETECT 0x14 -+ -+#define IPU_SPDATA_GPIO_NUM 4 -+#define IPU_SPDATA_IRQ_PIN_NAME_LEN 16 - --#define IPU7_SPDATA_GPIO_NUM 4 --#define IPU7_SPDATA_IRQ_PIN_NAME_LEN 16 -+void set_built_in_pdata(struct ipu_isys_subdev_pdata *pdata); - - enum connection_type { - TYPE_DIRECT, -@@ -122,6 +162,11 @@ struct ipu_gpio_info { - bool valid; - }; - -+struct ipu_irq_info { -+ int irq_pin; -+ char irq_pin_name[IPU_SPDATA_IRQ_PIN_NAME_LEN]; -+}; -+ - /* Each I2C client linked to 1 set of CTL Logic */ - struct control_logic_data { - struct device *dev; -@@ -133,9 +178,7 @@ struct control_logic_data { - bool completed; - }; - --int ipu_get_acpi_devices(void **spdata); -- --struct ipu7_isys_subdev_pdata *get_built_in_pdata(void); -+struct ipu_isys_subdev_pdata *get_built_in_pdata(void); - - int ipu_acpi_get_cam_data(struct device *dev, - struct sensor_bios_data *sensor); -@@ -146,17 +189,29 @@ int ipu_acpi_get_dep_data(struct device *dev, - int ipu_acpi_get_control_logic_data(struct device *dev, - struct control_logic_data **ctl_data); - -+struct intel_ipu6_regulator { -+ char *src_dev_name; -+ char *src_rail; -+ char *dest_rail; -+}; -+ - struct ipu_i2c_helper { - int (*fn)(struct device *dev, void *priv, -- struct ipu7_isys_csi2_config *csi2, -+ struct ipu_isys_csi2_config *csi2, - bool reprobe); - void *driver_data; - }; - -+struct ipu_i2c_new_dev { -+ struct list_head list; -+ struct i2c_board_info info; -+ unsigned short int bus; -+}; -+ - struct ipu_camera_module_data { - struct list_head list; -- struct ipu7_isys_subdev_info sd; -- struct ipu7_isys_csi2_config csi2; -+ struct ipu_isys_subdev_info sd; -+ struct ipu_isys_csi2_config csi2; - unsigned int ext_clk; - void *pdata; /* Ptr to generated platform data*/ - void *priv; /* Private for specific subdevice */ -diff --git a/include/media/ipu-get-acpi.h b/include/media/ipu-get-acpi.h -new file mode 100644 -index 000000000000..da2ed5117c53 ---- /dev/null -+++ b/include/media/ipu-get-acpi.h -@@ -0,0 +1,30 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#ifndef MEDIA_INTEL_IPU_GET_ACPI_H -+#define MEDIA_INTEL_IPU_GET_ACPI_H -+ -+int ipu_get_acpi_devices(void *driver_data, -+ struct device *dev, -+ void **spdata, -+ void **built_in_pdata, -+ int (*fn) -+ (struct device *, void *, -+ void *csi2, -+ bool reprobe)); -+ -+int ipu_get_acpi_devices_new(void **spdata); -+ -+#endif /* MEDIA_INTEL_IPU_GET_ACPI_H */ -diff --git a/include/media/serdes-pdata.h b/include/media/serdes-pdata.h -new file mode 100644 -index 000000000000..829f730140b1 ---- /dev/null -+++ b/include/media/serdes-pdata.h -@@ -0,0 +1,37 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2023-2025 Intel Corporation */ -+ -+#ifndef MEDIA_SERDES_PDATA_H -+#define MEDIA_SERDES_PDATA_H -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+struct serdes_subdev_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ unsigned short rx_port; -+ unsigned short phy_i2c_addr; -+ unsigned short ser_alias; -+ char suffix[5]; /* suffix for subdevs */ -+ unsigned short ser_phys_addr; -+ unsigned int sensor_dt; -+}; -+ -+struct serdes_platform_data { -+ unsigned int subdev_num; -+ struct serdes_subdev_info *subdev_info; -+ unsigned int reset_gpio; -+ unsigned int FPD_gpio; -+ char suffix; -+ unsigned int link_freq_mbps; -+ enum v4l2_mbus_type bus_type; -+ unsigned int deser_nlanes; -+ unsigned int ser_nlanes; -+ unsigned int des_port; -+ char ser_name[I2C_NAME_SIZE]; -+ struct i2c_board_info *deser_board_info; -+}; -+ -+#endif --- -2.43.0 - diff --git a/SPECS/kernel-rt/0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet b/SPECS/kernel-rt/0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet deleted file mode 100644 index 42cc2018f..000000000 --- a/SPECS/kernel-rt/0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet +++ /dev/null @@ -1,65 +0,0 @@ -From e6c2937b1b1e7c00a0057b02c6468ac7c901e5d7 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:47 -0700 -Subject: [PATCH 17/24] KVM: VMX: Set up interception for CET MSRs - -Enable/disable CET MSRs interception per associated feature configuration. - -Shadow Stack feature requires all CET MSRs passed through to guest to make -it supported in user and supervisor mode while IBT feature only depends on -MSR_IA32_{U,S}_CETS_CET to enable user and supervisor IBT. - -Note, this MSR design introduced an architectural limitation of SHSTK and -IBT control for guest, i.e., when SHSTK is exposed, IBT is also available -to guest from architectural perspective since IBT relies on subset of SHSTK -relevant MSRs. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/vmx.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 9098d4ee4f79..ca10c1b142dc 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4137,6 +4137,8 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) - - static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { -+ bool set; -+ - if (!cpu_has_vmx_msr_bitmap()) - return; - -@@ -4184,6 +4186,23 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - - vmx_recalc_pmu_msr_intercepts(vcpu); - -+ if (kvm_cpu_cap_has(X86_FEATURE_SHSTK)) { -+ set = !guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK); -+ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL0_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL1_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL2_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL3_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_INT_SSP_TAB, MSR_TYPE_RW, set); -+ } -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_SHSTK) || kvm_cpu_cap_has(X86_FEATURE_IBT)) { -+ set = !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK); -+ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_U_CET, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, set); -+ } - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be - * filtered by userspace. --- -2.43.0 - diff --git a/SPECS/kernel-rt/0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi b/SPECS/kernel-rt/0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi deleted file mode 100644 index 92d1408ca..000000000 --- a/SPECS/kernel-rt/0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi +++ /dev/null @@ -1,169 +0,0 @@ -From 273e1c51bb3131051f1e896847368d164ee3cabf Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Aug 2022 09:58:24 -0700 -Subject: [PATCH 17/44] KVM: nVMX: Add support for the secondary VM exit - controls - -Enable the secondary VM exit controls to prepare for nested FRED. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Allow writing MSR_IA32_VMX_EXIT_CTLS2 (Sean). -* Add TB from Xuelian Guo. - -Change in v3: -* Read secondary VM exit controls from vmcs_conf insteasd of the hardware - MSR MSR_IA32_VMX_EXIT_CTLS2 to avoid advertising features to L1 that KVM - itself doesn't support, e.g. because the expected entry+exit pairs aren't - supported. (Sean Christopherson) ---- - Documentation/virt/kvm/x86/nested-vmx.rst | 1 + - arch/x86/kvm/vmx/capabilities.h | 1 + - arch/x86/kvm/vmx/nested.c | 26 ++++++++++++++++++++++- - arch/x86/kvm/vmx/vmcs12.c | 1 + - arch/x86/kvm/vmx/vmcs12.h | 2 ++ - arch/x86/kvm/x86.h | 2 +- - 6 files changed, 31 insertions(+), 2 deletions(-) - -diff --git a/Documentation/virt/kvm/x86/nested-vmx.rst b/Documentation/virt/kvm/x86/nested-vmx.rst -index ac2095d41f02..e64ef231f310 100644 ---- a/Documentation/virt/kvm/x86/nested-vmx.rst -+++ b/Documentation/virt/kvm/x86/nested-vmx.rst -@@ -217,6 +217,7 @@ struct shadow_vmcs is ever changed. - u16 host_fs_selector; - u16 host_gs_selector; - u16 host_tr_selector; -+ u64 secondary_vm_exit_controls; - }; - - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 1100291d42ea..ebf4cbd2df70 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -34,6 +34,7 @@ struct nested_vmx_msrs { - u32 pinbased_ctls_high; - u32 exit_ctls_low; - u32 exit_ctls_high; -+ u64 secondary_exit_ctls; - u32 entry_ctls_low; - u32 entry_ctls_high; - u32 misc_low; -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index a6a3e0cbbe89..611ccff1dc90 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -1567,6 +1567,11 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) - return -EINVAL; - vmx->nested.msrs.vmfunc_controls = data; - return 0; -+ case MSR_IA32_VMX_EXIT_CTLS2: -+ if (data & ~vmcs_config.nested.secondary_exit_ctls) -+ return -EINVAL; -+ vmx->nested.msrs.secondary_exit_ctls = data; -+ return 0; - default: - /* - * The rest of the VMX capability MSRs do not support restore. -@@ -1606,6 +1611,9 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) - if (msr_index == MSR_IA32_VMX_EXIT_CTLS) - *pdata |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR; - break; -+ case MSR_IA32_VMX_EXIT_CTLS2: -+ *pdata = msrs->secondary_exit_ctls; -+ break; - case MSR_IA32_VMX_TRUE_ENTRY_CTLS: - case MSR_IA32_VMX_ENTRY_CTLS: - *pdata = vmx_control_msr( -@@ -2556,6 +2564,11 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 - exec_control &= ~VM_EXIT_LOAD_IA32_EFER; - vm_exit_controls_set(vmx, exec_control); - -+ if (exec_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { -+ exec_control = __secondary_vm_exit_controls_get(vmcs01); -+ secondary_vm_exit_controls_set(vmx, exec_control); -+ } -+ - /* - * Interrupt/Exception Fields - */ -@@ -7198,7 +7211,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - VM_EXIT_HOST_ADDR_SPACE_SIZE | - #endif - VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT | -- VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE; -+ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE | -+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; - msrs->exit_ctls_high |= - VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | - VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | -@@ -7207,6 +7221,16 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - - /* We support free control of debug control saving. */ - msrs->exit_ctls_low &= ~VM_EXIT_SAVE_DEBUG_CONTROLS; -+ -+ if (msrs->exit_ctls_high & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { -+ msrs->secondary_exit_ctls = vmcs_conf->vmexit_2nd_ctrl; -+ /* -+ * As the secondary VM exit control is always loaded, do not -+ * advertise any feature in it to nVMX until its nVMX support -+ * is ready. -+ */ -+ msrs->secondary_exit_ctls &= 0; -+ } - } - - static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, -diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c -index 4233b5ca9461..3b01175f392a 100644 ---- a/arch/x86/kvm/vmx/vmcs12.c -+++ b/arch/x86/kvm/vmx/vmcs12.c -@@ -66,6 +66,7 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD64(HOST_IA32_PAT, host_ia32_pat), - FIELD64(HOST_IA32_EFER, host_ia32_efer), - FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), -+ FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), - FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), - FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), - FIELD(EXCEPTION_BITMAP, exception_bitmap), -diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h -index 4ad6b16525b9..7866fdce7a23 100644 ---- a/arch/x86/kvm/vmx/vmcs12.h -+++ b/arch/x86/kvm/vmx/vmcs12.h -@@ -191,6 +191,7 @@ struct __packed vmcs12 { - u16 host_gs_selector; - u16 host_tr_selector; - u16 guest_pml_index; -+ u64 secondary_vm_exit_controls; - }; - - /* -@@ -372,6 +373,7 @@ static inline void vmx_check_vmcs12_offsets(void) - CHECK_OFFSET(host_gs_selector, 992); - CHECK_OFFSET(host_tr_selector, 994); - CHECK_OFFSET(guest_pml_index, 996); -+ CHECK_OFFSET(secondary_vm_exit_controls, 998); - } - - extern const unsigned short vmcs12_field_offsets[]; -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 5f2279e11f87..c7f9e719c9b0 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -95,7 +95,7 @@ do { \ - * associated feature that KVM supports for nested virtualization. - */ - #define KVM_FIRST_EMULATED_VMX_MSR MSR_IA32_VMX_BASIC --#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_VMFUNC -+#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_EXIT_CTLS2 - - #define KVM_DEFAULT_PLE_GAP 128 - #define KVM_VMX_DEFAULT_PLE_WINDOW 4096 --- -2.43.0 - diff --git a/SPECS/kernel-rt/0017-KVM-x86-Advertise-support-for-FRED.nmi b/SPECS/kernel-rt/0017-KVM-x86-Advertise-support-for-FRED.nmi new file mode 100644 index 000000000..227c59fc1 --- /dev/null +++ b/SPECS/kernel-rt/0017-KVM-x86-Advertise-support-for-FRED.nmi @@ -0,0 +1,37 @@ +From a4a595380145401de86d399e5e87227fe0a63a74 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Wed, 11 May 2022 17:53:27 -0700 +Subject: [PATCH 17/44] KVM: x86: Advertise support for FRED + +Advertise support for FRED to userspace after changes required to enable +FRED in a KVM guest are in place. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Don't advertise FRED/LKGS together, LKGS can be advertised as an + independent feature (Sean). +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/cpuid.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c +index 52524e0ca97f7..f4ff5ccbcf1e4 100644 +--- a/arch/x86/kvm/cpuid.c ++++ b/arch/x86/kvm/cpuid.c +@@ -1014,6 +1014,7 @@ void kvm_set_cpu_caps(void) + F(FSRS), + F(FSRC), + F(WRMSRNS), ++ X86_64_F(FRED), + X86_64_F(LKGS), + F(AMX_FP16), + F(AVX_IFMA), +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet b/SPECS/kernel-rt/0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet deleted file mode 100644 index 37829ac3b..000000000 --- a/SPECS/kernel-rt/0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet +++ /dev/null @@ -1,85 +0,0 @@ -From 8483034a48f6b9549b5db703eb01e59b7d08f327 Mon Sep 17 00:00:00 2001 -From: Vinicius Costa Gomes -Date: Wed, 19 Mar 2025 11:17:06 +0500 -Subject: [PATCH 17/19] af_packet: Fix wrong timestamps in tcpdump - -Since commit '97dc7cd ("ptp: Support late timestamp -determination")', timestamps may need to be retrieved with input from -the driver (.ndo_get_tstamp()). Support was missing from mmap'ed -AF_PACKET sockets. - -The problem was noticed using the igc driver, that since commit -'069b142 ("igc: Add support for PTP .getcyclesx64()")' has migrated -to use .ndo_get_tstamp() that timestamps were wrong on the receiving -side, when using tcpdump. - -Signed-off-by: Vinicius Costa Gomes ---- - net/packet/af_packet.c | 29 ++++++++++++++++++++--------- - 1 file changed, 20 insertions(+), 9 deletions(-) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index a7017d7f0927..64d5b197f732 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -451,15 +451,20 @@ static int __packet_get_status(const struct packet_sock *po, void *frame) - } - } - --static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, -- unsigned int flags) -+static __u32 tpacket_get_timestamp(struct net_device *dev, struct sk_buff *skb, -+ struct timespec64 *ts, unsigned int flags, -+ unsigned int sk_tsflags) - { - struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); - -- if (shhwtstamps && -- (flags & SOF_TIMESTAMPING_RAW_HARDWARE) && -- ktime_to_timespec64_cond(shhwtstamps->hwtstamp, ts)) -- return TP_STATUS_TS_RAW_HARDWARE; -+ bool cycles = sk_tsflags & SOF_TIMESTAMPING_BIND_PHC; -+ -+ if (shhwtstamps && shhwtstamps->hwtstamp && -+ (flags & SOF_TIMESTAMPING_RAW_HARDWARE)) { -+ ktime_t tstamp = netdev_get_tstamp(dev, shhwtstamps, cycles); -+ -+ return ktime_to_timespec64_cond(tstamp, ts) ? TP_STATUS_TS_RAW_HARDWARE : 0; -+ } - - if ((flags & SOF_TIMESTAMPING_SOFTWARE) && - ktime_to_timespec64_cond(skb_tstamp(skb), ts)) -@@ -471,11 +476,16 @@ static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, - static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame, - struct sk_buff *skb) - { -+ struct net_device *dev = skb->dev; -+ unsigned int sk_tsflags; - union tpacket_uhdr h; - struct timespec64 ts; - __u32 ts_status; - -- if (!(ts_status = tpacket_get_timestamp(skb, &ts, READ_ONCE(po->tp_tstamp)))) -+ sk_tsflags = READ_ONCE(po->sk.sk_tsflags); -+ ts_status = tpacket_get_timestamp(dev, skb, &ts, READ_ONCE(po->tp_tstamp), sk_tsflags); -+ -+ if (!(ts_status)) - return 0; - - h.raw = frame; -@@ -2446,9 +2456,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, - /* Always timestamp; prefer an existing software timestamp taken - * closer to the time of capture. - */ -- ts_status = tpacket_get_timestamp(skb, &ts, -+ ts_status = tpacket_get_timestamp(dev, skb, &ts, - READ_ONCE(po->tp_tstamp) | -- SOF_TIMESTAMPING_SOFTWARE); -+ SOF_TIMESTAMPING_SOFTWARE, -+ READ_ONCE(sk->sk_tsflags)); - if (!ts_status) - ktime_get_real_ts64(&ts); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl b/SPECS/kernel-rt/0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl new file mode 100644 index 000000000..46cbb4c5f --- /dev/null +++ b/SPECS/kernel-rt/0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl @@ -0,0 +1,81 @@ +From 741735b8bbc82f09f79cd78d0705862ec5ab0ac9 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Tue, 25 Nov 2025 17:23:12 +0100 +Subject: [PATCH 17/17] cpuidle: Warn instead of bailing out if target + residency check fails + +It turns out that the change in commit 76934e495cdc ("cpuidle: Add +sanity check for exit latency and target residency") goes too far +because there are systems in the field on which the check introduced +by that commit does not pass. + +For this reason, change __cpuidle_driver_init() return type back to void +and make it print a warning when the check mentioned above does not +pass. + +Fixes: 76934e495cdc ("cpuidle: Add sanity check for exit latency and target residency") +Reported-by: Val Packett +Closes: https://lore.kernel.org/linux-pm/20251121010756.6687-1-val@packett.cool/ +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Link: https://patch.msgid.link/2808566.mvXUDI8C0e@rafael.j.wysocki +--- + drivers/cpuidle/driver.c | 18 ++++++++---------- + 1 file changed, 8 insertions(+), 10 deletions(-) + +diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c +index 1c295a93d5829..370664c47e659 100644 +--- a/drivers/cpuidle/driver.c ++++ b/drivers/cpuidle/driver.c +@@ -8,6 +8,8 @@ + * This code is licenced under the GPL. + */ + ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ + #include + #include + #include +@@ -152,7 +154,7 @@ static void cpuidle_setup_broadcast_timer(void *arg) + * __cpuidle_driver_init - initialize the driver's internal data + * @drv: a valid pointer to a struct cpuidle_driver + */ +-static int __cpuidle_driver_init(struct cpuidle_driver *drv) ++static void __cpuidle_driver_init(struct cpuidle_driver *drv) + { + int i; + +@@ -195,15 +197,13 @@ static int __cpuidle_driver_init(struct cpuidle_driver *drv) + s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); + + /* +- * Ensure that the exit latency of a CPU idle state does not +- * exceed its target residency which is assumed in cpuidle in +- * multiple places. ++ * Warn if the exit latency of a CPU idle state exceeds its ++ * target residency which is assumed to never happen in cpuidle ++ * in multiple places. + */ + if (s->exit_latency_ns > s->target_residency_ns) +- return -EINVAL; ++ pr_warn("Idle state %d target residency too low\n", i); + } +- +- return 0; + } + + /** +@@ -233,9 +233,7 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) + if (cpuidle_disabled()) + return -ENODEV; + +- ret = __cpuidle_driver_init(drv); +- if (ret) +- return ret; ++ __cpuidle_driver_init(drv); + + ret = __cpuidle_set_driver(drv); + if (ret) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet b/SPECS/kernel-rt/0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet new file mode 100644 index 000000000..78034c8b2 --- /dev/null +++ b/SPECS/kernel-rt/0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet @@ -0,0 +1,35 @@ +From 207e0a018d608eb9b472c2781aa2c6316e2778e8 Mon Sep 17 00:00:00 2001 +From: KhaiWenTan +Date: Thu, 23 Jan 2025 11:14:34 +0800 +Subject: [PATCH 17/18] net: phy: Set eee_cfg.eee_enabled according to PHY + +During the phy_probe function, the default value for eee_cfg.eee_enabled +was configured based on the PHY hardware. However, forcing +eee_cfg.eee_enabled to be true would cause the interface to be unable to +ping and set speed. + +This patch removes the implementation that forces eee_cfg.eee_enabled +to be true for EEE. This will make the eee_cfg.eee_enabled for EEE +follow the PHY hardware's default behavior. + +Signed-off-by: Choong Yong Liang +Signed-off-by: KhaiWenTan +--- + drivers/net/phy/phy_device.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 7a67c900e79a5..72a140275c4eb 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2825,7 +2825,6 @@ void phy_support_eee(struct phy_device *phydev) + { + linkmode_copy(phydev->advertising_eee, phydev->supported_eee); + phydev->eee_cfg.tx_lpi_enabled = true; +- phydev->eee_cfg.eee_enabled = true; + } + EXPORT_SYMBOL(phy_support_eee); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf b/SPECS/kernel-rt/0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf deleted file mode 100644 index cac3a3502..000000000 --- a/SPECS/kernel-rt/0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf +++ /dev/null @@ -1,199 +0,0 @@ -From 1dd4e6038b365981501006c931a9b6fe6fa2e115 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 14:16:58 +0000 -Subject: [PATCH 017/100] perf/x86/intel: Allocate arch-PEBS buffer and - initialize PEBS_BASE MSR - -Arch-PEBS introduces a new MSR IA32_PEBS_BASE to store the arch-PEBS -buffer physical address. This patch allocates arch-PEBS buffer and then -initialize IA32_PEBS_BASE MSR with the buffer physical address. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 2 + - arch/x86/events/intel/ds.c | 69 ++++++++++++++++++++++++++------- - arch/x86/events/perf_event.h | 7 +++- - arch/x86/include/asm/intel_ds.h | 3 +- - 4 files changed, 66 insertions(+), 15 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index a1e91cc4bfdc..d4eaaeff0614 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5592,6 +5592,7 @@ static void intel_pmu_cpu_starting(int cpu) - return; - - init_debug_store_on_cpu(cpu); -+ init_arch_pebs_buf_on_cpu(cpu); - /* - * Deal with CPUs that don't clear their LBRs on power-up, and that may - * even boot with LBRs enabled. -@@ -5689,6 +5690,7 @@ static void free_excl_cntrs(struct cpu_hw_events *cpuc) - static void intel_pmu_cpu_dying(int cpu) - { - fini_debug_store_on_cpu(cpu); -+ fini_arch_pebs_buf_on_cpu(cpu); - } - - void intel_cpuc_finish(struct cpu_hw_events *cpuc) -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index e5f88c48bbd6..3f9ee8dbe6b7 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -625,13 +625,18 @@ static int alloc_pebs_buffer(int cpu) - int max, node = cpu_to_node(cpu); - void *buffer, *insn_buff, *cea; - -- if (!x86_pmu.ds_pebs) -+ if (!intel_pmu_has_pebs()) - return 0; - -- buffer = dsalloc_pages(bsiz, GFP_KERNEL, cpu); -+ buffer = dsalloc_pages(bsiz, preemptible() ? GFP_KERNEL : GFP_ATOMIC, cpu); - if (unlikely(!buffer)) - return -ENOMEM; - -+ if (x86_pmu.arch_pebs) { -+ hwev->pebs_vaddr = buffer; -+ return 0; -+ } -+ - /* - * HSW+ already provides us the eventing ip; no need to allocate this - * buffer then. -@@ -644,7 +649,7 @@ static int alloc_pebs_buffer(int cpu) - } - per_cpu(insn_buffer, cpu) = insn_buff; - } -- hwev->ds_pebs_vaddr = buffer; -+ hwev->pebs_vaddr = buffer; - /* Update the cpu entry area mapping */ - cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer; - ds->pebs_buffer_base = (unsigned long) cea; -@@ -660,17 +665,20 @@ static void release_pebs_buffer(int cpu) - struct cpu_hw_events *hwev = per_cpu_ptr(&cpu_hw_events, cpu); - void *cea; - -- if (!x86_pmu.ds_pebs) -+ if (!intel_pmu_has_pebs()) - return; - -- kfree(per_cpu(insn_buffer, cpu)); -- per_cpu(insn_buffer, cpu) = NULL; -+ if (x86_pmu.ds_pebs) { -+ kfree(per_cpu(insn_buffer, cpu)); -+ per_cpu(insn_buffer, cpu) = NULL; - -- /* Clear the fixmap */ -- cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer; -- ds_clear_cea(cea, x86_pmu.pebs_buffer_size); -- dsfree_pages(hwev->ds_pebs_vaddr, x86_pmu.pebs_buffer_size); -- hwev->ds_pebs_vaddr = NULL; -+ /* Clear the fixmap */ -+ cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer; -+ ds_clear_cea(cea, x86_pmu.pebs_buffer_size); -+ } -+ -+ dsfree_pages(hwev->pebs_vaddr, x86_pmu.pebs_buffer_size); -+ hwev->pebs_vaddr = NULL; - } - - static int alloc_bts_buffer(int cpu) -@@ -823,6 +831,41 @@ void reserve_ds_buffers(void) - } - } - -+void init_arch_pebs_buf_on_cpu(int cpu) -+{ -+ struct cpu_hw_events *cpuc = per_cpu_ptr(&cpu_hw_events, cpu); -+ u64 arch_pebs_base; -+ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ if (alloc_pebs_buffer(cpu) < 0 || !cpuc->pebs_vaddr) { -+ WARN(1, "Fail to allocate PEBS buffer on CPU %d\n", cpu); -+ x86_pmu.pebs_active = 0; -+ return; -+ } -+ -+ /* -+ * 4KB-aligned pointer of the output buffer -+ * (__alloc_pages_node() return page aligned address) -+ * Buffer Size = 4KB * 2^SIZE -+ * contiguous physical buffer (__alloc_pages_node() with order) -+ */ -+ arch_pebs_base = virt_to_phys(cpuc->pebs_vaddr) | PEBS_BUFFER_SHIFT; -+ wrmsr_on_cpu(cpu, MSR_IA32_PEBS_BASE, (u32)arch_pebs_base, -+ (u32)(arch_pebs_base >> 32)); -+ x86_pmu.pebs_active = 1; -+} -+ -+void fini_arch_pebs_buf_on_cpu(int cpu) -+{ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ release_pebs_buffer(cpu); -+ wrmsr_on_cpu(cpu, MSR_IA32_PEBS_BASE, 0, 0); -+} -+ - /* - * BTS - */ -@@ -2877,8 +2920,8 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs, - return; - } - -- base = cpuc->ds_pebs_vaddr; -- top = (void *)((u64)cpuc->ds_pebs_vaddr + -+ base = cpuc->pebs_vaddr; -+ top = (void *)((u64)cpuc->pebs_vaddr + - (index.split.wr << ARCH_PEBS_INDEX_WR_SHIFT)); - - mask = hybrid(cpuc->pmu, arch_pebs_cap).counters & cpuc->pebs_enabled; -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index a5145e8f1ddb..82e8c20611b9 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -283,8 +283,9 @@ struct cpu_hw_events { - * Intel DebugStore bits - */ - struct debug_store *ds; -- void *ds_pebs_vaddr; - void *ds_bts_vaddr; -+ /* DS based PEBS or arch-PEBS buffer address */ -+ void *pebs_vaddr; - u64 pebs_enabled; - int n_pebs; - int n_large_pebs; -@@ -1618,6 +1619,10 @@ extern void intel_cpuc_finish(struct cpu_hw_events *cpuc); - - int intel_pmu_init(void); - -+void init_arch_pebs_buf_on_cpu(int cpu); -+ -+void fini_arch_pebs_buf_on_cpu(int cpu); -+ - void init_debug_store_on_cpu(int cpu); - - void fini_debug_store_on_cpu(int cpu); -diff --git a/arch/x86/include/asm/intel_ds.h b/arch/x86/include/asm/intel_ds.h -index 5dbeac48a5b9..023c2883f9f3 100644 ---- a/arch/x86/include/asm/intel_ds.h -+++ b/arch/x86/include/asm/intel_ds.h -@@ -4,7 +4,8 @@ - #include - - #define BTS_BUFFER_SIZE (PAGE_SIZE << 4) --#define PEBS_BUFFER_SIZE (PAGE_SIZE << 4) -+#define PEBS_BUFFER_SHIFT 4 -+#define PEBS_BUFFER_SIZE (PAGE_SIZE << PEBS_BUFFER_SHIFT) - - /* The maximal number of PEBS events: */ - #define MAX_PEBS_EVENTS_FMT4 8 --- -2.43.0 - diff --git a/SPECS/kernel-rt/0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo b/SPECS/kernel-rt/0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo new file mode 100644 index 000000000..2230ffa56 --- /dev/null +++ b/SPECS/kernel-rt/0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo @@ -0,0 +1,91 @@ +From 22e27c5561fe77b109c96d7cf87004c2db14ace0 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 11:29:27 -0500 +Subject: [PATCH 17/21] tools/power turbostat: Validate APERF access for VMWARE + +VMWARE correctly enumerates lack of APERF and MPERF in CPUID, +but turbostat didn't consult that before attempting to access them. + +Since VMWARE allows access, but always returns 0, turbostat +got confusd into an infinite reset loop. + +Head this off by listening to CPUID.6.APERF_MPERF +(and rename the existing variable to make this more clear) + +Reported-by: David Arcari +Tested-by: David Arcari +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index ab28ac6e74b63..0064f9091c7f0 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -497,7 +497,7 @@ unsigned int summary_only; + unsigned int list_header_only; + unsigned int dump_only; + unsigned int force_load; +-unsigned int has_aperf; ++unsigned int cpuid_has_aperf_mperf; + unsigned int has_aperf_access; + unsigned int has_epb; + unsigned int has_turbo; +@@ -8404,7 +8404,7 @@ void linux_perf_init(void) + if (access("/proc/sys/kernel/perf_event_paranoid", F_OK)) + return; + +- if (BIC_IS_ENABLED(BIC_IPC) && has_aperf) { ++ if (BIC_IS_ENABLED(BIC_IPC) && cpuid_has_aperf_mperf) { + fd_instr_count_percpu = calloc(topo.max_cpu_num + 1, sizeof(int)); + if (fd_instr_count_percpu == NULL) + err(-1, "calloc fd_instr_count_percpu"); +@@ -8524,7 +8524,7 @@ void rapl_perf_init(void) + /* Assumes msr_counter_info is populated */ + static int has_amperf_access(void) + { +- return msr_counter_arch_infos[MSR_ARCH_INFO_APERF_INDEX].present && ++ return cpuid_has_aperf_mperf && msr_counter_arch_infos[MSR_ARCH_INFO_APERF_INDEX].present && + msr_counter_arch_infos[MSR_ARCH_INFO_MPERF_INDEX].present; + } + +@@ -8936,7 +8936,7 @@ void process_cpuid() + */ + + __cpuid(0x6, eax, ebx, ecx, edx); +- has_aperf = ecx & (1 << 0); ++ cpuid_has_aperf_mperf = ecx & (1 << 0); + do_dts = eax & (1 << 0); + if (do_dts) + BIC_PRESENT(BIC_CoreTmp); +@@ -8954,7 +8954,7 @@ void process_cpuid() + if (!quiet) + fprintf(outf, "CPUID(6): %sAPERF, %sTURBO, %sDTS, %sPTM, %sHWP, " + "%sHWPnotify, %sHWPwindow, %sHWPepp, %sHWPpkg, %sEPB\n", +- has_aperf ? "" : "No-", ++ cpuid_has_aperf_mperf ? "" : "No-", + has_turbo ? "" : "No-", + do_dts ? "" : "No-", + do_ptm ? "" : "No-", +@@ -9032,7 +9032,7 @@ void process_cpuid() + base_mhz, max_mhz, bus_mhz); + } + +- if (has_aperf) ++ if (cpuid_has_aperf_mperf) + aperf_mperf_multiplier = platform->need_perf_multiplier ? 1024 : 1; + + BIC_PRESENT(BIC_IRQ); +@@ -10231,7 +10231,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.12.01 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.12.02 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0018-IPU7-PSYS-driver-addition.ipu b/SPECS/kernel-rt/0018-IPU7-PSYS-driver-addition.ipu deleted file mode 100644 index 7233d6bd8..000000000 --- a/SPECS/kernel-rt/0018-IPU7-PSYS-driver-addition.ipu +++ /dev/null @@ -1,2862 +0,0 @@ -From 4bce4e3dd4b320221103c2e9c83b07e875e2ef80 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Wed, 21 Jan 2026 10:55:42 +0800 -Subject: [PATCH 18/21] IPU7 PSYS driver addition - -Include IPU7 psys drivers into staging folder. -These drivers are from ipu7-drivers github repo for PTL PV release. - -Remove Linux version code macro to match with kernel version to merge -as recommended from checkpatch. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/staging/media/ipu7/psys/Makefile | 18 + - drivers/staging/media/ipu7/psys/ipu-psys.c | 1551 +++++++++++++++++ - .../staging/media/ipu7/psys/ipu7-fw-psys.c | 603 +++++++ - .../staging/media/ipu7/psys/ipu7-fw-psys.h | 42 + - drivers/staging/media/ipu7/psys/ipu7-psys.c | 398 +++++ - drivers/staging/media/ipu7/psys/ipu7-psys.h | 184 ++ - 6 files changed, 2796 insertions(+) - create mode 100644 drivers/staging/media/ipu7/psys/Makefile - create mode 100644 drivers/staging/media/ipu7/psys/ipu-psys.c - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-fw-psys.c - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-fw-psys.h - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-psys.c - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-psys.h - -diff --git a/drivers/staging/media/ipu7/psys/Makefile b/drivers/staging/media/ipu7/psys/Makefile -new file mode 100644 -index 000000000000..33eb383a14bc ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/Makefile -@@ -0,0 +1,18 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2017 - 2024 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+intel-ipu7-psys-objs += ipu-psys.o \ -+ ipu7-psys.o \ -+ ipu7-fw-psys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o -+ -+ccflags-y += -I$(src)/ -+ccflags-y += -I$(src)/../ -diff --git a/drivers/staging/media/ipu7/psys/ipu-psys.c b/drivers/staging/media/ipu7/psys/ipu-psys.c -new file mode 100644 -index 000000000000..d6f3cea52f8a ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu-psys.c -@@ -0,0 +1,1551 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2013 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "ipu7.h" -+#include "ipu7-mmu.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+#include "ipu7-boot.h" -+ -+#define IPU_PSYS_NUM_DEVICES 4U -+ -+static int psys_runtime_pm_resume(struct device *dev); -+static int psys_runtime_pm_suspend(struct device *dev); -+ -+#define IPU_FW_CALL_TIMEOUT_JIFFIES \ -+ msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) -+ -+static dev_t ipu7_psys_dev_t; -+static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); -+static DEFINE_MUTEX(ipu7_psys_mutex); -+ -+static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ struct vm_area_struct *vma; -+ unsigned long start, end; -+ int npages, array_size; -+ struct page **pages; -+ struct sg_table *sgt; -+ int ret = -ENOMEM; -+ int nr = 0; -+ u32 flags; -+ -+ start = (unsigned long)attach->userptr; -+ end = PAGE_ALIGN(start + attach->len); -+ npages = PHYS_PFN(end - (start & PAGE_MASK)); -+ array_size = npages * sizeof(struct page *); -+ -+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -+ if (!sgt) -+ return -ENOMEM; -+ -+ WARN_ON_ONCE(attach->npages); -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ goto free_sgt; -+ -+ mmap_read_lock(current->mm); -+ vma = vma_lookup(current->mm, start); -+ if (unlikely(!vma)) { -+ ret = -EFAULT; -+ goto error_up_read; -+ } -+ mmap_read_unlock(current->mm); -+ -+ flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; -+ nr = pin_user_pages_fast(start & PAGE_MASK, npages, -+ flags, pages); -+ if (nr < npages) -+ goto error; -+ -+ attach->pages = pages; -+ attach->npages = npages; -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, npages, -+ start & ~PAGE_MASK, attach->len, -+ GFP_KERNEL); -+ if (ret < 0) -+ goto error; -+ -+ attach->sgt = sgt; -+ -+ return 0; -+ -+error_up_read: -+ mmap_read_unlock(current->mm); -+error: -+ if (nr) -+ unpin_user_pages(pages, nr); -+ -+ kvfree(pages); -+free_sgt: -+ kfree(sgt); -+ -+ pr_err("failed to get userpages:%d\n", ret); -+ -+ return ret; -+} -+ -+static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ if (!attach || !attach->userptr || !attach->sgt) -+ return; -+ -+ unpin_user_pages(attach->pages, attach->npages); -+ -+ kvfree(attach->pages); -+ -+ sg_free_table(attach->sgt); -+ kfree(attach->sgt); -+ attach->sgt = NULL; -+} -+ -+static int ipu7_dma_buf_attach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_psys_kbuffer *kbuf = dbuf->priv; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ int ret; -+ -+ ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); -+ if (!ipu7_attach) -+ return -ENOMEM; -+ -+ ipu7_attach->len = kbuf->len; -+ ipu7_attach->userptr = kbuf->userptr; -+ -+ attach->priv = ipu7_attach; -+ -+ ret = ipu7_psys_get_userpages(ipu7_attach); -+ if (ret) { -+ kfree(ipu7_attach); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_detach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ -+ ipu7_psys_put_userpages(ipu7_attach); -+ kfree(ipu7_attach); -+ attach->priv = NULL; -+} -+ -+static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, -+ enum dma_data_direction dir) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ unsigned long attrs; -+ int ret; -+ -+ attrs = DMA_ATTR_SKIP_CPU_SYNC; -+ ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, -+ attrs); -+ if (ret) { -+ dev_err(attach->dev, "pci buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, -+ DMA_BIDIRECTIONAL); -+ -+ ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); -+ if (ret) { -+ dev_err(attach->dev, "ipu7 buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); -+ -+ return ipu7_attach->sgt; -+} -+ -+static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, -+ struct sg_table *sgt, -+ enum dma_data_direction dir) -+{ -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ -+ ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+} -+ -+static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) -+{ -+ return -ENOTTY; -+} -+ -+static void ipu7_dma_buf_release(struct dma_buf *buf) -+{ -+ struct ipu7_psys_kbuffer *kbuf = buf->priv; -+ -+ if (!kbuf) -+ return; -+ -+ if (kbuf->db_attach) -+ ipu7_psys_put_userpages(kbuf->db_attach->priv); -+ -+ kfree(kbuf); -+} -+ -+static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, -+ enum dma_data_direction dir) -+{ -+ return -ENOTTY; -+} -+ -+static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (list_empty(&dmabuf->attachments)) -+ return -EINVAL; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) -+ return -EINVAL; -+ -+ map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); -+ map->is_iomem = false; -+ if (!map->vaddr) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (WARN_ON(list_empty(&dmabuf->attachments))) -+ return; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || -+ !ipu7_attach->npages)) -+ return; -+ -+ vm_unmap_ram(map->vaddr, ipu7_attach->npages); -+} -+ -+static struct dma_buf_ops ipu7_dma_buf_ops = { -+ .attach = ipu7_dma_buf_attach, -+ .detach = ipu7_dma_buf_detach, -+ .map_dma_buf = ipu7_dma_buf_map, -+ .unmap_dma_buf = ipu7_dma_buf_unmap, -+ .release = ipu7_dma_buf_release, -+ .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, -+ .mmap = ipu7_dma_buf_mmap, -+ .vmap = ipu7_dma_buf_vmap, -+ .vunmap = ipu7_dma_buf_vunmap, -+}; -+ -+static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) -+{ -+ u8 graph_id = 0; -+ -+ for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { -+ if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) -+ break; -+ } -+ -+ if (graph_id == IPU_PSYS_NUM_STREAMS) -+ return -EBUSY; -+ -+ fh->psys->graph_id[graph_id] = graph_id; -+ return graph_id; -+} -+ -+static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) -+{ -+ fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; -+} -+ -+static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, -+ struct ipu7_bus_device *adev) -+{ -+ if (tq->msg_task) -+ ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), -+ tq->msg_task, tq->task_dma_addr, 0); -+ -+ list_del(&tq->list); -+ kfree(tq); -+} -+ -+static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct ipu_psys_task_ack *ack; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ -+ mutex_destroy(&ip->event_mutex); -+ mutex_destroy(&ip->task_mutex); -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { -+ list_del(&ack->list); -+ kfree(ack); -+ } -+} -+ -+static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ u8 i; -+ -+ INIT_LIST_HEAD(&ip->event_list); -+ INIT_LIST_HEAD(&ip->ack_list); -+ -+ INIT_LIST_HEAD(&ip->tq_running_list); -+ INIT_LIST_HEAD(&ip->tq_list); -+ -+ for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ if (!event) -+ goto event_cleanup; -+ -+ list_add(&event->list, &ip->event_list); -+ } -+ -+ for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { -+ tq = kzalloc(sizeof(*tq), GFP_KERNEL); -+ if (!tq) -+ goto tq_cleanup; -+ -+ list_add(&tq->list, &ip->tq_list); -+ -+ tq->msg_task = -+ ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), -+ &tq->task_dma_addr, GFP_KERNEL, 0); -+ -+ if (!tq->msg_task) { -+ dev_err(dev, "Failed to allocate msg task.\n"); -+ goto tq_cleanup; -+ } -+ } -+ -+ init_completion(&ip->graph_open); -+ init_completion(&ip->graph_close); -+ -+ return 0; -+ -+tq_cleanup: -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+event_cleanup: -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ return -ENOMEM; -+} -+ -+static int ipu7_psys_open(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_psys_fh *fh; -+ struct ipu7_psys_stream *ip; -+ int ret; -+ -+ fh = kzalloc(sizeof(*fh), GFP_KERNEL); -+ if (!fh) -+ return -ENOMEM; -+ -+ ip = kzalloc(sizeof(*ip), GFP_KERNEL); -+ if (!ip) { -+ ret = -ENOMEM; -+ goto alloc_failed; -+ } -+ -+ ret = ipu7_psys_stream_init(ip, psys->adev); -+ if (ret) -+ goto stream_init_failed; -+ -+ fh->ip = ip; -+ ip->fh = fh; -+ -+ fh->psys = psys; -+ -+ file->private_data = fh; -+ -+ mutex_init(&fh->mutex); -+ INIT_LIST_HEAD(&fh->bufmap); -+ init_waitqueue_head(&fh->wait); -+ -+ mutex_init(&ip->task_mutex); -+ mutex_init(&ip->event_mutex); -+ -+ mutex_lock(&psys->mutex); -+ -+ ret = ipu7_psys_get_graph_id(fh); -+ if (ret < 0) -+ goto open_failed; -+ -+ fh->ip->graph_id = ret; -+ -+ ret = pm_runtime_get_sync(dev); -+ if (ret < 0) { -+ dev_err(dev, "Runtime PM failed (%d)\n", ret); -+ goto rpm_put; -+ } -+ -+ list_add_tail(&fh->list, &psys->fhs); -+ -+ mutex_unlock(&psys->mutex); -+ -+ return 0; -+ -+rpm_put: -+ pm_runtime_put(dev); -+ ipu7_psys_put_graph_id(fh); -+ -+open_failed: -+ ipu7_psys_stream_deinit(ip, psys->adev); -+ -+ mutex_destroy(&fh->mutex); -+ -+ mutex_unlock(&psys->mutex); -+ -+stream_init_failed: -+ kfree(ip); -+ -+alloc_failed: -+ kfree(fh); -+ -+ return ret; -+} -+ -+static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ if (!kbuf) -+ return; -+ -+ kbuf->valid = false; -+ if (kbuf->kaddr) { -+ struct iosys_map dmap; -+ -+ iosys_map_set_vaddr(&dmap, kbuf->kaddr); -+ dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); -+ } -+ -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ -+ if (kbuf->sgt) -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ if (kbuf->db_attach) -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ dma_buf_put(kbuf->dbuf); -+ -+ kbuf->db_attach = NULL; -+ kbuf->dbuf = NULL; -+ kbuf->sgt = NULL; -+} -+ -+static int ipu7_psys_release(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct ipu7_psys_kbuffer *kbuf, *kbuf0; -+ struct dma_buf_attachment *dba; -+ -+ mutex_lock(&fh->mutex); -+ /* clean up buffers */ -+ if (!list_empty(&fh->bufmap)) { -+ list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { -+ list_del(&kbuf->list); -+ dba = kbuf->db_attach; -+ -+ /* Unmap and release buffers */ -+ if (kbuf->dbuf && dba) { -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ } else { -+ if (dba) -+ ipu7_psys_put_userpages(dba->priv); -+ kfree(kbuf); -+ } -+ } -+ } -+ mutex_unlock(&fh->mutex); -+ -+ ipu7_psys_stream_deinit(fh->ip, psys->adev); -+ -+ mutex_lock(&psys->mutex); -+ list_del(&fh->list); -+ -+ ipu7_psys_put_graph_id(fh); -+ kfree(fh->ip); -+ -+ mutex_unlock(&psys->mutex); -+ mutex_destroy(&fh->mutex); -+ kfree(fh); -+ -+ pm_runtime_put_sync(&psys->adev->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, -+ struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -+ struct dma_buf *dbuf; -+ int ret; -+ -+ if (!buf->base.userptr) { -+ dev_err(dev, "Buffer allocation not supported\n"); -+ return -EINVAL; -+ } -+ -+ if (!PAGE_ALIGNED(buf->base.userptr)) { -+ dev_err(dev, "Not page-aligned userptr is not supported\n"); -+ return -EINVAL; -+ } -+ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) -+ return -ENOMEM; -+ -+ kbuf->len = buf->len; -+ kbuf->userptr = buf->base.userptr; -+ kbuf->flags = buf->flags; -+ -+ exp_info.ops = &ipu7_dma_buf_ops; -+ exp_info.size = kbuf->len; -+ exp_info.flags = O_RDWR; -+ exp_info.priv = kbuf; -+ -+ dbuf = dma_buf_export(&exp_info); -+ if (IS_ERR(dbuf)) { -+ kfree(kbuf); -+ return PTR_ERR(dbuf); -+ } -+ -+ ret = dma_buf_fd(dbuf, 0); -+ if (ret < 0) { -+ dma_buf_put(dbuf); -+ return ret; -+ } -+ -+ kbuf->fd = ret; -+ buf->base.fd = ret; -+ buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; -+ buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; -+ kbuf->flags = buf->flags; -+ -+ mutex_lock(&fh->mutex); -+ list_add(&kbuf->list, &fh->bufmap); -+ mutex_unlock(&fh->mutex); -+ -+ dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", -+ buf->base.userptr, buf->len, buf->base.fd); -+ -+ return 0; -+} -+ -+static int -+ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) -+{ -+ return 0; -+} -+ -+static struct ipu7_psys_kbuffer * -+ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) -+{ -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ list_for_each_entry(kbuf, &fh->bufmap, list) { -+ if (kbuf->fd == fd) -+ return kbuf; -+ } -+ -+ return NULL; -+} -+ -+static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ -+ if (!kbuf || fd != kbuf->fd) { -+ dev_err(dev, "invalid kbuffer\n"); -+ return -EINVAL; -+ } -+ -+ /* From now on it is not safe to use this kbuffer */ -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ -+ list_del(&kbuf->list); -+ -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+ dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); -+ -+ return 0; -+} -+ -+static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct device *dev = &psys->adev->isp->pdev->dev; -+ struct dma_buf *dbuf; -+ struct iosys_map dmap; -+ int ret; -+ -+ dbuf = dma_buf_get(fd); -+ if (IS_ERR(dbuf)) -+ return -EINVAL; -+ -+ if (!kbuf) { -+ /* This fd isn't generated by ipu7_psys_getbuf, it -+ * is a new fd. Create a new kbuf item for this fd, and -+ * add this kbuf to bufmap list. -+ */ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ -+ /* fd valid and found, need remap */ -+ if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { -+ dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", -+ fd, kbuf); -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ if (ret) -+ goto mapbuf_fail; -+ -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ /* changed external dmabuf */ -+ if (!kbuf) { -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ } -+ -+ if (kbuf->sgt) { -+ dev_dbg(dev, "fd %d has been mapped!\n", fd); -+ dma_buf_put(dbuf); -+ goto mapbuf_end; -+ } -+ -+ kbuf->dbuf = dbuf; -+ -+ if (kbuf->len == 0) -+ kbuf->len = kbuf->dbuf->size; -+ -+ kbuf->fd = fd; -+ -+ kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); -+ if (IS_ERR(kbuf->db_attach)) { -+ ret = PTR_ERR(kbuf->db_attach); -+ dev_err(dev, "dma buf attach failed\n"); -+ goto attach_fail; -+ } -+ -+ kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, -+ DMA_BIDIRECTIONAL); -+ if (IS_ERR_OR_NULL(kbuf->sgt)) { -+ ret = -EINVAL; -+ kbuf->sgt = NULL; -+ dev_err(dev, "dma buf map attachment failed\n"); -+ goto kbuf_map_fail; -+ } -+ -+ if (!kbuf->userptr) { -+ ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ if (ret) { -+ dev_dbg(dev, "ipu7 buf map failed\n"); -+ goto kbuf_map_fail; -+ } -+ } -+ -+ kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); -+ -+ /* no need vmap for imported dmabufs */ -+ if (!kbuf->userptr) -+ goto mapbuf_end; -+ -+ dmap.is_iomem = false; -+ ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); -+ if (ret) { -+ dev_err(dev, "dma buf vmap failed\n"); -+ goto kbuf_map_fail; -+ } -+ kbuf->kaddr = dmap.vaddr; -+ -+mapbuf_end: -+ dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", -+ __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, -+ kbuf->len); -+ kbuf->valid = true; -+ -+ return 0; -+ -+kbuf_map_fail: -+ if (!IS_ERR_OR_NULL(kbuf->sgt)) { -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ } -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ -+attach_fail: -+ list_del(&kbuf->list); -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+mapbuf_fail: -+ dma_buf_put(dbuf); -+ -+ dev_err(dev, "%s failed for fd %d\n", __func__, fd); -+ return ret; -+} -+ -+static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ long ret; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ long ret; -+ -+ dev_dbg(dev, "IOC_UNMAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ if (!kbuf) { -+ dev_err(dev, -+ "buffer with fd %d not found\n", fd); -+ mutex_unlock(&fh->mutex); -+ return -EINVAL; -+ } -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { -+ dev_err(&psys->dev, "nodes is wrong\n"); -+ return -EINVAL; -+ } -+ -+ if (copy_from_user(fh->ip->nodes, graph->nodes, -+ graph->num_nodes * sizeof(*graph->nodes))) { -+ dev_err(&psys->dev, "Failed to copy nodes\n"); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_open); -+ -+ ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to open graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_open, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Open graph %d timeout\n", -+ fh->ip->graph_id); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Failed to set graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ graph->graph_id = fh->ip->graph_id; -+ -+ return 0; -+} -+ -+static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_close); -+ -+ ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to close graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_close, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Close graph %d timeout\n", -+ fh->ip->graph_id); -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Failed to close graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct ipu_psys_task_queue * -+ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_request *task) -+{ -+ struct device *dev = &ip->fh->psys->dev; -+ struct ipu7_psys_kbuffer *kbuf = NULL; -+ struct ipu_psys_task_queue *tq; -+ int fd, prevfd = -1; -+ u32 i; -+ -+ if (task->term_buf_count > MAX_GRAPH_TERMINALS) { -+ dev_err(dev, "num_teminal_buffer is too large\n"); -+ return NULL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (list_empty(&ip->tq_list)) { -+ dev_err(dev, "No available take queue for stream %p\n", ip); -+ goto unlock; -+ } -+ -+ tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, -+ list); -+ -+ if (copy_from_user(tq->task_buffers, -+ task->task_buffers, -+ task->term_buf_count * -+ sizeof(*task->task_buffers))) { -+ dev_err(dev, "failed to copy task buffers\n"); -+ goto unlock; -+ } -+ -+ for (i = 0; i < task->term_buf_count; i++) { -+ fd = tq->task_buffers[i].term_buf.base.fd; -+ kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); -+ if (!kbuf) { -+ dev_err(dev, "fd %d not found\n", fd); -+ goto unlock; -+ } -+ tq->ipu7_addr[i] = kbuf->dma_addr -+ + tq->task_buffers[i].term_buf.data_offset; -+ -+ if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & -+ IPU_BUFFER_FLAG_NO_FLUSH)) -+ continue; -+ -+ prevfd = fd; -+ -+ if (kbuf->kaddr) -+ clflush_cache_range(kbuf->kaddr, kbuf->len); -+ } -+ -+ dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_running_list); -+ -+ mutex_unlock(&ip->task_mutex); -+ return tq; -+ -+unlock: -+ mutex_unlock(&ip->task_mutex); -+ return NULL; -+} -+ -+static long ipu_psys_task_request(struct ipu_psys_task_request *task, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct ipu_psys_task_queue *tq; -+ int ret = 0; -+ -+ if (task->term_buf_count == 0 || !task->task_buffers) { -+ dev_err(&psys->dev, "task_buffer is NULL\n"); -+ return -EINVAL; -+ } -+ -+ tq = ipu7_psys_get_task_queue(fh->ip, task); -+ if (!tq) { -+ dev_err(&psys->dev, "Failed to get task queue\n"); -+ return -EINVAL; -+ } -+ -+ ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to request task %d\n", -+ fh->ip->graph_id); -+ mutex_lock(&fh->ip->task_mutex); -+ list_move_tail(&tq->list, &fh->ip->tq_list); -+ mutex_unlock(&fh->ip->task_mutex); -+ return ret; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; -+ -+ return 0; -+} -+ -+static unsigned int ipu7_psys_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_stream *ip = fh->ip; -+ unsigned int res = 0; -+ -+ dev_dbg(dev, "ipu psys poll\n"); -+ -+ poll_wait(file, &fh->wait, wait); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->ack_list)) -+ res = POLLIN; -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(dev, "ipu psys poll res %u\n", res); -+ -+ return res; -+} -+ -+static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ union { -+ struct ipu_psys_graph_info graph; -+ struct ipu_psys_task_request task; -+ struct ipu_psys_buffer buf; -+ struct ipu_psys_event ev; -+ } karg; -+ struct ipu7_psys_fh *fh = file->private_data; -+ long err = 0; -+ void __user *up = (void __user *)arg; -+ bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && -+ cmd != IPU_IOC_GRAPH_CLOSE); -+ -+ if (copy) { -+ if (_IOC_SIZE(cmd) > sizeof(karg)) -+ return -ENOTTY; -+ -+ if (_IOC_DIR(cmd) & _IOC_WRITE) { -+ err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); -+ if (err) -+ return -EFAULT; -+ } -+ } -+ -+ switch (cmd) { -+ case IPU_IOC_MAPBUF: -+ err = ipu7_psys_mapbuf(arg, fh); -+ break; -+ case IPU_IOC_UNMAPBUF: -+ err = ipu7_psys_unmapbuf(arg, fh); -+ break; -+ case IPU_IOC_GETBUF: -+ err = ipu7_psys_getbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_PUTBUF: -+ err = ipu7_psys_putbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_GRAPH_OPEN: -+ err = ipu_psys_graph_open(&karg.graph, fh); -+ break; -+ case IPU_IOC_GRAPH_CLOSE: -+ err = ipu_psys_graph_close(arg, fh); -+ break; -+ case IPU_IOC_TASK_REQUEST: -+ err = ipu_psys_task_request(&karg.task, fh); -+ break; -+ case IPU_IOC_DQEVENT: -+ err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); -+ break; -+ default: -+ err = -ENOTTY; -+ break; -+ } -+ -+ if (err) -+ return err; -+ -+ if (copy && _IOC_DIR(cmd) & _IOC_READ) -+ if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static const struct file_operations ipu7_psys_fops = { -+ .open = ipu7_psys_open, -+ .release = ipu7_psys_release, -+ .unlocked_ioctl = ipu7_psys_ioctl, -+ .poll = ipu7_psys_poll, -+ .owner = THIS_MODULE, -+}; -+ -+static void ipu7_psys_dev_release(struct device *dev) -+{ -+} -+ -+static int psys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ int ret; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ if (!ipu_buttress_auth_done(adev->isp)) { -+ dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); -+ return 0; -+ } -+ -+ ipu7_psys_setup_hw(psys); -+ -+ ipu7_psys_subdomains_power(psys, 1); -+ -+ ret = ipu7_boot_start_fw(psys->adev); -+ if (ret) { -+ dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); -+ return ret; -+ } -+ -+ ret = ipu7_fw_psys_open(psys); -+ if (ret) { -+ dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); -+ return ret; -+ } -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ psys->ready = 1; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return 0; -+} -+ -+static int psys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (!psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ psys->ready = 0; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ipu7_fw_psys_close(psys); -+ -+ ipu7_boot_stop_fw(psys->adev); -+ -+ ipu7_psys_subdomains_power(psys, 0); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+/* The following PM callbacks are needed to enable runtime PM in IPU PCI -+ * device resume, otherwise, runtime PM can't work in PCI resume from -+ * S3 state. -+ */ -+static int psys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static int psys_suspend(struct device *dev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(dev); -+ unsigned long flags; -+ int ret = 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) -+ ret = -EBUSY; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops psys_pm_ops = { -+ .runtime_suspend = psys_runtime_pm_suspend, -+ .runtime_resume = psys_runtime_pm_resume, -+ .suspend = psys_suspend, -+ .resume = psys_resume, -+}; -+ -+#ifdef CONFIG_DEBUG_FS -+static int psys_fw_log_init(struct ipu7_psys *psys) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log; -+ void *log_buf; -+ -+ if (psys->fw_log) -+ return 0; -+ -+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -+ if (!fw_log) -+ return -ENOMEM; -+ -+ mutex_init(&fw_log->mutex); -+ -+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!log_buf) -+ return -ENOMEM; -+ -+ fw_log->head = log_buf; -+ fw_log->addr = log_buf; -+ fw_log->count = 0; -+ fw_log->size = 0; -+ -+ psys->fw_log = fw_log; -+ -+ return 0; -+} -+ -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_psys *psys = file->private_data; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ struct device *dev = &psys->adev->auxdev.dev; -+ u32 log_size; -+ void *buf; -+ int ret = 0; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations psys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, psys, &psys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ psys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ -+static const struct bus_type ipu7_psys_bus = { -+ .name = "intel-ipu7-psys", -+}; -+ -+static int ipu7_psys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ struct device *dev = &auxdev->dev; -+ struct ipu7_psys *psys; -+ unsigned int minor; -+ unsigned int i; -+ int ret; -+ -+ if (!adev->isp->ipu7_bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, -+ IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); -+ if (ret) { -+ dev_err(dev, "can't alloc psys chrdev region (%d)\n", -+ ret); -+ return ret; -+ } -+ -+ ret = pm_runtime_resume_and_get(&auxdev->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ goto out_unregister_chr_region; -+ -+ mutex_lock(&ipu7_psys_mutex); -+ -+ minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); -+ if (minor == IPU_PSYS_NUM_DEVICES) { -+ dev_err(dev, "too many devices\n"); -+ goto out_unlock; -+ } -+ -+ psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); -+ if (!psys) { -+ ret = -ENOMEM; -+ goto out_unlock; -+ } -+ -+ for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) -+ psys->graph_id[i] = INVALID_STREAM_ID; -+ -+ adev->auxdrv_data = -+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(dev->driver); -+ -+ psys->adev = adev; -+ psys->pdata = adev->pdata; -+ -+ cdev_init(&psys->cdev, &ipu7_psys_fops); -+ psys->cdev.owner = ipu7_psys_fops.owner; -+ -+ ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); -+ if (ret) { -+ dev_err(dev, "cdev_add failed (%d)\n", ret); -+ goto out_unlock; -+ } -+ -+ set_bit(minor, ipu7_psys_devices); -+ -+ spin_lock_init(&psys->ready_lock); -+ -+ psys->ready = 0; -+ psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; -+ -+ mutex_init(&psys->mutex); -+ INIT_LIST_HEAD(&psys->fhs); -+ -+ ret = ipu7_fw_psys_init(psys); -+ if (ret) { -+ dev_err(dev, "FW init failed(%d)\n", ret); -+ goto out_mutex_destroy; -+ } -+ -+ psys->dev.bus = &ipu7_psys_bus; -+ psys->dev.parent = dev; -+ psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); -+ psys->dev.release = ipu7_psys_dev_release; -+ dev_set_name(&psys->dev, "ipu7-psys%d", minor); -+ ret = device_register(&psys->dev); -+ if (ret < 0) { -+ dev_err(&psys->dev, "psys device_register failed\n"); -+ goto out_fw_release; -+ } -+ -+ dev_set_drvdata(dev, psys); -+ mutex_unlock(&ipu7_psys_mutex); -+#ifdef CONFIG_DEBUG_FS -+ psys_fw_log_init(psys); -+ ipu7_psys_init_debugfs(psys); -+#endif -+ dev_info(dev, "IPU psys probe done.\n"); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ pm_runtime_put(&auxdev->dev); -+ -+ return 0; -+ -+out_fw_release: -+ ipu7_fw_psys_release(psys); -+out_mutex_destroy: -+ mutex_destroy(&psys->mutex); -+ cdev_del(&psys->cdev); -+out_unlock: -+ /* Safe to call even if the init is not called */ -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+out_unregister_chr_region: -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ pm_runtime_put(&auxdev->dev); -+ -+ return ret; -+} -+ -+static void ipu7_psys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); -+ struct device *dev = &auxdev->dev; -+#ifdef CONFIG_DEBUG_FS -+ struct ipu7_device *isp = psys->adev->isp; -+ -+ if (isp->ipu7_dir) -+ debugfs_remove_recursive(psys->debugfsdir); -+#endif -+ -+ mutex_lock(&ipu7_psys_mutex); -+ ipu7_fw_psys_release(psys); -+ device_unregister(&psys->dev); -+ clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); -+ cdev_del(&psys->cdev); -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ mutex_destroy(&psys->mutex); -+ -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ -+ dev_info(dev, "removed\n"); -+} -+ -+static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ struct device *dev = &psys->adev->auxdev.dev; -+ void __iomem *base = psys->pdata->base; -+ u32 status, state; -+ int r; -+ -+ mutex_lock(&psys->mutex); -+ r = pm_runtime_get_if_in_use(dev); -+ if (!r || WARN_ON_ONCE(r < 0)) { -+ mutex_unlock(&psys->mutex); -+ return IRQ_NONE; -+ } -+ -+ state = ipu7_boot_get_boot_state(adev); -+ if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { -+ dev_warn(&psys->dev, "error state %u\n", state); -+ } else { -+ /* Disable irq before clear irq status */ -+ status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); -+ writel(0, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ -+ if (status & IRQ_FROM_LOCAL_FW) -+ ipu7_psys_handle_events(psys); -+ -+ writel(IRQ_FROM_LOCAL_FW, -+ base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ } -+ -+ pm_runtime_put(dev); -+ mutex_unlock(&psys->mutex); -+ -+ return IRQ_HANDLED; -+} -+ -+static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { -+ .isr_threaded = psys_isr_threaded, -+ .wake_isr_thread = true, -+}; -+ -+static const struct auxiliary_device_id ipu7_psys_id_table[] = { -+ { -+ .name = "intel_ipu7.psys", -+ .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, -+ }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); -+ -+static struct auxiliary_driver ipu7_psys_driver = { -+ .name = IPU_PSYS_NAME, -+ .probe = ipu7_psys_probe, -+ .remove = ipu7_psys_remove, -+ .id_table = ipu7_psys_id_table, -+ .driver = { -+ .pm = &psys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(ipu7_psys_driver); -+ -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_AUTHOR("Tianshu Qiu "); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 processing system driver"); -+MODULE_IMPORT_NS("INTEL_IPU7"); -+MODULE_IMPORT_NS("DMA_BUF"); -diff --git a/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c -new file mode 100644 -index 000000000000..15ba548ecd83 ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c -@@ -0,0 +1,603 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2016 - 2024 Intel Corporation -+ -+#include -+#include -+ -+#include -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+#include "abi/ipu7_fw_psys_config_abi.h" -+ -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-syscom.h" -+#include "ipu7-psys.h" -+ -+#define TLV_TYPE(type) ((u32)(type) & 0x3FU) -+#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) -+ -+/* -+ * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) -+ -+/* -+ * Special node resource ID to identify a generic external node. -+ * Required when there is a link to/from IPU and that node. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_syscom_context *syscom; -+ struct ipu7_psys_config *psys_config; -+ struct syscom_queue_config *queue_configs; -+ dma_addr_t psys_config_dma_addr; -+ u32 freq; -+ int i, num_queues, ret; -+ -+ /* Allocate and init syscom context. */ -+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -+ GFP_KERNEL); -+ if (!syscom) -+ return -ENOMEM; -+ -+ adev->syscom = syscom; -+ syscom->num_input_queues = FWPS_MSG_ABI_MAX_INPUT_QUEUES; -+ syscom->num_output_queues = FWPS_MSG_ABI_MAX_OUTPUT_QUEUES; -+ num_queues = syscom->num_input_queues + syscom->num_output_queues; -+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -+ GFP_KERNEL); -+ if (!queue_configs) { -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ syscom->queue_configs = queue_configs; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = -+ IPU_PSYS_ACK_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = -+ IPU_PSYS_LOG_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = -+ IPU_PSYS_CMD_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = -+ FWPS_MSG_HOST2FW_MAX_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = -+ 0; -+ -+ for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { -+ queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; -+ queue_configs[i].token_size_in_bytes = -+ sizeof(struct ia_gofo_msg_indirect); -+ } -+ -+ /* Allocate PSYS subsys config. */ -+ psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), -+ &psys_config_dma_addr, GFP_KERNEL, 0); -+ if (!psys_config) { -+ dev_err(dev, "Failed to allocate psys subsys config.\n"); -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ psys->subsys_config = psys_config; -+ psys->subsys_config_dma_addr = psys_config_dma_addr; -+ memset(psys_config, 0, sizeof(struct ipu7_psys_config)); -+ ret = ipu_buttress_get_psys_freq(adev->isp, &freq); -+ if (ret) { -+ dev_err(dev, "Failed to get PSYS frequency.\n"); -+ ipu7_fw_psys_release(psys); -+ return ret; -+ } -+ -+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -+ freq, psys_config_dma_addr, 1U); -+ if (ret) -+ ipu7_fw_psys_release(psys); -+ return ret; -+} -+ -+void ipu7_fw_psys_release(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ -+ ipu7_boot_release_boot_config(adev); -+ if (psys->subsys_config) { -+ ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), -+ psys->subsys_config, -+ psys->subsys_config_dma_addr, 0); -+ psys->subsys_config = NULL; -+ psys->subsys_config_dma_addr = 0; -+ } -+} -+ -+static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ int ret; -+ -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ return ret; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ if (ack_header->header.tlv_header.tlv_type == type) -+ return 0; -+ -+ return -EAGAIN; -+} -+ -+static int ipu7_fw_dev_open(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_open *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys open\n"); -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | -+ IPU_MSG_DEVICE_OPEN_SEND_IRQ); -+ token->enable_power_gating = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_open(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_open(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to open PSYS dev.\n"); -+ return ret; -+ } -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev open done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN; -+ return 0; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev open timeout!\n"); -+ -+ return ret; -+} -+ -+static int ipu7_fw_dev_close(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | -+ IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+void ipu7_fw_psys_close(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_close(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to close PSYS dev.\n"); -+ return; -+ } -+ -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev close done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; -+ return; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev close timeout!\n"); -+} -+ -+static void -+ipu7_fw_psys_build_node_profile(const struct node_profile *profile, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_cb_profile *cb_profile = -+ (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*cb_profile); -+ -+ memcpy(cb_profile->profile_base.teb, profile->teb, -+ sizeof(cb_profile->profile_base.teb)); -+ -+ memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); -+ memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); -+ memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); -+ -+ cb_profile->profile_base.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); -+ cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+} -+ -+/* skip term, return false */ -+static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_term); -+ -+ if (!term->term_id && !term->buf_size) -+ return false; -+ -+ memset(msg_term, 0, sizeof(*msg_term)); -+ msg_term->term_id = term->term_id; -+ /* Disable progress message on connect terminals */ -+ msg_term->event_req_bm = 0U; -+ msg_term->payload_size = term->buf_size; -+ -+ msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); -+ msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+ return true; -+} -+ -+/* When skip processing node, just return false */ -+static bool ipu7_fw_psys_build_node(const struct graph_node *node, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_node); -+ bool ret = false; -+ u8 i = 0; -+ u8 max_terms = 0; -+ -+ memset(msg_node, 0, sizeof(*msg_node)); -+ /** -+ * Pass node info to FW, do not check for external IP and ISYS -+ * As FW expects a external node -+ */ -+ if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && -+ node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] == 0U) -+ return false; -+ } -+ -+ /** -+ * Sanity check for dummy node, TEB should set to required one -+ */ -+ if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || -+ node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || -+ node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) -+ return false; -+ } -+ -+ msg_node->node_rsrc_id = node->node_rsrc_id; -+ msg_node->node_ctx_id = node->node_ctx_id; -+ msg_node->num_frags = 1; -+ -+ *buf_ptr_ptr += buf_size; -+ -+ msg_node->profiles_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr -+ - (uintptr_t)&msg_node->profiles_list); -+ for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { -+ ipu7_fw_psys_build_node_profile(&node->profiles[i], -+ buf_ptr_ptr); -+ msg_node->profiles_list.num_elems++; -+ } -+ -+ msg_node->terms_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_node->terms_list); -+ max_terms = ARRAY_SIZE(node->terminals); -+ for (i = 0; i < max_terms && i < node->num_terms; i++) { -+ ret = ipu7_fw_psys_build_node_term(&node->terminals[i], -+ buf_ptr_ptr); -+ if (ret) -+ msg_node->terms_list.num_elems++; -+ } -+ -+ buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; -+ msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); -+ msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ return true; -+} -+ -+static bool ipu7_fw_psys_build_link(const struct graph_link *link, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; -+ -+ if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && -+ !link->ep_src.term_id && !link->ep_dst.term_id) -+ return false; -+ -+ msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; -+ msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; -+ -+ msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; -+ msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; -+ -+ msg_link->foreign_key = link->foreign_key; -+ msg_link->streaming_mode = link->streaming_mode; -+ msg_link->pbk_id = link->pbk_id; -+ msg_link->pbk_slot_id = link->pbk_slot_id; -+ msg_link->delayed_link = link->delayed_link; -+ -+ *buf_ptr_ptr += sizeof(*msg_link); -+ -+ msg_link->link_options.num_elems = 0; -+ msg_link->link_options.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_link->link_options); -+ msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); -+ msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); -+ -+ return true; -+} -+ -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *buf_ptr; -+ struct ipu7_msg_graph_open *graph_open; -+ u32 buf_size = 0; -+ bool ret = false; -+ u8 i = 0; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); -+ buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!buf_ptr) -+ return -ENODATA; -+ -+ graph_open = (struct ipu7_msg_graph_open *)buf_ptr; -+ -+ memset(graph_open, 0, sizeof(*graph_open)); -+ graph_open->graph_id = ip->graph_id; -+ graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP -+ | IPU_MSG_GRAPH_OPEN_SEND_IRQ); -+ -+ buf_ptr += sizeof(*graph_open); -+ graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->nodes); -+ for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { -+ ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); -+ if (ret) -+ graph_open->nodes.num_elems++; -+ } -+ -+ graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->links); -+ for (i = 0; i < ARRAY_SIZE(graph->links); i++) { -+ ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); -+ if (ret) -+ graph_open->links.num_elems++; -+ } -+ -+ buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); -+ graph_open->header.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); -+ graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ graph_open->header.user_token = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_graph_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->graph_id = graph_id; -+ token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP -+ | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_task *msg = tq->msg_task; -+ struct ia_gofo_msg_indirect *ind; -+ u32 node_q_id = ip->q_id[task->node_ctx_id]; -+ u32 teb_hi, teb_lo; -+ u64 teb; -+ u8 i, term_id; -+ u8 num_terms; -+ -+ ind = ipu7_syscom_get_token(ctx, node_q_id); -+ if (!ind) -+ return -ENODATA; -+ -+ memset(msg, 0, sizeof(*msg)); -+ msg->graph_id = task->graph_id; -+ msg->node_ctx_id = task->node_ctx_id; -+ msg->profile_idx = 0U; /* Only one profile on HKR */ -+ msg->frame_id = task->frame_id; -+ msg->frag_id = 0U; /* No frag, set to 0 */ -+ /* -+ * Each task has a flag indicating if ack needed, it may be used to -+ * reduce interrupts if multiple CBs supported. -+ */ -+ msg->req_done_msg = 1; -+ msg->req_done_irq = 1; -+ -+ memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, -+ sizeof(task->payload_reuse_bm)); -+ -+ teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; -+ teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; -+ teb = (teb_lo | (((u64)teb_hi) << 32)); -+ -+ num_terms = ip->nodes[msg->node_ctx_id].num_terms; -+ for (i = 0U; i < num_terms; i++) { -+ term_id = tq->task_buffers[i].term_id; -+ if ((1U << term_id) & teb) -+ msg->term_buffers[term_id] = tq->ipu7_addr[i]; -+ } -+ -+ msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); -+ msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); -+ msg->header.user_token = (uintptr_t)tq; -+ -+ ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); -+ ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); -+ ind->header.msg_options.num_elems = 0; -+ ind->header.msg_options.head_offset = 0; -+ ind->ref_header = msg->header.tlv_header; -+ ind->ref_msg_ptr = tq->task_dma_addr; -+ -+ ipu7_syscom_put_token(ctx, node_q_id); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *token; -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ return 0; -+} -+ -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys) -+{ -+ void *token; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ }; -+ -+ return 0; -+} -+ -diff --git a/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h -new file mode 100644 -index 000000000000..c43f235386d1 ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2016 - 2024 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_PSYS_H -+#define IPU7_FW_PSYS_H -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+ -+#include "ipu7-syscom.h" -+ -+#include -+ -+#define IPU_PSYS_MAX_GRAPH_NUMS (8U) -+#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS -+ -+struct ipu7_msg_to_str { -+ const enum ipu7_msg_type type; -+ const char *msg; -+}; -+ -+struct ipu7_psys; -+struct ipu7_psys_stream; -+struct ipu_psys_task_queue; -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys); -+void ipu7_fw_psys_release(struct ipu7_psys *psys); -+int ipu7_fw_psys_open(struct ipu7_psys *psys); -+void ipu7_fw_psys_close(struct ipu7_psys *psys); -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip); -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys); -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys); -+#endif /* IPU7_FW_PSYS_H */ -diff --git a/drivers/staging/media/ipu7/psys/ipu7-psys.c b/drivers/staging/media/ipu7/psys/ipu7-psys.c -new file mode 100644 -index 000000000000..1ce52ae75a4c ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-psys.c -@@ -0,0 +1,398 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2020 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-fw-psys.h" -+ -+#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+static const struct ipu7_msg_to_str ps_fw_msg[] = { -+ {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, -+ {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, -+ {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, -+ {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, -+ {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, -+ {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, -+ {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, -+ {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, -+ {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, -+}; -+ -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_device *isp = psys->adev->isp; -+ void __iomem *base = isp->base; -+ u32 mask; -+ u32 val; -+ u32 req; -+ int ret; -+ -+ /* power domain req */ -+ mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : -+ IPU7_PSYS_DOMAIN_POWER_MASK; -+ req = on ? mask : 0; -+ val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); -+ -+ dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+ if ((val & mask) == req) { -+ dev_warn(dev, -+ "psys sub-domains power already in request state.\n"); -+ return; -+ } -+ writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); -+ ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, -+ val, -+ !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && -+ ((val & mask) == req), -+ 100, DOMAIN_POWE_TIMEOUT_US); -+ if (ret) -+ dev_err(dev, -+ "Psys sub-domains power %s timeout! status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+} -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys) -+{ -+ void __iomem *base = psys->pdata->base; -+ u32 val = IRQ_FROM_LOCAL_FW; -+ -+ /* Enable TO_SW IRQ from FW */ -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ -+ /* correct the initial printf configuration */ -+ writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -+} -+ -+static struct ipu7_psys_stream* -+ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) -+{ -+ struct ipu7_psys_fh *fh; -+ -+ list_for_each_entry(fh, &psys->fhs, list) { -+ if (fh->ip->graph_id == graph_id) -+ return fh->ip; -+ } -+ -+ return NULL; -+} -+ -+static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, -+ const void *buffer) -+{ -+ const struct ipu7_msg_graph_open_ack *ack_msg = -+ (const struct ipu7_msg_graph_open_ack *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ struct ipu7_psys_stream *ip; -+ struct device *dev = &psys->dev; -+ const struct ia_gofo_tlv_header *msg_tlv_item; -+ u16 num_items; -+ u16 head_offset; -+ u32 i; -+ -+ dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto open_graph_exit; -+ } -+ -+ num_items = ack_header->header.msg_options.num_elems; -+ if (!num_items) { -+ dev_err(dev, "%s, num_items is 0\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ head_offset = ack_header->header.msg_options.head_offset; -+ msg_tlv_item = (const struct ia_gofo_tlv_header *) -+ ((u8 *)&ack_header->header.msg_options + head_offset); -+ -+ if (!msg_tlv_item) { -+ dev_err(dev, "%s: failed to get tlv item\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ for (i = 0U; i < num_items; i++) { -+ u32 option_type = msg_tlv_item->tlv_type; -+ u32 option_bytes = msg_tlv_item->tlv_len32 * -+ TLV_ITEM_ALIGNMENT; -+ struct ipu7_msg_graph_open_ack_task_q_info *msg_option = -+ (void *)msg_tlv_item; -+ -+ switch (option_type) { -+ case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: -+ /* -+ * Should do check that: -+ * - Each managed node has a queue ID -+ * - Queue ID's are sane -+ */ -+ dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", -+ msg_option->node_ctx_id, msg_option->q_id); -+ ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; -+ break; -+ default: -+ /* -+ * Only one option supported -+ */ -+ dev_err(dev, "not supported %u\n", option_type); -+ break; -+ } -+ -+ msg_tlv_item = (struct ia_gofo_tlv_header *) -+ (((u8 *)msg_tlv_item) + option_bytes); -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; -+ -+open_graph_exit: -+ complete(&ip->graph_open); -+} -+ -+static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, -+ void *buffer, u32 error) -+{ -+ const struct ipu7_msg_task_done *ack_msg = -+ (const struct ipu7_msg_task_done *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ const struct ia_gofo_msg_header *msg_header = &ack_header->header; -+ struct ipu_psys_task_queue *tq; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ struct ipu_psys_task_ack *event; -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ tq = (void *)(uintptr_t)msg_header->user_token; -+ if (!tq) { -+ dev_err(dev, "%s: task_token is NULL\n", __func__); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { -+ dev_err(dev, "%s: graph %d node %d error %d\n", __func__, -+ ack_msg->graph_id, ack_msg->node_ctx_id, -+ tq->task_state); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ return -ENOENT; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_DONE; -+ dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->event_list)) { -+ event = list_first_entry(&ip->event_list, -+ struct ipu_psys_task_ack, list); -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_move_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_dbg(dev, "event queue is full, add new one\n"); -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ -+ if (event) { -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_add_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_err(dev, "failed to alloc event buf\n"); -+ } -+ } -+ mutex_unlock(&ip->event_mutex); -+ -+ wake_up_interruptible(&ip->fh->wait); -+ -+ return 0; -+} -+ -+static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, -+ void *buffer) -+{ -+ struct ipu7_msg_graph_close_ack *ack_msg = -+ (struct ipu7_msg_graph_close_ack *)buffer; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ -+ dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto graph_close_exit; -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ -+graph_close_exit: -+ complete(&ip->graph_close); -+} -+ -+void ipu7_psys_handle_events(struct ipu7_psys *psys) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ struct device *dev = &psys->dev; -+ u32 error = 0; -+ int ret = 0; -+ -+ do { -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_psys_get_log(psys); -+#endif -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ break; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, -+ ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); -+ -+ if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { -+ dev_err(dev, "group %d, code %d, detail: %d, %d\n", -+ ack_header->err.err_group, -+ ack_header->err.err_code, -+ ack_header->err.err_detail[0], -+ ack_header->err.err_detail[1]); -+ error = (ack_header->err.err_group == -+ IPU_MSG_ERR_GROUP_TASK) ? -+ IPU_PSYS_EVT_ERROR_FRAME : -+ IPU_PSYS_EVT_ERROR_INTERNAL; -+ } -+ -+ switch (ack_header->header.tlv_header.tlv_type) { -+ case IPU_MSG_TYPE_GRAPH_OPEN_ACK: -+ ipu7_psys_handle_graph_open_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_TASK_DONE: -+ ipu7_psys_handle_task_done(psys, buffer, error); -+ break; -+ case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: -+ ipu7_psys_handle_graph_close_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_GENERAL_ERR: -+ /* already printed the log above for general error */ -+ break; -+ default: -+ dev_err(&psys->dev, "Unknown type %d\n", -+ ack_header->header.tlv_header.tlv_type); -+ } -+ } while (1); -+} -+ -+static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, -+ struct ipu_psys_event *event) -+{ -+ struct ipu_psys_task_ack *ack; -+ -+ mutex_lock(&ip->event_mutex); -+ /* Check if there is already an event in the list */ -+ if (list_empty(&ip->ack_list)) { -+ mutex_unlock(&ip->event_mutex); -+ return -EAGAIN; -+ } -+ -+ ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); -+ -+ event->graph_id = ack->graph_id; -+ event->node_ctx_id = ack->node_ctx_id; -+ event->frame_id = ack->frame_id; -+ event->error = ack->err_code; -+ -+ list_move_tail(&ack->list, &ip->event_list); -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", -+ event->graph_id, event->node_ctx_id, event->frame_id); -+ -+ return 0; -+} -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); -+ -+ if (!(f_flags & O_NONBLOCK)) { -+ ret = wait_event_interruptible(fh->wait, -+ !ipu7_psys_get_event(fh->ip, -+ event)); -+ if (ret == -ERESTARTSYS) -+ return ret; -+ } else { -+ ret = ipu7_psys_get_event(fh->ip, event); -+ } -+ -+ return ret; -+} -diff --git a/drivers/staging/media/ipu7/psys/ipu7-psys.h b/drivers/staging/media/ipu7/psys/ipu7-psys.h -new file mode 100644 -index 000000000000..a72ce4a7c5b9 ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-psys.h -@@ -0,0 +1,184 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2013 - 2024 Intel Corporation */ -+ -+#ifndef IPU7_PSYS_H -+#define IPU7_PSYS_H -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-fw-psys.h" -+ -+#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq -+ -+#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 -+#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 -+#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 -+#define IPU_PSYS_LOG_QUEUE_SIZE 256 -+#define IPU_PSYS_OUT_MSG_SIZE 256 -+ -+/** -+ * Each event from FW will be first queued into a -+ * event queue, define the queue depth here -+ */ -+#define TASK_EVENT_QUEUE_SIZE 3U -+/** -+ * Each task queue from user will be first queued into -+ * a task queue, define the queue depth here -+ */ -+#define TASK_REQUEST_QUEUE_SIZE 8U -+ -+#define INVALID_STREAM_ID 0xFF -+/** -+ * Task request queues per stream -+ * -+ * Each task will first assigned a task queue buffer here, -+ * all the nodes will share the same task queue, maximum -+ * queue will be full there. -+ */ -+struct ipu_psys_task_queue { -+ struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; -+ dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; -+ -+ struct ipu7_msg_task *msg_task; -+ dma_addr_t task_dma_addr; -+ -+ struct list_head list; -+ -+ /* task state of each task input, represent ipu7_msg_task_state */ -+ enum ipu7_msg_task_state task_state; -+}; -+ -+struct psys_fw_log { -+ struct mutex mutex; /* protect whole struct */ -+ void *head; -+ void *addr; -+ u32 count; /* running counter of log */ -+ u32 size; /* actual size of log content, in bits */ -+}; -+ -+/** -+ * Task quest event context -+ * -+ * Each task request should get its event ack from FW and save -+ * to this structure and for user dequeue purpose. -+ */ -+struct ipu_psys_task_ack { -+ u8 graph_id; /* graph id of the task request */ -+ u8 node_ctx_id; /* logical node id */ -+ u8 frame_id; /* frame id of the original task request */ -+ -+ struct list_head list; -+ -+ u32 err_code; /* error indication to user */ -+}; -+ -+/** -+ * stream here is equal to pipe, each stream has -+ * its dedicated graph_id, and task request queue. -+ * -+ * For multiple stream supported design. -+ */ -+struct ipu7_psys_stream { -+ struct ipu7_psys_fh *fh; -+ -+ u8 graph_id; /* graph_id on this stream */ -+ -+ /* Handle events from FW */ -+ struct mutex event_mutex; -+ struct list_head event_list; /* Reserved event list */ -+ struct list_head ack_list; /* Received ack from FW */ -+ -+ /* Serialize task queue */ -+ struct mutex task_mutex; -+ struct list_head tq_list; /* Reserved task queue list */ -+ struct list_head tq_running_list; /* Running task sent to FW */ -+ -+ u8 num_nodes; /* Number of enabled nodes */ -+ struct graph_node nodes[MAX_GRAPH_NODES]; -+ u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ -+ -+ struct completion graph_open; -+ struct completion graph_close; -+ -+ /* Graph state, represent enum ipu7_msg_graph_state */ -+ enum ipu7_msg_graph_state graph_state; -+}; -+ -+struct task_struct; -+struct ipu7_psys { -+ struct cdev cdev; -+ struct device dev; -+ -+ struct mutex mutex; /* Psys various */ -+ int ready; /* psys fw status */ -+ spinlock_t ready_lock; /* protect psys firmware state */ -+ -+ struct list_head fhs; -+ -+ struct ipu7_psys_pdata *pdata; -+ struct ipu7_bus_device *adev; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif -+ -+ unsigned long timeout; -+ -+ struct psys_fw_log *fw_log; -+ -+ /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ -+ u8 graph_id[IPU_PSYS_NUM_STREAMS]; -+ -+ /* Device state, represent enum ipu7_msg_dev_state */ -+ enum ipu7_msg_dev_state dev_state; -+ -+ struct ipu7_psys_config *subsys_config; -+ dma_addr_t subsys_config_dma_addr; -+}; -+ -+struct ipu7_psys_fh { -+ struct ipu7_psys *psys; -+ struct mutex mutex; /* Protects bufmap & kcmds fields */ -+ struct list_head list; -+ struct list_head bufmap; -+ wait_queue_head_t wait; -+ -+ struct ipu7_psys_stream *ip; -+}; -+ -+struct ipu7_dma_buf_attach { -+ struct device *dev; -+ u64 len; -+ uintptr_t *userptr; -+ struct sg_table *sgt; -+ struct page **pages; -+ size_t npages; -+}; -+ -+struct ipu7_psys_kbuffer { -+ u64 len; -+ uintptr_t *userptr; -+ u32 flags; -+ int fd; -+ void *kaddr; -+ struct list_head list; -+ dma_addr_t dma_addr; -+ struct sg_table *sgt; -+ struct dma_buf_attachment *db_attach; -+ struct dma_buf *dbuf; -+ bool valid; /* True when buffer is usable */ -+}; -+ -+#define inode_to_ipu_psys(inode) \ -+ container_of((inode)->i_cdev, struct ipu7_psys, cdev) -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys); -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); -+void ipu7_psys_handle_events(struct ipu7_psys *psys); -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags); -+ -+#endif /* IPU7_PSYS_H */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet b/SPECS/kernel-rt/0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet deleted file mode 100644 index 9195d874f..000000000 --- a/SPECS/kernel-rt/0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet +++ /dev/null @@ -1,116 +0,0 @@ -From 251c9e433daa663f7f6014c26cc4a1bf2ed1f272 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:48 -0700 -Subject: [PATCH 18/24] KVM: VMX: Set host constant supervisor states to VMCS - fields - -Save constant values to HOST_{S_CET,SSP,INTR_SSP_TABLE} field explicitly. -Kernel IBT is supported and the setting in MSR_IA32_S_CET is static after -post-boot(The exception is BIOS call case but vCPU thread never across it) -and KVM doesn't need to refresh HOST_S_CET field before every VM-Enter/ -VM-Exit sequence. - -Host supervisor shadow stack is not enabled now and SSP is not accessible -to kernel mode, thus it's safe to set host IA32_INT_SSP_TAB/SSP VMCS field -to 0s. When shadow stack is enabled for CPL3, SSP is reloaded from PL3_SSP -before it exits to userspace. Check SDM Vol 2A/B Chapter 3/4 for SYSCALL/ -SYSRET/SYSENTER SYSEXIT/RDSSP/CALL etc. - -Prevent KVM module loading if host supervisor shadow stack SHSTK_EN is set -in MSR_IA32_S_CET as KVM cannot co-exit with it correctly. - -Suggested-by: Sean Christopherson -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/capabilities.h | 5 +++++ - arch/x86/kvm/vmx/vmx.c | 15 +++++++++++++++ - arch/x86/kvm/x86.c | 12 ++++++++++++ - arch/x86/kvm/x86.h | 1 + - 4 files changed, 33 insertions(+) - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 874c6dd34665..fd1d43ebe1df 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -105,6 +105,11 @@ static inline bool cpu_has_save_perf_global_ctrl(void) - return vmcs_config.vmexit_ctrl & VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL; - } - -+static inline bool cpu_has_load_cet_ctrl(void) -+{ -+ return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_CET_STATE); -+} -+ - static inline bool cpu_has_vmx_mpx(void) - { - return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index ca10c1b142dc..9393abf9f183 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4371,6 +4371,21 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) - if (cpu_has_load_ia32_efer()) - vmcs_write64(HOST_IA32_EFER, kvm_host.efer); - -+ /* -+ * Supervisor shadow stack is not enabled on host side, i.e., -+ * host IA32_S_CET.SHSTK_EN bit is guaranteed to 0 now, per SDM -+ * description(RDSSP instruction), SSP is not readable in CPL0, -+ * so resetting the two registers to 0s at VM-Exit does no harm -+ * to kernel execution. When execution flow exits to userspace, -+ * SSP is reloaded from IA32_PL3_SSP. Check SDM Vol.2A/B Chapter -+ * 3 and 4 for details. -+ */ -+ if (cpu_has_load_cet_ctrl()) { -+ vmcs_writel(HOST_S_CET, kvm_host.s_cet); -+ vmcs_writel(HOST_SSP, 0); -+ vmcs_writel(HOST_INTR_SSP_TABLE, 0); -+ } -+ - /* - * When running a guest with a mediated PMU, guest state is resident in - * hardware after VM-Exit. Zero PERF_GLOBAL_CTRL on exit so that host -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 42e4fac999de..83fd91494a4b 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9827,6 +9827,18 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - return -EIO; - } - -+ if (boot_cpu_has(X86_FEATURE_SHSTK)) { -+ rdmsrl(MSR_IA32_S_CET, kvm_host.s_cet); -+ /* -+ * Linux doesn't yet support supervisor shadow stacks (SSS), so -+ * KVM doesn't save/restore the associated MSRs, i.e. KVM may -+ * clobber the host values. Yell and refuse to load if SSS is -+ * unexpectedly enabled, e.g. to avoid crashing the host. -+ */ -+ if (WARN_ON_ONCE(kvm_host.s_cet & CET_SHSTK_EN)) -+ return -EIO; -+ } -+ - memset(&kvm_caps, 0, sizeof(kvm_caps)); - - x86_emulator_cache = kvm_alloc_emulator_cache(); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index b54ec7d37f1f..1f631b3459e8 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -50,6 +50,7 @@ struct kvm_host_values { - u64 efer; - u64 xcr0; - u64 xss; -+ u64 s_cet; - u64 arch_capabilities; - }; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi b/SPECS/kernel-rt/0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi deleted file mode 100644 index 4dd7e492d..000000000 --- a/SPECS/kernel-rt/0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi +++ /dev/null @@ -1,516 +0,0 @@ -From 3961eaba21b1ab7dc2bf9e82c577d5b62a44bf5c Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 22:45:34 -0700 -Subject: [PATCH 18/44] KVM: nVMX: Add FRED VMCS fields to nested VMX context - handling - -Extend nested VMX context management to include FRED-related VMCS fields. -This enables proper handling of FRED state during nested virtualization. - -Because KVM always sets SECONDARY_VM_EXIT_SAVE_IA32_FRED, FRED MSRs are -always saved to vmcs02. However an L1 VMM may choose to clear this bit, -i.e., not to save FRED MSRs to vmcs12. This is not a problem when the L1 -VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, as KVM then immediately loads -host FRED MSRs of vmcs12 to guest FRED MSRs of vmcs01. However if the L1 -VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should retain FRED MSRs -to run the L1 VMM. - -To propagate guest FRED MSRs from vmcs02 to vmcs01, save them in -sync_vmcs02_to_vmcs12() regardless of whether -SECONDARY_VM_EXIT_SAVE_IA32_FRED is set in vmcs12. Then, use the saved -values to set guest FRED MSRs in vmcs01 within load_vmcs12_host_state() -when !nested_cpu_load_host_fred_state(). - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v6: -* Handle FRED MSR pre-vmenter save/restore (Chao Gao). -* Save FRED MSRs of vmcs02 at VM-Exit even an L1 VMM clears - SECONDARY_VM_EXIT_SAVE_IA32_FRED. -* Save FRED MSRs in sync_vmcs02_to_vmcs12() instead of its rare version. - -Change in v5: -* Add TB from Xuelian Guo. - -Changes in v4: -* Advertise VMX nested exception as if the CPU supports it (Chao Gao). -* Split FRED state management controls (Chao Gao). - -Changes in v3: -* Add and use nested_cpu_has_fred(vmcs12) because vmcs02 should be set - from vmcs12 if and only if the field is enabled in L1's VMX config - (Sean Christopherson). -* Fix coding style issues (Sean Christopherson). - -Changes in v2: -* Remove hyperv TLFS related changes (Jeremi Piotrowski). -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). ---- - Documentation/virt/kvm/x86/nested-vmx.rst | 18 ++++ - arch/x86/kvm/vmx/capabilities.h | 5 + - arch/x86/kvm/vmx/nested.c | 113 +++++++++++++++++++++- - arch/x86/kvm/vmx/nested.h | 22 +++++ - arch/x86/kvm/vmx/vmcs12.c | 18 ++++ - arch/x86/kvm/vmx/vmcs12.h | 36 +++++++ - arch/x86/kvm/vmx/vmcs_shadow_fields.h | 4 + - arch/x86/kvm/vmx/vmx.h | 41 ++++++++ - 8 files changed, 255 insertions(+), 2 deletions(-) - -diff --git a/Documentation/virt/kvm/x86/nested-vmx.rst b/Documentation/virt/kvm/x86/nested-vmx.rst -index e64ef231f310..87fa9f3877ab 100644 ---- a/Documentation/virt/kvm/x86/nested-vmx.rst -+++ b/Documentation/virt/kvm/x86/nested-vmx.rst -@@ -218,6 +218,24 @@ struct shadow_vmcs is ever changed. - u16 host_gs_selector; - u16 host_tr_selector; - u64 secondary_vm_exit_controls; -+ u64 guest_ia32_fred_config; -+ u64 guest_ia32_fred_rsp1; -+ u64 guest_ia32_fred_rsp2; -+ u64 guest_ia32_fred_rsp3; -+ u64 guest_ia32_fred_stklvls; -+ u64 guest_ia32_fred_ssp1; -+ u64 guest_ia32_fred_ssp2; -+ u64 guest_ia32_fred_ssp3; -+ u64 host_ia32_fred_config; -+ u64 host_ia32_fred_rsp1; -+ u64 host_ia32_fred_rsp2; -+ u64 host_ia32_fred_rsp3; -+ u64 host_ia32_fred_stklvls; -+ u64 host_ia32_fred_ssp1; -+ u64 host_ia32_fred_ssp2; -+ u64 host_ia32_fred_ssp3; -+ u64 injected_event_data; -+ u64 original_event_data; - }; - - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index ebf4cbd2df70..3cd66020c926 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -80,6 +80,11 @@ static inline bool cpu_has_vmx_basic_no_hw_errcode(void) - return vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; - } - -+static inline bool cpu_has_vmx_nested_exception(void) -+{ -+ return vmcs_config.basic & VMX_BASIC_NESTED_EXCEPTION; -+} -+ - static inline bool cpu_has_virtual_nmis(void) - { - return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS && -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 611ccff1dc90..7b3cd9b6f7c0 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -741,6 +741,9 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - nested_vmx_merge_msr_bitmaps_rw(MSR_FS_BASE); - nested_vmx_merge_msr_bitmaps_rw(MSR_GS_BASE); - nested_vmx_merge_msr_bitmaps_rw(MSR_KERNEL_GS_BASE); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_FRED_RSP0, MSR_TYPE_RW); - #endif - nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_SPEC_CTRL); - nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD); -@@ -1327,9 +1330,11 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) - const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT | - VMX_BASIC_INOUT | - VMX_BASIC_TRUE_CTLS | -- VMX_BASIC_NO_HW_ERROR_CODE_CC; -+ VMX_BASIC_NO_HW_ERROR_CODE_CC | -+ VMX_BASIC_NESTED_EXCEPTION; - -- const u64 reserved_bits = GENMASK_ULL(63, 57) | -+ const u64 reserved_bits = GENMASK_ULL(63, 59) | -+ BIT_ULL(57) | - GENMASK_ULL(47, 45) | - BIT_ULL(31); - -@@ -2581,6 +2586,8 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 - vmcs12->vm_entry_instruction_len); - vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, - vmcs12->guest_interruptibility_info); -+ if (cpu_has_vmx_fred()) -+ vmcs_write64(INJECTED_EVENT_DATA, vmcs12->injected_event_data); - vmx->loaded_vmcs->nmi_known_unmasked = - !(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_NMI); - } else { -@@ -2735,6 +2742,17 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) - vmcs12->guest_ssp, vmcs12->guest_ssp_tbl); - - set_cr4_guest_host_mask(vmx); -+ -+ if (nested_cpu_load_guest_fred_state(vmcs12)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->guest_ia32_fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->guest_ia32_fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->guest_ia32_fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->guest_ia32_fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->guest_ia32_fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->guest_ia32_fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->guest_ia32_fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->guest_ia32_fred_ssp3); -+ } - } - - /* -@@ -2801,6 +2819,18 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat); - } - -+ if (!vmx->nested.nested_run_pending || -+ !nested_cpu_load_guest_fred_state(vmcs12)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.pre_vmenter_fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.pre_vmenter_fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.pre_vmenter_fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.pre_vmenter_fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.pre_vmenter_fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.pre_vmenter_fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.pre_vmenter_fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.pre_vmenter_fred_ssp3); -+ } -+ - vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset( - vcpu->arch.l1_tsc_offset, - vmx_get_l2_tsc_offset(vcpu), -@@ -3715,6 +3745,18 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, - &vmx->nested.pre_vmenter_ssp, - &vmx->nested.pre_vmenter_ssp_tbl); - -+ if (!vmx->nested.nested_run_pending || -+ !nested_cpu_load_guest_fred_state(vmcs12)) { -+ vmx->nested.pre_vmenter_fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); -+ vmx->nested.pre_vmenter_fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); -+ vmx->nested.pre_vmenter_fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); -+ vmx->nested.pre_vmenter_fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); -+ vmx->nested.pre_vmenter_fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); -+ vmx->nested.pre_vmenter_fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); -+ vmx->nested.pre_vmenter_fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); -+ vmx->nested.pre_vmenter_fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); -+ } -+ - /* - * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* - * nested early checks are disabled. In the event of a "late" VM-Fail, -@@ -4022,6 +4064,8 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, - u32 idt_vectoring; - unsigned int nr; - -+ vmcs12->original_event_data = 0; -+ - /* - * Per the SDM, VM-Exits due to double and triple faults are never - * considered to occur during event delivery, even if the double/triple -@@ -4060,6 +4104,13 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, - vcpu->arch.exception.error_code; - } - -+ if ((vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && -+ (vmcs12->guest_cr4 & X86_CR4_FRED) && -+ (vcpu->arch.exception.nested)) -+ idt_vectoring |= VECTORING_INFO_NESTED_EXCEPTION_MASK; -+ -+ vmcs12->original_event_data = vcpu->arch.exception.event_data; -+ - vmcs12->idt_vectoring_info_field = idt_vectoring; - } else if (vcpu->arch.nmi_injected) { - vmcs12->idt_vectoring_info_field = -@@ -4805,6 +4856,26 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) - - if (vmcs12->vm_exit_controls & VM_EXIT_SAVE_IA32_EFER) - vmcs12->guest_ia32_efer = vcpu->arch.efer; -+ -+ vmx->nested.fred_msr_at_vmexit.fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); -+ vmx->nested.fred_msr_at_vmexit.fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); -+ vmx->nested.fred_msr_at_vmexit.fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); -+ vmx->nested.fred_msr_at_vmexit.fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); -+ vmx->nested.fred_msr_at_vmexit.fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); -+ vmx->nested.fred_msr_at_vmexit.fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); -+ vmx->nested.fred_msr_at_vmexit.fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); -+ vmx->nested.fred_msr_at_vmexit.fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); -+ -+ if (nested_cpu_save_guest_fred_state(vmcs12)) { -+ vmcs12->guest_ia32_fred_config = vmx->nested.fred_msr_at_vmexit.fred_config; -+ vmcs12->guest_ia32_fred_rsp1 = vmx->nested.fred_msr_at_vmexit.fred_rsp1; -+ vmcs12->guest_ia32_fred_rsp2 = vmx->nested.fred_msr_at_vmexit.fred_rsp2; -+ vmcs12->guest_ia32_fred_rsp3 = vmx->nested.fred_msr_at_vmexit.fred_rsp3; -+ vmcs12->guest_ia32_fred_stklvls = vmx->nested.fred_msr_at_vmexit.fred_stklvls; -+ vmcs12->guest_ia32_fred_ssp1 = vmx->nested.fred_msr_at_vmexit.fred_ssp1; -+ vmcs12->guest_ia32_fred_ssp2 = vmx->nested.fred_msr_at_vmexit.fred_ssp2; -+ vmcs12->guest_ia32_fred_ssp3 = vmx->nested.fred_msr_at_vmexit.fred_ssp3; -+ } - } - - /* -@@ -4849,6 +4920,21 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - - vmcs12->vm_exit_intr_info = exit_intr_info; - vmcs12->vm_exit_instruction_len = exit_insn_len; -+ -+ /* -+ * When there is a valid original event, the exiting event is a nested -+ * event during delivery of the earlier original event. -+ * -+ * FRED event delivery reflects this relationship by setting the value -+ * of the nested exception bit of VM-exit interruption information -+ * (aka exiting-event identification) to that of the valid bit of the -+ * IDT-vectoring information (aka original-event identification). -+ */ -+ if ((vmcs12->idt_vectoring_info_field & VECTORING_INFO_VALID_MASK) && -+ (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && -+ (vmcs12->guest_cr4 & X86_CR4_FRED)) -+ vmcs12->vm_exit_intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; -+ - vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO); - - /* -@@ -4877,6 +4963,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) - { -+ struct vcpu_vmx *vmx = to_vmx(vcpu); - enum vm_entry_failure_code ignored; - struct kvm_segment seg; - -@@ -4943,6 +5030,26 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, - vmcs12->host_ia32_perf_global_ctrl)); - -+ if (nested_cpu_load_host_fred_state(vmcs12)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->host_ia32_fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->host_ia32_fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->host_ia32_fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->host_ia32_fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->host_ia32_fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->host_ia32_fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->host_ia32_fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->host_ia32_fred_ssp3); -+ } else { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.fred_msr_at_vmexit.fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.fred_msr_at_vmexit.fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.fred_msr_at_vmexit.fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.fred_msr_at_vmexit.fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.fred_msr_at_vmexit.fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.fred_msr_at_vmexit.fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.fred_msr_at_vmexit.fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.fred_msr_at_vmexit.fred_ssp3); -+ } -+ - /* Set L1 segment info according to Intel SDM - 27.5.2 Loading Host Segment and Descriptor-Table Registers */ - seg = (struct kvm_segment) { -@@ -7401,6 +7508,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs) - msrs->basic |= VMX_BASIC_INOUT; - if (cpu_has_vmx_basic_no_hw_errcode()) - msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC; -+ if (cpu_has_vmx_nested_exception()) -+ msrs->basic |= VMX_BASIC_NESTED_EXCEPTION; - } - - static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs) -diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h -index 983484d42ebf..a99d3d83d58e 100644 ---- a/arch/x86/kvm/vmx/nested.h -+++ b/arch/x86/kvm/vmx/nested.h -@@ -249,6 +249,11 @@ static inline bool nested_cpu_has_save_preemption_timer(struct vmcs12 *vmcs12) - VM_EXIT_SAVE_VMX_PREEMPTION_TIMER; - } - -+static inline bool nested_cpu_has_secondary_vm_exit_controls(struct vmcs12 *vmcs12) -+{ -+ return vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; -+} -+ - static inline bool nested_exit_on_nmi(struct kvm_vcpu *vcpu) - { - return nested_cpu_has_nmi_exiting(get_vmcs12(vcpu)); -@@ -269,6 +274,23 @@ static inline bool nested_cpu_has_encls_exit(struct vmcs12 *vmcs12) - return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENCLS_EXITING); - } - -+static inline bool nested_cpu_load_guest_fred_state(struct vmcs12 *vmcs12) -+{ -+ return vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED; -+} -+ -+static inline bool nested_cpu_save_guest_fred_state(struct vmcs12 *vmcs12) -+{ -+ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && -+ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_SAVE_IA32_FRED; -+} -+ -+static inline bool nested_cpu_load_host_fred_state(struct vmcs12 *vmcs12) -+{ -+ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && -+ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED; -+} -+ - /* - * if fixed0[i] == 1: val[i] must be 1 - * if fixed1[i] == 0: val[i] must be 0 -diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c -index 3b01175f392a..9691e709061f 100644 ---- a/arch/x86/kvm/vmx/vmcs12.c -+++ b/arch/x86/kvm/vmx/vmcs12.c -@@ -67,6 +67,24 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD64(HOST_IA32_EFER, host_ia32_efer), - FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), - FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), -+ FIELD64(INJECTED_EVENT_DATA, injected_event_data), -+ FIELD64(ORIGINAL_EVENT_DATA, original_event_data), -+ FIELD64(GUEST_IA32_FRED_CONFIG, guest_ia32_fred_config), -+ FIELD64(GUEST_IA32_FRED_RSP1, guest_ia32_fred_rsp1), -+ FIELD64(GUEST_IA32_FRED_RSP2, guest_ia32_fred_rsp2), -+ FIELD64(GUEST_IA32_FRED_RSP3, guest_ia32_fred_rsp3), -+ FIELD64(GUEST_IA32_FRED_STKLVLS, guest_ia32_fred_stklvls), -+ FIELD64(GUEST_IA32_FRED_SSP1, guest_ia32_fred_ssp1), -+ FIELD64(GUEST_IA32_FRED_SSP2, guest_ia32_fred_ssp2), -+ FIELD64(GUEST_IA32_FRED_SSP3, guest_ia32_fred_ssp3), -+ FIELD64(HOST_IA32_FRED_CONFIG, host_ia32_fred_config), -+ FIELD64(HOST_IA32_FRED_RSP1, host_ia32_fred_rsp1), -+ FIELD64(HOST_IA32_FRED_RSP2, host_ia32_fred_rsp2), -+ FIELD64(HOST_IA32_FRED_RSP3, host_ia32_fred_rsp3), -+ FIELD64(HOST_IA32_FRED_STKLVLS, host_ia32_fred_stklvls), -+ FIELD64(HOST_IA32_FRED_SSP1, host_ia32_fred_ssp1), -+ FIELD64(HOST_IA32_FRED_SSP2, host_ia32_fred_ssp2), -+ FIELD64(HOST_IA32_FRED_SSP3, host_ia32_fred_ssp3), - FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), - FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), - FIELD(EXCEPTION_BITMAP, exception_bitmap), -diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h -index 7866fdce7a23..a3853536a575 100644 ---- a/arch/x86/kvm/vmx/vmcs12.h -+++ b/arch/x86/kvm/vmx/vmcs12.h -@@ -192,6 +192,24 @@ struct __packed vmcs12 { - u16 host_tr_selector; - u16 guest_pml_index; - u64 secondary_vm_exit_controls; -+ u64 guest_ia32_fred_config; -+ u64 guest_ia32_fred_rsp1; -+ u64 guest_ia32_fred_rsp2; -+ u64 guest_ia32_fred_rsp3; -+ u64 guest_ia32_fred_stklvls; -+ u64 guest_ia32_fred_ssp1; -+ u64 guest_ia32_fred_ssp2; -+ u64 guest_ia32_fred_ssp3; -+ u64 host_ia32_fred_config; -+ u64 host_ia32_fred_rsp1; -+ u64 host_ia32_fred_rsp2; -+ u64 host_ia32_fred_rsp3; -+ u64 host_ia32_fred_stklvls; -+ u64 host_ia32_fred_ssp1; -+ u64 host_ia32_fred_ssp2; -+ u64 host_ia32_fred_ssp3; -+ u64 injected_event_data; -+ u64 original_event_data; - }; - - /* -@@ -374,6 +392,24 @@ static inline void vmx_check_vmcs12_offsets(void) - CHECK_OFFSET(host_tr_selector, 994); - CHECK_OFFSET(guest_pml_index, 996); - CHECK_OFFSET(secondary_vm_exit_controls, 998); -+ CHECK_OFFSET(guest_ia32_fred_config, 1006); -+ CHECK_OFFSET(guest_ia32_fred_rsp1, 1014); -+ CHECK_OFFSET(guest_ia32_fred_rsp2, 1022); -+ CHECK_OFFSET(guest_ia32_fred_rsp3, 1030); -+ CHECK_OFFSET(guest_ia32_fred_stklvls, 1038); -+ CHECK_OFFSET(guest_ia32_fred_ssp1, 1046); -+ CHECK_OFFSET(guest_ia32_fred_ssp2, 1054); -+ CHECK_OFFSET(guest_ia32_fred_ssp3, 1062); -+ CHECK_OFFSET(host_ia32_fred_config, 1070); -+ CHECK_OFFSET(host_ia32_fred_rsp1, 1078); -+ CHECK_OFFSET(host_ia32_fred_rsp2, 1086); -+ CHECK_OFFSET(host_ia32_fred_rsp3, 1094); -+ CHECK_OFFSET(host_ia32_fred_stklvls, 1102); -+ CHECK_OFFSET(host_ia32_fred_ssp1, 1110); -+ CHECK_OFFSET(host_ia32_fred_ssp2, 1118); -+ CHECK_OFFSET(host_ia32_fred_ssp3, 1126); -+ CHECK_OFFSET(injected_event_data, 1134); -+ CHECK_OFFSET(original_event_data, 1142); - } - - extern const unsigned short vmcs12_field_offsets[]; -diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -index cad128d1657b..da338327c2b3 100644 ---- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h -+++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -@@ -74,6 +74,10 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) - /* 64-bit */ - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) -+SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) -+SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) -+SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) -+SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) - - #undef SHADOW_FIELD_RO - #undef SHADOW_FIELD_RW -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 05fa52a7581c..d0adb0f31d03 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -67,6 +67,37 @@ struct pt_desc { - struct pt_ctx guest; - }; - -+/* -+ * Used to snapshot FRED MSRs that may NOT be saved to vmcs12 as specified -+ * in the VM-Exit controls of vmcs12 configured by L1 VMM. -+ * -+ * FRED MSRs are *always* saved into vmcs02 because KVM always sets -+ * SECONDARY_VM_EXIT_SAVE_IA32_FRED. However an L1 VMM may choose to clear -+ * this bit, resulting in FRED MSRs not being propagated to vmcs12 from -+ * vmcs02. When the L1 VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, this is -+ * not a problem, since KVM then immediately loads the host FRED MSRs of -+ * vmcs12 to the guest FRED MSRs of vmcs01. -+ * -+ * But if the L1 VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should -+ * retain the FRED MSRs, i.e., propagate the guest FRED MSRs of vmcs02 to -+ * the guest FRED MSRs of vmcs01. -+ * -+ * This structure stores guest FRED MSRs that an L1 VMM opts not to save -+ * during VM-Exits from L2 to L1. These MSRs may still be retained for -+ * running the L1 VMM if SECONDARY_VM_EXIT_LOAD_IA32_FRED is cleared in -+ * vmcs12. -+ */ -+struct fred_msr_at_vmexit { -+ u64 fred_config; -+ u64 fred_rsp1; -+ u64 fred_rsp2; -+ u64 fred_rsp3; -+ u64 fred_stklvls; -+ u64 fred_ssp1; -+ u64 fred_ssp2; -+ u64 fred_ssp3; -+}; -+ - /* - * The nested_vmx structure is part of vcpu_vmx, and holds information we need - * for correct emulation of VMX (i.e., nested VMX) on this vcpu. -@@ -184,6 +215,16 @@ struct nested_vmx { - u64 pre_vmenter_s_cet; - u64 pre_vmenter_ssp; - u64 pre_vmenter_ssp_tbl; -+ u64 pre_vmenter_fred_config; -+ u64 pre_vmenter_fred_rsp1; -+ u64 pre_vmenter_fred_rsp2; -+ u64 pre_vmenter_fred_rsp3; -+ u64 pre_vmenter_fred_stklvls; -+ u64 pre_vmenter_fred_ssp1; -+ u64 pre_vmenter_fred_ssp2; -+ u64 pre_vmenter_fred_ssp3; -+ -+ struct fred_msr_at_vmexit fred_msr_at_vmexit; - - /* to migrate it to L1 if L2 writes to L1's CR8 directly */ - int l1_tpr_threshold; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi b/SPECS/kernel-rt/0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi new file mode 100644 index 000000000..bff5ccb9f --- /dev/null +++ b/SPECS/kernel-rt/0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi @@ -0,0 +1,162 @@ +From 9796175f56299f2cec3b9677aa71f4bd82f30ad4 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Aug 2022 09:58:24 -0700 +Subject: [PATCH 18/44] KVM: nVMX: Enable support for secondary VM exit + controls + +Add support for secondary VM exit controls in nested VMX to facilitate +future FRED integration. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v8: +* Relocate secondary_vm_exit_controls to the last u64 padding field. +* Remove the change to Documentation/virt/kvm/x86/nested-vmx.rst. + +Changes in v5: +* Allow writing MSR_IA32_VMX_EXIT_CTLS2 (Sean). +* Add TB from Xuelian Guo. + +Change in v3: +* Read secondary VM exit controls from vmcs_conf insteasd of the hardware + MSR MSR_IA32_VMX_EXIT_CTLS2 to avoid advertising features to L1 that KVM + itself doesn't support, e.g. because the expected entry+exit pairs aren't + supported. (Sean Christopherson) +--- + arch/x86/kvm/vmx/capabilities.h | 1 + + arch/x86/kvm/vmx/nested.c | 26 +++++++++++++++++++++++++- + arch/x86/kvm/vmx/vmcs12.c | 1 + + arch/x86/kvm/vmx/vmcs12.h | 3 ++- + arch/x86/kvm/x86.h | 2 +- + 5 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index 651507627ef32..f390f9f883c30 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -34,6 +34,7 @@ struct nested_vmx_msrs { + u32 pinbased_ctls_high; + u32 exit_ctls_low; + u32 exit_ctls_high; ++ u64 secondary_exit_ctls; + u32 entry_ctls_low; + u32 entry_ctls_high; + u32 misc_low; +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index bcea087b642fd..b994f264acc0a 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -1531,6 +1531,11 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) + return -EINVAL; + vmx->nested.msrs.vmfunc_controls = data; + return 0; ++ case MSR_IA32_VMX_EXIT_CTLS2: ++ if (data & ~vmcs_config.nested.secondary_exit_ctls) ++ return -EINVAL; ++ vmx->nested.msrs.secondary_exit_ctls = data; ++ return 0; + default: + /* + * The rest of the VMX capability MSRs do not support restore. +@@ -1570,6 +1575,9 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) + if (msr_index == MSR_IA32_VMX_EXIT_CTLS) + *pdata |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR; + break; ++ case MSR_IA32_VMX_EXIT_CTLS2: ++ *pdata = msrs->secondary_exit_ctls; ++ break; + case MSR_IA32_VMX_TRUE_ENTRY_CTLS: + case MSR_IA32_VMX_ENTRY_CTLS: + *pdata = vmx_control_msr( +@@ -2520,6 +2528,11 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 + exec_control &= ~VM_EXIT_LOAD_IA32_EFER; + vm_exit_controls_set(vmx, exec_control); + ++ if (exec_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { ++ exec_control = __secondary_vm_exit_controls_get(vmcs01); ++ secondary_vm_exit_controls_set(vmx, exec_control); ++ } ++ + /* + * Interrupt/Exception Fields + */ +@@ -7187,7 +7200,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, + VM_EXIT_HOST_ADDR_SPACE_SIZE | + #endif + VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT | +- VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE; ++ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE | ++ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; + msrs->exit_ctls_high |= + VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | + VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | +@@ -7200,6 +7214,16 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, + + /* We support free control of debug control saving. */ + msrs->exit_ctls_low &= ~VM_EXIT_SAVE_DEBUG_CONTROLS; ++ ++ if (msrs->exit_ctls_high & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { ++ msrs->secondary_exit_ctls = vmcs_conf->vmexit_2nd_ctrl; ++ /* ++ * As the secondary VM exit control is always loaded, do not ++ * advertise any feature in it to nVMX until its nVMX support ++ * is ready. ++ */ ++ msrs->secondary_exit_ctls &= 0; ++ } + } + + static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, +diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c +index 4233b5ca9461a..3b01175f392ae 100644 +--- a/arch/x86/kvm/vmx/vmcs12.c ++++ b/arch/x86/kvm/vmx/vmcs12.c +@@ -66,6 +66,7 @@ const unsigned short vmcs12_field_offsets[] = { + FIELD64(HOST_IA32_PAT, host_ia32_pat), + FIELD64(HOST_IA32_EFER, host_ia32_efer), + FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), ++ FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), + FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), + FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), + FIELD(EXCEPTION_BITMAP, exception_bitmap), +diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h +index 4ad6b16525b93..fa5306dc0311f 100644 +--- a/arch/x86/kvm/vmx/vmcs12.h ++++ b/arch/x86/kvm/vmx/vmcs12.h +@@ -71,7 +71,7 @@ struct __packed vmcs12 { + u64 pml_address; + u64 encls_exiting_bitmap; + u64 tsc_multiplier; +- u64 padding64[1]; /* room for future expansion */ ++ u64 secondary_vm_exit_controls; + /* + * To allow migration of L1 (complete with its L2 guests) between + * machines of different natural widths (32 or 64 bit), we cannot have +@@ -261,6 +261,7 @@ static inline void vmx_check_vmcs12_offsets(void) + CHECK_OFFSET(pml_address, 312); + CHECK_OFFSET(encls_exiting_bitmap, 320); + CHECK_OFFSET(tsc_multiplier, 328); ++ CHECK_OFFSET(secondary_vm_exit_controls, 336); + CHECK_OFFSET(cr0_guest_host_mask, 344); + CHECK_OFFSET(cr4_guest_host_mask, 352); + CHECK_OFFSET(cr0_read_shadow, 360); +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index e9c6f304b02e4..1576f192a647b 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -95,7 +95,7 @@ do { \ + * associated feature that KVM supports for nested virtualization. + */ + #define KVM_FIRST_EMULATED_VMX_MSR MSR_IA32_VMX_BASIC +-#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_VMFUNC ++#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_EXIT_CTLS2 + + #define KVM_DEFAULT_PLE_GAP 128 + #define KVM_VMX_DEFAULT_PLE_WINDOW 4096 +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet b/SPECS/kernel-rt/0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet new file mode 100644 index 000000000..778350603 --- /dev/null +++ b/SPECS/kernel-rt/0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet @@ -0,0 +1,38 @@ +From 1ae1abe63f9eb8907fd4d0805b5759983b4d707f Mon Sep 17 00:00:00 2001 +From: KhaiWenTan +Date: Tue, 13 Jan 2026 22:52:50 +0800 +Subject: [PATCH 18/18] net: stmmac: intel: Initialize plat->phy_interfaces in + tgl_common_data() + +Commit d3836052fe09 ("net: stmmac: intel: convert speed_mode_2500() +to get_interfaces()") has made the initialization of phy_interfaces +to be done after Intel mgbe xpcs setup. + +The pcs-xpcs setup rely on checking the phy_interfaces is whether +SGMII or 1000BASEX. Without the phy_interfaces initialized first, +pcs-xpcs setup will be skipped no matter the condition, causing ping +issue for 100Mbps and 10Mbps. + +Adding the initialization of plat->phy_interfaces to tgl_common_data() +ensure the pcs-xpcs setup will be executed for SGMII and 1000BASEX. + +Signed-off-by: KhaiWenTan +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index 932432846800d..0bd4ae619289e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -940,6 +940,7 @@ static int tgl_common_data(struct pci_dev *pdev, + plat->rx_queues_to_use = 6; + plat->tx_queues_to_use = 4; + plat->clk_ptp_rate = 204800000; ++ plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + plat->get_interfaces = tgl_get_interfaces; + + plat->safety_feat_cfg->tsoee = 1; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf b/SPECS/kernel-rt/0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf deleted file mode 100644 index e5a1b412c..000000000 --- a/SPECS/kernel-rt/0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf +++ /dev/null @@ -1,61 +0,0 @@ -From d1e990d74ea175d2e98eaf8c156f941eb190897e Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 05:36:55 +0000 -Subject: [PATCH 018/100] perf/x86/intel: Update dyn_constranit base on PEBS - event precise level - -arch-PEBS provides CPUIDs to enumerate which counters support PEBS -sampling and precise distribution PEBS sampling. Thus PEBS constraints -should be dynamically configured base on these counter and precise -distribution bitmap instead of defining them statically. - -Update event dyn_constraint base on PEBS event precise level. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 9 +++++++++ - arch/x86/events/intel/ds.c | 1 + - 2 files changed, 10 insertions(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index d4eaaeff0614..b91c8f24c8f6 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -4254,6 +4254,8 @@ static int intel_pmu_hw_config(struct perf_event *event) - } - - if (event->attr.precise_ip) { -+ struct arch_pebs_cap pebs_cap = hybrid(event->pmu, arch_pebs_cap); -+ - if ((event->attr.config & INTEL_ARCH_EVENT_MASK) == INTEL_FIXED_VLBR_EVENT) - return -EINVAL; - -@@ -4267,6 +4269,13 @@ static int intel_pmu_hw_config(struct perf_event *event) - } - if (x86_pmu.pebs_aliases) - x86_pmu.pebs_aliases(event); -+ -+ if (x86_pmu.arch_pebs) { -+ u64 cntr_mask = event->attr.precise_ip >= 3 ? -+ pebs_cap.pdists : pebs_cap.counters; -+ if (cntr_mask != hybrid(event->pmu, intel_ctrl)) -+ event->hw.dyn_constraint &= cntr_mask; -+ } - } - - if (needs_branch_stack(event)) { -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 3f9ee8dbe6b7..f8b5275409d6 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2983,6 +2983,7 @@ static void __init intel_arch_pebs_init(void) - x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; - x86_pmu.drain_pebs = intel_pmu_drain_arch_pebs; - x86_pmu.pebs_capable = ~0ULL; -+ x86_pmu.flags |= PMU_FL_PEBS_ALL; - - x86_pmu.pebs_enable = __intel_pmu_pebs_enable; - x86_pmu.pebs_disable = __intel_pmu_pebs_disable; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo b/SPECS/kernel-rt/0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo new file mode 100644 index 000000000..e99bd5e27 --- /dev/null +++ b/SPECS/kernel-rt/0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo @@ -0,0 +1,261 @@ +From cacfc54ae3d9f400fc328bb39a44a13d4c60a387 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 15:30:36 -0500 +Subject: [PATCH 18/21] tools/power turbostat: Print "nan" for out of range + percentages + +Sometimes counters return junk. +For the cases where values > 100% is invalid, print "nan". + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 92 +++++++++++++++------------ + 1 file changed, 53 insertions(+), 39 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 0064f9091c7f0..f59dcee3c816e 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2999,6 +2999,25 @@ void print_header(char *delim) + outp += sprintf(outp, "\n"); + } + ++/* ++ * pct() ++ * ++ * If absolute value is < 1.1, return percentage ++ * otherwise, return nan ++ * ++ * return value is appropriate for printing percentages with %f ++ * while flagging some obvious erroneous values. ++ */ ++double pct(double d) ++{ ++ ++ double abs = fabs(d); ++ ++ if (abs < 1.10) ++ return (100.0 * d); ++ return nan(""); ++} ++ + int dump_counters(PER_THREAD_PARAMS) + { + int i; +@@ -3026,7 +3045,7 @@ int dump_counters(PER_THREAD_PARAMS) + + outp += sprintf(outp, "LLC refs: %lld", t->llc.references); + outp += sprintf(outp, "LLC miss: %lld", t->llc.misses); +- outp += sprintf(outp, "LLC Hit%%: %.2f", 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ outp += sprintf(outp, "LLC Hit%%: %.2f", pct((t->llc.references - t->llc.misses) / t->llc.references)); + + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + outp += +@@ -3248,7 +3267,7 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), 1.0 / units * t->aperf / interval_float); + + if (DO_BIC(BIC_Busy)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->mperf / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(t->mperf / tsc)); + + if (DO_BIC(BIC_Bzy_MHz)) { + if (has_base_hz) +@@ -3291,7 +3310,7 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), t->llc.references / interval_float / 1000); + + if (DO_BIC(BIC_LLC_HIT)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), pct((t->llc.references - t->llc.misses) / t->llc.references)); + } + + +@@ -3305,7 +3324,7 @@ int format_counters(PER_THREAD_PARAMS) + if (mp->type == COUNTER_USEC) + outp += print_float_value(&printed, delim, t->counter[i] / interval_float / 10000); + else +- outp += print_float_value(&printed, delim, 100.0 * t->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(t->counter[i] / tsc)); + } + } + +@@ -3319,7 +3338,7 @@ int format_counters(PER_THREAD_PARAMS) + if (pp->type == COUNTER_USEC) + outp += print_float_value(&printed, delim, t->perf_counter[i] / interval_float / 10000); + else +- outp += print_float_value(&printed, delim, 100.0 * t->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(t->perf_counter[i] / tsc)); + } + } + +@@ -3333,34 +3352,34 @@ int format_counters(PER_THREAD_PARAMS) + break; + + case PMT_TYPE_XTAL_TIME: +- value_converted = 100.0 * value_raw / crystal_hz / interval_float; ++ value_converted = pct(value_raw / crystal_hz / interval_float); + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: +- value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; ++ value_converted = pct(value_raw / tcore_clock_freq_hz / interval_float); + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); + } + } + + /* C1 */ + if (DO_BIC(BIC_CPU_c1)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->c1 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(t->c1 / tsc)); + + /* print per-core data only for 1st thread in core */ + if (!is_cpu_first_thread_in_core(t, c)) + goto done; + + if (DO_BIC(BIC_CPU_c3)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->c3 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->c3 / tsc)); + if (DO_BIC(BIC_CPU_c6)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->c6 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->c6 / tsc)); + if (DO_BIC(BIC_CPU_c7)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->c7 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->c7 / tsc)); + + /* Mod%c6 */ + if (DO_BIC(BIC_Mod_c6)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->mc6_us / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->mc6_us / tsc)); + + if (DO_BIC(BIC_CoreTmp)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), c->core_temp_c); +@@ -3376,7 +3395,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(mp->width, &printed, delim, c->counter[i]); + else if (mp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * c->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(c->counter[i] / tsc)); + } + + /* Added perf Core counters */ +@@ -3386,7 +3405,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * c->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(c->perf_counter[i] / tsc)); + } + + /* Added PMT Core counters */ +@@ -3399,12 +3418,12 @@ int format_counters(PER_THREAD_PARAMS) + break; + + case PMT_TYPE_XTAL_TIME: +- value_converted = 100.0 * value_raw / crystal_hz / interval_float; ++ value_converted = pct(value_raw / crystal_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: +- value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; ++ value_converted = pct(value_raw / tcore_clock_freq_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + } + } +@@ -3463,46 +3482,41 @@ int format_counters(PER_THREAD_PARAMS) + + /* Totl%C0, Any%C0 GFX%C0 CPUGFX% */ + if (DO_BIC(BIC_Totl_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_wtd_core_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100 * p->pkg_wtd_core_c0 / tsc); /* can exceed 100% */ + if (DO_BIC(BIC_Any_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_any_core_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_any_core_c0 / tsc)); + if (DO_BIC(BIC_GFX_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_any_gfxe_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_any_gfxe_c0 / tsc)); + if (DO_BIC(BIC_CPUGFX)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_both_core_gfxe_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_both_core_gfxe_c0 / tsc)); + + if (DO_BIC(BIC_Pkgpc2)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc2 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc2 / tsc)); + if (DO_BIC(BIC_Pkgpc3)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc3 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc3 / tsc)); + if (DO_BIC(BIC_Pkgpc6)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc6 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc6 / tsc)); + if (DO_BIC(BIC_Pkgpc7)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc7 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc7 / tsc)); + if (DO_BIC(BIC_Pkgpc8)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc8 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc8 / tsc)); + if (DO_BIC(BIC_Pkgpc9)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc9 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc9 / tsc)); + if (DO_BIC(BIC_Pkgpc10)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc10 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc10 / tsc)); + + if (DO_BIC(BIC_Diec6)) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->die_c6 / crystal_hz / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->die_c6 / crystal_hz / interval_float)); + + if (DO_BIC(BIC_CPU_LPI)) { + if (p->cpu_lpi >= 0) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- 100.0 * p->cpu_lpi / 1000000.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->cpu_lpi / 1000000.0 / interval_float)); + else + outp += sprintf(outp, "%s(neg)", (printed++ ? delim : "")); + } + if (DO_BIC(BIC_SYS_LPI)) { + if (p->sys_lpi >= 0) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- 100.0 * p->sys_lpi / 1000000.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->sys_lpi / 1000000.0 / interval_float)); + else + outp += sprintf(outp, "%s(neg)", (printed++ ? delim : "")); + } +@@ -3556,7 +3570,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); + else if (mp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * p->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(p->counter[i] / tsc)); + } + + /* Added perf Package Counters */ +@@ -3569,7 +3583,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * p->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(p->perf_counter[i] / tsc)); + } + + /* Added PMT Package Counters */ +@@ -3582,12 +3596,12 @@ int format_counters(PER_THREAD_PARAMS) + break; + + case PMT_TYPE_XTAL_TIME: +- value_converted = 100.0 * value_raw / crystal_hz / interval_float; ++ value_converted = pct(value_raw / crystal_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: +- value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; ++ value_converted = pct(value_raw / tcore_clock_freq_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + } + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi b/SPECS/kernel-rt/0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi deleted file mode 100644 index bd2984c16..000000000 --- a/SPECS/kernel-rt/0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi +++ /dev/null @@ -1,195 +0,0 @@ -From b52a89c6eff252b16233c77be9157a29ab770079 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Sun, 4 Jun 2023 23:58:33 -0700 -Subject: [PATCH 19/44] KVM: nVMX: Add FRED-related VMCS field checks - -As with real hardware, nested VMX validates various VMCS fields, including -control and guest/host state fields. This patch adds checks for FRED-related -VMCS fields to support nested FRED functionality. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/vmx/nested.c | 117 +++++++++++++++++++++++++++++++++----- - 1 file changed, 104 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 7b3cd9b6f7c0..7b1a45f06ae6 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3067,6 +3067,8 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) - { - struct vcpu_vmx *vmx = to_vmx(vcpu); -+ bool fred_enabled = (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && -+ (vmcs12->guest_cr4 & X86_CR4_FRED); - - if (CC(!vmx_control_verify(vmcs12->vm_entry_controls, - vmx->nested.msrs.entry_ctls_low, -@@ -3084,22 +3086,11 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - u8 vector = intr_info & INTR_INFO_VECTOR_MASK; - u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; - bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; -+ bool has_nested_exception = vmx->nested.msrs.basic & VMX_BASIC_NESTED_EXCEPTION; - bool urg = nested_cpu_has2(vmcs12, - SECONDARY_EXEC_UNRESTRICTED_GUEST); - bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; - -- /* VM-entry interruption-info field: interruption type */ -- if (CC(intr_type == INTR_TYPE_RESERVED) || -- CC(intr_type == INTR_TYPE_OTHER_EVENT && -- !nested_cpu_supports_monitor_trap_flag(vcpu))) -- return -EINVAL; -- -- /* VM-entry interruption-info field: vector */ -- if (CC(intr_type == INTR_TYPE_NMI_INTR && vector != NMI_VECTOR) || -- CC(intr_type == INTR_TYPE_HARD_EXCEPTION && vector > 31) || -- CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) -- return -EINVAL; -- - /* - * Cannot deliver error code in real mode or if the interrupt - * type is not hardware exception. For other cases, do the -@@ -3124,8 +3115,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - if (CC(intr_info & INTR_INFO_RESVD_BITS_MASK)) - return -EINVAL; - -- /* VM-entry instruction length */ -+ /* -+ * When the CPU enumerates VMX nested-exception support, bit 13 -+ * (set to indicate a nested exception) of the intr info field -+ * may have value 1. Otherwise bit 13 is reserved. -+ */ -+ if (CC(!(has_nested_exception && intr_type == INTR_TYPE_HARD_EXCEPTION) && -+ intr_info & INTR_INFO_NESTED_EXCEPTION_MASK)) -+ return -EINVAL; -+ - switch (intr_type) { -+ case INTR_TYPE_EXT_INTR: -+ break; -+ case INTR_TYPE_RESERVED: -+ return -EINVAL; -+ case INTR_TYPE_NMI_INTR: -+ if (CC(vector != NMI_VECTOR)) -+ return -EINVAL; -+ break; -+ case INTR_TYPE_HARD_EXCEPTION: -+ if (CC(vector > 31)) -+ return -EINVAL; -+ break; - case INTR_TYPE_SOFT_EXCEPTION: - case INTR_TYPE_SOFT_INTR: - case INTR_TYPE_PRIV_SW_EXCEPTION: -@@ -3133,6 +3144,24 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - CC(vmcs12->vm_entry_instruction_len == 0 && - CC(!nested_cpu_has_zero_length_injection(vcpu)))) - return -EINVAL; -+ break; -+ case INTR_TYPE_OTHER_EVENT: -+ switch (vector) { -+ case 0: -+ if (CC(!nested_cpu_supports_monitor_trap_flag(vcpu))) -+ return -EINVAL; -+ break; -+ case 1: -+ case 2: -+ if (CC(!fred_enabled)) -+ return -EINVAL; -+ if (CC(vmcs12->vm_entry_instruction_len > X86_MAX_INSTRUCTION_LENGTH)) -+ return -EINVAL; -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; - } - } - -@@ -3220,9 +3249,29 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - if (ia32e) { - if (CC(!(vmcs12->host_cr4 & X86_CR4_PAE))) - return -EINVAL; -+ if (vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS && -+ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED) { -+ if (CC(vmcs12->host_ia32_fred_config & -+ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || -+ CC(vmcs12->host_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->host_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->host_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->host_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->host_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->host_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_config & PAGE_MASK, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp3, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp3, vcpu))) -+ return -EINVAL; -+ } - } else { - if (CC(vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) || - CC(vmcs12->host_cr4 & X86_CR4_PCIDE) || -+ CC(vmcs12->host_cr4 & X86_CR4_FRED) || - CC((vmcs12->host_rip) >> 32)) - return -EINVAL; - } -@@ -3390,6 +3439,48 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, - CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) - return -EINVAL; - -+ if (ia32e) { -+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED) { -+ if (CC(vmcs12->guest_ia32_fred_config & -+ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || -+ CC(vmcs12->guest_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->guest_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->guest_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->guest_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->guest_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->guest_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_config & PAGE_MASK, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp3, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp3, vcpu))) -+ return -EINVAL; -+ } -+ if (vmcs12->guest_cr4 & X86_CR4_FRED) { -+ unsigned int ss_dpl = VMX_AR_DPL(vmcs12->guest_ss_ar_bytes); -+ switch (ss_dpl) { -+ case 0: -+ if (CC(!(vmcs12->guest_cs_ar_bytes & VMX_AR_L_MASK))) -+ return -EINVAL; -+ break; -+ case 1: -+ case 2: -+ return -EINVAL; -+ case 3: -+ if (CC(vmcs12->guest_rflags & X86_EFLAGS_IOPL)) -+ return -EINVAL; -+ if (CC(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_STI)) -+ return -EINVAL; -+ break; -+ } -+ } -+ } else { -+ if (CC(vmcs12->guest_cr4 & X86_CR4_FRED)) -+ return -EINVAL; -+ } -+ - if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { - if (CC(!is_valid_cet_state(vcpu, vmcs12->guest_s_cet, vmcs12->guest_ssp, - vmcs12->guest_ssp_tbl))) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi b/SPECS/kernel-rt/0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi new file mode 100644 index 000000000..5bc80d105 --- /dev/null +++ b/SPECS/kernel-rt/0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi @@ -0,0 +1,501 @@ +From a008f98d01a9c82935b67bbe85548ca3a6c2cce5 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 22:45:34 -0700 +Subject: [PATCH 19/44] KVM: nVMX: Handle FRED VMCS fields in nested VMX + context + +Extend nested VMX context management to include FRED-related VMCS fields, +enabling proper handling of FRED state during nested virtualization. + +Because KVM always sets SECONDARY_VM_EXIT_SAVE_IA32_FRED, FRED MSRs are +always saved to vmcs02. However an L1 VMM may choose to clear this bit, +i.e., not to save FRED MSRs to vmcs12. This is not a problem when the L1 +VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, as KVM then immediately loads +host FRED MSRs of vmcs12 to guest FRED MSRs of vmcs01. However if the L1 +VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should retain FRED MSRs +to run the L1 VMM. + +To propagate guest FRED MSRs from vmcs02 to vmcs01, save them in +sync_vmcs02_to_vmcs12() regardless of whether +SECONDARY_VM_EXIT_SAVE_IA32_FRED is set in vmcs12. Then, use the saved +values to set guest FRED MSRs in vmcs01 within load_vmcs12_host_state() +when !nested_cpu_load_host_fred_state(). + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v9: +* Rebase to kvm-x86/next. +* Guard FRED state save/restore with guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) + (syzbot & Chao). + +Changes in v8: +* Make the newly added FRED fields 64-bit aligned in vmcs12 (Isaku). +* Remove the change to Documentation/virt/kvm/x86/nested-vmx.rst. + +Change in v6: +* Handle FRED MSR pre-vmenter save/restore (Chao Gao). +* Save FRED MSRs of vmcs02 at VM-Exit even an L1 VMM clears + SECONDARY_VM_EXIT_SAVE_IA32_FRED. +* Save FRED MSRs in sync_vmcs02_to_vmcs12() instead of its rare version. + +Change in v5: +* Add TB from Xuelian Guo. + +Changes in v4: +* Advertise VMX nested exception as if the CPU supports it (Chao Gao). +* Split FRED state management controls (Chao Gao). + +Changes in v3: +* Add and use nested_cpu_has_fred(vmcs12) because vmcs02 should be set + from vmcs12 if and only if the field is enabled in L1's VMX config + (Sean Christopherson). +* Fix coding style issues (Sean Christopherson). + +Changes in v2: +* Remove hyperv TLFS related changes (Jeremi Piotrowski). +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). +--- + arch/x86/kvm/vmx/capabilities.h | 5 ++ + arch/x86/kvm/vmx/nested.c | 118 +++++++++++++++++++++++++- + arch/x86/kvm/vmx/nested.h | 22 +++++ + arch/x86/kvm/vmx/vmcs12.c | 18 ++++ + arch/x86/kvm/vmx/vmcs12.h | 37 ++++++++ + arch/x86/kvm/vmx/vmcs_shadow_fields.h | 4 + + arch/x86/kvm/vmx/vmx.h | 41 +++++++++ + 7 files changed, 243 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index f390f9f883c30..5eba2530ffb40 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -80,6 +80,11 @@ static inline bool cpu_has_vmx_basic_no_hw_errcode_cc(void) + return vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; + } + ++static inline bool cpu_has_vmx_nested_exception(void) ++{ ++ return vmcs_config.basic & VMX_BASIC_NESTED_EXCEPTION; ++} ++ + static inline bool cpu_has_virtual_nmis(void) + { + return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS && +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index b994f264acc0a..da49de1800609 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -705,6 +705,9 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, + + nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, + MSR_KERNEL_GS_BASE, MSR_TYPE_RW); ++ ++ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, ++ MSR_IA32_FRED_RSP0, MSR_TYPE_RW); + #endif + nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, + MSR_IA32_SPEC_CTRL, MSR_TYPE_RW); +@@ -1291,9 +1294,11 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) + const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT | + VMX_BASIC_INOUT | + VMX_BASIC_TRUE_CTLS | +- VMX_BASIC_NO_HW_ERROR_CODE_CC; ++ VMX_BASIC_NO_HW_ERROR_CODE_CC | ++ VMX_BASIC_NESTED_EXCEPTION; + +- const u64 reserved_bits = GENMASK_ULL(63, 57) | ++ const u64 reserved_bits = GENMASK_ULL(63, 59) | ++ BIT_ULL(57) | + GENMASK_ULL(47, 45) | + BIT_ULL(31); + +@@ -2545,6 +2550,8 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 + vmcs12->vm_entry_instruction_len); + vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, + vmcs12->guest_interruptibility_info); ++ if (cpu_has_vmx_fred()) ++ vmcs_write64(INJECTED_EVENT_DATA, vmcs12->injected_event_data); + vmx->loaded_vmcs->nmi_known_unmasked = + !(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_NMI); + } else { +@@ -2699,6 +2706,18 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) + vmcs12->guest_ssp, vmcs12->guest_ssp_tbl); + + set_cr4_guest_host_mask(vmx); ++ ++ if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED) && ++ nested_cpu_load_guest_fred_state(vmcs12)) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->guest_ia32_fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->guest_ia32_fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->guest_ia32_fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->guest_ia32_fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->guest_ia32_fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->guest_ia32_fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->guest_ia32_fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->guest_ia32_fred_ssp3); ++ } + } + + /* +@@ -2765,6 +2784,18 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, + vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat); + } + ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) && ++ (!vmx->nested.nested_run_pending || !nested_cpu_load_guest_fred_state(vmcs12))) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.pre_vmenter_fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.pre_vmenter_fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.pre_vmenter_fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.pre_vmenter_fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.pre_vmenter_fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.pre_vmenter_fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.pre_vmenter_fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.pre_vmenter_fred_ssp3); ++ } ++ + vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset( + vcpu->arch.l1_tsc_offset, + vmx_get_l2_tsc_offset(vcpu), +@@ -3679,6 +3710,18 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, + &vmx->nested.pre_vmenter_ssp, + &vmx->nested.pre_vmenter_ssp_tbl); + ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) && ++ (!vmx->nested.nested_run_pending || !nested_cpu_load_guest_fred_state(vmcs12))) { ++ vmx->nested.pre_vmenter_fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); ++ vmx->nested.pre_vmenter_fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); ++ vmx->nested.pre_vmenter_fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); ++ vmx->nested.pre_vmenter_fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); ++ vmx->nested.pre_vmenter_fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); ++ vmx->nested.pre_vmenter_fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); ++ vmx->nested.pre_vmenter_fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); ++ vmx->nested.pre_vmenter_fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); ++ } ++ + /* + * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* + * nested early checks are disabled. In the event of a "late" VM-Fail, +@@ -3986,6 +4029,8 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, + u32 idt_vectoring; + unsigned int nr; + ++ vmcs12->original_event_data = 0; ++ + /* + * Per the SDM, VM-Exits due to double and triple faults are never + * considered to occur during event delivery, even if the double/triple +@@ -4024,6 +4069,13 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, + vcpu->arch.exception.error_code; + } + ++ if ((vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && ++ (vmcs12->guest_cr4 & X86_CR4_FRED) && ++ (vcpu->arch.exception.nested)) ++ idt_vectoring |= VECTORING_INFO_NESTED_EXCEPTION_MASK; ++ ++ vmcs12->original_event_data = vcpu->arch.exception.event_data; ++ + vmcs12->idt_vectoring_info_field = idt_vectoring; + } else if (vcpu->arch.nmi_injected) { + vmcs12->idt_vectoring_info_field = +@@ -4766,6 +4818,28 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) + vmcs_read_cet_state(&vmx->vcpu, &vmcs12->guest_s_cet, + &vmcs12->guest_ssp, + &vmcs12->guest_ssp_tbl); ++ ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) { ++ vmx->nested.fred_msr_at_vmexit.fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); ++ vmx->nested.fred_msr_at_vmexit.fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); ++ vmx->nested.fred_msr_at_vmexit.fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); ++ vmx->nested.fred_msr_at_vmexit.fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); ++ vmx->nested.fred_msr_at_vmexit.fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); ++ vmx->nested.fred_msr_at_vmexit.fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); ++ vmx->nested.fred_msr_at_vmexit.fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); ++ vmx->nested.fred_msr_at_vmexit.fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); ++ ++ if (nested_cpu_save_guest_fred_state(vmcs12)) { ++ vmcs12->guest_ia32_fred_config = vmx->nested.fred_msr_at_vmexit.fred_config; ++ vmcs12->guest_ia32_fred_rsp1 = vmx->nested.fred_msr_at_vmexit.fred_rsp1; ++ vmcs12->guest_ia32_fred_rsp2 = vmx->nested.fred_msr_at_vmexit.fred_rsp2; ++ vmcs12->guest_ia32_fred_rsp3 = vmx->nested.fred_msr_at_vmexit.fred_rsp3; ++ vmcs12->guest_ia32_fred_stklvls = vmx->nested.fred_msr_at_vmexit.fred_stklvls; ++ vmcs12->guest_ia32_fred_ssp1 = vmx->nested.fred_msr_at_vmexit.fred_ssp1; ++ vmcs12->guest_ia32_fred_ssp2 = vmx->nested.fred_msr_at_vmexit.fred_ssp2; ++ vmcs12->guest_ia32_fred_ssp3 = vmx->nested.fred_msr_at_vmexit.fred_ssp3; ++ } ++ } + } + + /* +@@ -4810,6 +4884,21 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, + + vmcs12->vm_exit_intr_info = exit_intr_info; + vmcs12->vm_exit_instruction_len = exit_insn_len; ++ ++ /* ++ * When there is a valid original event, the exiting event is a nested ++ * event during delivery of the earlier original event. ++ * ++ * FRED event delivery reflects this relationship by setting the value ++ * of the nested exception bit of VM-exit interruption information ++ * (aka exiting-event identification) to that of the valid bit of the ++ * IDT-vectoring information (aka original-event identification). ++ */ ++ if ((vmcs12->idt_vectoring_info_field & VECTORING_INFO_VALID_MASK) && ++ (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && ++ (vmcs12->guest_cr4 & X86_CR4_FRED)) ++ vmcs12->vm_exit_intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; ++ + vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO); + + /* +@@ -4838,6 +4927,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, + static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12) + { ++ struct vcpu_vmx *vmx = to_vmx(vcpu); + enum vm_entry_failure_code ignored; + struct kvm_segment seg; + +@@ -4912,6 +5002,28 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, + WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, + vmcs12->host_ia32_perf_global_ctrl)); + ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) { ++ if (nested_cpu_load_host_fred_state(vmcs12)) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->host_ia32_fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->host_ia32_fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->host_ia32_fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->host_ia32_fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->host_ia32_fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->host_ia32_fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->host_ia32_fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->host_ia32_fred_ssp3); ++ } else { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.fred_msr_at_vmexit.fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.fred_msr_at_vmexit.fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.fred_msr_at_vmexit.fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.fred_msr_at_vmexit.fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.fred_msr_at_vmexit.fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.fred_msr_at_vmexit.fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.fred_msr_at_vmexit.fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.fred_msr_at_vmexit.fred_ssp3); ++ } ++ } ++ + /* Set L1 segment info according to Intel SDM + 27.5.2 Loading Host Segment and Descriptor-Table Registers */ + seg = (struct kvm_segment) { +@@ -7398,6 +7510,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs) + msrs->basic |= VMX_BASIC_INOUT; + if (cpu_has_vmx_basic_no_hw_errcode_cc()) + msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC; ++ if (cpu_has_vmx_nested_exception()) ++ msrs->basic |= VMX_BASIC_NESTED_EXCEPTION; + } + + static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs) +diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h +index 983484d42ebf9..a99d3d83d58e2 100644 +--- a/arch/x86/kvm/vmx/nested.h ++++ b/arch/x86/kvm/vmx/nested.h +@@ -249,6 +249,11 @@ static inline bool nested_cpu_has_save_preemption_timer(struct vmcs12 *vmcs12) + VM_EXIT_SAVE_VMX_PREEMPTION_TIMER; + } + ++static inline bool nested_cpu_has_secondary_vm_exit_controls(struct vmcs12 *vmcs12) ++{ ++ return vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; ++} ++ + static inline bool nested_exit_on_nmi(struct kvm_vcpu *vcpu) + { + return nested_cpu_has_nmi_exiting(get_vmcs12(vcpu)); +@@ -269,6 +274,23 @@ static inline bool nested_cpu_has_encls_exit(struct vmcs12 *vmcs12) + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENCLS_EXITING); + } + ++static inline bool nested_cpu_load_guest_fred_state(struct vmcs12 *vmcs12) ++{ ++ return vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED; ++} ++ ++static inline bool nested_cpu_save_guest_fred_state(struct vmcs12 *vmcs12) ++{ ++ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && ++ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_SAVE_IA32_FRED; ++} ++ ++static inline bool nested_cpu_load_host_fred_state(struct vmcs12 *vmcs12) ++{ ++ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && ++ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED; ++} ++ + /* + * if fixed0[i] == 1: val[i] must be 1 + * if fixed1[i] == 0: val[i] must be 0 +diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c +index 3b01175f392ae..9691e709061ff 100644 +--- a/arch/x86/kvm/vmx/vmcs12.c ++++ b/arch/x86/kvm/vmx/vmcs12.c +@@ -67,6 +67,24 @@ const unsigned short vmcs12_field_offsets[] = { + FIELD64(HOST_IA32_EFER, host_ia32_efer), + FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), + FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), ++ FIELD64(INJECTED_EVENT_DATA, injected_event_data), ++ FIELD64(ORIGINAL_EVENT_DATA, original_event_data), ++ FIELD64(GUEST_IA32_FRED_CONFIG, guest_ia32_fred_config), ++ FIELD64(GUEST_IA32_FRED_RSP1, guest_ia32_fred_rsp1), ++ FIELD64(GUEST_IA32_FRED_RSP2, guest_ia32_fred_rsp2), ++ FIELD64(GUEST_IA32_FRED_RSP3, guest_ia32_fred_rsp3), ++ FIELD64(GUEST_IA32_FRED_STKLVLS, guest_ia32_fred_stklvls), ++ FIELD64(GUEST_IA32_FRED_SSP1, guest_ia32_fred_ssp1), ++ FIELD64(GUEST_IA32_FRED_SSP2, guest_ia32_fred_ssp2), ++ FIELD64(GUEST_IA32_FRED_SSP3, guest_ia32_fred_ssp3), ++ FIELD64(HOST_IA32_FRED_CONFIG, host_ia32_fred_config), ++ FIELD64(HOST_IA32_FRED_RSP1, host_ia32_fred_rsp1), ++ FIELD64(HOST_IA32_FRED_RSP2, host_ia32_fred_rsp2), ++ FIELD64(HOST_IA32_FRED_RSP3, host_ia32_fred_rsp3), ++ FIELD64(HOST_IA32_FRED_STKLVLS, host_ia32_fred_stklvls), ++ FIELD64(HOST_IA32_FRED_SSP1, host_ia32_fred_ssp1), ++ FIELD64(HOST_IA32_FRED_SSP2, host_ia32_fred_ssp2), ++ FIELD64(HOST_IA32_FRED_SSP3, host_ia32_fred_ssp3), + FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), + FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), + FIELD(EXCEPTION_BITMAP, exception_bitmap), +diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h +index fa5306dc0311f..051016a3afba4 100644 +--- a/arch/x86/kvm/vmx/vmcs12.h ++++ b/arch/x86/kvm/vmx/vmcs12.h +@@ -191,6 +191,25 @@ struct __packed vmcs12 { + u16 host_gs_selector; + u16 host_tr_selector; + u16 guest_pml_index; ++ u16 padding16[1]; /* align to 64-bit boundary */ ++ u64 guest_ia32_fred_config; ++ u64 guest_ia32_fred_rsp1; ++ u64 guest_ia32_fred_rsp2; ++ u64 guest_ia32_fred_rsp3; ++ u64 guest_ia32_fred_stklvls; ++ u64 guest_ia32_fred_ssp1; ++ u64 guest_ia32_fred_ssp2; ++ u64 guest_ia32_fred_ssp3; ++ u64 host_ia32_fred_config; ++ u64 host_ia32_fred_rsp1; ++ u64 host_ia32_fred_rsp2; ++ u64 host_ia32_fred_rsp3; ++ u64 host_ia32_fred_stklvls; ++ u64 host_ia32_fred_ssp1; ++ u64 host_ia32_fred_ssp2; ++ u64 host_ia32_fred_ssp3; ++ u64 injected_event_data; ++ u64 original_event_data; + }; + + /* +@@ -373,6 +392,24 @@ static inline void vmx_check_vmcs12_offsets(void) + CHECK_OFFSET(host_gs_selector, 992); + CHECK_OFFSET(host_tr_selector, 994); + CHECK_OFFSET(guest_pml_index, 996); ++ CHECK_OFFSET(guest_ia32_fred_config, 1000); ++ CHECK_OFFSET(guest_ia32_fred_rsp1, 1008); ++ CHECK_OFFSET(guest_ia32_fred_rsp2, 1016); ++ CHECK_OFFSET(guest_ia32_fred_rsp3, 1024); ++ CHECK_OFFSET(guest_ia32_fred_stklvls, 1032); ++ CHECK_OFFSET(guest_ia32_fred_ssp1, 1040); ++ CHECK_OFFSET(guest_ia32_fred_ssp2, 1048); ++ CHECK_OFFSET(guest_ia32_fred_ssp3, 1056); ++ CHECK_OFFSET(host_ia32_fred_config, 1064); ++ CHECK_OFFSET(host_ia32_fred_rsp1, 1072); ++ CHECK_OFFSET(host_ia32_fred_rsp2, 1080); ++ CHECK_OFFSET(host_ia32_fred_rsp3, 1088); ++ CHECK_OFFSET(host_ia32_fred_stklvls, 1096); ++ CHECK_OFFSET(host_ia32_fred_ssp1, 1104); ++ CHECK_OFFSET(host_ia32_fred_ssp2, 1112); ++ CHECK_OFFSET(host_ia32_fred_ssp3, 1120); ++ CHECK_OFFSET(injected_event_data, 1128); ++ CHECK_OFFSET(original_event_data, 1136); + } + + extern const unsigned short vmcs12_field_offsets[]; +diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +index cad128d1657be..da338327c2b34 100644 +--- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h ++++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +@@ -74,6 +74,10 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) + /* 64-bit */ + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) ++SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) ++SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) ++SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) ++SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) + + #undef SHADOW_FIELD_RO + #undef SHADOW_FIELD_RW +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index c4a3b28553fbd..36dcc888e5c63 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -67,6 +67,37 @@ struct pt_desc { + struct pt_ctx guest; + }; + ++/* ++ * Used to snapshot FRED MSRs that may NOT be saved to vmcs12 as specified ++ * in the VM-Exit controls of vmcs12 configured by L1 VMM. ++ * ++ * FRED MSRs are *always* saved into vmcs02 because KVM always sets ++ * SECONDARY_VM_EXIT_SAVE_IA32_FRED. However an L1 VMM may choose to clear ++ * this bit, resulting in FRED MSRs not being propagated to vmcs12 from ++ * vmcs02. When the L1 VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, this is ++ * not a problem, since KVM then immediately loads the host FRED MSRs of ++ * vmcs12 to the guest FRED MSRs of vmcs01. ++ * ++ * But if the L1 VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should ++ * retain the FRED MSRs, i.e., propagate the guest FRED MSRs of vmcs02 to ++ * the guest FRED MSRs of vmcs01. ++ * ++ * This structure stores guest FRED MSRs that an L1 VMM opts not to save ++ * during VM-Exits from L2 to L1. These MSRs may still be retained for ++ * running the L1 VMM if SECONDARY_VM_EXIT_LOAD_IA32_FRED is cleared in ++ * vmcs12. ++ */ ++struct fred_msr_at_vmexit { ++ u64 fred_config; ++ u64 fred_rsp1; ++ u64 fred_rsp2; ++ u64 fred_rsp3; ++ u64 fred_stklvls; ++ u64 fred_ssp1; ++ u64 fred_ssp2; ++ u64 fred_ssp3; ++}; ++ + /* + * The nested_vmx structure is part of vcpu_vmx, and holds information we need + * for correct emulation of VMX (i.e., nested VMX) on this vcpu. +@@ -184,6 +215,16 @@ struct nested_vmx { + u64 pre_vmenter_s_cet; + u64 pre_vmenter_ssp; + u64 pre_vmenter_ssp_tbl; ++ u64 pre_vmenter_fred_config; ++ u64 pre_vmenter_fred_rsp1; ++ u64 pre_vmenter_fred_rsp2; ++ u64 pre_vmenter_fred_rsp3; ++ u64 pre_vmenter_fred_stklvls; ++ u64 pre_vmenter_fred_ssp1; ++ u64 pre_vmenter_fred_ssp2; ++ u64 pre_vmenter_fred_ssp3; ++ ++ struct fred_msr_at_vmexit fred_msr_at_vmexit; + + /* to migrate it to L1 if L2 writes to L1's CR8 directly */ + int l1_tpr_threshold; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet b/SPECS/kernel-rt/0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet deleted file mode 100644 index 849bd5e02..000000000 --- a/SPECS/kernel-rt/0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet +++ /dev/null @@ -1,127 +0,0 @@ -From 4b93c7a331f8b3a34bffa76517f950f60ef3b401 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:49 -0700 -Subject: [PATCH 19/24] KVM: x86: Don't emulate instructions guarded by CET - -Don't emulate the branch instructions, e.g., CALL/RET/JMP etc., when CET -is active in guest, return KVM_INTERNAL_ERROR_EMULATION to userspace to -handle it. - -KVM doesn't emulate CPU behaviors to check CET protected stuffs while -emulating guest instructions, instead it stops emulation on detecting -the instructions in process are CET protected. By doing so, it can avoid -generating bogus #CP in guest and preventing CET protected execution flow -subversion from guest side. - -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/emulate.c | 46 ++++++++++++++++++++++++++++++++---------- - 1 file changed, 35 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c -index 1349e278cd2a..80b9d1e4a50a 100644 ---- a/arch/x86/kvm/emulate.c -+++ b/arch/x86/kvm/emulate.c -@@ -178,6 +178,8 @@ - #define IncSP ((u64)1 << 54) /* SP is incremented before ModRM calc */ - #define TwoMemOp ((u64)1 << 55) /* Instruction has two memory operand */ - #define IsBranch ((u64)1 << 56) /* Instruction is considered a branch. */ -+#define ShadowStack ((u64)1 << 57) /* Instruction protected by Shadow Stack. */ -+#define IndirBrnTrk ((u64)1 << 58) /* Instruction protected by IBT. */ - - #define DstXacc (DstAccLo | SrcAccHi | SrcWrite) - -@@ -4068,9 +4070,11 @@ static const struct opcode group4[] = { - static const struct opcode group5[] = { - F(DstMem | SrcNone | Lock, em_inc), - F(DstMem | SrcNone | Lock, em_dec), -- I(SrcMem | NearBranch | IsBranch, em_call_near_abs), -- I(SrcMemFAddr | ImplicitOps | IsBranch, em_call_far), -- I(SrcMem | NearBranch | IsBranch, em_jmp_abs), -+ I(SrcMem | NearBranch | IsBranch | ShadowStack | IndirBrnTrk, -+ em_call_near_abs), -+ I(SrcMemFAddr | ImplicitOps | IsBranch | ShadowStack | IndirBrnTrk, -+ em_call_far), -+ I(SrcMem | NearBranch | IsBranch | IndirBrnTrk, em_jmp_abs), - I(SrcMemFAddr | ImplicitOps | IsBranch, em_jmp_far), - I(SrcMem | Stack | TwoMemOp, em_push), D(Undefined), - }; -@@ -4332,11 +4336,11 @@ static const struct opcode opcode_table[256] = { - /* 0xC8 - 0xCF */ - I(Stack | SrcImmU16 | Src2ImmByte | IsBranch, em_enter), - I(Stack | IsBranch, em_leave), -- I(ImplicitOps | SrcImmU16 | IsBranch, em_ret_far_imm), -- I(ImplicitOps | IsBranch, em_ret_far), -- D(ImplicitOps | IsBranch), DI(SrcImmByte | IsBranch, intn), -+ I(ImplicitOps | SrcImmU16 | IsBranch | ShadowStack, em_ret_far_imm), -+ I(ImplicitOps | IsBranch | ShadowStack, em_ret_far), -+ D(ImplicitOps | IsBranch), DI(SrcImmByte | IsBranch | ShadowStack, intn), - D(ImplicitOps | No64 | IsBranch), -- II(ImplicitOps | IsBranch, em_iret, iret), -+ II(ImplicitOps | IsBranch | ShadowStack, em_iret, iret), - /* 0xD0 - 0xD7 */ - G(Src2One | ByteOp, group2), G(Src2One, group2), - G(Src2CL | ByteOp, group2), G(Src2CL, group2), -@@ -4352,7 +4356,7 @@ static const struct opcode opcode_table[256] = { - I2bvIP(SrcImmUByte | DstAcc, em_in, in, check_perm_in), - I2bvIP(SrcAcc | DstImmUByte, em_out, out, check_perm_out), - /* 0xE8 - 0xEF */ -- I(SrcImm | NearBranch | IsBranch, em_call), -+ I(SrcImm | NearBranch | IsBranch | ShadowStack, em_call), - D(SrcImm | ImplicitOps | NearBranch | IsBranch), - I(SrcImmFAddr | No64 | IsBranch, em_jmp_far), - D(SrcImmByte | ImplicitOps | NearBranch | IsBranch), -@@ -4371,7 +4375,8 @@ static const struct opcode opcode_table[256] = { - static const struct opcode twobyte_table[256] = { - /* 0x00 - 0x0F */ - G(0, group6), GD(0, &group7), N, N, -- N, I(ImplicitOps | EmulateOnUD | IsBranch, em_syscall), -+ N, I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack | IndirBrnTrk, -+ em_syscall), - II(ImplicitOps | Priv, em_clts, clts), N, - DI(ImplicitOps | Priv, invd), DI(ImplicitOps | Priv, wbinvd), N, N, - N, D(ImplicitOps | ModRM | SrcMem | NoAccess), N, N, -@@ -4402,8 +4407,9 @@ static const struct opcode twobyte_table[256] = { - IIP(ImplicitOps, em_rdtsc, rdtsc, check_rdtsc), - II(ImplicitOps | Priv, em_rdmsr, rdmsr), - IIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc), -- I(ImplicitOps | EmulateOnUD | IsBranch, em_sysenter), -- I(ImplicitOps | Priv | EmulateOnUD | IsBranch, em_sysexit), -+ I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack | IndirBrnTrk, -+ em_sysenter), -+ I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack, em_sysexit), - N, N, - N, N, N, N, N, N, N, N, - /* 0x40 - 0x4F */ -@@ -4941,6 +4947,24 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int - if (ctxt->d == 0) - return EMULATION_FAILED; - -+ if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_CET) { -+ u64 u_cet, s_cet; -+ bool stop_em; -+ -+ if (ctxt->ops->get_msr(ctxt, MSR_IA32_U_CET, &u_cet) || -+ ctxt->ops->get_msr(ctxt, MSR_IA32_S_CET, &s_cet)) -+ return EMULATION_FAILED; -+ -+ stop_em = ((u_cet & CET_SHSTK_EN) || (s_cet & CET_SHSTK_EN)) && -+ (opcode.flags & ShadowStack); -+ -+ stop_em |= ((u_cet & CET_ENDBR_EN) || (s_cet & CET_ENDBR_EN)) && -+ (opcode.flags & IndirBrnTrk); -+ -+ if (stop_em) -+ return EMULATION_FAILED; -+ } -+ - ctxt->execute = opcode.u.execute; - - if (unlikely(emulation_type & EMULTYPE_TRAP_UD) && --- -2.43.0 - diff --git a/SPECS/kernel-rt/0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf b/SPECS/kernel-rt/0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf deleted file mode 100644 index c1a646005..000000000 --- a/SPECS/kernel-rt/0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf +++ /dev/null @@ -1,323 +0,0 @@ -From 6ef22e5aced242e831b90e56ad3aaf15d611b8a7 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 05:53:23 +0000 -Subject: [PATCH 019/100] perf/x86/intel: Setup PEBS data configuration and - enable legacy groups - -Different with legacy PEBS, arch-PEBS provides per-counter PEBS data -configuration by programing MSR IA32_PMC_GPx/FXx_CFG_C MSRs. - -This patch obtains PEBS data configuration from event attribute and then -writes the PEBS data configuration to MSR IA32_PMC_GPx/FXx_CFG_C and -enable corresponding PEBS groups. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 127 +++++++++++++++++++++++++++++++ - arch/x86/events/intel/ds.c | 17 +++++ - arch/x86/events/perf_event.h | 12 +++ - arch/x86/include/asm/intel_ds.h | 7 ++ - arch/x86/include/asm/msr-index.h | 8 ++ - 5 files changed, 171 insertions(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index b91c8f24c8f6..fabe2e750a0b 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -2563,6 +2563,39 @@ static void intel_pmu_disable_fixed(struct perf_event *event) - cpuc->fixed_ctrl_val &= ~mask; - } - -+static inline void __intel_pmu_update_event_ext(int idx, u64 ext) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ u32 msr = idx < INTEL_PMC_IDX_FIXED ? -+ x86_pmu_cfg_c_addr(idx, true) : -+ x86_pmu_cfg_c_addr(idx - INTEL_PMC_IDX_FIXED, false); -+ -+ cpuc->cfg_c_val[idx] = ext; -+ wrmsrq(msr, ext); -+} -+ -+static void intel_pmu_disable_event_ext(struct perf_event *event) -+{ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ /* -+ * Only clear CFG_C MSR for PEBS counter group events, -+ * it avoids the HW counter's value to be added into -+ * other PEBS records incorrectly after PEBS counter -+ * group events are disabled. -+ * -+ * For other events, it's unnecessary to clear CFG_C MSRs -+ * since CFG_C doesn't take effect if counter is in -+ * disabled state. That helps to reduce the WRMSR overhead -+ * in context switches. -+ */ -+ if (!is_pebs_counter_event_group(event)) -+ return; -+ -+ __intel_pmu_update_event_ext(event->hw.idx, 0); -+} -+ - static void intel_pmu_disable_event(struct perf_event *event) - { - struct hw_perf_event *hwc = &event->hw; -@@ -2571,9 +2604,12 @@ static void intel_pmu_disable_event(struct perf_event *event) - switch (idx) { - case 0 ... INTEL_PMC_IDX_FIXED - 1: - intel_clear_masks(event, idx); -+ intel_pmu_disable_event_ext(event); - x86_pmu_disable_event(event); - break; - case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1: -+ intel_pmu_disable_event_ext(event); -+ fallthrough; - case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END: - intel_pmu_disable_fixed(event); - break; -@@ -2944,6 +2980,67 @@ static void intel_pmu_enable_acr(struct perf_event *event) - - DEFINE_STATIC_CALL_NULL(intel_pmu_enable_acr_event, intel_pmu_enable_acr); - -+static void intel_pmu_enable_event_ext(struct perf_event *event) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct hw_perf_event *hwc = &event->hw; -+ union arch_pebs_index cached, index; -+ struct arch_pebs_cap cap; -+ u64 ext = 0; -+ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ cap = hybrid(cpuc->pmu, arch_pebs_cap); -+ -+ if (event->attr.precise_ip) { -+ u64 pebs_data_cfg = intel_get_arch_pebs_data_config(event); -+ -+ ext |= ARCH_PEBS_EN; -+ if (hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) -+ ext |= (-hwc->sample_period) & ARCH_PEBS_RELOAD; -+ -+ if (pebs_data_cfg && cap.caps) { -+ if (pebs_data_cfg & PEBS_DATACFG_MEMINFO) -+ ext |= ARCH_PEBS_AUX & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_GP) -+ ext |= ARCH_PEBS_GPR & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_XMMS) -+ ext |= ARCH_PEBS_VECR_XMM & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_LBRS) -+ ext |= ARCH_PEBS_LBR & cap.caps; -+ } -+ -+ if (cpuc->n_pebs == cpuc->n_large_pebs) -+ index.split.thresh = ARCH_PEBS_THRESH_MUL; -+ else -+ index.split.thresh = ARCH_PEBS_THRESH_SINGLE; -+ -+ rdmsrq(MSR_IA32_PEBS_INDEX, cached.full); -+ if (index.split.thresh != cached.split.thresh || !cached.split.en) { -+ if (cached.split.thresh == ARCH_PEBS_THRESH_MUL && -+ cached.split.wr > 0) { -+ /* -+ * Large PEBS was enabled. -+ * Drain PEBS buffer before applying the single PEBS. -+ */ -+ intel_pmu_drain_pebs_buffer(); -+ } else { -+ index.split.wr = 0; -+ index.split.full = 0; -+ index.split.en = 1; -+ wrmsrq(MSR_IA32_PEBS_INDEX, index.full); -+ } -+ } -+ } -+ -+ if (cpuc->cfg_c_val[hwc->idx] != ext) -+ __intel_pmu_update_event_ext(hwc->idx, ext); -+} -+ - static void intel_pmu_enable_event(struct perf_event *event) - { - u64 enable_mask = ARCH_PERFMON_EVENTSEL_ENABLE; -@@ -2959,10 +3056,12 @@ static void intel_pmu_enable_event(struct perf_event *event) - enable_mask |= ARCH_PERFMON_EVENTSEL_BR_CNTR; - intel_set_masks(event, idx); - static_call_cond(intel_pmu_enable_acr_event)(event); -+ intel_pmu_enable_event_ext(event); - __x86_pmu_enable_event(hwc, enable_mask); - break; - case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1: - static_call_cond(intel_pmu_enable_acr_event)(event); -+ intel_pmu_enable_event_ext(event); - fallthrough; - case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END: - intel_pmu_enable_fixed(event); -@@ -5440,6 +5539,29 @@ static inline bool intel_pmu_broken_perf_cap(void) - return false; - } - -+static inline void __intel_update_pmu_caps(struct pmu *pmu) -+{ -+ struct pmu *dest_pmu = pmu ? pmu : x86_get_pmu(smp_processor_id()); -+ -+ if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_XMM) -+ dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS; -+} -+ -+static inline void __intel_update_large_pebs_flags(struct pmu *pmu) -+{ -+ u64 caps = hybrid(pmu, arch_pebs_cap).caps; -+ -+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_TIME; -+ if (caps & ARCH_PEBS_LBR) -+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_BRANCH_STACK; -+ -+ if (!(caps & ARCH_PEBS_AUX)) -+ x86_pmu.large_pebs_flags &= ~PERF_SAMPLE_DATA_SRC; -+ if (!(caps & ARCH_PEBS_GPR)) -+ x86_pmu.large_pebs_flags &= -+ ~(PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER); -+} -+ - static void update_pmu_cap(struct pmu *pmu) - { - unsigned int eax, ebx, ecx, edx; -@@ -5480,6 +5602,9 @@ static void update_pmu_cap(struct pmu *pmu) - &eax, &ebx, &ecx, &edx); - hybrid(pmu, arch_pebs_cap).counters = ((u64)ecx << 32) | eax; - hybrid(pmu, arch_pebs_cap).pdists = ((u64)edx << 32) | ebx; -+ -+ __intel_update_pmu_caps(pmu); -+ __intel_update_large_pebs_flags(pmu); - } else { - WARN_ON(x86_pmu.arch_pebs == 1); - x86_pmu.arch_pebs = 0; -@@ -5640,6 +5765,8 @@ static void intel_pmu_cpu_starting(int cpu) - } - } - -+ __intel_update_pmu_caps(cpuc->pmu); -+ - if (!cpuc->shared_regs) - return; - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index f8b5275409d6..530f5a8bc9d2 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1513,6 +1513,18 @@ pebs_update_state(bool needed_cb, struct cpu_hw_events *cpuc, - } - } - -+u64 intel_get_arch_pebs_data_config(struct perf_event *event) -+{ -+ u64 pebs_data_cfg = 0; -+ -+ if (WARN_ON(event->hw.idx < 0 || event->hw.idx >= X86_PMC_IDX_MAX)) -+ return 0; -+ -+ pebs_data_cfg |= pebs_update_adaptive_cfg(event); -+ -+ return pebs_data_cfg; -+} -+ - void intel_pmu_pebs_add(struct perf_event *event) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -@@ -2969,6 +2981,11 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs, - - index.split.wr = 0; - index.split.full = 0; -+ index.split.en = 1; -+ if (cpuc->n_pebs == cpuc->n_large_pebs) -+ index.split.thresh = ARCH_PEBS_THRESH_MUL; -+ else -+ index.split.thresh = ARCH_PEBS_THRESH_SINGLE; - wrmsrq(MSR_IA32_PEBS_INDEX, index.full); - } - -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index 82e8c20611b9..db4ec2975de4 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -304,6 +304,8 @@ struct cpu_hw_events { - /* Intel ACR configuration */ - u64 acr_cfg_b[X86_PMC_IDX_MAX]; - u64 acr_cfg_c[X86_PMC_IDX_MAX]; -+ /* Cached CFG_C values */ -+ u64 cfg_c_val[X86_PMC_IDX_MAX]; - - /* - * Intel LBR bits -@@ -1216,6 +1218,14 @@ static inline unsigned int x86_pmu_fixed_ctr_addr(int index) - x86_pmu.addr_offset(index, false) : index); - } - -+static inline unsigned int x86_pmu_cfg_c_addr(int index, bool gp) -+{ -+ u32 base = gp ? MSR_IA32_PMC_V6_GP0_CFG_C : MSR_IA32_PMC_V6_FX0_CFG_C; -+ -+ return base + (x86_pmu.addr_offset ? x86_pmu.addr_offset(index, false) : -+ index * MSR_IA32_PMC_V6_STEP); -+} -+ - static inline int x86_pmu_rdpmc_index(int index) - { - return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index; -@@ -1779,6 +1789,8 @@ void intel_pmu_pebs_data_source_cmt(void); - - void intel_pmu_pebs_data_source_lnl(void); - -+u64 intel_get_arch_pebs_data_config(struct perf_event *event); -+ - int intel_pmu_setup_lbr_filter(struct perf_event *event); - - void intel_pt_interrupt(void); -diff --git a/arch/x86/include/asm/intel_ds.h b/arch/x86/include/asm/intel_ds.h -index 023c2883f9f3..7bb80c993bef 100644 ---- a/arch/x86/include/asm/intel_ds.h -+++ b/arch/x86/include/asm/intel_ds.h -@@ -7,6 +7,13 @@ - #define PEBS_BUFFER_SHIFT 4 - #define PEBS_BUFFER_SIZE (PAGE_SIZE << PEBS_BUFFER_SHIFT) - -+/* -+ * The largest PEBS record could consume a page, ensure -+ * a record at least can be written after triggering PMI. -+ */ -+#define ARCH_PEBS_THRESH_MUL ((PEBS_BUFFER_SIZE - PAGE_SIZE) >> PEBS_BUFFER_SHIFT) -+#define ARCH_PEBS_THRESH_SINGLE 1 -+ - /* The maximal number of PEBS events: */ - #define MAX_PEBS_EVENTS_FMT4 8 - #define MAX_PEBS_EVENTS 32 -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 7c306dd0713c..737d51629c03 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -330,6 +330,14 @@ - #define ARCH_PEBS_OFFSET_MASK 0x7fffff - #define ARCH_PEBS_INDEX_WR_SHIFT 4 - -+#define ARCH_PEBS_RELOAD 0xffffffff -+#define ARCH_PEBS_LBR_SHIFT 40 -+#define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT) -+#define ARCH_PEBS_VECR_XMM BIT_ULL(49) -+#define ARCH_PEBS_GPR BIT_ULL(61) -+#define ARCH_PEBS_AUX BIT_ULL(62) -+#define ARCH_PEBS_EN BIT_ULL(63) -+ - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) - #define RTIT_CTL_CYCLEACC BIT(1) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu b/SPECS/kernel-rt/0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu deleted file mode 100644 index f25fcb7d1..000000000 --- a/SPECS/kernel-rt/0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu +++ /dev/null @@ -1,1937 +0,0 @@ -From 31b97b296aa51821c160494b464e2619cd24ac2e Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Sun, 21 Sep 2025 11:51:07 +0800 -Subject: [PATCH 19/21] porting gmsl isx031 code between PTL IPU7 beta release - and PV release - -Update max9x deserializer driver and isx031 sensor driver. -Recent sensor driver changes are pushed in ipu6-drivers repo -under drivers/media/i2c path. - -Strip code is only needed for internal use to strip out -kernel version macro unrelated to the target kernel version. - -Signed-off-by: linya14x -Signed-off-by: hepengpx ---- - drivers/media/i2c/isx031.c | 410 +++++++++++++++++++------ - drivers/media/i2c/max9x/Makefile | 10 +- - drivers/media/i2c/max9x/max9295.c | 38 +-- - drivers/media/i2c/max9x/max9296.c | 46 +-- - drivers/media/i2c/max9x/max9296.h | 2 +- - drivers/media/i2c/max9x/max96724.c | 55 ++-- - drivers/media/i2c/max9x/max9x_pdata.h | 1 + - drivers/media/i2c/max9x/regmap-retry.c | 52 ++++ - drivers/media/i2c/max9x/regmap-retry.h | 18 ++ - drivers/media/i2c/max9x/serdes.c | 216 ++++++------- - drivers/media/i2c/max9x/serdes.h | 6 +- - 11 files changed, 551 insertions(+), 303 deletions(-) - create mode 100644 drivers/media/i2c/max9x/regmap-retry.c - create mode 100644 drivers/media/i2c/max9x/regmap-retry.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index ddc3e512efa4..3cf638c86438 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -17,6 +17,11 @@ - #include - #define to_isx031(_sd) container_of(_sd, struct isx031, sd) - -+#define ISX031_OTP_TYPE_NAME_L 0x7E8A -+#define ISX031_OTP_TYPE_NAME_H 0x7E8B -+#define ISX031_OTP_TYPE_NAME_H_FIELD 0x0F -+#define ISX031_OTP_MODULE_ID_L 0x031 -+ - #define ISX031_REG_MODE_SET_F 0x8A01 - #define ISX031_MODE_STANDBY 0x00 - #define ISX031_MODE_STREAMING 0x80 -@@ -33,6 +38,9 @@ - #define ISX031_MODE_4LANES_30FPS 0x17 - #define ISX031_MODE_2LANES_30FPS 0x18 - -+/* To serialize asynchronous callbacks */ -+static DEFINE_MUTEX(isx031_mutex); -+ - struct isx031_reg { - enum { - ISX031_REG_LEN_DELAY = 0, -@@ -52,13 +60,17 @@ struct isx031_link_freq_config { - const struct isx031_reg_list reg_list; - }; - --struct isx031_driver_mode { -+struct isx031_drive_mode { - int lanes; - int fps; - int mode; - }; - --static const struct isx031_driver_mode isx031_driver_modes[] = { -+struct isx031_info { -+ bool is_direct; -+}; -+ -+static const struct isx031_drive_mode isx031_drive_modes[] = { - { 4, 60, ISX031_MODE_4LANES_60FPS }, - { 4, 30, ISX031_MODE_4LANES_30FPS }, - { 2, 30, ISX031_MODE_2LANES_30FPS }, -@@ -94,9 +106,6 @@ struct isx031 { - const struct isx031_mode *pre_mode; - u8 lanes; - -- /* To serialize asynchronus callbacks */ -- struct mutex mutex; -- - /* i2c client */ - struct i2c_client *client; - -@@ -105,6 +114,15 @@ struct isx031 { - - /* Streaming on/off */ - bool streaming; -+ -+ struct v4l2_ctrl_handler ctrls; -+ -+ /* MIPI direct connection */ -+ bool is_direct; -+}; -+ -+static const s64 isx031_link_frequencies[] = { -+ 300000000ULL - }; - - static const struct isx031_reg isx031_init_reg[] = { -@@ -116,7 +134,7 @@ static const struct isx031_reg isx031_init_reg[] = { - static const struct isx031_reg isx031_framesync_reg[] = { - /* External sync */ - {ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */ -- {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ -+ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ - {ISX031_REG_LEN_08BIT, 0x0153, 0x00}, - {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */ - {ISX031_REG_LEN_08BIT, 0x0144, 0x00}, -@@ -242,24 +260,8 @@ static const struct isx031_mode supported_modes[] = { - }, - }; - --static int isx031_reset(struct gpio_desc *reset_gpio) -+static int isx031_read_reg(struct i2c_client *client, u16 reg, u16 len, u32 *val) - { -- if (!IS_ERR_OR_NULL(reset_gpio)) { -- gpiod_set_value_cansleep(reset_gpio, 0); -- usleep_range(500, 1000); -- gpiod_set_value_cansleep(reset_gpio, 1); -- /*Needs to sleep for quite a while before register writes*/ -- usleep_range(200 * 1000, 200 * 1000 + 500); -- -- return 0; -- } -- -- return -EINVAL; --} -- --static int isx031_read_reg(struct isx031 *isx031, u16 reg, u16 len, u32 *val) --{ -- struct i2c_client *client = isx031->client; - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = {0}; -@@ -306,8 +308,24 @@ static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) - return 0; - } - -+static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ int ret; -+ int retry = 100; -+ -+ while (retry--) { -+ ret = isx031_write_reg(isx031, reg, len, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ - static int isx031_write_reg_list(struct isx031 *isx031, -- const struct isx031_reg_list *r_list) -+ const struct isx031_reg_list *r_list, -+ bool isRetry) - { - struct i2c_client *client = v4l2_get_subdevdata(&isx031->sd); - unsigned int i; -@@ -318,9 +336,14 @@ static int isx031_write_reg_list(struct isx031 *isx031, - msleep(r_list->regs[i].val); - continue; - } -- ret = isx031_write_reg(isx031, r_list->regs[i].address, -- ISX031_REG_LEN_08BIT, -- r_list->regs[i].val); -+ ret = isRetry ? -+ isx031_write_reg_retry(isx031, -+ r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val) : -+ isx031_write_reg(isx031, r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", -@@ -332,28 +355,29 @@ static int isx031_write_reg_list(struct isx031 *isx031, - return 0; - } - --static int isx031_find_driver_mode(int lanes, int fps) -+static int isx031_find_drive_mode(int lanes, int fps) - { - int i; - -- for (i = 0; i < ARRAY_SIZE(isx031_driver_modes); i++) { -- if (isx031_driver_modes[i].lanes == lanes && isx031_driver_modes[i].fps == fps) -- return isx031_driver_modes[i].mode; -+ for (i = 0; i < ARRAY_SIZE(isx031_drive_modes); i++) { -+ if (isx031_drive_modes[i].lanes == lanes && isx031_drive_modes[i].fps == fps) -+ return isx031_drive_modes[i].mode; - } - - return -EINVAL; - } - --static int isx031_set_driver_mode(struct isx031 *isx031) -+static int isx031_set_drive_mode(struct isx031 *isx031) - { - int ret; - int mode; - -- mode = isx031_find_driver_mode(isx031->lanes, isx031->cur_mode->fps); -+ mode = isx031_find_drive_mode(isx031->lanes, isx031->cur_mode->fps); - if (mode < 0) - return mode; - -- ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); -+ - return ret; - } - -@@ -372,7 +396,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - - retry = 50; - while (retry--) { -- ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, &val); - if (ret == 0) - break; -@@ -380,10 +404,13 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - } - cur_mode = val; - -- //TODO: only set if isx031->lanes != 0, means get lanes from pdata -- ret = isx031_set_driver_mode(isx031); -+ /* Note: Ideally, drive mode should only be set if isx031->lanes != 0, -+ * which would mean the number of lanes is obtained from platform data. -+ * Currently, drive mode is always set. -+ */ -+ ret = isx031_set_drive_mode(isx031); - if (ret) { -- dev_err(&client->dev, "failed to set driver mode"); -+ dev_err(&client->dev, "failed to set drive mode"); - return ret; - } - -@@ -394,17 +421,17 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return ret; - } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -- mode); -+ (u32)mode); - if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", - cur_mode, mode); - return ret; - } - -- /*streaming transit to standby need 1 frame+5ms*/ -+ /* streaming transit to standby need 1 frame+5ms */ - retry = 50; - while (retry--) { -- ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, &val); - if (ret == 0 && val == state) - break; -@@ -414,7 +441,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return 0; - } - --static int isx031_identify_module(struct isx031 *isx031) -+static int isx031_initialize_module(struct isx031 *isx031) - { - struct i2c_client *client = isx031->client; - int ret; -@@ -422,7 +449,7 @@ static int isx031_identify_module(struct isx031 *isx031) - u32 val; - - while (retry--) { -- ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, &val); - if (ret == 0) - break; -@@ -434,17 +461,17 @@ static int isx031_identify_module(struct isx031 *isx031) - - dev_dbg(&client->dev, "sensor in mode 0x%x", val); - -- /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly*/ -+ /* if sensor already in ISX031_STATE_STARTUP, can access i2c write directly */ - if (val == ISX031_STATE_STREAMING) { - if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) - return ret; - } - -- ret = isx031_write_reg_list(isx031, &isx031_init_reg_list); -+ ret = isx031_write_reg_list(isx031, &isx031_init_reg_list, true); - if (ret) - return ret; - if (isx031->platform_data != NULL && !isx031->platform_data->irq_pin_flags) { -- ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list); -+ ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list, false); - if (ret) { - dev_err(&client->dev, "failed in set framesync."); - return ret; -@@ -454,6 +481,49 @@ static int isx031_identify_module(struct isx031 *isx031) - return 0; - } - -+static int isx031_identify_module(struct i2c_client *client) -+{ -+ u32 NAME_L = 0; -+ u32 NAME_H = 0; -+ int ret = 0; -+ int i = 0; -+ int retry = 50; -+ -+ for (i = 0; i < retry; i++) { -+ ret = isx031_read_reg(client, ISX031_OTP_TYPE_NAME_L, -+ ISX031_REG_LEN_08BIT, &NAME_L); -+ if (!ret) -+ break; -+ } -+ -+ if (i == retry) { -+ dev_err(&client->dev, "isx031 read NAME_L failed"); -+ return ret; -+ } -+ -+ for (i = 0; i < retry; i++) { -+ ret = isx031_read_reg(client, ISX031_OTP_TYPE_NAME_H, -+ ISX031_REG_LEN_08BIT, &NAME_H); -+ if (!ret) -+ break; -+ } -+ -+ if (i == retry) { -+ dev_err(&client->dev, "isx031 read NAME_H failed"); -+ return ret; -+ } -+ -+ if (((NAME_H & ISX031_OTP_TYPE_NAME_H_FIELD) << 8 | NAME_L) != -+ ISX031_OTP_MODULE_ID_L) { -+ dev_err(&client->dev, "isx031 module id mismatch: 0x%4.4x\n", -+ ((NAME_H & ISX031_OTP_TYPE_NAME_H_FIELD) << 8 | -+ NAME_L)); -+ return -ENODEV; -+ } -+ -+ return ret; -+} -+ - static void isx031_update_pad_format(const struct isx031_mode *mode, - struct v4l2_mbus_framefmt *fmt) - { -@@ -463,6 +533,45 @@ static void isx031_update_pad_format(const struct isx031_mode *mode, - fmt->field = V4L2_FIELD_NONE; - } - -+static int isx031_get_mipi_lane(struct isx031 *isx031, struct device *dev) -+{ -+ struct fwnode_handle *endpoint; -+ struct v4l2_fwnode_endpoint bus_cfg = { -+ .bus_type = V4L2_MBUS_CSI2_DPHY, -+ }; -+ -+ int ret; -+ -+ endpoint = -+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!endpoint) { -+ dev_err(dev, "endpoint node not found"); -+ return -EPROBE_DEFER; -+ } -+ -+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); -+ if (ret) { -+ dev_err(dev, "parsing endpoint node fail"); -+ goto out_err; -+ } -+ -+ /* Check the number of MIPI CSI2 data lanes */ -+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && -+ bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { -+ dev_err(dev, "only 2 or 4 data lanes are currently supported"); -+ goto out_err; -+ } -+ -+ isx031->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; -+ -+out_err: -+ v4l2_fwnode_endpoint_free(&bus_cfg); -+ fwnode_handle_put(endpoint); -+ -+ return ret; -+} -+ - static int isx031_start_streaming(struct isx031 *isx031) - { - int ret; -@@ -471,7 +580,7 @@ static int isx031_start_streaming(struct isx031 *isx031) - - if (isx031->cur_mode != isx031->pre_mode) { - reg_list = &isx031->cur_mode->reg_list; -- ret = isx031_write_reg_list(isx031, reg_list); -+ ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "failed to set stream mode"); - return ret; -@@ -481,6 +590,14 @@ static int isx031_start_streaming(struct isx031 *isx031) - dev_dbg(&client->dev, "same mode, skip write reg list"); - } - -+ if (isx031->is_direct) { -+ ret = __v4l2_ctrl_handler_setup(&isx031->ctrls); -+ if (ret) { -+ dev_err(&client->dev, "failed to setup ctrls"); -+ return ret; -+ } -+ } -+ - ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); - if (ret) { - dev_err(&client->dev, "failed to start streaming"); -@@ -506,13 +623,12 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - if (isx031->streaming == enable) - return 0; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - - ret = isx031_start_streaming(isx031); -@@ -528,7 +644,8 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - - isx031->streaming = enable; - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(&isx031_mutex); - - return ret; - } -@@ -553,11 +670,14 @@ static int __maybe_unused isx031_suspend(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - if (isx031->streaming) - isx031_stop_streaming(isx031); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); -+ -+ /* Active low gpio reset, set 1 to power off sensor */ -+ gpiod_set_value_cansleep(isx031->reset_gpio, 1); - - return 0; - } -@@ -568,38 +688,60 @@ static int __maybe_unused isx031_resume(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - const struct isx031_reg_list *reg_list; -- int ret; -+ int count = 0; -+ int ret = 0; -+ -+ mutex_lock(&isx031_mutex); -+ -+ /* Active low gpio reset, set 0 to power on sensor, -+ * sensor must on back before start resume -+ */ -+ if (isx031->reset_gpio != NULL) { -+ do { -+ gpiod_set_value_cansleep(isx031->reset_gpio, 0); -+ ret = gpiod_get_value_cansleep(isx031->reset_gpio); -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ if (++count >= 20) { -+ dev_err(&client->dev, -+ "%s: failed to power on reset gpio, reset gpio is %d", -+ __func__, ret); -+ break; -+ } -+ } while (ret != 0); -+ } - -- if (isx031->reset_gpio != NULL) -- isx031_reset(isx031->reset_gpio); -+ ret = isx031_identify_module(isx031->client); -+ if (ret) { -+ dev_err(&client->dev, "isx031 identify module failed"); -+ goto err_unlock; -+ } - -- ret = isx031_identify_module(isx031); -+ ret = isx031_initialize_module(isx031); - if (ret == 0) { - reg_list = &isx031->cur_mode->reg_list; -- ret = isx031_write_reg_list(isx031, reg_list); -+ ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); -- return ret; -+ goto err_unlock; - } - } else { -- dev_err(&client->dev, "isx031 resume failed"); -- return ret; -+ dev_err(&client->dev, "isx031 resume initialization failed"); -+ goto err_unlock; - } -- -- mutex_lock(&isx031->mutex); - if (isx031->streaming) { - ret = isx031_start_streaming(isx031); - if (ret) { - isx031->streaming = false; - isx031_stop_streaming(isx031); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - } - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(&isx031_mutex); - -- return 0; -+ return ret; - } - - static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -@@ -638,7 +780,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - if (i >= ARRAY_SIZE(supported_modes)) - mode = &supported_modes[0]; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -@@ -647,7 +789,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - isx031->cur_mode = mode; - } - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); - - return 0; - } -@@ -658,26 +800,24 @@ static int isx031_get_format(struct v4l2_subdev *sd, - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - isx031_update_pad_format(isx031->cur_mode, &fmt->format); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); - - return 0; - } - - static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - { -- struct isx031 *isx031 = to_isx031(sd); -- -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - isx031_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); - - return 0; - } -@@ -707,15 +847,42 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { - .open = isx031_open, - }; - -+static int isx031_set_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ return 0; -+}; -+ -+static const struct v4l2_ctrl_ops isx031_ctrl_ops = { -+ .s_ctrl = isx031_set_ctrl, -+}; -+ -+static int isx031_ctrls_init(struct isx031 *sensor) -+{ -+ int ret = 0; -+ struct v4l2_ctrl *ctrl; -+ -+ v4l2_ctrl_handler_init(&sensor->ctrls, 10); -+ -+ /* There's a need to set the link frequency because IPU6 dictates it. */ -+ ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &isx031_ctrl_ops, -+ V4L2_CID_LINK_FREQ, -+ ARRAY_SIZE(isx031_link_frequencies) - 1, 0, -+ isx031_link_frequencies); -+ -+ if (ctrl) -+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ -+ sensor->sd.ctrl_handler = &sensor->ctrls; -+ return ret; -+} -+ - static void isx031_remove(struct i2c_client *client) - { - struct v4l2_subdev *sd = i2c_get_clientdata(client); -- struct isx031 *isx031 = to_isx031(sd); - - v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); - - } - -@@ -723,6 +890,7 @@ static int isx031_probe(struct i2c_client *client) - { - struct v4l2_subdev *sd; - struct isx031 *isx031; -+ const struct isx031_info *info; - const struct isx031_reg_list *reg_list; - int ret; - -@@ -736,19 +904,37 @@ static int isx031_probe(struct i2c_client *client) - dev_warn(&client->dev, "no platform data provided\n"); - - isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", -- GPIOD_OUT_HIGH); -+ GPIOD_OUT_LOW); -+ - if (IS_ERR(isx031->reset_gpio)) - return -EPROBE_DEFER; - else if (isx031->reset_gpio == NULL) - dev_warn(&client->dev, "Reset GPIO not found"); -- else { -- dev_dbg(&client->dev, "Found reset GPIO"); -- isx031_reset(isx031->reset_gpio); -+ else -+ dev_dbg(&client->dev, "Reset GPIO found"); -+ -+ ret = isx031_identify_module(client); -+ if (ret) { -+ dev_err(&client->dev, "isx031 identify module failed"); -+ return ret; - } - -+ info = device_get_match_data(&client->dev); -+ if (info) -+ isx031->is_direct = info->is_direct; -+ else -+ isx031->is_direct = false; -+ - /* initialize subdevice */ - sd = &isx031->sd; - v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops); -+ if (isx031->is_direct) { -+ ret = isx031_ctrls_init(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to init sensor ctrls: %d", ret); -+ return ret; -+ } -+ } - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - sd->internal_ops = &isx031_internal_ops; - sd->entity.ops = &isx031_subdev_entity_ops; -@@ -758,36 +944,47 @@ static int isx031_probe(struct i2c_client *client) - isx031->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad); - if (ret < 0) { -- dev_err(&client->dev, -- "%s : media entity init Failed %d\n", __func__, ret); -- return ret; -+ dev_err(&client->dev, "failed to init entity pads: %d", ret); -+ goto probe_error_v4l2_ctrl_handler_free; - } - -- ret = isx031_identify_module(isx031); -- if (ret) { -- dev_err(&client->dev, "failed to find sensor: %d", ret); -- return ret; -+ if (isx031->is_direct) { -+ isx031->sd.state_lock = isx031->sd.ctrl_handler->lock; -+ v4l2_subdev_init_finalize(&isx031->sd); - } - -- if (isx031->platform_data) -+ if (isx031->platform_data && isx031->platform_data->suffix[0]) - snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %s", - isx031->platform_data->suffix); - - if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - -- mutex_init(&isx031->mutex); -+ if (isx031->is_direct) { -+ /* mipi sensor read info from fwnode entrypoint bus cfg */ -+ ret = isx031_get_mipi_lane(isx031, &client->dev); -+ if (ret) { -+ dev_err(&client->dev, "failed to get MIPI lane configuration"); -+ return ret; -+ } -+ } - - /* 1920x1536 default */ -- isx031->cur_mode = NULL; -- isx031->pre_mode = &supported_modes[0]; -- reg_list = &isx031->pre_mode->reg_list; -- ret = isx031_write_reg_list(isx031, reg_list); -+ isx031->pre_mode = NULL; -+ isx031->cur_mode = &supported_modes[0]; -+ ret = isx031_initialize_module(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to initialize sensor: %d", ret); -+ return ret; -+ } -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "failed to apply preset mode"); - goto probe_error_media_entity_cleanup; - } -- isx031->cur_mode = isx031->pre_mode; -+ isx031->pre_mode = isx031->cur_mode; -+ - ret = v4l2_async_register_subdev_sensor(&isx031->sd); - if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", -@@ -807,8 +1004,9 @@ static int isx031_probe(struct i2c_client *client) - - probe_error_media_entity_cleanup: - media_entity_cleanup(&isx031->sd.entity); -- pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); -+ -+probe_error_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(isx031->sd.ctrl_handler); - - return ret; - } -@@ -819,13 +1017,25 @@ static const struct dev_pm_ops isx031_pm_ops = { - - static const struct i2c_device_id isx031_id_table[] = { - { "isx031", 0 }, -- { /* sentinel */ }, -+ {}, - }; - MODULE_DEVICE_TABLE(i2c, isx031_id_table); - -+static const struct isx031_info isx031_mipi_info = { -+ .is_direct = true, -+}; -+ -+static const struct acpi_device_id isx031_acpi_ids[] = { -+ { "INTC3031", (kernel_ulong_t)&isx031_mipi_info }, -+ {} -+}; -+ -+MODULE_DEVICE_TABLE(acpi, isx031_acpi_ids); -+ - static struct i2c_driver isx031_i2c_driver = { - .driver = { - .name = "isx031", -+ .acpi_match_table = ACPI_PTR(isx031_acpi_ids), - .pm = &isx031_pm_ops, - }, - .probe = isx031_probe, -@@ -835,6 +1045,8 @@ static struct i2c_driver isx031_i2c_driver = { - - module_i2c_driver(isx031_i2c_driver); - --MODULE_AUTHOR("Hao Yao "); - MODULE_DESCRIPTION("isx031 sensor driver"); -+MODULE_AUTHOR("Hao Yao "); -+MODULE_AUTHOR("Jonathan Lui "); -+MODULE_AUTHOR("Wei Khang, Goh "); - MODULE_LICENSE("GPL v2"); -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -index ab533b790f72..fce29b31ed68 100644 ---- a/drivers/media/i2c/max9x/Makefile -+++ b/drivers/media/i2c/max9x/Makefile -@@ -1,10 +1,2 @@ --ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU7)),) --ccflags-y += -I$(src)/../../pci/intel/ipu7/ --endif -- --ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU6)),) --ccflags-y += -I$(src)/../../pci/intel/ipu6/ --endif -- - obj-m += max9x.o --max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -+max9x-y += regmap-retry.o serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index cc1ee2f5ff92..b83b344cdc6b 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -30,6 +30,7 @@ - #include - - #include "max9295.h" -+#include "regmap-retry.h" - - static const char *const max9295_gpio_chip_names[] = { - "MFP1", -@@ -176,7 +177,7 @@ static int max9295_setup_gpio(struct max9x_common *common) - { - struct device *dev = common->dev; - int ret; -- struct max9x_gpio_pdata *gpio_pdata; -+ struct max9x_gpio_pdata *gpio_pdata = NULL; - - if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -@@ -261,14 +262,14 @@ static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, - struct device *dev = common->dev; - struct regmap *map = common->map; - int data_type_slot, dt; -- int ret; -+ int ret = 0; - - for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { - dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; - dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", - pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); - -- TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), - MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -@@ -522,13 +523,13 @@ static int max9295_enable_rclk(struct max9x_common *common) - ); - - // Configure reference generation output on MFP4 -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_GPIO_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) - ); - - // Enable output -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) - ); -@@ -553,7 +554,7 @@ static int max9295_set_local_control_channel_enabled(struct max9x_common *common - - dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ return regmap_update_bits_retry(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, - MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); - } - -@@ -575,8 +576,8 @@ static int max9295_verify_devid(struct max9x_common *common) - int ret; - - // Fetch and output chip name + revision -- TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -- TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_REV, &dev_rev)); - - switch (dev_id) { - case MAX9295A: -@@ -617,10 +618,10 @@ static int max9295_enable(struct max9x_common *common) - TRY(ret, max9295_set_local_control_channel_enabled(common, false)); - - /* Clear the pipe maps */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_9, 0)); - - /* Clear the csi port selections */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); - - return 0; - } -@@ -694,15 +695,14 @@ static int max9295_remap_reset(struct max9x_common *common) - struct device *dev = common->dev; - struct max9x_pdata *pdata = dev->platform_data; - u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -- common->client->addr; -+ common->client->addr; - u32 virt_addr = common->client->addr; - - dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, - phys_addr); - -- TRY(ret, regmap_update_bits( -- common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -- FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ TRY(ret, regmap_update_bits(common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); - - return 0; - } -@@ -717,18 +717,18 @@ static int max9295_add_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr || src == 0) { - dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", - MAX9295_I2C_SRC(i2c_id, alias), virt_addr, - MAX9295_I2C_DST(i2c_id, alias), phys_addr); -- TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) - ); - -- TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_SRC(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) - ); - } -@@ -746,10 +746,10 @@ static int max9295_remove_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr) { -- return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ return regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); - } - } -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index 41074d60cc01..be144f188de0 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -215,14 +215,14 @@ static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_i - * For second pair of PHYs, first lanes are on the master PHY too. - * - * PHY 0 + 1 -- * CLK = PHY 1 -+ * CLK is on PHY 1 - * PHY1 Lane 0 = D0 - * PHY1 Lane 1 = D1 - * PHY0 Lane 0 = D2 - * PHY0 Lane 1 = D3 - * - * PHY 2 + 3 -- * CLK = PHY 2 -+ * CLK is on PHY 2 - * PHY2 Lane 0 = D0 - * PHY2 Lane 1 = D1 - * PHY3 Lane 0 = D2 -@@ -500,7 +500,7 @@ static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned in - { - struct device *dev = common->dev; - struct max9x_serdes_csi_link *csi_link; -- int ret; -+ int ret = 0; - - if (csi_id > common->num_csi_links) - return -EINVAL; -@@ -513,45 +513,49 @@ static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned in - if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, "Tried to enable CSI port with no lanes???")) - return -EINVAL; - -- if (enable) -- csi_link->usecount++; -- else if (csi_link->usecount > 0) -- csi_link->usecount--; -+ mutex_lock(&csi_link->csi_mutex); - - dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, (enable ? "enable" : "disable"), csi_link->usecount); - -- if (enable && csi_link->usecount == 1) { -+ if (enable && csi_link->usecount == 0) { - // Enable && first user - ret = max9296_set_initial_deskew(common, csi_id, csi_link->config.auto_init_deskew_enabled, - csi_link->config.initial_deskew_width); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_dpll_freq(common, csi_id, csi_link->config.freq_mhz); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_dpll_enabled(common, csi_id, true); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_enabled(common, csi_id, true); - if (ret) -- return ret; -+ goto err_unlock; - -- } else if (!enable && csi_link->usecount == 0) { -+ } else if (!enable && csi_link->usecount == 1) { - // Disable && no more users - ret = max9296_set_phy_enabled(common, csi_id, false); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_dpll_enabled(common, csi_id, false); - if (ret) -- return ret; -- -+ goto err_unlock; - } - -- return 0; -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+err_unlock: -+ mutex_unlock(&csi_link->csi_mutex); -+ -+ return ret; - } - - static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable) -@@ -624,10 +628,11 @@ static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned - if (ret) - return ret; - -- if (common->csi_link[config->map[map_id].dst_csi].config.auto_start) { -+ if (!config->map[map_id].is_csi_enabled && common->csi_link[config->map[map_id].dst_csi].config.auto_start) { - ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, true); - if (ret) - return ret; -+ config->map[map_id].is_csi_enabled = true; - } - } - -@@ -730,7 +735,7 @@ static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned i - link_cfg = MAX9296_LINK_B; - else { - dev_err(dev, "No link was detected"); -- return -1; -+ return -EINVAL; - } - - dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -@@ -807,9 +812,12 @@ static int max9296_disable_serial_link(struct max9x_common *common, unsigned int - return ret; - - for (map_id = 0; map_id < config->num_maps; map_id++) { -+ if (!config->map[map_id].is_csi_enabled) -+ continue; - ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); - if (ret) - return ret; -+ config->map[map_id].is_csi_enabled = false; - } - } - -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -index 38c344669df4..acc6bb03405b 100644 ---- a/drivers/media/i2c/max9x/max9296.h -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -49,7 +49,7 @@ enum max9296_link_mode { - #define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ - /* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ - --#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 350 - - #define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 - -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index b214e1176963..1d253a6faf71 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -30,6 +30,7 @@ - #include - - #include "max96724.h" -+#include "regmap-retry.h" - - // Params - int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -@@ -337,7 +338,7 @@ static int max96724_set_all_reset(struct max9x_common *common, bool enable) - - dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX96724_RESET_ALL, -+ return regmap_update_bits_retry(map, MAX96724_RESET_ALL, - MAX96724_RESET_ALL_FIELD, - MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); - } -@@ -354,8 +355,8 @@ static int max96724_soft_reset(struct max9x_common *common) - return ret; - - /* Wait for hardware available after soft reset */ -- /* TODO: Optimize sleep time 45 ms */ -- msleep(45); -+ /* TODO: Optimize sleep time 20 ms */ -+ msleep(20); - - ret = max96724_set_all_reset(common, 0); - if (ret) -@@ -454,7 +455,7 @@ static int max96724_set_serial_link_rate(struct max9x_common *common, unsigned i - struct device *dev = common->dev; - struct regmap *map = common->map; - struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -- unsigned int tx_rate, rx_rate; -+ int tx_rate, rx_rate; - - tx_rate = max9x_serdes_mhz_to_rate(max96724_tx_rates, ARRAY_SIZE(max96724_tx_rates), config->tx_freq_mhz); - if (tx_rate < 0) -@@ -570,7 +571,7 @@ static int max96724_set_csi_link_enabled(struct max9x_common *common, - { - struct device *dev = common->dev; - struct max9x_serdes_csi_link *csi_link; -- int ret; -+ int ret = 0; - - if (csi_id > common->num_csi_links) - return -EINVAL; -@@ -585,32 +586,37 @@ static int max96724_set_csi_link_enabled(struct max9x_common *common, - "Tried to enable CSI port with no lanes???")) - return -EINVAL; - -- // Keep track of number of enabled maps using this CSI link -- if (enable) -- csi_link->usecount++; -- else if (csi_link->usecount > 0) -- csi_link->usecount--; -+ mutex_lock(&csi_link->csi_mutex); - - dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, - (enable ? "enable" : "disable"), csi_link->usecount); - -- if (enable && csi_link->usecount == 1) { -+ if (enable && csi_link->usecount == 0) { - // Enable && first user - - ret = max96724_set_phy_enabled(common, csi_id, true); - if (ret) -- return ret; -+ goto err_unlock; - -- } else if (!enable && csi_link->usecount == 0) { -+ } else if (!enable && csi_link->usecount == 1) { - // Disable && no more users - - ret = max96724_set_phy_enabled(common, csi_id, false); - if (ret) -- return ret; -+ goto err_unlock; - - } - -- return 0; -+ // Keep track of number of enabled maps using this CSI link -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+err_unlock: -+ mutex_unlock(&csi_link->csi_mutex); -+ -+ return ret; - } - - static int max96724_csi_double_pixel(struct max9x_common *common, -@@ -719,11 +725,14 @@ static int max96724_set_serial_link_routing(struct max9x_common *common, - if (ret) - return ret; - -- ret = max96724_set_csi_link_enabled(common, -+ if (!config->map[map_id].is_csi_enabled) { -+ ret = max96724_set_csi_link_enabled(common, - config->map[map_id].dst_csi, - true); -- if (ret) -- return ret; -+ if (ret) -+ return ret; -+ config->map[map_id].is_csi_enabled = true; -+ } - - ret = max96724_csi_double_pixel(common, - config->map[map_id].dst_csi, -@@ -766,16 +775,18 @@ static int max96724_disable_serial_link(struct max9x_common *common, - return ret; - - for (map_id = 0; map_id < config->num_maps; map_id++) { -+ if (!config->map[map_id].is_csi_enabled) -+ continue; - ret = max96724_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); - if (ret) - return ret; -+ config->map[map_id].is_csi_enabled = false; - } - } - -- /* TODO: if disabling serial link, serializer can't perform i2c communication. */ -- // ret = max96724_set_serial_link_state(common, link_id, false); -- // if (ret) -- // return ret; -+ ret = max96724_set_serial_link_state(common, link_id, false); -+ if (ret) -+ return ret; - - return 0; - } -diff --git a/drivers/media/i2c/max9x/max9x_pdata.h b/drivers/media/i2c/max9x/max9x_pdata.h -index 874e7412315f..04da75fc7d54 100644 ---- a/drivers/media/i2c/max9x/max9x_pdata.h -+++ b/drivers/media/i2c/max9x/max9x_pdata.h -@@ -39,6 +39,7 @@ struct max9x_serdes_mipi_map { - u16 dst_vc; - u16 dst_dt; - u16 dst_csi; -+ bool is_csi_enabled; - }; - - struct max9x_video_pipe_pdata { -diff --git a/drivers/media/i2c/max9x/regmap-retry.c b/drivers/media/i2c/max9x/regmap-retry.c -new file mode 100644 -index 000000000000..9d9ebc33b7a6 ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.c -@@ -0,0 +1,52 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2025 Intel Corporation -+ */ -+ -+#include "regmap-retry.h" -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_write(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_update_bits(map, reg, mask, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -diff --git a/drivers/media/i2c/max9x/regmap-retry.h b/drivers/media/i2c/max9x/regmap-retry.h -new file mode 100644 -index 000000000000..e4347c47d2af ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.h -@@ -0,0 +1,18 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2025 Intel Corporation -+ */ -+ -+#ifndef _REGMAP_RETRY_H -+#define _REGMAP_RETRY_H -+ -+#include -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val); -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val); -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val); -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index 633c55504273..08c44a28bb8c 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -31,6 +31,7 @@ - #include - - #include "serdes.h" -+#include "regmap-retry.h" - - static const s64 max9x_op_sys_clock[] = { - MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -@@ -132,7 +133,7 @@ static const struct of_device_id max9x_of_match[] = { - MODULE_DEVICE_TABLE(of, max9x_of_match); - - static const struct i2c_device_id max9x_id[] = { -- { "max9x", MAX9296 }, -+ { "max9x", 0 }, - { "max9296", MAX9296 }, - { "max96724", MAX96724 }, - { "max9295", MAX9295 }, -@@ -314,7 +315,7 @@ static void *parse_serdes_pdata(struct device *dev) - */ - struct serdes_platform_data *serdes_pdata = dev->platform_data; - unsigned int num_ports = serdes_pdata->subdev_num; -- unsigned int csi_port = (serdes_pdata->des_port / 90); -+ unsigned int csi_port = serdes_pdata->des_port / 90; - struct max9x_pdata *des_pdata = devm_kzalloc(dev, sizeof(*des_pdata), GFP_KERNEL); - - snprintf(des_pdata->suffix, sizeof(des_pdata->suffix), "%c", serdes_pdata->suffix); -@@ -383,36 +384,28 @@ static void *parse_serdes_pdata(struct device *dev) - csi_link->auto_initial_deskew = true; - csi_link->initial_deskew_width = 7; - csi_link->auto_start = false; -- csi_link->num_maps = 2; -+ csi_link->num_maps = serdes_pdata->deser_nlanes; - csi_link->maps = devm_kzalloc(dev, csi_link->num_maps * sizeof(*csi_link->maps), GFP_KERNEL); - if (csi_port == 1) { - SET_PHY_MAP(csi_link->maps, 0, 0, 1, 0); /* 0 (DA0) -> PHY1.0 */ - SET_PHY_MAP(csi_link->maps, 1, 1, 1, 1); /* 1 (DA1) -> PHY1.1 */ -+ if (csi_link->num_maps == 4) { -+ SET_PHY_MAP(csi_link->maps, 2, 2, 0, 0); /* 2 (DA2) -> PHY0.0 */ -+ SET_PHY_MAP(csi_link->maps, 3, 3, 0, 1); /* 3 (DA3) -> PHY0.1 */ -+ } - } else if (csi_port == 2) { -- SET_PHY_MAP(csi_link->maps, 0, 2, 2, 0); /* 0 (DA0) -> PHY2.0 */ -- SET_PHY_MAP(csi_link->maps, 1, 2, 2, 1); /* 1 (DA1) -> PHY2.1 */ -+ SET_PHY_MAP(csi_link->maps, 0, 0, 2, 0); /* 0 (DA0) -> PHY2.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 1, 2, 1); /* 1 (DA1) -> PHY2.1 */ -+ if (csi_link->num_maps == 4) { -+ SET_PHY_MAP(csi_link->maps, 2, 2, 3, 0); /* 2 (DA2) -> PHY3.0 */ -+ SET_PHY_MAP(csi_link->maps, 3, 3, 3, 1); /* 3 (DA3) -> PHY3.1 */ -+ } - } - } while (0); - - return des_pdata; - } - --static int regmap_read_retry(struct regmap *map, unsigned int reg, -- unsigned int *val) --{ -- int ret = 0; -- int i = 0; -- -- for (i = 0; i < 50; i++) { -- ret = regmap_read(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- - static int max9x_enable_resume(struct max9x_common *common) - { - struct device *dev = common->dev; -@@ -457,6 +450,7 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - unsigned int phys_addr, virt_addr; - struct i2c_client *phys_client; - struct regmap *phys_map, *virt_map; -+ struct device *dev = &serial_link->remote.client->dev; - unsigned int val; - const struct regmap_config regmap_config = { - .reg_bits = 16, -@@ -466,27 +460,23 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - if (!serial_link->remote.pdata) - return 0; - -- ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; -- - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; - if (phys_addr == virt_addr) - return 0; - -- dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ dev_info(dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); - - phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); - if (IS_ERR_OR_NULL(phys_client)) { -- dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_client); - return ret; - } - - phys_map = regmap_init_i2c(phys_client, ®map_config); - if (IS_ERR_OR_NULL(phys_map)) { -- dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_map); - goto err_client; - } -@@ -494,36 +484,36 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); - - virt_map = ser_common->map; -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { -- dev_info(common->dev, "Remap not necessary"); -+ dev_info(dev, "Remap not necessary"); - ret = 0; - goto err_regmap; - } - - ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ dev_err(dev, "Device not present at 0x%02x", phys_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ dev_info(dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { -- dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ dev_err(dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); - goto err_regmap; - } - -- msleep(100); -+ usleep_range(1000, 1050); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ dev_err(dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ dev_info(dev, "DEV_ID after: 0x%02x", val); - } - - err_regmap: -@@ -531,8 +521,6 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - err_client: - i2c_unregister_device(phys_client); - -- max9x_des_deisolate_serial_link(common, link_id); -- - return ret; - } - -@@ -582,42 +570,31 @@ int max9x_common_resume(struct max9x_common *common) - struct max9x_common *des_common = NULL; - struct device *dev = common->dev; - u32 des_link; -- u32 phys_addr, virt_addr; - int ret = 0; -- int retry = 50; - -- if (dev->platform_data) { -+ if (dev->platform_data && common->type == MAX9X_SERIALIZER) { - struct max9x_pdata *pdata = dev->platform_data; -+ WARN_ON(pdata->num_serial_links < 1); - -- virt_addr = common->client->addr; -- phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -- -- if (common->type == MAX9X_SERIALIZER) { -- WARN_ON(pdata->num_serial_links < 1); -- -- des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -- if (des_common) { -- des_link = pdata->serial_links[0].des_link_id; -- ret = max9x_remap_serializers_resume(des_common, des_link); -- if (ret) -- return ret; -- ret = max9x_des_isolate_serial_link(des_common, des_link); -- if (ret) -- goto err_reset_serializer; -- } -+ des_common = max9x_client_to_common( -+ pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ } else { -+ return ret; - } - } - -- while (retry--) { -- ret = max9x_verify_devid(common); -- if (ret) -- msleep(100); -- else -- break; -- } -+ ret = max9x_verify_devid(common); - if (ret) { - dev_err(dev, "can't get devid after resume"); -- goto err_deisolate; -+ goto err_reset_serializer; - } - - ret = max9x_enable_resume(common); -@@ -632,20 +609,14 @@ int max9x_common_resume(struct max9x_common *common) - goto err_disable; - } - -- if (common->type == MAX9X_SERIALIZER && des_common) { -+ if (common->type == MAX9X_SERIALIZER && des_common) - max9x_des_deisolate_serial_link(des_common, des_link); -- } - - return 0; - - err_disable: - max9x_disable(common); - --err_deisolate: -- if (common->type == MAX9X_SERIALIZER && des_common) { -- max9x_des_deisolate_serial_link(des_common, des_link); -- } -- - err_reset_serializer: - if (common->type == MAX9X_SERIALIZER) { - if (common->common_ops && common->common_ops->remap_reset) { -@@ -655,6 +626,10 @@ int max9x_common_resume(struct max9x_common *common) - } - } - -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ - return ret; - } - -@@ -664,26 +639,11 @@ int max9x_common_suspend(struct max9x_common *common) - - dev_dbg(common->dev, "try to suspend"); - -- max9x_disable_translations(common); -- - for (link_id = 0; link_id < common->num_serial_links; link_id++) - max9x_disable_serial_link(common, link_id); - - max9x_disable(common); - -- if (common->type == MAX9X_SERIALIZER) { -- struct device *dev = common->dev; -- int ret; -- -- if (dev->platform_data) { -- if (common->common_ops && common->common_ops->remap_reset) { -- ret = common->common_ops->remap_reset(common); -- if (ret) -- return ret; -- } -- } -- } -- - return 0; - } - -@@ -886,11 +846,15 @@ void max9x_destroy(struct max9x_common *common) - /* unregister devices? */ - - v4l2_async_unregister_subdev(&common->v4l.sd); -+ v4l2_subdev_cleanup(&common->v4l.sd); - media_entity_cleanup(&common->v4l.sd.entity); - - i2c_mux_del_adapters(common->muxc); - mutex_destroy(&common->link_mutex); - mutex_destroy(&common->isolate_mutex); -+ for (int i = 0; i < common->num_csi_links; i++) { -+ mutex_destroy(&common->csi_link[i].csi_mutex); -+ } - } - EXPORT_SYMBOL(max9x_destroy); - -@@ -1018,6 +982,7 @@ int max9x_enable(struct max9x_common *common) - ret = max9x_remap_addr(common); - if (ret) - goto err; -+ msleep(20); - } - - if (common->common_ops && common->common_ops->enable) { -@@ -1099,7 +1064,7 @@ int max9x_verify_devid(struct max9x_common *common) - * Fetch and output chip name + revision - * try both virtual address and physical address - */ -- ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); -+ ret = regmap_read_retry(map, MAX9X_DEV_ID, &dev_id); - if (ret) { - dev_warn(dev, "Failed to read chip ID from virtual address"); - if (phys_map) { -@@ -1119,7 +1084,7 @@ int max9x_verify_devid(struct max9x_common *common) - } - common->des = &max9x_chips[chip_type]; - common->type = common->des->serdes_type; -- TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, common->des->rev_reg, &dev_rev)); - dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); - - dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -@@ -1149,7 +1114,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - ret = max9x_des_isolate_serial_link(common, link_id); - if (ret) -- return ret; -+ goto err_deisolate; - - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; -@@ -1176,14 +1141,14 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - if (IS_ERR_OR_NULL(virt_map)) - goto err_virt_client; - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { - dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_virt_regmap; - } - -- ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_virt_regmap; -@@ -1191,7 +1156,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { - dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); -@@ -1200,7 +1165,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - usleep_range(1000, 1050); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_virt_regmap; -@@ -1218,10 +1183,11 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - err_client: - i2c_unregister_device(phys_client); - -- max9x_deselect_i2c_chan(common->muxc, link_id); -- -+err_deisolate: - max9x_des_deisolate_serial_link(common, link_id); - -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ - return ret; - } - -@@ -1320,7 +1286,7 @@ static void max9x_des_s_csi_link(struct max9x_common *common, - if (common->csi_link[csi_link_id].config.auto_start) - continue; /* Already started at probe */ - -- if (enable) { -+ if (enable && !video_pipe->config.map[map_id].is_csi_enabled) { - if (common->csi_link_ops->enable) { - err = common->csi_link_ops->enable( - common, csi_link_id); -@@ -1329,8 +1295,9 @@ static void max9x_des_s_csi_link(struct max9x_common *common, - common->dev, - "csi_link_ops->enable CSI %d failed: %d", - csi_link_id, err); -+ video_pipe->config.map[map_id].is_csi_enabled = true; - } -- } else { -+ } else if (!enable && video_pipe->config.map[map_id].is_csi_enabled) { - if (common->csi_link_ops->disable) { - err = common->csi_link_ops->disable( - common, csi_link_id); -@@ -1339,6 +1306,7 @@ static void max9x_des_s_csi_link(struct max9x_common *common, - common->dev, - "csi_link_ops->disable CSI %d failed: %d", - csi_link_id, err); -+ video_pipe->config.map[map_id].is_csi_enabled = false; - } - } - } -@@ -1352,7 +1320,7 @@ static int _max9x_s_remote_stream(struct max9x_common *common, u32 sink_pad, - struct v4l2_subdev *remote_sd; - int ret = 0; - -- if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ if (sink_pad >= common->v4l.num_pads) - return -EINVAL; - - remote_pad = media_pad_remote_pad_first(&common->v4l.pads[sink_pad]); -@@ -1393,10 +1361,10 @@ static int _max9x_s_remote_stream(struct max9x_common *common, u32 sink_pad, - static int _max9x_des_set_stream(struct max9x_common *common, u32 sink_pad, - u32 sink_stream, int enable) - { -- u32 rxport; -+ int rxport = 0; - int ret = 0; - -- if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ if (sink_pad >= common->v4l.num_pads) - return -EINVAL; - - rxport = sink_pad - common->num_csi_links; -@@ -1524,7 +1492,7 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - return ERR_PTR(-EINVAL); - } - -- if (fmt->pad < 0 || fmt->pad >= common->v4l.num_pads) { -+ if (fmt->pad >= common->v4l.num_pads) { - dev_err(sd->dev, "%s invalid pad %d", __func__, fmt->pad); - return ERR_PTR(-EINVAL); - } -@@ -1532,10 +1500,7 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); - -- if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -- return &common->v4l.ffmts[fmt->pad]; -- -- return ERR_PTR(-EINVAL); -+ return &common->v4l.ffmts[fmt->pad]; - } - - static int max9x_get_fmt(struct v4l2_subdev *sd, -@@ -1599,7 +1564,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - - if (((common->type != MAX9X_DESERIALIZER) && - (common->type != MAX9X_SERIALIZER)) || -- pad < 0 || pad >= common->v4l.num_pads) -+ pad >= common->v4l.num_pads) - return -EINVAL; - - desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -@@ -1611,7 +1576,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - for_each_active_route(&state->routing, route) { - if (route->source_pad != pad) - continue; -- if (route->sink_pad >= common->v4l.num_pads) { -+ if (unlikely(route->sink_pad >= common->v4l.num_pads)) { - ret = -EINVAL; - dev_err(common->dev, "Found invalid route sink_pad!"); - goto out_unlock; -@@ -1786,6 +1751,7 @@ static int max9x_registered(struct v4l2_subdev *sd) - if (subdev_pdata) { - struct max9x_pdata *ser_pdata = - subdev_pdata->board_info.platform_data; -+ struct v4l2_subdev *subdev = NULL; - - WARN_ON(ser_pdata->num_serial_links < 1); - -@@ -1797,13 +1763,12 @@ static int max9x_registered(struct v4l2_subdev *sd) - * physical i2c at the same time - */ - ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; -- -- struct v4l2_subdev *subdev = -- v4l2_i2c_new_subdev_board(sd->v4l2_dev, -- common->muxc->adapter[link_id], -- &subdev_pdata->board_info, NULL); -+ if (!ret) -+ subdev = v4l2_i2c_new_subdev_board( -+ sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, -+ NULL); - - ret = max9x_des_deisolate_serial_link(common, link_id); - if (ret) -@@ -1858,7 +1823,7 @@ static int max9x_registered(struct v4l2_subdev *sd) - .dev_id = "", - .table = { - GPIO_LOOKUP("", 0, "reset", -- GPIO_ACTIVE_HIGH), -+ GPIO_ACTIVE_LOW), - {} - }, - }; -@@ -2140,7 +2105,7 @@ int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) - struct device *dev = common->dev; - int ret; - -- if (link_id >= common->num_serial_links) -+ if (unlikely(link_id >= common->num_serial_links)) - return 0; - - serial_link = &common->serial_link[link_id]; -@@ -2423,6 +2388,7 @@ static int max9x_parse_csi_link_pdata(struct max9x_common *common, - - struct max9x_serdes_csi_link *csi_link = &common->csi_link[csi_link_id]; - -+ mutex_init(&csi_link->csi_mutex); - csi_link->enabled = true; - - csi_link->config.num_maps = csi_link_pdata->num_maps; -@@ -2461,13 +2427,9 @@ static int max9x_parse_subdev_pdata(struct max9x_common *common, - int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(10000); - -- dev_dbg(common->dev, "try to select %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2481,7 +2443,7 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - usleep_range(1000, 1050); - - if (time_is_before_jiffies(timeout)) { -- dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ dev_warn(common->dev, "select %d TIMEOUT", chan_id); - return -ETIMEDOUT; - } - } while (1); -@@ -2499,12 +2461,8 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - -- dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2633,8 +2591,6 @@ int max9x_setup_translations(struct max9x_common *common) - break; - } - -- msleep(10); -- - return err; - } - -diff --git a/drivers/media/i2c/max9x/serdes.h b/drivers/media/i2c/max9x/serdes.h -index 227f61e03229..3ea4ba4d7e66 100644 ---- a/drivers/media/i2c/max9x/serdes.h -+++ b/drivers/media/i2c/max9x/serdes.h -@@ -41,10 +41,7 @@ - #include - #include - --#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) --#include "ipu6-isys.h" --#endif --#include -+#include - #include "max9x_pdata.h" - - #define MAX9X_VDD_REGULATOR_NAME "vdd" -@@ -219,6 +216,7 @@ struct max9x_serdes_csi_link { - bool enabled; - unsigned int usecount; - struct max9x_serdes_csi_config config; -+ struct mutex csi_mutex; - }; - - struct max9x_serdes_video_pipe { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo b/SPECS/kernel-rt/0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo new file mode 100644 index 000000000..85abd949f --- /dev/null +++ b/SPECS/kernel-rt/0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo @@ -0,0 +1,40 @@ +From 19d42f94450222eb8ccd902bee736c5ad133461c Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 12:55:26 -0500 +Subject: [PATCH 19/21] tools/power turbostat: Print percentages in 8-columns + +Added counters that are FORMAT_PERCENT +do not need to be 64-bits -- 32 is plenty. +This allows the output code to fit them, +and their header, into 8-columns. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index f59dcee3c816e..28625143a1b99 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -3482,7 +3482,7 @@ int format_counters(PER_THREAD_PARAMS) + + /* Totl%C0, Any%C0 GFX%C0 CPUGFX% */ + if (DO_BIC(BIC_Totl_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100 * p->pkg_wtd_core_c0 / tsc); /* can exceed 100% */ ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100 * p->pkg_wtd_core_c0 / tsc); /* can exceed 100% */ + if (DO_BIC(BIC_Any_c0)) + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_any_core_c0 / tsc)); + if (DO_BIC(BIC_GFX_c0)) +@@ -10997,7 +10997,7 @@ void probe_cpuidle_residency(void) + if (is_deferred_skip(name_buf)) + continue; + +- add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_USEC, FORMAT_PERCENT, SYSFS_PERCPU, 0); ++ add_counter(0, path, name_buf, 32, SCOPE_CPU, COUNTER_USEC, FORMAT_PERCENT, SYSFS_PERCPU, 0); + + if (state > max_state) + max_state = state; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi b/SPECS/kernel-rt/0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi deleted file mode 100644 index 4b63d8cb7..000000000 --- a/SPECS/kernel-rt/0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi +++ /dev/null @@ -1,267 +0,0 @@ -From aaa541b31cb3920955b836a68e85863e70726970 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 2 Jan 2024 23:51:33 -0800 -Subject: [PATCH 20/44] KVM: nVMX: Add prerequisites to SHADOW_FIELD_R[OW] - macros - -Add VMX feature checks before accessing VMCS fields via SHADOW_FIELD_R[OW] -macros, as some fields may not be supported on all CPUs. - -Functions like copy_shadow_to_vmcs12() and copy_vmcs12_to_shadow() access -VMCS fields that may not exist on certain hardware, such as -INJECTED_EVENT_DATA. To avoid VMREAD/VMWRITE warnings, skip syncing fields -tied to unsupported VMX features. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change since v2: -* Add __SHADOW_FIELD_R[OW] for better readability or maintability (Sean). ---- - arch/x86/kvm/vmx/nested.c | 79 +++++++++++++++++++-------- - arch/x86/kvm/vmx/vmcs_shadow_fields.h | 41 +++++++++----- - 2 files changed, 83 insertions(+), 37 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 7b1a45f06ae6..9374560fc91e 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -55,14 +55,14 @@ struct shadow_vmcs_field { - u16 offset; - }; - static struct shadow_vmcs_field shadow_read_only_fields[] = { --#define SHADOW_FIELD_RO(x, y) { x, offsetof(struct vmcs12, y) }, -+#define __SHADOW_FIELD_RO(x, y, c) { x, offsetof(struct vmcs12, y) }, - #include "vmcs_shadow_fields.h" - }; - static int max_shadow_read_only_fields = - ARRAY_SIZE(shadow_read_only_fields); - - static struct shadow_vmcs_field shadow_read_write_fields[] = { --#define SHADOW_FIELD_RW(x, y) { x, offsetof(struct vmcs12, y) }, -+#define __SHADOW_FIELD_RW(x, y, c) { x, offsetof(struct vmcs12, y) }, - #include "vmcs_shadow_fields.h" - }; - static int max_shadow_read_write_fields = -@@ -85,6 +85,17 @@ static void init_vmcs_shadow_fields(void) - pr_err("Missing field from shadow_read_only_field %x\n", - field + 1); - -+ switch (field) { -+#define __SHADOW_FIELD_RO(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#include "vmcs_shadow_fields.h" -+ default: -+ break; -+ } -+ - clear_bit(field, vmx_vmread_bitmap); - if (field & 1) - #ifdef CONFIG_X86_64 -@@ -110,24 +121,13 @@ static void init_vmcs_shadow_fields(void) - field <= GUEST_TR_AR_BYTES, - "Update vmcs12_write_any() to drop reserved bits from AR_BYTES"); - -- /* -- * PML and the preemption timer can be emulated, but the -- * processor cannot vmwrite to fields that don't exist -- * on bare metal. -- */ - switch (field) { -- case GUEST_PML_INDEX: -- if (!cpu_has_vmx_pml()) -- continue; -- break; -- case VMX_PREEMPTION_TIMER_VALUE: -- if (!cpu_has_vmx_preemption_timer()) -- continue; -- break; -- case GUEST_INTR_STATUS: -- if (!cpu_has_vmx_apicv()) -- continue; -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ - break; -+#include "vmcs_shadow_fields.h" - default: - break; - } -@@ -1669,8 +1669,8 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) - /* - * Copy the writable VMCS shadow fields back to the VMCS12, in case they have - * been modified by the L1 guest. Note, "writable" in this context means -- * "writable by the guest", i.e. tagged SHADOW_FIELD_RW; the set of -- * fields tagged SHADOW_FIELD_RO may or may not align with the "read-only" -+ * "writable by the guest", i.e. tagged __SHADOW_FIELD_RW; the set of -+ * fields tagged __SHADOW_FIELD_RO may or may not align with the "read-only" - * VM-exit information fields (which are actually writable if the vCPU is - * configured to support "VMWRITE to any supported field in the VMCS"). - */ -@@ -1691,6 +1691,18 @@ static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx) - - for (i = 0; i < max_shadow_read_write_fields; i++) { - field = shadow_read_write_fields[i]; -+ -+ switch (field.encoding) { -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#include "vmcs_shadow_fields.h" -+ default: -+ break; -+ } -+ - val = __vmcs_readl(field.encoding); - vmcs12_write_any(vmcs12, field.encoding, field.offset, val); - } -@@ -1725,6 +1737,23 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx) - for (q = 0; q < ARRAY_SIZE(fields); q++) { - for (i = 0; i < max_fields[q]; i++) { - field = fields[q][i]; -+ -+ switch (field.encoding) { -+#define __SHADOW_FIELD_RO(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#include "vmcs_shadow_fields.h" -+ default: -+ break; -+ } -+ - val = vmcs12_read_any(vmcs12, field.encoding, - field.offset); - __vmcs_writel(field.encoding, val); -@@ -6028,9 +6057,10 @@ static int handle_vmread(struct kvm_vcpu *vcpu) - static bool is_shadow_field_rw(unsigned long field) - { - switch (field) { --#define SHADOW_FIELD_RW(x, y) case x: -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ return c; - #include "vmcs_shadow_fields.h" -- return true; - default: - break; - } -@@ -6040,9 +6070,10 @@ static bool is_shadow_field_rw(unsigned long field) - static bool is_shadow_field_ro(unsigned long field) - { - switch (field) { --#define SHADOW_FIELD_RO(x, y) case x: -+#define __SHADOW_FIELD_RO(x, y, c) \ -+ case x: \ -+ return c; - #include "vmcs_shadow_fields.h" -- return true; - default: - break; - } -diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -index da338327c2b3..607945ada35f 100644 ---- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h -+++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -@@ -1,14 +1,17 @@ --#if !defined(SHADOW_FIELD_RO) && !defined(SHADOW_FIELD_RW) -+#if !defined(__SHADOW_FIELD_RO) && !defined(__SHADOW_FIELD_RW) - BUILD_BUG_ON(1) - #endif - --#ifndef SHADOW_FIELD_RO --#define SHADOW_FIELD_RO(x, y) -+#ifndef __SHADOW_FIELD_RO -+#define __SHADOW_FIELD_RO(x, y, c) - #endif --#ifndef SHADOW_FIELD_RW --#define SHADOW_FIELD_RW(x, y) -+#ifndef __SHADOW_FIELD_RW -+#define __SHADOW_FIELD_RW(x, y, c) - #endif - -+#define SHADOW_FIELD_RO(x, y) __SHADOW_FIELD_RO(x, y, true) -+#define SHADOW_FIELD_RW(x, y) __SHADOW_FIELD_RW(x, y, true) -+ - /* - * We do NOT shadow fields that are modified when L0 - * traps and emulates any vmx instruction (e.g. VMPTRLD, -@@ -32,8 +35,12 @@ BUILD_BUG_ON(1) - */ - - /* 16-bits */ --SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status) --SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index) -+__SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status, cpu_has_vmx_apicv()) -+/* -+ * PML can be emulated, but the processor cannot vmwrite to the VMCS field -+ * GUEST_PML_INDEX that doesn't exist on bare metal. -+ */ -+__SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index, cpu_has_vmx_pml()) - SHADOW_FIELD_RW(HOST_FS_SELECTOR, host_fs_selector) - SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) - -@@ -41,9 +48,9 @@ SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) - SHADOW_FIELD_RO(VM_EXIT_REASON, vm_exit_reason) - SHADOW_FIELD_RO(VM_EXIT_INTR_INFO, vm_exit_intr_info) - SHADOW_FIELD_RO(VM_EXIT_INSTRUCTION_LEN, vm_exit_instruction_len) -+SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) - SHADOW_FIELD_RO(IDT_VECTORING_INFO_FIELD, idt_vectoring_info_field) - SHADOW_FIELD_RO(IDT_VECTORING_ERROR_CODE, idt_vectoring_error_code) --SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) - SHADOW_FIELD_RO(GUEST_CS_AR_BYTES, guest_cs_ar_bytes) - SHADOW_FIELD_RO(GUEST_SS_AR_BYTES, guest_ss_ar_bytes) - SHADOW_FIELD_RW(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control) -@@ -54,7 +61,12 @@ SHADOW_FIELD_RW(VM_ENTRY_INTR_INFO_FIELD, vm_entry_intr_info_field) - SHADOW_FIELD_RW(VM_ENTRY_INSTRUCTION_LEN, vm_entry_instruction_len) - SHADOW_FIELD_RW(TPR_THRESHOLD, tpr_threshold) - SHADOW_FIELD_RW(GUEST_INTERRUPTIBILITY_INFO, guest_interruptibility_info) --SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value) -+/* -+ * The preemption timer can be emulated, but the processor cannot vmwrite to -+ * the VMCS field VMX_PREEMPTION_TIMER_VALUE that doesn't exist on bare metal. -+ */ -+__SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value, -+ cpu_has_vmx_preemption_timer()) - - /* Natural width */ - SHADOW_FIELD_RO(EXIT_QUALIFICATION, exit_qualification) -@@ -74,10 +86,13 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) - /* 64-bit */ - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) --SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) --SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) --SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) --SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) -+__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data, cpu_has_vmx_fred()) -+__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data, cpu_has_vmx_fred()) -+__SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data, cpu_has_vmx_fred()) -+__SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data, cpu_has_vmx_fred()) - - #undef SHADOW_FIELD_RO - #undef SHADOW_FIELD_RW -+ -+#undef __SHADOW_FIELD_RO -+#undef __SHADOW_FIELD_RW --- -2.43.0 - diff --git a/SPECS/kernel-rt/0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi b/SPECS/kernel-rt/0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi new file mode 100644 index 000000000..43c43185e --- /dev/null +++ b/SPECS/kernel-rt/0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi @@ -0,0 +1,197 @@ +From f8cc3a950037a639a22008ed75f78a41af8a879f Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Sun, 4 Jun 2023 23:58:33 -0700 +Subject: [PATCH 20/44] KVM: nVMX: Validate FRED-related VMCS fields + +Extend nested VMX field validation to include FRED-specific VMCS fields, +mirroring hardware behavior. + +This enables support for nested FRED by ensuring control and guest/host +state fields are properly checked. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/vmx/nested.c | 117 +++++++++++++++++++++++++++++++++----- + 1 file changed, 104 insertions(+), 13 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index da49de1800609..a6ebe3f2fdd22 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -3032,6 +3032,8 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12) + { + struct vcpu_vmx *vmx = to_vmx(vcpu); ++ bool fred_enabled = (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && ++ (vmcs12->guest_cr4 & X86_CR4_FRED); + + if (CC(!vmx_control_verify(vmcs12->vm_entry_controls, + vmx->nested.msrs.entry_ctls_low, +@@ -3049,22 +3051,11 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + u8 vector = intr_info & INTR_INFO_VECTOR_MASK; + u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; + bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; ++ bool has_nested_exception = vmx->nested.msrs.basic & VMX_BASIC_NESTED_EXCEPTION; + bool urg = nested_cpu_has2(vmcs12, + SECONDARY_EXEC_UNRESTRICTED_GUEST); + bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; + +- /* VM-entry interruption-info field: interruption type */ +- if (CC(intr_type == INTR_TYPE_RESERVED) || +- CC(intr_type == INTR_TYPE_OTHER_EVENT && +- !nested_cpu_supports_monitor_trap_flag(vcpu))) +- return -EINVAL; +- +- /* VM-entry interruption-info field: vector */ +- if (CC(intr_type == INTR_TYPE_NMI_INTR && vector != NMI_VECTOR) || +- CC(intr_type == INTR_TYPE_HARD_EXCEPTION && vector > 31) || +- CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) +- return -EINVAL; +- + /* + * Cannot deliver error code in real mode or if the interrupt + * type is not hardware exception. For other cases, do the +@@ -3088,8 +3079,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + if (CC(intr_info & INTR_INFO_RESVD_BITS_MASK)) + return -EINVAL; + +- /* VM-entry instruction length */ ++ /* ++ * When the CPU enumerates VMX nested-exception support, bit 13 ++ * (set to indicate a nested exception) of the intr info field ++ * may have value 1. Otherwise bit 13 is reserved. ++ */ ++ if (CC(!(has_nested_exception && intr_type == INTR_TYPE_HARD_EXCEPTION) && ++ intr_info & INTR_INFO_NESTED_EXCEPTION_MASK)) ++ return -EINVAL; ++ + switch (intr_type) { ++ case INTR_TYPE_EXT_INTR: ++ break; ++ case INTR_TYPE_RESERVED: ++ return -EINVAL; ++ case INTR_TYPE_NMI_INTR: ++ if (CC(vector != NMI_VECTOR)) ++ return -EINVAL; ++ break; ++ case INTR_TYPE_HARD_EXCEPTION: ++ if (CC(vector > 31)) ++ return -EINVAL; ++ break; + case INTR_TYPE_SOFT_EXCEPTION: + case INTR_TYPE_SOFT_INTR: + case INTR_TYPE_PRIV_SW_EXCEPTION: +@@ -3097,6 +3108,24 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + CC(vmcs12->vm_entry_instruction_len == 0 && + CC(!nested_cpu_has_zero_length_injection(vcpu)))) + return -EINVAL; ++ break; ++ case INTR_TYPE_OTHER_EVENT: ++ switch (vector) { ++ case 0: ++ if (CC(!nested_cpu_supports_monitor_trap_flag(vcpu))) ++ return -EINVAL; ++ break; ++ case 1: ++ case 2: ++ if (CC(!fred_enabled)) ++ return -EINVAL; ++ if (CC(vmcs12->vm_entry_instruction_len > X86_MAX_INSTRUCTION_LENGTH)) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; + } + } + +@@ -3183,9 +3212,29 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, + if (ia32e) { + if (CC(!(vmcs12->host_cr4 & X86_CR4_PAE))) + return -EINVAL; ++ if (vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS && ++ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED) { ++ if (CC(vmcs12->host_ia32_fred_config & ++ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || ++ CC(vmcs12->host_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->host_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->host_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->host_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->host_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->host_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_config & PAGE_MASK, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp3, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp3, vcpu))) ++ return -EINVAL; ++ } + } else { + if (CC(vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) || + CC(vmcs12->host_cr4 & X86_CR4_PCIDE) || ++ CC(vmcs12->host_cr4 & X86_CR4_FRED) || + CC((vmcs12->host_rip) >> 32)) + return -EINVAL; + } +@@ -3354,6 +3403,48 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, + CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) + return -EINVAL; + ++ if (ia32e) { ++ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED) { ++ if (CC(vmcs12->guest_ia32_fred_config & ++ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || ++ CC(vmcs12->guest_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->guest_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->guest_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->guest_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->guest_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->guest_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_config & PAGE_MASK, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp3, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp3, vcpu))) ++ return -EINVAL; ++ } ++ if (vmcs12->guest_cr4 & X86_CR4_FRED) { ++ unsigned int ss_dpl = VMX_AR_DPL(vmcs12->guest_ss_ar_bytes); ++ switch (ss_dpl) { ++ case 0: ++ if (CC(!(vmcs12->guest_cs_ar_bytes & VMX_AR_L_MASK))) ++ return -EINVAL; ++ break; ++ case 1: ++ case 2: ++ return -EINVAL; ++ case 3: ++ if (CC(vmcs12->guest_rflags & X86_EFLAGS_IOPL)) ++ return -EINVAL; ++ if (CC(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_STI)) ++ return -EINVAL; ++ break; ++ } ++ } ++ } else { ++ if (CC(vmcs12->guest_cr4 & X86_CR4_FRED)) ++ return -EINVAL; ++ } ++ + if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { + if (nested_vmx_check_cet_state_common(vcpu, vmcs12->guest_s_cet, + vmcs12->guest_ssp, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet b/SPECS/kernel-rt/0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet deleted file mode 100644 index 774e57be5..000000000 --- a/SPECS/kernel-rt/0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet +++ /dev/null @@ -1,290 +0,0 @@ -From fa7418f912e0d36ec4f998dfb6d458ec23dee047 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:50 -0700 -Subject: [PATCH 20/24] KVM: x86: Enable CET virtualization for VMX and - advertise to userspace - -Expose CET features to guest if KVM/host can support them, clear CPUID -feature bits if KVM/host cannot support. - -Set CPUID feature bits so that CET features are available in guest CPUID. -Add CR4.CET bit support in order to allow guest set CET master control -bit. - -Disable KVM CET feature if unrestricted_guest is unsupported/disabled as -KVM does not support emulating CET. - -The CET load-bits in VM_ENTRY/VM_EXIT control fields should be set to make -guest CET xstates isolated from host's. - -On platforms with VMX_BASIC[bit56] == 0, inject #CP at VMX entry with error -code will fail, and if VMX_BASIC[bit56] == 1, #CP injection with or without -error code is allowed. Disable CET feature bits if the MSR bit is cleared -so that nested VMM can inject #CP if and only if VMX_BASIC[bit56] == 1. - -Don't expose CET feature if either of {U,S}_CET xstate bits is cleared -in host XSS or if XSAVES isn't supported. - -CET MSRs are reset to 0s after RESET, power-up and INIT, clear guest CET -xsave-area fields so that guest CET MSRs are reset to 0s after the events. - -Meanwhile explicitly disable SHSTK and IBT for SVM because CET KVM enabling -for SVM is not ready. - -Signed-off-by: Yang Weijiang -Signed-off-by: Mathias Krause -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 2 +- - arch/x86/include/asm/vmx.h | 1 + - arch/x86/kvm/cpuid.c | 2 ++ - arch/x86/kvm/svm/svm.c | 4 ++++ - arch/x86/kvm/vmx/capabilities.h | 5 +++++ - arch/x86/kvm/vmx/vmx.c | 30 +++++++++++++++++++++++++++++- - arch/x86/kvm/vmx/vmx.h | 4 +++- - arch/x86/kvm/x86.c | 22 +++++++++++++++++++--- - arch/x86/kvm/x86.h | 3 +++ - 9 files changed, 67 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 6b567512984a..3b64414a6730 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -142,7 +142,7 @@ - | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ - | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ - | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ -- | X86_CR4_LAM_SUP)) -+ | X86_CR4_LAM_SUP | X86_CR4_CET)) - - #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index 6a3fb81e852a..b92ff87e3560 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -135,6 +135,7 @@ - #define VMX_BASIC_DUAL_MONITOR_TREATMENT BIT_ULL(49) - #define VMX_BASIC_INOUT BIT_ULL(54) - #define VMX_BASIC_TRUE_CTLS BIT_ULL(55) -+#define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56) - - static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic) - { -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index b9f278efb907..5f76dd7a7620 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -946,6 +946,7 @@ void kvm_set_cpu_caps(void) - VENDOR_F(WAITPKG), - F(SGX_LC), - F(BUS_LOCK_DETECT), -+ F(SHSTK), - ); - - /* -@@ -972,6 +973,7 @@ void kvm_set_cpu_caps(void) - F(AMX_INT8), - F(AMX_BF16), - F(FLUSH_L1D), -+ F(IBT), - ); - - if (boot_cpu_has(X86_FEATURE_AMD_IBPB_RET) && -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 2797c3ab7854..239436ae074b 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -5285,6 +5285,10 @@ static __init void svm_set_cpu_caps(void) - kvm_caps.supported_perf_cap = 0; - kvm_caps.supported_xss = 0; - -+ /* KVM doesn't yet support CET virtualization for SVM. */ -+ kvm_cpu_cap_clear(X86_FEATURE_SHSTK); -+ kvm_cpu_cap_clear(X86_FEATURE_IBT); -+ - /* CPUID 0x80000001 and 0x8000000A (SVM features) */ - if (nested) { - kvm_cpu_cap_set(X86_FEATURE_SVM); -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index fd1d43ebe1df..79cd9d316ba4 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -73,6 +73,11 @@ static inline bool cpu_has_vmx_basic_inout(void) - return vmcs_config.basic & VMX_BASIC_INOUT; - } - -+static inline bool cpu_has_vmx_basic_no_hw_errcode(void) -+{ -+ return vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; -+} -+ - static inline bool cpu_has_virtual_nmis(void) - { - return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS && -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 9393abf9f183..8a9fb9a02080 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2604,6 +2604,7 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - { VM_ENTRY_LOAD_IA32_EFER, VM_EXIT_LOAD_IA32_EFER }, - { VM_ENTRY_LOAD_BNDCFGS, VM_EXIT_CLEAR_BNDCFGS }, - { VM_ENTRY_LOAD_IA32_RTIT_CTL, VM_EXIT_CLEAR_IA32_RTIT_CTL }, -+ { VM_ENTRY_LOAD_CET_STATE, VM_EXIT_LOAD_CET_STATE }, - }; - - memset(vmcs_conf, 0, sizeof(*vmcs_conf)); -@@ -4937,6 +4938,14 @@ void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); /* 22.2.1 */ - -+ if (kvm_cpu_cap_has(X86_FEATURE_SHSTK)) { -+ vmcs_writel(GUEST_SSP, 0); -+ vmcs_writel(GUEST_INTR_SSP_TABLE, 0); -+ } -+ if (kvm_cpu_cap_has(X86_FEATURE_IBT) || -+ kvm_cpu_cap_has(X86_FEATURE_SHSTK)) -+ vmcs_writel(GUEST_S_CET, 0); -+ - kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu); - - vpid_sync_context(vmx->vpid); -@@ -6385,6 +6394,10 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - if (vmcs_read32(VM_EXIT_MSR_STORE_COUNT) > 0) - vmx_dump_msrs("guest autostore", &vmx->msr_autostore.guest); - -+ if (vmentry_ctl & VM_ENTRY_LOAD_CET_STATE) -+ pr_err("S_CET = 0x%016lx, SSP = 0x%016lx, SSP TABLE = 0x%016lx\n", -+ vmcs_readl(GUEST_S_CET), vmcs_readl(GUEST_SSP), -+ vmcs_readl(GUEST_INTR_SSP_TABLE)); - pr_err("*** Host State ***\n"); - pr_err("RIP = 0x%016lx RSP = 0x%016lx\n", - vmcs_readl(HOST_RIP), vmcs_readl(HOST_RSP)); -@@ -6415,6 +6428,10 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - vmcs_read64(HOST_IA32_PERF_GLOBAL_CTRL)); - if (vmcs_read32(VM_EXIT_MSR_LOAD_COUNT) > 0) - vmx_dump_msrs("host autoload", &vmx->msr_autoload.host); -+ if (vmexit_ctl & VM_EXIT_LOAD_CET_STATE) -+ pr_err("S_CET = 0x%016lx, SSP = 0x%016lx, SSP TABLE = 0x%016lx\n", -+ vmcs_readl(HOST_S_CET), vmcs_readl(HOST_SSP), -+ vmcs_readl(HOST_INTR_SSP_TABLE)); - - pr_err("*** Control State ***\n"); - pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", -@@ -7998,7 +8015,6 @@ static __init void vmx_set_cpu_caps(void) - kvm_cpu_cap_set(X86_FEATURE_UMIP); - - /* CPUID 0xD.1 */ -- kvm_caps.supported_xss = 0; - if (!cpu_has_vmx_xsaves()) - kvm_cpu_cap_clear(X86_FEATURE_XSAVES); - -@@ -8010,6 +8026,18 @@ static __init void vmx_set_cpu_caps(void) - - if (cpu_has_vmx_waitpkg()) - kvm_cpu_cap_check_and_set(X86_FEATURE_WAITPKG); -+ -+ /* -+ * Disable CET if unrestricted_guest is unsupported as KVM doesn't -+ * enforce CET HW behaviors in emulator. On platforms with -+ * VMX_BASIC[bit56] == 0, inject #CP at VMX entry with error code -+ * fails, so disable CET in this case too. -+ */ -+ if (!cpu_has_load_cet_ctrl() || !enable_unrestricted_guest || -+ !cpu_has_vmx_basic_no_hw_errcode()) { -+ kvm_cpu_cap_clear(X86_FEATURE_SHSTK); -+ kvm_cpu_cap_clear(X86_FEATURE_IBT); -+ } - } - - static bool vmx_is_io_intercepted(struct kvm_vcpu *vcpu, -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 7eb57f5cb975..b8bf296eb9a4 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -484,7 +484,8 @@ static inline u8 vmx_get_rvi(void) - VM_ENTRY_LOAD_IA32_EFER | \ - VM_ENTRY_LOAD_BNDCFGS | \ - VM_ENTRY_PT_CONCEAL_PIP | \ -- VM_ENTRY_LOAD_IA32_RTIT_CTL) -+ VM_ENTRY_LOAD_IA32_RTIT_CTL | \ -+ VM_ENTRY_LOAD_CET_STATE) - - #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS \ - (VM_EXIT_SAVE_DEBUG_CONTROLS | \ -@@ -507,6 +508,7 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_CLEAR_BNDCFGS | \ - VM_EXIT_PT_CONCEAL_PIP | \ - VM_EXIT_CLEAR_IA32_RTIT_CTL | \ -+ VM_EXIT_LOAD_CET_STATE | \ - VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 83fd91494a4b..da04331a4200 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -227,7 +227,8 @@ static struct kvm_user_return_msrs __percpu *user_return_msrs; - | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 \ - | XFEATURE_MASK_PKRU | XFEATURE_MASK_XTILE) - --#define KVM_SUPPORTED_XSS 0 -+#define KVM_SUPPORTED_XSS (XFEATURE_MASK_CET_USER | \ -+ XFEATURE_MASK_CET_KERNEL) - - bool __read_mostly allow_smaller_maxphyaddr = 0; - EXPORT_SYMBOL_GPL(allow_smaller_maxphyaddr); -@@ -9926,6 +9927,20 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - if (!kvm_cpu_cap_has(X86_FEATURE_XSAVES)) - kvm_caps.supported_xss = 0; - -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && -+ !kvm_cpu_cap_has(X86_FEATURE_IBT)) -+ kvm_caps.supported_xss &= ~(XFEATURE_MASK_CET_USER | -+ XFEATURE_MASK_CET_KERNEL); -+ -+ if ((kvm_caps.supported_xss & (XFEATURE_MASK_CET_USER | -+ XFEATURE_MASK_CET_KERNEL)) != -+ (XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL)) { -+ kvm_cpu_cap_clear(X86_FEATURE_SHSTK); -+ kvm_cpu_cap_clear(X86_FEATURE_IBT); -+ kvm_caps.supported_xss &= ~(XFEATURE_MASK_CET_USER | -+ XFEATURE_MASK_CET_KERNEL); -+ } -+ - if (kvm_caps.has_tsc_control) { - /* - * Make sure the user can only configure tsc_khz values that -@@ -12607,10 +12622,11 @@ static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) - /* - * On INIT, only select XSTATE components are zeroed, most components - * are unchanged. Currently, the only components that are zeroed and -- * supported by KVM are MPX related. -+ * supported by KVM are MPX and CET related. - */ - xfeatures_mask = (kvm_caps.supported_xcr0 | kvm_caps.supported_xss) & -- (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); -+ (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR | -+ XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL); - if (!xfeatures_mask) - return; - -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 1f631b3459e8..41ab002f6471 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -681,6 +681,9 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) - __reserved_bits |= X86_CR4_PCIDE; \ - if (!__cpu_has(__c, X86_FEATURE_LAM)) \ - __reserved_bits |= X86_CR4_LAM_SUP; \ -+ if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ -+ !__cpu_has(__c, X86_FEATURE_IBT)) \ -+ __reserved_bits |= X86_CR4_CET; \ - __reserved_bits; \ - }) - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu b/SPECS/kernel-rt/0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu deleted file mode 100644 index 67f5eaca1..000000000 --- a/SPECS/kernel-rt/0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu +++ /dev/null @@ -1,81 +0,0 @@ -From db8fcde6e507256c5260568ab213221c90783e1c Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Thu, 22 Jan 2026 18:10:31 +0800 -Subject: [PATCH 20/21] Update lt6911gxd sensor driver to fix timeout issue - after S3 - -Suspend function and resume function is added -to address Lontium issue unable to stream after -system wakes up from S3 state. - -JIRA ID: NEXIMAGING-50 - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/lt6911gxd.c | 43 +++++++++++++++++++++++++++++++++++ - 1 file changed, 43 insertions(+) - -diff --git a/drivers/media/i2c/lt6911gxd.c b/drivers/media/i2c/lt6911gxd.c -index d791560ce1a3..1cdc22652955 100644 ---- a/drivers/media/i2c/lt6911gxd.c -+++ b/drivers/media/i2c/lt6911gxd.c -@@ -622,6 +622,48 @@ static int lt6911gxd_probe(struct i2c_client *client) - return ret; - } - -+static int lt6911gxd_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ /* Active low gpio reset, set 1 to power off sensor */ -+ gpiod_set_value_cansleep(lt6911gxd->reset_gpio, 1); -+ -+ return 0; -+} -+ -+static int lt6911gxd_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ int ret = 0; -+ int count = 0; -+ -+ if (lt6911gxd->reset_gpio != NULL) { -+ do { -+ gpiod_set_value_cansleep(lt6911gxd->reset_gpio, 0); -+ ret = gpiod_get_value_cansleep(lt6911gxd->reset_gpio); -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ if (++count >= 20) { -+ dev_err(&client->dev, -+ "%s: failed to power on reset gpio, reset gpio is %d", -+ __func__, ret); -+ break; -+ } -+ } while (ret != 0); -+ } -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops lt6911gxd_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(lt6911gxd_suspend, lt6911gxd_resume) -+}; -+ - static const struct acpi_device_id lt6911gxd_acpi_ids[] = { - { "INTC1124" }, - {} -@@ -632,6 +674,7 @@ static struct i2c_driver lt6911gxd_i2c_driver = { - .driver = { - .name = "lt6911gxd", - .acpi_match_table = ACPI_PTR(lt6911gxd_acpi_ids), -+ .pm = <6911gxd_pm_ops, - }, - .probe = lt6911gxd_probe, - .remove = lt6911gxd_remove, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf b/SPECS/kernel-rt/0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf deleted file mode 100644 index 9fc055119..000000000 --- a/SPECS/kernel-rt/0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf +++ /dev/null @@ -1,230 +0,0 @@ -From c3c3be9b3e43a7a7c0d1e797bad3b93a1b5a0b49 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 12 Dec 2024 02:04:34 +0000 -Subject: [PATCH 020/100] perf/x86/intel: Add counter group support for - arch-PEBS - -Base on previous adaptive PEBS counter snapshot support, add counter -group support for architectural PEBS. Since arch-PEBS shares same -counter group layout with adaptive PEBS, directly reuse -__setup_pebs_counter_group() helper to process arch-PEBS counter group. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 38 ++++++++++++++++++++++++++++--- - arch/x86/events/intel/ds.c | 29 ++++++++++++++++++++--- - arch/x86/include/asm/msr-index.h | 6 +++++ - arch/x86/include/asm/perf_event.h | 13 ++++++++--- - 4 files changed, 77 insertions(+), 9 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index fabe2e750a0b..d77bbc1d43fa 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3012,6 +3012,17 @@ static void intel_pmu_enable_event_ext(struct perf_event *event) - - if (pebs_data_cfg & PEBS_DATACFG_LBRS) - ext |= ARCH_PEBS_LBR & cap.caps; -+ -+ if (pebs_data_cfg & -+ (PEBS_DATACFG_CNTR_MASK << PEBS_DATACFG_CNTR_SHIFT)) -+ ext |= ARCH_PEBS_CNTR_GP & cap.caps; -+ -+ if (pebs_data_cfg & -+ (PEBS_DATACFG_FIX_MASK << PEBS_DATACFG_FIX_SHIFT)) -+ ext |= ARCH_PEBS_CNTR_FIXED & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_METRICS) -+ ext |= ARCH_PEBS_CNTR_METRICS & cap.caps; - } - - if (cpuc->n_pebs == cpuc->n_large_pebs) -@@ -3037,6 +3048,9 @@ static void intel_pmu_enable_event_ext(struct perf_event *event) - } - } - -+ if (is_pebs_counter_event_group(event)) -+ ext |= ARCH_PEBS_CNTR_ALLOW; -+ - if (cpuc->cfg_c_val[hwc->idx] != ext) - __intel_pmu_update_event_ext(hwc->idx, ext); - } -@@ -4320,6 +4334,20 @@ static bool intel_pmu_is_acr_group(struct perf_event *event) - return false; - } - -+static inline bool intel_pmu_has_pebs_counter_group(struct pmu *pmu) -+{ -+ u64 caps; -+ -+ if (x86_pmu.intel_cap.pebs_format >= 6 && x86_pmu.intel_cap.pebs_baseline) -+ return true; -+ -+ caps = hybrid(pmu, arch_pebs_cap).caps; -+ if (x86_pmu.arch_pebs && (caps & ARCH_PEBS_CNTR_MASK)) -+ return true; -+ -+ return false; -+} -+ - static inline void intel_pmu_set_acr_cntr_constr(struct perf_event *event, - u64 *cause_mask, int *num) - { -@@ -4466,8 +4494,7 @@ static int intel_pmu_hw_config(struct perf_event *event) - } - - if ((event->attr.sample_type & PERF_SAMPLE_READ) && -- (x86_pmu.intel_cap.pebs_format >= 6) && -- x86_pmu.intel_cap.pebs_baseline && -+ intel_pmu_has_pebs_counter_group(event->pmu) && - is_sampling_event(event) && - event->attr.precise_ip) - event->group_leader->hw.flags |= PERF_X86_EVENT_PEBS_CNTR; -@@ -5554,6 +5581,8 @@ static inline void __intel_update_large_pebs_flags(struct pmu *pmu) - x86_pmu.large_pebs_flags |= PERF_SAMPLE_TIME; - if (caps & ARCH_PEBS_LBR) - x86_pmu.large_pebs_flags |= PERF_SAMPLE_BRANCH_STACK; -+ if (caps & ARCH_PEBS_CNTR_MASK) -+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_READ; - - if (!(caps & ARCH_PEBS_AUX)) - x86_pmu.large_pebs_flags &= ~PERF_SAMPLE_DATA_SRC; -@@ -7252,8 +7281,11 @@ __init int intel_pmu_init(void) - * Many features on and after V6 require dynamic constraint, - * e.g., Arch PEBS, ACR. - */ -- if (version >= 6) -+ if (version >= 6) { - x86_pmu.flags |= PMU_FL_DYN_CONSTRAINT; -+ x86_pmu.late_setup = intel_pmu_late_setup; -+ } -+ - /* - * Install the hw-cache-events table: - */ -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 530f5a8bc9d2..9876e367174f 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1515,13 +1515,20 @@ pebs_update_state(bool needed_cb, struct cpu_hw_events *cpuc, - - u64 intel_get_arch_pebs_data_config(struct perf_event *event) - { -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - u64 pebs_data_cfg = 0; -+ u64 cntr_mask; - - if (WARN_ON(event->hw.idx < 0 || event->hw.idx >= X86_PMC_IDX_MAX)) - return 0; - - pebs_data_cfg |= pebs_update_adaptive_cfg(event); - -+ cntr_mask = (PEBS_DATACFG_CNTR_MASK << PEBS_DATACFG_CNTR_SHIFT) | -+ (PEBS_DATACFG_FIX_MASK << PEBS_DATACFG_FIX_SHIFT) | -+ PEBS_DATACFG_CNTR | PEBS_DATACFG_METRICS; -+ pebs_data_cfg |= cpuc->pebs_data_cfg & cntr_mask; -+ - return pebs_data_cfg; - } - -@@ -2426,6 +2433,24 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - } - } - -+ if (header->cntr) { -+ struct arch_pebs_cntr_header *cntr = next_record; -+ unsigned int nr; -+ -+ next_record += sizeof(struct arch_pebs_cntr_header); -+ -+ if (is_pebs_counter_event_group(event)) { -+ __setup_pebs_counter_group(cpuc, event, -+ (struct pebs_cntr_header *)cntr, next_record); -+ data->sample_flags |= PERF_SAMPLE_READ; -+ } -+ -+ nr = hweight32(cntr->cntr) + hweight32(cntr->fixed); -+ if (cntr->metrics == INTEL_CNTR_METRICS) -+ nr += 2; -+ next_record += nr * sizeof(u64); -+ } -+ - /* Parse followed fragments if there are. */ - if (arch_pebs_record_continued(header)) { - at = at + header->size; -@@ -3072,10 +3097,8 @@ static void __init intel_ds_pebs_init(void) - break; - - case 6: -- if (x86_pmu.intel_cap.pebs_baseline) { -+ if (x86_pmu.intel_cap.pebs_baseline) - x86_pmu.large_pebs_flags |= PERF_SAMPLE_READ; -- x86_pmu.late_setup = intel_pmu_late_setup; -- } - fallthrough; - case 5: - x86_pmu.pebs_ept = 1; -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 737d51629c03..41852e8690d7 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -331,12 +331,18 @@ - #define ARCH_PEBS_INDEX_WR_SHIFT 4 - - #define ARCH_PEBS_RELOAD 0xffffffff -+#define ARCH_PEBS_CNTR_ALLOW BIT_ULL(35) -+#define ARCH_PEBS_CNTR_GP BIT_ULL(36) -+#define ARCH_PEBS_CNTR_FIXED BIT_ULL(37) -+#define ARCH_PEBS_CNTR_METRICS BIT_ULL(38) - #define ARCH_PEBS_LBR_SHIFT 40 - #define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT) - #define ARCH_PEBS_VECR_XMM BIT_ULL(49) - #define ARCH_PEBS_GPR BIT_ULL(61) - #define ARCH_PEBS_AUX BIT_ULL(62) - #define ARCH_PEBS_EN BIT_ULL(63) -+#define ARCH_PEBS_CNTR_MASK (ARCH_PEBS_CNTR_GP | ARCH_PEBS_CNTR_FIXED | \ -+ ARCH_PEBS_CNTR_METRICS) - - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 1f62efe3fce2..d6e90c8fa87c 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -141,16 +141,16 @@ - #define ARCH_PERFMON_EVENTS_COUNT 7 - - #define PEBS_DATACFG_MEMINFO BIT_ULL(0) --#define PEBS_DATACFG_GP BIT_ULL(1) -+#define PEBS_DATACFG_GP BIT_ULL(1) - #define PEBS_DATACFG_XMMS BIT_ULL(2) - #define PEBS_DATACFG_LBRS BIT_ULL(3) --#define PEBS_DATACFG_LBR_SHIFT 24 - #define PEBS_DATACFG_CNTR BIT_ULL(4) -+#define PEBS_DATACFG_METRICS BIT_ULL(5) -+#define PEBS_DATACFG_LBR_SHIFT 24 - #define PEBS_DATACFG_CNTR_SHIFT 32 - #define PEBS_DATACFG_CNTR_MASK GENMASK_ULL(15, 0) - #define PEBS_DATACFG_FIX_SHIFT 48 - #define PEBS_DATACFG_FIX_MASK GENMASK_ULL(7, 0) --#define PEBS_DATACFG_METRICS BIT_ULL(5) - - /* Steal the highest bit of pebs_data_cfg for SW usage */ - #define PEBS_UPDATE_DS_SW BIT_ULL(63) -@@ -607,6 +607,13 @@ struct arch_pebs_lbr_header { - u64 ler_info; - }; - -+struct arch_pebs_cntr_header { -+ u32 cntr; -+ u32 fixed; -+ u32 metrics; -+ u32 reserved; -+}; -+ - /* - * AMD Extended Performance Monitoring and Debug cpuid feature detection - */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo b/SPECS/kernel-rt/0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo new file mode 100644 index 000000000..9fe7f583a --- /dev/null +++ b/SPECS/kernel-rt/0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo @@ -0,0 +1,131 @@ +From 022168cfacd440abb4a1b9efc4f9929c5dd32dd8 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 13:32:34 -0500 +Subject: [PATCH 20/21] tools/power turbostat: Print wide names only for RAW + 64-bit columns + +Print a wide column header only for the case of a 64-bit RAW counter. + +It turns out that wide column headers otherwise are more harm than good. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 40 ++++++++++++++------------- + 1 file changed, 21 insertions(+), 19 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 28625143a1b99..9329a503464ab 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2723,20 +2723,22 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + + /* + * print_name() +- * Print column header name for 64-bit counter in 16 columns (at least 8-char plus a tab) ++ * Print column header name for raw 64-bit counter in 16 columns (at least 8-char plus a tab) + * Otherwise, allow the name + tab to fit within 8-coumn tab-stop. + * In both cases, left justififed, just like other turbostat columns, + * to allow the column values to consume the tab. + * +- * Yes, 32-bit counters can overflow 8-columns, but then they are usually 64-bit counters. +- * 64-bit counters can overflow 16-columns, but rarely do. ++ * Yes, 32-bit counters can overflow 8-columns, and ++ * 64-bit counters can overflow 16-columns, but that is uncommon. + */ +-static inline int print_name(int width, int *printed, char *delim, char *name) ++static inline int print_name(int width, int *printed, char *delim, char *name, enum counter_type type, enum counter_format format) + { +- if (width <= 32) +- return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); +- else ++ UNUSED(type); ++ ++ if (format == FORMAT_RAW && width >= 64) + return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); ++ else ++ return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); + } + static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) + { +@@ -2819,21 +2821,21 @@ void print_header(char *delim) + outp += sprintf(outp, "%sLLC%%hit", (printed++ ? delim : "")); + + for (mp = sys.tp; mp; mp = mp->next) +- outp += print_name(mp->width, &printed, delim, mp->name); ++ outp += print_name(mp->width, &printed, delim, mp->name, mp->type, mp->format); + + for (pp = sys.perf_tp; pp; pp = pp->next) +- outp += print_name(pp->width, &printed, delim, pp->name); ++ outp += print_name(pp->width, &printed, delim, pp->name, pp->type, pp->format); + + ppmt = sys.pmt_tp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += print_name(32, &printed, delim, ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + } + +@@ -2867,22 +2869,22 @@ void print_header(char *delim) + } + + for (mp = sys.cp; mp; mp = mp->next) +- outp += print_name(mp->width, &printed, delim, mp->name); ++ outp += print_name(mp->width, &printed, delim, mp->name, mp->type, mp->format); + + for (pp = sys.perf_cp; pp; pp = pp->next) +- outp += print_name(pp->width, &printed, delim, pp->name); ++ outp += print_name(pp->width, &printed, delim, pp->name, pp->type, pp->format); + + ppmt = sys.pmt_cp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += print_name(32, &printed, delim, ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + } + +@@ -2970,21 +2972,21 @@ void print_header(char *delim) + outp += sprintf(outp, "%sUncMHz", (printed++ ? delim : "")); + + for (mp = sys.pp; mp; mp = mp->next) +- outp += print_name(mp->width, &printed, delim, mp->name); ++ outp += print_name(mp->width, &printed, delim, mp->name, mp->type, mp->format); + + for (pp = sys.perf_pp; pp; pp = pp->next) +- outp += print_name(pp->width, &printed, delim, pp->name); ++ outp += print_name(pp->width, &printed, delim, pp->name, pp->type, pp->format); + + ppmt = sys.pmt_pp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += print_name(32, &printed, delim, ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + } + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi b/SPECS/kernel-rt/0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi deleted file mode 100644 index 016ab967c..000000000 --- a/SPECS/kernel-rt/0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi +++ /dev/null @@ -1,58 +0,0 @@ -From 5360d6c11aca43d55397c6080c1a94cb5ff95f6c Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Aug 2022 10:03:32 -0700 -Subject: [PATCH 21/44] KVM: nVMX: Allow VMX FRED controls - -Allow nVMX FRED controls as nested FRED support is in place. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/vmx/nested.c | 5 +++-- - arch/x86/kvm/vmx/vmx.c | 1 + - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 9374560fc91e..9ac36ddf3f05 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -7458,7 +7458,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - * advertise any feature in it to nVMX until its nVMX support - * is ready. - */ -- msrs->secondary_exit_ctls &= 0; -+ msrs->secondary_exit_ctls &= SECONDARY_VM_EXIT_SAVE_IA32_FRED | -+ SECONDARY_VM_EXIT_LOAD_IA32_FRED; - } - } - -@@ -7474,7 +7475,7 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, - VM_ENTRY_IA32E_MODE | - #endif - VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS | -- VM_ENTRY_LOAD_CET_STATE; -+ VM_ENTRY_LOAD_CET_STATE | VM_ENTRY_LOAD_IA32_FRED; - msrs->entry_ctls_high |= - (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER | - VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 96edf88dd924..d04085f2d4a2 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -8007,6 +8007,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) - - entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1); - cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM)); -+ cr4_fixed1_update(X86_CR4_FRED, eax, feature_bit(FRED)); - - #undef cr4_fixed1_update - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi b/SPECS/kernel-rt/0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi new file mode 100644 index 000000000..899391407 --- /dev/null +++ b/SPECS/kernel-rt/0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi @@ -0,0 +1,267 @@ +From d6bcc838ff53f463231b16ad441bc4b5e27d9a46 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 2 Jan 2024 23:51:33 -0800 +Subject: [PATCH 21/44] KVM: nVMX: Guard SHADOW_FIELD_R[OW] macros with VMX + feature checks + +Add VMX feature checks to the SHADOW_FIELD_R[OW] macros to prevent access +to VMCS fields that may be unsupported on some CPUs. + +Functions like copy_shadow_to_vmcs12() and copy_vmcs12_to_shadow() access +VMCS fields that may not exist on certain hardware, such as +INJECTED_EVENT_DATA. To avoid VMREAD/VMWRITE warnings, skip syncing fields +tied to unsupported VMX features. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change since v2: +* Add __SHADOW_FIELD_R[OW] for better readability or maintability (Sean). +--- + arch/x86/kvm/vmx/nested.c | 79 +++++++++++++++++++-------- + arch/x86/kvm/vmx/vmcs_shadow_fields.h | 41 +++++++++----- + 2 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index a6ebe3f2fdd22..361152678aead 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -55,14 +55,14 @@ struct shadow_vmcs_field { + u16 offset; + }; + static struct shadow_vmcs_field shadow_read_only_fields[] = { +-#define SHADOW_FIELD_RO(x, y) { x, offsetof(struct vmcs12, y) }, ++#define __SHADOW_FIELD_RO(x, y, c) { x, offsetof(struct vmcs12, y) }, + #include "vmcs_shadow_fields.h" + }; + static int max_shadow_read_only_fields = + ARRAY_SIZE(shadow_read_only_fields); + + static struct shadow_vmcs_field shadow_read_write_fields[] = { +-#define SHADOW_FIELD_RW(x, y) { x, offsetof(struct vmcs12, y) }, ++#define __SHADOW_FIELD_RW(x, y, c) { x, offsetof(struct vmcs12, y) }, + #include "vmcs_shadow_fields.h" + }; + static int max_shadow_read_write_fields = +@@ -85,6 +85,17 @@ static void init_vmcs_shadow_fields(void) + pr_err("Missing field from shadow_read_only_field %x\n", + field + 1); + ++ switch (field) { ++#define __SHADOW_FIELD_RO(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#include "vmcs_shadow_fields.h" ++ default: ++ break; ++ } ++ + clear_bit(field, vmx_vmread_bitmap); + if (field & 1) + #ifdef CONFIG_X86_64 +@@ -110,24 +121,13 @@ static void init_vmcs_shadow_fields(void) + field <= GUEST_TR_AR_BYTES, + "Update vmcs12_write_any() to drop reserved bits from AR_BYTES"); + +- /* +- * PML and the preemption timer can be emulated, but the +- * processor cannot vmwrite to fields that don't exist +- * on bare metal. +- */ + switch (field) { +- case GUEST_PML_INDEX: +- if (!cpu_has_vmx_pml()) +- continue; +- break; +- case VMX_PREEMPTION_TIMER_VALUE: +- if (!cpu_has_vmx_preemption_timer()) +- continue; +- break; +- case GUEST_INTR_STATUS: +- if (!cpu_has_vmx_apicv()) +- continue; ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ + break; ++#include "vmcs_shadow_fields.h" + default: + break; + } +@@ -1633,8 +1633,8 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) + /* + * Copy the writable VMCS shadow fields back to the VMCS12, in case they have + * been modified by the L1 guest. Note, "writable" in this context means +- * "writable by the guest", i.e. tagged SHADOW_FIELD_RW; the set of +- * fields tagged SHADOW_FIELD_RO may or may not align with the "read-only" ++ * "writable by the guest", i.e. tagged __SHADOW_FIELD_RW; the set of ++ * fields tagged __SHADOW_FIELD_RO may or may not align with the "read-only" + * VM-exit information fields (which are actually writable if the vCPU is + * configured to support "VMWRITE to any supported field in the VMCS"). + */ +@@ -1655,6 +1655,18 @@ static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx) + + for (i = 0; i < max_shadow_read_write_fields; i++) { + field = shadow_read_write_fields[i]; ++ ++ switch (field.encoding) { ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#include "vmcs_shadow_fields.h" ++ default: ++ break; ++ } ++ + val = __vmcs_readl(field.encoding); + vmcs12_write_any(vmcs12, field.encoding, field.offset, val); + } +@@ -1689,6 +1701,23 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx) + for (q = 0; q < ARRAY_SIZE(fields); q++) { + for (i = 0; i < max_fields[q]; i++) { + field = fields[q][i]; ++ ++ switch (field.encoding) { ++#define __SHADOW_FIELD_RO(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#include "vmcs_shadow_fields.h" ++ default: ++ break; ++ } ++ + val = vmcs12_read_any(vmcs12, field.encoding, + field.offset); + __vmcs_writel(field.encoding, val); +@@ -6002,9 +6031,10 @@ static int handle_vmread(struct kvm_vcpu *vcpu) + static bool is_shadow_field_rw(unsigned long field) + { + switch (field) { +-#define SHADOW_FIELD_RW(x, y) case x: ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ return c; + #include "vmcs_shadow_fields.h" +- return true; + default: + break; + } +@@ -6014,9 +6044,10 @@ static bool is_shadow_field_rw(unsigned long field) + static bool is_shadow_field_ro(unsigned long field) + { + switch (field) { +-#define SHADOW_FIELD_RO(x, y) case x: ++#define __SHADOW_FIELD_RO(x, y, c) \ ++ case x: \ ++ return c; + #include "vmcs_shadow_fields.h" +- return true; + default: + break; + } +diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +index da338327c2b34..607945ada35fb 100644 +--- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h ++++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +@@ -1,14 +1,17 @@ +-#if !defined(SHADOW_FIELD_RO) && !defined(SHADOW_FIELD_RW) ++#if !defined(__SHADOW_FIELD_RO) && !defined(__SHADOW_FIELD_RW) + BUILD_BUG_ON(1) + #endif + +-#ifndef SHADOW_FIELD_RO +-#define SHADOW_FIELD_RO(x, y) ++#ifndef __SHADOW_FIELD_RO ++#define __SHADOW_FIELD_RO(x, y, c) + #endif +-#ifndef SHADOW_FIELD_RW +-#define SHADOW_FIELD_RW(x, y) ++#ifndef __SHADOW_FIELD_RW ++#define __SHADOW_FIELD_RW(x, y, c) + #endif + ++#define SHADOW_FIELD_RO(x, y) __SHADOW_FIELD_RO(x, y, true) ++#define SHADOW_FIELD_RW(x, y) __SHADOW_FIELD_RW(x, y, true) ++ + /* + * We do NOT shadow fields that are modified when L0 + * traps and emulates any vmx instruction (e.g. VMPTRLD, +@@ -32,8 +35,12 @@ BUILD_BUG_ON(1) + */ + + /* 16-bits */ +-SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status) +-SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index) ++__SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status, cpu_has_vmx_apicv()) ++/* ++ * PML can be emulated, but the processor cannot vmwrite to the VMCS field ++ * GUEST_PML_INDEX that doesn't exist on bare metal. ++ */ ++__SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index, cpu_has_vmx_pml()) + SHADOW_FIELD_RW(HOST_FS_SELECTOR, host_fs_selector) + SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) + +@@ -41,9 +48,9 @@ SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) + SHADOW_FIELD_RO(VM_EXIT_REASON, vm_exit_reason) + SHADOW_FIELD_RO(VM_EXIT_INTR_INFO, vm_exit_intr_info) + SHADOW_FIELD_RO(VM_EXIT_INSTRUCTION_LEN, vm_exit_instruction_len) ++SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) + SHADOW_FIELD_RO(IDT_VECTORING_INFO_FIELD, idt_vectoring_info_field) + SHADOW_FIELD_RO(IDT_VECTORING_ERROR_CODE, idt_vectoring_error_code) +-SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) + SHADOW_FIELD_RO(GUEST_CS_AR_BYTES, guest_cs_ar_bytes) + SHADOW_FIELD_RO(GUEST_SS_AR_BYTES, guest_ss_ar_bytes) + SHADOW_FIELD_RW(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control) +@@ -54,7 +61,12 @@ SHADOW_FIELD_RW(VM_ENTRY_INTR_INFO_FIELD, vm_entry_intr_info_field) + SHADOW_FIELD_RW(VM_ENTRY_INSTRUCTION_LEN, vm_entry_instruction_len) + SHADOW_FIELD_RW(TPR_THRESHOLD, tpr_threshold) + SHADOW_FIELD_RW(GUEST_INTERRUPTIBILITY_INFO, guest_interruptibility_info) +-SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value) ++/* ++ * The preemption timer can be emulated, but the processor cannot vmwrite to ++ * the VMCS field VMX_PREEMPTION_TIMER_VALUE that doesn't exist on bare metal. ++ */ ++__SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value, ++ cpu_has_vmx_preemption_timer()) + + /* Natural width */ + SHADOW_FIELD_RO(EXIT_QUALIFICATION, exit_qualification) +@@ -74,10 +86,13 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) + /* 64-bit */ + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) +-SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) +-SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) +-SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) +-SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) ++__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data, cpu_has_vmx_fred()) ++__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data, cpu_has_vmx_fred()) ++__SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data, cpu_has_vmx_fred()) ++__SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data, cpu_has_vmx_fred()) + + #undef SHADOW_FIELD_RO + #undef SHADOW_FIELD_RW ++ ++#undef __SHADOW_FIELD_RO ++#undef __SHADOW_FIELD_RW +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet b/SPECS/kernel-rt/0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet deleted file mode 100644 index 822478a18..000000000 --- a/SPECS/kernel-rt/0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet +++ /dev/null @@ -1,106 +0,0 @@ -From 45a9614af06267963709a963e2dc0ca2d516b6e1 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:51 -0700 -Subject: [PATCH 21/24] KVM: nVMX: Virtualize NO_HW_ERROR_CODE_CC for L1 event - injection to L2 - -Per SDM description(Vol.3D, Appendix A.1): -"If bit 56 is read as 1, software can use VM entry to deliver a hardware -exception with or without an error code, regardless of vector" - -Modify has_error_code check before inject events to nested guest. Only -enforce the check when guest is in real mode, the exception is not hard -exception and the platform doesn't enumerate bit56 in VMX_BASIC, in all -other case ignore the check to make the logic consistent with SDM. - -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 28 +++++++++++++++++++--------- - arch/x86/kvm/vmx/nested.h | 5 +++++ - 2 files changed, 24 insertions(+), 9 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 36405c80dc15..f40ba4adb5ee 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -1305,9 +1305,10 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) - { - const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT | - VMX_BASIC_INOUT | -- VMX_BASIC_TRUE_CTLS; -+ VMX_BASIC_TRUE_CTLS | -+ VMX_BASIC_NO_HW_ERROR_CODE_CC; - -- const u64 reserved_bits = GENMASK_ULL(63, 56) | -+ const u64 reserved_bits = GENMASK_ULL(63, 57) | - GENMASK_ULL(47, 45) | - BIT_ULL(31); - -@@ -2982,7 +2983,6 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - u8 vector = intr_info & INTR_INFO_VECTOR_MASK; - u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; - bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; -- bool should_have_error_code; - bool urg = nested_cpu_has2(vmcs12, - SECONDARY_EXEC_UNRESTRICTED_GUEST); - bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; -@@ -2999,12 +2999,20 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) - return -EINVAL; - -- /* VM-entry interruption-info field: deliver error code */ -- should_have_error_code = -- intr_type == INTR_TYPE_HARD_EXCEPTION && prot_mode && -- x86_exception_has_error_code(vector); -- if (CC(has_error_code != should_have_error_code)) -- return -EINVAL; -+ /* -+ * Cannot deliver error code in real mode or if the interrupt -+ * type is not hardware exception. For other cases, do the -+ * consistency check only if the vCPU doesn't enumerate -+ * VMX_BASIC_NO_HW_ERROR_CODE_CC. -+ */ -+ if (!prot_mode || intr_type != INTR_TYPE_HARD_EXCEPTION) { -+ if (CC(has_error_code)) -+ return -EINVAL; -+ } else if (!nested_cpu_has_no_hw_errcode_cc(vcpu)) { -+ if (CC(has_error_code != -+ x86_exception_has_error_code(vector))) -+ return -EINVAL; -+ } - - /* VM-entry exception error code */ - if (CC(has_error_code && -@@ -7238,6 +7246,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs) - msrs->basic |= VMX_BASIC_TRUE_CTLS; - if (cpu_has_vmx_basic_inout()) - msrs->basic |= VMX_BASIC_INOUT; -+ if (cpu_has_vmx_basic_no_hw_errcode()) -+ msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC; - } - - static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs) -diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h -index 6eedcfc91070..983484d42ebf 100644 ---- a/arch/x86/kvm/vmx/nested.h -+++ b/arch/x86/kvm/vmx/nested.h -@@ -309,6 +309,11 @@ static inline bool nested_cr4_valid(struct kvm_vcpu *vcpu, unsigned long val) - __kvm_is_valid_cr4(vcpu, val); - } - -+static inline bool nested_cpu_has_no_hw_errcode_cc(struct kvm_vcpu *vcpu) -+{ -+ return to_vmx(vcpu)->nested.msrs.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; -+} -+ - /* No difference in the restrictions on guest and host CR4 in VMX operation. */ - #define nested_guest_cr4_valid nested_cr4_valid - #define nested_host_cr4_valid nested_cr4_valid --- -2.43.0 - diff --git a/SPECS/kernel-rt/0021-Update-compilation-path-for-IPU7-drivers.ipu b/SPECS/kernel-rt/0021-Update-compilation-path-for-IPU7-drivers.ipu deleted file mode 100644 index 5290c8c9c..000000000 --- a/SPECS/kernel-rt/0021-Update-compilation-path-for-IPU7-drivers.ipu +++ /dev/null @@ -1,66 +0,0 @@ -From 8ea0697fea1ac028582ad9c479804e1414c288b2 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 26 Jan 2026 17:52:11 +0800 -Subject: [PATCH 21/21] Update compilation path for IPU7 drivers - -The new compilation path for IPU7 drivers is located -under drivers/staging/media/ipu7 - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/Kconfig | 2 +- - drivers/media/pci/intel/Makefile | 2 +- - drivers/staging/media/Kconfig | 2 +- - drivers/staging/media/Makefile | 2 +- - 4 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index f6b1d7915f83..f2960393622a 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -1,7 +1,7 @@ - # SPDX-License-Identifier: GPL-2.0-only - - source "drivers/media/pci/intel/ipu3/Kconfig" --source "drivers/media/pci/intel/ipu7/Kconfig" -+#source "drivers/media/pci/intel/ipu7/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - - config IPU_BRIDGE -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index b022340db8ba..b58608f4ebb6 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -5,4 +5,4 @@ - obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ --obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -+#obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig -index 6da885037207..ab250c89cd4d 100644 ---- a/drivers/staging/media/Kconfig -+++ b/drivers/staging/media/Kconfig -@@ -28,7 +28,7 @@ source "drivers/staging/media/imx/Kconfig" - - source "drivers/staging/media/ipu3/Kconfig" - --#source "drivers/staging/media/ipu7/Kconfig" -+source "drivers/staging/media/ipu7/Kconfig" - - source "drivers/staging/media/max96712/Kconfig" - -diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile -index 22d19580e91f..4a073938b2b2 100644 ---- a/drivers/staging/media/Makefile -+++ b/drivers/staging/media/Makefile -@@ -8,5 +8,5 @@ obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ - obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ - obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ - obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ --#obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ - obj-$(CONFIG_DVB_AV7110) += av7110/ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf b/SPECS/kernel-rt/0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf deleted file mode 100644 index 60e4a5e30..000000000 --- a/SPECS/kernel-rt/0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf +++ /dev/null @@ -1,190 +0,0 @@ -From 5ee14fc8d425c3bba966478d12f600eddf7632e2 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 12 Dec 2024 01:33:27 +0000 -Subject: [PATCH 021/100] perf/x86/intel: Support SSP register capturing for - arch-PEBS - -Arch-PEBS supports to capture shadow stack pointer (SSP) register in GPR -group. This patch supports to capture and output SSP register at -interrupt or user space, but capturing SSP at user space requires -'exclude_kernel' attribute must be set. That avoids kernel space SSP -register is captured unintentionally. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 15 +++++++++++++++ - arch/x86/events/intel/core.c | 3 ++- - arch/x86/events/intel/ds.c | 9 +++++++-- - arch/x86/events/perf_event.h | 4 ++++ - arch/x86/include/asm/perf_event.h | 1 + - arch/x86/include/uapi/asm/perf_regs.h | 4 +++- - arch/x86/kernel/perf_regs.c | 7 +++++++ - 7 files changed, 39 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 94a8ea4389e4..ad919f980389 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -651,6 +651,21 @@ int x86_pmu_hw_config(struct perf_event *event) - return -EINVAL; - } - -+ if (unlikely(event->attr.sample_regs_user & BIT_ULL(PERF_REG_X86_SSP))) { -+ /* Only arch-PEBS supports to capture SSP register. */ -+ if (!x86_pmu.arch_pebs || !event->attr.precise_ip) -+ return -EINVAL; -+ /* Only user space is allowed to capture. */ -+ if (!event->attr.exclude_kernel) -+ return -EINVAL; -+ } -+ -+ if (unlikely(event->attr.sample_regs_intr & BIT_ULL(PERF_REG_X86_SSP))) { -+ /* Only arch-PEBS supports to capture SSP register. */ -+ if (!x86_pmu.arch_pebs || !event->attr.precise_ip) -+ return -EINVAL; -+ } -+ - /* sample_regs_user never support XMM registers */ - if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)) - return -EINVAL; -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index d77bbc1d43fa..007414c37ad1 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -4153,12 +4153,13 @@ static void intel_pebs_aliases_skl(struct perf_event *event) - static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event) - { - unsigned long flags = x86_pmu.large_pebs_flags; -+ u64 gprs_mask = x86_pmu.arch_pebs ? ARCH_PEBS_GP_REGS : PEBS_GP_REGS; - - if (event->attr.use_clockid) - flags &= ~PERF_SAMPLE_TIME; - if (!event->attr.exclude_kernel) - flags &= ~PERF_SAMPLE_REGS_USER; -- if (event->attr.sample_regs_user & ~PEBS_GP_REGS) -+ if (event->attr.sample_regs_user & ~gprs_mask) - flags &= ~(PERF_SAMPLE_REGS_USER | PERF_SAMPLE_REGS_INTR); - return flags; - } -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 9876e367174f..3a88f62ae127 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1432,6 +1432,7 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event) - u64 sample_type = attr->sample_type; - u64 pebs_data_cfg = 0; - bool gprs, tsx_weight; -+ u64 gprs_mask; - - if (!(sample_type & ~(PERF_SAMPLE_IP|PERF_SAMPLE_TIME)) && - attr->precise_ip > 1) -@@ -1446,10 +1447,11 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event) - * + precise_ip < 2 for the non event IP - * + For RTM TSX weight we need GPRs for the abort code. - */ -+ gprs_mask = x86_pmu.arch_pebs ? ARCH_PEBS_GP_REGS : PEBS_GP_REGS; - gprs = ((sample_type & PERF_SAMPLE_REGS_INTR) && -- (attr->sample_regs_intr & PEBS_GP_REGS)) || -+ (attr->sample_regs_intr & gprs_mask)) || - ((sample_type & PERF_SAMPLE_REGS_USER) && -- (attr->sample_regs_user & PEBS_GP_REGS)); -+ (attr->sample_regs_user & gprs_mask)); - - tsx_weight = (sample_type & PERF_SAMPLE_WEIGHT_TYPE) && - ((attr->config & INTEL_ARCH_EVENT_MASK) == -@@ -2241,6 +2243,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ssp = 0; - - format_group = basic->format_group; - -@@ -2357,6 +2360,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ssp = 0; - - __setup_perf_sample_data(event, iregs, data); - -@@ -2393,6 +2397,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - - __setup_pebs_gpr_group(event, regs, (struct pebs_gprs *)gprs, - sample_type); -+ perf_regs->ssp = gprs->ssp; - } - - if (header->aux) { -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index db4ec2975de4..95779d8e6cf5 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -183,6 +183,10 @@ struct amd_nb { - (1ULL << PERF_REG_X86_R14) | \ - (1ULL << PERF_REG_X86_R15)) - -+#define ARCH_PEBS_GP_REGS \ -+ (PEBS_GP_REGS | \ -+ (1ULL << PERF_REG_X86_SSP)) -+ - /* - * Per register state. - */ -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index d6e90c8fa87c..452a2222ca6b 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -708,6 +708,7 @@ extern void perf_events_lapic_init(void); - struct pt_regs; - struct x86_perf_regs { - struct pt_regs regs; -+ u64 ssp; - u64 *xmm_regs; - }; - -diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h -index 7c9d2bb3833b..f9c5b16b1882 100644 ---- a/arch/x86/include/uapi/asm/perf_regs.h -+++ b/arch/x86/include/uapi/asm/perf_regs.h -@@ -27,9 +27,11 @@ enum perf_event_x86_regs { - PERF_REG_X86_R13, - PERF_REG_X86_R14, - PERF_REG_X86_R15, -+ /* arch-PEBS supports to capture shadow stack pointer (SSP) */ -+ PERF_REG_X86_SSP, - /* These are the limits for the GPRs. */ - PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, -- PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1, -+ PERF_REG_X86_64_MAX = PERF_REG_X86_SSP + 1, - - /* These all need two bits set because they are 128bit */ - PERF_REG_X86_XMM0 = 32, -diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c -index 624703af80a1..985bd616200e 100644 ---- a/arch/x86/kernel/perf_regs.c -+++ b/arch/x86/kernel/perf_regs.c -@@ -54,6 +54,8 @@ static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = { - PT_REGS_OFFSET(PERF_REG_X86_R13, r13), - PT_REGS_OFFSET(PERF_REG_X86_R14, r14), - PT_REGS_OFFSET(PERF_REG_X86_R15, r15), -+ /* The pt_regs struct does not store Shadow stack pointer. */ -+ (unsigned int) -1, - #endif - }; - -@@ -68,6 +70,11 @@ u64 perf_reg_value(struct pt_regs *regs, int idx) - return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0]; - } - -+ if (idx == PERF_REG_X86_SSP) { -+ perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ return perf_regs->ssp; -+ } -+ - if (WARN_ON_ONCE(idx >= ARRAY_SIZE(pt_regs_offset))) - return 0; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0021-tools-power-turbostat-version-2025.12.02.turbo b/SPECS/kernel-rt/0021-tools-power-turbostat-version-2025.12.02.turbo new file mode 100644 index 000000000..b6ce41633 --- /dev/null +++ b/SPECS/kernel-rt/0021-tools-power-turbostat-version-2025.12.02.turbo @@ -0,0 +1,984 @@ +From 19ae0bfb527358cc88946183174f8e0d16e2dea1 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 11:45:42 -0500 +Subject: [PATCH 21/21] tools/power turbostat: version 2025.12.02 + +Since release 2025.09.09: + +Add LLC statistics columns: + LLCkRPS = Last Level Cache Thousands of References Per Second + LLC%hit = Last Level Cache Hit % +Recognize Wildcat Lake and Nova Lake platforms +Add MSR check for Android +Add APERF check for VMWARE +Add RAPL check for AWS +minor fixes + +This patch: + +White-space only, resulting from running Lindent +on everything except the tab-justified data-tables, +and using -l150 instead of -l80 to allow long lines. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 326 +++++++++----------------- + 1 file changed, 108 insertions(+), 218 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 9329a503464ab..5ad45c2ac5bd8 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -563,8 +563,7 @@ static struct gfx_sysfs_info gfx_info[GFX_MAX]; + + int get_msr(int cpu, off_t offset, unsigned long long *msr); + int add_counter(unsigned int msr_num, char *path, char *name, +- unsigned int width, enum counter_scope scope, +- enum counter_type type, enum counter_format format, int flags, int package_num); ++ unsigned int width, enum counter_scope scope, enum counter_type type, enum counter_format format, int flags, int package_num); + + /* Model specific support Start */ + +@@ -1308,8 +1307,7 @@ char *progname; + + #define CPU_SUBSET_MAXCPUS 8192 /* need to use before probe... */ + cpu_set_t *cpu_present_set, *cpu_possible_set, *cpu_effective_set, *cpu_allowed_set, *cpu_affinity_set, *cpu_subset; +-size_t cpu_present_setsize, cpu_possible_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, +- cpu_subset_size; ++size_t cpu_present_setsize, cpu_possible_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, cpu_subset_size; + #define MAX_ADDED_THREAD_COUNTERS 24 + #define MAX_ADDED_CORE_COUNTERS 8 + #define MAX_ADDED_PACKAGE_COUNTERS 16 +@@ -2696,8 +2694,7 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + if (mode == SHOW_LIST) { + deferred_add_names[deferred_add_index++] = name_list; + if (deferred_add_index >= MAX_DEFERRED) { +- fprintf(stderr, "More than max %d un-recognized --add options '%s'\n", +- MAX_DEFERRED, name_list); ++ fprintf(stderr, "More than max %d un-recognized --add options '%s'\n", MAX_DEFERRED, name_list); + help(); + exit(1); + } +@@ -2706,8 +2703,7 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + if (debug) + fprintf(stderr, "deferred \"%s\"\n", name_list); + if (deferred_skip_index >= MAX_DEFERRED) { +- fprintf(stderr, "More than max %d un-recognized --skip options '%s'\n", +- MAX_DEFERRED, name_list); ++ fprintf(stderr, "More than max %d un-recognized --skip options '%s'\n", MAX_DEFERRED, name_list); + help(); + exit(1); + } +@@ -2740,6 +2736,7 @@ static inline int print_name(int width, int *printed, char *delim, char *name, e + else + return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); + } ++ + static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) + { + if (width <= 32) +@@ -2747,6 +2744,7 @@ static inline int print_hex_value(int width, int *printed, char *delim, unsigned + else + return (sprintf(outp, "%s%016llx", (*printed++ ? delim : ""), value)); + } ++ + static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) + { + if (width <= 32) +@@ -2754,6 +2752,7 @@ static inline int print_decimal_value(int width, int *printed, char *delim, unsi + else + return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + } ++ + static inline int print_float_value(int *printed, char *delim, double value) + { + return (sprintf(outp, "%s%0.2f", (*printed++ ? delim : ""), value)); +@@ -3050,9 +3049,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "LLC Hit%%: %.2f", pct((t->llc.references - t->llc.misses) / t->llc.references)); + + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { +- outp += +- sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +- t->counter[i], mp->sp->path); ++ outp += sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, t->counter[i], mp->sp->path); + } + } + +@@ -3071,9 +3068,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "Joules: %0llX (scale: %lf)\n", energy_value, energy_scale); + + for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { +- outp += +- sprintf(outp, "cADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +- c->counter[i], mp->sp->path); ++ outp += sprintf(outp, "cADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, c->counter[i], mp->sp->path); + } + outp += sprintf(outp, "mc6_us: %016llX\n", c->mc6_us); + } +@@ -3108,9 +3103,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "PTM: %dC\n", p->pkg_temp_c); + + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { +- outp += +- sprintf(outp, "pADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +- p->counter[i], mp->sp->path); ++ outp += sprintf(outp, "pADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, p->counter[i], mp->sp->path); + } + } + +@@ -3246,8 +3239,7 @@ int format_counters(PER_THREAD_PARAMS) + } + if (DO_BIC(BIC_Node)) { + if (t) +- outp += sprintf(outp, "%s%d", +- (printed++ ? delim : ""), cpus[t->cpu_id].physical_node_id); ++ outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), cpus[t->cpu_id].physical_node_id); + else + outp += sprintf(outp, "%s-", (printed++ ? delim : "")); + } +@@ -3273,11 +3265,9 @@ int format_counters(PER_THREAD_PARAMS) + + if (DO_BIC(BIC_Bzy_MHz)) { + if (has_base_hz) +- outp += +- sprintf(outp, "%s%.0f", (printed++ ? delim : ""), base_hz / units * t->aperf / t->mperf); ++ outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), base_hz / units * t->aperf / t->mperf); + else +- outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), +- tsc / units * t->aperf / t->mperf / interval_float); ++ outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), tsc / units * t->aperf / t->mperf / interval_float); + } + + if (DO_BIC(BIC_TSC_MHz)) +@@ -3315,7 +3305,6 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, fmt8, (printed++ ? delim : ""), pct((t->llc.references - t->llc.misses) / t->llc.references)); + } + +- + /* Added Thread Counters */ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) +@@ -3431,12 +3420,9 @@ int format_counters(PER_THREAD_PARAMS) + } + + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&c->core_energy, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&c->core_energy, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_Cor_J) && platform->has_per_core_rapl) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&c->core_energy, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&c->core_energy, RAPL_UNIT_JOULES, interval_float)); + + /* print per-package data only for 1st core in package */ + if (!is_cpu_first_core_in_package(t, p)) +@@ -3451,8 +3437,7 @@ int format_counters(PER_THREAD_PARAMS) + if (p->gfx_rc6_ms == -1) { /* detect GFX counter reset */ + outp += sprintf(outp, "%s**.**", (printed++ ? delim : "")); + } else { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- p->gfx_rc6_ms / 10.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), p->gfx_rc6_ms / 10.0 / interval_float); + } + } + +@@ -3469,8 +3454,7 @@ int format_counters(PER_THREAD_PARAMS) + if (p->sam_mc6_ms == -1) { /* detect GFX counter reset */ + outp += sprintf(outp, "%s**.**", (printed++ ? delim : "")); + } else { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- p->sam_mc6_ms / 10.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), p->sam_mc6_ms / 10.0 / interval_float); + } + } + +@@ -3524,41 +3508,27 @@ int format_counters(PER_THREAD_PARAMS) + } + + if (DO_BIC(BIC_PkgWatt)) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_CorWatt) && !platform->has_per_core_rapl) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_GFXWatt)) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_RAMWatt)) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_Pkg_J)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_Cor_J) && !platform->has_per_core_rapl) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_GFX_J)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_RAM_J)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_PKG__)) + outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->rapl_pkg_perf_status, RAPL_UNIT_WATTS, interval_float)); ++ sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->rapl_pkg_perf_status, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_RAM__)) + outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->rapl_dram_perf_status, RAPL_UNIT_WATTS, interval_float)); ++ sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->rapl_dram_perf_status, RAPL_UNIT_WATTS, interval_float)); + /* UncMHz */ + if (DO_BIC(BIC_UNCORE_MHZ)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), p->uncore_mhz); +@@ -3580,8 +3550,7 @@ int format_counters(PER_THREAD_PARAMS) + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); + else if (pp->type == COUNTER_K2M) +- outp += +- sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); ++ outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); + else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) +@@ -3719,8 +3688,7 @@ int delta_package(struct pkg_data *new, struct pkg_data *old) + old->energy_gfx.raw_value = new->energy_gfx.raw_value - old->energy_gfx.raw_value; + old->energy_dram.raw_value = new->energy_dram.raw_value - old->energy_dram.raw_value; + old->rapl_pkg_perf_status.raw_value = new->rapl_pkg_perf_status.raw_value - old->rapl_pkg_perf_status.raw_value; +- old->rapl_dram_perf_status.raw_value = +- new->rapl_dram_perf_status.raw_value - old->rapl_dram_perf_status.raw_value; ++ old->rapl_dram_perf_status.raw_value = new->rapl_dram_perf_status.raw_value - old->rapl_dram_perf_status.raw_value; + + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) +@@ -3827,8 +3795,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + /* check for TSC < 1 Mcycles over interval */ + if (old->tsc < (1000 * 1000)) + errx(-3, "Insanely slow TSC rate, TSC stops in idle?\n" +- "You can disable all c-states by booting with \"idle=poll\"\n" +- "or just the deep ones with \"processor.max_cstate=1\""); ++ "You can disable all c-states by booting with \"idle=poll\"\n" "or just the deep ones with \"processor.max_cstate=1\""); + + old->c1 = new->c1 - old->c1; + +@@ -3857,8 +3824,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + old->c1 = 0; + else { + /* normal case, derive c1 */ +- old->c1 = (old->tsc * tsc_tweak) - old->mperf - core_delta->c3 +- - core_delta->c6 - core_delta->c7; ++ old->c1 = (old->tsc * tsc_tweak) - old->mperf - core_delta->c3 - core_delta->c6 - core_delta->c7; + } + } + +@@ -3910,8 +3876,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + return 0; + } + +-int delta_cpu(struct thread_data *t, struct core_data *c, +- struct pkg_data *p, struct thread_data *t2, struct core_data *c2, struct pkg_data *p2) ++int delta_cpu(struct thread_data *t, struct core_data *c, struct pkg_data *p, struct thread_data *t2, struct core_data *c2, struct pkg_data *p2) + { + int retval = 0; + +@@ -4391,8 +4356,7 @@ unsigned long long get_legacy_uncore_mhz(int package) + */ + for (die = 0; die <= topo.max_die_id; ++die) { + +- sprintf(path, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/current_freq_khz", +- package, die); ++ sprintf(path, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/current_freq_khz", package, die); + + if (access(path, R_OK) == 0) + return (snapshot_sysfs_counter(path) / 1000); +@@ -4702,8 +4666,7 @@ int get_rapl_counters(int cpu, unsigned int domain, struct core_data *c, struct + const ssize_t actual_read_size = read(rci->fd_perf, &perf_data[0], sizeof(perf_data)); + + if (actual_read_size != expected_read_size) +- err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, +- actual_read_size); ++ err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, actual_read_size); + } + + for (unsigned int i = 0, pi = 1; i < NUM_RAPL_COUNTERS; ++i) { +@@ -4941,8 +4904,7 @@ int get_smi_aperf_mperf(unsigned int cpu, struct thread_data *t) + const ssize_t actual_read_size = read(mci->fd_perf, &perf_data[0], sizeof(perf_data)); + + if (actual_read_size != expected_read_size) +- err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, +- actual_read_size); ++ err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, actual_read_size); + } + + for (unsigned int i = 0, pi = 1; i < NUM_MSR_COUNTERS; ++i) { +@@ -5255,48 +5217,39 @@ char *pkg_cstate_limit_strings[] = { "unknown", "reserved", "pc0", "pc1", "pc2", + "pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "pc10", "unlimited" + }; + +-int nhm_pkg_cstate_limits[16] = +- { PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int nhm_pkg_cstate_limits[16] = { PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int snb_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int snb_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int hsw_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int hsw_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int slv_pkg_cstate_limits[16] = +- { PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int slv_pkg_cstate_limits[16] = { PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCL__6, PCL__7 + }; + +-int amt_pkg_cstate_limits[16] = +- { PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int amt_pkg_cstate_limits[16] = { PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int phi_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int phi_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int glm_pkg_cstate_limits[16] = +- { PCLUNL, PCL__1, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCL_10, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int glm_pkg_cstate_limits[16] = { PCLUNL, PCL__1, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCL_10, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int skx_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int skx_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int icx_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL__6, PCL__6, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int icx_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL__6, PCL__6, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +@@ -5371,8 +5324,7 @@ static void dump_power_ctl(void) + return; + + get_msr(base_cpu, MSR_IA32_POWER_CTL, &msr); +- fprintf(outf, "cpu%d: MSR_IA32_POWER_CTL: 0x%08llx (C1E auto-promotion: %sabled)\n", +- base_cpu, msr, msr & 0x2 ? "EN" : "DIS"); ++ fprintf(outf, "cpu%d: MSR_IA32_POWER_CTL: 0x%08llx (C1E auto-promotion: %sabled)\n", base_cpu, msr, msr & 0x2 ? "EN" : "DIS"); + + /* C-state Pre-wake Disable (CSTATE_PREWAKE_DISABLE) */ + if (platform->has_cst_prewake_bit) +@@ -5465,8 +5417,7 @@ static void dump_turbo_ratio_limits(int trl_msr_offset) + ratio = (msr >> shift) & 0xFF; + group_size = (core_counts >> shift) & 0xFF; + if (ratio) +- fprintf(outf, "%d * %.1f = %.1f MHz max turbo %d active cores\n", +- ratio, bclk, ratio * bclk, group_size); ++ fprintf(outf, "%d * %.1f = %.1f MHz max turbo %d active cores\n", ratio, bclk, ratio * bclk, group_size); + } + + return; +@@ -5564,9 +5515,7 @@ static void dump_knl_turbo_ratio_limits(void) + + for (i = buckets_no - 1; i >= 0; i--) + if (i > 0 ? ratio[i] != ratio[i - 1] : 1) +- fprintf(outf, +- "%d * %.1f = %.1f MHz max turbo %d active cores\n", +- ratio[i], bclk, ratio[i] * bclk, cores[i]); ++ fprintf(outf, "%d * %.1f = %.1f MHz max turbo %d active cores\n", ratio[i], bclk, ratio[i] * bclk, cores[i]); + } + + static void dump_cst_cfg(void) +@@ -5651,43 +5600,37 @@ void print_irtl(void) + if (platform->supported_cstates & PC3) { + get_msr(base_cpu, MSR_PKGC3_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC3_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC6) { + get_msr(base_cpu, MSR_PKGC6_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC6_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC7) { + get_msr(base_cpu, MSR_PKGC7_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC7_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC8) { + get_msr(base_cpu, MSR_PKGC8_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC8_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC9) { + get_msr(base_cpu, MSR_PKGC9_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC9_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC10) { + get_msr(base_cpu, MSR_PKGC10_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC10_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + } + +@@ -6221,8 +6164,7 @@ void re_initialize(void) + perf_llc_init(); + added_perf_counters_init(); + pmt_init(); +- fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, +- topo.allowed_cpus); ++ fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, topo.allowed_cpus); + } + + void set_max_cpu_num(void) +@@ -6798,8 +6740,8 @@ int probe_dev_msr(void) + struct stat sb; + char pathname[32]; + +- sprintf(pathname, "/dev/msr%d", base_cpu); +- return !stat(pathname, &sb); ++ sprintf(pathname, "/dev/msr%d", base_cpu); ++ return !stat(pathname, &sb); + } + + int probe_dev_cpu_msr(void) +@@ -7013,8 +6955,7 @@ static void probe_intel_uncore_frequency_legacy(void) + int k, l; + char path_base[128]; + +- sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d", i, +- j); ++ sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d", i, j); + + sprintf(path, "%s/current_freq_khz", path_base); + if (access(path, R_OK)) +@@ -7097,8 +7038,7 @@ static void probe_intel_uncore_frequency_cluster(void) + */ + if BIC_IS_ENABLED + (BIC_UNCORE_MHZ) +- add_counter(0, path, name_buf, 0, SCOPE_PACKAGE, COUNTER_K2M, FORMAT_AVERAGE, 0, +- package_id); ++ add_counter(0, path, name_buf, 0, SCOPE_PACKAGE, COUNTER_K2M, FORMAT_AVERAGE, 0, package_id); + + if (quiet) + continue; +@@ -7107,8 +7047,7 @@ static void probe_intel_uncore_frequency_cluster(void) + k = read_sysfs_int(path); + sprintf(path, "%s/max_freq_khz", path_base); + l = read_sysfs_int(path); +- fprintf(outf, "Uncore Frequency package%d domain%d cluster%d: %d - %d MHz ", package_id, domain_id, +- cluster_id, k / 1000, l / 1000); ++ fprintf(outf, "Uncore Frequency package%d domain%d cluster%d: %d - %d MHz ", package_id, domain_id, cluster_id, k / 1000, l / 1000); + + sprintf(path, "%s/initial_min_freq_khz", path_base); + k = read_sysfs_int(path); +@@ -7170,21 +7109,17 @@ static void probe_graphics(void) + else + goto next; + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms", +- gt0_is_gt ? GFX_rc6 : SAM_mc6); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms", gt0_is_gt ? GFX_rc6 : SAM_mc6); + + set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/cur_freq", gt0_is_gt ? GFX_MHz : SAM_MHz); + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq", +- gt0_is_gt ? GFX_ACTMHz : SAM_ACTMHz); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq", gt0_is_gt ? GFX_ACTMHz : SAM_ACTMHz); + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms", +- gt0_is_gt ? SAM_mc6 : GFX_rc6); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms", gt0_is_gt ? SAM_mc6 : GFX_rc6); + + set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/cur_freq", gt0_is_gt ? SAM_MHz : GFX_MHz); + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq", +- gt0_is_gt ? SAM_ACTMHz : GFX_ACTMHz); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq", gt0_is_gt ? SAM_ACTMHz : GFX_ACTMHz); + + goto end; + } +@@ -7439,8 +7374,7 @@ int print_hwp(PER_THREAD_PARAMS) + "(high %d guar %d eff %d low %d)\n", + cpu, msr, + (unsigned int)HWP_HIGHEST_PERF(msr), +- (unsigned int)HWP_GUARANTEED_PERF(msr), +- (unsigned int)HWP_MOSTEFFICIENT_PERF(msr), (unsigned int)HWP_LOWEST_PERF(msr)); ++ (unsigned int)HWP_GUARANTEED_PERF(msr), (unsigned int)HWP_MOSTEFFICIENT_PERF(msr), (unsigned int)HWP_LOWEST_PERF(msr)); + + if (get_msr(cpu, MSR_HWP_REQUEST, &msr)) + return 0; +@@ -7451,8 +7385,7 @@ int print_hwp(PER_THREAD_PARAMS) + (unsigned int)(((msr) >> 0) & 0xff), + (unsigned int)(((msr) >> 8) & 0xff), + (unsigned int)(((msr) >> 16) & 0xff), +- (unsigned int)(((msr) >> 24) & 0xff), +- (unsigned int)(((msr) >> 32) & 0xff3), (unsigned int)(((msr) >> 42) & 0x1)); ++ (unsigned int)(((msr) >> 24) & 0xff), (unsigned int)(((msr) >> 32) & 0xff3), (unsigned int)(((msr) >> 42) & 0x1)); + + if (has_hwp_pkg) { + if (get_msr(cpu, MSR_HWP_REQUEST_PKG, &msr)) +@@ -7463,23 +7396,20 @@ int print_hwp(PER_THREAD_PARAMS) + cpu, msr, + (unsigned int)(((msr) >> 0) & 0xff), + (unsigned int)(((msr) >> 8) & 0xff), +- (unsigned int)(((msr) >> 16) & 0xff), +- (unsigned int)(((msr) >> 24) & 0xff), (unsigned int)(((msr) >> 32) & 0xff3)); ++ (unsigned int)(((msr) >> 16) & 0xff), (unsigned int)(((msr) >> 24) & 0xff), (unsigned int)(((msr) >> 32) & 0xff3)); + } + if (has_hwp_notify) { + if (get_msr(cpu, MSR_HWP_INTERRUPT, &msr)) + return 0; + + fprintf(outf, "cpu%d: MSR_HWP_INTERRUPT: 0x%08llx " +- "(%s_Guaranteed_Perf_Change, %s_Excursion_Min)\n", +- cpu, msr, ((msr) & 0x1) ? "EN" : "Dis", ((msr) & 0x2) ? "EN" : "Dis"); ++ "(%s_Guaranteed_Perf_Change, %s_Excursion_Min)\n", cpu, msr, ((msr) & 0x1) ? "EN" : "Dis", ((msr) & 0x2) ? "EN" : "Dis"); + } + if (get_msr(cpu, MSR_HWP_STATUS, &msr)) + return 0; + + fprintf(outf, "cpu%d: MSR_HWP_STATUS: 0x%08llx " +- "(%sGuaranteed_Perf_Change, %sExcursion_Min)\n", +- cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x4) ? "" : "No-"); ++ "(%sGuaranteed_Perf_Change, %sExcursion_Min)\n", cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x4) ? "" : "No-"); + + return 0; + } +@@ -7524,8 +7454,7 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 6) ? "VR-Therm, " : "", + (msr & 1 << 5) ? "Auto-HWP, " : "", + (msr & 1 << 4) ? "Graphics, " : "", +- (msr & 1 << 2) ? "bit2, " : "", +- (msr & 1 << 1) ? "ThermStatus, " : "", (msr & 1 << 0) ? "PROCHOT, " : ""); ++ (msr & 1 << 2) ? "bit2, " : "", (msr & 1 << 1) ? "ThermStatus, " : "", (msr & 1 << 0) ? "PROCHOT, " : ""); + fprintf(outf, " (Logged: %s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", + (msr & 1 << 31) ? "bit31, " : "", + (msr & 1 << 30) ? "bit30, " : "", +@@ -7538,8 +7467,7 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 22) ? "VR-Therm, " : "", + (msr & 1 << 21) ? "Auto-HWP, " : "", + (msr & 1 << 20) ? "Graphics, " : "", +- (msr & 1 << 18) ? "bit18, " : "", +- (msr & 1 << 17) ? "ThermStatus, " : "", (msr & 1 << 16) ? "PROCHOT, " : ""); ++ (msr & 1 << 18) ? "bit18, " : "", (msr & 1 << 17) ? "ThermStatus, " : "", (msr & 1 << 16) ? "PROCHOT, " : ""); + + } + if (platform->plr_msrs & PLR_GFX) { +@@ -7551,16 +7479,14 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 4) ? "Graphics, " : "", + (msr & 1 << 6) ? "VR-Therm, " : "", + (msr & 1 << 8) ? "Amps, " : "", +- (msr & 1 << 9) ? "GFXPwr, " : "", +- (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 9) ? "GFXPwr, " : "", (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); + fprintf(outf, " (Logged: %s%s%s%s%s%s%s%s)\n", + (msr & 1 << 16) ? "PROCHOT, " : "", + (msr & 1 << 17) ? "ThermStatus, " : "", + (msr & 1 << 20) ? "Graphics, " : "", + (msr & 1 << 22) ? "VR-Therm, " : "", + (msr & 1 << 24) ? "Amps, " : "", +- (msr & 1 << 25) ? "GFXPwr, " : "", +- (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 25) ? "GFXPwr, " : "", (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); + } + if (platform->plr_msrs & PLR_RING) { + get_msr(cpu, MSR_RING_PERF_LIMIT_REASONS, &msr); +@@ -7569,14 +7495,12 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 0) ? "PROCHOT, " : "", + (msr & 1 << 1) ? "ThermStatus, " : "", + (msr & 1 << 6) ? "VR-Therm, " : "", +- (msr & 1 << 8) ? "Amps, " : "", +- (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 8) ? "Amps, " : "", (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); + fprintf(outf, " (Logged: %s%s%s%s%s%s)\n", + (msr & 1 << 16) ? "PROCHOT, " : "", + (msr & 1 << 17) ? "ThermStatus, " : "", + (msr & 1 << 22) ? "VR-Therm, " : "", +- (msr & 1 << 24) ? "Amps, " : "", +- (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 24) ? "Amps, " : "", (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); + } + return 0; + } +@@ -7704,8 +7628,7 @@ void print_power_limit_msr(int cpu, unsigned long long msr, char *label) + cpu, label, + ((msr >> 15) & 1) ? "EN" : "DIS", + ((msr >> 0) & 0x7FFF) * rapl_power_units, +- (1.0 + (((msr >> 22) & 0x3) / 4.0)) * (1 << ((msr >> 17) & 0x1F)) * rapl_time_units, +- (((msr >> 16) & 1) ? "EN" : "DIS")); ++ (1.0 + (((msr >> 22) & 0x3) / 4.0)) * (1 << ((msr >> 17) & 0x1F)) * rapl_time_units, (((msr >> 16) & 1) ? "EN" : "DIS")); + + return; + } +@@ -7906,8 +7829,7 @@ int print_rapl(PER_THREAD_PARAMS) + cpu, msr, + ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units, + ((msr >> 16) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); ++ ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + + } + if (valid_rapl_msrs & RAPL_PKG) { +@@ -7915,16 +7837,14 @@ int print_rapl(PER_THREAD_PARAMS) + if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr)) + return -9; + +- fprintf(outf, "cpu%d: MSR_PKG_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 63) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_PKG_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 63) & 1 ? "" : "UN"); + + print_power_limit_msr(cpu, msr, "PKG Limit #1"); + fprintf(outf, "cpu%d: PKG Limit #2: %sabled (%0.3f Watts, %f* sec, clamp %sabled)\n", + cpu, + ((msr >> 47) & 1) ? "EN" : "DIS", + ((msr >> 32) & 0x7FFF) * rapl_power_units, +- (1.0 + (((msr >> 54) & 0x3) / 4.0)) * (1 << ((msr >> 49) & 0x1F)) * rapl_time_units, +- ((msr >> 48) & 1) ? "EN" : "DIS"); ++ (1.0 + (((msr >> 54) & 0x3) / 4.0)) * (1 << ((msr >> 49) & 0x1F)) * rapl_time_units, ((msr >> 48) & 1) ? "EN" : "DIS"); + + if (get_msr(cpu, MSR_VR_CURRENT_CONFIG, &msr)) + return -9; +@@ -7942,14 +7862,12 @@ int print_rapl(PER_THREAD_PARAMS) + cpu, msr, + ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units, + ((msr >> 16) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); ++ ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + } + if (valid_rapl_msrs & RAPL_DRAM) { + if (get_msr(cpu, MSR_DRAM_POWER_LIMIT, &msr)) + return -9; +- fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 31) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + + print_power_limit_msr(cpu, msr, "DRAM Limit"); + } +@@ -7962,8 +7880,7 @@ int print_rapl(PER_THREAD_PARAMS) + if (valid_rapl_msrs & RAPL_CORE_POWER_LIMIT) { + if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr)) + return -9; +- fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 31) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + print_power_limit_msr(cpu, msr, "Cores Limit"); + } + if (valid_rapl_msrs & RAPL_GFX) { +@@ -7974,8 +7891,7 @@ int print_rapl(PER_THREAD_PARAMS) + + if (get_msr(cpu, MSR_PP1_POWER_LIMIT, &msr)) + return -9; +- fprintf(outf, "cpu%d: MSR_PP1_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 31) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_PP1_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + print_power_limit_msr(cpu, msr, "GFX Limit"); + } + return 0; +@@ -8015,7 +7931,7 @@ void probe_rapl_msrs(void) + return; + } + +- valid_rapl_msrs = platform->plat_rapl_msrs; /* success */ ++ valid_rapl_msrs = platform->plat_rapl_msrs; /* success */ + } + + /* +@@ -8161,8 +8077,7 @@ int print_thermal(PER_THREAD_PARAMS) + + dts = (msr >> 16) & 0x7F; + dts2 = (msr >> 8) & 0x7F; +- fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", +- cpu, msr, tj_max - dts, tj_max - dts2); ++ fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", cpu, msr, tj_max - dts, tj_max - dts2); + } + + if (do_dts && debug) { +@@ -8173,16 +8088,14 @@ int print_thermal(PER_THREAD_PARAMS) + + dts = (msr >> 16) & 0x7F; + resolution = (msr >> 27) & 0xF; +- fprintf(outf, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", +- cpu, msr, tj_max - dts, resolution); ++ fprintf(outf, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", cpu, msr, tj_max - dts, resolution); + + if (get_msr(cpu, MSR_IA32_THERM_INTERRUPT, &msr)) + return 0; + + dts = (msr >> 16) & 0x7F; + dts2 = (msr >> 8) & 0x7F; +- fprintf(outf, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", +- cpu, msr, tj_max - dts, tj_max - dts2); ++ fprintf(outf, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", cpu, msr, tj_max - dts, tj_max - dts2); + } + + return 0; +@@ -8256,8 +8169,7 @@ void decode_misc_enable_msr(void) + msr & MSR_IA32_MISC_ENABLE_TM1 ? "" : "No-", + msr & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP ? "" : "No-", + msr & MSR_IA32_MISC_ENABLE_MWAIT ? "" : "No-", +- msr & MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE ? "No-" : "", +- msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE ? "No-" : ""); ++ msr & MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE ? "No-" : "", msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE ? "No-" : ""); + } + + void decode_misc_feature_control(void) +@@ -8296,8 +8208,7 @@ void decode_misc_pwr_mgmt_msr(void) + + if (!get_msr(base_cpu, MSR_MISC_PWR_MGMT, &msr)) + fprintf(outf, "cpu%d: MSR_MISC_PWR_MGMT: 0x%08llx (%sable-EIST_Coordination %sable-EPB %sable-OOB)\n", +- base_cpu, msr, +- msr & (1 << 0) ? "DIS" : "EN", msr & (1 << 1) ? "EN" : "DIS", msr & (1 << 8) ? "EN" : "DIS"); ++ base_cpu, msr, msr & (1 << 0) ? "DIS" : "EN", msr & (1 << 1) ? "EN" : "DIS", msr & (1 << 8) ? "EN" : "DIS"); + } + + /* +@@ -8369,8 +8280,7 @@ static int has_perf_instr_count_access(void) + return (fd != -1); + } + +-int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai, +- double *scale_, enum rapl_unit *unit_) ++int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai, double *scale_, enum rapl_unit *unit_) + { + int ret = -1; + +@@ -8763,8 +8673,7 @@ void cstate_perf_init_(bool soft_c1) + cci->source[cai->rci_index] = COUNTER_SOURCE_PERF; + + /* User MSR for this counter */ +- } else if (pkg_cstate_limit >= cai->pkg_cstate_limit +- && add_msr_counter(cpu, cai->msr) >= 0) { ++ } else if (pkg_cstate_limit >= cai->pkg_cstate_limit && add_msr_counter(cpu, cai->msr) >= 0) { + cci->source[cai->rci_index] = COUNTER_SOURCE_MSR; + cci->msr[cai->rci_index] = cai->msr; + } +@@ -8882,8 +8791,7 @@ void process_cpuid() + hygon_genuine = 1; + + if (!quiet) +- fprintf(outf, "CPUID(0): %.4s%.4s%.4s 0x%x CPUID levels\n", +- (char *)&ebx, (char *)&edx, (char *)&ecx, max_level); ++ fprintf(outf, "CPUID(0): %.4s%.4s%.4s 0x%x CPUID levels\n", (char *)&ebx, (char *)&edx, (char *)&ecx, max_level); + + __cpuid(1, fms, ebx, ecx, edx); + family = (fms >> 8) & 0xf; +@@ -8912,8 +8820,7 @@ void process_cpuid() + __cpuid(0x80000000, max_extended_level, ebx, ecx, edx); + + if (!quiet) { +- fprintf(outf, "CPUID(1): family:model:stepping 0x%x:%x:%x (%d:%d:%d)", +- family, model, stepping, family, model, stepping); ++ fprintf(outf, "CPUID(1): family:model:stepping 0x%x:%x:%x (%d:%d:%d)", family, model, stepping, family, model, stepping); + if (ucode_patch_valid) + fprintf(outf, " microcode 0x%x", (unsigned int)((ucode_patch >> 32) & 0xFFFFFFFF)); + fputc('\n', outf); +@@ -8927,8 +8834,7 @@ void process_cpuid() + ecx_flags & (1 << 8) ? "TM2" : "-", + edx_flags & (1 << 4) ? "TSC" : "-", + edx_flags & (1 << 5) ? "MSR" : "-", +- edx_flags & (1 << 22) ? "ACPI-TM" : "-", +- edx_flags & (1 << 28) ? "HT" : "-", edx_flags & (1 << 29) ? "TM" : "-"); ++ edx_flags & (1 << 22) ? "ACPI-TM" : "-", edx_flags & (1 << 28) ? "HT" : "-", edx_flags & (1 << 29) ? "TM" : "-"); + } + + probe_platform_features(family, model); +@@ -8976,8 +8882,7 @@ void process_cpuid() + do_ptm ? "" : "No-", + has_hwp ? "" : "No-", + has_hwp_notify ? "" : "No-", +- has_hwp_activity_window ? "" : "No-", +- has_hwp_epp ? "" : "No-", has_hwp_pkg ? "" : "No-", has_epb ? "" : "No-"); ++ has_hwp_activity_window ? "" : "No-", has_hwp_epp ? "" : "No-", has_hwp_pkg ? "" : "No-", has_epb ? "" : "No-"); + + if (!quiet) + decode_misc_enable_msr(); +@@ -9011,8 +8916,7 @@ void process_cpuid() + + if (ebx_tsc != 0) { + if (!quiet && (ebx != 0)) +- fprintf(outf, "CPUID(0x15): eax_crystal: %d ebx_tsc: %d ecx_crystal_hz: %d\n", +- eax_crystal, ebx_tsc, crystal_hz); ++ fprintf(outf, "CPUID(0x15): eax_crystal: %d ebx_tsc: %d ecx_crystal_hz: %d\n", eax_crystal, ebx_tsc, crystal_hz); + + if (crystal_hz == 0) + crystal_hz = platform->crystal_freq; +@@ -9044,8 +8948,7 @@ void process_cpuid() + tsc_tweak = base_hz / tsc_hz; + + if (!quiet) +- fprintf(outf, "CPUID(0x16): base_mhz: %d max_mhz: %d bus_mhz: %d\n", +- base_mhz, max_mhz, bus_mhz); ++ fprintf(outf, "CPUID(0x16): base_mhz: %d max_mhz: %d bus_mhz: %d\n", base_mhz, max_mhz, bus_mhz); + } + + if (cpuid_has_aperf_mperf) +@@ -9709,8 +9612,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo) + + perf_config = read_perf_config(perf_device, pinfo->event); + if (perf_config == (unsigned int)-1) { +- warnx("%s: perf/%s/%s: failed to read %s", +- __func__, perf_device, pinfo->event, "config"); ++ warnx("%s: perf/%s/%s: failed to read %s", __func__, perf_device, pinfo->event, "config"); + continue; + } + +@@ -9721,8 +9623,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo) + + fd_perf = open_perf_counter(cpu, perf_type, perf_config, -1, 0); + if (fd_perf == -1) { +- warnx("%s: perf/%s/%s: failed to open counter on cpu%d", +- __func__, perf_device, pinfo->event, cpu); ++ warnx("%s: perf/%s/%s: failed to open counter on cpu%d", __func__, perf_device, pinfo->event, cpu); + continue; + } + +@@ -9731,8 +9632,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo) + pinfo->scale = perf_scale; + + if (debug) +- fprintf(stderr, "Add perf/%s/%s cpu%d: %d\n", +- perf_device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]); ++ fprintf(stderr, "Add perf/%s/%s cpu%d: %d\n", perf_device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]); + } + + pinfo = pinfo->next; +@@ -10046,8 +9946,7 @@ int pmt_add_counter(unsigned int guid, unsigned int seq, const char *name, enum + } + + if (conflict) { +- fprintf(stderr, "%s: conflicting parameters for the PMT counter with the same name %s\n", +- __func__, name); ++ fprintf(stderr, "%s: conflicting parameters for the PMT counter with the same name %s\n", __func__, name); + exit(1); + } + +@@ -10090,8 +9989,7 @@ void pmt_init(void) + * CWF with newer firmware might require a PMT_TYPE_XTAL_TIME intead of PMT_TYPE_TCORE_CLOCK. + */ + pmt_add_counter(PMT_CWF_MC1E_GUID, seq, "CPU%c1e", PMT_TYPE_TCORE_CLOCK, +- PMT_COUNTER_CWF_MC1E_LSB, PMT_COUNTER_CWF_MC1E_MSB, offset, SCOPE_CPU, +- FORMAT_DELTA, cpu_num, PMT_OPEN_TRY); ++ PMT_COUNTER_CWF_MC1E_LSB, PMT_COUNTER_CWF_MC1E_MSB, offset, SCOPE_CPU, FORMAT_DELTA, cpu_num, PMT_OPEN_TRY); + + /* + * Rather complex logic for each time we go to the next loop iteration, +@@ -10287,8 +10185,7 @@ struct msr_counter *find_msrp_by_name(struct msr_counter *head, char *name) + } + + int add_counter(unsigned int msr_num, char *path, char *name, +- unsigned int width, enum counter_scope scope, +- enum counter_type type, enum counter_format format, int flags, int id) ++ unsigned int width, enum counter_scope scope, enum counter_type type, enum counter_format format, int flags, int id) + { + struct msr_counter *msrp; + +@@ -10397,9 +10294,7 @@ int add_counter(unsigned int msr_num, char *path, char *name, + struct perf_counter_info *make_perf_counter_info(const char *perf_device, + const char *perf_event, + const char *name, +- unsigned int width, +- enum counter_scope scope, +- enum counter_type type, enum counter_format format) ++ unsigned int width, enum counter_scope scope, enum counter_type type, enum counter_format format) + { + struct perf_counter_info *pinfo; + +@@ -10474,8 +10369,7 @@ int add_perf_counter(const char *perf_device, const char *perf_event, const char + + // FIXME: we might not have debug here yet + if (debug) +- fprintf(stderr, "%s: %s/%s, name: %s, scope%d\n", +- __func__, pinfo->device, pinfo->event, pinfo->name, pinfo->scope); ++ fprintf(stderr, "%s: %s/%s, name: %s, scope%d\n", __func__, pinfo->device, pinfo->event, pinfo->name, pinfo->scope); + + return 0; + } +@@ -10644,8 +10538,7 @@ int pmt_parse_from_path(const char *target_path, unsigned int *out_guid, unsigne + + pmt_diriter_init(&pmt_iter); + +- for (dirname = pmt_diriter_begin(&pmt_iter, SYSFS_TELEM_PATH); dirname != NULL; +- dirname = pmt_diriter_next(&pmt_iter)) { ++ for (dirname = pmt_diriter_begin(&pmt_iter, SYSFS_TELEM_PATH); dirname != NULL; dirname = pmt_diriter_next(&pmt_iter)) { + + fd_telem_dir = openat(dirfd(pmt_iter.dir), dirname->d_name, O_RDONLY | O_DIRECTORY); + if (fd_telem_dir == -1) +@@ -10657,8 +10550,7 @@ int pmt_parse_from_path(const char *target_path, unsigned int *out_guid, unsigne + } + + if (fstat(fd_telem_dir, &stat) == -1) { +- fprintf(stderr, "%s: Failed to stat %s directory: %s", __func__, +- dirname->d_name, strerror(errno)); ++ fprintf(stderr, "%s: Failed to stat %s directory: %s", __func__, dirname->d_name, strerror(errno)); + continue; + } + +@@ -10754,8 +10646,7 @@ void parse_add_command_pmt(char *add_command) + } + + if (!has_scope) { +- printf("%s: invalid value for scope. Expected cpu%%u, core%%u or package%%u.\n", +- __func__); ++ printf("%s: invalid value for scope. Expected cpu%%u, core%%u or package%%u.\n", __func__); + exit(1); + } + +@@ -10831,8 +10722,7 @@ void parse_add_command_pmt(char *add_command) + } + + if (!has_format) { +- fprintf(stderr, "%s: Invalid format %s. Expected raw, average or delta\n", +- __func__, format_name); ++ fprintf(stderr, "%s: Invalid format %s. Expected raw, average or delta\n", __func__, format_name); + exit(1); + } + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet b/SPECS/kernel-rt/0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet deleted file mode 100644 index 0e9d7e949..000000000 --- a/SPECS/kernel-rt/0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet +++ /dev/null @@ -1,262 +0,0 @@ -From e943f26ae8b4682211ca37fbca3994ef999f600a Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:52 -0700 -Subject: [PATCH 22/24] KVM: nVMX: Enable CET support for nested guest - -Set up CET MSRs, related VM_ENTRY/EXIT control bits and fixed CR4 setting -to enable CET for nested VM. - -vmcs12 and vmcs02 needs to be synced when L2 exits to L1 or when L1 wants -to resume L2, that way correct CET states can be observed by one another. - -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 80 ++++++++++++++++++++++++++++++++++++++- - arch/x86/kvm/vmx/vmcs12.c | 6 +++ - arch/x86/kvm/vmx/vmcs12.h | 14 ++++++- - arch/x86/kvm/vmx/vmx.c | 2 + - arch/x86/kvm/vmx/vmx.h | 3 ++ - 5 files changed, 102 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index f40ba4adb5ee..0f8dbf3cc6c3 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -746,6 +746,27 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD); - nested_vmx_merge_msr_bitmaps_write(MSR_IA32_FLUSH_CMD); - -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_U_CET, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_S_CET, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL0_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL1_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL2_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL3_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_INT_SSP_TAB, MSR_TYPE_RW); -+ - nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, - MSR_IA32_APERF, MSR_TYPE_R); - -@@ -2554,6 +2575,32 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 - } - } - -+static inline void cet_vmcs_fields_get(struct kvm_vcpu *vcpu, u64 *s_cet, -+ u64 *ssp, u64 *ssp_tbl) -+{ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) || -+ guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ *s_cet = vmcs_readl(GUEST_S_CET); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { -+ *ssp = vmcs_readl(GUEST_SSP); -+ *ssp_tbl = vmcs_readl(GUEST_INTR_SSP_TABLE); -+ } -+} -+ -+static inline void cet_vmcs_fields_set(struct kvm_vcpu *vcpu, u64 s_cet, -+ u64 ssp, u64 ssp_tbl) -+{ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) || -+ guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ vmcs_writel(GUEST_S_CET, s_cet); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { -+ vmcs_writel(GUEST_SSP, ssp); -+ vmcs_writel(GUEST_INTR_SSP_TABLE, ssp_tbl); -+ } -+} -+ - static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) - { - struct hv_enlightened_vmcs *hv_evmcs = nested_vmx_evmcs(vmx); -@@ -2670,6 +2717,10 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) - vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr); - vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr); - -+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) -+ cet_vmcs_fields_set(&vmx->vcpu, vmcs12->guest_s_cet, -+ vmcs12->guest_ssp, vmcs12->guest_ssp_tbl); -+ - set_cr4_guest_host_mask(vmx); - } - -@@ -2709,6 +2760,13 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - kvm_set_dr(vcpu, 7, vcpu->arch.dr7); - vmx_guest_debugctl_write(vcpu, vmx->nested.pre_vmenter_debugctl); - } -+ -+ if (!vmx->nested.nested_run_pending || -+ !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE)) -+ cet_vmcs_fields_set(vcpu, vmx->nested.pre_vmenter_s_cet, -+ vmx->nested.pre_vmenter_ssp, -+ vmx->nested.pre_vmenter_ssp_tbl); -+ - if (kvm_mpx_supported() && (!vmx->nested.nested_run_pending || - !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) - vmcs_write64(GUEST_BNDCFGS, vmx->nested.pre_vmenter_bndcfgs); -@@ -3585,6 +3643,12 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, - !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) - vmx->nested.pre_vmenter_bndcfgs = vmcs_read64(GUEST_BNDCFGS); - -+ if (!vmx->nested.nested_run_pending || -+ !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE)) -+ cet_vmcs_fields_get(vcpu, &vmx->nested.pre_vmenter_s_cet, -+ &vmx->nested.pre_vmenter_ssp, -+ &vmx->nested.pre_vmenter_ssp_tbl); -+ - /* - * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* - * nested early checks are disabled. In the event of a "late" VM-Fail, -@@ -4512,6 +4576,9 @@ static bool is_vmcs12_ext_field(unsigned long field) - case GUEST_IDTR_BASE: - case GUEST_PENDING_DBG_EXCEPTIONS: - case GUEST_BNDCFGS: -+ case GUEST_S_CET: -+ case GUEST_SSP: -+ case GUEST_INTR_SSP_TABLE: - return true; - default: - break; -@@ -4562,6 +4629,10 @@ static void sync_vmcs02_to_vmcs12_rare(struct kvm_vcpu *vcpu, - vmcs12->guest_pending_dbg_exceptions = - vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS); - -+ cet_vmcs_fields_get(&vmx->vcpu, &vmcs12->guest_s_cet, -+ &vmcs12->guest_ssp, -+ &vmcs12->guest_ssp_tbl); -+ - vmx->nested.need_sync_vmcs02_to_vmcs12_rare = false; - } - -@@ -4793,6 +4864,10 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - if (vmcs12->vm_exit_controls & VM_EXIT_CLEAR_BNDCFGS) - vmcs_write64(GUEST_BNDCFGS, 0); - -+ if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_CET_STATE) -+ cet_vmcs_fields_set(vcpu, vmcs12->host_s_cet, vmcs12->host_ssp, -+ vmcs12->host_ssp_tbl); -+ - if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PAT) { - vmcs_write64(GUEST_IA32_PAT, vmcs12->host_ia32_pat); - vcpu->arch.pat = vmcs12->host_ia32_pat; -@@ -7070,7 +7145,7 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - VM_EXIT_HOST_ADDR_SPACE_SIZE | - #endif - VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT | -- VM_EXIT_CLEAR_BNDCFGS; -+ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE; - msrs->exit_ctls_high |= - VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | - VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | -@@ -7092,7 +7167,8 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, - #ifdef CONFIG_X86_64 - VM_ENTRY_IA32E_MODE | - #endif -- VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS; -+ VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS | -+ VM_ENTRY_LOAD_CET_STATE; - msrs->entry_ctls_high |= - (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER | - VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); -diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c -index 106a72c923ca..4233b5ca9461 100644 ---- a/arch/x86/kvm/vmx/vmcs12.c -+++ b/arch/x86/kvm/vmx/vmcs12.c -@@ -139,6 +139,9 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD(GUEST_PENDING_DBG_EXCEPTIONS, guest_pending_dbg_exceptions), - FIELD(GUEST_SYSENTER_ESP, guest_sysenter_esp), - FIELD(GUEST_SYSENTER_EIP, guest_sysenter_eip), -+ FIELD(GUEST_S_CET, guest_s_cet), -+ FIELD(GUEST_SSP, guest_ssp), -+ FIELD(GUEST_INTR_SSP_TABLE, guest_ssp_tbl), - FIELD(HOST_CR0, host_cr0), - FIELD(HOST_CR3, host_cr3), - FIELD(HOST_CR4, host_cr4), -@@ -151,5 +154,8 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD(HOST_IA32_SYSENTER_EIP, host_ia32_sysenter_eip), - FIELD(HOST_RSP, host_rsp), - FIELD(HOST_RIP, host_rip), -+ FIELD(HOST_S_CET, host_s_cet), -+ FIELD(HOST_SSP, host_ssp), -+ FIELD(HOST_INTR_SSP_TABLE, host_ssp_tbl), - }; - const unsigned int nr_vmcs12_fields = ARRAY_SIZE(vmcs12_field_offsets); -diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h -index 56fd150a6f24..4ad6b16525b9 100644 ---- a/arch/x86/kvm/vmx/vmcs12.h -+++ b/arch/x86/kvm/vmx/vmcs12.h -@@ -117,7 +117,13 @@ struct __packed vmcs12 { - natural_width host_ia32_sysenter_eip; - natural_width host_rsp; - natural_width host_rip; -- natural_width paddingl[8]; /* room for future expansion */ -+ natural_width host_s_cet; -+ natural_width host_ssp; -+ natural_width host_ssp_tbl; -+ natural_width guest_s_cet; -+ natural_width guest_ssp; -+ natural_width guest_ssp_tbl; -+ natural_width paddingl[2]; /* room for future expansion */ - u32 pin_based_vm_exec_control; - u32 cpu_based_vm_exec_control; - u32 exception_bitmap; -@@ -294,6 +300,12 @@ static inline void vmx_check_vmcs12_offsets(void) - CHECK_OFFSET(host_ia32_sysenter_eip, 656); - CHECK_OFFSET(host_rsp, 664); - CHECK_OFFSET(host_rip, 672); -+ CHECK_OFFSET(host_s_cet, 680); -+ CHECK_OFFSET(host_ssp, 688); -+ CHECK_OFFSET(host_ssp_tbl, 696); -+ CHECK_OFFSET(guest_s_cet, 704); -+ CHECK_OFFSET(guest_ssp, 712); -+ CHECK_OFFSET(guest_ssp_tbl, 720); - CHECK_OFFSET(pin_based_vm_exec_control, 744); - CHECK_OFFSET(cpu_based_vm_exec_control, 748); - CHECK_OFFSET(exception_bitmap, 752); -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 8a9fb9a02080..779f73d09e68 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7786,6 +7786,8 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) - cr4_fixed1_update(X86_CR4_PKE, ecx, feature_bit(PKU)); - cr4_fixed1_update(X86_CR4_UMIP, ecx, feature_bit(UMIP)); - cr4_fixed1_update(X86_CR4_LA57, ecx, feature_bit(LA57)); -+ cr4_fixed1_update(X86_CR4_CET, ecx, feature_bit(SHSTK)); -+ cr4_fixed1_update(X86_CR4_CET, edx, feature_bit(IBT)); - - entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1); - cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM)); -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index b8bf296eb9a4..ef8fd345f1ae 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -181,6 +181,9 @@ struct nested_vmx { - */ - u64 pre_vmenter_debugctl; - u64 pre_vmenter_bndcfgs; -+ u64 pre_vmenter_s_cet; -+ u64 pre_vmenter_ssp; -+ u64 pre_vmenter_ssp_tbl; - - /* to migrate it to L1 if L2 writes to L1's CR8 directly */ - int l1_tpr_threshold; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi b/SPECS/kernel-rt/0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi new file mode 100644 index 000000000..22b8ef8a4 --- /dev/null +++ b/SPECS/kernel-rt/0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi @@ -0,0 +1,59 @@ +From f99e13d619df7fd1fa38f90971e329c4d524e863 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Aug 2022 10:03:32 -0700 +Subject: [PATCH 22/44] KVM: nVMX: Enable VMX FRED controls + +Permit use of VMX FRED controls in nested VMX now that support for nested +FRED is implemented. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/vmx/nested.c | 5 +++-- + arch/x86/kvm/vmx/vmx.c | 1 + + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index 361152678aead..4d61549588457 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -7456,7 +7456,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, + * advertise any feature in it to nVMX until its nVMX support + * is ready. + */ +- msrs->secondary_exit_ctls &= 0; ++ msrs->secondary_exit_ctls &= SECONDARY_VM_EXIT_SAVE_IA32_FRED | ++ SECONDARY_VM_EXIT_LOAD_IA32_FRED; + } + } + +@@ -7472,7 +7473,7 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, + VM_ENTRY_IA32E_MODE | + #endif + VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS | +- VM_ENTRY_LOAD_CET_STATE; ++ VM_ENTRY_LOAD_CET_STATE | VM_ENTRY_LOAD_IA32_FRED; + msrs->entry_ctls_high |= + (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER | + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 4fdf80d02dadd..823ad3300f656 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7983,6 +7983,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) + + entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1); + cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM)); ++ cr4_fixed1_update(X86_CR4_FRED, eax, feature_bit(FRED)); + + #undef cr4_fixed1_update + } +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0022-perf-core-Support-to-capture-higher-width-vector-regi.perf b/SPECS/kernel-rt/0022-perf-core-Support-to-capture-higher-width-vector-regi.perf deleted file mode 100644 index 1b0985236..000000000 --- a/SPECS/kernel-rt/0022-perf-core-Support-to-capture-higher-width-vector-regi.perf +++ /dev/null @@ -1,625 +0,0 @@ -From cfe395a25e116bb796eb55e94adb894304aca978 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 09:05:25 +0000 -Subject: [PATCH 022/100] perf/core: Support to capture higher width vector - registers - -Arch-PEBS supports to capture more vector registers like OPMASK/YMM/ZMM -registers besides XMM registers. This patch extends PERF_SAMPLE_REGS_INTR -and PERF_SAMPLE_REGS_USER attributes to support these new vector registers -capturing at interrupt and user space. - -The arrays sample_regs_intr/user__ext[] is added into perf_event_attr -structure to record user configured extended register bitmap and a helper -perf_reg_ext_validate() is added to validate if these registers are -supported on some specific PMUs. Furthermore considering to leave enough -space to support more GPRs like R16 ~ R31 introduced by APX in the future, -directly extend the array size to 7. - -This patch just adds the common perf/core support, the x86/intel specific -support would be added in next patch. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/arm/kernel/perf_regs.c | 6 ++ - arch/arm64/kernel/perf_regs.c | 6 ++ - arch/csky/kernel/perf_regs.c | 5 ++ - arch/loongarch/kernel/perf_regs.c | 5 ++ - arch/mips/kernel/perf_regs.c | 5 ++ - arch/powerpc/perf/perf_regs.c | 5 ++ - arch/riscv/kernel/perf_regs.c | 5 ++ - arch/s390/kernel/perf_regs.c | 5 ++ - arch/x86/include/asm/perf_event.h | 4 ++ - arch/x86/include/uapi/asm/perf_regs.h | 79 ++++++++++++++++++++- - arch/x86/kernel/perf_regs.c | 64 ++++++++++++++++- - include/linux/perf_event.h | 4 ++ - include/linux/perf_regs.h | 10 +++ - include/uapi/linux/perf_event.h | 11 +++ - kernel/events/core.c | 98 +++++++++++++++++++++++++-- - 15 files changed, 304 insertions(+), 8 deletions(-) - -diff --git a/arch/arm/kernel/perf_regs.c b/arch/arm/kernel/perf_regs.c -index 0529f90395c9..86b2002d0846 100644 ---- a/arch/arm/kernel/perf_regs.c -+++ b/arch/arm/kernel/perf_regs.c -@@ -37,3 +37,9 @@ void perf_get_regs_user(struct perf_regs *regs_user, - regs_user->regs = task_pt_regs(current); - regs_user->abi = perf_reg_abi(current); - } -+ -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ -diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c -index b4eece3eb17d..1c91fd3530d5 100644 ---- a/arch/arm64/kernel/perf_regs.c -+++ b/arch/arm64/kernel/perf_regs.c -@@ -104,3 +104,9 @@ void perf_get_regs_user(struct perf_regs *regs_user, - regs_user->regs = task_pt_regs(current); - regs_user->abi = perf_reg_abi(current); - } -+ -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ -diff --git a/arch/csky/kernel/perf_regs.c b/arch/csky/kernel/perf_regs.c -index 09b7f88a2d6a..d2e2af0bf1ad 100644 ---- a/arch/csky/kernel/perf_regs.c -+++ b/arch/csky/kernel/perf_regs.c -@@ -26,6 +26,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - return PERF_SAMPLE_REGS_ABI_32; -diff --git a/arch/loongarch/kernel/perf_regs.c b/arch/loongarch/kernel/perf_regs.c -index 263ac4ab5af6..e1df67e3fab4 100644 ---- a/arch/loongarch/kernel/perf_regs.c -+++ b/arch/loongarch/kernel/perf_regs.c -@@ -34,6 +34,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_value(struct pt_regs *regs, int idx) - { - if (WARN_ON_ONCE((u32)idx >= PERF_REG_LOONGARCH_MAX)) -diff --git a/arch/mips/kernel/perf_regs.c b/arch/mips/kernel/perf_regs.c -index e686780d1647..bbb5f25b9191 100644 ---- a/arch/mips/kernel/perf_regs.c -+++ b/arch/mips/kernel/perf_regs.c -@@ -37,6 +37,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_value(struct pt_regs *regs, int idx) - { - long v; -diff --git a/arch/powerpc/perf/perf_regs.c b/arch/powerpc/perf/perf_regs.c -index 350dccb0143c..d919c628aee3 100644 ---- a/arch/powerpc/perf/perf_regs.c -+++ b/arch/powerpc/perf/perf_regs.c -@@ -132,6 +132,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - if (is_tsk_32bit_task(task)) -diff --git a/arch/riscv/kernel/perf_regs.c b/arch/riscv/kernel/perf_regs.c -index fd304a248de6..5beb60544c9a 100644 ---- a/arch/riscv/kernel/perf_regs.c -+++ b/arch/riscv/kernel/perf_regs.c -@@ -26,6 +26,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - #if __riscv_xlen == 64 -diff --git a/arch/s390/kernel/perf_regs.c b/arch/s390/kernel/perf_regs.c -index a6b058ee4a36..9247573229b0 100644 ---- a/arch/s390/kernel/perf_regs.c -+++ b/arch/s390/kernel/perf_regs.c -@@ -42,6 +42,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - if (test_tsk_thread_flag(task, TIF_31BIT)) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 452a2222ca6b..a4f7cfc3fddb 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -710,6 +710,10 @@ struct x86_perf_regs { - struct pt_regs regs; - u64 ssp; - u64 *xmm_regs; -+ u64 *opmask_regs; -+ u64 *ymmh_regs; -+ u64 *zmmh_regs; -+ u64 *h16zmm_regs; - }; - - extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs); -diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h -index f9c5b16b1882..5e2d9796b2cc 100644 ---- a/arch/x86/include/uapi/asm/perf_regs.h -+++ b/arch/x86/include/uapi/asm/perf_regs.h -@@ -33,7 +33,7 @@ enum perf_event_x86_regs { - PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, - PERF_REG_X86_64_MAX = PERF_REG_X86_SSP + 1, - -- /* These all need two bits set because they are 128bit */ -+ /* These all need two bits set because they are 128 bits */ - PERF_REG_X86_XMM0 = 32, - PERF_REG_X86_XMM1 = 34, - PERF_REG_X86_XMM2 = 36, -@@ -53,6 +53,83 @@ enum perf_event_x86_regs { - - /* These include both GPRs and XMMX registers */ - PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2, -+ -+ /* Leave bits[127:64] for other GP registers, like R16 ~ R31.*/ -+ -+ /* -+ * Each YMM register need 4 bits to represent because they are 256 bits. -+ * PERF_REG_X86_YMMH0 = 128 -+ */ -+ PERF_REG_X86_YMM0 = 128, -+ PERF_REG_X86_YMM1 = PERF_REG_X86_YMM0 + 4, -+ PERF_REG_X86_YMM2 = PERF_REG_X86_YMM1 + 4, -+ PERF_REG_X86_YMM3 = PERF_REG_X86_YMM2 + 4, -+ PERF_REG_X86_YMM4 = PERF_REG_X86_YMM3 + 4, -+ PERF_REG_X86_YMM5 = PERF_REG_X86_YMM4 + 4, -+ PERF_REG_X86_YMM6 = PERF_REG_X86_YMM5 + 4, -+ PERF_REG_X86_YMM7 = PERF_REG_X86_YMM6 + 4, -+ PERF_REG_X86_YMM8 = PERF_REG_X86_YMM7 + 4, -+ PERF_REG_X86_YMM9 = PERF_REG_X86_YMM8 + 4, -+ PERF_REG_X86_YMM10 = PERF_REG_X86_YMM9 + 4, -+ PERF_REG_X86_YMM11 = PERF_REG_X86_YMM10 + 4, -+ PERF_REG_X86_YMM12 = PERF_REG_X86_YMM11 + 4, -+ PERF_REG_X86_YMM13 = PERF_REG_X86_YMM12 + 4, -+ PERF_REG_X86_YMM14 = PERF_REG_X86_YMM13 + 4, -+ PERF_REG_X86_YMM15 = PERF_REG_X86_YMM14 + 4, -+ PERF_REG_X86_YMM_MAX = PERF_REG_X86_YMM15 + 4, -+ -+ /* -+ * Each ZMM register needs 8 bits to represent because they are 512 bits -+ * PERF_REG_X86_ZMMH0 = 192 -+ */ -+ PERF_REG_X86_ZMM0 = PERF_REG_X86_YMM_MAX, -+ PERF_REG_X86_ZMM1 = PERF_REG_X86_ZMM0 + 8, -+ PERF_REG_X86_ZMM2 = PERF_REG_X86_ZMM1 + 8, -+ PERF_REG_X86_ZMM3 = PERF_REG_X86_ZMM2 + 8, -+ PERF_REG_X86_ZMM4 = PERF_REG_X86_ZMM3 + 8, -+ PERF_REG_X86_ZMM5 = PERF_REG_X86_ZMM4 + 8, -+ PERF_REG_X86_ZMM6 = PERF_REG_X86_ZMM5 + 8, -+ PERF_REG_X86_ZMM7 = PERF_REG_X86_ZMM6 + 8, -+ PERF_REG_X86_ZMM8 = PERF_REG_X86_ZMM7 + 8, -+ PERF_REG_X86_ZMM9 = PERF_REG_X86_ZMM8 + 8, -+ PERF_REG_X86_ZMM10 = PERF_REG_X86_ZMM9 + 8, -+ PERF_REG_X86_ZMM11 = PERF_REG_X86_ZMM10 + 8, -+ PERF_REG_X86_ZMM12 = PERF_REG_X86_ZMM11 + 8, -+ PERF_REG_X86_ZMM13 = PERF_REG_X86_ZMM12 + 8, -+ PERF_REG_X86_ZMM14 = PERF_REG_X86_ZMM13 + 8, -+ PERF_REG_X86_ZMM15 = PERF_REG_X86_ZMM14 + 8, -+ PERF_REG_X86_ZMM16 = PERF_REG_X86_ZMM15 + 8, -+ PERF_REG_X86_ZMM17 = PERF_REG_X86_ZMM16 + 8, -+ PERF_REG_X86_ZMM18 = PERF_REG_X86_ZMM17 + 8, -+ PERF_REG_X86_ZMM19 = PERF_REG_X86_ZMM18 + 8, -+ PERF_REG_X86_ZMM20 = PERF_REG_X86_ZMM19 + 8, -+ PERF_REG_X86_ZMM21 = PERF_REG_X86_ZMM20 + 8, -+ PERF_REG_X86_ZMM22 = PERF_REG_X86_ZMM21 + 8, -+ PERF_REG_X86_ZMM23 = PERF_REG_X86_ZMM22 + 8, -+ PERF_REG_X86_ZMM24 = PERF_REG_X86_ZMM23 + 8, -+ PERF_REG_X86_ZMM25 = PERF_REG_X86_ZMM24 + 8, -+ PERF_REG_X86_ZMM26 = PERF_REG_X86_ZMM25 + 8, -+ PERF_REG_X86_ZMM27 = PERF_REG_X86_ZMM26 + 8, -+ PERF_REG_X86_ZMM28 = PERF_REG_X86_ZMM27 + 8, -+ PERF_REG_X86_ZMM29 = PERF_REG_X86_ZMM28 + 8, -+ PERF_REG_X86_ZMM30 = PERF_REG_X86_ZMM29 + 8, -+ PERF_REG_X86_ZMM31 = PERF_REG_X86_ZMM30 + 8, -+ PERF_REG_X86_ZMM_MAX = PERF_REG_X86_ZMM31 + 8, -+ -+ /* -+ * OPMASK Registers -+ * PERF_REG_X86_OPMASK0 = 448 -+ */ -+ PERF_REG_X86_OPMASK0 = PERF_REG_X86_ZMM_MAX, -+ PERF_REG_X86_OPMASK1 = PERF_REG_X86_OPMASK0 + 1, -+ PERF_REG_X86_OPMASK2 = PERF_REG_X86_OPMASK1 + 1, -+ PERF_REG_X86_OPMASK3 = PERF_REG_X86_OPMASK2 + 1, -+ PERF_REG_X86_OPMASK4 = PERF_REG_X86_OPMASK3 + 1, -+ PERF_REG_X86_OPMASK5 = PERF_REG_X86_OPMASK4 + 1, -+ PERF_REG_X86_OPMASK6 = PERF_REG_X86_OPMASK5 + 1, -+ PERF_REG_X86_OPMASK7 = PERF_REG_X86_OPMASK6 + 1, -+ -+ PERF_REG_X86_VEC_MAX = PERF_REG_X86_OPMASK7 + 1, - }; - - #define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1)) -diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c -index 985bd616200e..466ccd67ea99 100644 ---- a/arch/x86/kernel/perf_regs.c -+++ b/arch/x86/kernel/perf_regs.c -@@ -59,12 +59,55 @@ static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = { - #endif - }; - -+static u64 perf_reg_ext_value(struct pt_regs *regs, int idx) -+{ -+ struct x86_perf_regs *perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ u64 data; -+ int mod; -+ -+ switch (idx) { -+ case PERF_REG_X86_YMM0 ... PERF_REG_X86_YMM_MAX - 1: -+ idx -= PERF_REG_X86_YMM0; -+ mod = idx % 4; -+ if (mod < 2) -+ data = !perf_regs->xmm_regs ? 0 : perf_regs->xmm_regs[idx / 4 + mod]; -+ else -+ data = !perf_regs->ymmh_regs ? 0 : perf_regs->ymmh_regs[idx / 4 + mod - 2]; -+ return data; -+ case PERF_REG_X86_ZMM0 ... PERF_REG_X86_ZMM16 - 1: -+ idx -= PERF_REG_X86_ZMM0; -+ mod = idx % 8; -+ if (mod < 4) { -+ if (mod < 2) -+ data = !perf_regs->xmm_regs ? 0 : perf_regs->xmm_regs[idx / 8 + mod]; -+ else -+ data = !perf_regs->ymmh_regs ? 0 : perf_regs->ymmh_regs[idx / 8 + mod - 2]; -+ } else { -+ data = !perf_regs->zmmh_regs ? 0 : perf_regs->zmmh_regs[idx / 8 + mod - 4]; -+ } -+ return data; -+ case PERF_REG_X86_ZMM16 ... PERF_REG_X86_ZMM_MAX - 1: -+ idx -= PERF_REG_X86_ZMM16; -+ return !perf_regs->h16zmm_regs ? 0 : perf_regs->h16zmm_regs[idx]; -+ case PERF_REG_X86_OPMASK0 ... PERF_REG_X86_OPMASK7: -+ idx -= PERF_REG_X86_OPMASK0; -+ return !perf_regs->opmask_regs ? 0 : perf_regs->opmask_regs[idx]; -+ default: -+ WARN_ON_ONCE(1); -+ break; -+ } -+ -+ return 0; -+} -+ - u64 perf_reg_value(struct pt_regs *regs, int idx) - { -- struct x86_perf_regs *perf_regs; -+ struct x86_perf_regs *perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ -+ if (idx >= PERF_REG_EXTENDED_OFFSET) -+ return perf_reg_ext_value(regs, idx); - - if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) { -- perf_regs = container_of(regs, struct x86_perf_regs, regs); - if (!perf_regs->xmm_regs) - return 0; - return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0]; -@@ -102,6 +145,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - return PERF_SAMPLE_REGS_ABI_32; -@@ -127,6 +175,18 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ if (!mask || !size || size > PERF_NUM_EXT_REGS) -+ return -EINVAL; -+ -+ if (find_last_bit(mask, size) > -+ (PERF_REG_X86_VEC_MAX - PERF_REG_EXTENDED_OFFSET)) -+ return -EINVAL; -+ -+ return 0; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - if (!user_64bit_mode(task_pt_regs(task))) -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index ec9d96025683..dd9bfe42c011 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -305,6 +305,7 @@ struct perf_event_pmu_context; - #define PERF_PMU_CAP_EXTENDED_HW_TYPE 0x0100 - #define PERF_PMU_CAP_AUX_PAUSE 0x0200 - #define PERF_PMU_CAP_AUX_PREFER_LARGE 0x0400 -+#define PERF_PMU_CAP_MORE_EXT_REGS 0x0800 - - /** - * pmu::scope -@@ -1476,6 +1477,9 @@ static inline void perf_clear_branch_entry_bitfields(struct perf_branch_entry *b - br->reserved = 0; - } - -+extern bool has_more_extended_intr_regs(struct perf_event *event); -+extern bool has_more_extended_user_regs(struct perf_event *event); -+extern bool has_more_extended_regs(struct perf_event *event); - extern void perf_output_sample(struct perf_output_handle *handle, - struct perf_event_header *header, - struct perf_sample_data *data, -diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h -index f632c5725f16..aa4dfb5af552 100644 ---- a/include/linux/perf_regs.h -+++ b/include/linux/perf_regs.h -@@ -9,6 +9,8 @@ struct perf_regs { - struct pt_regs *regs; - }; - -+#define PERF_REG_EXTENDED_OFFSET 64 -+ - #ifdef CONFIG_HAVE_PERF_REGS - #include - -@@ -21,6 +23,8 @@ int perf_reg_validate(u64 mask); - u64 perf_reg_abi(struct task_struct *task); - void perf_get_regs_user(struct perf_regs *regs_user, - struct pt_regs *regs); -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size); -+ - #else - - #define PERF_REG_EXTENDED_MASK 0 -@@ -35,6 +39,12 @@ static inline int perf_reg_validate(u64 mask) - return mask ? -ENOSYS : 0; - } - -+static inline int perf_reg_ext_validate(unsigned long *mask, -+ unsigned int size) -+{ -+ return -EINVAL; -+} -+ - static inline u64 perf_reg_abi(struct task_struct *task) - { - return PERF_SAMPLE_REGS_ABI_NONE; -diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h -index 78a362b80027..8de3c287096f 100644 ---- a/include/uapi/linux/perf_event.h -+++ b/include/uapi/linux/perf_event.h -@@ -382,6 +382,10 @@ enum perf_event_read_format { - #define PERF_ATTR_SIZE_VER6 120 /* Add: aux_sample_size */ - #define PERF_ATTR_SIZE_VER7 128 /* Add: sig_data */ - #define PERF_ATTR_SIZE_VER8 136 /* Add: config3 */ -+#define PERF_ATTR_SIZE_VER9 168 /* add: sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE] */ -+ -+#define PERF_EXT_REGS_ARRAY_SIZE 7 -+#define PERF_NUM_EXT_REGS (PERF_EXT_REGS_ARRAY_SIZE * 64) - - /* - * 'struct perf_event_attr' contains various attributes that define -@@ -543,6 +547,13 @@ struct perf_event_attr { - __u64 sig_data; - - __u64 config3; /* extension of config2 */ -+ -+ /* -+ * Extension sets of regs to dump for each sample. -+ * See asm/perf_regs.h for details. -+ */ -+ __u64 sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE]; -+ __u64 sample_regs_user_ext[PERF_EXT_REGS_ARRAY_SIZE]; - }; - - /* -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 872122e074e5..0ff00d0b59ea 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -7434,6 +7434,21 @@ perf_output_sample_regs(struct perf_output_handle *handle, - } - } - -+static void -+perf_output_sample_regs_ext(struct perf_output_handle *handle, -+ struct pt_regs *regs, -+ unsigned long *mask, -+ unsigned int size) -+{ -+ int bit; -+ u64 val; -+ -+ for_each_set_bit(bit, mask, size) { -+ val = perf_reg_value(regs, bit + PERF_REG_EXTENDED_OFFSET); -+ perf_output_put(handle, val); -+ } -+} -+ - static void perf_sample_regs_user(struct perf_regs *regs_user, - struct pt_regs *regs) - { -@@ -7866,6 +7881,26 @@ static void perf_output_read(struct perf_output_handle *handle, - perf_output_read_one(handle, event, enabled, running); - } - -+inline bool has_more_extended_intr_regs(struct perf_event *event) -+{ -+ return !!bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS); -+} -+ -+inline bool has_more_extended_user_regs(struct perf_event *event) -+{ -+ return !!bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_user_ext, -+ PERF_NUM_EXT_REGS); -+} -+ -+inline bool has_more_extended_regs(struct perf_event *event) -+{ -+ return has_more_extended_intr_regs(event) || -+ has_more_extended_user_regs(event); -+} -+ - void perf_output_sample(struct perf_output_handle *handle, - struct perf_event_header *header, - struct perf_sample_data *data, -@@ -7991,6 +8026,12 @@ void perf_output_sample(struct perf_output_handle *handle, - perf_output_sample_regs(handle, - data->regs_user.regs, - mask); -+ if (has_more_extended_user_regs(event)) { -+ perf_output_sample_regs_ext( -+ handle, data->regs_user.regs, -+ (unsigned long *)event->attr.sample_regs_user_ext, -+ PERF_NUM_EXT_REGS); -+ } - } - } - -@@ -8023,6 +8064,12 @@ void perf_output_sample(struct perf_output_handle *handle, - perf_output_sample_regs(handle, - data->regs_intr.regs, - mask); -+ if (has_more_extended_intr_regs(event)) { -+ perf_output_sample_regs_ext( -+ handle, data->regs_intr.regs, -+ (unsigned long *)event->attr.sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS); -+ } - } - } - -@@ -8277,6 +8324,12 @@ void perf_prepare_sample(struct perf_sample_data *data, - if (data->regs_user.regs) { - u64 mask = event->attr.sample_regs_user; - size += hweight64(mask) * sizeof(u64); -+ -+ if (has_more_extended_user_regs(event)) { -+ size += bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_user_ext, -+ PERF_NUM_EXT_REGS) * sizeof(u64); -+ } - } - - data->dyn_size += size; -@@ -8340,6 +8393,12 @@ void perf_prepare_sample(struct perf_sample_data *data, - u64 mask = event->attr.sample_regs_intr; - - size += hweight64(mask) * sizeof(u64); -+ -+ if (has_more_extended_intr_regs(event)) { -+ size += bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS) * sizeof(u64); -+ } - } - - data->dyn_size += size; -@@ -12598,6 +12657,12 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) - goto err_destroy; - } - -+ if (!(pmu->capabilities & PERF_PMU_CAP_MORE_EXT_REGS) && -+ has_more_extended_regs(event)) { -+ ret = -EOPNOTSUPP; -+ goto err_destroy; -+ } -+ - if (pmu->capabilities & PERF_PMU_CAP_NO_EXCLUDE && - event_has_any_exclude_flag(event)) { - ret = -EINVAL; -@@ -13130,9 +13195,19 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, - } - - if (attr->sample_type & PERF_SAMPLE_REGS_USER) { -- ret = perf_reg_validate(attr->sample_regs_user); -- if (ret) -- return ret; -+ if (attr->sample_regs_user != 0) { -+ ret = perf_reg_validate(attr->sample_regs_user); -+ if (ret) -+ return ret; -+ } -+ if (!!bitmap_weight((unsigned long *)attr->sample_regs_user_ext, -+ PERF_NUM_EXT_REGS)) { -+ ret = perf_reg_ext_validate( -+ (unsigned long *)attr->sample_regs_user_ext, -+ PERF_NUM_EXT_REGS); -+ if (ret) -+ return ret; -+ } - } - - if (attr->sample_type & PERF_SAMPLE_STACK_USER) { -@@ -13153,8 +13228,21 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, - if (!attr->sample_max_stack) - attr->sample_max_stack = sysctl_perf_event_max_stack; - -- if (attr->sample_type & PERF_SAMPLE_REGS_INTR) -- ret = perf_reg_validate(attr->sample_regs_intr); -+ if (attr->sample_type & PERF_SAMPLE_REGS_INTR) { -+ if (attr->sample_regs_intr != 0) { -+ ret = perf_reg_validate(attr->sample_regs_intr); -+ if (ret) -+ return ret; -+ } -+ if (!!bitmap_weight((unsigned long *)attr->sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS)) { -+ ret = perf_reg_ext_validate( -+ (unsigned long *)attr->sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS); -+ if (ret) -+ return ret; -+ } -+ } - - #ifndef CONFIG_CGROUP_PERF - if (attr->sample_type & PERF_SAMPLE_CGROUP) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0022-x86-fred-Enable-FRED-by-default.nmi b/SPECS/kernel-rt/0022-x86-fred-Enable-FRED-by-default.nmi deleted file mode 100644 index 9b93befbf..000000000 --- a/SPECS/kernel-rt/0022-x86-fred-Enable-FRED-by-default.nmi +++ /dev/null @@ -1,31 +0,0 @@ -From 993fec078b97b8a3ebea1efbaa821d7f61f1a8d7 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Fri, 14 Feb 2025 10:31:18 -0800 -Subject: [PATCH 22/44] x86/fred: Enable FRED by default - -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kernel/cpu/common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c -index cb14919f92da..96f28657072e 100644 ---- a/arch/x86/kernel/cpu/common.c -+++ b/arch/x86/kernel/cpu/common.c -@@ -1693,7 +1693,7 @@ static void __init cpu_parse_early_param(void) - - /* Minimize the gap between FRED is available and available but disabled. */ - arglen = cmdline_find_option(boot_command_line, "fred", arg, sizeof(arg)); -- if (arglen != 2 || strncmp(arg, "on", 2)) -+ if (arglen == 3 && !strncmp(arg, "off", 3)) - setup_clear_cpu_cap(X86_FEATURE_FRED); - - arglen = cmdline_find_option(boot_command_line, "clearcpuid", arg, sizeof(arg)); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet b/SPECS/kernel-rt/0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet deleted file mode 100644 index 5439d6537..000000000 --- a/SPECS/kernel-rt/0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet +++ /dev/null @@ -1,45 +0,0 @@ -From 825f0784fbb7768bf0c047882f3cf25b0c532717 Mon Sep 17 00:00:00 2001 -From: Chao Gao -Date: Fri, 4 Jul 2025 01:49:53 -0700 -Subject: [PATCH 23/24] KVM: nVMX: Add consistency checks for CR0.WP and - CR4.CET - -Add consistency checks for CR4.CET and CR0.WP in guest-state or host-state -area in the VMCS12. This ensures that configurations with CR4.CET set and -CR0.WP not set result in VM-entry failure, aligning with architectural -behavior. - -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 0f8dbf3cc6c3..bdaca6ada244 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3147,6 +3147,9 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - CC(!kvm_vcpu_is_legal_cr3(vcpu, vmcs12->host_cr3))) - return -EINVAL; - -+ if (CC(vmcs12->host_cr4 & X86_CR4_CET && !(vmcs12->host_cr0 & X86_CR0_WP))) -+ return -EINVAL; -+ - if (CC(is_noncanonical_msr_address(vmcs12->host_ia32_sysenter_esp, vcpu)) || - CC(is_noncanonical_msr_address(vmcs12->host_ia32_sysenter_eip, vcpu))) - return -EINVAL; -@@ -3261,6 +3264,9 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, - CC(!nested_guest_cr4_valid(vcpu, vmcs12->guest_cr4))) - return -EINVAL; - -+ if (CC(vmcs12->guest_cr4 & X86_CR4_CET && !(vmcs12->guest_cr0 & X86_CR0_WP))) -+ return -EINVAL; -+ - if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS) && - (CC(!kvm_dr7_valid(vmcs12->guest_dr7)) || - CC(!vmx_is_valid_debugctl(vcpu, vmcs12->guest_ia32_debugctl, false)))) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi b/SPECS/kernel-rt/0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi new file mode 100644 index 000000000..270e29e79 --- /dev/null +++ b/SPECS/kernel-rt/0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi @@ -0,0 +1,140 @@ +From e21ba60640193c49ee8eafd79f948fa519dccca9 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Fri, 27 Oct 2023 00:00:09 -0700 +Subject: [PATCH 23/44] KVM: selftests: Run debug_regs test with FRED enabled + +Run another round of debug_regs test with FRED enabled if FRED is +available. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + .../selftests/kvm/include/x86/processor.h | 4 ++ + tools/testing/selftests/kvm/x86/debug_regs.c | 50 ++++++++++++++----- + 2 files changed, 41 insertions(+), 13 deletions(-) + +diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h +index 51cd84b9ca664..1b07685533a1c 100644 +--- a/tools/testing/selftests/kvm/include/x86/processor.h ++++ b/tools/testing/selftests/kvm/include/x86/processor.h +@@ -59,6 +59,7 @@ const char *ex_str(int vector); + #define X86_CR4_SMEP (1ul << 20) + #define X86_CR4_SMAP (1ul << 21) + #define X86_CR4_PKE (1ul << 22) ++#define X86_CR4_FRED (1ul << 32) + + struct xstate_header { + u64 xstate_bv; +@@ -175,6 +176,9 @@ struct kvm_x86_cpu_feature { + #define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26) + #define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29) + #define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31) ++#define X86_FEATURE_FRED KVM_X86_CPU_FEATURE(0x7, 1, EAX, 17) ++#define X86_FEATURE_LKGS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 18) ++#define X86_FEATURE_WRMSRNS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 19) + #define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17) + #define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18) + #define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3) +diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c +index 2d814c1d1dc44..80cef67010ea2 100644 +--- a/tools/testing/selftests/kvm/x86/debug_regs.c ++++ b/tools/testing/selftests/kvm/x86/debug_regs.c +@@ -20,7 +20,7 @@ uint32_t guest_value; + + extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; + +-static void guest_code(void) ++static noinline void guest_test_code(void) + { + /* Create a pending interrupt on current vCPU */ + x2apic_enable(); +@@ -64,6 +64,15 @@ static void guest_code(void) + + /* DR6.BD test */ + asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); ++} ++ ++static void guest_code(void) ++{ ++ guest_test_code(); ++ ++ if (get_cr4() & X86_CR4_FRED) ++ guest_test_code(); ++ + GUEST_DONE(); + } + +@@ -78,19 +87,15 @@ static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) + vcpu_regs_set(vcpu, ®s); + } + +-int main(void) ++void run_test(struct kvm_vcpu *vcpu) + { + struct kvm_guest_debug debug; ++ struct kvm_run *run = vcpu->run; + unsigned long long target_dr6, target_rip; +- struct kvm_vcpu *vcpu; +- struct kvm_run *run; +- struct kvm_vm *vm; +- struct ucall uc; +- uint64_t cmd; + int i; + /* Instruction lengths starting at ss_start */ + int ss_size[6] = { +- 1, /* sti*/ ++ 1, /* sti */ + 2, /* xor */ + 2, /* cpuid */ + 5, /* mov */ +@@ -98,11 +103,6 @@ int main(void) + 1, /* cli */ + }; + +- TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); +- +- vm = vm_create_with_one_vcpu(&vcpu, guest_code); +- run = vcpu->run; +- + /* Test software BPs - int3 */ + memset(&debug, 0, sizeof(debug)); + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; +@@ -205,6 +205,30 @@ int main(void) + /* Disable all debug controls, run to the end */ + memset(&debug, 0, sizeof(debug)); + vcpu_guest_debug_set(vcpu, &debug); ++} ++ ++int main(void) ++{ ++ struct kvm_vcpu *vcpu; ++ struct kvm_vm *vm; ++ struct ucall uc; ++ uint64_t cmd; ++ ++ TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); ++ ++ vm = vm_create_with_one_vcpu(&vcpu, guest_code); ++ ++ run_test(vcpu); ++ ++ if (kvm_cpu_has(X86_FEATURE_FRED)) { ++ struct kvm_sregs sregs; ++ ++ vcpu_sregs_get(vcpu, &sregs); ++ sregs.cr4 |= X86_CR4_FRED; ++ vcpu_sregs_set(vcpu, &sregs); ++ ++ run_test(vcpu); ++ } + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf b/SPECS/kernel-rt/0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf deleted file mode 100644 index be9b339cf..000000000 --- a/SPECS/kernel-rt/0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf +++ /dev/null @@ -1,368 +0,0 @@ -From 448cd074e716108e97a5fea5027f7cac653a5ade Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 09:39:33 +0000 -Subject: [PATCH 023/100] perf/x86/intel: Support arch-PEBS vector registers - group capturing - -Add x86/intel specific vector register (VECR) group capturing for -arch-PEBS. Enable corresponding VECR group bits in -GPx_CFG_C/FX0_CFG_C MSRs if users configures these vector registers -bitmap in perf_event_attr and parse VECR group in arch-PEBS record. - -Currently vector registers capturing is only supported by PEBS based -sampling, PMU driver would return error if PMI based sampling tries to -capture these vector registers. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 90 +++++++++++++++++++++++++++++- - arch/x86/events/intel/core.c | 15 +++++ - arch/x86/events/intel/ds.c | 93 ++++++++++++++++++++++++++++--- - arch/x86/include/asm/msr-index.h | 6 ++ - arch/x86/include/asm/perf_event.h | 20 +++++++ - 5 files changed, 214 insertions(+), 10 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index ad919f980389..56d197a56d59 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -581,6 +581,73 @@ int x86_pmu_max_precise(struct pmu *pmu) - return precise; - } - -+static bool has_vec_regs(struct perf_event *event, bool user, -+ int start, int end) -+{ -+ int idx = (start - PERF_REG_EXTENDED_OFFSET) / 64; -+ int s = start % 64; -+ int e = end % 64; -+ u64 regs_mask; -+ -+ if (user) -+ regs_mask = event->attr.sample_regs_user_ext[idx]; -+ else -+ regs_mask = event->attr.sample_regs_intr_ext[idx]; -+ -+ return regs_mask & GENMASK_ULL(e, s); -+} -+ -+static inline bool has_ymm_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_YMM0, PERF_REG_X86_YMM_MAX - 1); -+} -+ -+static inline bool has_zmm_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_ZMM0, PERF_REG_X86_ZMM8 - 1) || -+ has_vec_regs(event, user, PERF_REG_X86_ZMM8, PERF_REG_X86_ZMM16 - 1); -+} -+ -+static inline bool has_h16zmm_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_ZMM16, PERF_REG_X86_ZMM24 - 1) || -+ has_vec_regs(event, user, PERF_REG_X86_ZMM24, PERF_REG_X86_ZMM_MAX - 1); -+} -+ -+static inline bool has_opmask_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_OPMASK0, PERF_REG_X86_OPMASK7); -+} -+ -+static bool ext_vec_regs_supported(struct perf_event *event, bool user) -+{ -+ u64 caps = hybrid(event->pmu, arch_pebs_cap).caps; -+ -+ if (!(event->pmu->capabilities & PERF_PMU_CAP_MORE_EXT_REGS)) -+ return false; -+ -+ if (has_opmask_regs(event, user) && !(caps & ARCH_PEBS_VECR_OPMASK)) -+ return false; -+ -+ if (has_ymm_regs(event, user) && !(caps & ARCH_PEBS_VECR_YMMH)) -+ return false; -+ -+ if (has_zmm_regs(event, user) && !(caps & ARCH_PEBS_VECR_ZMMH)) -+ return false; -+ -+ if (has_h16zmm_regs(event, user) && !(caps & ARCH_PEBS_VECR_H16ZMM)) -+ return false; -+ -+ if (!event->attr.precise_ip) -+ return false; -+ -+ /* Only user space sampling is allowed for extended vector registers. */ -+ if (user && !event->attr.exclude_kernel) -+ return false; -+ -+ return true; -+} -+ - int x86_pmu_hw_config(struct perf_event *event) - { - if (event->attr.precise_ip) { -@@ -666,9 +733,12 @@ int x86_pmu_hw_config(struct perf_event *event) - return -EINVAL; - } - -- /* sample_regs_user never support XMM registers */ -- if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)) -- return -EINVAL; -+ if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)) { -+ /* Only user space sampling is allowed for XMM registers. */ -+ if (!event->attr.exclude_kernel) -+ return -EINVAL; -+ } -+ - /* - * Besides the general purpose registers, XMM registers may - * be collected in PEBS on some platforms, e.g. Icelake -@@ -681,6 +751,20 @@ int x86_pmu_hw_config(struct perf_event *event) - return -EINVAL; - } - -+ /* -+ * Architectural PEBS supports to capture more vector registers besides -+ * XMM registers, like YMM, OPMASK and ZMM registers. -+ */ -+ if (unlikely(has_more_extended_user_regs(event))) { -+ if (!ext_vec_regs_supported(event, true)) -+ return -EINVAL; -+ } -+ -+ if (unlikely(has_more_extended_intr_regs(event))) { -+ if (!ext_vec_regs_supported(event, false)) -+ return -EINVAL; -+ } -+ - return x86_setup_perfctr(event); - } - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 007414c37ad1..0ed9cb66b49e 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3010,6 +3010,18 @@ static void intel_pmu_enable_event_ext(struct perf_event *event) - if (pebs_data_cfg & PEBS_DATACFG_XMMS) - ext |= ARCH_PEBS_VECR_XMM & cap.caps; - -+ if (pebs_data_cfg & PEBS_DATACFG_YMMHS) -+ ext |= ARCH_PEBS_VECR_YMMH & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_OPMASKS) -+ ext |= ARCH_PEBS_VECR_OPMASK & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_ZMMHS) -+ ext |= ARCH_PEBS_VECR_ZMMH & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_H16ZMMS) -+ ext |= ARCH_PEBS_VECR_H16ZMM & cap.caps; -+ - if (pebs_data_cfg & PEBS_DATACFG_LBRS) - ext |= ARCH_PEBS_LBR & cap.caps; - -@@ -5573,6 +5585,9 @@ static inline void __intel_update_pmu_caps(struct pmu *pmu) - - if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_XMM) - dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS; -+ -+ if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_EXT) -+ dest_pmu->capabilities |= PERF_PMU_CAP_MORE_EXT_REGS; - } - - static inline void __intel_update_large_pebs_flags(struct pmu *pmu) -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 3a88f62ae127..5c4500d95174 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1426,6 +1426,34 @@ void intel_pmu_pebs_late_setup(struct cpu_hw_events *cpuc) - PERF_SAMPLE_TRANSACTION | \ - PERF_SAMPLE_DATA_PAGE_SIZE) - -+static u64 pebs_get_ext_reg_data_cfg(unsigned long *ext_reg) -+{ -+ u64 pebs_data_cfg = 0; -+ int bit; -+ -+ for_each_set_bit(bit, ext_reg, PERF_NUM_EXT_REGS) { -+ switch (bit + PERF_REG_EXTENDED_OFFSET) { -+ case PERF_REG_X86_OPMASK0 ... PERF_REG_X86_OPMASK7: -+ pebs_data_cfg |= PEBS_DATACFG_OPMASKS; -+ break; -+ case PERF_REG_X86_YMM0 ... PERF_REG_X86_YMM_MAX - 1: -+ pebs_data_cfg |= PEBS_DATACFG_YMMHS | PEBS_DATACFG_XMMS; -+ break; -+ case PERF_REG_X86_ZMM0 ... PERF_REG_X86_ZMM16 - 1: -+ pebs_data_cfg |= PEBS_DATACFG_ZMMHS | PEBS_DATACFG_YMMHS | -+ PEBS_DATACFG_XMMS; -+ break; -+ case PERF_REG_X86_ZMM16 ... PERF_REG_X86_ZMM_MAX - 1: -+ pebs_data_cfg |= PEBS_DATACFG_H16ZMMS; -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return pebs_data_cfg; -+} -+ - static u64 pebs_update_adaptive_cfg(struct perf_event *event) - { - struct perf_event_attr *attr = &event->attr; -@@ -1460,9 +1488,21 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event) - if (gprs || (attr->precise_ip < 2) || tsx_weight) - pebs_data_cfg |= PEBS_DATACFG_GP; - -- if ((sample_type & PERF_SAMPLE_REGS_INTR) && -- (attr->sample_regs_intr & PERF_REG_EXTENDED_MASK)) -- pebs_data_cfg |= PEBS_DATACFG_XMMS; -+ if (sample_type & PERF_SAMPLE_REGS_INTR) { -+ if (attr->sample_regs_intr & PERF_REG_EXTENDED_MASK) -+ pebs_data_cfg |= PEBS_DATACFG_XMMS; -+ -+ pebs_data_cfg |= pebs_get_ext_reg_data_cfg( -+ (unsigned long *)event->attr.sample_regs_intr_ext); -+ } -+ -+ if (sample_type & PERF_SAMPLE_REGS_USER) { -+ if (attr->sample_regs_user & PERF_REG_EXTENDED_MASK) -+ pebs_data_cfg |= PEBS_DATACFG_XMMS; -+ -+ pebs_data_cfg |= pebs_get_ext_reg_data_cfg( -+ (unsigned long *)event->attr.sample_regs_user_ext); -+ } - - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { - /* -@@ -2243,6 +2283,10 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ymmh_regs = NULL; -+ perf_regs->opmask_regs = NULL; -+ perf_regs->zmmh_regs = NULL; -+ perf_regs->h16zmm_regs = NULL; - perf_regs->ssp = 0; - - format_group = basic->format_group; -@@ -2360,6 +2404,10 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ymmh_regs = NULL; -+ perf_regs->opmask_regs = NULL; -+ perf_regs->zmmh_regs = NULL; -+ perf_regs->h16zmm_regs = NULL; - perf_regs->ssp = 0; - - __setup_perf_sample_data(event, iregs, data); -@@ -2410,14 +2458,45 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - meminfo->tsx_tuning, ax); - } - -- if (header->xmm) { -+ if (header->xmm || header->ymmh || header->opmask || -+ header->zmmh || header->h16zmm) { - struct arch_pebs_xmm *xmm; -+ struct arch_pebs_ymmh *ymmh; -+ struct arch_pebs_zmmh *zmmh; -+ struct arch_pebs_h16zmm *h16zmm; -+ struct arch_pebs_opmask *opmask; - - next_record += sizeof(struct arch_pebs_xer_header); - -- xmm = next_record; -- perf_regs->xmm_regs = xmm->xmm; -- next_record = xmm + 1; -+ if (header->xmm) { -+ xmm = next_record; -+ perf_regs->xmm_regs = xmm->xmm; -+ next_record = xmm + 1; -+ } -+ -+ if (header->ymmh) { -+ ymmh = next_record; -+ perf_regs->ymmh_regs = ymmh->ymmh; -+ next_record = ymmh + 1; -+ } -+ -+ if (header->opmask) { -+ opmask = next_record; -+ perf_regs->opmask_regs = opmask->opmask; -+ next_record = opmask + 1; -+ } -+ -+ if (header->zmmh) { -+ zmmh = next_record; -+ perf_regs->zmmh_regs = zmmh->zmmh; -+ next_record = zmmh + 1; -+ } -+ -+ if (header->h16zmm) { -+ h16zmm = next_record; -+ perf_regs->h16zmm_regs = h16zmm->h16zmm; -+ next_record = h16zmm + 1; -+ } - } - - if (header->lbr) { -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 41852e8690d7..84a1cbe6ab30 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -338,6 +338,12 @@ - #define ARCH_PEBS_LBR_SHIFT 40 - #define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT) - #define ARCH_PEBS_VECR_XMM BIT_ULL(49) -+#define ARCH_PEBS_VECR_YMMH BIT_ULL(50) -+#define ARCH_PEBS_VECR_OPMASK BIT_ULL(53) -+#define ARCH_PEBS_VECR_ZMMH BIT_ULL(54) -+#define ARCH_PEBS_VECR_H16ZMM BIT_ULL(55) -+#define ARCH_PEBS_VECR_EXT_SHIFT 50 -+#define ARCH_PEBS_VECR_EXT (0x3full << ARCH_PEBS_VECR_EXT_SHIFT) - #define ARCH_PEBS_GPR BIT_ULL(61) - #define ARCH_PEBS_AUX BIT_ULL(62) - #define ARCH_PEBS_EN BIT_ULL(63) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index a4f7cfc3fddb..e723666c1de2 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -146,6 +146,10 @@ - #define PEBS_DATACFG_LBRS BIT_ULL(3) - #define PEBS_DATACFG_CNTR BIT_ULL(4) - #define PEBS_DATACFG_METRICS BIT_ULL(5) -+#define PEBS_DATACFG_YMMHS BIT_ULL(6) -+#define PEBS_DATACFG_OPMASKS BIT_ULL(7) -+#define PEBS_DATACFG_ZMMHS BIT_ULL(8) -+#define PEBS_DATACFG_H16ZMMS BIT_ULL(9) - #define PEBS_DATACFG_LBR_SHIFT 24 - #define PEBS_DATACFG_CNTR_SHIFT 32 - #define PEBS_DATACFG_CNTR_MASK GENMASK_ULL(15, 0) -@@ -593,6 +597,22 @@ struct arch_pebs_xmm { - u64 xmm[16*2]; /* two entries for each register */ - }; - -+struct arch_pebs_ymmh { -+ u64 ymmh[16*2]; /* two entries for each register */ -+}; -+ -+struct arch_pebs_opmask { -+ u64 opmask[8]; -+}; -+ -+struct arch_pebs_zmmh { -+ u64 zmmh[16*4]; /* four entries for each register */ -+}; -+ -+struct arch_pebs_h16zmm { -+ u64 h16zmm[16*8]; /* eight entries for each register */ -+}; -+ - #define ARCH_PEBS_LBR_NAN 0x0 - #define ARCH_PEBS_LBR_NUM_8 0x1 - #define ARCH_PEBS_LBR_NUM_16 0x2 --- -2.43.0 - diff --git a/SPECS/kernel-rt/0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi b/SPECS/kernel-rt/0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi deleted file mode 100644 index 9e7b527a4..000000000 --- a/SPECS/kernel-rt/0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi +++ /dev/null @@ -1,31 +0,0 @@ -From 7589f16ab5ffcf24895f0e3d91e09f8d8d9750dd Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Fri, 18 Jul 2025 19:46:29 -0700 -Subject: [PATCH 23/44] x86/entry/fred: Simply push __KERNEL_CS - -Simply push __KERNEL_CS directly, rather than moving it into RAX and -then pushing RAX. - -Suggested-by: H. Peter Anvin (Intel) -Signed-off-by: Xin Li (Intel) ---- - arch/x86/entry/entry_64_fred.S | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S -index 29c5c32c16c3..cb5ff2b1f6e7 100644 ---- a/arch/x86/entry/entry_64_fred.S -+++ b/arch/x86/entry/entry_64_fred.S -@@ -97,8 +97,7 @@ SYM_FUNC_START(asm_fred_entry_from_kvm) - push %rdi /* fred_ss handed in by the caller */ - push %rbp - pushf -- mov $__KERNEL_CS, %rax -- push %rax -+ push $__KERNEL_CS - - /* - * Unlike the IDT event delivery, FRED _always_ pushes an error code --- -2.43.0 - diff --git a/SPECS/kernel-rt/0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet b/SPECS/kernel-rt/0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet deleted file mode 100644 index 1ea739e5c..000000000 --- a/SPECS/kernel-rt/0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet +++ /dev/null @@ -1,117 +0,0 @@ -From cb08dfc62db2381f55172c749199bf2a24bfcc85 Mon Sep 17 00:00:00 2001 -From: Chao Gao -Date: Fri, 4 Jul 2025 01:49:54 -0700 -Subject: [PATCH 24/24] KVM: nVMX: Add consistency checks for CET states - -Introduce consistency checks for CET states during nested VM-entry. - -A VMCS contains both guest and host CET states, each comprising the -IA32_S_CET MSR, SSP, and IA32_INTERRUPT_SSP_TABLE_ADDR MSR. Various -checks are applied to CET states during VM-entry as documented in SDM -Vol3 Chapter "VM ENTRIES". Implement all these checks during nested -VM-entry to emulate the architectural behavior. - -In summary, there are three kinds of checks on guest/host CET states -during VM-entry: - -A. Checks applied to both guest states and host states: - - * The IA32_S_CET field must not set any reserved bits; bits 10 (SUPPRESS) - and 11 (TRACKER) cannot both be set. - * SSP should not have bits 1:0 set. - * The IA32_INTERRUPT_SSP_TABLE_ADDR field must be canonical. - -B. Checks applied to host states only - - * IA32_S_CET MSR and SSP must be canonical if the CPU enters 64-bit mode - after VM-exit. Otherwise, IA32_S_CET and SSP must have their higher 32 - bits cleared. - -C. Checks applied to guest states only: - - * IA32_S_CET MSR and SSP are not required to be canonical (i.e., 63:N-1 - are identical, where N is the CPU's maximum linear-address width). But, - bits 63:N of SSP must be identical. - -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 47 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 47 insertions(+) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index bdaca6ada244..a6a3e0cbbe89 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3137,6 +3137,17 @@ static bool is_l1_noncanonical_address_on_vmexit(u64 la, struct vmcs12 *vmcs12) - return !__is_canonical_address(la, l1_address_bits_on_exit); - } - -+static bool is_valid_cet_state(struct kvm_vcpu *vcpu, u64 s_cet, u64 ssp, u64 ssp_tbl) -+{ -+ if (!is_cet_msr_valid(vcpu, s_cet) || !IS_ALIGNED(ssp, 4)) -+ return false; -+ -+ if (is_noncanonical_msr_address(ssp_tbl, vcpu)) -+ return false; -+ -+ return true; -+} -+ - static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) - { -@@ -3206,6 +3217,26 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - return -EINVAL; - } - -+ if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_CET_STATE) { -+ if (CC(!is_valid_cet_state(vcpu, vmcs12->host_s_cet, vmcs12->host_ssp, -+ vmcs12->host_ssp_tbl))) -+ return -EINVAL; -+ -+ /* -+ * IA32_S_CET and SSP must be canonical if the host will -+ * enter 64-bit mode after VM-exit; otherwise, higher -+ * 32-bits must be all 0s. -+ */ -+ if (ia32e) { -+ if (CC(is_noncanonical_msr_address(vmcs12->host_s_cet, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ssp, vcpu))) -+ return -EINVAL; -+ } else { -+ if (CC(vmcs12->host_s_cet >> 32) || CC(vmcs12->host_ssp >> 32)) -+ return -EINVAL; -+ } -+ } -+ - return 0; - } - -@@ -3316,6 +3347,22 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, - CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) - return -EINVAL; - -+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { -+ if (CC(!is_valid_cet_state(vcpu, vmcs12->guest_s_cet, vmcs12->guest_ssp, -+ vmcs12->guest_ssp_tbl))) -+ return -EINVAL; -+ -+ /* -+ * Guest SSP must have 63:N bits identical, rather than -+ * be canonical (i.e., 63:N-1 bits identical), where N is -+ * the CPU's maximum linear-address width. Similar to -+ * is_noncanonical_msr_address(), use the host's -+ * linear-address width. -+ */ -+ if (CC(!__is_canonical_address(vmcs12->guest_ssp, max_host_virt_addr_bits() + 1))) -+ return -EINVAL; -+ } -+ - if (nested_check_guest_non_reg_state(vmcs12)) - return -EINVAL; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi b/SPECS/kernel-rt/0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi new file mode 100644 index 000000000..69b069a5c --- /dev/null +++ b/SPECS/kernel-rt/0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi @@ -0,0 +1,161 @@ +From fedae904881c69f1c3d84867c5f2b7e2ee5f8e80 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Mon, 6 Nov 2023 23:06:15 -0800 +Subject: [PATCH 24/44] KVM: selftests: Add a new VM guest mode to run user + level code + +Add a new VM guest mode VM_MODE_PXXV48_4K_USER to set the user bit of +guest page table entries, thus allow user level code to run in guests. + +Suggested-by: Sean Christopherson +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + .../testing/selftests/kvm/include/kvm_util.h | 1 + + tools/testing/selftests/kvm/lib/kvm_util.c | 5 +++- + .../testing/selftests/kvm/lib/x86/processor.c | 24 ++++++++++++++----- + tools/testing/selftests/kvm/lib/x86/vmx.c | 4 ++-- + 4 files changed, 25 insertions(+), 9 deletions(-) + +diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h +index d3f3e455c0310..9ab8968972eea 100644 +--- a/tools/testing/selftests/kvm/include/kvm_util.h ++++ b/tools/testing/selftests/kvm/include/kvm_util.h +@@ -185,6 +185,7 @@ enum vm_guest_mode { + VM_MODE_P36V48_64K, + VM_MODE_P47V47_16K, + VM_MODE_P36V47_16K, ++ VM_MODE_PXXV48_4K_USER, /* For 48bits VA but ANY bits PA with USER bit set */ + NUM_VM_MODES, + }; + +diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c +index 1a93d63616714..e64c23a9dad05 100644 +--- a/tools/testing/selftests/kvm/lib/kvm_util.c ++++ b/tools/testing/selftests/kvm/lib/kvm_util.c +@@ -209,6 +209,7 @@ const char *vm_guest_mode_string(uint32_t i) + [VM_MODE_P36V48_64K] = "PA-bits:36, VA-bits:48, 64K pages", + [VM_MODE_P47V47_16K] = "PA-bits:47, VA-bits:47, 16K pages", + [VM_MODE_P36V47_16K] = "PA-bits:36, VA-bits:47, 16K pages", ++ [VM_MODE_PXXV48_4K_USER] = "PA-bits:ANY, VA-bits:48, 4K user pages", + }; + _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, + "Missing new mode strings?"); +@@ -236,6 +237,7 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = { + [VM_MODE_P36V48_64K] = { 36, 48, 0x10000, 16 }, + [VM_MODE_P47V47_16K] = { 47, 47, 0x4000, 14 }, + [VM_MODE_P36V47_16K] = { 36, 47, 0x4000, 14 }, ++ [VM_MODE_PXXV48_4K_USER] = { 0, 0, 0x1000, 12 }, + }; + _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, + "Missing new mode params?"); +@@ -311,6 +313,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) + vm->pgtable_levels = 3; + break; + case VM_MODE_PXXV48_4K: ++ case VM_MODE_PXXV48_4K_USER: + #ifdef __x86_64__ + kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits); + kvm_init_vm_address_properties(vm); +@@ -327,7 +330,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) + vm->pgtable_levels = 4; + vm->va_bits = 48; + #else +- TEST_FAIL("VM_MODE_PXXV48_4K not supported on non-x86 platforms"); ++ TEST_FAIL("VM_MODE_PXXV48_4K(_USER) not supported on non-x86 platforms"); + #endif + break; + case VM_MODE_P47V64_4K: +diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c +index b418502c5ecc6..529997765fc63 100644 +--- a/tools/testing/selftests/kvm/lib/x86/processor.c ++++ b/tools/testing/selftests/kvm/lib/x86/processor.c +@@ -158,8 +158,8 @@ bool kvm_is_tdp_enabled(void) + + void virt_arch_pgd_alloc(struct kvm_vm *vm) + { +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " +- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), ++ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + /* If needed, create page map l4 table. */ + if (!vm->pgd_created) { +@@ -195,6 +195,8 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, + + if (!(*pte & PTE_PRESENT_MASK)) { + *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK; ++ if (vm->mode == VM_MODE_PXXV48_4K_USER) ++ *pte |= PTE_USER_MASK; + if (current_level == target_level) + *pte |= PTE_LARGE_MASK | (paddr & PHYSICAL_PAGE_MASK); + else +@@ -221,7 +223,7 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) + uint64_t *pml4e, *pdpe, *pde; + uint64_t *pte; + +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), + "Unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + TEST_ASSERT((vaddr % pg_size) == 0, +@@ -269,6 +271,9 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) + *pte |= vm->arch.c_bit; + else + *pte |= vm->arch.s_bit; ++ ++ if (vm->mode == VM_MODE_PXXV48_4K_USER) ++ *pte |= PTE_USER_MASK; + } + + void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +@@ -318,8 +323,8 @@ uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, + TEST_ASSERT(*level >= PG_LEVEL_NONE && *level < PG_LEVEL_NUM, + "Invalid PG_LEVEL_* '%d'", *level); + +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " +- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), ++ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, + (vaddr >> vm->page_shift)), + "Invalid virtual address, vaddr: 0x%lx", +@@ -526,7 +531,14 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu) + { + struct kvm_sregs sregs; + +- TEST_ASSERT_EQ(vm->mode, VM_MODE_PXXV48_4K); ++ switch (vm->mode) { ++ case VM_MODE_PXXV48_4K: ++ case VM_MODE_PXXV48_4K_USER: ++ break; ++ ++ default: ++ TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); ++ } + + /* Set mode specific system register values. */ + vcpu_sregs_get(vcpu, &sregs); +diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c +index d4d1208dd0238..4f7655b0830dd 100644 +--- a/tools/testing/selftests/kvm/lib/x86/vmx.c ++++ b/tools/testing/selftests/kvm/lib/x86/vmx.c +@@ -401,8 +401,8 @@ void __nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, + struct eptPageTableEntry *pt = vmx->eptp_hva, *pte; + uint16_t index; + +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " +- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), ++ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + TEST_ASSERT((nested_paddr >> 48) == 0, + "Nested physical address 0x%lx requires 5-level paging", +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi b/SPECS/kernel-rt/0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi deleted file mode 100644 index 851b2ac42..000000000 --- a/SPECS/kernel-rt/0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi +++ /dev/null @@ -1,140 +0,0 @@ -From a53bdf01705c28fe51beb4d2c1cfcc76d5dee00f Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Fri, 27 Oct 2023 00:00:09 -0700 -Subject: [PATCH 24/44] KVM: selftests: Run debug_regs test with FRED enabled - -Run another round of debug_regs test with FRED enabled if FRED is -available. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - .../selftests/kvm/include/x86/processor.h | 4 ++ - tools/testing/selftests/kvm/x86/debug_regs.c | 50 ++++++++++++++----- - 2 files changed, 41 insertions(+), 13 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h -index 232964f2a687..bcda4856180f 100644 ---- a/tools/testing/selftests/kvm/include/x86/processor.h -+++ b/tools/testing/selftests/kvm/include/x86/processor.h -@@ -57,6 +57,7 @@ extern uint64_t guest_tsc_khz; - #define X86_CR4_SMEP (1ul << 20) - #define X86_CR4_SMAP (1ul << 21) - #define X86_CR4_PKE (1ul << 22) -+#define X86_CR4_FRED (1ul << 32) - - struct xstate_header { - u64 xstate_bv; -@@ -173,6 +174,9 @@ struct kvm_x86_cpu_feature { - #define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26) - #define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29) - #define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31) -+#define X86_FEATURE_FRED KVM_X86_CPU_FEATURE(0x7, 1, EAX, 17) -+#define X86_FEATURE_LKGS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 18) -+#define X86_FEATURE_WRMSRNS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 19) - #define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17) - #define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18) - #define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3) -diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c -index 2d814c1d1dc4..80cef67010ea 100644 ---- a/tools/testing/selftests/kvm/x86/debug_regs.c -+++ b/tools/testing/selftests/kvm/x86/debug_regs.c -@@ -20,7 +20,7 @@ uint32_t guest_value; - - extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; - --static void guest_code(void) -+static noinline void guest_test_code(void) - { - /* Create a pending interrupt on current vCPU */ - x2apic_enable(); -@@ -64,6 +64,15 @@ static void guest_code(void) - - /* DR6.BD test */ - asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); -+} -+ -+static void guest_code(void) -+{ -+ guest_test_code(); -+ -+ if (get_cr4() & X86_CR4_FRED) -+ guest_test_code(); -+ - GUEST_DONE(); - } - -@@ -78,19 +87,15 @@ static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) - vcpu_regs_set(vcpu, ®s); - } - --int main(void) -+void run_test(struct kvm_vcpu *vcpu) - { - struct kvm_guest_debug debug; -+ struct kvm_run *run = vcpu->run; - unsigned long long target_dr6, target_rip; -- struct kvm_vcpu *vcpu; -- struct kvm_run *run; -- struct kvm_vm *vm; -- struct ucall uc; -- uint64_t cmd; - int i; - /* Instruction lengths starting at ss_start */ - int ss_size[6] = { -- 1, /* sti*/ -+ 1, /* sti */ - 2, /* xor */ - 2, /* cpuid */ - 5, /* mov */ -@@ -98,11 +103,6 @@ int main(void) - 1, /* cli */ - }; - -- TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); -- -- vm = vm_create_with_one_vcpu(&vcpu, guest_code); -- run = vcpu->run; -- - /* Test software BPs - int3 */ - memset(&debug, 0, sizeof(debug)); - debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; -@@ -205,6 +205,30 @@ int main(void) - /* Disable all debug controls, run to the end */ - memset(&debug, 0, sizeof(debug)); - vcpu_guest_debug_set(vcpu, &debug); -+} -+ -+int main(void) -+{ -+ struct kvm_vcpu *vcpu; -+ struct kvm_vm *vm; -+ struct ucall uc; -+ uint64_t cmd; -+ -+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); -+ -+ vm = vm_create_with_one_vcpu(&vcpu, guest_code); -+ -+ run_test(vcpu); -+ -+ if (kvm_cpu_has(X86_FEATURE_FRED)) { -+ struct kvm_sregs sregs; -+ -+ vcpu_sregs_get(vcpu, &sregs); -+ sregs.cr4 |= X86_CR4_FRED; -+ vcpu_sregs_set(vcpu, &sregs); -+ -+ run_test(vcpu); -+ } - - vcpu_run(vcpu); - TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0024-gfs2-Prevent-recursive-memory-reclaim.patch b/SPECS/kernel-rt/0024-gfs2-Prevent-recursive-memory-reclaim.patch deleted file mode 100644 index 573bbfe0e..000000000 --- a/SPECS/kernel-rt/0024-gfs2-Prevent-recursive-memory-reclaim.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 625f186d2f968ecdbe3d5482f69aba6a07d388eb Mon Sep 17 00:00:00 2001 -From: Andreas Gruenbacher -Date: Thu, 13 Nov 2025 12:05:37 +0000 -Subject: [PATCH 24/51] gfs2: Prevent recursive memory reclaim - -Function new_inode() returns a new inode with inode->i_mapping->gfp_mask -set to GFP_HIGHUSER_MOVABLE. This value includes the __GFP_FS flag, so -allocations in that address space can recurse into filesystem memory -reclaim. We don't want that to happen because it can consume a -significant amount of stack memory. - -Worse than that is that it can also deadlock: for example, in several -places, gfs2_unstuff_dinode() is called inside filesystem transactions. -This calls filemap_grab_folio(), which can allocate a new folio, which -can trigger memory reclaim. If memory reclaim recurses into the -filesystem and starts another transaction, a deadlock will ensue. - -To fix these kinds of problems, prevent memory reclaim from recursing -into filesystem code by making sure that the gfp_mask of inode address -spaces doesn't include __GFP_FS. - -The "meta" and resource group address spaces were already using GFP_NOFS -as their gfp_mask (which doesn't include __GFP_FS). The default value -of GFP_HIGHUSER_MOVABLE is less restrictive than GFP_NOFS, though. To -avoid being overly limiting, use the default value and only knock off -the __GFP_FS flag. I'm not sure if this will actually make a -difference, but it also shouldn't hurt. - -This patch is loosely based on commit ad22c7a043c2 ("xfs: prevent stack -overflows from page cache allocation"). - -Fixes xfstest generic/273. - -Fixes: dc0b9435238c ("gfs: Don't use GFP_NOFS in gfs2_unstuff_dinode") -Reviewed-by: Andrew Price -Signed-off-by: Andreas Gruenbacher ---- - fs/gfs2/glock.c | 5 ++++- - fs/gfs2/inode.c | 15 +++++++++++++++ - fs/gfs2/inode.h | 1 + - fs/gfs2/ops_fstype.c | 2 +- - 4 files changed, 21 insertions(+), 2 deletions(-) - -diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c -index a6535413a0b4..d00cedf4460c 100644 ---- a/fs/gfs2/glock.c -+++ b/fs/gfs2/glock.c -@@ -1205,10 +1205,13 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, - - mapping = gfs2_glock2aspace(gl); - if (mapping) { -+ gfp_t gfp_mask; -+ - mapping->a_ops = &gfs2_meta_aops; - mapping->host = sdp->sd_inode; - mapping->flags = 0; -- mapping_set_gfp_mask(mapping, GFP_NOFS); -+ gfp_mask = mapping_gfp_mask(sdp->sd_inode->i_mapping); -+ mapping_set_gfp_mask(mapping, gfp_mask); - mapping->i_private_data = NULL; - mapping->writeback_index = 0; - } -diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c -index 8760e7e20c9d..35c1ccc1747f 100644 ---- a/fs/gfs2/inode.c -+++ b/fs/gfs2/inode.c -@@ -89,6 +89,19 @@ static int iget_set(struct inode *inode, void *opaque) - return 0; - } - -+void gfs2_setup_inode(struct inode *inode) -+{ -+ gfp_t gfp_mask; -+ -+ /* -+ * Ensure all page cache allocations are done from GFP_NOFS context to -+ * prevent direct reclaim recursion back into the filesystem and blowing -+ * stacks or deadlocking. -+ */ -+ gfp_mask = mapping_gfp_mask(inode->i_mapping); -+ mapping_set_gfp_mask(inode->i_mapping, gfp_mask & ~__GFP_FS); -+} -+ - /** - * gfs2_inode_lookup - Lookup an inode - * @sb: The super block -@@ -132,6 +145,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, - struct gfs2_glock *io_gl; - int extra_flags = 0; - -+ gfs2_setup_inode(inode); - error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, - &ip->i_gl); - if (unlikely(error)) -@@ -752,6 +766,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, - error = -ENOMEM; - if (!inode) - goto fail_gunlock; -+ gfs2_setup_inode(inode); - ip = GFS2_I(inode); - - error = posix_acl_create(dir, &mode, &default_acl, &acl); -diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h -index e43f08eb26e7..2fcd96dd1361 100644 ---- a/fs/gfs2/inode.h -+++ b/fs/gfs2/inode.h -@@ -86,6 +86,7 @@ static inline int gfs2_check_internal_file_size(struct inode *inode, - return -EIO; - } - -+void gfs2_setup_inode(struct inode *inode); - struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, - u64 no_addr, u64 no_formal_ino, - unsigned int blktype); -diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c -index efe99b732551..468ff57386dc 100644 ---- a/fs/gfs2/ops_fstype.c -+++ b/fs/gfs2/ops_fstype.c -@@ -1183,7 +1183,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) - - mapping = gfs2_aspace(sdp); - mapping->a_ops = &gfs2_rgrp_aops; -- mapping_set_gfp_mask(mapping, GFP_NOFS); -+ gfs2_setup_inode(sdp->sd_inode); - - error = init_names(sdp, silent); - if (error) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0024-perf-tools-Support-to-show-SSP-register.perf b/SPECS/kernel-rt/0024-perf-tools-Support-to-show-SSP-register.perf deleted file mode 100644 index 4a5fc993a..000000000 --- a/SPECS/kernel-rt/0024-perf-tools-Support-to-show-SSP-register.perf +++ /dev/null @@ -1,78 +0,0 @@ -From da2cc6441f6077d5e279ea137ee3b53562090746 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 10:10:56 +0000 -Subject: [PATCH 024/100] perf tools: Support to show SSP register - -Add SSP register support. - -Reviewed-by: Ian Rogers -Signed-off-by: Dapeng Mi ---- - tools/arch/x86/include/uapi/asm/perf_regs.h | 7 ++++++- - tools/perf/arch/x86/util/perf_regs.c | 2 ++ - tools/perf/util/intel-pt.c | 2 +- - tools/perf/util/perf-regs-arch/perf_regs_x86.c | 2 ++ - 4 files changed, 11 insertions(+), 2 deletions(-) - -diff --git a/tools/arch/x86/include/uapi/asm/perf_regs.h b/tools/arch/x86/include/uapi/asm/perf_regs.h -index 7c9d2bb3833b..1c7ab5af5cc1 100644 ---- a/tools/arch/x86/include/uapi/asm/perf_regs.h -+++ b/tools/arch/x86/include/uapi/asm/perf_regs.h -@@ -27,9 +27,14 @@ enum perf_event_x86_regs { - PERF_REG_X86_R13, - PERF_REG_X86_R14, - PERF_REG_X86_R15, -+ /* arch-PEBS supports to capture shadow stack pointer (SSP) */ -+ PERF_REG_X86_SSP, - /* These are the limits for the GPRs. */ - PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, -- PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1, -+ /* PERF_REG_X86_64_MAX used generally, for PEBS, etc. */ -+ PERF_REG_X86_64_MAX = PERF_REG_X86_SSP + 1, -+ /* PERF_REG_INTEL_PT_MAX ignores the SSP register. */ -+ PERF_REG_INTEL_PT_MAX = PERF_REG_X86_R15 + 1, - - /* These all need two bits set because they are 128bit */ - PERF_REG_X86_XMM0 = 32, -diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c -index 12fd93f04802..9f492568f3b4 100644 ---- a/tools/perf/arch/x86/util/perf_regs.c -+++ b/tools/perf/arch/x86/util/perf_regs.c -@@ -36,6 +36,8 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG(R14, PERF_REG_X86_R14), - SMPL_REG(R15, PERF_REG_X86_R15), - #endif -+ SMPL_REG(SSP, PERF_REG_X86_SSP), -+ - SMPL_REG2(XMM0, PERF_REG_X86_XMM0), - SMPL_REG2(XMM1, PERF_REG_X86_XMM1), - SMPL_REG2(XMM2, PERF_REG_X86_XMM2), -diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c -index 9b1011fe4826..a6b53718be7d 100644 ---- a/tools/perf/util/intel-pt.c -+++ b/tools/perf/util/intel-pt.c -@@ -2181,7 +2181,7 @@ static u64 *intel_pt_add_gp_regs(struct regs_dump *intr_regs, u64 *pos, - u32 bit; - int i; - -- for (i = 0, bit = 1; i < PERF_REG_X86_64_MAX; i++, bit <<= 1) { -+ for (i = 0, bit = 1; i < PERF_REG_INTEL_PT_MAX; i++, bit <<= 1) { - /* Get the PEBS gp_regs array index */ - int n = pebs_gp_regs[i] - 1; - -diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -index 708954a9d35d..c0e95215b577 100644 ---- a/tools/perf/util/perf-regs-arch/perf_regs_x86.c -+++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -@@ -54,6 +54,8 @@ const char *__perf_reg_name_x86(int id) - return "R14"; - case PERF_REG_X86_R15: - return "R15"; -+ case PERF_REG_X86_SSP: -+ return "SSP"; - - #define XMM(x) \ - case PERF_REG_X86_XMM ## x: \ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi b/SPECS/kernel-rt/0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi deleted file mode 100644 index 607ec9dc1..000000000 --- a/SPECS/kernel-rt/0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi +++ /dev/null @@ -1,161 +0,0 @@ -From 73d356ce29f00c03cc2008285fbfd6dbc6b0c0bf Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Mon, 6 Nov 2023 23:06:15 -0800 -Subject: [PATCH 25/44] KVM: selftests: Add a new VM guest mode to run user - level code - -Add a new VM guest mode VM_MODE_PXXV48_4K_USER to set the user bit of -guest page table entries, thus allow user level code to run in guests. - -Suggested-by: Sean Christopherson -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - .../testing/selftests/kvm/include/kvm_util.h | 1 + - tools/testing/selftests/kvm/lib/kvm_util.c | 5 +++- - .../testing/selftests/kvm/lib/x86/processor.c | 24 ++++++++++++++----- - tools/testing/selftests/kvm/lib/x86/vmx.c | 4 ++-- - 4 files changed, 25 insertions(+), 9 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h -index 23a506d7eca3..e5ea563fff94 100644 ---- a/tools/testing/selftests/kvm/include/kvm_util.h -+++ b/tools/testing/selftests/kvm/include/kvm_util.h -@@ -182,6 +182,7 @@ enum vm_guest_mode { - VM_MODE_P36V48_64K, - VM_MODE_P47V47_16K, - VM_MODE_P36V47_16K, -+ VM_MODE_PXXV48_4K_USER, /* For 48bits VA but ANY bits PA with USER bit set */ - NUM_VM_MODES, - }; - -diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c -index c3f5142b0a54..fc2bdf43ee23 100644 ---- a/tools/testing/selftests/kvm/lib/kvm_util.c -+++ b/tools/testing/selftests/kvm/lib/kvm_util.c -@@ -239,6 +239,7 @@ const char *vm_guest_mode_string(uint32_t i) - [VM_MODE_P36V48_64K] = "PA-bits:36, VA-bits:48, 64K pages", - [VM_MODE_P47V47_16K] = "PA-bits:47, VA-bits:47, 16K pages", - [VM_MODE_P36V47_16K] = "PA-bits:36, VA-bits:47, 16K pages", -+ [VM_MODE_PXXV48_4K_USER] = "PA-bits:ANY, VA-bits:48, 4K user pages", - }; - _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, - "Missing new mode strings?"); -@@ -266,6 +267,7 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = { - [VM_MODE_P36V48_64K] = { 36, 48, 0x10000, 16 }, - [VM_MODE_P47V47_16K] = { 47, 47, 0x4000, 14 }, - [VM_MODE_P36V47_16K] = { 36, 47, 0x4000, 14 }, -+ [VM_MODE_PXXV48_4K_USER] = { 0, 0, 0x1000, 12 }, - }; - _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, - "Missing new mode params?"); -@@ -341,6 +343,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) - vm->pgtable_levels = 3; - break; - case VM_MODE_PXXV48_4K: -+ case VM_MODE_PXXV48_4K_USER: - #ifdef __x86_64__ - kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits); - kvm_init_vm_address_properties(vm); -@@ -357,7 +360,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) - vm->pgtable_levels = 4; - vm->va_bits = 48; - #else -- TEST_FAIL("VM_MODE_PXXV48_4K not supported on non-x86 platforms"); -+ TEST_FAIL("VM_MODE_PXXV48_4K(_USER) not supported on non-x86 platforms"); - #endif - break; - case VM_MODE_P47V64_4K: -diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c -index d4c19ac885a9..3e30732f4c3d 100644 ---- a/tools/testing/selftests/kvm/lib/x86/processor.c -+++ b/tools/testing/selftests/kvm/lib/x86/processor.c -@@ -124,8 +124,8 @@ bool kvm_is_tdp_enabled(void) - - void virt_arch_pgd_alloc(struct kvm_vm *vm) - { -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " -- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), -+ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); - - /* If needed, create page map l4 table. */ - if (!vm->pgd_created) { -@@ -161,6 +161,8 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, - - if (!(*pte & PTE_PRESENT_MASK)) { - *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK; -+ if (vm->mode == VM_MODE_PXXV48_4K_USER) -+ *pte |= PTE_USER_MASK; - if (current_level == target_level) - *pte |= PTE_LARGE_MASK | (paddr & PHYSICAL_PAGE_MASK); - else -@@ -187,7 +189,7 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) - uint64_t *pml4e, *pdpe, *pde; - uint64_t *pte; - -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), - "Unknown or unsupported guest mode, mode: 0x%x", vm->mode); - - TEST_ASSERT((vaddr % pg_size) == 0, -@@ -235,6 +237,9 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) - *pte |= vm->arch.c_bit; - else - *pte |= vm->arch.s_bit; -+ -+ if (vm->mode == VM_MODE_PXXV48_4K_USER) -+ *pte |= PTE_USER_MASK; - } - - void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) -@@ -284,8 +289,8 @@ uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, - TEST_ASSERT(*level >= PG_LEVEL_NONE && *level < PG_LEVEL_NUM, - "Invalid PG_LEVEL_* '%d'", *level); - -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " -- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), -+ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", -@@ -492,7 +497,14 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu) - { - struct kvm_sregs sregs; - -- TEST_ASSERT_EQ(vm->mode, VM_MODE_PXXV48_4K); -+ switch (vm->mode) { -+ case VM_MODE_PXXV48_4K: -+ case VM_MODE_PXXV48_4K_USER: -+ break; -+ -+ default: -+ TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); -+ } - - /* Set mode specific system register values. */ - vcpu_sregs_get(vcpu, &sregs); -diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c -index d4d1208dd023..4f7655b0830d 100644 ---- a/tools/testing/selftests/kvm/lib/x86/vmx.c -+++ b/tools/testing/selftests/kvm/lib/x86/vmx.c -@@ -401,8 +401,8 @@ void __nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - struct eptPageTableEntry *pt = vmx->eptp_hva, *pte; - uint16_t index; - -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " -- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), -+ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); - - TEST_ASSERT((nested_paddr >> 48) == 0, - "Nested physical address 0x%lx requires 5-level paging", --- -2.43.0 - diff --git a/SPECS/kernel-rt/0025-KVM-selftests-Add-fred-exception-tests.nmi b/SPECS/kernel-rt/0025-KVM-selftests-Add-fred-exception-tests.nmi new file mode 100644 index 000000000..923aec8d9 --- /dev/null +++ b/SPECS/kernel-rt/0025-KVM-selftests-Add-fred-exception-tests.nmi @@ -0,0 +1,391 @@ +From 81a70fda214064510d227cd886577e3f2e97079d Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 11 May 2023 08:17:30 +0000 +Subject: [PATCH 25/44] KVM: selftests: Add fred exception tests + +Add tests for FRED event data and VMX nested-exception. + +FRED is designed to save a complete event context in its stack frame, +e.g., FRED saves the faulting linear address of a #PF into a 64-bit +event data field defined in FRED stack frame. As such, FRED VMX adds +event data handling during VMX transitions. + +Besides, FRED introduces event stack levels to dispatch an event handler +onto a stack based on current stack level and stack levels defined in +IA32_FRED_STKLVLS MSR for each exception vector. VMX nested-exception +support ensures a correct event stack level is chosen when a VM entry +injects a nested exception, which is regarded as occurred in ring 0. + +To fully test the underlying FRED VMX code, this test should be run one +more round with EPT disabled to inject page faults as nested exceptions. + +Originally-by: Shan Kang +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + tools/testing/selftests/kvm/Makefile.kvm | 1 + + .../selftests/kvm/include/x86/processor.h | 32 ++ + tools/testing/selftests/kvm/x86/fred_test.c | 293 ++++++++++++++++++ + 3 files changed, 326 insertions(+) + create mode 100644 tools/testing/selftests/kvm/x86/fred_test.c + +diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm +index 148d427ff24be..5d84b8fcbaad4 100644 +--- a/tools/testing/selftests/kvm/Makefile.kvm ++++ b/tools/testing/selftests/kvm/Makefile.kvm +@@ -73,6 +73,7 @@ TEST_GEN_PROGS_x86 += x86/dirty_log_page_splitting_test + TEST_GEN_PROGS_x86 += x86/feature_msrs_test + TEST_GEN_PROGS_x86 += x86/exit_on_emulation_failure_test + TEST_GEN_PROGS_x86 += x86/fastops_test ++TEST_GEN_PROGS_x86 += x86/fred_test + TEST_GEN_PROGS_x86 += x86/fix_hypercall_test + TEST_GEN_PROGS_x86 += x86/hwcr_msr_test + TEST_GEN_PROGS_x86 += x86/hyperv_clock +diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h +index 1b07685533a1c..7f2b971a7a9f2 100644 +--- a/tools/testing/selftests/kvm/include/x86/processor.h ++++ b/tools/testing/selftests/kvm/include/x86/processor.h +@@ -1498,4 +1498,36 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, + + bool sys_clocksource_is_based_on_tsc(void); + ++/* ++ * FRED related data structures and functions ++ */ ++ ++#define FRED_SSX_NMI BIT_ULL(18) ++ ++struct fred_stack { ++ u64 r15; ++ u64 r14; ++ u64 r13; ++ u64 r12; ++ u64 bp; ++ u64 bx; ++ u64 r11; ++ u64 r10; ++ u64 r9; ++ u64 r8; ++ u64 ax; ++ u64 cx; ++ u64 dx; ++ u64 si; ++ u64 di; ++ u64 error_code; ++ u64 ip; ++ u64 csx; ++ u64 flags; ++ u64 sp; ++ u64 ssx; ++ u64 event_data; ++ u64 reserved; ++}; ++ + #endif /* SELFTEST_KVM_PROCESSOR_H */ +diff --git a/tools/testing/selftests/kvm/x86/fred_test.c b/tools/testing/selftests/kvm/x86/fred_test.c +new file mode 100644 +index 0000000000000..3d0703f6eba7c +--- /dev/null ++++ b/tools/testing/selftests/kvm/x86/fred_test.c +@@ -0,0 +1,293 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * FRED nested exception tests ++ * ++ * Copyright (C) 2023, Intel, Inc. ++ */ ++#define _GNU_SOURCE /* for program_invocation_short_name */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "apic.h" ++#include "kvm_util.h" ++#include "test_util.h" ++#include "guest_modes.h" ++#include "processor.h" ++ ++#define IRQ_VECTOR 0xAA ++ ++#define FRED_STKLVL(v, l) (_AT(unsigned long, l) << (2 * (v))) ++#define FRED_CONFIG_ENTRYPOINT(p) _AT(unsigned long, (p)) ++ ++/* This address is already mapped in guest page table. */ ++#define FRED_VALID_RSP 0x8000 ++ ++/* ++ * The following addresses are not yet mapped in both EPT and guest page ++ * tables at the beginning. As a result, it causes an EPT violation VM ++ * exit with an original guest #PF to access any of them for the first ++ * time. ++ * ++ * Use these addresses as guest FRED RSP0 to generate nested #PFs to test ++ * if event data are properly virtualized. ++ */ ++static unsigned long fred_invalid_rsp[4] = { ++ 0x0, ++ 0xf0000000, ++ 0xe0000000, ++ 0xd0000000, ++}; ++ ++extern char asm_user_nop[]; ++extern char asm_user_ud[]; ++extern char asm_done_fault[]; ++ ++extern void asm_test_fault(int test); ++ ++/* ++ * user level code for triggering faults. ++ */ ++asm(".pushsection .text\n" ++ ".align 4096\n" ++ ++ ".type asm_user_nop, @function\n" ++ "asm_user_nop:\n" ++ "1: .byte 0x90\n" ++ "jmp 1b\n" ++ ++ ".org asm_user_nop + 16, 0xcc\n" ++ ".type asm_user_ud, @function\n" ++ "asm_user_ud:\n" ++ /* Trigger a #UD */ ++ "ud2\n" ++ ++ ".align 4096, 0xcc\n" ++ ".popsection"); ++ ++/* Send current stack level and #PF address */ ++#define GUEST_SYNC_CSL_FA(__stage, __pf_address) \ ++ GUEST_SYNC_ARGS(__stage, __pf_address, 0, 0, 0) ++ ++void fred_entry_from_user(struct fred_stack *stack) ++{ ++ u32 current_stack_level = rdmsr(MSR_IA32_FRED_CONFIG) & 0x3; ++ ++ GUEST_SYNC_CSL_FA(current_stack_level, stack->event_data); ++ ++ /* Do NOT go back to user level, continue the next test instead */ ++ stack->ssx = 0x18; ++ stack->csx = 0x10; ++ stack->ip = (u64)&asm_done_fault; ++} ++ ++void fred_entry_from_kernel(struct fred_stack *stack) ++{ ++ /* ++ * Keep NMI blocked to delay the delivery of the next NMI until ++ * returning to user level. ++ * */ ++ stack->ssx &= ~FRED_SSX_NMI; ++} ++ ++#define PUSH_REGS \ ++ "push %rdi\n" \ ++ "push %rsi\n" \ ++ "push %rdx\n" \ ++ "push %rcx\n" \ ++ "push %rax\n" \ ++ "push %r8\n" \ ++ "push %r9\n" \ ++ "push %r10\n" \ ++ "push %r11\n" \ ++ "push %rbx\n" \ ++ "push %rbp\n" \ ++ "push %r12\n" \ ++ "push %r13\n" \ ++ "push %r14\n" \ ++ "push %r15\n" ++ ++#define POP_REGS \ ++ "pop %r15\n" \ ++ "pop %r14\n" \ ++ "pop %r13\n" \ ++ "pop %r12\n" \ ++ "pop %rbp\n" \ ++ "pop %rbx\n" \ ++ "pop %r11\n" \ ++ "pop %r10\n" \ ++ "pop %r9\n" \ ++ "pop %r8\n" \ ++ "pop %rax\n" \ ++ "pop %rcx\n" \ ++ "pop %rdx\n" \ ++ "pop %rsi\n" \ ++ "pop %rdi\n" ++ ++/* ++ * FRED entry points. ++ */ ++asm(".pushsection .text\n" ++ ".type asm_fred_entrypoint_user, @function\n" ++ ".align 4096\n" ++ "asm_fred_entrypoint_user:\n" ++ "endbr64\n" ++ PUSH_REGS ++ "movq %rsp, %rdi\n" ++ "call fred_entry_from_user\n" ++ POP_REGS ++ /* Do NOT go back to user level, continue the next test instead */ ++ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ ++ ++ ".org asm_fred_entrypoint_user + 256, 0xcc\n" ++ ".type asm_fred_entrypoint_kernel, @function\n" ++ "asm_fred_entrypoint_kernel:\n" ++ "endbr64\n" ++ PUSH_REGS ++ "movq %rsp, %rdi\n" ++ "call fred_entry_from_kernel\n" ++ POP_REGS ++ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ ++ ".align 4096, 0xcc\n" ++ ".popsection"); ++ ++extern char asm_fred_entrypoint_user[]; ++ ++/* ++ * Prepare a FRED stack frame for ERETU to return to user level code, ++ * nop or ud2. ++ * ++ * Because FRED RSP0 is deliberately not mapped in guest page table, ++ * the delivery of interrupt/NMI or #UD from ring 3 causes a nested ++ * #PF, which is then delivered on FRED RSPx (x is 1, 2 or 3, ++ * determinated by MSR FRED_STKLVL[PF_VECTOR]). ++ */ ++asm(".pushsection .text\n" ++ ".type asm_test_fault, @function\n" ++ ".align 4096\n" ++ "asm_test_fault:\n" ++ "endbr64\n" ++ "push %rbp\n" ++ "mov %rsp, %rbp\n" ++ "and $(~0x3f), %rsp\n" ++ "push $0\n" ++ "push $0\n" ++ "mov $0x2b, %rax\n" ++ /* Unblock NMI */ ++ "bts $18, %rax\n" ++ /* Set long mode bit */ ++ "bts $57, %rax\n" ++ "push %rax\n" ++ /* No stack required for the FRED user level test code */ ++ "push $0\n" ++ "pushf\n" ++ "pop %rax\n" ++ /* Allow external interrupts */ ++ "bts $9, %rax\n" ++ "push %rax\n" ++ "mov $0x33, %rax\n" ++ "push %rax\n" ++ "cmp $0, %edi\n" ++ "jne 1f\n" ++ "lea asm_user_nop(%rip), %rax\n" ++ "jmp 2f\n" ++ "1: lea asm_user_ud(%rip), %rax\n" ++ "2: push %rax\n" ++ "push $0\n" ++ /* ERETU to user level code to allow event delivery immediately */ ++ ".byte 0xf3,0x0f,0x01,0xca\n" ++ "asm_done_fault:\n" ++ "mov %rbp, %rsp\n" ++ "pop %rbp\n" ++ "ret\n" ++ ".align 4096, 0xcc\n" ++ ".popsection"); ++ ++/* ++ * To fully test the underlying FRED VMX code, this test should be run one ++ * more round with EPT disabled to inject page faults as nested exceptions. ++ */ ++static void guest_code(void) ++{ ++ wrmsr(MSR_IA32_FRED_CONFIG, ++ FRED_CONFIG_ENTRYPOINT(asm_fred_entrypoint_user)); ++ ++ wrmsr(MSR_IA32_FRED_RSP1, FRED_VALID_RSP); ++ wrmsr(MSR_IA32_FRED_RSP2, FRED_VALID_RSP); ++ wrmsr(MSR_IA32_FRED_RSP3, FRED_VALID_RSP); ++ ++ /* Enable FRED */ ++ set_cr4(get_cr4() | X86_CR4_FRED); ++ ++ x2apic_enable(); ++ ++ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 1)); ++ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[1]); ++ /* 1: ud2 to generate #UD */ ++ asm_test_fault(1); ++ ++ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 2)); ++ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[2]); ++ asm volatile("cli"); ++ /* Create a pending interrupt on current vCPU */ ++ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | ++ APIC_DM_FIXED | IRQ_VECTOR); ++ /* Return to ring 3 */ ++ asm_test_fault(0); ++ x2apic_write_reg(APIC_EOI, 0); ++ ++ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 3)); ++ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[3]); ++ /* ++ * The first NMI is just to have NMI blocked in ring 0, because ++ * fred_entry_from_kernel() deliberately clears the NMI bit in ++ * FRED stack frame. ++ */ ++ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | ++ APIC_DM_NMI | NMI_VECTOR); ++ /* The second NMI will be delivered after returning to ring 3 */ ++ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | ++ APIC_DM_NMI | NMI_VECTOR); ++ /* Return to ring 3 */ ++ asm_test_fault(0); ++ ++ GUEST_DONE(); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct kvm_vcpu *vcpu; ++ struct kvm_vm *vm; ++ struct ucall uc; ++ uint64_t expected_current_stack_level = 1; ++ ++ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_FRED)); ++ ++ vm = __vm_create_with_vcpus(VM_SHAPE(VM_MODE_PXXV48_4K_USER), 1, 0, ++ guest_code, &vcpu); ++ ++ while (true) { ++ uint64_t r; ++ ++ vcpu_run(vcpu); ++ ++ r = get_ucall(vcpu, &uc); ++ ++ if (r == UCALL_DONE) ++ break; ++ ++ if (r == UCALL_SYNC) { ++ TEST_ASSERT((uc.args[1] == expected_current_stack_level) && ++ (uc.args[2] == fred_invalid_rsp[expected_current_stack_level] - 8), ++ "Incorrect stack level %lx and #PF address %lx\n", ++ uc.args[1], uc.args[2]); ++ expected_current_stack_level++; ++ } ++ } ++ ++ kvm_vm_free(vm); ++ return 0; ++} +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf b/SPECS/kernel-rt/0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf deleted file mode 100644 index 030b85e23..000000000 --- a/SPECS/kernel-rt/0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf +++ /dev/null @@ -1,390 +0,0 @@ -From 843251f6ff33766dde028d65f727675e035a8ee1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 17 Feb 2025 15:47:06 +0000 -Subject: [PATCH 025/100] perf tools: Enhance arch__intr/user_reg_mask() - helpers - -Arch-PEBS supports to capture more higher-width vector registers, like -YMM/ZMM registers, while the return value "uint64_t" of these 2 helpers -is not enough to represent these new added registors. Thus enhance these -two helpers by passing a "unsigned long" pointer, so these two helpers -can return more bits via this pointer. - -Currently only sample_intr_regs supports these new added vector -registers, but change arch__user_reg_mask() for the sake of consistency -as well. - -Signed-off-by: Dapeng Mi ---- - tools/perf/arch/arm/util/perf_regs.c | 8 ++++---- - tools/perf/arch/arm64/util/perf_regs.c | 11 ++++++----- - tools/perf/arch/csky/util/perf_regs.c | 8 ++++---- - tools/perf/arch/loongarch/util/perf_regs.c | 8 ++++---- - tools/perf/arch/mips/util/perf_regs.c | 8 ++++---- - tools/perf/arch/powerpc/util/perf_regs.c | 17 +++++++++-------- - tools/perf/arch/riscv/util/perf_regs.c | 8 ++++---- - tools/perf/arch/s390/util/perf_regs.c | 8 ++++---- - tools/perf/arch/x86/util/perf_regs.c | 13 +++++++------ - tools/perf/util/evsel.c | 6 ++++-- - tools/perf/util/parse-regs-options.c | 6 +++--- - tools/perf/util/perf_regs.c | 8 ++++---- - tools/perf/util/perf_regs.h | 4 ++-- - 13 files changed, 59 insertions(+), 54 deletions(-) - -diff --git a/tools/perf/arch/arm/util/perf_regs.c b/tools/perf/arch/arm/util/perf_regs.c -index f94a0210c7b7..14f18d518c96 100644 ---- a/tools/perf/arch/arm/util/perf_regs.c -+++ b/tools/perf/arch/arm/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/arm64/util/perf_regs.c b/tools/perf/arch/arm64/util/perf_regs.c -index 09308665e28a..9bcf4755290c 100644 ---- a/tools/perf/arch/arm64/util/perf_regs.c -+++ b/tools/perf/arch/arm64/util/perf_regs.c -@@ -140,12 +140,12 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op) - return SDT_ARG_VALID; - } - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, -@@ -170,10 +170,11 @@ uint64_t arch__user_reg_mask(void) - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); - if (fd != -1) { - close(fd); -- return attr.sample_regs_user; -+ *(uint64_t *)mask = attr.sample_regs_user; -+ return; - } - } -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/csky/util/perf_regs.c b/tools/perf/arch/csky/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/csky/util/perf_regs.c -+++ b/tools/perf/arch/csky/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/loongarch/util/perf_regs.c b/tools/perf/arch/loongarch/util/perf_regs.c -index f94a0210c7b7..14f18d518c96 100644 ---- a/tools/perf/arch/loongarch/util/perf_regs.c -+++ b/tools/perf/arch/loongarch/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/mips/util/perf_regs.c b/tools/perf/arch/mips/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/mips/util/perf_regs.c -+++ b/tools/perf/arch/mips/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c -index bd36cfd420a2..e5d042305030 100644 ---- a/tools/perf/arch/powerpc/util/perf_regs.c -+++ b/tools/perf/arch/powerpc/util/perf_regs.c -@@ -187,7 +187,7 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op) - return SDT_ARG_VALID; - } - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, -@@ -199,7 +199,7 @@ uint64_t arch__intr_reg_mask(void) - }; - int fd; - u32 version; -- u64 extended_mask = 0, mask = PERF_REGS_MASK; -+ u64 extended_mask = 0; - - /* - * Get the PVR value to set the extended -@@ -210,8 +210,10 @@ uint64_t arch__intr_reg_mask(void) - extended_mask = PERF_REG_PMU_MASK_300; - else if ((version == PVR_POWER10) || (version == PVR_POWER11)) - extended_mask = PERF_REG_PMU_MASK_31; -- else -- return mask; -+ else { -+ *(u64 *)mask = PERF_REGS_MASK; -+ return; -+ } - - attr.sample_regs_intr = extended_mask; - attr.sample_period = 1; -@@ -224,14 +226,13 @@ uint64_t arch__intr_reg_mask(void) - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); - if (fd != -1) { - close(fd); -- mask |= extended_mask; -+ *(u64 *)mask = PERF_REGS_MASK | extended_mask; - } -- return mask; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/riscv/util/perf_regs.c b/tools/perf/arch/riscv/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/riscv/util/perf_regs.c -+++ b/tools/perf/arch/riscv/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/s390/util/perf_regs.c b/tools/perf/arch/s390/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/s390/util/perf_regs.c -+++ b/tools/perf/arch/s390/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c -index 9f492568f3b4..5b163f0a651a 100644 ---- a/tools/perf/arch/x86/util/perf_regs.c -+++ b/tools/perf/arch/x86/util/perf_regs.c -@@ -283,7 +283,7 @@ const struct sample_reg *arch__sample_reg_masks(void) - return sample_reg_masks; - } - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, -@@ -295,6 +295,9 @@ uint64_t arch__intr_reg_mask(void) - .exclude_kernel = 1, - }; - int fd; -+ -+ *(u64 *)mask = PERF_REGS_MASK; -+ - /* - * In an unnamed union, init it here to build on older gcc versions - */ -@@ -320,13 +323,11 @@ uint64_t arch__intr_reg_mask(void) - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); - if (fd != -1) { - close(fd); -- return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK); -+ *(u64 *)mask = PERF_REG_EXTENDED_MASK | PERF_REGS_MASK; - } -- -- return PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } -diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c -index d264c143b592..f9e79b86b794 100644 ---- a/tools/perf/util/evsel.c -+++ b/tools/perf/util/evsel.c -@@ -1041,17 +1041,19 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o - if (param->record_mode == CALLCHAIN_DWARF) { - if (!function) { - const char *arch = perf_env__arch(evsel__env(evsel)); -+ uint64_t mask = 0; - -+ arch__user_reg_mask((unsigned long *)&mask); - evsel__set_sample_bit(evsel, REGS_USER); - evsel__set_sample_bit(evsel, STACK_USER); - if (opts->sample_user_regs && -- DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) { -+ DWARF_MINIMAL_REGS(arch) != mask) { - attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch); - pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, " - "specifying a subset with --user-regs may render DWARF unwinding unreliable, " - "so the minimal registers set (IP, SP) is explicitly forced.\n"); - } else { -- attr->sample_regs_user |= arch__user_reg_mask(); -+ attr->sample_regs_user |= mask; - } - attr->sample_stack_user = param->dump_size; - attr->exclude_callchain_user = 1; -diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c -index cda1c620968e..3dcd8dc4f81b 100644 ---- a/tools/perf/util/parse-regs-options.c -+++ b/tools/perf/util/parse-regs-options.c -@@ -16,7 +16,7 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - const struct sample_reg *r = NULL; - char *s, *os = NULL, *p; - int ret = -1; -- uint64_t mask; -+ uint64_t mask = 0; - - if (unset) - return 0; -@@ -28,9 +28,9 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - return -1; - - if (intr) -- mask = arch__intr_reg_mask(); -+ arch__intr_reg_mask((unsigned long *)&mask); - else -- mask = arch__user_reg_mask(); -+ arch__user_reg_mask((unsigned long *)&mask); - - /* str may be NULL in case no arg is passed to -I */ - if (str) { -diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c -index 44b90bbf2d07..7a96290fd1e6 100644 ---- a/tools/perf/util/perf_regs.c -+++ b/tools/perf/util/perf_regs.c -@@ -11,14 +11,14 @@ int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused, - return SDT_ARG_SKIP; - } - --uint64_t __weak arch__intr_reg_mask(void) -+void __weak arch__intr_reg_mask(unsigned long *mask) - { -- return 0; -+ *(uint64_t *)mask = 0; - } - --uint64_t __weak arch__user_reg_mask(void) -+void __weak arch__user_reg_mask(unsigned long *mask) - { -- return 0; -+ *(uint64_t *)mask = 0; - } - - static const struct sample_reg sample_reg_masks[] = { -diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h -index f2d0736d65cc..316d280e5cd7 100644 ---- a/tools/perf/util/perf_regs.h -+++ b/tools/perf/util/perf_regs.h -@@ -24,8 +24,8 @@ enum { - }; - - int arch_sdt_arg_parse_op(char *old_op, char **new_op); --uint64_t arch__intr_reg_mask(void); --uint64_t arch__user_reg_mask(void); -+void arch__intr_reg_mask(unsigned long *mask); -+void arch__user_reg_mask(unsigned long *mask); - const struct sample_reg *arch__sample_reg_masks(void); - - const char *perf_reg_name(int id, const char *arch); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0026-KVM-selftests-Add-fred-exception-tests.nmi b/SPECS/kernel-rt/0026-KVM-selftests-Add-fred-exception-tests.nmi deleted file mode 100644 index 18eddb17b..000000000 --- a/SPECS/kernel-rt/0026-KVM-selftests-Add-fred-exception-tests.nmi +++ /dev/null @@ -1,391 +0,0 @@ -From bae362a9e3d087209ade9bae2dc874ae58f3cee7 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 11 May 2023 08:17:30 +0000 -Subject: [PATCH 26/44] KVM: selftests: Add fred exception tests - -Add tests for FRED event data and VMX nested-exception. - -FRED is designed to save a complete event context in its stack frame, -e.g., FRED saves the faulting linear address of a #PF into a 64-bit -event data field defined in FRED stack frame. As such, FRED VMX adds -event data handling during VMX transitions. - -Besides, FRED introduces event stack levels to dispatch an event handler -onto a stack based on current stack level and stack levels defined in -IA32_FRED_STKLVLS MSR for each exception vector. VMX nested-exception -support ensures a correct event stack level is chosen when a VM entry -injects a nested exception, which is regarded as occurred in ring 0. - -To fully test the underlying FRED VMX code, this test should be run one -more round with EPT disabled to inject page faults as nested exceptions. - -Originally-by: Shan Kang -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - tools/testing/selftests/kvm/Makefile.kvm | 1 + - .../selftests/kvm/include/x86/processor.h | 32 ++ - tools/testing/selftests/kvm/x86/fred_test.c | 293 ++++++++++++++++++ - 3 files changed, 326 insertions(+) - create mode 100644 tools/testing/selftests/kvm/x86/fred_test.c - -diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm -index f6fe7a07a0a2..c6dacd69d11c 100644 ---- a/tools/testing/selftests/kvm/Makefile.kvm -+++ b/tools/testing/selftests/kvm/Makefile.kvm -@@ -73,6 +73,7 @@ TEST_GEN_PROGS_x86 += x86/dirty_log_page_splitting_test - TEST_GEN_PROGS_x86 += x86/feature_msrs_test - TEST_GEN_PROGS_x86 += x86/exit_on_emulation_failure_test - TEST_GEN_PROGS_x86 += x86/fastops_test -+TEST_GEN_PROGS_x86 += x86/fred_test - TEST_GEN_PROGS_x86 += x86/fix_hypercall_test - TEST_GEN_PROGS_x86 += x86/hwcr_msr_test - TEST_GEN_PROGS_x86 += x86/hyperv_clock -diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h -index bcda4856180f..f411b7422aa5 100644 ---- a/tools/testing/selftests/kvm/include/x86/processor.h -+++ b/tools/testing/selftests/kvm/include/x86/processor.h -@@ -1465,4 +1465,36 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, - - bool sys_clocksource_is_based_on_tsc(void); - -+/* -+ * FRED related data structures and functions -+ */ -+ -+#define FRED_SSX_NMI BIT_ULL(18) -+ -+struct fred_stack { -+ u64 r15; -+ u64 r14; -+ u64 r13; -+ u64 r12; -+ u64 bp; -+ u64 bx; -+ u64 r11; -+ u64 r10; -+ u64 r9; -+ u64 r8; -+ u64 ax; -+ u64 cx; -+ u64 dx; -+ u64 si; -+ u64 di; -+ u64 error_code; -+ u64 ip; -+ u64 csx; -+ u64 flags; -+ u64 sp; -+ u64 ssx; -+ u64 event_data; -+ u64 reserved; -+}; -+ - #endif /* SELFTEST_KVM_PROCESSOR_H */ -diff --git a/tools/testing/selftests/kvm/x86/fred_test.c b/tools/testing/selftests/kvm/x86/fred_test.c -new file mode 100644 -index 000000000000..3d0703f6eba7 ---- /dev/null -+++ b/tools/testing/selftests/kvm/x86/fred_test.c -@@ -0,0 +1,293 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * FRED nested exception tests -+ * -+ * Copyright (C) 2023, Intel, Inc. -+ */ -+#define _GNU_SOURCE /* for program_invocation_short_name */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "apic.h" -+#include "kvm_util.h" -+#include "test_util.h" -+#include "guest_modes.h" -+#include "processor.h" -+ -+#define IRQ_VECTOR 0xAA -+ -+#define FRED_STKLVL(v, l) (_AT(unsigned long, l) << (2 * (v))) -+#define FRED_CONFIG_ENTRYPOINT(p) _AT(unsigned long, (p)) -+ -+/* This address is already mapped in guest page table. */ -+#define FRED_VALID_RSP 0x8000 -+ -+/* -+ * The following addresses are not yet mapped in both EPT and guest page -+ * tables at the beginning. As a result, it causes an EPT violation VM -+ * exit with an original guest #PF to access any of them for the first -+ * time. -+ * -+ * Use these addresses as guest FRED RSP0 to generate nested #PFs to test -+ * if event data are properly virtualized. -+ */ -+static unsigned long fred_invalid_rsp[4] = { -+ 0x0, -+ 0xf0000000, -+ 0xe0000000, -+ 0xd0000000, -+}; -+ -+extern char asm_user_nop[]; -+extern char asm_user_ud[]; -+extern char asm_done_fault[]; -+ -+extern void asm_test_fault(int test); -+ -+/* -+ * user level code for triggering faults. -+ */ -+asm(".pushsection .text\n" -+ ".align 4096\n" -+ -+ ".type asm_user_nop, @function\n" -+ "asm_user_nop:\n" -+ "1: .byte 0x90\n" -+ "jmp 1b\n" -+ -+ ".org asm_user_nop + 16, 0xcc\n" -+ ".type asm_user_ud, @function\n" -+ "asm_user_ud:\n" -+ /* Trigger a #UD */ -+ "ud2\n" -+ -+ ".align 4096, 0xcc\n" -+ ".popsection"); -+ -+/* Send current stack level and #PF address */ -+#define GUEST_SYNC_CSL_FA(__stage, __pf_address) \ -+ GUEST_SYNC_ARGS(__stage, __pf_address, 0, 0, 0) -+ -+void fred_entry_from_user(struct fred_stack *stack) -+{ -+ u32 current_stack_level = rdmsr(MSR_IA32_FRED_CONFIG) & 0x3; -+ -+ GUEST_SYNC_CSL_FA(current_stack_level, stack->event_data); -+ -+ /* Do NOT go back to user level, continue the next test instead */ -+ stack->ssx = 0x18; -+ stack->csx = 0x10; -+ stack->ip = (u64)&asm_done_fault; -+} -+ -+void fred_entry_from_kernel(struct fred_stack *stack) -+{ -+ /* -+ * Keep NMI blocked to delay the delivery of the next NMI until -+ * returning to user level. -+ * */ -+ stack->ssx &= ~FRED_SSX_NMI; -+} -+ -+#define PUSH_REGS \ -+ "push %rdi\n" \ -+ "push %rsi\n" \ -+ "push %rdx\n" \ -+ "push %rcx\n" \ -+ "push %rax\n" \ -+ "push %r8\n" \ -+ "push %r9\n" \ -+ "push %r10\n" \ -+ "push %r11\n" \ -+ "push %rbx\n" \ -+ "push %rbp\n" \ -+ "push %r12\n" \ -+ "push %r13\n" \ -+ "push %r14\n" \ -+ "push %r15\n" -+ -+#define POP_REGS \ -+ "pop %r15\n" \ -+ "pop %r14\n" \ -+ "pop %r13\n" \ -+ "pop %r12\n" \ -+ "pop %rbp\n" \ -+ "pop %rbx\n" \ -+ "pop %r11\n" \ -+ "pop %r10\n" \ -+ "pop %r9\n" \ -+ "pop %r8\n" \ -+ "pop %rax\n" \ -+ "pop %rcx\n" \ -+ "pop %rdx\n" \ -+ "pop %rsi\n" \ -+ "pop %rdi\n" -+ -+/* -+ * FRED entry points. -+ */ -+asm(".pushsection .text\n" -+ ".type asm_fred_entrypoint_user, @function\n" -+ ".align 4096\n" -+ "asm_fred_entrypoint_user:\n" -+ "endbr64\n" -+ PUSH_REGS -+ "movq %rsp, %rdi\n" -+ "call fred_entry_from_user\n" -+ POP_REGS -+ /* Do NOT go back to user level, continue the next test instead */ -+ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ -+ -+ ".org asm_fred_entrypoint_user + 256, 0xcc\n" -+ ".type asm_fred_entrypoint_kernel, @function\n" -+ "asm_fred_entrypoint_kernel:\n" -+ "endbr64\n" -+ PUSH_REGS -+ "movq %rsp, %rdi\n" -+ "call fred_entry_from_kernel\n" -+ POP_REGS -+ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ -+ ".align 4096, 0xcc\n" -+ ".popsection"); -+ -+extern char asm_fred_entrypoint_user[]; -+ -+/* -+ * Prepare a FRED stack frame for ERETU to return to user level code, -+ * nop or ud2. -+ * -+ * Because FRED RSP0 is deliberately not mapped in guest page table, -+ * the delivery of interrupt/NMI or #UD from ring 3 causes a nested -+ * #PF, which is then delivered on FRED RSPx (x is 1, 2 or 3, -+ * determinated by MSR FRED_STKLVL[PF_VECTOR]). -+ */ -+asm(".pushsection .text\n" -+ ".type asm_test_fault, @function\n" -+ ".align 4096\n" -+ "asm_test_fault:\n" -+ "endbr64\n" -+ "push %rbp\n" -+ "mov %rsp, %rbp\n" -+ "and $(~0x3f), %rsp\n" -+ "push $0\n" -+ "push $0\n" -+ "mov $0x2b, %rax\n" -+ /* Unblock NMI */ -+ "bts $18, %rax\n" -+ /* Set long mode bit */ -+ "bts $57, %rax\n" -+ "push %rax\n" -+ /* No stack required for the FRED user level test code */ -+ "push $0\n" -+ "pushf\n" -+ "pop %rax\n" -+ /* Allow external interrupts */ -+ "bts $9, %rax\n" -+ "push %rax\n" -+ "mov $0x33, %rax\n" -+ "push %rax\n" -+ "cmp $0, %edi\n" -+ "jne 1f\n" -+ "lea asm_user_nop(%rip), %rax\n" -+ "jmp 2f\n" -+ "1: lea asm_user_ud(%rip), %rax\n" -+ "2: push %rax\n" -+ "push $0\n" -+ /* ERETU to user level code to allow event delivery immediately */ -+ ".byte 0xf3,0x0f,0x01,0xca\n" -+ "asm_done_fault:\n" -+ "mov %rbp, %rsp\n" -+ "pop %rbp\n" -+ "ret\n" -+ ".align 4096, 0xcc\n" -+ ".popsection"); -+ -+/* -+ * To fully test the underlying FRED VMX code, this test should be run one -+ * more round with EPT disabled to inject page faults as nested exceptions. -+ */ -+static void guest_code(void) -+{ -+ wrmsr(MSR_IA32_FRED_CONFIG, -+ FRED_CONFIG_ENTRYPOINT(asm_fred_entrypoint_user)); -+ -+ wrmsr(MSR_IA32_FRED_RSP1, FRED_VALID_RSP); -+ wrmsr(MSR_IA32_FRED_RSP2, FRED_VALID_RSP); -+ wrmsr(MSR_IA32_FRED_RSP3, FRED_VALID_RSP); -+ -+ /* Enable FRED */ -+ set_cr4(get_cr4() | X86_CR4_FRED); -+ -+ x2apic_enable(); -+ -+ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 1)); -+ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[1]); -+ /* 1: ud2 to generate #UD */ -+ asm_test_fault(1); -+ -+ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 2)); -+ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[2]); -+ asm volatile("cli"); -+ /* Create a pending interrupt on current vCPU */ -+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | -+ APIC_DM_FIXED | IRQ_VECTOR); -+ /* Return to ring 3 */ -+ asm_test_fault(0); -+ x2apic_write_reg(APIC_EOI, 0); -+ -+ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 3)); -+ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[3]); -+ /* -+ * The first NMI is just to have NMI blocked in ring 0, because -+ * fred_entry_from_kernel() deliberately clears the NMI bit in -+ * FRED stack frame. -+ */ -+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | -+ APIC_DM_NMI | NMI_VECTOR); -+ /* The second NMI will be delivered after returning to ring 3 */ -+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | -+ APIC_DM_NMI | NMI_VECTOR); -+ /* Return to ring 3 */ -+ asm_test_fault(0); -+ -+ GUEST_DONE(); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ struct kvm_vcpu *vcpu; -+ struct kvm_vm *vm; -+ struct ucall uc; -+ uint64_t expected_current_stack_level = 1; -+ -+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_FRED)); -+ -+ vm = __vm_create_with_vcpus(VM_SHAPE(VM_MODE_PXXV48_4K_USER), 1, 0, -+ guest_code, &vcpu); -+ -+ while (true) { -+ uint64_t r; -+ -+ vcpu_run(vcpu); -+ -+ r = get_ucall(vcpu, &uc); -+ -+ if (r == UCALL_DONE) -+ break; -+ -+ if (r == UCALL_SYNC) { -+ TEST_ASSERT((uc.args[1] == expected_current_stack_level) && -+ (uc.args[2] == fred_invalid_rsp[expected_current_stack_level] - 8), -+ "Incorrect stack level %lx and #PF address %lx\n", -+ uc.args[1], uc.args[2]); -+ expected_current_stack_level++; -+ } -+ } -+ -+ kvm_vm_free(vm); -+ return 0; -+} --- -2.43.0 - diff --git a/SPECS/kernel-rt/0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi b/SPECS/kernel-rt/0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi new file mode 100644 index 000000000..74f1f4464 --- /dev/null +++ b/SPECS/kernel-rt/0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi @@ -0,0 +1,45 @@ +From 8a16c90be344f69517684d96852307171e0555b7 Mon Sep 17 00:00:00 2001 +From: Chenyi Qiang +Date: Fri, 27 Dec 2024 00:47:38 -0800 +Subject: [PATCH 26/44] KVM: selftests: Add the 2nd VM exit controls MSR to the + hidden VMX MSR list + +Signed-off-by: Chenyi Qiang +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + tools/arch/x86/include/asm/msr-index.h | 1 + + tools/testing/selftests/kvm/x86/feature_msrs_test.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h +index 9e1720d73244f..baf5e16484185 100644 +--- a/tools/arch/x86/include/asm/msr-index.h ++++ b/tools/arch/x86/include/asm/msr-index.h +@@ -1225,6 +1225,7 @@ + #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 + #define MSR_IA32_VMX_VMFUNC 0x00000491 + #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 ++#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 + + /* Resctrl MSRs: */ + /* - Intel: */ +diff --git a/tools/testing/selftests/kvm/x86/feature_msrs_test.c b/tools/testing/selftests/kvm/x86/feature_msrs_test.c +index a72f13ae2edbb..815dcb7ad2145 100644 +--- a/tools/testing/selftests/kvm/x86/feature_msrs_test.c ++++ b/tools/testing/selftests/kvm/x86/feature_msrs_test.c +@@ -27,6 +27,7 @@ static bool is_hidden_vmx_msr(uint32_t msr) + case MSR_IA32_VMX_PINBASED_CTLS: + case MSR_IA32_VMX_PROCBASED_CTLS: + case MSR_IA32_VMX_EXIT_CTLS: ++ case MSR_IA32_VMX_EXIT_CTLS2: + case MSR_IA32_VMX_ENTRY_CTLS: + return true; + default: +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf b/SPECS/kernel-rt/0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf deleted file mode 100644 index e97225454..000000000 --- a/SPECS/kernel-rt/0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf +++ /dev/null @@ -1,466 +0,0 @@ -From d324a3c4549ba5b1ebf92549ee2fa806a1e70038 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 11:01:25 +0000 -Subject: [PATCH 026/100] perf tools: Enhance sample_regs_user/intr to capture - more registers - -Intel architectural PEBS supports to capture more vector registers like -OPMASK/YMM/ZMM registers besides already supported XMM registers. - -arch-PEBS vector registers (VCER) capturing on perf core/pmu driver -(Intel) has been supported by previous patches. This patch adds perf -tool's part support. In detail, add support for the new -sample_regs_intr/user_ext register selector in perf_event_attr. These 32 -bytes bitmap is used to select the new register group OPMASK, YMMH, ZMMH -and ZMM in VECR. Update perf regs to introduce the new registers. - -This single patch only introduces the generic support, x86/intel specific -support would be added in next patch. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - tools/include/uapi/linux/perf_event.h | 14 +++++++++++++ - tools/perf/builtin-script.c | 23 +++++++++++++++----- - tools/perf/util/evsel.c | 30 ++++++++++++++++++++------- - tools/perf/util/parse-regs-options.c | 23 ++++++++++++-------- - tools/perf/util/perf_regs.h | 16 +++++++++++++- - tools/perf/util/record.h | 4 ++-- - tools/perf/util/sample.h | 6 +++++- - tools/perf/util/session.c | 29 +++++++++++++++----------- - tools/perf/util/synthetic-events.c | 12 +++++++---- - 9 files changed, 116 insertions(+), 41 deletions(-) - -diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h -index 78a362b80027..36f17a0fb39e 100644 ---- a/tools/include/uapi/linux/perf_event.h -+++ b/tools/include/uapi/linux/perf_event.h -@@ -382,6 +382,13 @@ enum perf_event_read_format { - #define PERF_ATTR_SIZE_VER6 120 /* Add: aux_sample_size */ - #define PERF_ATTR_SIZE_VER7 128 /* Add: sig_data */ - #define PERF_ATTR_SIZE_VER8 136 /* Add: config3 */ -+#define PERF_ATTR_SIZE_VER9 168 /* add: sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE] */ -+ -+#define PERF_EXT_REGS_ARRAY_SIZE 7 -+#define PERF_NUM_EXT_REGS (PERF_EXT_REGS_ARRAY_SIZE * 64) -+ -+#define PERF_SAMPLE_ARRAY_SIZE (PERF_EXT_REGS_ARRAY_SIZE + 1) -+#define PERF_SAMPLE_REGS_NUM ((PERF_SAMPLE_ARRAY_SIZE) * 64) - - /* - * 'struct perf_event_attr' contains various attributes that define -@@ -543,6 +550,13 @@ struct perf_event_attr { - __u64 sig_data; - - __u64 config3; /* extension of config2 */ -+ -+ /* -+ * Extension sets of regs to dump for each sample. -+ * See asm/perf_regs.h for details. -+ */ -+ __u64 sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE]; -+ __u64 sample_regs_user_ext[PERF_EXT_REGS_ARRAY_SIZE]; - }; - - /* -diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c -index d9fbdcf72f25..130b03fb78a8 100644 ---- a/tools/perf/builtin-script.c -+++ b/tools/perf/builtin-script.c -@@ -724,21 +724,32 @@ static int perf_session__check_output_opt(struct perf_session *session) - } - - static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch, -- FILE *fp) -+ unsigned long *mask_ext, FILE *fp) - { -+ unsigned int mask_size = sizeof(mask) * 8; - unsigned i = 0, r; - int printed = 0; -+ u64 val; - - if (!regs || !regs->regs) - return 0; - - printed += fprintf(fp, " ABI:%" PRIu64 " ", regs->abi); - -- for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) { -- u64 val = regs->regs[i++]; -+ for_each_set_bit(r, (unsigned long *)&mask, mask_size) { -+ val = regs->regs[i++]; - printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val); - } - -+ if (!mask_ext) -+ return printed; -+ -+ for_each_set_bit(r, mask_ext, PERF_NUM_EXT_REGS) { -+ val = regs->regs[i++]; -+ printed += fprintf(fp, "%5s:0x%"PRIx64" ", -+ perf_reg_name(r + mask_size, arch), val); -+ } -+ - return printed; - } - -@@ -799,7 +810,8 @@ static int perf_sample__fprintf_iregs(struct perf_sample *sample, - return 0; - - return perf_sample__fprintf_regs(perf_sample__intr_regs(sample), -- attr->sample_regs_intr, arch, fp); -+ attr->sample_regs_intr, arch, -+ (unsigned long *)attr->sample_regs_intr_ext, fp); - } - - static int perf_sample__fprintf_uregs(struct perf_sample *sample, -@@ -809,7 +821,8 @@ static int perf_sample__fprintf_uregs(struct perf_sample *sample, - return 0; - - return perf_sample__fprintf_regs(perf_sample__user_regs(sample), -- attr->sample_regs_user, arch, fp); -+ attr->sample_regs_user, arch, -+ (unsigned long *)attr->sample_regs_user_ext, fp); - } - - static int perf_sample__fprintf_start(struct perf_script *script, -diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c -index f9e79b86b794..c89eeff51d28 100644 ---- a/tools/perf/util/evsel.c -+++ b/tools/perf/util/evsel.c -@@ -1046,7 +1046,7 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o - arch__user_reg_mask((unsigned long *)&mask); - evsel__set_sample_bit(evsel, REGS_USER); - evsel__set_sample_bit(evsel, STACK_USER); -- if (opts->sample_user_regs && -+ if (bitmap_weight(opts->sample_user_regs, PERF_SAMPLE_REGS_NUM) && - DWARF_MINIMAL_REGS(arch) != mask) { - attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch); - pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, " -@@ -1383,15 +1383,19 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, - if (callchain && callchain->enabled && !evsel->no_aux_samples) - evsel__config_callchain(evsel, opts, callchain); - -- if (opts->sample_intr_regs && !evsel->no_aux_samples && -- !evsel__is_dummy_event(evsel)) { -- attr->sample_regs_intr = opts->sample_intr_regs; -+ if (bitmap_weight(opts->sample_intr_regs, PERF_SAMPLE_REGS_NUM) && -+ !evsel->no_aux_samples && !evsel__is_dummy_event(evsel)) { -+ attr->sample_regs_intr = opts->sample_intr_regs[0]; -+ memcpy(attr->sample_regs_intr_ext, &opts->sample_intr_regs[1], -+ PERF_NUM_EXT_REGS / 8); - evsel__set_sample_bit(evsel, REGS_INTR); - } - -- if (opts->sample_user_regs && !evsel->no_aux_samples && -- !evsel__is_dummy_event(evsel)) { -- attr->sample_regs_user |= opts->sample_user_regs; -+ if (bitmap_weight(opts->sample_user_regs, PERF_SAMPLE_REGS_NUM) && -+ !evsel->no_aux_samples && !evsel__is_dummy_event(evsel)) { -+ attr->sample_regs_user |= opts->sample_user_regs[0]; -+ memcpy(attr->sample_regs_user_ext, &opts->sample_user_regs[1], -+ PERF_NUM_EXT_REGS / 8); - evsel__set_sample_bit(evsel, REGS_USER); - } - -@@ -3230,10 +3234,16 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, - - if (regs->abi) { - u64 mask = evsel->core.attr.sample_regs_user; -+ unsigned long *mask_ext = -+ (unsigned long *)evsel->core.attr.sample_regs_user_ext; -+ u64 *user_regs_mask; - - sz = hweight64(mask) * sizeof(u64); -+ sz += bitmap_weight(mask_ext, PERF_NUM_EXT_REGS) * sizeof(u64); - OVERFLOW_CHECK(array, sz, max_size); - regs->mask = mask; -+ user_regs_mask = (u64 *)regs->mask_ext; -+ memcpy(&user_regs_mask[1], mask_ext, PERF_NUM_EXT_REGS); - regs->regs = (u64 *)array; - array = (void *)array + sz; - } -@@ -3287,10 +3297,16 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, - - if (regs->abi != PERF_SAMPLE_REGS_ABI_NONE) { - u64 mask = evsel->core.attr.sample_regs_intr; -+ unsigned long *mask_ext = -+ (unsigned long *)evsel->core.attr.sample_regs_intr_ext; -+ u64 *intr_regs_mask; - - sz = hweight64(mask) * sizeof(u64); -+ sz += bitmap_weight(mask_ext, PERF_NUM_EXT_REGS) * sizeof(u64); - OVERFLOW_CHECK(array, sz, max_size); - regs->mask = mask; -+ intr_regs_mask = (u64 *)regs->mask_ext; -+ memcpy(&intr_regs_mask[1], mask_ext, PERF_NUM_EXT_REGS); - regs->regs = (u64 *)array; - array = (void *)array + sz; - } -diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c -index 3dcd8dc4f81b..42b176705ccf 100644 ---- a/tools/perf/util/parse-regs-options.c -+++ b/tools/perf/util/parse-regs-options.c -@@ -12,11 +12,13 @@ - static int - __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - { -+ unsigned int size = PERF_SAMPLE_REGS_NUM; - uint64_t *mode = (uint64_t *)opt->value; - const struct sample_reg *r = NULL; - char *s, *os = NULL, *p; - int ret = -1; -- uint64_t mask = 0; -+ DECLARE_BITMAP(mask, size); -+ DECLARE_BITMAP(mask_tmp, size); - - if (unset) - return 0; -@@ -24,13 +26,14 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - /* - * cannot set it twice - */ -- if (*mode) -+ if (bitmap_weight((unsigned long *)mode, size)) - return -1; - -+ bitmap_zero(mask, size); - if (intr) -- arch__intr_reg_mask((unsigned long *)&mask); -+ arch__intr_reg_mask(mask); - else -- arch__user_reg_mask((unsigned long *)&mask); -+ arch__user_reg_mask(mask); - - /* str may be NULL in case no arg is passed to -I */ - if (str) { -@@ -47,7 +50,8 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - if (!strcmp(s, "?")) { - fprintf(stderr, "available registers: "); - for (r = arch__sample_reg_masks(); r->name; r++) { -- if (r->mask & mask) -+ bitmap_and(mask_tmp, mask, r->mask_ext, size); -+ if (bitmap_weight(mask_tmp, size)) - fprintf(stderr, "%s ", r->name); - } - fputc('\n', stderr); -@@ -55,7 +59,8 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - goto error; - } - for (r = arch__sample_reg_masks(); r->name; r++) { -- if ((r->mask & mask) && !strcasecmp(s, r->name)) -+ bitmap_and(mask_tmp, mask, r->mask_ext, size); -+ if (bitmap_weight(mask_tmp, size) && !strcasecmp(s, r->name)) - break; - } - if (!r || !r->name) { -@@ -64,7 +69,7 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - goto error; - } - -- *mode |= r->mask; -+ bitmap_or((unsigned long *)mode, (unsigned long *)mode, r->mask_ext, size); - - if (!p) - break; -@@ -75,8 +80,8 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - ret = 0; - - /* default to all possible regs */ -- if (*mode == 0) -- *mode = mask; -+ if (!bitmap_weight((unsigned long *)mode, size)) -+ bitmap_or((unsigned long *)mode, (unsigned long *)mode, mask, size); - error: - free(os); - return ret; -diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h -index 316d280e5cd7..d60a74623a0f 100644 ---- a/tools/perf/util/perf_regs.h -+++ b/tools/perf/util/perf_regs.h -@@ -4,18 +4,32 @@ - - #include - #include -+#include -+#include -+#include "util/record.h" - - struct regs_dump; - - struct sample_reg { - const char *name; -- uint64_t mask; -+ union { -+ uint64_t mask; -+ DECLARE_BITMAP(mask_ext, PERF_SAMPLE_REGS_NUM); -+ }; - }; - - #define SMPL_REG_MASK(b) (1ULL << (b)) - #define SMPL_REG(n, b) { .name = #n, .mask = SMPL_REG_MASK(b) } - #define SMPL_REG2_MASK(b) (3ULL << (b)) - #define SMPL_REG2(n, b) { .name = #n, .mask = SMPL_REG2_MASK(b) } -+#define SMPL_REG_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0x1ULL << (b % __BITS_PER_LONG) } -+#define SMPL_REG2_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0x3ULL << (b % __BITS_PER_LONG) } -+#define SMPL_REG4_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0xfULL << (b % __BITS_PER_LONG) } -+#define SMPL_REG8_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0xffULL << (b % __BITS_PER_LONG) } - #define SMPL_REG_END { .name = NULL } - - enum { -diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h -index ea3a6c4657ee..aa855f49cf3a 100644 ---- a/tools/perf/util/record.h -+++ b/tools/perf/util/record.h -@@ -58,8 +58,8 @@ struct record_opts { - unsigned int auxtrace_mmap_pages; - unsigned int user_freq; - u64 branch_stack; -- u64 sample_intr_regs; -- u64 sample_user_regs; -+ u64 sample_intr_regs[PERF_SAMPLE_ARRAY_SIZE]; -+ u64 sample_user_regs[PERF_SAMPLE_ARRAY_SIZE]; - u64 default_interval; - u64 user_interval; - size_t auxtrace_snapshot_size; -diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h -index fae834144ef4..345a7e3c4b71 100644 ---- a/tools/perf/util/sample.h -+++ b/tools/perf/util/sample.h -@@ -4,13 +4,17 @@ - - #include - #include -+#include - - /* number of register is bound by the number of bits in regs_dump::mask (64) */ - #define PERF_SAMPLE_REGS_CACHE_SIZE (8 * sizeof(u64)) - - struct regs_dump { - u64 abi; -- u64 mask; -+ union { -+ u64 mask; -+ DECLARE_BITMAP(mask_ext, PERF_SAMPLE_REGS_NUM); -+ }; - u64 *regs; - - /* Cached values/mask filled by first register access. */ -diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c -index 26ae078278cd..276d3931184c 100644 ---- a/tools/perf/util/session.c -+++ b/tools/perf/util/session.c -@@ -915,12 +915,13 @@ static void branch_stack__printf(struct perf_sample *sample, - } - } - --static void regs_dump__printf(u64 mask, u64 *regs, const char *arch) -+static void regs_dump__printf(struct regs_dump *regs, const char *arch) - { -+ unsigned int size = PERF_SAMPLE_REGS_NUM; - unsigned rid, i = 0; - -- for_each_set_bit(rid, (unsigned long *) &mask, sizeof(mask) * 8) { -- u64 val = regs[i++]; -+ for_each_set_bit(rid, regs->mask_ext, size) { -+ u64 val = regs->regs[i++]; - - printf(".... %-5s 0x%016" PRIx64 "\n", - perf_reg_name(rid, arch), val); -@@ -941,16 +942,20 @@ static inline const char *regs_dump_abi(struct regs_dump *d) - return regs_abi[d->abi]; - } - --static void regs__printf(const char *type, struct regs_dump *regs, const char *arch) -+static void regs__printf(bool intr, struct regs_dump *regs, const char *arch) - { -- u64 mask = regs->mask; -+ u64 *mask = (u64 *)®s->mask_ext; - -- printf("... %s regs: mask 0x%" PRIx64 " ABI %s\n", -- type, -- mask, -- regs_dump_abi(regs)); -+ if (intr) -+ printf("... intr regs: mask 0x"); -+ else -+ printf("... user regs: mask 0x"); -+ -+ for (int i = 0; i < PERF_SAMPLE_ARRAY_SIZE; i++) -+ printf("%" PRIx64 "", mask[i]); -+ printf(" ABI %s\n", regs_dump_abi(regs)); - -- regs_dump__printf(mask, regs->regs, arch); -+ regs_dump__printf(regs, arch); - } - - static void regs_user__printf(struct perf_sample *sample, const char *arch) -@@ -963,7 +968,7 @@ static void regs_user__printf(struct perf_sample *sample, const char *arch) - user_regs = perf_sample__user_regs(sample); - - if (user_regs->regs) -- regs__printf("user", user_regs, arch); -+ regs__printf(false, user_regs, arch); - } - - static void regs_intr__printf(struct perf_sample *sample, const char *arch) -@@ -976,7 +981,7 @@ static void regs_intr__printf(struct perf_sample *sample, const char *arch) - intr_regs = perf_sample__intr_regs(sample); - - if (intr_regs->regs) -- regs__printf("intr", intr_regs, arch); -+ regs__printf(true, intr_regs, arch); - } - - static void stack_user__printf(struct stack_dump *dump) -diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c -index cb2c1ace304a..ee4c9fdd02a3 100644 ---- a/tools/perf/util/synthetic-events.c -+++ b/tools/perf/util/synthetic-events.c -@@ -1518,7 +1518,8 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, - if (type & PERF_SAMPLE_REGS_USER) { - if (sample->user_regs && sample->user_regs->abi) { - result += sizeof(u64); -- sz = hweight64(sample->user_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->user_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - result += sz; - } else { - result += sizeof(u64); -@@ -1546,7 +1547,8 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, - if (type & PERF_SAMPLE_REGS_INTR) { - if (sample->intr_regs && sample->intr_regs->abi) { - result += sizeof(u64); -- sz = hweight64(sample->intr_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->intr_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - result += sz; - } else { - result += sizeof(u64); -@@ -1723,7 +1725,8 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo - if (type & PERF_SAMPLE_REGS_USER) { - if (sample->user_regs && sample->user_regs->abi) { - *array++ = sample->user_regs->abi; -- sz = hweight64(sample->user_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->user_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - memcpy(array, sample->user_regs->regs, sz); - array = (void *)array + sz; - } else { -@@ -1759,7 +1762,8 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo - if (type & PERF_SAMPLE_REGS_INTR) { - if (sample->intr_regs && sample->intr_regs->abi) { - *array++ = sample->intr_regs->abi; -- sz = hweight64(sample->intr_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->intr_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - memcpy(array, sample->intr_regs->regs, sz); - array = (void *)array + sz; - } else { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0026-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch b/SPECS/kernel-rt/0026-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch deleted file mode 100644 index e8ed016e5..000000000 --- a/SPECS/kernel-rt/0026-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch +++ /dev/null @@ -1,170 +0,0 @@ -From be64fd5cb0ae6b027bd7f2cbdfd38dde02c6d7c4 Mon Sep 17 00:00:00 2001 -From: Lorenzo Bianconi -Date: Wed, 8 Oct 2025 12:41:48 +0200 -Subject: [PATCH 26/51] wifi: mt76: wed: use proper wed reference in mt76 wed - driver callabacks - -MT7996 driver can use both wed and wed_hif2 devices to offload traffic -from/to the wireless NIC. In the current codebase we assume to always -use the primary wed device in wed callbacks resulting in the following -crash if the hw runs wed_hif2 (e.g. 6GHz link). - -[ 297.455876] Unable to handle kernel read from unreadable memory at virtual address 000000000000080a -[ 297.464928] Mem abort info: -[ 297.467722] ESR = 0x0000000096000005 -[ 297.471461] EC = 0x25: DABT (current EL), IL = 32 bits -[ 297.476766] SET = 0, FnV = 0 -[ 297.479809] EA = 0, S1PTW = 0 -[ 297.482940] FSC = 0x05: level 1 translation fault -[ 297.487809] Data abort info: -[ 297.490679] ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 -[ 297.496156] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 -[ 297.501196] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 -[ 297.506500] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000107480000 -[ 297.512927] [000000000000080a] pgd=08000001097fb003, p4d=08000001097fb003, pud=08000001097fb003, pmd=0000000000000000 -[ 297.523532] Internal error: Oops: 0000000096000005 [#1] SMP -[ 297.715393] CPU: 2 UID: 0 PID: 45 Comm: kworker/u16:2 Tainted: G O 6.12.50 #0 -[ 297.723908] Tainted: [O]=OOT_MODULE -[ 297.727384] Hardware name: Banana Pi BPI-R4 (2x SFP+) (DT) -[ 297.732857] Workqueue: nf_ft_offload_del nf_flow_rule_route_ipv6 [nf_flow_table] -[ 297.740254] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) -[ 297.747205] pc : mt76_wed_offload_disable+0x64/0xa0 [mt76] -[ 297.752688] lr : mtk_wed_flow_remove+0x58/0x80 -[ 297.757126] sp : ffffffc080fe3ae0 -[ 297.760430] x29: ffffffc080fe3ae0 x28: ffffffc080fe3be0 x27: 00000000deadbef7 -[ 297.767557] x26: ffffff80c5ebca00 x25: 0000000000000001 x24: ffffff80c85f4c00 -[ 297.774683] x23: ffffff80c1875b78 x22: ffffffc080d42cd0 x21: ffffffc080660018 -[ 297.781809] x20: ffffff80c6a076d0 x19: ffffff80c6a043c8 x18: 0000000000000000 -[ 297.788935] x17: 0000000000000000 x16: 0000000000000001 x15: 0000000000000000 -[ 297.796060] x14: 0000000000000019 x13: ffffff80c0ad8ec0 x12: 00000000fa83b2da -[ 297.803185] x11: ffffff80c02700c0 x10: ffffff80c0ad8ec0 x9 : ffffff81fef96200 -[ 297.810311] x8 : ffffff80c02700c0 x7 : ffffff80c02700d0 x6 : 0000000000000002 -[ 297.817435] x5 : 0000000000000400 x4 : 0000000000000000 x3 : 0000000000000000 -[ 297.824561] x2 : 0000000000000001 x1 : 0000000000000800 x0 : ffffff80c6a063c8 -[ 297.831686] Call trace: -[ 297.834123] mt76_wed_offload_disable+0x64/0xa0 [mt76] -[ 297.839254] mtk_wed_flow_remove+0x58/0x80 -[ 297.843342] mtk_flow_offload_cmd+0x434/0x574 -[ 297.847689] mtk_wed_setup_tc_block_cb+0x30/0x40 -[ 297.852295] nf_flow_offload_ipv6_hook+0x7f4/0x964 [nf_flow_table] -[ 297.858466] nf_flow_rule_route_ipv6+0x438/0x4a4 [nf_flow_table] -[ 297.864463] process_one_work+0x174/0x300 -[ 297.868465] worker_thread+0x278/0x430 -[ 297.872204] kthread+0xd8/0xdc -[ 297.875251] ret_from_fork+0x10/0x20 -[ 297.878820] Code: 928b5ae0 8b000273 91400a60 f943fa61 (79401421) -[ 297.884901] ---[ end trace 0000000000000000 ]--- - -Fix the issue detecting the proper wed reference to use running wed -callabacks. - -Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support") -Tested-by: Daniel Pawlik -Tested-by: Matteo Croce -Signed-off-by: Lorenzo Bianconi -Link: https://patch.msgid.link/20251008-wed-fixes-v1-1-8f7678583385@kernel.org -Signed-off-by: Felix Fietkau ---- - drivers/net/wireless/mediatek/mt76/mt76.h | 9 +++++++++ - drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 1 + - drivers/net/wireless/mediatek/mt76/wed.c | 10 +++++----- - include/linux/soc/mediatek/mtk_wed.h | 1 + - 4 files changed, 16 insertions(+), 5 deletions(-) - -diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h -index 47c143e6a79a..eba26875aded 100644 ---- a/drivers/net/wireless/mediatek/mt76/mt76.h -+++ b/drivers/net/wireless/mediatek/mt76/mt76.h -@@ -1226,6 +1226,15 @@ static inline int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, - #define mt76_dereference(p, dev) \ - rcu_dereference_protected(p, lockdep_is_held(&(dev)->mutex)) - -+static inline struct mt76_dev *mt76_wed_to_dev(struct mtk_wed_device *wed) -+{ -+#ifdef CONFIG_NET_MEDIATEK_SOC_WED -+ if (wed->wlan.hif2) -+ return container_of(wed, struct mt76_dev, mmio.wed_hif2); -+#endif /* CONFIG_NET_MEDIATEK_SOC_WED */ -+ return container_of(wed, struct mt76_dev, mmio.wed); -+} -+ - static inline struct mt76_wcid * - __mt76_wcid_ptr(struct mt76_dev *dev, u16 idx) - { -diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c -index 30b40f4a91be..b2af25aef762 100644 ---- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c -+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c -@@ -562,6 +562,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, - - wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE; - wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf; -+ wed->wlan.hif2 = hif2; - - wed->wlan.amsdu_max_subframes = 8; - wed->wlan.amsdu_max_len = 1536; -diff --git a/drivers/net/wireless/mediatek/mt76/wed.c b/drivers/net/wireless/mediatek/mt76/wed.c -index 63f69e152b1c..3ff547e0b250 100644 ---- a/drivers/net/wireless/mediatek/mt76/wed.c -+++ b/drivers/net/wireless/mediatek/mt76/wed.c -@@ -8,7 +8,7 @@ - - void mt76_wed_release_rx_buf(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - int i; - - for (i = 0; i < dev->rx_token_size; i++) { -@@ -31,8 +31,8 @@ EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf); - #ifdef CONFIG_NET_MEDIATEK_SOC_WED - u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); - struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc; -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; - struct mt76_txwi_cache *t = NULL; - int i; -@@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_init_rx_buf); - - int mt76_wed_offload_enable(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - - spin_lock_bh(&dev->token_lock); - dev->token_size = wed->wlan.token_start; -@@ -164,7 +164,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_dma_setup); - - void mt76_wed_offload_disable(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - - spin_lock_bh(&dev->token_lock); - dev->token_size = dev->drv->token_size; -@@ -174,7 +174,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_offload_disable); - - void mt76_wed_reset_complete(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - - complete(&dev->mmio.wed_reset_complete); - } -diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h -index d8949a4ed0dc..8d34bee714e8 100644 ---- a/include/linux/soc/mediatek/mtk_wed.h -+++ b/include/linux/soc/mediatek/mtk_wed.h -@@ -154,6 +154,7 @@ struct mtk_wed_device { - bool wcid_512; - bool hw_rro; - bool msi; -+ bool hif2; - - u16 token_start; - unsigned int nbuf; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi b/SPECS/kernel-rt/0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi deleted file mode 100644 index cd6f260d1..000000000 --- a/SPECS/kernel-rt/0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi +++ /dev/null @@ -1,45 +0,0 @@ -From f9a2b243be42dd5923308b6302a9e42ee231813e Mon Sep 17 00:00:00 2001 -From: Chenyi Qiang -Date: Fri, 27 Dec 2024 00:47:38 -0800 -Subject: [PATCH 27/44] KVM: selftests: Add the 2nd VM exit controls MSR to the - hidden VMX MSR list - -Signed-off-by: Chenyi Qiang -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - tools/arch/x86/include/asm/msr-index.h | 1 + - tools/testing/selftests/kvm/x86/feature_msrs_test.c | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h -index daebfd926f08..a40a87a942cb 100644 ---- a/tools/arch/x86/include/asm/msr-index.h -+++ b/tools/arch/x86/include/asm/msr-index.h -@@ -1202,6 +1202,7 @@ - #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 - #define MSR_IA32_VMX_VMFUNC 0x00000491 - #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 -+#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 - - /* Resctrl MSRs: */ - /* - Intel: */ -diff --git a/tools/testing/selftests/kvm/x86/feature_msrs_test.c b/tools/testing/selftests/kvm/x86/feature_msrs_test.c -index a72f13ae2edb..815dcb7ad214 100644 ---- a/tools/testing/selftests/kvm/x86/feature_msrs_test.c -+++ b/tools/testing/selftests/kvm/x86/feature_msrs_test.c -@@ -27,6 +27,7 @@ static bool is_hidden_vmx_msr(uint32_t msr) - case MSR_IA32_VMX_PINBASED_CTLS: - case MSR_IA32_VMX_PROCBASED_CTLS: - case MSR_IA32_VMX_EXIT_CTLS: -+ case MSR_IA32_VMX_EXIT_CTLS2: - case MSR_IA32_VMX_ENTRY_CTLS: - return true; - default: --- -2.43.0 - diff --git a/SPECS/kernel-rt/0027-perf-tools-Support-to-capture-more-vector-registers-x.perf b/SPECS/kernel-rt/0027-perf-tools-Support-to-capture-more-vector-registers-x.perf deleted file mode 100644 index 84362fa5d..000000000 --- a/SPECS/kernel-rt/0027-perf-tools-Support-to-capture-more-vector-registers-x.perf +++ /dev/null @@ -1,381 +0,0 @@ -From 6990e5be1d5915afc40ccedeb57b9b350bd5afad Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 11:26:40 +0000 -Subject: [PATCH 027/100] perf tools: Support to capture more vector registers - (x86/Intel) - -Intel architectural PEBS supports to capture more vector registers like -OPMASK/YMM/ZMM registers besides already supported XMM registers. - -This patch adds Intel specific support to capture these new vector -registers for perf tools. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - tools/arch/x86/include/uapi/asm/perf_regs.h | 79 ++++++++++- - tools/perf/arch/x86/util/perf_regs.c | 129 +++++++++++++++++- - .../perf/util/perf-regs-arch/perf_regs_x86.c | 82 +++++++++++ - 3 files changed, 285 insertions(+), 5 deletions(-) - -diff --git a/tools/arch/x86/include/uapi/asm/perf_regs.h b/tools/arch/x86/include/uapi/asm/perf_regs.h -index 1c7ab5af5cc1..c05c6ec127c8 100644 ---- a/tools/arch/x86/include/uapi/asm/perf_regs.h -+++ b/tools/arch/x86/include/uapi/asm/perf_regs.h -@@ -36,7 +36,7 @@ enum perf_event_x86_regs { - /* PERF_REG_INTEL_PT_MAX ignores the SSP register. */ - PERF_REG_INTEL_PT_MAX = PERF_REG_X86_R15 + 1, - -- /* These all need two bits set because they are 128bit */ -+ /* These all need two bits set because they are 128 bits */ - PERF_REG_X86_XMM0 = 32, - PERF_REG_X86_XMM1 = 34, - PERF_REG_X86_XMM2 = 36, -@@ -56,6 +56,83 @@ enum perf_event_x86_regs { - - /* These include both GPRs and XMMX registers */ - PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2, -+ -+ /* Leave bits[127:64] for other GP registers, like R16 ~ R31.*/ -+ -+ /* -+ * Each YMM register need 4 bits to represent because they are 256 bits. -+ * PERF_REG_X86_YMMH0 = 128 -+ */ -+ PERF_REG_X86_YMM0 = 128, -+ PERF_REG_X86_YMM1 = PERF_REG_X86_YMM0 + 4, -+ PERF_REG_X86_YMM2 = PERF_REG_X86_YMM1 + 4, -+ PERF_REG_X86_YMM3 = PERF_REG_X86_YMM2 + 4, -+ PERF_REG_X86_YMM4 = PERF_REG_X86_YMM3 + 4, -+ PERF_REG_X86_YMM5 = PERF_REG_X86_YMM4 + 4, -+ PERF_REG_X86_YMM6 = PERF_REG_X86_YMM5 + 4, -+ PERF_REG_X86_YMM7 = PERF_REG_X86_YMM6 + 4, -+ PERF_REG_X86_YMM8 = PERF_REG_X86_YMM7 + 4, -+ PERF_REG_X86_YMM9 = PERF_REG_X86_YMM8 + 4, -+ PERF_REG_X86_YMM10 = PERF_REG_X86_YMM9 + 4, -+ PERF_REG_X86_YMM11 = PERF_REG_X86_YMM10 + 4, -+ PERF_REG_X86_YMM12 = PERF_REG_X86_YMM11 + 4, -+ PERF_REG_X86_YMM13 = PERF_REG_X86_YMM12 + 4, -+ PERF_REG_X86_YMM14 = PERF_REG_X86_YMM13 + 4, -+ PERF_REG_X86_YMM15 = PERF_REG_X86_YMM14 + 4, -+ PERF_REG_X86_YMM_MAX = PERF_REG_X86_YMM15 + 4, -+ -+ /* -+ * Each ZMM register needs 8 bits to represent because they are 512 bits -+ * PERF_REG_X86_ZMMH0 = 192 -+ */ -+ PERF_REG_X86_ZMM0 = PERF_REG_X86_YMM_MAX, -+ PERF_REG_X86_ZMM1 = PERF_REG_X86_ZMM0 + 8, -+ PERF_REG_X86_ZMM2 = PERF_REG_X86_ZMM1 + 8, -+ PERF_REG_X86_ZMM3 = PERF_REG_X86_ZMM2 + 8, -+ PERF_REG_X86_ZMM4 = PERF_REG_X86_ZMM3 + 8, -+ PERF_REG_X86_ZMM5 = PERF_REG_X86_ZMM4 + 8, -+ PERF_REG_X86_ZMM6 = PERF_REG_X86_ZMM5 + 8, -+ PERF_REG_X86_ZMM7 = PERF_REG_X86_ZMM6 + 8, -+ PERF_REG_X86_ZMM8 = PERF_REG_X86_ZMM7 + 8, -+ PERF_REG_X86_ZMM9 = PERF_REG_X86_ZMM8 + 8, -+ PERF_REG_X86_ZMM10 = PERF_REG_X86_ZMM9 + 8, -+ PERF_REG_X86_ZMM11 = PERF_REG_X86_ZMM10 + 8, -+ PERF_REG_X86_ZMM12 = PERF_REG_X86_ZMM11 + 8, -+ PERF_REG_X86_ZMM13 = PERF_REG_X86_ZMM12 + 8, -+ PERF_REG_X86_ZMM14 = PERF_REG_X86_ZMM13 + 8, -+ PERF_REG_X86_ZMM15 = PERF_REG_X86_ZMM14 + 8, -+ PERF_REG_X86_ZMM16 = PERF_REG_X86_ZMM15 + 8, -+ PERF_REG_X86_ZMM17 = PERF_REG_X86_ZMM16 + 8, -+ PERF_REG_X86_ZMM18 = PERF_REG_X86_ZMM17 + 8, -+ PERF_REG_X86_ZMM19 = PERF_REG_X86_ZMM18 + 8, -+ PERF_REG_X86_ZMM20 = PERF_REG_X86_ZMM19 + 8, -+ PERF_REG_X86_ZMM21 = PERF_REG_X86_ZMM20 + 8, -+ PERF_REG_X86_ZMM22 = PERF_REG_X86_ZMM21 + 8, -+ PERF_REG_X86_ZMM23 = PERF_REG_X86_ZMM22 + 8, -+ PERF_REG_X86_ZMM24 = PERF_REG_X86_ZMM23 + 8, -+ PERF_REG_X86_ZMM25 = PERF_REG_X86_ZMM24 + 8, -+ PERF_REG_X86_ZMM26 = PERF_REG_X86_ZMM25 + 8, -+ PERF_REG_X86_ZMM27 = PERF_REG_X86_ZMM26 + 8, -+ PERF_REG_X86_ZMM28 = PERF_REG_X86_ZMM27 + 8, -+ PERF_REG_X86_ZMM29 = PERF_REG_X86_ZMM28 + 8, -+ PERF_REG_X86_ZMM30 = PERF_REG_X86_ZMM29 + 8, -+ PERF_REG_X86_ZMM31 = PERF_REG_X86_ZMM30 + 8, -+ PERF_REG_X86_ZMM_MAX = PERF_REG_X86_ZMM31 + 8, -+ -+ /* -+ * OPMASK Registers -+ * PERF_REG_X86_OPMASK0 = 448 -+ */ -+ PERF_REG_X86_OPMASK0 = PERF_REG_X86_ZMM_MAX, -+ PERF_REG_X86_OPMASK1 = PERF_REG_X86_OPMASK0 + 1, -+ PERF_REG_X86_OPMASK2 = PERF_REG_X86_OPMASK1 + 1, -+ PERF_REG_X86_OPMASK3 = PERF_REG_X86_OPMASK2 + 1, -+ PERF_REG_X86_OPMASK4 = PERF_REG_X86_OPMASK3 + 1, -+ PERF_REG_X86_OPMASK5 = PERF_REG_X86_OPMASK4 + 1, -+ PERF_REG_X86_OPMASK6 = PERF_REG_X86_OPMASK5 + 1, -+ PERF_REG_X86_OPMASK7 = PERF_REG_X86_OPMASK6 + 1, -+ -+ PERF_REG_X86_VEC_MAX = PERF_REG_X86_OPMASK7 + 1, - }; - - #define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1)) -diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c -index 5b163f0a651a..bade6c64770c 100644 ---- a/tools/perf/arch/x86/util/perf_regs.c -+++ b/tools/perf/arch/x86/util/perf_regs.c -@@ -54,6 +54,66 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG2(XMM13, PERF_REG_X86_XMM13), - SMPL_REG2(XMM14, PERF_REG_X86_XMM14), - SMPL_REG2(XMM15, PERF_REG_X86_XMM15), -+ -+ SMPL_REG4_EXT(YMM0, PERF_REG_X86_YMM0), -+ SMPL_REG4_EXT(YMM1, PERF_REG_X86_YMM1), -+ SMPL_REG4_EXT(YMM2, PERF_REG_X86_YMM2), -+ SMPL_REG4_EXT(YMM3, PERF_REG_X86_YMM3), -+ SMPL_REG4_EXT(YMM4, PERF_REG_X86_YMM4), -+ SMPL_REG4_EXT(YMM5, PERF_REG_X86_YMM5), -+ SMPL_REG4_EXT(YMM6, PERF_REG_X86_YMM6), -+ SMPL_REG4_EXT(YMM7, PERF_REG_X86_YMM7), -+ SMPL_REG4_EXT(YMM8, PERF_REG_X86_YMM8), -+ SMPL_REG4_EXT(YMM9, PERF_REG_X86_YMM9), -+ SMPL_REG4_EXT(YMM10, PERF_REG_X86_YMM10), -+ SMPL_REG4_EXT(YMM11, PERF_REG_X86_YMM11), -+ SMPL_REG4_EXT(YMM12, PERF_REG_X86_YMM12), -+ SMPL_REG4_EXT(YMM13, PERF_REG_X86_YMM13), -+ SMPL_REG4_EXT(YMM14, PERF_REG_X86_YMM14), -+ SMPL_REG4_EXT(YMM15, PERF_REG_X86_YMM15), -+ -+ SMPL_REG8_EXT(ZMM0, PERF_REG_X86_ZMM0), -+ SMPL_REG8_EXT(ZMM1, PERF_REG_X86_ZMM1), -+ SMPL_REG8_EXT(ZMM2, PERF_REG_X86_ZMM2), -+ SMPL_REG8_EXT(ZMM3, PERF_REG_X86_ZMM3), -+ SMPL_REG8_EXT(ZMM4, PERF_REG_X86_ZMM4), -+ SMPL_REG8_EXT(ZMM5, PERF_REG_X86_ZMM5), -+ SMPL_REG8_EXT(ZMM6, PERF_REG_X86_ZMM6), -+ SMPL_REG8_EXT(ZMM7, PERF_REG_X86_ZMM7), -+ SMPL_REG8_EXT(ZMM8, PERF_REG_X86_ZMM8), -+ SMPL_REG8_EXT(ZMM9, PERF_REG_X86_ZMM9), -+ SMPL_REG8_EXT(ZMM10, PERF_REG_X86_ZMM10), -+ SMPL_REG8_EXT(ZMM11, PERF_REG_X86_ZMM11), -+ SMPL_REG8_EXT(ZMM12, PERF_REG_X86_ZMM12), -+ SMPL_REG8_EXT(ZMM13, PERF_REG_X86_ZMM13), -+ SMPL_REG8_EXT(ZMM14, PERF_REG_X86_ZMM14), -+ SMPL_REG8_EXT(ZMM15, PERF_REG_X86_ZMM15), -+ SMPL_REG8_EXT(ZMM16, PERF_REG_X86_ZMM16), -+ SMPL_REG8_EXT(ZMM17, PERF_REG_X86_ZMM17), -+ SMPL_REG8_EXT(ZMM18, PERF_REG_X86_ZMM18), -+ SMPL_REG8_EXT(ZMM19, PERF_REG_X86_ZMM19), -+ SMPL_REG8_EXT(ZMM20, PERF_REG_X86_ZMM20), -+ SMPL_REG8_EXT(ZMM21, PERF_REG_X86_ZMM21), -+ SMPL_REG8_EXT(ZMM22, PERF_REG_X86_ZMM22), -+ SMPL_REG8_EXT(ZMM23, PERF_REG_X86_ZMM23), -+ SMPL_REG8_EXT(ZMM24, PERF_REG_X86_ZMM24), -+ SMPL_REG8_EXT(ZMM25, PERF_REG_X86_ZMM25), -+ SMPL_REG8_EXT(ZMM26, PERF_REG_X86_ZMM26), -+ SMPL_REG8_EXT(ZMM27, PERF_REG_X86_ZMM27), -+ SMPL_REG8_EXT(ZMM28, PERF_REG_X86_ZMM28), -+ SMPL_REG8_EXT(ZMM29, PERF_REG_X86_ZMM29), -+ SMPL_REG8_EXT(ZMM30, PERF_REG_X86_ZMM30), -+ SMPL_REG8_EXT(ZMM31, PERF_REG_X86_ZMM31), -+ -+ SMPL_REG_EXT(OPMASK0, PERF_REG_X86_OPMASK0), -+ SMPL_REG_EXT(OPMASK1, PERF_REG_X86_OPMASK1), -+ SMPL_REG_EXT(OPMASK2, PERF_REG_X86_OPMASK2), -+ SMPL_REG_EXT(OPMASK3, PERF_REG_X86_OPMASK3), -+ SMPL_REG_EXT(OPMASK4, PERF_REG_X86_OPMASK4), -+ SMPL_REG_EXT(OPMASK5, PERF_REG_X86_OPMASK5), -+ SMPL_REG_EXT(OPMASK6, PERF_REG_X86_OPMASK6), -+ SMPL_REG_EXT(OPMASK7, PERF_REG_X86_OPMASK7), -+ - SMPL_REG_END - }; - -@@ -283,13 +343,59 @@ const struct sample_reg *arch__sample_reg_masks(void) - return sample_reg_masks; - } - --void arch__intr_reg_mask(unsigned long *mask) -+static void check_ext2_regs_mask(struct perf_event_attr *attr, bool user, -+ int idx, u64 fmask, unsigned long *mask) -+{ -+ u64 reg_mask[PERF_SAMPLE_ARRAY_SIZE] = { 0 }; -+ int fd; -+ -+ if (user) { -+ attr->sample_regs_user = 0; -+ attr->sample_regs_user_ext[idx] = fmask; -+ } else { -+ attr->sample_regs_intr = 0; -+ attr->sample_regs_intr_ext[idx] = fmask; -+ } -+ -+ /* reg_mask[] includes sample_regs_intr regs, so index need add 1. */ -+ reg_mask[idx + 1] = fmask; -+ -+ fd = sys_perf_event_open(attr, 0, -1, -1, 0); -+ if (fd != -1) { -+ close(fd); -+ bitmap_or(mask, mask, (unsigned long *)reg_mask, -+ PERF_SAMPLE_REGS_NUM); -+ } -+} -+ -+#define PERF_REG_EXTENDED_YMM_MASK GENMASK_ULL(63, 0) -+#define PERF_REG_EXTENDED_ZMM_MASK GENMASK_ULL(63, 0) -+#define PERF_REG_EXTENDED_OPMASK_MASK GENMASK_ULL(7, 0) -+ -+static void get_ext2_regs_mask(struct perf_event_attr *attr, bool user, -+ unsigned long *mask) -+{ -+ event_attr_init(attr); -+ -+ /* Check YMM regs, bits 128 ~ 191. */ -+ check_ext2_regs_mask(attr, user, 1, PERF_REG_EXTENDED_YMM_MASK, mask); -+ /* Check ZMM 0-7 regs, bits 192 ~ 255. */ -+ check_ext2_regs_mask(attr, user, 2, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check ZMM 8-15 regs, bits 256 ~ 319. */ -+ check_ext2_regs_mask(attr, user, 3, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check ZMM 16-23 regs, bits 320 ~ 383. */ -+ check_ext2_regs_mask(attr, user, 4, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check ZMM 16-23 regs, bits 384 ~ 447. */ -+ check_ext2_regs_mask(attr, user, 5, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check OPMASK regs, bits 448 ~ 455. */ -+ check_ext2_regs_mask(attr, user, 6, PERF_REG_EXTENDED_OPMASK_MASK, mask); -+} -+ -+static void arch__get_reg_mask(unsigned long *mask, bool user) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_CPU_CYCLES, -- .sample_type = PERF_SAMPLE_REGS_INTR, -- .sample_regs_intr = PERF_REG_EXTENDED_MASK, - .precise_ip = 1, - .disabled = 1, - .exclude_kernel = 1, -@@ -298,6 +404,14 @@ void arch__intr_reg_mask(unsigned long *mask) - - *(u64 *)mask = PERF_REGS_MASK; - -+ if (user) { -+ attr.sample_type = PERF_SAMPLE_REGS_USER; -+ attr.sample_regs_user = PERF_REG_EXTENDED_MASK; -+ } else { -+ attr.sample_type = PERF_SAMPLE_REGS_INTR; -+ attr.sample_regs_intr = PERF_REG_EXTENDED_MASK; -+ } -+ - /* - * In an unnamed union, init it here to build on older gcc versions - */ -@@ -325,9 +439,16 @@ void arch__intr_reg_mask(unsigned long *mask) - close(fd); - *(u64 *)mask = PERF_REG_EXTENDED_MASK | PERF_REGS_MASK; - } -+ -+ get_ext2_regs_mask(&attr, user, mask); -+} -+ -+void arch__intr_reg_mask(unsigned long *mask) -+{ -+ arch__get_reg_mask(mask, false); - } - - void arch__user_reg_mask(unsigned long *mask) - { -- *(uint64_t *)mask = PERF_REGS_MASK; -+ arch__get_reg_mask(mask, true); - } -diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -index c0e95215b577..eb1e3d716f27 100644 ---- a/tools/perf/util/perf-regs-arch/perf_regs_x86.c -+++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -@@ -78,6 +78,88 @@ const char *__perf_reg_name_x86(int id) - XMM(14) - XMM(15) - #undef XMM -+ -+#define YMM(x) \ -+ case PERF_REG_X86_YMM ## x: \ -+ case PERF_REG_X86_YMM ## x + 1: \ -+ case PERF_REG_X86_YMM ## x + 2: \ -+ case PERF_REG_X86_YMM ## x + 3: \ -+ return "YMM" #x; -+ YMM(0) -+ YMM(1) -+ YMM(2) -+ YMM(3) -+ YMM(4) -+ YMM(5) -+ YMM(6) -+ YMM(7) -+ YMM(8) -+ YMM(9) -+ YMM(10) -+ YMM(11) -+ YMM(12) -+ YMM(13) -+ YMM(14) -+ YMM(15) -+#undef YMM -+ -+#define ZMM(x) \ -+ case PERF_REG_X86_ZMM ## x: \ -+ case PERF_REG_X86_ZMM ## x + 1: \ -+ case PERF_REG_X86_ZMM ## x + 2: \ -+ case PERF_REG_X86_ZMM ## x + 3: \ -+ case PERF_REG_X86_ZMM ## x + 4: \ -+ case PERF_REG_X86_ZMM ## x + 5: \ -+ case PERF_REG_X86_ZMM ## x + 6: \ -+ case PERF_REG_X86_ZMM ## x + 7: \ -+ return "ZMM" #x; -+ ZMM(0) -+ ZMM(1) -+ ZMM(2) -+ ZMM(3) -+ ZMM(4) -+ ZMM(5) -+ ZMM(6) -+ ZMM(7) -+ ZMM(8) -+ ZMM(9) -+ ZMM(10) -+ ZMM(11) -+ ZMM(12) -+ ZMM(13) -+ ZMM(14) -+ ZMM(15) -+ ZMM(16) -+ ZMM(17) -+ ZMM(18) -+ ZMM(19) -+ ZMM(20) -+ ZMM(21) -+ ZMM(22) -+ ZMM(23) -+ ZMM(24) -+ ZMM(25) -+ ZMM(26) -+ ZMM(27) -+ ZMM(28) -+ ZMM(29) -+ ZMM(30) -+ ZMM(31) -+#undef ZMM -+ -+#define OPMASK(x) \ -+ case PERF_REG_X86_OPMASK ## x: \ -+ return "opmask" #x; -+ -+ OPMASK(0) -+ OPMASK(1) -+ OPMASK(2) -+ OPMASK(3) -+ OPMASK(4) -+ OPMASK(5) -+ OPMASK(6) -+ OPMASK(7) -+#undef OPMASK - default: - return NULL; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi b/SPECS/kernel-rt/0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi new file mode 100644 index 000000000..62208a1e8 --- /dev/null +++ b/SPECS/kernel-rt/0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi @@ -0,0 +1,77 @@ +From e90bc85022702e9787922ed6135654f84e58b467 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Tue, 8 Jul 2025 04:37:17 -0700 +Subject: [PATCH 27/44] task_stack.h: Add a new helper + task_empty_stack_pointer() + +Introduce a new helper function, task_empty_stack_pointer(), to replace + + (unsigned long)task_stack_page(current) + THREAD_SIZE + +Sugguested-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +--- + arch/x86/include/asm/fred.h | 2 +- + arch/x86/kernel/dumpstack.c | 2 +- + arch/x86/kvm/vmx/vmx.c | 2 +- + include/linux/sched/task_stack.h | 5 +++++ + 4 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h +index 12b34d5b2953e..263fd5267f711 100644 +--- a/arch/x86/include/asm/fred.h ++++ b/arch/x86/include/asm/fred.h +@@ -98,7 +98,7 @@ static __always_inline void fred_sync_rsp0(unsigned long rsp0) + + static __always_inline void fred_update_rsp0(void) + { +- unsigned long rsp0 = (unsigned long) task_stack_page(current) + THREAD_SIZE; ++ unsigned long rsp0 = (unsigned long)task_empty_stack_pointer(current); + + if (cpu_feature_enabled(X86_FEATURE_FRED) && (__this_cpu_read(fred_rsp0) != rsp0)) { + wrmsrns(MSR_IA32_FRED_RSP0, rsp0); +diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c +index 71ee20102a8af..18c19e6fd960c 100644 +--- a/arch/x86/kernel/dumpstack.c ++++ b/arch/x86/kernel/dumpstack.c +@@ -31,7 +31,7 @@ bool noinstr in_task_stack(unsigned long *stack, struct task_struct *task, + struct stack_info *info) + { + unsigned long *begin = task_stack_page(task); +- unsigned long *end = task_stack_page(task) + THREAD_SIZE; ++ unsigned long *end = task_empty_stack_pointer(task); + + if (stack < begin || stack >= end) + return false; +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 823ad3300f656..e845e56b12c4c 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -6578,7 +6578,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + "RSP2=0x%016llx, RSP3=0x%016llx\n", + vmcs_read64(HOST_IA32_FRED_CONFIG), + vmcs_read64(HOST_IA32_FRED_STKLVLS), +- (unsigned long)task_stack_page(current) + THREAD_SIZE, ++ (unsigned long)task_empty_stack_pointer(current), + vmcs_read64(HOST_IA32_FRED_RSP1), + vmcs_read64(HOST_IA32_FRED_RSP2), + vmcs_read64(HOST_IA32_FRED_RSP3)); +diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h +index 1fab7e9043a3c..43050ec1012b8 100644 +--- a/include/linux/sched/task_stack.h ++++ b/include/linux/sched/task_stack.h +@@ -23,6 +23,11 @@ static __always_inline void *task_stack_page(const struct task_struct *task) + return task->stack; + } + ++static __always_inline void *task_empty_stack_pointer(const struct task_struct *task) ++{ ++ return (void *)((unsigned long)task_stack_page(task) + THREAD_SIZE); ++} ++ + #define setup_thread_stack(new,old) do { } while(0) + + static __always_inline unsigned long *end_of_stack(const struct task_struct *task) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf b/SPECS/kernel-rt/0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf deleted file mode 100644 index fefa96214..000000000 --- a/SPECS/kernel-rt/0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf +++ /dev/null @@ -1,93 +0,0 @@ -From df18ccaa75adc4705ffede3ef28f269bee179308 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Tue, 29 Oct 2024 12:09:28 +0000 -Subject: [PATCH 028/100] perf tools/tests: Add vector registers PEBS sampling - test - -Current adaptive PEBS supports to capture some vector registers like XMM -register, and arch-PEBS supports to capture wider vector registers like -YMM and ZMM registers. This patch adds a perf test case to verify these -vector registers can be captured correctly. - -Suggested-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - tools/perf/tests/shell/record.sh | 55 ++++++++++++++++++++++++++++++++ - 1 file changed, 55 insertions(+) - -diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh -index b1ad24fb3b33..6ef799128793 100755 ---- a/tools/perf/tests/shell/record.sh -+++ b/tools/perf/tests/shell/record.sh -@@ -120,6 +120,60 @@ test_register_capture() { - echo "Register capture test [Success]" - } - -+test_vec_register_capture() { -+ echo "Vector register capture test" -+ if ! perf record -o /dev/null --quiet -e instructions:p true 2> /dev/null -+ then -+ echo "Vector register capture test [Skipped missing event]" -+ return -+ fi -+ if ! perf record --intr-regs=\? 2>&1 | grep -q 'XMM0' -+ then -+ echo "Vector register capture test [Skipped missing XMM registers]" -+ return -+ fi -+ if ! perf record -o - --intr-regs=xmm0 -e instructions:p \ -+ -c 100000 ${testprog} 2> /dev/null \ -+ | perf script -F ip,sym,iregs -i - 2> /dev/null \ -+ | grep -q "XMM0:" -+ then -+ echo "Vector register capture test [Failed missing XMM output]" -+ err=1 -+ return -+ fi -+ echo "Vector registe (XMM) capture test [Success]" -+ if ! perf record --intr-regs=\? 2>&1 | grep -q 'YMM0' -+ then -+ echo "Vector register capture test [Skipped missing YMM registers]" -+ return -+ fi -+ if ! perf record -o - --intr-regs=ymm0 -e instructions:p \ -+ -c 100000 ${testprog} 2> /dev/null \ -+ | perf script -F ip,sym,iregs -i - 2> /dev/null \ -+ | grep -q "YMM0:" -+ then -+ echo "Vector register capture test [Failed missing YMM output]" -+ err=1 -+ return -+ fi -+ echo "Vector registe (YMM) capture test [Success]" -+ if ! perf record --intr-regs=\? 2>&1 | grep -q 'ZMM0' -+ then -+ echo "Vector register capture test [Skipped missing ZMM registers]" -+ return -+ fi -+ if ! perf record -o - --intr-regs=zmm0 -e instructions:p \ -+ -c 100000 ${testprog} 2> /dev/null \ -+ | perf script -F ip,sym,iregs -i - 2> /dev/null \ -+ | grep -q "ZMM0:" -+ then -+ echo "Vector register capture test [Failed missing ZMM output]" -+ err=1 -+ return -+ fi -+ echo "Vector register (ZMM) capture test [Success]" -+} -+ - test_system_wide() { - echo "Basic --system-wide mode test" - if ! perf record -aB --synth=no -o "${perfdata}" ${testprog} 2> /dev/null -@@ -395,6 +449,7 @@ fi - - test_per_thread - test_register_capture -+test_vec_register_capture - test_system_wide - test_workload - test_branch_counter --- -2.43.0 - diff --git a/SPECS/kernel-rt/0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi b/SPECS/kernel-rt/0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi deleted file mode 100644 index d9e5de974..000000000 --- a/SPECS/kernel-rt/0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi +++ /dev/null @@ -1,77 +0,0 @@ -From a965c8232d9c74ee5c7340c206fc9776ce1a7961 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Tue, 8 Jul 2025 04:37:17 -0700 -Subject: [PATCH 28/44] task_stack.h: Add a new helper - task_empty_stack_pointer() - -Introduce a new helper function, task_empty_stack_pointer(), to replace - - (unsigned long)task_stack_page(current) + THREAD_SIZE - -Sugguested-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) ---- - arch/x86/include/asm/fred.h | 2 +- - arch/x86/kernel/dumpstack.c | 2 +- - arch/x86/kvm/vmx/vmx.c | 2 +- - include/linux/sched/task_stack.h | 5 +++++ - 4 files changed, 8 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h -index 12b34d5b2953..263fd5267f71 100644 ---- a/arch/x86/include/asm/fred.h -+++ b/arch/x86/include/asm/fred.h -@@ -98,7 +98,7 @@ static __always_inline void fred_sync_rsp0(unsigned long rsp0) - - static __always_inline void fred_update_rsp0(void) - { -- unsigned long rsp0 = (unsigned long) task_stack_page(current) + THREAD_SIZE; -+ unsigned long rsp0 = (unsigned long)task_empty_stack_pointer(current); - - if (cpu_feature_enabled(X86_FEATURE_FRED) && (__this_cpu_read(fred_rsp0) != rsp0)) { - wrmsrns(MSR_IA32_FRED_RSP0, rsp0); -diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c -index 71ee20102a8a..18c19e6fd960 100644 ---- a/arch/x86/kernel/dumpstack.c -+++ b/arch/x86/kernel/dumpstack.c -@@ -31,7 +31,7 @@ bool noinstr in_task_stack(unsigned long *stack, struct task_struct *task, - struct stack_info *info) - { - unsigned long *begin = task_stack_page(task); -- unsigned long *end = task_stack_page(task) + THREAD_SIZE; -+ unsigned long *end = task_empty_stack_pointer(task); - - if (stack < begin || stack >= end) - return false; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index d04085f2d4a2..090b49679925 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -6607,7 +6607,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - "RSP2=0x%016llx, RSP3=0x%016llx\n", - vmcs_read64(HOST_IA32_FRED_CONFIG), - vmcs_read64(HOST_IA32_FRED_STKLVLS), -- (unsigned long)task_stack_page(current) + THREAD_SIZE, -+ (unsigned long)task_empty_stack_pointer(current), - vmcs_read64(HOST_IA32_FRED_RSP1), - vmcs_read64(HOST_IA32_FRED_RSP2), - vmcs_read64(HOST_IA32_FRED_RSP3)); -diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h -index 1fab7e9043a3..43050ec1012b 100644 ---- a/include/linux/sched/task_stack.h -+++ b/include/linux/sched/task_stack.h -@@ -23,6 +23,11 @@ static __always_inline void *task_stack_page(const struct task_struct *task) - return task->stack; - } - -+static __always_inline void *task_empty_stack_pointer(const struct task_struct *task) -+{ -+ return (void *)((unsigned long)task_stack_page(task) + THREAD_SIZE); -+} -+ - #define setup_thread_stack(new,old) do { } while(0) - - static __always_inline unsigned long *end_of_stack(const struct task_struct *task) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0028-x86-fred-Allow-variable-sized-event-frame.nmi b/SPECS/kernel-rt/0028-x86-fred-Allow-variable-sized-event-frame.nmi new file mode 100644 index 000000000..e1e0d465b --- /dev/null +++ b/SPECS/kernel-rt/0028-x86-fred-Allow-variable-sized-event-frame.nmi @@ -0,0 +1,191 @@ +From c9ceb2fcb58c3aad046d17899910abb540a5f703 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Mon, 17 Mar 2025 20:46:29 -0700 +Subject: [PATCH 28/44] x86/fred: Allow variable-sized event frame + +A FRED event frame could contain different amount of information for +different event types, or perhaps even for different instances of the +same event type. Thus the size of an event frame pushed by a FRED CPU +is not fixed and the address of the pt_regs structure that is used to +save a user level context of current task is not at a fixed offset +from top of current task kernel stack. + +Add a new field named 'user_pt_regs' in the thread_info structure to +save the address of user level context pt_regs structure, thus to +eliminate the need of any advance information of event frame size +and allow a FRED CPU to push variable-sized event frame. + +For IDT user level event delivery, a pt_regs structure is pushed by +hardware and software _always_ at a fixed offset from top of current +task kernel stack, so simply initialize user_pt_regs to point to the +pt_regs structure no matter whether one is pushed or not. + +While for FRED user level event delivery, user_pt_regs is updated with +a pt_regs structure pointer generated in asm_fred_entrypoint_user(). + +Suggested-by: H. Peter Anvin (Intel) +Signed-off-by: Xin Li (Intel) +--- + +Change in v3A: +* Add declaration for __top_init_kernel_stack[] (Intel lkp). + +Change in v3: +* Replace "(struct pt_regs *)TOP_OF_INIT_STACK - 1" with + (struct pt_regs *)__top_init_kernel_stack (Brian Gerst). +--- + arch/x86/entry/entry_fred.c | 10 ++++++++++ + arch/x86/include/asm/processor.h | 12 ++++++------ + arch/x86/include/asm/thread_info.h | 11 ++++++++--- + arch/x86/kernel/process.c | 22 ++++++++++++++++++++++ + include/linux/thread_info.h | 1 + + kernel/fork.c | 6 ++++++ + 6 files changed, 53 insertions(+), 9 deletions(-) + +diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c +index f004a4dc74c2d..a5f5bdd16ad8d 100644 +--- a/arch/x86/entry/entry_fred.c ++++ b/arch/x86/entry/entry_fred.c +@@ -228,6 +228,16 @@ __visible noinstr void fred_entry_from_user(struct pt_regs *regs) + /* Invalidate orig_ax so that syscall_get_nr() works correctly */ + regs->orig_ax = -1; + ++ /* ++ * A FRED event frame could contain different amount of information ++ * for different event types, or perhaps even for different instances ++ * of the same event type. Thus the size of an event frame pushed by ++ * a FRED CPU is not fixed and the address of the pt_regs structure ++ * that is used to save a user level context of current task is not ++ * at a fixed offset from top of current task stack. ++ */ ++ current->thread_info.user_pt_regs = regs; ++ + switch (regs->fred_ss.type) { + case EVENT_TYPE_EXTINT: + return fred_extint(regs); +diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h +index a24c7805acdb5..b7893536099a9 100644 +--- a/arch/x86/include/asm/processor.h ++++ b/arch/x86/include/asm/processor.h +@@ -645,12 +645,12 @@ static __always_inline void prefetchw(const void *x) + + #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) + +-#define task_pt_regs(task) \ +-({ \ +- unsigned long __ptr = (unsigned long)task_stack_page(task); \ +- __ptr += THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; \ +- ((struct pt_regs *)__ptr) - 1; \ +-}) ++/* ++ * Note, this can't be converted to an inline function as this header ++ * file defines 'struct thread_struct' which is used in the task_struct ++ * structure definition. ++ */ ++#define task_pt_regs(task) ((task)->thread_info.user_pt_regs) + + #ifdef CONFIG_X86_32 + #define INIT_THREAD { \ +diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h +index e71e0e8362ed8..0bdd2335ecd22 100644 +--- a/arch/x86/include/asm/thread_info.h ++++ b/arch/x86/include/asm/thread_info.h +@@ -56,6 +56,7 @@ + */ + #ifndef __ASSEMBLER__ + struct task_struct; ++struct pt_regs; + #include + #include + +@@ -66,11 +67,15 @@ struct thread_info { + #ifdef CONFIG_SMP + u32 cpu; /* current CPU */ + #endif ++ struct pt_regs *user_pt_regs; + }; + +-#define INIT_THREAD_INFO(tsk) \ +-{ \ +- .flags = 0, \ ++extern unsigned long __top_init_kernel_stack[]; ++ ++#define INIT_THREAD_INFO(tsk) \ ++{ \ ++ .flags = 0, \ ++ .user_pt_regs = (struct pt_regs *)__top_init_kernel_stack, \ + } + + #else /* !__ASSEMBLER__ */ +diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c +index 4c718f8adc592..d456c3d9da965 100644 +--- a/arch/x86/kernel/process.c ++++ b/arch/x86/kernel/process.c +@@ -114,6 +114,28 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) + return 0; + } + ++/* ++ * Initialize thread_info.user_pt_regs for IDT event delivery. ++ * ++ * For IDT user level event delivery, a pt_regs structure is pushed by both ++ * hardware and software and always resides at a fixed offset from top of ++ * current task kernel stack, thus thread_info.user_pt_regs is a per-task ++ * constant and NEVER changes after initialization. ++ * ++ * While for FRED user level event delivery, user_pt_regs is updated in ++ * fred_entry_from_user() immediately after user level event delivery. ++ * ++ * Note: thread_info.user_pt_regs of the init task is initialized at build ++ * time. ++ */ ++void arch_init_user_pt_regs(struct task_struct *tsk) ++{ ++ unsigned long init_sp = (unsigned long)task_empty_stack_pointer(tsk); ++ ++ init_sp -= TOP_OF_KERNEL_STACK_PADDING; ++ tsk->thread_info.user_pt_regs = (struct pt_regs *)init_sp - 1; ++} ++ + #ifdef CONFIG_X86_64 + void arch_release_task_struct(struct task_struct *tsk) + { +diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h +index dd925d84fa46c..f95d38ed04d7f 100644 +--- a/include/linux/thread_info.h ++++ b/include/linux/thread_info.h +@@ -225,6 +225,7 @@ void arch_task_cache_init(void); /* for CONFIG_SH */ + void arch_release_task_struct(struct task_struct *tsk); + int arch_dup_task_struct(struct task_struct *dst, + struct task_struct *src); ++void arch_init_user_pt_regs(struct task_struct *tsk); + + #endif /* __KERNEL__ */ + +diff --git a/kernel/fork.c b/kernel/fork.c +index 3da0f08615a95..962d48bea9f71 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -855,6 +855,10 @@ int __weak arch_dup_task_struct(struct task_struct *dst, + return 0; + } + ++void __weak arch_init_user_pt_regs(struct task_struct *tsk) ++{ ++} ++ + void set_task_stack_end_magic(struct task_struct *tsk) + { + unsigned long *stackend; +@@ -882,6 +886,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) + if (err) + goto free_tsk; + ++ arch_init_user_pt_regs(tsk); ++ + #ifdef CONFIG_THREAD_INFO_IN_TASK + refcount_set(&tsk->stack_refcount, 1); + #endif +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0029-iomap-allocate-s_dio_done_wq-for-async-reads-as-well.patch b/SPECS/kernel-rt/0029-iomap-allocate-s_dio_done_wq-for-async-reads-as-well.patch deleted file mode 100644 index 971995d5d..000000000 --- a/SPECS/kernel-rt/0029-iomap-allocate-s_dio_done_wq-for-async-reads-as-well.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 8c13b4db6deaca6496ea46e752512ed88b5941fc Mon Sep 17 00:00:00 2001 -From: Christoph Hellwig -Date: Mon, 24 Nov 2025 15:00:13 +0100 -Subject: [PATCH 29/51] iomap: allocate s_dio_done_wq for async reads as well - -Since commit 222f2c7c6d14 ("iomap: always run error completions in user -context"), read error completions are deferred to s_dio_done_wq. This -means the workqueue also needs to be allocated for async reads. - -Fixes: 222f2c7c6d14 ("iomap: always run error completions in user context") -Reported-by: syzbot+a2b9a4ed0d61b1efb3f5@syzkaller.appspotmail.com -Signed-off-by: Christoph Hellwig -Link: https://patch.msgid.link/20251124140013.902853-1-hch@lst.de -Tested-by: syzbot+a2b9a4ed0d61b1efb3f5@syzkaller.appspotmail.com -Reviewed-by: Dave Chinner -Reviewed-by: Darrick J. Wong -Signed-off-by: Christian Brauner ---- - fs/iomap/direct-io.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c -index 46aa85af13dc..d143831bb05c 100644 ---- a/fs/iomap/direct-io.c -+++ b/fs/iomap/direct-io.c -@@ -717,12 +717,12 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, - } - goto out_free_dio; - } -+ } - -- if (!wait_for_completion && !inode->i_sb->s_dio_done_wq) { -- ret = sb_init_dio_done_wq(inode->i_sb); -- if (ret < 0) -- goto out_free_dio; -- } -+ if (!wait_for_completion && !inode->i_sb->s_dio_done_wq) { -+ ret = sb_init_dio_done_wq(inode->i_sb); -+ if (ret < 0) -+ goto out_free_dio; - } - - inode_dio_begin(inode); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf b/SPECS/kernel-rt/0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf deleted file mode 100644 index f873d51a2..000000000 --- a/SPECS/kernel-rt/0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf +++ /dev/null @@ -1,29 +0,0 @@ -From 3338e742d86ce8f096e18376a7bbc6ecdf0b85ce Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Wed, 21 Aug 2024 08:28:53 +0000 -Subject: [PATCH 029/100] perf/x86/intel: Add PMU support for WildcatLake - -WildcatLake is a variant of PantherLake and shares same PMU features, -so directly reuse Pantherlake's code to enable PMU features for -WildcatLake. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 0ed9cb66b49e..b6ed211695ff 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -7956,6 +7956,7 @@ __init int intel_pmu_init(void) - break; - - case INTEL_PANTHERLAKE_L: -+ case INTEL_WILDCATLAKE_L: - pr_cont("Pantherlake Hybrid events, "); - name = "pantherlake_hybrid"; - goto lnl_common; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi b/SPECS/kernel-rt/0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi new file mode 100644 index 000000000..017a81b2c --- /dev/null +++ b/SPECS/kernel-rt/0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi @@ -0,0 +1,77 @@ +From 94caa6eca5e0c8c589f685694ec2bc9ad48d5469 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Mon, 17 Mar 2025 20:48:22 -0700 +Subject: [PATCH 29/44] x86: Remove the padding space at top of the init stack + +Because the owner of the init stack, init task, doesn't have any user +level context, there will NEVER be an actual pt_regs structure pushed +at top of the init stack. + +However a zeroed pt_regs structure is created at build time and kept +at top of the init stack for task_pt_regs() to function properly with +the init task in the same manner as a normal task with user level +context. + +Besides, task_pt_regs() no longer converts a fixed offset from top of +a task kernel stack to a pt_regs structure pointer, but rather returns +whatever in the thread_info.user_pt_regs field, which is initialized +at build time to '(struct pt_regs *)TOP_OF_INIT_STACK - 1' for the +init task. + +As a result, there is no point to reserve any padding space at top of +the init stack, so remove the padding space. + +Signed-off-by: Xin Li (Intel) +--- + arch/x86/include/asm/processor.h | 16 ++++++++++++++-- + arch/x86/kernel/vmlinux.lds.S | 7 +++++-- + 2 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h +index b7893536099a9..d7f438923d9c2 100644 +--- a/arch/x86/include/asm/processor.h ++++ b/arch/x86/include/asm/processor.h +@@ -640,8 +640,20 @@ static __always_inline void prefetchw(const void *x) + "m" (*(const char *)x)); + } + +-#define TOP_OF_INIT_STACK ((unsigned long)&init_stack + sizeof(init_stack) - \ +- TOP_OF_KERNEL_STACK_PADDING) ++extern unsigned long __end_init_stack[]; ++ ++/* ++ * No need to reserve extra padding space above the pt_regs structure ++ * at top of the init stack, because its owner init task doesn't have ++ * any user level context, thus there will NEVER be an actual pt_regs ++ * structure pushed at top of the init stack. ++ * ++ * However a zeroed pt_regs structure is created at build time and kept ++ * at top of the init stack for task_pt_regs() to function properly with ++ * the init task in the same manner as a normal task with user level ++ * context. ++ */ ++#define TOP_OF_INIT_STACK ((unsigned long)&__end_init_stack) + + #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) + +diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S +index d7af4a64c211b..3286c683d174c 100644 +--- a/arch/x86/kernel/vmlinux.lds.S ++++ b/arch/x86/kernel/vmlinux.lds.S +@@ -176,8 +176,11 @@ SECTIONS + /* init_task */ + INIT_TASK_DATA(THREAD_SIZE) + +- /* equivalent to task_pt_regs(&init_task) */ +- __top_init_kernel_stack = __end_init_stack - TOP_OF_KERNEL_STACK_PADDING - PTREGS_SIZE; ++ /* ++ * task_pt_regs(&init_task) is: ++ * '(struct pt_regs *)&__end_init_stack - 1' ++ */ ++ __top_init_kernel_stack = __end_init_stack - PTREGS_SIZE; + + #ifdef CONFIG_X86_32 + /* 32 bit has nosave before _edata */ +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0029-x86-fred-Allow-variable-sized-event-frame.nmi b/SPECS/kernel-rt/0029-x86-fred-Allow-variable-sized-event-frame.nmi deleted file mode 100644 index 47b92153c..000000000 --- a/SPECS/kernel-rt/0029-x86-fred-Allow-variable-sized-event-frame.nmi +++ /dev/null @@ -1,191 +0,0 @@ -From ff0b0040cf8eaf7226662dd875b6d987ea30f52d Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Mon, 17 Mar 2025 20:46:29 -0700 -Subject: [PATCH 29/44] x86/fred: Allow variable-sized event frame - -A FRED event frame could contain different amount of information for -different event types, or perhaps even for different instances of the -same event type. Thus the size of an event frame pushed by a FRED CPU -is not fixed and the address of the pt_regs structure that is used to -save a user level context of current task is not at a fixed offset -from top of current task kernel stack. - -Add a new field named 'user_pt_regs' in the thread_info structure to -save the address of user level context pt_regs structure, thus to -eliminate the need of any advance information of event frame size -and allow a FRED CPU to push variable-sized event frame. - -For IDT user level event delivery, a pt_regs structure is pushed by -hardware and software _always_ at a fixed offset from top of current -task kernel stack, so simply initialize user_pt_regs to point to the -pt_regs structure no matter whether one is pushed or not. - -While for FRED user level event delivery, user_pt_regs is updated with -a pt_regs structure pointer generated in asm_fred_entrypoint_user(). - -Suggested-by: H. Peter Anvin (Intel) -Signed-off-by: Xin Li (Intel) ---- - -Change in v3A: -* Add declaration for __top_init_kernel_stack[] (Intel lkp). - -Change in v3: -* Replace "(struct pt_regs *)TOP_OF_INIT_STACK - 1" with - (struct pt_regs *)__top_init_kernel_stack (Brian Gerst). ---- - arch/x86/entry/entry_fred.c | 10 ++++++++++ - arch/x86/include/asm/processor.h | 12 ++++++------ - arch/x86/include/asm/thread_info.h | 11 ++++++++--- - arch/x86/kernel/process.c | 22 ++++++++++++++++++++++ - include/linux/thread_info.h | 1 + - kernel/fork.c | 6 ++++++ - 6 files changed, 53 insertions(+), 9 deletions(-) - -diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c -index d80861a4cd00..f8847f2d6257 100644 ---- a/arch/x86/entry/entry_fred.c -+++ b/arch/x86/entry/entry_fred.c -@@ -229,6 +229,16 @@ __visible noinstr void fred_entry_from_user(struct pt_regs *regs) - /* Invalidate orig_ax so that syscall_get_nr() works correctly */ - regs->orig_ax = -1; - -+ /* -+ * A FRED event frame could contain different amount of information -+ * for different event types, or perhaps even for different instances -+ * of the same event type. Thus the size of an event frame pushed by -+ * a FRED CPU is not fixed and the address of the pt_regs structure -+ * that is used to save a user level context of current task is not -+ * at a fixed offset from top of current task stack. -+ */ -+ current->thread_info.user_pt_regs = regs; -+ - switch (regs->fred_ss.type) { - case EVENT_TYPE_EXTINT: - return fred_extint(regs); -diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h -index bde58f6510ac..769bffa0711e 100644 ---- a/arch/x86/include/asm/processor.h -+++ b/arch/x86/include/asm/processor.h -@@ -645,12 +645,12 @@ static __always_inline void prefetchw(const void *x) - - #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) - --#define task_pt_regs(task) \ --({ \ -- unsigned long __ptr = (unsigned long)task_stack_page(task); \ -- __ptr += THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; \ -- ((struct pt_regs *)__ptr) - 1; \ --}) -+/* -+ * Note, this can't be converted to an inline function as this header -+ * file defines 'struct thread_struct' which is used in the task_struct -+ * structure definition. -+ */ -+#define task_pt_regs(task) ((task)->thread_info.user_pt_regs) - - #ifdef CONFIG_X86_32 - #define INIT_THREAD { \ -diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h -index 9282465eea21..07c6a6a92c65 100644 ---- a/arch/x86/include/asm/thread_info.h -+++ b/arch/x86/include/asm/thread_info.h -@@ -56,6 +56,7 @@ - */ - #ifndef __ASSEMBLER__ - struct task_struct; -+struct pt_regs; - #include - #include - -@@ -66,11 +67,15 @@ struct thread_info { - #ifdef CONFIG_SMP - u32 cpu; /* current CPU */ - #endif -+ struct pt_regs *user_pt_regs; - }; - --#define INIT_THREAD_INFO(tsk) \ --{ \ -- .flags = 0, \ -+extern unsigned long __top_init_kernel_stack[]; -+ -+#define INIT_THREAD_INFO(tsk) \ -+{ \ -+ .flags = 0, \ -+ .user_pt_regs = (struct pt_regs *)__top_init_kernel_stack, \ - } - - #else /* !__ASSEMBLER__ */ -diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c -index 1b7960cf6eb0..94872f06458e 100644 ---- a/arch/x86/kernel/process.c -+++ b/arch/x86/kernel/process.c -@@ -104,6 +104,28 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) - return 0; - } - -+/* -+ * Initialize thread_info.user_pt_regs for IDT event delivery. -+ * -+ * For IDT user level event delivery, a pt_regs structure is pushed by both -+ * hardware and software and always resides at a fixed offset from top of -+ * current task kernel stack, thus thread_info.user_pt_regs is a per-task -+ * constant and NEVER changes after initialization. -+ * -+ * While for FRED user level event delivery, user_pt_regs is updated in -+ * fred_entry_from_user() immediately after user level event delivery. -+ * -+ * Note: thread_info.user_pt_regs of the init task is initialized at build -+ * time. -+ */ -+void arch_init_user_pt_regs(struct task_struct *tsk) -+{ -+ unsigned long init_sp = (unsigned long)task_empty_stack_pointer(tsk); -+ -+ init_sp -= TOP_OF_KERNEL_STACK_PADDING; -+ tsk->thread_info.user_pt_regs = (struct pt_regs *)init_sp - 1; -+} -+ - #ifdef CONFIG_X86_64 - void arch_release_task_struct(struct task_struct *tsk) - { -diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h -index dd925d84fa46..f95d38ed04d7 100644 ---- a/include/linux/thread_info.h -+++ b/include/linux/thread_info.h -@@ -225,6 +225,7 @@ void arch_task_cache_init(void); /* for CONFIG_SH */ - void arch_release_task_struct(struct task_struct *tsk); - int arch_dup_task_struct(struct task_struct *dst, - struct task_struct *src); -+void arch_init_user_pt_regs(struct task_struct *tsk); - - #endif /* __KERNEL__ */ - -diff --git a/kernel/fork.c b/kernel/fork.c -index af673856499d..0399720dd7b9 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -856,6 +856,10 @@ int __weak arch_dup_task_struct(struct task_struct *dst, - return 0; - } - -+void __weak arch_init_user_pt_regs(struct task_struct *tsk) -+{ -+} -+ - void set_task_stack_end_magic(struct task_struct *tsk) - { - unsigned long *stackend; -@@ -883,6 +887,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) - if (err) - goto free_tsk; - -+ arch_init_user_pt_regs(tsk); -+ - #ifdef CONFIG_THREAD_INFO_IN_TASK - refcount_set(&tsk->stack_refcount, 1); - #endif --- -2.43.0 - diff --git a/SPECS/kernel-rt/0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi b/SPECS/kernel-rt/0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi deleted file mode 100644 index b37d8e60b..000000000 --- a/SPECS/kernel-rt/0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi +++ /dev/null @@ -1,77 +0,0 @@ -From 24b55d01c54cca5de5e1f6d696489736f1c903d6 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Mon, 17 Mar 2025 20:48:22 -0700 -Subject: [PATCH 30/44] x86: Remove the padding space at top of the init stack - -Because the owner of the init stack, init task, doesn't have any user -level context, there will NEVER be an actual pt_regs structure pushed -at top of the init stack. - -However a zeroed pt_regs structure is created at build time and kept -at top of the init stack for task_pt_regs() to function properly with -the init task in the same manner as a normal task with user level -context. - -Besides, task_pt_regs() no longer converts a fixed offset from top of -a task kernel stack to a pt_regs structure pointer, but rather returns -whatever in the thread_info.user_pt_regs field, which is initialized -at build time to '(struct pt_regs *)TOP_OF_INIT_STACK - 1' for the -init task. - -As a result, there is no point to reserve any padding space at top of -the init stack, so remove the padding space. - -Signed-off-by: Xin Li (Intel) ---- - arch/x86/include/asm/processor.h | 16 ++++++++++++++-- - arch/x86/kernel/vmlinux.lds.S | 7 +++++-- - 2 files changed, 19 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h -index 769bffa0711e..39c969f6b211 100644 ---- a/arch/x86/include/asm/processor.h -+++ b/arch/x86/include/asm/processor.h -@@ -640,8 +640,20 @@ static __always_inline void prefetchw(const void *x) - "m" (*(const char *)x)); - } - --#define TOP_OF_INIT_STACK ((unsigned long)&init_stack + sizeof(init_stack) - \ -- TOP_OF_KERNEL_STACK_PADDING) -+extern unsigned long __end_init_stack[]; -+ -+/* -+ * No need to reserve extra padding space above the pt_regs structure -+ * at top of the init stack, because its owner init task doesn't have -+ * any user level context, thus there will NEVER be an actual pt_regs -+ * structure pushed at top of the init stack. -+ * -+ * However a zeroed pt_regs structure is created at build time and kept -+ * at top of the init stack for task_pt_regs() to function properly with -+ * the init task in the same manner as a normal task with user level -+ * context. -+ */ -+#define TOP_OF_INIT_STACK ((unsigned long)&__end_init_stack) - - #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) - -diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S -index 4fa0be732af1..0ca5bcffc6ed 100644 ---- a/arch/x86/kernel/vmlinux.lds.S -+++ b/arch/x86/kernel/vmlinux.lds.S -@@ -181,8 +181,11 @@ SECTIONS - /* init_task */ - INIT_TASK_DATA(THREAD_SIZE) - -- /* equivalent to task_pt_regs(&init_task) */ -- __top_init_kernel_stack = __end_init_stack - TOP_OF_KERNEL_STACK_PADDING - PTREGS_SIZE; -+ /* -+ * task_pt_regs(&init_task) is: -+ * '(struct pt_regs *)&__end_init_stack - 1' -+ */ -+ __top_init_kernel_stack = __end_init_stack - PTREGS_SIZE; - - #ifdef CONFIG_X86_32 - /* 32 bit has nosave before _edata */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi b/SPECS/kernel-rt/0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi new file mode 100644 index 000000000..4e739f197 --- /dev/null +++ b/SPECS/kernel-rt/0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi @@ -0,0 +1,106 @@ +From a9e1f47b9dcac0d8c774e54b75d928a0d78267e4 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Wed, 14 May 2025 07:07:55 -0700 +Subject: [PATCH 30/44] x86/fred: Provide separate IRQ vs. NMI wrappers for + entry from KVM + +Provide separate wrappers for forwarding IRQs vs NMIs from KVM in +anticipation of adding support for NMI source reporting, which will add +an NMI-only parameter, i.e. will further pollute the current API with a +param that is a hardcoded for one of the two call sites. + +Opportunistically tag the non-FRED NMI wrapper __always_inline, as the +compiler could theoretically generate a function call and trigger and a +(completely benign) "leaving noinstr" warning. + +Signed-off-by: Sean Christopherson +Signed-off-by: Sohil Mehta +--- +v7: New patch +--- + arch/x86/include/asm/fred.h | 30 ++++++++++++++++++++++-------- + arch/x86/kvm/vmx/vmx.c | 4 ++-- + 2 files changed, 24 insertions(+), 10 deletions(-) + +diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h +index 263fd5267f711..15d1c8e958378 100644 +--- a/arch/x86/include/asm/fred.h ++++ b/arch/x86/include/asm/fred.h +@@ -9,6 +9,7 @@ + #include + + #include ++#include + #include + #include + +@@ -71,15 +72,27 @@ __visible void fred_entry_from_user(struct pt_regs *regs); + __visible void fred_entry_from_kernel(struct pt_regs *regs); + __visible void __fred_entry_from_kvm(struct pt_regs *regs); + +-/* Can be called from noinstr code, thus __always_inline */ +-static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) ++/* Must be called from noinstr code, thus __always_inline */ ++static __always_inline void fred_nmi_from_kvm(void) + { + struct fred_ss ss = { +- .ss =__KERNEL_DS, +- .type = type, +- .vector = vector, +- .nmi = type == EVENT_TYPE_NMI, +- .lm = 1, ++ .ss = __KERNEL_DS, ++ .type = EVENT_TYPE_NMI, ++ .vector = NMI_VECTOR, ++ .nmi = true, ++ .lm = 1, ++ }; ++ ++ asm_fred_entry_from_kvm(ss); ++} ++ ++static inline void fred_irq_from_kvm(unsigned int vector) ++{ ++ struct fred_ss ss = { ++ .ss = __KERNEL_DS, ++ .type = EVENT_TYPE_EXTINT, ++ .vector = vector, ++ .lm = 1, + }; + + asm_fred_entry_from_kvm(ss); +@@ -110,7 +123,8 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret + static inline void cpu_init_fred_exceptions(void) { } + static inline void cpu_init_fred_rsps(void) { } + static inline void fred_complete_exception_setup(void) { } +-static inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { } ++static __always_inline void fred_nmi_from_kvm(void) { } ++static inline void fred_irq_from_kvm(unsigned int vector) { } + static inline void fred_sync_rsp0(unsigned long rsp0) { } + static inline void fred_update_rsp0(void) { } + #endif /* CONFIG_X86_FRED */ +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index e845e56b12c4c..d37251f4e8b2d 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7235,7 +7235,7 @@ static void handle_external_interrupt_irqoff(struct kvm_vcpu *vcpu, + */ + kvm_before_interrupt(vcpu, KVM_HANDLING_IRQ); + if (IS_ENABLED(CONFIG_X86_FRED)) +- fred_entry_from_kvm(EVENT_TYPE_EXTINT, vector); ++ fred_irq_from_kvm(vector); + else + vmx_do_interrupt_irqoff(gate_offset((gate_desc *)host_idt_base + vector)); + kvm_after_interrupt(vcpu); +@@ -7521,7 +7521,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) + + kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); + if (cpu_feature_enabled(X86_FEATURE_FRED)) +- fred_entry_from_kvm(EVENT_TYPE_NMI, NMI_VECTOR); ++ fred_nmi_from_kvm(); + else + vmx_do_nmi_irqoff(); + kvm_after_interrupt(vcpu); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf b/SPECS/kernel-rt/0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf deleted file mode 100644 index 039723391..000000000 --- a/SPECS/kernel-rt/0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf +++ /dev/null @@ -1,35 +0,0 @@ -From ba4e183fa2975441b9538062dae5cd8b4476ce9f Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Wed, 17 Aug 2022 05:31:27 -0700 -Subject: [PATCH 031/100] perf evsel: Update the hint for the usage of the load - latency event - -The current message is not providing enough information. It's hard for -a user to figure out what's the auxiliary event. - -Adding the event name of the auxiliary event in the hint. The user can -simply cut & paste. - -Suggested-by: Stephane Eranian -Signed-off-by: Kan Liang ---- - tools/perf/util/evsel.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c -index c89eeff51d28..0c516c8f2d4f 100644 ---- a/tools/perf/util/evsel.c -+++ b/tools/perf/util/evsel.c -@@ -3866,7 +3866,8 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, - break; - case ENODATA: - return scnprintf(msg, size, "Cannot collect data source with the load latency event alone. " -- "Please add an auxiliary event in front of the load latency event."); -+ "Please add an auxiliary event in front of the load latency event. " -+ "For example, -e {mem-loads-aux,%s}.", evsel__name(evsel)); - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi b/SPECS/kernel-rt/0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi new file mode 100644 index 000000000..cdb0269e9 --- /dev/null +++ b/SPECS/kernel-rt/0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi @@ -0,0 +1,113 @@ +From ec499a1323d06e705bd45308dcd03c469be29d34 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 9 Jun 2025 12:40:04 -0700 +Subject: [PATCH 31/44] x86/fred: Pass event data to the NMI entry point from + KVM + +Extend the FRED NMI entry point from KVM to take an extra argument to +allow KVM to invoke the FRED event dispatch framework with event data. + +This API is used to pass the NMI-source bitmap for NMI-induced VM exits. +Read the VMCS exit qualification field to get the NMI-source information +and store it as event data precisely in the format expected by the FRED +event framework. + +Read the VMCS exit qualification unconditionally since almost all +upcoming CPUs are expected to enable FRED and NMI-source together. In +the rare case that NMI-source isn't enabled, the extra VMREAD would be +harmless since the exit qualification is expected to be zero. + +Suggested-by: Sean Christopherson +Originally-by: Zeng Guang +Signed-off-by: Sohil Mehta +--- +v7: Pass the event data from KVM only for NMI. (Sean) + +v6: No change + +v5: Read the VMCS exit qualification unconditionally. (Sean) + Combine related patches into one. +--- + arch/x86/entry/entry_64_fred.S | 2 +- + arch/x86/include/asm/fred.h | 11 ++++++----- + arch/x86/kvm/vmx/vmx.c | 2 +- + 3 files changed, 8 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S +index fafbd3e68cb87..14b385551012f 100644 +--- a/arch/x86/entry/entry_64_fred.S ++++ b/arch/x86/entry/entry_64_fred.S +@@ -93,7 +93,7 @@ SYM_FUNC_START(asm_fred_entry_from_kvm) + * +--------+-----------------+ + */ + push $0 /* Reserved, must be 0 */ +- push $0 /* Event data, 0 for IRQ/NMI */ ++ push %rsi /* Event data for NMI */ + push %rdi /* fred_ss handed in by the caller */ + push %rbp + pushf +diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h +index 15d1c8e958378..18deb29f50501 100644 +--- a/arch/x86/include/asm/fred.h ++++ b/arch/x86/include/asm/fred.h +@@ -66,14 +66,14 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) + + void asm_fred_entrypoint_user(void); + void asm_fred_entrypoint_kernel(void); +-void asm_fred_entry_from_kvm(struct fred_ss); ++void asm_fred_entry_from_kvm(struct fred_ss ss, unsigned long edata); + + __visible void fred_entry_from_user(struct pt_regs *regs); + __visible void fred_entry_from_kernel(struct pt_regs *regs); + __visible void __fred_entry_from_kvm(struct pt_regs *regs); + + /* Must be called from noinstr code, thus __always_inline */ +-static __always_inline void fred_nmi_from_kvm(void) ++static __always_inline void fred_nmi_from_kvm(unsigned long edata) + { + struct fred_ss ss = { + .ss = __KERNEL_DS, +@@ -83,7 +83,7 @@ static __always_inline void fred_nmi_from_kvm(void) + .lm = 1, + }; + +- asm_fred_entry_from_kvm(ss); ++ asm_fred_entry_from_kvm(ss, edata); + } + + static inline void fred_irq_from_kvm(unsigned int vector) +@@ -95,7 +95,8 @@ static inline void fred_irq_from_kvm(unsigned int vector) + .lm = 1, + }; + +- asm_fred_entry_from_kvm(ss); ++ /* Event data is always zero for IRQ */ ++ asm_fred_entry_from_kvm(ss, 0); + } + + void cpu_init_fred_exceptions(void); +@@ -123,7 +124,7 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret + static inline void cpu_init_fred_exceptions(void) { } + static inline void cpu_init_fred_rsps(void) { } + static inline void fred_complete_exception_setup(void) { } +-static __always_inline void fred_nmi_from_kvm(void) { } ++static __always_inline void fred_nmi_from_kvm(unsigned long edata) { } + static inline void fred_irq_from_kvm(unsigned int vector) { } + static inline void fred_sync_rsp0(unsigned long rsp0) { } + static inline void fred_update_rsp0(void) { } +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index d37251f4e8b2d..8f1d105f333f1 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7521,7 +7521,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) + + kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); + if (cpu_feature_enabled(X86_FEATURE_FRED)) +- fred_nmi_from_kvm(); ++ fred_nmi_from_kvm(vmx_get_exit_qual(vcpu)); + else + vmx_do_nmi_irqoff(); + kvm_after_interrupt(vcpu); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi b/SPECS/kernel-rt/0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi deleted file mode 100644 index bdfcf734a..000000000 --- a/SPECS/kernel-rt/0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi +++ /dev/null @@ -1,106 +0,0 @@ -From 93a2531574a4a627a5088b29cfb4fee7282b97bf Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 14 May 2025 07:07:55 -0700 -Subject: [PATCH 31/44] x86/fred: Provide separate IRQ vs. NMI wrappers for - entry from KVM - -Provide separate wrappers for forwarding IRQs vs NMIs from KVM in -anticipation of adding support for NMI source reporting, which will add -an NMI-only parameter, i.e. will further pollute the current API with a -param that is a hardcoded for one of the two call sites. - -Opportunistically tag the non-FRED NMI wrapper __always_inline, as the -compiler could theoretically generate a function call and trigger and a -(completely benign) "leaving noinstr" warning. - -Signed-off-by: Sean Christopherson -Signed-off-by: Sohil Mehta ---- -v7: New patch ---- - arch/x86/include/asm/fred.h | 30 ++++++++++++++++++++++-------- - arch/x86/kvm/vmx/vmx.c | 4 ++-- - 2 files changed, 24 insertions(+), 10 deletions(-) - -diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h -index 263fd5267f71..15d1c8e95837 100644 ---- a/arch/x86/include/asm/fred.h -+++ b/arch/x86/include/asm/fred.h -@@ -9,6 +9,7 @@ - #include - - #include -+#include - #include - #include - -@@ -71,15 +72,27 @@ __visible void fred_entry_from_user(struct pt_regs *regs); - __visible void fred_entry_from_kernel(struct pt_regs *regs); - __visible void __fred_entry_from_kvm(struct pt_regs *regs); - --/* Can be called from noinstr code, thus __always_inline */ --static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) -+/* Must be called from noinstr code, thus __always_inline */ -+static __always_inline void fred_nmi_from_kvm(void) - { - struct fred_ss ss = { -- .ss =__KERNEL_DS, -- .type = type, -- .vector = vector, -- .nmi = type == EVENT_TYPE_NMI, -- .lm = 1, -+ .ss = __KERNEL_DS, -+ .type = EVENT_TYPE_NMI, -+ .vector = NMI_VECTOR, -+ .nmi = true, -+ .lm = 1, -+ }; -+ -+ asm_fred_entry_from_kvm(ss); -+} -+ -+static inline void fred_irq_from_kvm(unsigned int vector) -+{ -+ struct fred_ss ss = { -+ .ss = __KERNEL_DS, -+ .type = EVENT_TYPE_EXTINT, -+ .vector = vector, -+ .lm = 1, - }; - - asm_fred_entry_from_kvm(ss); -@@ -110,7 +123,8 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret - static inline void cpu_init_fred_exceptions(void) { } - static inline void cpu_init_fred_rsps(void) { } - static inline void fred_complete_exception_setup(void) { } --static inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { } -+static __always_inline void fred_nmi_from_kvm(void) { } -+static inline void fred_irq_from_kvm(unsigned int vector) { } - static inline void fred_sync_rsp0(unsigned long rsp0) { } - static inline void fred_update_rsp0(void) { } - #endif /* CONFIG_X86_FRED */ -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 090b49679925..4338e02c9229 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7256,7 +7256,7 @@ static void handle_external_interrupt_irqoff(struct kvm_vcpu *vcpu, - - kvm_before_interrupt(vcpu, KVM_HANDLING_IRQ); - if (cpu_feature_enabled(X86_FEATURE_FRED)) -- fred_entry_from_kvm(EVENT_TYPE_EXTINT, vector); -+ fred_irq_from_kvm(vector); - else - vmx_do_interrupt_irqoff(gate_offset((gate_desc *)host_idt_base + vector)); - kvm_after_interrupt(vcpu); -@@ -7542,7 +7542,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) - - kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); - if (cpu_feature_enabled(X86_FEATURE_FRED)) -- fred_entry_from_kvm(EVENT_TYPE_NMI, NMI_VECTOR); -+ fred_nmi_from_kvm(); - else - vmx_do_nmi_irqoff(); - kvm_after_interrupt(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf b/SPECS/kernel-rt/0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf deleted file mode 100644 index 437d12ae6..000000000 --- a/SPECS/kernel-rt/0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf +++ /dev/null @@ -1,31 +0,0 @@ -From 4ca9541349929f0caa8bc00ab966e2b36d835f8f Mon Sep 17 00:00:00 2001 -From: Zhenyu Wang -Date: Thu, 30 May 2024 11:19:23 +0000 -Subject: [PATCH 032/100] perf/x86/intel/cstate: Add Clearwater Forrest support - -Clearwater Forrest has same c-state residency counters like Sierra Forrest. -So this simply adds cpu model id for it. - -Cc: Artem Bityutskiy -Cc: Kan Liang -Reviewed-by: Kan Liang -Signed-off-by: Zhenyu Wang ---- - arch/x86/events/intel/cstate.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c -index ec753e39b007..a5f2e0be2337 100644 ---- a/arch/x86/events/intel/cstate.c -+++ b/arch/x86/events/intel/cstate.c -@@ -628,6 +628,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = { - X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &adl_cstates), - X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &srf_cstates), - X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &grr_cstates), -+ X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &srf_cstates), - - X86_MATCH_VFM(INTEL_ICELAKE_L, &icl_cstates), - X86_MATCH_VFM(INTEL_ICELAKE, &icl_cstates), --- -2.43.0 - diff --git a/SPECS/kernel-rt/0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi b/SPECS/kernel-rt/0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi new file mode 100644 index 000000000..dcde08e99 --- /dev/null +++ b/SPECS/kernel-rt/0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi @@ -0,0 +1,62 @@ +From 265183f7f2ee2e2634c6d2657e9bf9017eae2113 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Tue, 25 Mar 2025 18:17:06 +0000 +Subject: [PATCH 32/44] x86/cpufeatures: Add the CPUID feature bit for + NMI-source reporting + +NMI-source reporting is introduced to report the sources of NMIs with +FRED event delivery based on vectors in NMI interrupt messages or the +local APIC. This enables the kernel to avoid the latency incurred by +going over the entire NMI handler list and reduces ambiguity about the +source of an NMI. + +Enumerate NMI-source reporting in cpufeatures.h. Also, since NMI-source +reporting uses the FRED event dispatch framework, make it dependent on +FRED in the CPUID dependency table. This ensures that NMI-source +reporting gets disabled when FRED is disabled. + +NMI-source reporting is intended as a kernel feature and does not need +userspace enumeration or configuration. There is no need to expose it to +userspace through /proc/cpuinfo. + +Originally-by: Jacob Pan +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: No change. + +v5: Add NMI-source to the CPUID dependency table. + Do not expose NMI-source feature through /proc/cpuinfo. +--- + arch/x86/include/asm/cpufeatures.h | 1 + + arch/x86/kernel/cpu/cpuid-deps.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h +index 4091a776e37aa..b6a31d230a487 100644 +--- a/arch/x86/include/asm/cpufeatures.h ++++ b/arch/x86/include/asm/cpufeatures.h +@@ -322,6 +322,7 @@ + #define X86_FEATURE_FRED (12*32+17) /* "fred" Flexible Return and Event Delivery */ + #define X86_FEATURE_LKGS (12*32+18) /* Load "kernel" (userspace) GS */ + #define X86_FEATURE_WRMSRNS (12*32+19) /* Non-serializing WRMSR */ ++#define X86_FEATURE_NMI_SOURCE (12*32+20) /* NMI-Source reporting with FRED */ + #define X86_FEATURE_AMX_FP16 (12*32+21) /* AMX fp16 Support */ + #define X86_FEATURE_AVX_IFMA (12*32+23) /* Support for VPMADD52[H,L]UQ */ + #define X86_FEATURE_LAM (12*32+26) /* "lam" Linear Address Masking */ +diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c +index 46efcbd6afa41..87a334f639d51 100644 +--- a/arch/x86/kernel/cpu/cpuid-deps.c ++++ b/arch/x86/kernel/cpu/cpuid-deps.c +@@ -88,6 +88,7 @@ static const struct cpuid_dep cpuid_deps[] = { + { X86_FEATURE_AMX_INT8, X86_FEATURE_AMX_TILE }, + { X86_FEATURE_SHSTK, X86_FEATURE_XSAVES }, + { X86_FEATURE_FRED, X86_FEATURE_LKGS }, ++ { X86_FEATURE_NMI_SOURCE, X86_FEATURE_FRED }, + { X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL }, + {} + }; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi b/SPECS/kernel-rt/0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi deleted file mode 100644 index 99813b49e..000000000 --- a/SPECS/kernel-rt/0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi +++ /dev/null @@ -1,113 +0,0 @@ -From 92d3fbe4e179f178f01a25407284f25203660c7b Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 9 Jun 2025 12:40:04 -0700 -Subject: [PATCH 32/44] x86/fred: Pass event data to the NMI entry point from - KVM - -Extend the FRED NMI entry point from KVM to take an extra argument to -allow KVM to invoke the FRED event dispatch framework with event data. - -This API is used to pass the NMI-source bitmap for NMI-induced VM exits. -Read the VMCS exit qualification field to get the NMI-source information -and store it as event data precisely in the format expected by the FRED -event framework. - -Read the VMCS exit qualification unconditionally since almost all -upcoming CPUs are expected to enable FRED and NMI-source together. In -the rare case that NMI-source isn't enabled, the extra VMREAD would be -harmless since the exit qualification is expected to be zero. - -Suggested-by: Sean Christopherson -Originally-by: Zeng Guang -Signed-off-by: Sohil Mehta ---- -v7: Pass the event data from KVM only for NMI. (Sean) - -v6: No change - -v5: Read the VMCS exit qualification unconditionally. (Sean) - Combine related patches into one. ---- - arch/x86/entry/entry_64_fred.S | 2 +- - arch/x86/include/asm/fred.h | 11 ++++++----- - arch/x86/kvm/vmx/vmx.c | 2 +- - 3 files changed, 8 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S -index cb5ff2b1f6e7..1b6beb22a9b7 100644 ---- a/arch/x86/entry/entry_64_fred.S -+++ b/arch/x86/entry/entry_64_fred.S -@@ -93,7 +93,7 @@ SYM_FUNC_START(asm_fred_entry_from_kvm) - * +--------+-----------------+ - */ - push $0 /* Reserved, must be 0 */ -- push $0 /* Event data, 0 for IRQ/NMI */ -+ push %rsi /* Event data for NMI */ - push %rdi /* fred_ss handed in by the caller */ - push %rbp - pushf -diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h -index 15d1c8e95837..18deb29f5050 100644 ---- a/arch/x86/include/asm/fred.h -+++ b/arch/x86/include/asm/fred.h -@@ -66,14 +66,14 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) - - void asm_fred_entrypoint_user(void); - void asm_fred_entrypoint_kernel(void); --void asm_fred_entry_from_kvm(struct fred_ss); -+void asm_fred_entry_from_kvm(struct fred_ss ss, unsigned long edata); - - __visible void fred_entry_from_user(struct pt_regs *regs); - __visible void fred_entry_from_kernel(struct pt_regs *regs); - __visible void __fred_entry_from_kvm(struct pt_regs *regs); - - /* Must be called from noinstr code, thus __always_inline */ --static __always_inline void fred_nmi_from_kvm(void) -+static __always_inline void fred_nmi_from_kvm(unsigned long edata) - { - struct fred_ss ss = { - .ss = __KERNEL_DS, -@@ -83,7 +83,7 @@ static __always_inline void fred_nmi_from_kvm(void) - .lm = 1, - }; - -- asm_fred_entry_from_kvm(ss); -+ asm_fred_entry_from_kvm(ss, edata); - } - - static inline void fred_irq_from_kvm(unsigned int vector) -@@ -95,7 +95,8 @@ static inline void fred_irq_from_kvm(unsigned int vector) - .lm = 1, - }; - -- asm_fred_entry_from_kvm(ss); -+ /* Event data is always zero for IRQ */ -+ asm_fred_entry_from_kvm(ss, 0); - } - - void cpu_init_fred_exceptions(void); -@@ -123,7 +124,7 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret - static inline void cpu_init_fred_exceptions(void) { } - static inline void cpu_init_fred_rsps(void) { } - static inline void fred_complete_exception_setup(void) { } --static __always_inline void fred_nmi_from_kvm(void) { } -+static __always_inline void fred_nmi_from_kvm(unsigned long edata) { } - static inline void fred_irq_from_kvm(unsigned int vector) { } - static inline void fred_sync_rsp0(unsigned long rsp0) { } - static inline void fred_update_rsp0(void) { } -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 4338e02c9229..36afe1dba9f0 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7542,7 +7542,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) - - kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); - if (cpu_feature_enabled(X86_FEATURE_FRED)) -- fred_nmi_from_kvm(); -+ fred_nmi_from_kvm(vmx_get_exit_qual(vcpu)); - else - vmx_do_nmi_irqoff(); - kvm_after_interrupt(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf b/SPECS/kernel-rt/0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf deleted file mode 100644 index ab44599cb..000000000 --- a/SPECS/kernel-rt/0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf +++ /dev/null @@ -1,60 +0,0 @@ -From 97f6605d3a333f5d5be1044776127d60cf5ec84c Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:01 +0800 -Subject: [PATCH 033/100] KVM: x86/pmu: Correct typo "_COUTNERS" to "_COUNTERS" - -Fix typos. "_COUTNERS" -> "_COUNTERS". - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - arch/x86/include/asm/kvm_host.h | 8 ++++---- - arch/x86/kvm/vmx/pmu_intel.c | 6 +++--- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index f19a76d3ca0e..7fb2bdcf42a8 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -545,10 +545,10 @@ struct kvm_pmc { - #define KVM_MAX_NR_GP_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \ - KVM_MAX_NR_AMD_GP_COUNTERS) - --#define KVM_MAX_NR_INTEL_FIXED_COUTNERS 3 --#define KVM_MAX_NR_AMD_FIXED_COUTNERS 0 --#define KVM_MAX_NR_FIXED_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUTNERS, \ -- KVM_MAX_NR_AMD_FIXED_COUTNERS) -+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS 3 -+#define KVM_MAX_NR_AMD_FIXED_COUNTERS 0 -+#define KVM_MAX_NR_FIXED_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \ -+ KVM_MAX_NR_AMD_FIXED_COUNTERS) - - struct kvm_pmu { - u8 version; -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 0b173602821b..e8b37a38fbba 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -478,8 +478,8 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index) - }; - u64 eventsel; - -- BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUTNERS); -- BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUTNERS); -+ BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS); -+ BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS); - - /* - * Yell if perf reports support for a fixed counter but perf doesn't -@@ -625,7 +625,7 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu) - pmu->gp_counters[i].current_config = 0; - } - -- for (i = 0; i < KVM_MAX_NR_INTEL_FIXED_COUTNERS; i++) { -+ for (i = 0; i < KVM_MAX_NR_INTEL_FIXED_COUNTERS; i++) { - pmu->fixed_counters[i].type = KVM_PMC_FIXED; - pmu->fixed_counters[i].vcpu = vcpu; - pmu->fixed_counters[i].idx = i + KVM_FIXED_PMC_BASE_IDX; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi b/SPECS/kernel-rt/0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi deleted file mode 100644 index ba5728740..000000000 --- a/SPECS/kernel-rt/0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi +++ /dev/null @@ -1,62 +0,0 @@ -From 935fa17478d9e4794e9052128c8d933224d68fab Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Tue, 25 Mar 2025 18:17:06 +0000 -Subject: [PATCH 33/44] x86/cpufeatures: Add the CPUID feature bit for - NMI-source reporting - -NMI-source reporting is introduced to report the sources of NMIs with -FRED event delivery based on vectors in NMI interrupt messages or the -local APIC. This enables the kernel to avoid the latency incurred by -going over the entire NMI handler list and reduces ambiguity about the -source of an NMI. - -Enumerate NMI-source reporting in cpufeatures.h. Also, since NMI-source -reporting uses the FRED event dispatch framework, make it dependent on -FRED in the CPUID dependency table. This ensures that NMI-source -reporting gets disabled when FRED is disabled. - -NMI-source reporting is intended as a kernel feature and does not need -userspace enumeration or configuration. There is no need to expose it to -userspace through /proc/cpuinfo. - -Originally-by: Jacob Pan -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: No change. - -v5: Add NMI-source to the CPUID dependency table. - Do not expose NMI-source feature through /proc/cpuinfo. ---- - arch/x86/include/asm/cpufeatures.h | 1 + - arch/x86/kernel/cpu/cpuid-deps.c | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h -index 06fc0479a23f..6b20b92dcf2f 100644 ---- a/arch/x86/include/asm/cpufeatures.h -+++ b/arch/x86/include/asm/cpufeatures.h -@@ -322,6 +322,7 @@ - #define X86_FEATURE_FRED (12*32+17) /* "fred" Flexible Return and Event Delivery */ - #define X86_FEATURE_LKGS (12*32+18) /* Load "kernel" (userspace) GS */ - #define X86_FEATURE_WRMSRNS (12*32+19) /* Non-serializing WRMSR */ -+#define X86_FEATURE_NMI_SOURCE (12*32+20) /* NMI-Source reporting with FRED */ - #define X86_FEATURE_AMX_FP16 (12*32+21) /* AMX fp16 Support */ - #define X86_FEATURE_AVX_IFMA (12*32+23) /* Support for VPMADD52[H,L]UQ */ - #define X86_FEATURE_LAM (12*32+26) /* "lam" Linear Address Masking */ -diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c -index 46efcbd6afa4..87a334f639d5 100644 ---- a/arch/x86/kernel/cpu/cpuid-deps.c -+++ b/arch/x86/kernel/cpu/cpuid-deps.c -@@ -88,6 +88,7 @@ static const struct cpuid_dep cpuid_deps[] = { - { X86_FEATURE_AMX_INT8, X86_FEATURE_AMX_TILE }, - { X86_FEATURE_SHSTK, X86_FEATURE_XSAVES }, - { X86_FEATURE_FRED, X86_FEATURE_LKGS }, -+ { X86_FEATURE_NMI_SOURCE, X86_FEATURE_FRED }, - { X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL }, - {} - }; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi b/SPECS/kernel-rt/0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi new file mode 100644 index 000000000..8f78f3e67 --- /dev/null +++ b/SPECS/kernel-rt/0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi @@ -0,0 +1,307 @@ +From aab106dee39d86ec1bb627580d7b395179fc0d38 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Thu, 24 Apr 2025 23:16:45 +0000 +Subject: [PATCH 33/44] x86/nmi: Extend the registration interface to include + the NMI-source vector + +To prepare for NMI-source reporting, add a source vector argument to the +NMI handler registration interface. Later, this will be used to +register NMI handlers with a unique source vector that can be used to +identify the originator of the NMI. + +Vector 0 is reserved by the hardware for situations when a source vector +is not used while generating an NMI or the originator could not be +reliably identified. Registering an NMI handler with vector 0 is +equivalent to not using NMI-source reporting. + +For now, just extend the interface with no source information (vector 0) +for all the handlers. No functional change intended. + +Originally-by: Jacob Pan +Signed-off-by: Sohil Mehta +Reviewed-by: Xin Li (Intel) +--- +v7: Use an enum for defining source vectors (DaveH). + Use NMIS_NO_SOURCE instead of zero (DaveH). + Add review tag (Xin). + +v6: No change. + +v5: Split the patch into two parts. This one only extends the interface. +--- + arch/x86/events/amd/ibs.c | 2 +- + arch/x86/events/core.c | 2 +- + arch/x86/include/asm/nmi.h | 16 +++++++++++++++- + arch/x86/kernel/apic/hw_nmi.c | 3 +-- + arch/x86/kernel/cpu/mce/inject.c | 2 +- + arch/x86/kernel/cpu/mshyperv.c | 2 +- + arch/x86/kernel/kgdb.c | 6 ++---- + arch/x86/kernel/nmi_selftest.c | 6 +++--- + arch/x86/kernel/smp.c | 4 ++-- + arch/x86/platform/uv/uv_nmi.c | 4 ++-- + drivers/acpi/apei/ghes.c | 2 +- + drivers/char/ipmi/ipmi_watchdog.c | 3 +-- + drivers/edac/igen6_edac.c | 3 +-- + drivers/watchdog/hpwdt.c | 6 +++--- + 14 files changed, 35 insertions(+), 26 deletions(-) + +diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c +index 112f43b23ebf8..6f8f0d663f2fd 100644 +--- a/arch/x86/events/amd/ibs.c ++++ b/arch/x86/events/amd/ibs.c +@@ -1485,7 +1485,7 @@ static __init int perf_event_ibs_init(void) + if (ret) + goto err_op; + +- ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs"); ++ ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs", NMIS_NO_SOURCE); + if (ret) + goto err_nmi; + +diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c +index fa6c47b509897..c8a6175c48730 100644 +--- a/arch/x86/events/core.c ++++ b/arch/x86/events/core.c +@@ -2131,7 +2131,7 @@ static int __init init_hw_perf_events(void) + x86_pmu.config_mask = X86_RAW_EVENT_MASK; + + perf_events_lapic_init(); +- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI"); ++ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); + + unconstrained = (struct event_constraint) + __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, +diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h +index 79d88d12c8fbf..42820c4f59b9d 100644 +--- a/arch/x86/include/asm/nmi.h ++++ b/arch/x86/include/asm/nmi.h +@@ -48,12 +48,24 @@ enum { + + typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); + ++/** ++ * enum nmi_source_vectors - NMI-source vectors are used to identify the ++ * origin of an NMI and to route the NMI directly to the appropriate ++ * handler. ++ * ++ * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. ++ */ ++enum nmi_source_vectors { ++ NMIS_NO_SOURCE = 0, ++}; ++ + struct nmiaction { + struct list_head list; + nmi_handler_t handler; + u64 max_duration; + unsigned long flags; + const char *name; ++ enum nmi_source_vectors source_vector; + }; + + /** +@@ -62,6 +74,7 @@ struct nmiaction { + * @fn: The NMI handler + * @fg: Flags associated with the NMI handler + * @n: Name of the NMI handler ++ * @src: NMI-source vector for the NMI handler + * @init: Optional __init* attributes for struct nmiaction + * + * Adds the provided handler to the list of handlers for the specified +@@ -75,13 +88,14 @@ struct nmiaction { + * + * Return: 0 on success, or an error code on failure. + */ +-#define register_nmi_handler(t, fn, fg, n, init...) \ ++#define register_nmi_handler(t, fn, fg, n, src, init...)\ + ({ \ + static struct nmiaction init fn##_na = { \ + .list = LIST_HEAD_INIT(fn##_na.list), \ + .handler = (fn), \ + .name = (n), \ + .flags = (fg), \ ++ .source_vector = (src), \ + }; \ + __register_nmi_handler((t), &fn##_na); \ + }) +diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c +index 45af535c44a07..d09e771723ed2 100644 +--- a/arch/x86/kernel/apic/hw_nmi.c ++++ b/arch/x86/kernel/apic/hw_nmi.c +@@ -53,8 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); + + static int __init register_nmi_cpu_backtrace_handler(void) + { +- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, +- 0, "arch_bt"); ++ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); + return 0; + } + early_initcall(register_nmi_cpu_backtrace_handler); +diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c +index d02c4f556cd05..ba70ef8a1964f 100644 +--- a/arch/x86/kernel/cpu/mce/inject.c ++++ b/arch/x86/kernel/cpu/mce/inject.c +@@ -775,7 +775,7 @@ static int __init inject_init(void) + + debugfs_init(); + +- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); ++ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); + mce_register_injector_chain(&inject_nb); + + setup_inj_struct(&i_mce); +diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c +index c4febdbcfe4d8..a3b9b9beb338b 100644 +--- a/arch/x86/kernel/cpu/mshyperv.c ++++ b/arch/x86/kernel/cpu/mshyperv.c +@@ -557,7 +557,7 @@ static void __init ms_hyperv_init_platform(void) + } + + register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, +- "hv_nmi_unknown"); ++ "hv_nmi_unknown", NMIS_NO_SOURCE); + #endif + + #ifdef CONFIG_X86_IO_APIC +diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c +index 8b1a9733d13e3..80de0ac2e8ded 100644 +--- a/arch/x86/kernel/kgdb.c ++++ b/arch/x86/kernel/kgdb.c +@@ -602,13 +602,11 @@ int kgdb_arch_init(void) + if (retval) + goto out; + +- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, +- 0, "kgdb"); ++ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); + if (retval) + goto out1; + +- retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, +- 0, "kgdb"); ++ retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); + + if (retval) + goto out2; +diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c +index a010e9d062bf7..c4fffa868160f 100644 +--- a/arch/x86/kernel/nmi_selftest.c ++++ b/arch/x86/kernel/nmi_selftest.c +@@ -41,7 +41,7 @@ static void __init init_nmi_testsuite(void) + { + /* trap all the unknown NMIs we may generate */ + register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk", +- __initdata); ++ NMIS_NO_SOURCE, __initdata); + } + + static void __init cleanup_nmi_testsuite(void) +@@ -63,8 +63,8 @@ static void __init test_nmi_ipi(struct cpumask *mask) + { + unsigned long timeout; + +- if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, +- NMI_FLAG_FIRST, "nmi_selftest", __initdata)) { ++ if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, ++ "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { + nmi_fail = FAILURE; + return; + } +diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c +index b014e6d229f95..6c1d3ffbee6c9 100644 +--- a/arch/x86/kernel/smp.c ++++ b/arch/x86/kernel/smp.c +@@ -142,8 +142,8 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) + + static int register_stop_handler(void) + { +- return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, +- NMI_FLAG_FIRST, "smp_stop"); ++ return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, ++ "smp_stop", NMIS_NO_SOURCE); + } + + static void native_stop_other_cpus(int wait) +diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c +index 5c50e550ab635..5af368710f69d 100644 +--- a/arch/x86/platform/uv/uv_nmi.c ++++ b/arch/x86/platform/uv/uv_nmi.c +@@ -1029,10 +1029,10 @@ static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) + + static void uv_register_nmi_notifier(void) + { +- if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) ++ if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv", NMIS_NO_SOURCE)) + pr_warn("UV: NMI handler failed to register\n"); + +- if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) ++ if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping", NMIS_NO_SOURCE)) + pr_warn("UV: PING NMI handler failed to register\n"); + } + +diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c +index 97ee19f2cae06..38ea242ad3e39 100644 +--- a/drivers/acpi/apei/ghes.c ++++ b/drivers/acpi/apei/ghes.c +@@ -1443,7 +1443,7 @@ static void ghes_nmi_add(struct ghes *ghes) + { + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_nmi)) +- register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); ++ register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes", NMIS_NO_SOURCE); + list_add_rcu(&ghes->list, &ghes_nmi); + mutex_unlock(&ghes_list_mutex); + } +diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c +index a013ddbf14662..7e2fd81325c49 100644 +--- a/drivers/char/ipmi/ipmi_watchdog.c ++++ b/drivers/char/ipmi/ipmi_watchdog.c +@@ -1252,8 +1252,7 @@ static void check_parms(void) + } + } + if (do_nmi && !nmi_handler_registered) { +- rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, +- "ipmi"); ++ rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, "ipmi", NMIS_NO_SOURCE); + if (rv) { + pr_warn("Can't register nmi handler\n"); + return; +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 2fc59f9eed691..61c0e285bd9b6 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -1436,8 +1436,7 @@ static int register_err_handler(void) + return 0; + } + +- rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, +- 0, IGEN6_NMI_NAME); ++ rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, 0, IGEN6_NMI_NAME, NMIS_NO_SOURCE); + if (rc) { + igen6_printk(KERN_ERR, "Failed to register NMI handler\n"); + return rc; +diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c +index ae30e394d176e..90ae59a09270f 100644 +--- a/drivers/watchdog/hpwdt.c ++++ b/drivers/watchdog/hpwdt.c +@@ -242,13 +242,13 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) + /* + * Only one function can register for NMI_UNKNOWN + */ +- retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); ++ retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); + if (retval) + goto error; +- retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); ++ retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); + if (retval) + goto error1; +- retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); ++ retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); + if (retval) + goto error2; + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf b/SPECS/kernel-rt/0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf deleted file mode 100644 index a88fa4928..000000000 --- a/SPECS/kernel-rt/0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf +++ /dev/null @@ -1,45 +0,0 @@ -From 3a05d313a273338b2395f31ae2d3f0b6469746f5 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:02 +0800 -Subject: [PATCH 034/100] KVM: selftests: Add timing_info bit support in - vmx_pmu_caps_test - -A new bit PERF_CAPABILITIES[17] called "PEBS_TIMING_INFO" bit is added -to indicated if PEBS supports to record timing information in a new -"Retried Latency" field. - -Since KVM requires user can only set host consistent PEBS capabilities, -otherwise the PERF_CAPABILITIES setting would fail, so add -pebs_timing_info bit into "immutable_caps" to block host inconsistent -PEBS configuration and cause errors. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c -index a1f5ff45d518..f8deea220156 100644 ---- a/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c -+++ b/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c -@@ -29,7 +29,7 @@ static union perf_capabilities { - u64 pebs_baseline:1; - u64 perf_metrics:1; - u64 pebs_output_pt_available:1; -- u64 anythread_deprecated:1; -+ u64 pebs_timing_info:1; - }; - u64 capabilities; - } host_cap; -@@ -44,6 +44,7 @@ static const union perf_capabilities immutable_caps = { - .pebs_arch_reg = 1, - .pebs_format = -1, - .pebs_baseline = 1, -+ .pebs_timing_info = 1, - }; - - static const union perf_capabilities format_caps = { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0034-exfat-fix-refcount-leak-in-exfat_find.patch b/SPECS/kernel-rt/0034-exfat-fix-refcount-leak-in-exfat_find.patch deleted file mode 100644 index 99079c0ef..000000000 --- a/SPECS/kernel-rt/0034-exfat-fix-refcount-leak-in-exfat_find.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 9f3ac6b31ecb6a4f7a335c2c4db1b12c3d66ffcf Mon Sep 17 00:00:00 2001 -From: Shuhao Fu -Date: Tue, 21 Oct 2025 16:42:28 +0800 -Subject: [PATCH 34/51] exfat: fix refcount leak in exfat_find - -Fix refcount leaks in `exfat_find` related to `exfat_get_dentry_set`. - -Function `exfat_get_dentry_set` would increase the reference counter of -`es->bh` on success. Therefore, `exfat_put_dentry_set` must be called -after `exfat_get_dentry_set` to ensure refcount consistency. This patch -relocate two checks to avoid possible leaks. - -Fixes: 82ebecdc74ff ("exfat: fix improper check of dentry.stream.valid_size") -Fixes: 13940cef9549 ("exfat: add a check for invalid data size") -Signed-off-by: Shuhao Fu -Reviewed-by: Yuezhang Mo -Signed-off-by: Namjae Jeon ---- - fs/exfat/namei.c | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - -diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c -index d8964d736814..fcd0db6b4721 100644 ---- a/fs/exfat/namei.c -+++ b/fs/exfat/namei.c -@@ -645,16 +645,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname, - info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size); - info->size = le64_to_cpu(ep2->dentry.stream.size); - -- if (info->valid_size < 0) { -- exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size); -- return -EIO; -- } -- -- if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { -- exfat_fs_error(sb, "data size is invalid(%lld)", info->size); -- return -EIO; -- } -- - info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu); - if (!is_valid_cluster(sbi, info->start_clu) && info->size) { - exfat_warn(sb, "start_clu is invalid cluster(0x%x)", -@@ -692,6 +682,16 @@ static int exfat_find(struct inode *dir, struct qstr *qname, - 0); - exfat_put_dentry_set(&es, false); - -+ if (info->valid_size < 0) { -+ exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size); -+ return -EIO; -+ } -+ -+ if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { -+ exfat_fs_error(sb, "data size is invalid(%lld)", info->size); -+ return -EIO; -+ } -+ - if (ei->start_clu == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, - "non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)", --- -2.43.0 - diff --git a/SPECS/kernel-rt/0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi b/SPECS/kernel-rt/0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi new file mode 100644 index 000000000..e73b59d76 --- /dev/null +++ b/SPECS/kernel-rt/0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi @@ -0,0 +1,233 @@ +From 37e883d4f3ab4fab94feb9b7da02ae299794d04e Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Wed, 11 Jun 2025 23:10:01 -0700 +Subject: [PATCH 34/44] x86/nmi: Assign and register NMI-source vectors + +Prior to NMI-source support, the vector information was ignored by the +hardware while delivering NMIs. With NMI-source reporting, the initial +architecture supports a 16-bit source bitmap to identify the source of +the NMI. Upon receiving an NMI, this bitmap is delivered to the kernel +as part of the FRED event delivery mechanism. + +Assign a vector space of 1-15 that is specific to NMI-source and +independent of the IDT vector space. Though unlikely, the hardware may +extend the NMI-source bitmap in the future. Add a code comment to +clarify how the kernel support can be easily extended. + +Being a bitmap, the NMI-source vectors do not have any inherent priority +associated with them. The order of executing the NMI handlers is up to +the kernel. Existing NMI handling already has a priority mechanism for +the NMI handlers, with CPU-specific (NMI_LOCAL) handlers executed first, +followed by platform NMI handlers and unknown NMI (NMI_UNKNOWN) handlers +being last. Within each of these NMI types, the handlers registered with +NMI_FLAG_FIRST are given priority. + +NMI-source follows the same priority scheme to avoid unnecessary +complexity. Therefore, the NMI-source vectors are assigned arbitrarily, +except for vector 2. + +Vector 2 is reserved for external NMIs corresponding to the Local APIC - +LINT1 pin. Some third-party chipsets may send NMI messages with a fixed +vector value of 2. Using vector 2 for something else would lead to +confusion about the exact source. Do not assign it to any handler. + +NMI-source vectors are only assigned for NMI_LOCAL type handlers. +Platform NMI handlers have a single handler registered per type. They +don't need additional source information to differentiate among them. + +Use the assigned vectors to register the respective NMI handlers. Warn +if the vector values are unexpected. + +A couple of NMI handlers, such as the microcode rendezvous and the crash +reboot, do not use the typical NMI registration interface. Leave them +as-is for now. + +Originally-by: Jacob Pan +Signed-off-by: Sohil Mehta +Reviewed-by: Xin Li (Intel) +--- +v7: Use an enum to define the NMI-source vectors. (DaveH) + Add a BUILD_BUG_ON to validate the allocated vector count. (DaveH) + Add review tag from Xin. + +v6: Store source vector unconditionally. (PeterZ) + Add a warning for unexpected source vector values. (PeterZ) + +v5: Move the vector defines to nmi.h. + Combine vector allocation and registration into one patch. + Simplify NMI vector names. + Describe usage of vector 2 for external NMIs. + Get rid of vector priorities. +--- + arch/x86/events/core.c | 2 +- + arch/x86/include/asm/nmi.h | 46 ++++++++++++++++++++++++++++++++ + arch/x86/kernel/apic/hw_nmi.c | 2 +- + arch/x86/kernel/cpu/mce/inject.c | 2 +- + arch/x86/kernel/kgdb.c | 2 +- + arch/x86/kernel/nmi.c | 7 +++++ + arch/x86/kernel/nmi_selftest.c | 2 +- + arch/x86/kernel/smp.c | 2 +- + 8 files changed, 59 insertions(+), 6 deletions(-) + +diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c +index c8a6175c48730..e0efe4c00444e 100644 +--- a/arch/x86/events/core.c ++++ b/arch/x86/events/core.c +@@ -2131,7 +2131,7 @@ static int __init init_hw_perf_events(void) + x86_pmu.config_mask = X86_RAW_EVENT_MASK; + + perf_events_lapic_init(); +- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); ++ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_VECTOR_PMI); + + unconstrained = (struct event_constraint) + __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, +diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h +index 42820c4f59b9d..a48958a236fd4 100644 +--- a/arch/x86/include/asm/nmi.h ++++ b/arch/x86/include/asm/nmi.h +@@ -53,12 +53,58 @@ typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); + * origin of an NMI and to route the NMI directly to the appropriate + * handler. + * ++ * On CPUs that support NMI-source reporting with FRED, receiving an NMI ++ * with a valid vector sets the corresponding bit in the NMI-source ++ * bitmap. The bitmap is delivered as FRED event data on the stack. ++ * ++ * Multiple NMIs are coalesced in the NMI-source bitmap until the next ++ * NMI delivery. If an NMI is received without a vector or beyond the ++ * defined range, the CPU sets bit 0 of the NMI-source bitmap. ++ * ++ * Third-party chipsets may send NMI messages with a fixed vector of 2. ++ * Using vector 2 for some other purpose would cause confusion between ++ * those external NMI messages and the other purpose. Avoid using it. ++ * ++ * The vectors are in no particular priority order. Add new vector ++ * assignments sequentially in the list below before the COUNT. ++ * + * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. ++ * @NMIS_VECTOR_TEST: NMI selftest. ++ * @NMIS_VECTOR_EXTERNAL: Reserved to match external NMI vector 2. ++ * @NMIS_VECTOR_SMP_STOP: Panic stop CPU. ++ * @NMIS_VECTOR_BT: CPU backtrace. ++ * @NMIS_VECTOR_KGDB: Kernel debugger. ++ * @NMIS_VECTOR_MCE: MCE injection. ++ * @NMIS_VECTOR_PMI: PerfMon counters. ++ * ++ * @NMIS_VECTOR_COUNT: Count of the defined vectors. + */ + enum nmi_source_vectors { + NMIS_NO_SOURCE = 0, ++ NMIS_VECTOR_TEST, ++ NMIS_VECTOR_EXTERNAL = 2, ++ NMIS_VECTOR_SMP_STOP, ++ NMIS_VECTOR_BT, ++ NMIS_VECTOR_KGDB, ++ NMIS_VECTOR_MCE, ++ NMIS_VECTOR_PMI, ++ ++ NMIS_VECTOR_COUNT + }; + ++/* ++ * The early (and likely all future) hardware implementations of ++ * NMI-source reporting would only support a 16-bit source bitmap, with ++ * 1-15 being valid source vectors. ++ * ++ * If the hardware ever supports a larger bitmap, the kernel support can ++ * easily be extended to 64 bits by modifying the MAX below. However, ++ * care must be taken to reallocate the latency sensitive NMI sources ++ * within the first 15 vectors. Any source vector beyond the supported ++ * maximum on prior systems would set bit 0 in the NMI-source bitmap. ++ */ ++#define NMIS_VECTORS_MAX 16 ++ + struct nmiaction { + struct list_head list; + nmi_handler_t handler; +diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c +index d09e771723ed2..4e04f13d2de98 100644 +--- a/arch/x86/kernel/apic/hw_nmi.c ++++ b/arch/x86/kernel/apic/hw_nmi.c +@@ -53,7 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); + + static int __init register_nmi_cpu_backtrace_handler(void) + { +- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); ++ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_VECTOR_BT); + return 0; + } + early_initcall(register_nmi_cpu_backtrace_handler); +diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c +index ba70ef8a1964f..320068e01c22d 100644 +--- a/arch/x86/kernel/cpu/mce/inject.c ++++ b/arch/x86/kernel/cpu/mce/inject.c +@@ -775,7 +775,7 @@ static int __init inject_init(void) + + debugfs_init(); + +- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); ++ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_VECTOR_MCE); + mce_register_injector_chain(&inject_nb); + + setup_inj_struct(&i_mce); +diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c +index 80de0ac2e8ded..788b83e9404b6 100644 +--- a/arch/x86/kernel/kgdb.c ++++ b/arch/x86/kernel/kgdb.c +@@ -602,7 +602,7 @@ int kgdb_arch_init(void) + if (retval) + goto out; + +- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); ++ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_VECTOR_KGDB); + if (retval) + goto out1; + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index be93ec7255bfc..8071ad32aa119 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -182,6 +182,13 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action) + if (WARN_ON_ONCE(!action->handler || !list_empty(&action->list))) + return -EINVAL; + ++ /* NMI-source reporting should only be used for NMI_LOCAL */ ++ WARN_ON_ONCE((type != NMI_LOCAL) && (action->source_vector != NMIS_NO_SOURCE)); ++ ++ /* Check for valid vector values. See comment above NMIS_VECTORS_MAX */ ++ BUILD_BUG_ON(NMIS_VECTOR_COUNT > NMIS_VECTORS_MAX); ++ WARN_ON_ONCE(action->source_vector >= NMIS_VECTOR_COUNT); ++ + raw_spin_lock_irqsave(&desc->lock, flags); + + /* +diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c +index c4fffa868160f..f3918888e4942 100644 +--- a/arch/x86/kernel/nmi_selftest.c ++++ b/arch/x86/kernel/nmi_selftest.c +@@ -64,7 +64,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) + unsigned long timeout; + + if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, +- "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { ++ "nmi_selftest", NMIS_VECTOR_TEST, __initdata)) { + nmi_fail = FAILURE; + return; + } +diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c +index 6c1d3ffbee6c9..694e31bf445c5 100644 +--- a/arch/x86/kernel/smp.c ++++ b/arch/x86/kernel/smp.c +@@ -143,7 +143,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) + static int register_stop_handler(void) + { + return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, +- "smp_stop", NMIS_NO_SOURCE); ++ "smp_stop", NMIS_VECTOR_SMP_STOP); + } + + static void native_stop_other_cpus(int wait) +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi b/SPECS/kernel-rt/0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi deleted file mode 100644 index b52e2ceb2..000000000 --- a/SPECS/kernel-rt/0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi +++ /dev/null @@ -1,307 +0,0 @@ -From a07868c89eecc5d557fbe808e962adc61138e384 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Thu, 24 Apr 2025 23:16:45 +0000 -Subject: [PATCH 34/44] x86/nmi: Extend the registration interface to include - the NMI-source vector - -To prepare for NMI-source reporting, add a source vector argument to the -NMI handler registration interface. Later, this will be used to -register NMI handlers with a unique source vector that can be used to -identify the originator of the NMI. - -Vector 0 is reserved by the hardware for situations when a source vector -is not used while generating an NMI or the originator could not be -reliably identified. Registering an NMI handler with vector 0 is -equivalent to not using NMI-source reporting. - -For now, just extend the interface with no source information (vector 0) -for all the handlers. No functional change intended. - -Originally-by: Jacob Pan -Signed-off-by: Sohil Mehta -Reviewed-by: Xin Li (Intel) ---- -v7: Use an enum for defining source vectors (DaveH). - Use NMIS_NO_SOURCE instead of zero (DaveH). - Add review tag (Xin). - -v6: No change. - -v5: Split the patch into two parts. This one only extends the interface. ---- - arch/x86/events/amd/ibs.c | 2 +- - arch/x86/events/core.c | 2 +- - arch/x86/include/asm/nmi.h | 16 +++++++++++++++- - arch/x86/kernel/apic/hw_nmi.c | 3 +-- - arch/x86/kernel/cpu/mce/inject.c | 2 +- - arch/x86/kernel/cpu/mshyperv.c | 2 +- - arch/x86/kernel/kgdb.c | 6 ++---- - arch/x86/kernel/nmi_selftest.c | 6 +++--- - arch/x86/kernel/smp.c | 4 ++-- - arch/x86/platform/uv/uv_nmi.c | 4 ++-- - drivers/acpi/apei/ghes.c | 2 +- - drivers/char/ipmi/ipmi_watchdog.c | 3 +-- - drivers/edac/igen6_edac.c | 3 +-- - drivers/watchdog/hpwdt.c | 6 +++--- - 14 files changed, 35 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c -index 112f43b23ebf..6f8f0d663f2f 100644 ---- a/arch/x86/events/amd/ibs.c -+++ b/arch/x86/events/amd/ibs.c -@@ -1485,7 +1485,7 @@ static __init int perf_event_ibs_init(void) - if (ret) - goto err_op; - -- ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs"); -+ ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs", NMIS_NO_SOURCE); - if (ret) - goto err_nmi; - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 68d0bea0f62e..c7205c7c1376 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -2253,7 +2253,7 @@ static int __init init_hw_perf_events(void) - x86_pmu.config_mask = X86_RAW_EVENT_MASK; - - perf_events_lapic_init(); -- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI"); -+ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); - - unconstrained = (struct event_constraint) - __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, -diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h -index 79d88d12c8fb..42820c4f59b9 100644 ---- a/arch/x86/include/asm/nmi.h -+++ b/arch/x86/include/asm/nmi.h -@@ -48,12 +48,24 @@ enum { - - typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); - -+/** -+ * enum nmi_source_vectors - NMI-source vectors are used to identify the -+ * origin of an NMI and to route the NMI directly to the appropriate -+ * handler. -+ * -+ * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. -+ */ -+enum nmi_source_vectors { -+ NMIS_NO_SOURCE = 0, -+}; -+ - struct nmiaction { - struct list_head list; - nmi_handler_t handler; - u64 max_duration; - unsigned long flags; - const char *name; -+ enum nmi_source_vectors source_vector; - }; - - /** -@@ -62,6 +74,7 @@ struct nmiaction { - * @fn: The NMI handler - * @fg: Flags associated with the NMI handler - * @n: Name of the NMI handler -+ * @src: NMI-source vector for the NMI handler - * @init: Optional __init* attributes for struct nmiaction - * - * Adds the provided handler to the list of handlers for the specified -@@ -75,13 +88,14 @@ struct nmiaction { - * - * Return: 0 on success, or an error code on failure. - */ --#define register_nmi_handler(t, fn, fg, n, init...) \ -+#define register_nmi_handler(t, fn, fg, n, src, init...)\ - ({ \ - static struct nmiaction init fn##_na = { \ - .list = LIST_HEAD_INIT(fn##_na.list), \ - .handler = (fn), \ - .name = (n), \ - .flags = (fg), \ -+ .source_vector = (src), \ - }; \ - __register_nmi_handler((t), &fn##_na); \ - }) -diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c -index 45af535c44a0..d09e771723ed 100644 ---- a/arch/x86/kernel/apic/hw_nmi.c -+++ b/arch/x86/kernel/apic/hw_nmi.c -@@ -53,8 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); - - static int __init register_nmi_cpu_backtrace_handler(void) - { -- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, -- 0, "arch_bt"); -+ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); - return 0; - } - early_initcall(register_nmi_cpu_backtrace_handler); -diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c -index d02c4f556cd0..ba70ef8a1964 100644 ---- a/arch/x86/kernel/cpu/mce/inject.c -+++ b/arch/x86/kernel/cpu/mce/inject.c -@@ -775,7 +775,7 @@ static int __init inject_init(void) - - debugfs_init(); - -- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); -+ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); - mce_register_injector_chain(&inject_nb); - - setup_inj_struct(&i_mce); -diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c -index c78f860419d6..c093f7baab6a 100644 ---- a/arch/x86/kernel/cpu/mshyperv.c -+++ b/arch/x86/kernel/cpu/mshyperv.c -@@ -550,7 +550,7 @@ static void __init ms_hyperv_init_platform(void) - } - - register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, -- "hv_nmi_unknown"); -+ "hv_nmi_unknown", NMIS_NO_SOURCE); - #endif - - #ifdef CONFIG_X86_IO_APIC -diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c -index 8b1a9733d13e..80de0ac2e8de 100644 ---- a/arch/x86/kernel/kgdb.c -+++ b/arch/x86/kernel/kgdb.c -@@ -602,13 +602,11 @@ int kgdb_arch_init(void) - if (retval) - goto out; - -- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, -- 0, "kgdb"); -+ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); - if (retval) - goto out1; - -- retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, -- 0, "kgdb"); -+ retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); - - if (retval) - goto out2; -diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c -index a010e9d062bf..c4fffa868160 100644 ---- a/arch/x86/kernel/nmi_selftest.c -+++ b/arch/x86/kernel/nmi_selftest.c -@@ -41,7 +41,7 @@ static void __init init_nmi_testsuite(void) - { - /* trap all the unknown NMIs we may generate */ - register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk", -- __initdata); -+ NMIS_NO_SOURCE, __initdata); - } - - static void __init cleanup_nmi_testsuite(void) -@@ -63,8 +63,8 @@ static void __init test_nmi_ipi(struct cpumask *mask) - { - unsigned long timeout; - -- if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, -- NMI_FLAG_FIRST, "nmi_selftest", __initdata)) { -+ if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, -+ "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { - nmi_fail = FAILURE; - return; - } -diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c -index b014e6d229f9..6c1d3ffbee6c 100644 ---- a/arch/x86/kernel/smp.c -+++ b/arch/x86/kernel/smp.c -@@ -142,8 +142,8 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) - - static int register_stop_handler(void) - { -- return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, -- NMI_FLAG_FIRST, "smp_stop"); -+ return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, -+ "smp_stop", NMIS_NO_SOURCE); - } - - static void native_stop_other_cpus(int wait) -diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c -index 5c50e550ab63..5af368710f69 100644 ---- a/arch/x86/platform/uv/uv_nmi.c -+++ b/arch/x86/platform/uv/uv_nmi.c -@@ -1029,10 +1029,10 @@ static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) - - static void uv_register_nmi_notifier(void) - { -- if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) -+ if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv", NMIS_NO_SOURCE)) - pr_warn("UV: NMI handler failed to register\n"); - -- if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) -+ if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping", NMIS_NO_SOURCE)) - pr_warn("UV: PING NMI handler failed to register\n"); - } - -diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c -index a0d54993edb3..cbfb53250597 100644 ---- a/drivers/acpi/apei/ghes.c -+++ b/drivers/acpi/apei/ghes.c -@@ -1445,7 +1445,7 @@ static void ghes_nmi_add(struct ghes *ghes) - { - mutex_lock(&ghes_list_mutex); - if (list_empty(&ghes_nmi)) -- register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); -+ register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes", NMIS_NO_SOURCE); - list_add_rcu(&ghes->list, &ghes_nmi); - mutex_unlock(&ghes_list_mutex); - } -diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c -index a013ddbf1466..7e2fd81325c4 100644 ---- a/drivers/char/ipmi/ipmi_watchdog.c -+++ b/drivers/char/ipmi/ipmi_watchdog.c -@@ -1252,8 +1252,7 @@ static void check_parms(void) - } - } - if (do_nmi && !nmi_handler_registered) { -- rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, -- "ipmi"); -+ rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, "ipmi", NMIS_NO_SOURCE); - if (rv) { - pr_warn("Can't register nmi handler\n"); - return; -diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c -index 7968d3126f0e..d2423bab80d8 100644 ---- a/drivers/edac/igen6_edac.c -+++ b/drivers/edac/igen6_edac.c -@@ -1475,8 +1475,7 @@ static int register_err_handler(void) - return 0; - } - -- rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, -- 0, IGEN6_NMI_NAME); -+ rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, 0, IGEN6_NMI_NAME, NMIS_NO_SOURCE); - if (rc) { - igen6_printk(KERN_ERR, "Failed to register NMI handler\n"); - return rc; -diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c -index ae30e394d176..90ae59a09270 100644 ---- a/drivers/watchdog/hpwdt.c -+++ b/drivers/watchdog/hpwdt.c -@@ -242,13 +242,13 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) - /* - * Only one function can register for NMI_UNKNOWN - */ -- retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); -+ retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); - if (retval) - goto error; -- retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); -+ retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); - if (retval) - goto error1; -- retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); -+ retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); - if (retval) - goto error2; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf b/SPECS/kernel-rt/0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf deleted file mode 100644 index b547fb0b2..000000000 --- a/SPECS/kernel-rt/0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf +++ /dev/null @@ -1,152 +0,0 @@ -From ba9cccf06b726564ba30e62fe3ddfa4800d51283 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:03 +0800 -Subject: [PATCH 035/100] KVM: Selftests: Validate more arch-events in - pmu_counters_test - -Clearwater Forest introduces 5 new architectural events (4 topdown -level 1 metrics events and LBR inserts event). This patch supports -to validate these 5 newly added events. The detailed info about these -5 events can be found in SDM section 21.2.7 "Pre-defined Architectural - Performance Events". - -It becomes unrealistic to traverse all possible combinations of -unavailable events mask (may need dozens of minutes to finish all -possible combination validation). So only limit unavailable events mask -traverse to the first 8 arch-events. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/include/x86/pmu.h | 10 +++++++++ - .../selftests/kvm/include/x86/processor.h | 7 +++++- - tools/testing/selftests/kvm/lib/x86/pmu.c | 5 +++++ - .../selftests/kvm/x86/pmu_counters_test.c | 22 ++++++++++++++----- - 4 files changed, 38 insertions(+), 6 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h -index 3c10c4dc0ae8..2aabda2da002 100644 ---- a/tools/testing/selftests/kvm/include/x86/pmu.h -+++ b/tools/testing/selftests/kvm/include/x86/pmu.h -@@ -61,6 +61,11 @@ - #define INTEL_ARCH_BRANCHES_RETIRED RAW_EVENT(0xc4, 0x00) - #define INTEL_ARCH_BRANCHES_MISPREDICTED RAW_EVENT(0xc5, 0x00) - #define INTEL_ARCH_TOPDOWN_SLOTS RAW_EVENT(0xa4, 0x01) -+#define INTEL_ARCH_TOPDOWN_BE_BOUND RAW_EVENT(0xa4, 0x02) -+#define INTEL_ARCH_TOPDOWN_BAD_SPEC RAW_EVENT(0x73, 0x00) -+#define INTEL_ARCH_TOPDOWN_FE_BOUND RAW_EVENT(0x9c, 0x01) -+#define INTEL_ARCH_TOPDOWN_RETIRING RAW_EVENT(0xc2, 0x02) -+#define INTEL_ARCH_LBR_INSERTS RAW_EVENT(0xe4, 0x01) - - #define AMD_ZEN_CORE_CYCLES RAW_EVENT(0x76, 0x00) - #define AMD_ZEN_INSTRUCTIONS_RETIRED RAW_EVENT(0xc0, 0x00) -@@ -80,6 +85,11 @@ enum intel_pmu_architectural_events { - INTEL_ARCH_BRANCHES_RETIRED_INDEX, - INTEL_ARCH_BRANCHES_MISPREDICTED_INDEX, - INTEL_ARCH_TOPDOWN_SLOTS_INDEX, -+ INTEL_ARCH_TOPDOWN_BE_BOUND_INDEX, -+ INTEL_ARCH_TOPDOWN_BAD_SPEC_INDEX, -+ INTEL_ARCH_TOPDOWN_FE_BOUND_INDEX, -+ INTEL_ARCH_TOPDOWN_RETIRING_INDEX, -+ INTEL_ARCH_LBR_INSERTS_INDEX, - NR_INTEL_ARCH_EVENTS, - }; - -diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h -index 2efb05c2f2fb..232964f2a687 100644 ---- a/tools/testing/selftests/kvm/include/x86/processor.h -+++ b/tools/testing/selftests/kvm/include/x86/processor.h -@@ -265,7 +265,7 @@ struct kvm_x86_cpu_property { - #define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15) - #define X86_PROPERTY_PMU_GP_COUNTERS_BIT_WIDTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 16, 23) - #define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31) --#define X86_PROPERTY_PMU_EVENTS_MASK KVM_X86_CPU_PROPERTY(0xa, 0, EBX, 0, 7) -+#define X86_PROPERTY_PMU_EVENTS_MASK KVM_X86_CPU_PROPERTY(0xa, 0, EBX, 0, 12) - #define X86_PROPERTY_PMU_FIXED_COUNTERS_BITMASK KVM_X86_CPU_PROPERTY(0xa, 0, ECX, 0, 31) - #define X86_PROPERTY_PMU_NR_FIXED_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EDX, 0, 4) - #define X86_PROPERTY_PMU_FIXED_COUNTERS_BIT_WIDTH KVM_X86_CPU_PROPERTY(0xa, 0, EDX, 5, 12) -@@ -332,6 +332,11 @@ struct kvm_x86_pmu_feature { - #define X86_PMU_FEATURE_BRANCH_INSNS_RETIRED KVM_X86_PMU_FEATURE(EBX, 5) - #define X86_PMU_FEATURE_BRANCHES_MISPREDICTED KVM_X86_PMU_FEATURE(EBX, 6) - #define X86_PMU_FEATURE_TOPDOWN_SLOTS KVM_X86_PMU_FEATURE(EBX, 7) -+#define X86_PMU_FEATURE_TOPDOWN_BE_BOUND KVM_X86_PMU_FEATURE(EBX, 8) -+#define X86_PMU_FEATURE_TOPDOWN_BAD_SPEC KVM_X86_PMU_FEATURE(EBX, 9) -+#define X86_PMU_FEATURE_TOPDOWN_FE_BOUND KVM_X86_PMU_FEATURE(EBX, 10) -+#define X86_PMU_FEATURE_TOPDOWN_RETIRING KVM_X86_PMU_FEATURE(EBX, 11) -+#define X86_PMU_FEATURE_LBR_INSERTS KVM_X86_PMU_FEATURE(EBX, 12) - - #define X86_PMU_FEATURE_INSNS_RETIRED_FIXED KVM_X86_PMU_FEATURE(ECX, 0) - #define X86_PMU_FEATURE_CPU_CYCLES_FIXED KVM_X86_PMU_FEATURE(ECX, 1) -diff --git a/tools/testing/selftests/kvm/lib/x86/pmu.c b/tools/testing/selftests/kvm/lib/x86/pmu.c -index f31f0427c17c..5ab44bf54773 100644 ---- a/tools/testing/selftests/kvm/lib/x86/pmu.c -+++ b/tools/testing/selftests/kvm/lib/x86/pmu.c -@@ -19,6 +19,11 @@ const uint64_t intel_pmu_arch_events[] = { - INTEL_ARCH_BRANCHES_RETIRED, - INTEL_ARCH_BRANCHES_MISPREDICTED, - INTEL_ARCH_TOPDOWN_SLOTS, -+ INTEL_ARCH_TOPDOWN_BE_BOUND, -+ INTEL_ARCH_TOPDOWN_BAD_SPEC, -+ INTEL_ARCH_TOPDOWN_FE_BOUND, -+ INTEL_ARCH_TOPDOWN_RETIRING, -+ INTEL_ARCH_LBR_INSERTS, - }; - kvm_static_assert(ARRAY_SIZE(intel_pmu_arch_events) == NR_INTEL_ARCH_EVENTS); - -diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -index 8aaaf25b6111..342a72420177 100644 ---- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c -+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -@@ -75,6 +75,11 @@ static struct kvm_intel_pmu_event intel_event_to_feature(uint8_t idx) - [INTEL_ARCH_BRANCHES_RETIRED_INDEX] = { X86_PMU_FEATURE_BRANCH_INSNS_RETIRED, X86_PMU_FEATURE_NULL }, - [INTEL_ARCH_BRANCHES_MISPREDICTED_INDEX] = { X86_PMU_FEATURE_BRANCHES_MISPREDICTED, X86_PMU_FEATURE_NULL }, - [INTEL_ARCH_TOPDOWN_SLOTS_INDEX] = { X86_PMU_FEATURE_TOPDOWN_SLOTS, X86_PMU_FEATURE_TOPDOWN_SLOTS_FIXED }, -+ [INTEL_ARCH_TOPDOWN_BE_BOUND_INDEX] = { X86_PMU_FEATURE_TOPDOWN_BE_BOUND, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_TOPDOWN_BAD_SPEC_INDEX] = { X86_PMU_FEATURE_TOPDOWN_BAD_SPEC, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_TOPDOWN_FE_BOUND_INDEX] = { X86_PMU_FEATURE_TOPDOWN_FE_BOUND, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_TOPDOWN_RETIRING_INDEX] = { X86_PMU_FEATURE_TOPDOWN_RETIRING, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_LBR_INSERTS_INDEX] = { X86_PMU_FEATURE_LBR_INSERTS, X86_PMU_FEATURE_NULL }, - }; - - kvm_static_assert(ARRAY_SIZE(__intel_event_to_feature) == NR_INTEL_ARCH_EVENTS); -@@ -171,9 +176,12 @@ static void guest_assert_event_count(uint8_t idx, uint32_t pmc, uint32_t pmc_msr - fallthrough; - case INTEL_ARCH_CPU_CYCLES_INDEX: - case INTEL_ARCH_REFERENCE_CYCLES_INDEX: -+ case INTEL_ARCH_TOPDOWN_BE_BOUND_INDEX: -+ case INTEL_ARCH_TOPDOWN_FE_BOUND_INDEX: - GUEST_ASSERT_NE(count, 0); - break; - case INTEL_ARCH_TOPDOWN_SLOTS_INDEX: -+ case INTEL_ARCH_TOPDOWN_RETIRING_INDEX: - __GUEST_ASSERT(count >= NUM_INSNS_RETIRED, - "Expected top-down slots >= %u, got count = %lu", - NUM_INSNS_RETIRED, count); -@@ -612,15 +620,19 @@ static void test_intel_counters(void) - pr_info("Testing arch events, PMU version %u, perf_caps = %lx\n", - v, perf_caps[i]); - /* -- * To keep the total runtime reasonable, test every -- * possible non-zero, non-reserved bitmap combination -- * only with the native PMU version and the full bit -- * vector length. -+ * To keep the total runtime reasonable, especially after -+ * the total number of arch-events increasing to 13, It's -+ * impossible to test every possible non-zero, non-reserved -+ * bitmap combination. Only test the first 8-bits combination -+ * with the native PMU version and the full bit vector length. - */ - if (v == pmu_version) { -- for (k = 1; k < (BIT(NR_INTEL_ARCH_EVENTS) - 1); k++) -+ int max_events = min(NR_INTEL_ARCH_EVENTS, 8); -+ -+ for (k = 1; k < (BIT(max_events) - 1); k++) - test_arch_events(v, perf_caps[i], NR_INTEL_ARCH_EVENTS, k); - } -+ - /* - * Test single bits for all PMU version and lengths up - * the number of events +1 (to verify KVM doesn't do --- -2.43.0 - diff --git a/SPECS/kernel-rt/0035-fs-ntfs3-Initialize-allocated-memory-before-use.patch b/SPECS/kernel-rt/0035-fs-ntfs3-Initialize-allocated-memory-before-use.patch deleted file mode 100644 index 47ef528f2..000000000 --- a/SPECS/kernel-rt/0035-fs-ntfs3-Initialize-allocated-memory-before-use.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 9f11482aa1bf1a2770fb33ee4a4aa05cd6259d79 Mon Sep 17 00:00:00 2001 -From: Bartlomiej Kubik -Date: Wed, 5 Nov 2025 22:18:08 +0100 -Subject: [PATCH 35/51] fs/ntfs3: Initialize allocated memory before use - -KMSAN reports: Multiple uninitialized values detected: - -- KMSAN: uninit-value in ntfs_read_hdr (3) -- KMSAN: uninit-value in bcmp (3) - -Memory is allocated by __getname(), which is a wrapper for -kmem_cache_alloc(). This memory is used before being properly -cleared. Change kmem_cache_alloc() to kmem_cache_zalloc() to -properly allocate and clear memory before use. - -Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") -Fixes: 78ab59fee07f ("fs/ntfs3: Rework file operations") -Tested-by: syzbot+332bd4e9d148f11a87dc@syzkaller.appspotmail.com -Reported-by: syzbot+332bd4e9d148f11a87dc@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=332bd4e9d148f11a87dc - -Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") -Fixes: 78ab59fee07f ("fs/ntfs3: Rework file operations") -Tested-by: syzbot+0399100e525dd9696764@syzkaller.appspotmail.com -Reported-by: syzbot+0399100e525dd9696764@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=0399100e525dd9696764 - -Reviewed-by: Khalid Aziz -Signed-off-by: Bartlomiej Kubik -Signed-off-by: Konstantin Komarov ---- - fs/ntfs3/inode.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c -index b08b00912165..7a89d31ccc1c 100644 ---- a/fs/ntfs3/inode.c -+++ b/fs/ntfs3/inode.c -@@ -1273,7 +1273,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, - fa |= FILE_ATTRIBUTE_READONLY; - - /* Allocate PATH_MAX bytes. */ -- new_de = __getname(); -+ new_de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!new_de) { - err = -ENOMEM; - goto out1; -@@ -1714,7 +1714,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) - struct NTFS_DE *de; - - /* Allocate PATH_MAX bytes. */ -- de = __getname(); -+ de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!de) - return -ENOMEM; - -@@ -1752,7 +1752,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) - return -EINVAL; - - /* Allocate PATH_MAX bytes. */ -- de = __getname(); -+ de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!de) - return -ENOMEM; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi b/SPECS/kernel-rt/0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi new file mode 100644 index 000000000..ecd83eefc --- /dev/null +++ b/SPECS/kernel-rt/0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi @@ -0,0 +1,107 @@ +From 4f6cc876452148cc7132bfef9537c38c8736bb40 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Sat, 5 Apr 2025 17:18:24 +0000 +Subject: [PATCH 35/44] x86/nmi: Add support to handle NMIs with source + information + +The NMI-source bitmap is delivered as FRED event data to the kernel. +When available, use NMI-source based filtering to determine the exact +handlers to run. + +Activate NMI-source based filtering only for Local NMIs. While handling +platform NMI types (such as SERR and IOCHK), do not use the source +bitmap. They have only one handler registered per type, so there is no +need to disambiguate between multiple handlers. + +Some third-party chipsets may send NMI messages with a fixed vector of +2, which would result in bit 2 being set in the NMI-source bitmap. Skip +the local NMI handlers in this situation. + +Bit 0 of the source bitmap is set by the hardware whenever a source +vector was not used while generating an NMI, or the originator could not +be reliably identified. Poll all the registered handlers in that case. + +When multiple handlers need to be executed, adhere to the existing +priority scheme and execute the handlers registered with NMI_FLAG_FIRST +before others. + +The logic for handling legacy NMIs is unaffected since the source bitmap +would always have all bits set. + +Suggested-by: Peter Zijlstra (Intel) +Signed-off-by: Sohil Mehta +Reviewed-by: Xin Li (Intel) +--- +v7: Add review tag from Xin. + +v6: Get rid of a separate NMI source matching function (PeterZ) + Set source_bitmap to ULONG_MAX to match all sources by default + +v5: Significantly simplify NMI-source handling logic. + Get rid of a separate lookup table for NMI-source vectors. + Adhere to existing priority scheme for handling NMIs. +--- + arch/x86/kernel/nmi.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index 8071ad32aa119..3d2b636e93791 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -130,6 +130,7 @@ static void nmi_check_duration(struct nmiaction *action, u64 duration) + static int nmi_handle(unsigned int type, struct pt_regs *regs) + { + struct nmi_desc *desc = nmi_to_desc(type); ++ unsigned long source_bitmap = ULONG_MAX; + nmi_handler_t ehandler; + struct nmiaction *a; + int handled=0; +@@ -148,16 +149,45 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) + + rcu_read_lock(); + ++ /* ++ * Activate NMI source-based filtering only for Local NMIs. ++ * ++ * Platform NMI types (such as SERR and IOCHK) have only one ++ * handler registered per type, so there is no need to ++ * disambiguate between multiple handlers. ++ * ++ * Also, if a platform source ends up setting bit 2 in the ++ * source bitmap, the local NMI handlers would be skipped since ++ * none of them use this reserved vector. ++ * ++ * For Unknown NMIs, avoid using the source bitmap to ensure all ++ * potential handlers have a chance to claim responsibility. ++ */ ++ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) && type == NMI_LOCAL) { ++ source_bitmap = fred_event_data(regs); ++ ++ /* Reset the bitmap if a valid source could not be identified */ ++ if (WARN_ON_ONCE(!source_bitmap) || (source_bitmap & BIT(NMIS_NO_SOURCE))) ++ source_bitmap = ULONG_MAX; ++ } ++ + /* + * NMIs are edge-triggered, which means if you have enough + * of them concurrently, you can lose some because only one + * can be latched at any given time. Walk the whole list + * to handle those situations. ++ * ++ * However, NMI-source reporting does not have this limitation. ++ * When NMI sources have been identified, only run the handlers ++ * that match the reported vectors. + */ + list_for_each_entry_rcu(a, &desc->head, list) { + int thishandled; + u64 delta; + ++ if (!(source_bitmap & BIT(a->source_vector))) ++ continue; ++ + delta = sched_clock(); + thishandled = a->handler(type, regs); + handled += thishandled; +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi b/SPECS/kernel-rt/0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi deleted file mode 100644 index ec21fe447..000000000 --- a/SPECS/kernel-rt/0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi +++ /dev/null @@ -1,233 +0,0 @@ -From 56c4109f5224b35e215b6602acccb6fd5d7e7b20 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Wed, 11 Jun 2025 23:10:01 -0700 -Subject: [PATCH 35/44] x86/nmi: Assign and register NMI-source vectors - -Prior to NMI-source support, the vector information was ignored by the -hardware while delivering NMIs. With NMI-source reporting, the initial -architecture supports a 16-bit source bitmap to identify the source of -the NMI. Upon receiving an NMI, this bitmap is delivered to the kernel -as part of the FRED event delivery mechanism. - -Assign a vector space of 1-15 that is specific to NMI-source and -independent of the IDT vector space. Though unlikely, the hardware may -extend the NMI-source bitmap in the future. Add a code comment to -clarify how the kernel support can be easily extended. - -Being a bitmap, the NMI-source vectors do not have any inherent priority -associated with them. The order of executing the NMI handlers is up to -the kernel. Existing NMI handling already has a priority mechanism for -the NMI handlers, with CPU-specific (NMI_LOCAL) handlers executed first, -followed by platform NMI handlers and unknown NMI (NMI_UNKNOWN) handlers -being last. Within each of these NMI types, the handlers registered with -NMI_FLAG_FIRST are given priority. - -NMI-source follows the same priority scheme to avoid unnecessary -complexity. Therefore, the NMI-source vectors are assigned arbitrarily, -except for vector 2. - -Vector 2 is reserved for external NMIs corresponding to the Local APIC - -LINT1 pin. Some third-party chipsets may send NMI messages with a fixed -vector value of 2. Using vector 2 for something else would lead to -confusion about the exact source. Do not assign it to any handler. - -NMI-source vectors are only assigned for NMI_LOCAL type handlers. -Platform NMI handlers have a single handler registered per type. They -don't need additional source information to differentiate among them. - -Use the assigned vectors to register the respective NMI handlers. Warn -if the vector values are unexpected. - -A couple of NMI handlers, such as the microcode rendezvous and the crash -reboot, do not use the typical NMI registration interface. Leave them -as-is for now. - -Originally-by: Jacob Pan -Signed-off-by: Sohil Mehta -Reviewed-by: Xin Li (Intel) ---- -v7: Use an enum to define the NMI-source vectors. (DaveH) - Add a BUILD_BUG_ON to validate the allocated vector count. (DaveH) - Add review tag from Xin. - -v6: Store source vector unconditionally. (PeterZ) - Add a warning for unexpected source vector values. (PeterZ) - -v5: Move the vector defines to nmi.h. - Combine vector allocation and registration into one patch. - Simplify NMI vector names. - Describe usage of vector 2 for external NMIs. - Get rid of vector priorities. ---- - arch/x86/events/core.c | 2 +- - arch/x86/include/asm/nmi.h | 46 ++++++++++++++++++++++++++++++++ - arch/x86/kernel/apic/hw_nmi.c | 2 +- - arch/x86/kernel/cpu/mce/inject.c | 2 +- - arch/x86/kernel/kgdb.c | 2 +- - arch/x86/kernel/nmi.c | 7 +++++ - arch/x86/kernel/nmi_selftest.c | 2 +- - arch/x86/kernel/smp.c | 2 +- - 8 files changed, 59 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index c7205c7c1376..d02bac741bfe 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -2253,7 +2253,7 @@ static int __init init_hw_perf_events(void) - x86_pmu.config_mask = X86_RAW_EVENT_MASK; - - perf_events_lapic_init(); -- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); -+ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_VECTOR_PMI); - - unconstrained = (struct event_constraint) - __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, -diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h -index 42820c4f59b9..a48958a236fd 100644 ---- a/arch/x86/include/asm/nmi.h -+++ b/arch/x86/include/asm/nmi.h -@@ -53,12 +53,58 @@ typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); - * origin of an NMI and to route the NMI directly to the appropriate - * handler. - * -+ * On CPUs that support NMI-source reporting with FRED, receiving an NMI -+ * with a valid vector sets the corresponding bit in the NMI-source -+ * bitmap. The bitmap is delivered as FRED event data on the stack. -+ * -+ * Multiple NMIs are coalesced in the NMI-source bitmap until the next -+ * NMI delivery. If an NMI is received without a vector or beyond the -+ * defined range, the CPU sets bit 0 of the NMI-source bitmap. -+ * -+ * Third-party chipsets may send NMI messages with a fixed vector of 2. -+ * Using vector 2 for some other purpose would cause confusion between -+ * those external NMI messages and the other purpose. Avoid using it. -+ * -+ * The vectors are in no particular priority order. Add new vector -+ * assignments sequentially in the list below before the COUNT. -+ * - * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. -+ * @NMIS_VECTOR_TEST: NMI selftest. -+ * @NMIS_VECTOR_EXTERNAL: Reserved to match external NMI vector 2. -+ * @NMIS_VECTOR_SMP_STOP: Panic stop CPU. -+ * @NMIS_VECTOR_BT: CPU backtrace. -+ * @NMIS_VECTOR_KGDB: Kernel debugger. -+ * @NMIS_VECTOR_MCE: MCE injection. -+ * @NMIS_VECTOR_PMI: PerfMon counters. -+ * -+ * @NMIS_VECTOR_COUNT: Count of the defined vectors. - */ - enum nmi_source_vectors { - NMIS_NO_SOURCE = 0, -+ NMIS_VECTOR_TEST, -+ NMIS_VECTOR_EXTERNAL = 2, -+ NMIS_VECTOR_SMP_STOP, -+ NMIS_VECTOR_BT, -+ NMIS_VECTOR_KGDB, -+ NMIS_VECTOR_MCE, -+ NMIS_VECTOR_PMI, -+ -+ NMIS_VECTOR_COUNT - }; - -+/* -+ * The early (and likely all future) hardware implementations of -+ * NMI-source reporting would only support a 16-bit source bitmap, with -+ * 1-15 being valid source vectors. -+ * -+ * If the hardware ever supports a larger bitmap, the kernel support can -+ * easily be extended to 64 bits by modifying the MAX below. However, -+ * care must be taken to reallocate the latency sensitive NMI sources -+ * within the first 15 vectors. Any source vector beyond the supported -+ * maximum on prior systems would set bit 0 in the NMI-source bitmap. -+ */ -+#define NMIS_VECTORS_MAX 16 -+ - struct nmiaction { - struct list_head list; - nmi_handler_t handler; -diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c -index d09e771723ed..4e04f13d2de9 100644 ---- a/arch/x86/kernel/apic/hw_nmi.c -+++ b/arch/x86/kernel/apic/hw_nmi.c -@@ -53,7 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); - - static int __init register_nmi_cpu_backtrace_handler(void) - { -- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); -+ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_VECTOR_BT); - return 0; - } - early_initcall(register_nmi_cpu_backtrace_handler); -diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c -index ba70ef8a1964..320068e01c22 100644 ---- a/arch/x86/kernel/cpu/mce/inject.c -+++ b/arch/x86/kernel/cpu/mce/inject.c -@@ -775,7 +775,7 @@ static int __init inject_init(void) - - debugfs_init(); - -- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); -+ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_VECTOR_MCE); - mce_register_injector_chain(&inject_nb); - - setup_inj_struct(&i_mce); -diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c -index 80de0ac2e8de..788b83e9404b 100644 ---- a/arch/x86/kernel/kgdb.c -+++ b/arch/x86/kernel/kgdb.c -@@ -602,7 +602,7 @@ int kgdb_arch_init(void) - if (retval) - goto out; - -- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); -+ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_VECTOR_KGDB); - if (retval) - goto out1; - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index be93ec7255bf..8071ad32aa11 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -182,6 +182,13 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action) - if (WARN_ON_ONCE(!action->handler || !list_empty(&action->list))) - return -EINVAL; - -+ /* NMI-source reporting should only be used for NMI_LOCAL */ -+ WARN_ON_ONCE((type != NMI_LOCAL) && (action->source_vector != NMIS_NO_SOURCE)); -+ -+ /* Check for valid vector values. See comment above NMIS_VECTORS_MAX */ -+ BUILD_BUG_ON(NMIS_VECTOR_COUNT > NMIS_VECTORS_MAX); -+ WARN_ON_ONCE(action->source_vector >= NMIS_VECTOR_COUNT); -+ - raw_spin_lock_irqsave(&desc->lock, flags); - - /* -diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c -index c4fffa868160..f3918888e494 100644 ---- a/arch/x86/kernel/nmi_selftest.c -+++ b/arch/x86/kernel/nmi_selftest.c -@@ -64,7 +64,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) - unsigned long timeout; - - if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, -- "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { -+ "nmi_selftest", NMIS_VECTOR_TEST, __initdata)) { - nmi_fail = FAILURE; - return; - } -diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c -index 6c1d3ffbee6c..694e31bf445c 100644 ---- a/arch/x86/kernel/smp.c -+++ b/arch/x86/kernel/smp.c -@@ -143,7 +143,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) - static int register_stop_handler(void) - { - return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, -- "smp_stop", NMIS_NO_SOURCE); -+ "smp_stop", NMIS_VECTOR_SMP_STOP); - } - - static void native_stop_other_cpus(int wait) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0036-KVM-selftests-Relax-precise-event-count-validation-as.perf b/SPECS/kernel-rt/0036-KVM-selftests-Relax-precise-event-count-validation-as.perf deleted file mode 100644 index 60e4bad68..000000000 --- a/SPECS/kernel-rt/0036-KVM-selftests-Relax-precise-event-count-validation-as.perf +++ /dev/null @@ -1,159 +0,0 @@ -From 3ed66f373b3c90405254ff19a9a0393d9f07ae73 Mon Sep 17 00:00:00 2001 -From: dongsheng -Date: Fri, 18 Jul 2025 08:19:04 +0800 -Subject: [PATCH 036/100] KVM: selftests: Relax precise event count validation - as overcount issue - -For Intel Atom CPUs, the PMU events "Instruction Retired" or -"Branch Instruction Retired" may be overcounted for some certain -instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD -and complex SGX/SMX/CSTATE instructions/flows. - -The detailed information can be found in the errata (section SRF7): -https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/ - -For the Atom platforms before Sierra Forest (including Sierra Forest), -Both 2 events "Instruction Retired" and "Branch Instruction Retired" would -be overcounted on these certain instructions, but for Clearwater Forest -only "Instruction Retired" event is overcounted on these instructions. - -As the overcount issue on VM-Exit/VM-Entry, it has no way to validate -the precise count for these 2 events on these affected Atom platforms, -so just relax the precise event count check for these 2 events on these -Atom platforms. - -Signed-off-by: dongsheng -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/include/x86/pmu.h | 9 +++++ - tools/testing/selftests/kvm/lib/x86/pmu.c | 38 +++++++++++++++++++ - .../selftests/kvm/x86/pmu_counters_test.c | 17 ++++++++- - 3 files changed, 62 insertions(+), 2 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h -index 2aabda2da002..db14c08abc59 100644 ---- a/tools/testing/selftests/kvm/include/x86/pmu.h -+++ b/tools/testing/selftests/kvm/include/x86/pmu.h -@@ -104,4 +104,13 @@ enum amd_pmu_zen_events { - extern const uint64_t intel_pmu_arch_events[]; - extern const uint64_t amd_pmu_zen_events[]; - -+/* -+ * Flags for "Instruction Retired" and "Branch Instruction Retired" -+ * overcount flaws. -+ */ -+#define INST_RETIRED_OVERCOUNT BIT(0) -+#define BR_RETIRED_OVERCOUNT BIT(1) -+ -+extern uint32_t detect_inst_overcount_flags(void); -+ - #endif /* SELFTEST_KVM_PMU_H */ -diff --git a/tools/testing/selftests/kvm/lib/x86/pmu.c b/tools/testing/selftests/kvm/lib/x86/pmu.c -index 5ab44bf54773..fd4ed577c88f 100644 ---- a/tools/testing/selftests/kvm/lib/x86/pmu.c -+++ b/tools/testing/selftests/kvm/lib/x86/pmu.c -@@ -8,6 +8,7 @@ - #include - - #include "kvm_util.h" -+#include "processor.h" - #include "pmu.h" - - const uint64_t intel_pmu_arch_events[] = { -@@ -34,3 +35,40 @@ const uint64_t amd_pmu_zen_events[] = { - AMD_ZEN_BRANCHES_MISPREDICTED, - }; - kvm_static_assert(ARRAY_SIZE(amd_pmu_zen_events) == NR_AMD_ZEN_EVENTS); -+ -+/* -+ * For Intel Atom CPUs, the PMU events "Instruction Retired" or -+ * "Branch Instruction Retired" may be overcounted for some certain -+ * instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD -+ * and complex SGX/SMX/CSTATE instructions/flows. -+ * -+ * The detailed information can be found in the errata (section SRF7): -+ * https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/ -+ * -+ * For the Atom platforms before Sierra Forest (including Sierra Forest), -+ * Both 2 events "Instruction Retired" and "Branch Instruction Retired" would -+ * be overcounted on these certain instructions, but for Clearwater Forest -+ * only "Instruction Retired" event is overcounted on these instructions. -+ */ -+uint32_t detect_inst_overcount_flags(void) -+{ -+ uint32_t eax, ebx, ecx, edx; -+ uint32_t flags = 0; -+ -+ cpuid(1, &eax, &ebx, &ecx, &edx); -+ if (x86_family(eax) == 0x6) { -+ switch (x86_model(eax)) { -+ case 0xDD: /* Clearwater Forest */ -+ flags = INST_RETIRED_OVERCOUNT; -+ break; -+ case 0xAF: /* Sierra Forest */ -+ case 0x4D: /* Avaton, Rangely */ -+ case 0x5F: /* Denverton */ -+ case 0x86: /* Jacobsville */ -+ flags = INST_RETIRED_OVERCOUNT | BR_RETIRED_OVERCOUNT; -+ break; -+ } -+ } -+ -+ return flags; -+} -diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -index 342a72420177..074cdf323406 100644 ---- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c -+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -@@ -52,6 +52,9 @@ struct kvm_intel_pmu_event { - struct kvm_x86_pmu_feature fixed_event; - }; - -+ -+static uint8_t inst_overcount_flags; -+ - /* - * Wrap the array to appease the compiler, as the macros used to construct each - * kvm_x86_pmu_feature use syntax that's only valid in function scope, and the -@@ -163,10 +166,18 @@ static void guest_assert_event_count(uint8_t idx, uint32_t pmc, uint32_t pmc_msr - - switch (idx) { - case INTEL_ARCH_INSTRUCTIONS_RETIRED_INDEX: -- GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); -+ /* Relax precise count check due to VM-EXIT/VM-ENTRY overcount issue */ -+ if (inst_overcount_flags & INST_RETIRED_OVERCOUNT) -+ GUEST_ASSERT(count >= NUM_INSNS_RETIRED); -+ else -+ GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); - break; - case INTEL_ARCH_BRANCHES_RETIRED_INDEX: -- GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); -+ /* Relax precise count check due to VM-EXIT/VM-ENTRY overcount issue */ -+ if (inst_overcount_flags & BR_RETIRED_OVERCOUNT) -+ GUEST_ASSERT(count >= NUM_BRANCH_INSNS_RETIRED); -+ else -+ GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); - break; - case INTEL_ARCH_LLC_REFERENCES_INDEX: - case INTEL_ARCH_LLC_MISSES_INDEX: -@@ -335,6 +346,7 @@ static void test_arch_events(uint8_t pmu_version, uint64_t perf_capabilities, - length); - vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EVENTS_MASK, - unavailable_mask); -+ sync_global_to_guest(vm, inst_overcount_flags); - - run_vcpu(vcpu); - -@@ -673,6 +685,7 @@ int main(int argc, char *argv[]) - - kvm_pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION); - kvm_has_perf_caps = kvm_cpu_has(X86_FEATURE_PDCM); -+ inst_overcount_flags = detect_inst_overcount_flags(); - - test_intel_counters(); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi b/SPECS/kernel-rt/0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi deleted file mode 100644 index 7aaec7471..000000000 --- a/SPECS/kernel-rt/0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi +++ /dev/null @@ -1,107 +0,0 @@ -From 89398493ca183026c1d93d898389402f5eff31f7 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Sat, 5 Apr 2025 17:18:24 +0000 -Subject: [PATCH 36/44] x86/nmi: Add support to handle NMIs with source - information - -The NMI-source bitmap is delivered as FRED event data to the kernel. -When available, use NMI-source based filtering to determine the exact -handlers to run. - -Activate NMI-source based filtering only for Local NMIs. While handling -platform NMI types (such as SERR and IOCHK), do not use the source -bitmap. They have only one handler registered per type, so there is no -need to disambiguate between multiple handlers. - -Some third-party chipsets may send NMI messages with a fixed vector of -2, which would result in bit 2 being set in the NMI-source bitmap. Skip -the local NMI handlers in this situation. - -Bit 0 of the source bitmap is set by the hardware whenever a source -vector was not used while generating an NMI, or the originator could not -be reliably identified. Poll all the registered handlers in that case. - -When multiple handlers need to be executed, adhere to the existing -priority scheme and execute the handlers registered with NMI_FLAG_FIRST -before others. - -The logic for handling legacy NMIs is unaffected since the source bitmap -would always have all bits set. - -Suggested-by: Peter Zijlstra (Intel) -Signed-off-by: Sohil Mehta -Reviewed-by: Xin Li (Intel) ---- -v7: Add review tag from Xin. - -v6: Get rid of a separate NMI source matching function (PeterZ) - Set source_bitmap to ULONG_MAX to match all sources by default - -v5: Significantly simplify NMI-source handling logic. - Get rid of a separate lookup table for NMI-source vectors. - Adhere to existing priority scheme for handling NMIs. ---- - arch/x86/kernel/nmi.c | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index 8071ad32aa11..3d2b636e9379 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -130,6 +130,7 @@ static void nmi_check_duration(struct nmiaction *action, u64 duration) - static int nmi_handle(unsigned int type, struct pt_regs *regs) - { - struct nmi_desc *desc = nmi_to_desc(type); -+ unsigned long source_bitmap = ULONG_MAX; - nmi_handler_t ehandler; - struct nmiaction *a; - int handled=0; -@@ -148,16 +149,45 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) - - rcu_read_lock(); - -+ /* -+ * Activate NMI source-based filtering only for Local NMIs. -+ * -+ * Platform NMI types (such as SERR and IOCHK) have only one -+ * handler registered per type, so there is no need to -+ * disambiguate between multiple handlers. -+ * -+ * Also, if a platform source ends up setting bit 2 in the -+ * source bitmap, the local NMI handlers would be skipped since -+ * none of them use this reserved vector. -+ * -+ * For Unknown NMIs, avoid using the source bitmap to ensure all -+ * potential handlers have a chance to claim responsibility. -+ */ -+ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) && type == NMI_LOCAL) { -+ source_bitmap = fred_event_data(regs); -+ -+ /* Reset the bitmap if a valid source could not be identified */ -+ if (WARN_ON_ONCE(!source_bitmap) || (source_bitmap & BIT(NMIS_NO_SOURCE))) -+ source_bitmap = ULONG_MAX; -+ } -+ - /* - * NMIs are edge-triggered, which means if you have enough - * of them concurrently, you can lose some because only one - * can be latched at any given time. Walk the whole list - * to handle those situations. -+ * -+ * However, NMI-source reporting does not have this limitation. -+ * When NMI sources have been identified, only run the handlers -+ * that match the reported vectors. - */ - list_for_each_entry_rcu(a, &desc->head, list) { - int thishandled; - u64 delta; - -+ if (!(source_bitmap & BIT(a->source_vector))) -+ continue; -+ - delta = sched_clock(); - thishandled = a->handler(type, regs); - handled += thishandled; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi b/SPECS/kernel-rt/0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi new file mode 100644 index 000000000..5bf42935e --- /dev/null +++ b/SPECS/kernel-rt/0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi @@ -0,0 +1,216 @@ +From 31a8c7e6103a43d895d445522a8bce022e4eaacc Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 28 Apr 2025 15:55:03 +0000 +Subject: [PATCH 36/44] x86/nmi: Prepare for the new NMI-source vector encoding + +When using the send_IPI_* APIC calls, callers typically use NMI vector +0x2 to trigger NMIs. The APIC APIs convert the NMI vector to the NMI +delivery mode, which is eventually used to program the APIC. + +Before FRED, the hardware would ignore the vector used with NMI delivery +mode. However, with NMI-source reporting, the vector information is +relayed to the destination CPU, which sets the corresponding bit in the +NMI-source bitmap. Unfortunately, the kernel now needs to maintain a new +set of NMI vectors and differentiate them from the IDT vectors. + +Instead of creating a parallel set of send_NMI_* APIs to handle +NMI-source vectors, enhance the existing send_IPI_* APIs with a new +encoding scheme to handle the NMI delivery mode along with the +NMI-source vector. + +NMI-source vectors would be encoded as: + APIC_DM_NMI (0x400) | NMI_SOURCE_VECTOR (0x1-0xF) + +Also, introduce a helper to prepare the ICR value with the encoded +delivery mode and vector. Update the guest paravirtual APIC code to use +the new helper as well. + +While at it, rename APIC_DM_FIXED_MASK to the more appropriate +APIC_DM_MASK. + +Suggested-by: Sean Christopherson +Co-developed-by: Xin Li (Intel) +Signed-off-by: Xin Li (Intel) +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: Remove a redundant else statement. (PeterZ) + +v5: Use a simiplified encoding scheme for NMI-source vectors. +--- + arch/x86/include/asm/apic.h | 30 +++++++++++++++++++++++++++++ + arch/x86/include/asm/apicdef.h | 2 +- + arch/x86/kernel/apic/ipi.c | 4 ++-- + arch/x86/kernel/apic/local.h | 24 ++++++++++++----------- + arch/x86/kernel/apic/x2apic_savic.c | 2 +- + arch/x86/kernel/kvm.c | 9 +-------- + drivers/thermal/intel/therm_throt.c | 2 +- + 7 files changed, 49 insertions(+), 24 deletions(-) + +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +index a26e66d66444a..741a24f47a24d 100644 +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -480,6 +480,36 @@ static __always_inline void apic_update_vector(unsigned int cpu, unsigned int ve + apic->update_vector(cpu, vector, set); + } + ++/* ++ * Prepare the delivery mode and vector for the ICR. ++ * ++ * NMI-source vectors have the NMI delivery mode encoded within them to ++ * differentiate them from the IDT vectors. IDT vector 0x2 (NMI_VECTOR) ++ * is treated as an NMI request but without any NMI-source information. ++ */ ++static inline u16 __prepare_ICR_DM_vector(u16 dm_vector) ++{ ++ u16 vector = dm_vector & APIC_VECTOR_MASK; ++ u16 dm = dm_vector & APIC_DM_MASK; ++ ++ if (dm == APIC_DM_NMI) { ++ /* ++ * Pre-FRED, the actual vector is ignored for NMIs, but ++ * zero it if NMI-source reporting is not supported to ++ * avoid breakage on misbehaving hardware or hypervisors. ++ */ ++ if (!cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) ++ vector = 0; ++ ++ return dm | vector; ++ } ++ ++ if (vector == NMI_VECTOR) ++ return APIC_DM_NMI; ++ ++ return APIC_DM_FIXED | vector; ++} ++ + #else /* CONFIG_X86_LOCAL_APIC */ + + static inline u32 apic_read(u32 reg) { return 0; } +diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h +index be39a543fbe5d..2cf59601c1486 100644 +--- a/arch/x86/include/asm/apicdef.h ++++ b/arch/x86/include/asm/apicdef.h +@@ -87,8 +87,8 @@ + #define APIC_ICR_BUSY 0x01000 + #define APIC_DEST_LOGICAL 0x00800 + #define APIC_DEST_PHYSICAL 0x00000 ++#define APIC_DM_MASK 0x00700 + #define APIC_DM_FIXED 0x00000 +-#define APIC_DM_FIXED_MASK 0x00700 + #define APIC_DM_LOWEST 0x00100 + #define APIC_DM_SMI 0x00200 + #define APIC_DM_REMRD 0x00300 +diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c +index 98a57cb4aa861..4e8bc42f3bd56 100644 +--- a/arch/x86/kernel/apic/ipi.c ++++ b/arch/x86/kernel/apic/ipi.c +@@ -158,7 +158,7 @@ static void __default_send_IPI_shortcut(unsigned int shortcut, int vector) + * issues where otherwise the system hangs when the panic CPU tries + * to stop the others before launching the kdump kernel. + */ +- if (unlikely(vector == NMI_VECTOR)) ++ if (unlikely(is_nmi_vector(vector))) + apic_mem_wait_icr_idle_timeout(); + else + apic_mem_wait_icr_idle(); +@@ -175,7 +175,7 @@ void __default_send_IPI_dest_field(unsigned int dest_mask, int vector, + unsigned int dest_mode) + { + /* See comment in __default_send_IPI_shortcut() */ +- if (unlikely(vector == NMI_VECTOR)) ++ if (unlikely(is_nmi_vector(vector))) + apic_mem_wait_icr_idle_timeout(); + else + apic_mem_wait_icr_idle(); +diff --git a/arch/x86/kernel/apic/local.h b/arch/x86/kernel/apic/local.h +index bdcf609eb2835..9a54c589a4bf2 100644 +--- a/arch/x86/kernel/apic/local.h ++++ b/arch/x86/kernel/apic/local.h +@@ -24,22 +24,24 @@ extern u32 x2apic_max_apicid; + + /* IPI */ + ++u16 __prepare_ICR_DM_vector(u16 vector); ++ + DECLARE_STATIC_KEY_FALSE(apic_use_ipi_shorthand); + ++/* NMI-source vectors have the delivery mode encoded within them */ ++static inline bool is_nmi_vector(u16 vector) ++{ ++ if ((vector & APIC_DM_MASK) == APIC_DM_NMI) ++ return true; ++ if ((vector & APIC_VECTOR_MASK) == NMI_VECTOR) ++ return true; ++ return false; ++} ++ + static inline unsigned int __prepare_ICR(unsigned int shortcut, int vector, + unsigned int dest) + { +- unsigned int icr = shortcut | dest; +- +- switch (vector) { +- default: +- icr |= APIC_DM_FIXED | vector; +- break; +- case NMI_VECTOR: +- icr |= APIC_DM_NMI; +- break; +- } +- return icr; ++ return shortcut | dest | __prepare_ICR_DM_vector(vector); + } + + void default_init_apic_ldr(void); +diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c +index dbc5678bc3b68..a7ed866b6e124 100644 +--- a/arch/x86/kernel/apic/x2apic_savic.c ++++ b/arch/x86/kernel/apic/x2apic_savic.c +@@ -174,7 +174,7 @@ static void savic_icr_write(u32 icr_low, u32 icr_high) + + dsh = icr_low & APIC_DEST_ALLBUT; + vector = icr_low & APIC_VECTOR_MASK; +- nmi = ((icr_low & APIC_DM_FIXED_MASK) == APIC_DM_NMI); ++ nmi = ((icr_low & APIC_DM_MASK) == APIC_DM_NMI); + + switch (dsh) { + case APIC_DEST_SELF: +diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c +index b67d7c59dca0b..76674265dceda 100644 +--- a/arch/x86/kernel/kvm.c ++++ b/arch/x86/kernel/kvm.c +@@ -517,14 +517,7 @@ static void __send_ipi_mask(const struct cpumask *mask, int vector) + + local_irq_save(flags); + +- switch (vector) { +- default: +- icr = APIC_DM_FIXED | vector; +- break; +- case NMI_VECTOR: +- icr = APIC_DM_NMI; +- break; +- } ++ icr = __prepare_ICR_DM_vector(vector); + + for_each_cpu(cpu, mask) { + apic_id = per_cpu(x86_cpu_to_apicid, cpu); +diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c +index debc94e2dc169..5c0d2de2986e3 100644 +--- a/drivers/thermal/intel/therm_throt.c ++++ b/drivers/thermal/intel/therm_throt.c +@@ -740,7 +740,7 @@ void intel_init_thermal(struct cpuinfo_x86 *c) + * BIOS has programmed on AP based on BSP's info we saved since BIOS + * is always setting the same value for all threads/cores. + */ +- if ((h & APIC_DM_FIXED_MASK) != APIC_DM_FIXED) ++ if ((h & APIC_DM_MASK) != APIC_DM_FIXED) + apic_write(APIC_LVTTHMR, lvtthmr_init); + + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf b/SPECS/kernel-rt/0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf deleted file mode 100644 index b6391b73c..000000000 --- a/SPECS/kernel-rt/0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf +++ /dev/null @@ -1,59 +0,0 @@ -From fb7a5eb05f8661d9027a16da02d90bba5101d0e3 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:05 +0800 -Subject: [PATCH 037/100] KVM: selftests: Relax branches event count check for - event_filter test - -As the branches event overcount issue on Atom platforms, once there are -VM-Exits triggered (external interrupts) in the guest loop, the measured -branch event count could be larger than NUM_BRANCHES, this would lead to -the pmu_event_filter_test print warning to info the measured branches -event count is mismatched with expected number (NUM_BRANCHES). - -To eliminate this warning, relax the branches event count check on the -Atom platform which have the branches event overcount issue. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/x86/pmu_event_filter_test.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c -index c15513cd74d1..9c1a92f05786 100644 ---- a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c -+++ b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c -@@ -60,6 +60,8 @@ struct { - uint64_t instructions_retired; - } pmc_results; - -+static uint8_t inst_overcount_flags; -+ - /* - * If we encounter a #GP during the guest PMU sanity check, then the guest - * PMU is not functional. Inform the hypervisor via GUEST_SYNC(0). -@@ -214,8 +216,10 @@ static void remove_event(struct __kvm_pmu_event_filter *f, uint64_t event) - do { \ - uint64_t br = pmc_results.branches_retired; \ - uint64_t ir = pmc_results.instructions_retired; \ -+ bool br_matched = inst_overcount_flags & BR_RETIRED_OVERCOUNT ? \ -+ br >= NUM_BRANCHES : br == NUM_BRANCHES; \ - \ -- if (br && br != NUM_BRANCHES) \ -+ if (br && !br_matched) \ - pr_info("%s: Branch instructions retired = %lu (expected %u)\n", \ - __func__, br, NUM_BRANCHES); \ - TEST_ASSERT(br, "%s: Branch instructions retired = %lu (expected > 0)", \ -@@ -850,6 +854,9 @@ int main(int argc, char *argv[]) - if (use_amd_pmu()) - test_amd_deny_list(vcpu); - -+ if (use_intel_pmu()) -+ inst_overcount_flags = detect_inst_overcount_flags(); -+ - test_without_filter(vcpu); - test_member_deny_list(vcpu); - test_member_allow_list(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi b/SPECS/kernel-rt/0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi new file mode 100644 index 000000000..1566502c6 --- /dev/null +++ b/SPECS/kernel-rt/0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi @@ -0,0 +1,131 @@ +From 13e7f0e6412f745d21fde28786a8425e3b4ec531 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 28 Apr 2025 20:09:02 +0000 +Subject: [PATCH 37/44] x86/nmi: Enable NMI-source for IPIs delivered as NMIs + +With the IPI handling APIs ready to support the new NMI encoding, encode +the NMI delivery mode directly with the NMI-source vectors to trigger +NMIs. + +Move most of the existing NMI-based IPIs to use the new NMI-source +vectors, except for the microcode rendezvous NMI and the crash reboot +NMI. NMI handling for them is special-cased in exc_nmi() and does not +need NMI-source reporting. + +However, in the future, it might be useful to assign a source vector to +all NMI sources to improve isolation and debuggability. + +Originally-by: Jacob Pan +Suggested-by: Sean Christopherson +Co-developed-by: Xin Li (Intel) +Signed-off-by: Xin Li (Intel) +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: Include asm/nmi.h to avoid compile errors. (LKP) + +v5: Encode APIC_DM_NMI directly with the NMI-source vector. +--- + arch/x86/include/asm/apic.h | 8 ++++++++ + arch/x86/kernel/apic/hw_nmi.c | 2 +- + arch/x86/kernel/cpu/mce/inject.c | 2 +- + arch/x86/kernel/kgdb.c | 2 +- + arch/x86/kernel/nmi_selftest.c | 2 +- + arch/x86/kernel/smp.c | 2 +- + 6 files changed, 13 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +index 741a24f47a24d..fd62f99e5b0fc 100644 +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + + #define ARCH_APICTIMER_STOPS_ON_C3 1 +@@ -23,6 +24,13 @@ + #define APIC_EXTNMI_ALL 1 + #define APIC_EXTNMI_NONE 2 + ++/* Trigger NMIs with source information */ ++#define TEST_NMI (APIC_DM_NMI | NMIS_VECTOR_TEST) ++#define SMP_STOP_NMI (APIC_DM_NMI | NMIS_VECTOR_SMP_STOP) ++#define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) ++#define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) ++#define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) ++ + /* + * Debugging macros + */ +diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c +index 4e04f13d2de98..586f4b25feae6 100644 +--- a/arch/x86/kernel/apic/hw_nmi.c ++++ b/arch/x86/kernel/apic/hw_nmi.c +@@ -33,7 +33,7 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh) + #ifdef arch_trigger_cpumask_backtrace + static void nmi_raise_cpu_backtrace(cpumask_t *mask) + { +- __apic_send_IPI_mask(mask, NMI_VECTOR); ++ __apic_send_IPI_mask(mask, BT_NMI); + } + + void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) +diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c +index 320068e01c22d..81a04836ac740 100644 +--- a/arch/x86/kernel/cpu/mce/inject.c ++++ b/arch/x86/kernel/cpu/mce/inject.c +@@ -270,7 +270,7 @@ static void __maybe_unused raise_mce(struct mce *m) + mce_irq_ipi, NULL, 0); + preempt_enable(); + } else if (m->inject_flags & MCJ_NMI_BROADCAST) +- __apic_send_IPI_mask(mce_inject_cpumask, NMI_VECTOR); ++ __apic_send_IPI_mask(mce_inject_cpumask, MCE_NMI); + } + start = jiffies; + while (!cpumask_empty(mce_inject_cpumask)) { +diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c +index 788b83e9404b6..8a342df84d8e1 100644 +--- a/arch/x86/kernel/kgdb.c ++++ b/arch/x86/kernel/kgdb.c +@@ -416,7 +416,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) + */ + void kgdb_roundup_cpus(void) + { +- apic_send_IPI_allbutself(NMI_VECTOR); ++ apic_send_IPI_allbutself(KGDB_NMI); + } + #endif + +diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c +index f3918888e4942..d5578370b47ff 100644 +--- a/arch/x86/kernel/nmi_selftest.c ++++ b/arch/x86/kernel/nmi_selftest.c +@@ -72,7 +72,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) + /* sync above data before sending NMI */ + wmb(); + +- __apic_send_IPI_mask(mask, NMI_VECTOR); ++ __apic_send_IPI_mask(mask, TEST_NMI); + + /* Don't wait longer than a second */ + timeout = USEC_PER_SEC; +diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c +index 694e31bf445c5..eb49ee5b312fc 100644 +--- a/arch/x86/kernel/smp.c ++++ b/arch/x86/kernel/smp.c +@@ -217,7 +217,7 @@ static void native_stop_other_cpus(int wait) + pr_emerg("Shutting down cpus with NMI\n"); + + for_each_cpu(cpu, &cpus_stop_mask) +- __apic_send_IPI(cpu, NMI_VECTOR); ++ __apic_send_IPI(cpu, SMP_STOP_NMI); + } + /* + * Don't wait longer than 10 ms if the caller didn't +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi b/SPECS/kernel-rt/0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi deleted file mode 100644 index 7425f4c37..000000000 --- a/SPECS/kernel-rt/0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi +++ /dev/null @@ -1,202 +0,0 @@ -From 4bfa94030a3b19925581faf2cfe28c2c016e573b Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 28 Apr 2025 15:55:03 +0000 -Subject: [PATCH 37/44] x86/nmi: Prepare for the new NMI-source vector encoding - -When using the send_IPI_* APIC calls, callers typically use NMI vector -0x2 to trigger NMIs. The APIC APIs convert the NMI vector to the NMI -delivery mode, which is eventually used to program the APIC. - -Before FRED, the hardware would ignore the vector used with NMI delivery -mode. However, with NMI-source reporting, the vector information is -relayed to the destination CPU, which sets the corresponding bit in the -NMI-source bitmap. Unfortunately, the kernel now needs to maintain a new -set of NMI vectors and differentiate them from the IDT vectors. - -Instead of creating a parallel set of send_NMI_* APIs to handle -NMI-source vectors, enhance the existing send_IPI_* APIs with a new -encoding scheme to handle the NMI delivery mode along with the -NMI-source vector. - -NMI-source vectors would be encoded as: - APIC_DM_NMI (0x400) | NMI_SOURCE_VECTOR (0x1-0xF) - -Also, introduce a helper to prepare the ICR value with the encoded -delivery mode and vector. Update the guest paravirtual APIC code to use -the new helper as well. - -While at it, rename APIC_DM_FIXED_MASK to the more appropriate -APIC_DM_MASK. - -Suggested-by: Sean Christopherson -Co-developed-by: Xin Li (Intel) -Signed-off-by: Xin Li (Intel) -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: Remove a redundant else statement. (PeterZ) - -v5: Use a simiplified encoding scheme for NMI-source vectors. ---- - arch/x86/include/asm/apic.h | 30 +++++++++++++++++++++++++++++ - arch/x86/include/asm/apicdef.h | 2 +- - arch/x86/kernel/apic/ipi.c | 4 ++-- - arch/x86/kernel/apic/local.h | 24 ++++++++++++----------- - arch/x86/kernel/kvm.c | 9 +-------- - drivers/thermal/intel/therm_throt.c | 2 +- - 6 files changed, 48 insertions(+), 23 deletions(-) - -diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h -index 07ba4935e873..7448b349f864 100644 ---- a/arch/x86/include/asm/apic.h -+++ b/arch/x86/include/asm/apic.h -@@ -470,6 +470,36 @@ static __always_inline bool apic_id_valid(u32 apic_id) - return apic_id <= apic->max_apic_id; - } - -+/* -+ * Prepare the delivery mode and vector for the ICR. -+ * -+ * NMI-source vectors have the NMI delivery mode encoded within them to -+ * differentiate them from the IDT vectors. IDT vector 0x2 (NMI_VECTOR) -+ * is treated as an NMI request but without any NMI-source information. -+ */ -+static inline u16 __prepare_ICR_DM_vector(u16 dm_vector) -+{ -+ u16 vector = dm_vector & APIC_VECTOR_MASK; -+ u16 dm = dm_vector & APIC_DM_MASK; -+ -+ if (dm == APIC_DM_NMI) { -+ /* -+ * Pre-FRED, the actual vector is ignored for NMIs, but -+ * zero it if NMI-source reporting is not supported to -+ * avoid breakage on misbehaving hardware or hypervisors. -+ */ -+ if (!cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) -+ vector = 0; -+ -+ return dm | vector; -+ } -+ -+ if (vector == NMI_VECTOR) -+ return APIC_DM_NMI; -+ -+ return APIC_DM_FIXED | vector; -+} -+ - #else /* CONFIG_X86_LOCAL_APIC */ - - static inline u32 apic_read(u32 reg) { return 0; } -diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h -index 094106b6a538..3fb8fa73f6aa 100644 ---- a/arch/x86/include/asm/apicdef.h -+++ b/arch/x86/include/asm/apicdef.h -@@ -87,8 +87,8 @@ - #define APIC_ICR_BUSY 0x01000 - #define APIC_DEST_LOGICAL 0x00800 - #define APIC_DEST_PHYSICAL 0x00000 -+#define APIC_DM_MASK 0x00700 - #define APIC_DM_FIXED 0x00000 --#define APIC_DM_FIXED_MASK 0x00700 - #define APIC_DM_LOWEST 0x00100 - #define APIC_DM_SMI 0x00200 - #define APIC_DM_REMRD 0x00300 -diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c -index 98a57cb4aa86..4e8bc42f3bd5 100644 ---- a/arch/x86/kernel/apic/ipi.c -+++ b/arch/x86/kernel/apic/ipi.c -@@ -158,7 +158,7 @@ static void __default_send_IPI_shortcut(unsigned int shortcut, int vector) - * issues where otherwise the system hangs when the panic CPU tries - * to stop the others before launching the kdump kernel. - */ -- if (unlikely(vector == NMI_VECTOR)) -+ if (unlikely(is_nmi_vector(vector))) - apic_mem_wait_icr_idle_timeout(); - else - apic_mem_wait_icr_idle(); -@@ -175,7 +175,7 @@ void __default_send_IPI_dest_field(unsigned int dest_mask, int vector, - unsigned int dest_mode) - { - /* See comment in __default_send_IPI_shortcut() */ -- if (unlikely(vector == NMI_VECTOR)) -+ if (unlikely(is_nmi_vector(vector))) - apic_mem_wait_icr_idle_timeout(); - else - apic_mem_wait_icr_idle(); -diff --git a/arch/x86/kernel/apic/local.h b/arch/x86/kernel/apic/local.h -index bdcf609eb283..9a54c589a4bf 100644 ---- a/arch/x86/kernel/apic/local.h -+++ b/arch/x86/kernel/apic/local.h -@@ -24,22 +24,24 @@ extern u32 x2apic_max_apicid; - - /* IPI */ - -+u16 __prepare_ICR_DM_vector(u16 vector); -+ - DECLARE_STATIC_KEY_FALSE(apic_use_ipi_shorthand); - -+/* NMI-source vectors have the delivery mode encoded within them */ -+static inline bool is_nmi_vector(u16 vector) -+{ -+ if ((vector & APIC_DM_MASK) == APIC_DM_NMI) -+ return true; -+ if ((vector & APIC_VECTOR_MASK) == NMI_VECTOR) -+ return true; -+ return false; -+} -+ - static inline unsigned int __prepare_ICR(unsigned int shortcut, int vector, - unsigned int dest) - { -- unsigned int icr = shortcut | dest; -- -- switch (vector) { -- default: -- icr |= APIC_DM_FIXED | vector; -- break; -- case NMI_VECTOR: -- icr |= APIC_DM_NMI; -- break; -- } -- return icr; -+ return shortcut | dest | __prepare_ICR_DM_vector(vector); - } - - void default_init_apic_ldr(void); -diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c -index 8ae750cde0c6..826eb399ac46 100644 ---- a/arch/x86/kernel/kvm.c -+++ b/arch/x86/kernel/kvm.c -@@ -518,14 +518,7 @@ static void __send_ipi_mask(const struct cpumask *mask, int vector) - - local_irq_save(flags); - -- switch (vector) { -- default: -- icr = APIC_DM_FIXED | vector; -- break; -- case NMI_VECTOR: -- icr = APIC_DM_NMI; -- break; -- } -+ icr = __prepare_ICR_DM_vector(vector); - - for_each_cpu(cpu, mask) { - apic_id = per_cpu(x86_cpu_to_apicid, cpu); -diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c -index debc94e2dc16..5c0d2de2986e 100644 ---- a/drivers/thermal/intel/therm_throt.c -+++ b/drivers/thermal/intel/therm_throt.c -@@ -740,7 +740,7 @@ void intel_init_thermal(struct cpuinfo_x86 *c) - * BIOS has programmed on AP based on BSP's info we saved since BIOS - * is always setting the same value for all threads/cores. - */ -- if ((h & APIC_DM_FIXED_MASK) != APIC_DM_FIXED) -+ if ((h & APIC_DM_MASK) != APIC_DM_FIXED) - apic_write(APIC_LVTTHMR, lvtthmr_init); - - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi b/SPECS/kernel-rt/0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi new file mode 100644 index 000000000..82c981d75 --- /dev/null +++ b/SPECS/kernel-rt/0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi @@ -0,0 +1,97 @@ +From 34f97e270ecbf5046f0e6d1e2b677739bae2f3c6 Mon Sep 17 00:00:00 2001 +From: Jacob Pan +Date: Tue, 9 Jul 2024 07:39:03 -0700 +Subject: [PATCH 38/44] perf/x86: Enable NMI-source reporting for perfmon + +Program the designated PMI NMI-source vector into the local vector table +for the PMU. An NMI for the PMU would directly invoke the PMI handler +without polling other NMI handlers, resulting in reduced PMI delivery +latency. + +Co-developed-by: Zeng Guang +Signed-off-by: Zeng Guang +Signed-off-by: Jacob Pan +Signed-off-by: Sohil Mehta +Tested-by: Sandipan Das # AMD overlapping bits +Reviewed-by: Kan Liang +Reviewed-by: Xin Li (Intel) +--- +v7: Pick up a review tag (Xin). + +v6: Pick up a tested-by tag (Sandipan). + +v5: No significant change. +--- + arch/x86/events/core.c | 4 ++-- + arch/x86/events/intel/core.c | 6 +++--- + arch/x86/include/asm/apic.h | 1 + + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c +index e0efe4c00444e..bcdf78db0e72d 100644 +--- a/arch/x86/events/core.c ++++ b/arch/x86/events/core.c +@@ -1704,7 +1704,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs) + * This generic handler doesn't seem to have any issues where the + * unmasking occurs so it was left at the top. + */ +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + + for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { + if (!test_bit(idx, cpuc->active_mask)) +@@ -1746,7 +1746,7 @@ void perf_events_lapic_init(void) + /* + * Always use NMI for PMU + */ +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + } + + static int +diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c +index fe65be0b9d9c4..44d3b8440fa9e 100644 +--- a/arch/x86/events/intel/core.c ++++ b/arch/x86/events/intel/core.c +@@ -3315,7 +3315,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) + * NMI handler. + */ + if (!late_ack && !mid_ack) +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + intel_bts_disable_local(); + cpuc->enabled = 0; + __intel_pmu_disable_all(true); +@@ -3352,7 +3352,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) + + done: + if (mid_ack) +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + /* Only restore PMU state when it's active. See x86_pmu_disable(). */ + cpuc->enabled = pmu_enabled; + if (pmu_enabled) +@@ -3365,7 +3365,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) + * Haswell CPUs. + */ + if (late_ack) +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + return handled; + } + +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +index fd62f99e5b0fc..535064faa719d 100644 +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -30,6 +30,7 @@ + #define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) + #define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) + #define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) ++#define PERF_NMI (APIC_DM_NMI | NMIS_VECTOR_PMI) + + /* + * Debugging macros +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi b/SPECS/kernel-rt/0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi deleted file mode 100644 index 1bb43bfad..000000000 --- a/SPECS/kernel-rt/0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi +++ /dev/null @@ -1,131 +0,0 @@ -From 7edbfbfc60c698ae0c2daa70329ccc6508d930ec Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 28 Apr 2025 20:09:02 +0000 -Subject: [PATCH 38/44] x86/nmi: Enable NMI-source for IPIs delivered as NMIs - -With the IPI handling APIs ready to support the new NMI encoding, encode -the NMI delivery mode directly with the NMI-source vectors to trigger -NMIs. - -Move most of the existing NMI-based IPIs to use the new NMI-source -vectors, except for the microcode rendezvous NMI and the crash reboot -NMI. NMI handling for them is special-cased in exc_nmi() and does not -need NMI-source reporting. - -However, in the future, it might be useful to assign a source vector to -all NMI sources to improve isolation and debuggability. - -Originally-by: Jacob Pan -Suggested-by: Sean Christopherson -Co-developed-by: Xin Li (Intel) -Signed-off-by: Xin Li (Intel) -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: Include asm/nmi.h to avoid compile errors. (LKP) - -v5: Encode APIC_DM_NMI directly with the NMI-source vector. ---- - arch/x86/include/asm/apic.h | 8 ++++++++ - arch/x86/kernel/apic/hw_nmi.c | 2 +- - arch/x86/kernel/cpu/mce/inject.c | 2 +- - arch/x86/kernel/kgdb.c | 2 +- - arch/x86/kernel/nmi_selftest.c | 2 +- - arch/x86/kernel/smp.c | 2 +- - 6 files changed, 13 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h -index 7448b349f864..c1f06c6e86a5 100644 ---- a/arch/x86/include/asm/apic.h -+++ b/arch/x86/include/asm/apic.h -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - - #define ARCH_APICTIMER_STOPS_ON_C3 1 -@@ -23,6 +24,13 @@ - #define APIC_EXTNMI_ALL 1 - #define APIC_EXTNMI_NONE 2 - -+/* Trigger NMIs with source information */ -+#define TEST_NMI (APIC_DM_NMI | NMIS_VECTOR_TEST) -+#define SMP_STOP_NMI (APIC_DM_NMI | NMIS_VECTOR_SMP_STOP) -+#define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) -+#define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) -+#define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) -+ - /* - * Debugging macros - */ -diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c -index 4e04f13d2de9..586f4b25feae 100644 ---- a/arch/x86/kernel/apic/hw_nmi.c -+++ b/arch/x86/kernel/apic/hw_nmi.c -@@ -33,7 +33,7 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh) - #ifdef arch_trigger_cpumask_backtrace - static void nmi_raise_cpu_backtrace(cpumask_t *mask) - { -- __apic_send_IPI_mask(mask, NMI_VECTOR); -+ __apic_send_IPI_mask(mask, BT_NMI); - } - - void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) -diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c -index 320068e01c22..81a04836ac74 100644 ---- a/arch/x86/kernel/cpu/mce/inject.c -+++ b/arch/x86/kernel/cpu/mce/inject.c -@@ -270,7 +270,7 @@ static void __maybe_unused raise_mce(struct mce *m) - mce_irq_ipi, NULL, 0); - preempt_enable(); - } else if (m->inject_flags & MCJ_NMI_BROADCAST) -- __apic_send_IPI_mask(mce_inject_cpumask, NMI_VECTOR); -+ __apic_send_IPI_mask(mce_inject_cpumask, MCE_NMI); - } - start = jiffies; - while (!cpumask_empty(mce_inject_cpumask)) { -diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c -index 788b83e9404b..8a342df84d8e 100644 ---- a/arch/x86/kernel/kgdb.c -+++ b/arch/x86/kernel/kgdb.c -@@ -416,7 +416,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) - */ - void kgdb_roundup_cpus(void) - { -- apic_send_IPI_allbutself(NMI_VECTOR); -+ apic_send_IPI_allbutself(KGDB_NMI); - } - #endif - -diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c -index f3918888e494..d5578370b47f 100644 ---- a/arch/x86/kernel/nmi_selftest.c -+++ b/arch/x86/kernel/nmi_selftest.c -@@ -72,7 +72,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) - /* sync above data before sending NMI */ - wmb(); - -- __apic_send_IPI_mask(mask, NMI_VECTOR); -+ __apic_send_IPI_mask(mask, TEST_NMI); - - /* Don't wait longer than a second */ - timeout = USEC_PER_SEC; -diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c -index 694e31bf445c..eb49ee5b312f 100644 ---- a/arch/x86/kernel/smp.c -+++ b/arch/x86/kernel/smp.c -@@ -217,7 +217,7 @@ static void native_stop_other_cpus(int wait) - pr_emerg("Shutting down cpus with NMI\n"); - - for_each_cpu(cpu, &cpus_stop_mask) -- __apic_send_IPI(cpu, NMI_VECTOR); -+ __apic_send_IPI(cpu, SMP_STOP_NMI); - } - /* - * Don't wait longer than 10 ms if the caller didn't --- -2.43.0 - diff --git a/SPECS/kernel-rt/0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf b/SPECS/kernel-rt/0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf deleted file mode 100644 index 1d9f5af00..000000000 --- a/SPECS/kernel-rt/0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf +++ /dev/null @@ -1,67 +0,0 @@ -From b75b09963f02f502960d824f08eaa755fa3dc924 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 11:53:07 -0700 -Subject: [PATCH 039/100] KVM: x86: Add kvm_icr_to_lapic_irq() helper to allow - for fastpath IPIs - -Extract the code for converting an ICR message into a kvm_lapic_irq -structure into a local helper so that a fast-only IPI path can share the -conversion logic. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/lapic.c | 30 ++++++++++++++++++------------ - 1 file changed, 18 insertions(+), 12 deletions(-) - -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index 8172c2042dd6..9f9846980625 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -1481,24 +1481,30 @@ void kvm_apic_set_eoi_accelerated(struct kvm_vcpu *vcpu, int vector) - } - EXPORT_SYMBOL_GPL(kvm_apic_set_eoi_accelerated); - --void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high) -+static void kvm_icr_to_lapic_irq(struct kvm_lapic *apic, u32 icr_low, -+ u32 icr_high, struct kvm_lapic_irq *irq) - { -- struct kvm_lapic_irq irq; -- - /* KVM has no delay and should always clear the BUSY/PENDING flag. */ - WARN_ON_ONCE(icr_low & APIC_ICR_BUSY); - -- irq.vector = icr_low & APIC_VECTOR_MASK; -- irq.delivery_mode = icr_low & APIC_MODE_MASK; -- irq.dest_mode = icr_low & APIC_DEST_MASK; -- irq.level = (icr_low & APIC_INT_ASSERT) != 0; -- irq.trig_mode = icr_low & APIC_INT_LEVELTRIG; -- irq.shorthand = icr_low & APIC_SHORT_MASK; -- irq.msi_redir_hint = false; -+ irq->vector = icr_low & APIC_VECTOR_MASK; -+ irq->delivery_mode = icr_low & APIC_MODE_MASK; -+ irq->dest_mode = icr_low & APIC_DEST_MASK; -+ irq->level = (icr_low & APIC_INT_ASSERT) != 0; -+ irq->trig_mode = icr_low & APIC_INT_LEVELTRIG; -+ irq->shorthand = icr_low & APIC_SHORT_MASK; -+ irq->msi_redir_hint = false; - if (apic_x2apic_mode(apic)) -- irq.dest_id = icr_high; -+ irq->dest_id = icr_high; - else -- irq.dest_id = GET_XAPIC_DEST_FIELD(icr_high); -+ irq->dest_id = GET_XAPIC_DEST_FIELD(icr_high); -+} -+ -+void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high) -+{ -+ struct kvm_lapic_irq irq; -+ -+ kvm_icr_to_lapic_irq(apic, icr_low, icr_high, &irq); - - trace_kvm_apic_ipi(icr_low, irq.dest_id); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi b/SPECS/kernel-rt/0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi deleted file mode 100644 index ea9ca3d56..000000000 --- a/SPECS/kernel-rt/0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi +++ /dev/null @@ -1,97 +0,0 @@ -From 2ef0bc6ad76eec392bb9f9b10418c40339150dce Mon Sep 17 00:00:00 2001 -From: Jacob Pan -Date: Tue, 9 Jul 2024 07:39:03 -0700 -Subject: [PATCH 39/44] perf/x86: Enable NMI-source reporting for perfmon - -Program the designated PMI NMI-source vector into the local vector table -for the PMU. An NMI for the PMU would directly invoke the PMI handler -without polling other NMI handlers, resulting in reduced PMI delivery -latency. - -Co-developed-by: Zeng Guang -Signed-off-by: Zeng Guang -Signed-off-by: Jacob Pan -Signed-off-by: Sohil Mehta -Tested-by: Sandipan Das # AMD overlapping bits -Reviewed-by: Kan Liang -Reviewed-by: Xin Li (Intel) ---- -v7: Pick up a review tag (Xin). - -v6: Pick up a tested-by tag (Sandipan). - -v5: No significant change. ---- - arch/x86/events/core.c | 4 ++-- - arch/x86/events/intel/core.c | 6 +++--- - arch/x86/include/asm/apic.h | 1 + - 3 files changed, 6 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index d02bac741bfe..8081af5f66a0 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1813,7 +1813,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs) - * This generic handler doesn't seem to have any issues where the - * unmasking occurs so it was left at the top. - */ -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - - for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { - if (!test_bit(idx, cpuc->active_mask)) -@@ -1858,7 +1858,7 @@ void perf_events_lapic_init(void) - /* - * Always use NMI for PMU - */ -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - } - - static int -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index f0cd578a14e1..adc07ddbb5eb 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3457,7 +3457,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) - * NMI handler. - */ - if (!late_ack && !mid_ack) -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - intel_bts_disable_local(); - cpuc->enabled = 0; - __intel_pmu_disable_all(true); -@@ -3494,7 +3494,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) - - done: - if (mid_ack) -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - /* Only restore PMU state when it's active. See x86_pmu_disable(). */ - cpuc->enabled = pmu_enabled; - if (pmu_enabled) -@@ -3507,7 +3507,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) - * Haswell CPUs. - */ - if (late_ack) -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - return handled; - } - -diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h -index c1f06c6e86a5..59bb11538b18 100644 ---- a/arch/x86/include/asm/apic.h -+++ b/arch/x86/include/asm/apic.h -@@ -30,6 +30,7 @@ - #define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) - #define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) - #define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) -+#define PERF_NMI (APIC_DM_NMI | NMIS_VECTOR_PMI) - - /* - * Debugging macros --- -2.43.0 - diff --git a/SPECS/kernel-rt/0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi b/SPECS/kernel-rt/0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi new file mode 100644 index 000000000..74277736c --- /dev/null +++ b/SPECS/kernel-rt/0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi @@ -0,0 +1,41 @@ +From d129883bb9d1660ea0b1bed1196a7522fc9fe1fb Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 7 Apr 2025 11:06:15 +0000 +Subject: [PATCH 39/44] x86/nmi: Print source information with the unknown NMI + console message + +The NMI-source bitmap is a useful piece of information provided by the +NMI-source reporting feature. It is very helpful for debugging unknown +NMIs, as it can pinpoint the exact source that caused the NMI. + +Print the complete source bitmap along with the "unknown NMI" kernel log +message, since unexpected sources might have triggered the NMI. + +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: Drop the tracepoint modification part for now. + +v5: New patch +--- + arch/x86/kernel/nmi.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index 3d2b636e93791..0b5bb20c5eb71 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -379,6 +379,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs) + pr_emerg_ratelimited("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", + reason, smp_processor_id()); + ++ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) ++ pr_emerg_ratelimited("NMI-source bitmap is 0x%lx\n", fred_event_data(regs)); ++ + if (unknown_nmi_panic || panic_on_unrecovered_nmi) + nmi_panic(regs, "NMI: Not continuing"); + +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf b/SPECS/kernel-rt/0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf deleted file mode 100644 index 88772c090..000000000 --- a/SPECS/kernel-rt/0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf +++ /dev/null @@ -1,105 +0,0 @@ -From c9633b0e2ca1a7fd7d154bcb2010c6d8633f5224 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 1 Aug 2025 14:30:06 -0700 -Subject: [PATCH 040/100] KVM: x86: Only allow "fast" IPIs in fastpath - WRMSR(X2APIC_ICR) handler - -Explicitly restrict fastpath ICR writes to IPIs that are "fast", i.e. can -be delivered without having to walk all vCPUs, and that target at most 16 -vCPUs. Artificially restricting ICR writes to physical mode guarantees -at most one vCPU will receive in IPI (because x2APIC IDs are read-only), -but that delivery might not be "fast". E.g. even if the vCPU exists, KVM -might have to iterate over 4096 vCPUs to find the right one. - -Limiting delivery to fast IPIs aligns the WRMSR fastpath with -kvm_arch_set_irq_inatomic() (which also runs with IRQs disabled), and will -allow dropping the semi-arbitrary restrictions on delivery mode and type. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/lapic.c | 27 +++++++++++++++++++++++++-- - arch/x86/kvm/lapic.h | 2 +- - arch/x86/kvm/x86.c | 2 +- - 3 files changed, 27 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index 9f9846980625..bd3232dd7a63 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -2439,7 +2439,7 @@ EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi); - - #define X2APIC_ICR_RESERVED_BITS (GENMASK_ULL(31, 20) | GENMASK_ULL(17, 16) | BIT(13)) - --int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) -+static int __kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data, bool fast) - { - if (data & X2APIC_ICR_RESERVED_BITS) - return 1; -@@ -2454,7 +2454,20 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) - */ - data &= ~APIC_ICR_BUSY; - -- kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32)); -+ if (fast) { -+ struct kvm_lapic_irq irq; -+ int ignored; -+ -+ kvm_icr_to_lapic_irq(apic, (u32)data, (u32)(data >> 32), &irq); -+ -+ if (!kvm_irq_delivery_to_apic_fast(apic->vcpu->kvm, apic, &irq, -+ &ignored, NULL)) -+ return -EWOULDBLOCK; -+ -+ trace_kvm_apic_ipi((u32)data, irq.dest_id); -+ } else { -+ kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32)); -+ } - if (kvm_x86_ops.x2apic_icr_is_split) { - kvm_lapic_set_reg(apic, APIC_ICR, data); - kvm_lapic_set_reg(apic, APIC_ICR2, data >> 32); -@@ -2465,6 +2478,16 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) - return 0; - } - -+static int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) -+{ -+ return __kvm_x2apic_icr_write(apic, data, false); -+} -+ -+int kvm_x2apic_icr_write_fast(struct kvm_lapic *apic, u64 data) -+{ -+ return __kvm_x2apic_icr_write(apic, data, true); -+} -+ - static u64 kvm_x2apic_icr_read(struct kvm_lapic *apic) - { - if (kvm_x86_ops.x2apic_icr_is_split) -diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h -index 72de14527698..1b2d408816aa 100644 ---- a/arch/x86/kvm/lapic.h -+++ b/arch/x86/kvm/lapic.h -@@ -137,7 +137,7 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr); - void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu); - void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu); - --int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data); -+int kvm_x2apic_icr_write_fast(struct kvm_lapic *apic, u64 data); - int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data); - int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a1c49bc681c4..8c8b7d7902a0 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2149,7 +2149,7 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data - ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) && - ((data & APIC_MODE_MASK) == APIC_DM_FIXED) && - ((u32)(data >> 32) != X2APIC_BROADCAST)) -- return kvm_x2apic_icr_write(vcpu->arch.apic, data); -+ return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); - - return 1; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi b/SPECS/kernel-rt/0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi new file mode 100644 index 000000000..0053508bd --- /dev/null +++ b/SPECS/kernel-rt/0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi @@ -0,0 +1,83 @@ +From 04ecfe7f7ed82822ca0ffab7dca906fa85172086 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Sun, 11 May 2025 11:08:16 -0700 +Subject: [PATCH 40/44] x86/nmi: Include source information in NMI handler + tracepoint + +The NMI-source bitmap is critical information provided by the NMI-source +reporting feature. It can help identify issues when multiple NMIs occur +simultaneously or if certain NMI handlers consistently misbehave. + +For enhanced debugging, add the source bitmap to the nmi_handler() +tracepoint. + +Signed-off-by: Sohil Mehta +--- +v6: Split the patch in two parts. + Print the source bitmap accurately in non NMI_LOCAL cases. + +v5: New patch +--- + arch/x86/kernel/nmi.c | 3 ++- + include/trace/events/nmi.h | 13 ++++++++----- + 2 files changed, 10 insertions(+), 6 deletions(-) + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index 0b5bb20c5eb71..76a27164e414c 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -192,7 +192,8 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) + thishandled = a->handler(type, regs); + handled += thishandled; + delta = sched_clock() - delta; +- trace_nmi_handler(a->handler, (int)delta, thishandled); ++ trace_nmi_handler(a->handler, (int)delta, thishandled, ++ cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) ? fred_event_data(regs) : 0); + + nmi_check_duration(a, delta); + } +diff --git a/include/trace/events/nmi.h b/include/trace/events/nmi.h +index 18e0411398ba2..9ff8c4d49abe0 100644 +--- a/include/trace/events/nmi.h ++++ b/include/trace/events/nmi.h +@@ -10,29 +10,32 @@ + + TRACE_EVENT(nmi_handler, + +- TP_PROTO(void *handler, s64 delta_ns, int handled), ++ TP_PROTO(void *handler, s64 delta_ns, int handled, unsigned long source_bitmap), + +- TP_ARGS(handler, delta_ns, handled), ++ TP_ARGS(handler, delta_ns, handled, source_bitmap), + + TP_STRUCT__entry( + __field( void *, handler ) + __field( s64, delta_ns) + __field( int, handled ) ++ __field(unsigned long, source_bitmap) + ), + + TP_fast_assign( + __entry->handler = handler; + __entry->delta_ns = delta_ns; + __entry->handled = handled; ++ __entry->source_bitmap = source_bitmap; + ), + +- TP_printk("%ps() delta_ns: %lld handled: %d", ++ TP_printk("%ps() delta_ns: %lld handled: %d source_bitmap: 0x%lx", + __entry->handler, + __entry->delta_ns, +- __entry->handled) ++ __entry->handled, ++ __entry->source_bitmap) + ); + + #endif /* _TRACE_NMI_H */ + +-/* This part ust be outside protection */ ++/* This part must be outside protection */ + #include +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi b/SPECS/kernel-rt/0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi deleted file mode 100644 index b057d22e8..000000000 --- a/SPECS/kernel-rt/0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi +++ /dev/null @@ -1,41 +0,0 @@ -From 1bb482f979e679f6537d438bb177b73a1cf9d1f9 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 7 Apr 2025 11:06:15 +0000 -Subject: [PATCH 40/44] x86/nmi: Print source information with the unknown NMI - console message - -The NMI-source bitmap is a useful piece of information provided by the -NMI-source reporting feature. It is very helpful for debugging unknown -NMIs, as it can pinpoint the exact source that caused the NMI. - -Print the complete source bitmap along with the "unknown NMI" kernel log -message, since unexpected sources might have triggered the NMI. - -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: Drop the tracepoint modification part for now. - -v5: New patch ---- - arch/x86/kernel/nmi.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index 3d2b636e9379..0b5bb20c5eb7 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -379,6 +379,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs) - pr_emerg_ratelimited("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", - reason, smp_processor_id()); - -+ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) -+ pr_emerg_ratelimited("NMI-source bitmap is 0x%lx\n", fred_event_data(regs)); -+ - if (unknown_nmi_panic || panic_on_unrecovered_nmi) - nmi_panic(regs, "NMI: Not continuing"); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0041-KVM-VMX-Implement-NMI-source-injection.nmi b/SPECS/kernel-rt/0041-KVM-VMX-Implement-NMI-source-injection.nmi new file mode 100644 index 000000000..c91543dc9 --- /dev/null +++ b/SPECS/kernel-rt/0041-KVM-VMX-Implement-NMI-source-injection.nmi @@ -0,0 +1,142 @@ +From fe019bcf52969f9d5e0f39d1f9c8e7842c1709e4 Mon Sep 17 00:00:00 2001 +From: Zeng Guang +Date: Thu, 23 Nov 2023 23:53:55 +0800 +Subject: [PATCH 41/44] KVM: VMX: Implement NMI source injection + +With NMI source the vector field along with NMI encoding to trigger NMI through +APIC ICR and LVT register can be repurposed to identify the originator (source) +of the NMI. NMI source vector is delivered when FRED is enabled and reported as +an accumulated 16-bit bitmask in the exception event data field pushed on the +stack for a FRED exception. + +Introduce two new elements in struct kvm_vcpu_arch used for NMI source injection. +"nmi_source_pending" collects multiple NMIs from different sources arriving at +local APIC simultanously and coalesces into a 16-bit bitmask. "nmi_source_inject" +indicates the NMI sources that will be delivered in current NMI injection. + +KVM injects NMI source into the guest by specifying the NMI source in the injected +event data field of the VMCS. + +Signed-off-by: Zeng Guang +--- + arch/x86/include/asm/kvm_host.h | 2 ++ + arch/x86/kvm/lapic.c | 1 + + arch/x86/kvm/vmx/vmx.c | 10 ++++++++++ + arch/x86/kvm/x86.c | 21 +++++++++++++++++++-- + 4 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 558f260a1afd7..995608ea874db 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -970,6 +970,8 @@ struct kvm_vcpu_arch { + u64 tsc_scaling_ratio; /* current scaling ratio */ + + atomic_t nmi_queued; /* unprocessed asynchronous NMIs */ ++ atomic_t nmi_source_pending; /* unprocessed NMI Source */ ++ unsigned int nmi_source_inject; /* NMI Source to inject */ + /* Number of NMIs pending injection, not including hardware vNMIs. */ + unsigned int nmi_pending; + bool nmi_injected; /* Trying to inject an NMI this entry */ +diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c +index 0ae7f913d7826..acd0b4b5784d4 100644 +--- a/arch/x86/kvm/lapic.c ++++ b/arch/x86/kvm/lapic.c +@@ -1410,6 +1410,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, + + case APIC_DM_NMI: + result = 1; ++ atomic_or(1 << vector, &vcpu->arch.nmi_source_pending); + kvm_inject_nmi(vcpu); + kvm_vcpu_kick(vcpu); + break; +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 8f1d105f333f1..2515b8afd2d8f 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -5140,6 +5140,13 @@ void vmx_inject_nmi(struct kvm_vcpu *vcpu) + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, + INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR); + ++ if (is_fred_enabled(vcpu)) { ++ vmcs_write64(INJECTED_EVENT_DATA, ++ guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE) ? ++ vcpu->arch.nmi_source_inject : 0); ++ vcpu->arch.nmi_source_inject = 0; ++ } ++ + vmx_clear_hlt(vcpu); + } + +@@ -7347,6 +7354,9 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + switch (type) { + case INTR_TYPE_NMI_INTR: + vcpu->arch.nmi_injected = true; ++ ++ if (is_fred_enabled(vcpu)) ++ vcpu->arch.nmi_source_inject = vmcs_read64(event_data_field); + /* + * SDM 3: 27.7.1.2 (September 2008) + * Clear bit "block by NMI" before VM entry if a NMI +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 2f364c4e35879..295ab33cf2967 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -10916,6 +10916,11 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, + return r; + } + ++static inline bool kvm_is_nmi_source_enabled(struct kvm_vcpu *vcpu) ++{ ++ return is_fred_enabled(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE); ++} ++ + static void process_nmi(struct kvm_vcpu *vcpu) + { + unsigned int limit; +@@ -10930,7 +10935,8 @@ static void process_nmi(struct kvm_vcpu *vcpu) + * blocks NMIs). KVM will immediately inject one of the two NMIs, and + * will request an NMI window to handle the second NMI. + */ +- if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected) ++ if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected || ++ kvm_is_nmi_source_enabled(vcpu)) + limit = 1; + else + limit = 2; +@@ -10944,13 +10950,22 @@ static void process_nmi(struct kvm_vcpu *vcpu) + + vcpu->arch.nmi_pending += atomic_xchg(&vcpu->arch.nmi_queued, 0); + vcpu->arch.nmi_pending = min(vcpu->arch.nmi_pending, limit); ++ vcpu->arch.nmi_source_inject |= atomic_xchg(&vcpu->arch.nmi_source_pending, 0); + + if (vcpu->arch.nmi_pending && + (kvm_x86_call(set_vnmi_pending)(vcpu))) + vcpu->arch.nmi_pending--; + +- if (vcpu->arch.nmi_pending) ++ if (vcpu->arch.nmi_pending) { + kvm_make_request(KVM_REQ_EVENT, vcpu); ++ /* ++ * In case nmi source supported, if new NMI arrives when vCPU is ++ * trying NMI injection, it can be coalesced together and requires ++ * one elimination of nmi_pending. ++ */ ++ if (vcpu->arch.nmi_injected && kvm_is_nmi_source_enabled(vcpu)) ++ vcpu->arch.nmi_pending--; ++ } + } + + /* Return total number of NMIs pending injection to the VM */ +@@ -12984,6 +12999,8 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) + vcpu->arch.smi_pending = 0; + vcpu->arch.smi_count = 0; + atomic_set(&vcpu->arch.nmi_queued, 0); ++ atomic_set(&vcpu->arch.nmi_source_pending, 0); ++ vcpu->arch.nmi_source_inject = 0; + vcpu->arch.nmi_pending = 0; + vcpu->arch.nmi_injected = false; + kvm_clear_interrupt_queue(vcpu); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf b/SPECS/kernel-rt/0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf deleted file mode 100644 index 752bf914b..000000000 --- a/SPECS/kernel-rt/0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf +++ /dev/null @@ -1,43 +0,0 @@ -From 2b3e7165ae71445bc1beb9d199aa75d0654d074d Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 1 Aug 2025 14:30:37 -0700 -Subject: [PATCH 041/100] KVM: x86: Drop semi-arbitrary restrictions on IPI - type in fastpath - -Drop the restrictions on fastpath IPIs only working for fixed IRQs with a -physical destination now that the fastpath is explicitly limited to "fast" -delivery. Limiting delivery to a single physical APIC ID guarantees only -one vCPU will receive the event, but that isn't necessary "fast", e.g. if -the targeted vCPU is the last of 4096 vCPUs. And logical destination mode -or shorthand (to self) can also be fast, e.g. if only a few vCPUs are -being targeted. Lastly, there's nothing inherently slow about delivering -an NMI, INIT, SIPI, SMI, etc., i.e. there's no reason to artificially -limit fastpath delivery to fixed vector IRQs. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 8 +------- - 1 file changed, 1 insertion(+), 7 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 8c8b7d7902a0..ea117c4b20c8 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2145,13 +2145,7 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data - if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic)) - return 1; - -- if (((data & APIC_SHORT_MASK) == APIC_DEST_NOSHORT) && -- ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) && -- ((data & APIC_MODE_MASK) == APIC_DM_FIXED) && -- ((u32)(data >> 32) != X2APIC_BROADCAST)) -- return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); -- -- return 1; -+ return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); - } - - static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0041-comedi-pcl818-fix-null-ptr-deref-in-pcl818_ai_cancel.patch b/SPECS/kernel-rt/0041-comedi-pcl818-fix-null-ptr-deref-in-pcl818_ai_cancel.patch deleted file mode 100644 index 64fe95b8c..000000000 --- a/SPECS/kernel-rt/0041-comedi-pcl818-fix-null-ptr-deref-in-pcl818_ai_cancel.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 341e082ba6985c2e65f71ea3e27a06b2ed4ef211 Mon Sep 17 00:00:00 2001 -From: Nikita Zhandarovich -Date: Thu, 23 Oct 2025 17:14:56 +0300 -Subject: [PATCH 41/51] comedi: pcl818: fix null-ptr-deref in - pcl818_ai_cancel() - -Syzbot identified an issue [1] in pcl818_ai_cancel(), which stems from -the fact that in case of early device detach via pcl818_detach(), -subdevice dev->read_subdev may not have initialized its pointer to -&struct comedi_async as intended. Thus, any such dereferencing of -&s->async->cmd will lead to general protection fault and kernel crash. - -Mitigate this problem by removing a call to pcl818_ai_cancel() from -pcl818_detach() altogether. This way, if the subdevice setups its -support for async commands, everything async-related will be -handled via subdevice's own ->cancel() function in -comedi_device_detach_locked() even before pcl818_detach(). If no -support for asynchronous commands is provided, there is no need -to cancel anything either. - -[1] Syzbot crash: -Oops: general protection fault, probably for non-canonical -address 0xdffffc0000000005: 0000 [#1] SMP KASAN PTI -KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] -CPU: 1 UID: 0 PID: 6050 Comm: syz.0.18 Not tainted syzkaller #0 PREEMPT(full) -Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025 -RIP: 0010:pcl818_ai_cancel+0x69/0x3f0 drivers/comedi/drivers/pcl818.c:762 -... -Call Trace: - - pcl818_detach+0x66/0xd0 drivers/comedi/drivers/pcl818.c:1115 - comedi_device_detach_locked+0x178/0x750 drivers/comedi/drivers.c:207 - do_devconfig_ioctl drivers/comedi/comedi_fops.c:848 [inline] - comedi_unlocked_ioctl+0xcde/0x1020 drivers/comedi/comedi_fops.c:2178 - vfs_ioctl fs/ioctl.c:51 [inline] - __do_sys_ioctl fs/ioctl.c:597 [inline] -... - -Reported-by: syzbot+fce5d9d5bd067d6fbe9b@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=fce5d9d5bd067d6fbe9b -Fixes: 00aba6e7b565 ("staging: comedi: pcl818: remove 'neverending_ai' from private data") -Cc: stable@kernel.org -Signed-off-by: Nikita Zhandarovich -Reviewed-by: Ian Abbott -Link: https://patch.msgid.link/20251023141457.398685-1-n.zhandarovich@fintech.ru -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/drivers/pcl818.c | 5 ++--- - 1 file changed, 2 insertions(+), 3 deletions(-) - -diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c -index 4127adcfb229..06fe06396f23 100644 ---- a/drivers/comedi/drivers/pcl818.c -+++ b/drivers/comedi/drivers/pcl818.c -@@ -1111,10 +1111,9 @@ static void pcl818_detach(struct comedi_device *dev) - { - struct pcl818_private *devpriv = dev->private; - -- if (devpriv) { -- pcl818_ai_cancel(dev, dev->read_subdev); -+ if (devpriv) - pcl818_reset(dev); -- } -+ - pcl818_free_dma(dev); - comedi_legacy_detach(dev); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi b/SPECS/kernel-rt/0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi deleted file mode 100644 index 0d5c2cfbc..000000000 --- a/SPECS/kernel-rt/0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi +++ /dev/null @@ -1,83 +0,0 @@ -From fbfb91590d8fafc778d8cd6ae725920ceba05bcf Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Sun, 11 May 2025 11:08:16 -0700 -Subject: [PATCH 41/44] x86/nmi: Include source information in NMI handler - tracepoint - -The NMI-source bitmap is critical information provided by the NMI-source -reporting feature. It can help identify issues when multiple NMIs occur -simultaneously or if certain NMI handlers consistently misbehave. - -For enhanced debugging, add the source bitmap to the nmi_handler() -tracepoint. - -Signed-off-by: Sohil Mehta ---- -v6: Split the patch in two parts. - Print the source bitmap accurately in non NMI_LOCAL cases. - -v5: New patch ---- - arch/x86/kernel/nmi.c | 3 ++- - include/trace/events/nmi.h | 13 ++++++++----- - 2 files changed, 10 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index 0b5bb20c5eb7..76a27164e414 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -192,7 +192,8 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) - thishandled = a->handler(type, regs); - handled += thishandled; - delta = sched_clock() - delta; -- trace_nmi_handler(a->handler, (int)delta, thishandled); -+ trace_nmi_handler(a->handler, (int)delta, thishandled, -+ cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) ? fred_event_data(regs) : 0); - - nmi_check_duration(a, delta); - } -diff --git a/include/trace/events/nmi.h b/include/trace/events/nmi.h -index 18e0411398ba..6a22e89a39ca 100644 ---- a/include/trace/events/nmi.h -+++ b/include/trace/events/nmi.h -@@ -10,29 +10,32 @@ - - TRACE_EVENT(nmi_handler, - -- TP_PROTO(void *handler, s64 delta_ns, int handled), -+ TP_PROTO(void *handler, s64 delta_ns, int handled, unsigned long source_bitmap), - -- TP_ARGS(handler, delta_ns, handled), -+ TP_ARGS(handler, delta_ns, handled, source_bitmap), - - TP_STRUCT__entry( - __field( void *, handler ) - __field( s64, delta_ns) - __field( int, handled ) -+ __field(unsigned long, source_bitmap) - ), - - TP_fast_assign( - __entry->handler = handler; - __entry->delta_ns = delta_ns; - __entry->handled = handled; -+ __entry->source_bitmap = source_bitmap; - ), - -- TP_printk("%ps() delta_ns: %lld handled: %d", -+ TP_printk("%ps() delta_ns: %lld handled: %d source_bitmap: 0x%lx", - __entry->handler, - __entry->delta_ns, -- __entry->handled) -+ __entry->handled, -+ __entry->source_bitmap) - ); - - #endif /* _TRACE_NMI_H */ - --/* This part ust be outside protection */ -+/* This part must be outside protection */ - #include --- -2.43.0 - diff --git a/SPECS/kernel-rt/0042-KVM-VMX-Implement-NMI-source-injection.nmi b/SPECS/kernel-rt/0042-KVM-VMX-Implement-NMI-source-injection.nmi deleted file mode 100644 index 2083081b4..000000000 --- a/SPECS/kernel-rt/0042-KVM-VMX-Implement-NMI-source-injection.nmi +++ /dev/null @@ -1,142 +0,0 @@ -From 64e7e822c4c3535413b83c11732b59a0b6a2fff9 Mon Sep 17 00:00:00 2001 -From: Zeng Guang -Date: Thu, 23 Nov 2023 23:53:55 +0800 -Subject: [PATCH 42/44] KVM: VMX: Implement NMI source injection - -With NMI source the vector field along with NMI encoding to trigger NMI through -APIC ICR and LVT register can be repurposed to identify the originator (source) -of the NMI. NMI source vector is delivered when FRED is enabled and reported as -an accumulated 16-bit bitmask in the exception event data field pushed on the -stack for a FRED exception. - -Introduce two new elements in struct kvm_vcpu_arch used for NMI source injection. -"nmi_source_pending" collects multiple NMIs from different sources arriving at -local APIC simultanously and coalesces into a 16-bit bitmask. "nmi_source_inject" -indicates the NMI sources that will be delivered in current NMI injection. - -KVM injects NMI source into the guest by specifying the NMI source in the injected -event data field of the VMCS. - -Signed-off-by: Zeng Guang ---- - arch/x86/include/asm/kvm_host.h | 2 ++ - arch/x86/kvm/lapic.c | 1 + - arch/x86/kvm/vmx/vmx.c | 10 ++++++++++ - arch/x86/kvm/x86.c | 21 +++++++++++++++++++-- - 4 files changed, 32 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 2634c16b09c4..d4657bbfe460 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -969,6 +969,8 @@ struct kvm_vcpu_arch { - u64 tsc_scaling_ratio; /* current scaling ratio */ - - atomic_t nmi_queued; /* unprocessed asynchronous NMIs */ -+ atomic_t nmi_source_pending; /* unprocessed NMI Source */ -+ unsigned int nmi_source_inject; /* NMI Source to inject */ - /* Number of NMIs pending injection, not including hardware vNMIs. */ - unsigned int nmi_pending; - bool nmi_injected; /* Trying to inject an NMI this entry */ -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index e19545b8cc98..08ad0bd5e387 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -1315,6 +1315,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, - - case APIC_DM_NMI: - result = 1; -+ atomic_or(1 << vector, &vcpu->arch.nmi_source_pending); - kvm_inject_nmi(vcpu); - kvm_vcpu_kick(vcpu); - break; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 36afe1dba9f0..8bbba7aa13c3 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -5203,6 +5203,13 @@ void vmx_inject_nmi(struct kvm_vcpu *vcpu) - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, - INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR); - -+ if (is_fred_enabled(vcpu)) { -+ vmcs_write64(INJECTED_EVENT_DATA, -+ guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE) ? -+ vcpu->arch.nmi_source_inject : 0); -+ vcpu->arch.nmi_source_inject = 0; -+ } -+ - vmx_clear_hlt(vcpu); - } - -@@ -7368,6 +7375,9 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - switch (type) { - case INTR_TYPE_NMI_INTR: - vcpu->arch.nmi_injected = true; -+ -+ if (is_fred_enabled(vcpu)) -+ vcpu->arch.nmi_source_inject = vmcs_read64(event_data_field); - /* - * SDM 3: 27.7.1.2 (September 2008) - * Clear bit "block by NMI" before VM entry if a NMI -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index eb0a1e537a88..c36f22135434 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -10719,6 +10719,11 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, - return r; - } - -+static inline bool kvm_is_nmi_source_enabled(struct kvm_vcpu *vcpu) -+{ -+ return is_fred_enabled(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE); -+} -+ - static void process_nmi(struct kvm_vcpu *vcpu) - { - unsigned int limit; -@@ -10733,7 +10738,8 @@ static void process_nmi(struct kvm_vcpu *vcpu) - * blocks NMIs). KVM will immediately inject one of the two NMIs, and - * will request an NMI window to handle the second NMI. - */ -- if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected) -+ if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected || -+ kvm_is_nmi_source_enabled(vcpu)) - limit = 1; - else - limit = 2; -@@ -10747,13 +10753,22 @@ static void process_nmi(struct kvm_vcpu *vcpu) - - vcpu->arch.nmi_pending += atomic_xchg(&vcpu->arch.nmi_queued, 0); - vcpu->arch.nmi_pending = min(vcpu->arch.nmi_pending, limit); -+ vcpu->arch.nmi_source_inject |= atomic_xchg(&vcpu->arch.nmi_source_pending, 0); - - if (vcpu->arch.nmi_pending && - (kvm_x86_call(set_vnmi_pending)(vcpu))) - vcpu->arch.nmi_pending--; - -- if (vcpu->arch.nmi_pending) -+ if (vcpu->arch.nmi_pending) { - kvm_make_request(KVM_REQ_EVENT, vcpu); -+ /* -+ * In case nmi source supported, if new NMI arrives when vCPU is -+ * trying NMI injection, it can be coalesced together and requires -+ * one elimination of nmi_pending. -+ */ -+ if (vcpu->arch.nmi_injected && kvm_is_nmi_source_enabled(vcpu)) -+ vcpu->arch.nmi_pending--; -+ } - } - - /* Return total number of NMIs pending injection to the VM */ -@@ -12780,6 +12795,8 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - vcpu->arch.smi_pending = 0; - vcpu->arch.smi_count = 0; - atomic_set(&vcpu->arch.nmi_queued, 0); -+ atomic_set(&vcpu->arch.nmi_source_pending, 0); -+ vcpu->arch.nmi_source_inject = 0; - vcpu->arch.nmi_pending = 0; - vcpu->arch.nmi_injected = false; - kvm_clear_interrupt_queue(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi b/SPECS/kernel-rt/0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi new file mode 100644 index 000000000..51b9b3d09 --- /dev/null +++ b/SPECS/kernel-rt/0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi @@ -0,0 +1,36 @@ +From d1fce9be69dbc31277ccd237957469626601d47a Mon Sep 17 00:00:00 2001 +From: Zeng Guang +Date: Wed, 31 Jan 2024 21:52:30 +0800 +Subject: [PATCH 42/44] KVM: x86: Advise NMI Source to user space + +NMI Source can program unique values into the NMI vector at the originating +site which is generally ignored on handling of NMI interrupt, and repurpose +it to identify the different event source. It allows the software NMI exception +handler to call corresponding function reliably and efficiently without checking +all sources. + +The CPUID bit definition to support NMI Source: +CPUID.(EAX=07H.ECX=1):EAX.NMI_SOURCE[bit 20] + +Advertise NMI Source to user space for virtualization support. + +Signed-off-by: Zeng Guang +--- + arch/x86/kvm/cpuid.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c +index f4ff5ccbcf1e4..e4726b76915c9 100644 +--- a/arch/x86/kvm/cpuid.c ++++ b/arch/x86/kvm/cpuid.c +@@ -1019,6 +1019,7 @@ void kvm_set_cpu_caps(void) + F(AMX_FP16), + F(AVX_IFMA), + F(LAM), ++ F(NMI_SOURCE), + ); + + kvm_cpu_cap_init(CPUID_7_1_ECX, +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf b/SPECS/kernel-rt/0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf deleted file mode 100644 index 1389e3b10..000000000 --- a/SPECS/kernel-rt/0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf +++ /dev/null @@ -1,77 +0,0 @@ -From e6c9259fc57a374c6a1f0cbf22417b3a158273e2 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Mon, 4 Aug 2025 08:51:58 -0700 -Subject: [PATCH 042/100] KVM: x86: Unconditionally handle - MSR_IA32_TSC_DEADLINE in fastpath exits - -Drop the fastpath VM-Exit requirement that KVM can use the hypervisor -timer to emulate the APIC timer in TSC deadline mode. I.e. unconditionally -handle MSR_IA32_TSC_DEADLINE WRMSRs in the fastpath. Restricting the -fastpath to *maybe* using the VMX preemption timer is ineffective and -unnecessary. - -If the requested deadline can't be programmed into the VMX preemption -timer, KVM will fall back to hrtimers, i.e. the restriction is ineffective -as far as preventing any kind of worst case scenario. - -But guarding against a worst case scenario is completely unnecessary as -the "slow" path, start_sw_tscdeadline() => hrtimer_start(), explicitly -disables IRQs. In fact, the worst case scenario is when KVM thinks it -can use the VMX preemption timer, as KVM will eat the overhead of calling -into vmx_set_hv_timer() and falling back to hrtimers. - -Opportunistically limit kvm_can_use_hv_timer() to lapic.c as the fastpath -code was the only external user. - -Stating the obvious, this allows handling MSR_IA32_TSC_DEADLINE writes in -the fastpath on AMD CPUs. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/lapic.c | 2 +- - arch/x86/kvm/lapic.h | 1 - - arch/x86/kvm/x86.c | 3 --- - 3 files changed, 1 insertion(+), 5 deletions(-) - -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index bd3232dd7a63..e19545b8cc98 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -130,7 +130,7 @@ static bool kvm_can_post_timer_interrupt(struct kvm_vcpu *vcpu) - (kvm_mwait_in_guest(vcpu->kvm) || kvm_hlt_in_guest(vcpu->kvm)); - } - --bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu) -+static bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu) - { - return kvm_x86_ops.set_hv_timer - && !(kvm_mwait_in_guest(vcpu->kvm) || -diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h -index 1b2d408816aa..8b00e29741de 100644 ---- a/arch/x86/kvm/lapic.h -+++ b/arch/x86/kvm/lapic.h -@@ -249,7 +249,6 @@ void kvm_lapic_switch_to_hv_timer(struct kvm_vcpu *vcpu); - void kvm_lapic_expired_hv_timer(struct kvm_vcpu *vcpu); - bool kvm_lapic_hv_timer_in_use(struct kvm_vcpu *vcpu); - void kvm_lapic_restart_hv_timer(struct kvm_vcpu *vcpu); --bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu); - - static inline enum lapic_mode kvm_apic_mode(u64 apic_base) - { -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index ea117c4b20c8..63ca9185d133 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2150,9 +2150,6 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data - - static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) - { -- if (!kvm_can_use_hv_timer(vcpu)) -- return 1; -- - kvm_set_lapic_tscdeadline_msr(vcpu, data); - return 0; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0042-wifi-ath12k-Fix-MSDU-buffer-types-handling-in-RX-err.patch b/SPECS/kernel-rt/0042-wifi-ath12k-Fix-MSDU-buffer-types-handling-in-RX-err.patch deleted file mode 100644 index 904b8ceb5..000000000 --- a/SPECS/kernel-rt/0042-wifi-ath12k-Fix-MSDU-buffer-types-handling-in-RX-err.patch +++ /dev/null @@ -1,178 +0,0 @@ -From 4e6d41eb05d214a7bd69d6047ee4bbcccc6ed445 Mon Sep 17 00:00:00 2001 -From: Sarika Sharma -Date: Tue, 30 Sep 2025 14:45:50 +0530 -Subject: [PATCH 42/51] wifi: ath12k: Fix MSDU buffer types handling in RX - error path - -Currently, packets received on the REO exception ring from -unassociated peers are of MSDU buffer type, while the driver expects -link descriptor type packets. These packets are not parsed further due -to a return check on packet type in ath12k_hal_desc_reo_parse_err(), -but the associated skb is not freed. This may lead to kernel -crashes and buffer leaks. - -Hence to fix, update the RX error handler to explicitly drop -MSDU buffer type packets received on the REO exception ring. -This prevents further processing of invalid packets and ensures -stability in the RX error handling path. - -Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 - -Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") -Signed-off-by: Sarika Sharma -Reviewed-by: Baochen Qiang -Reviewed-by: Vasanthakumar Thiagarajan -Link: https://patch.msgid.link/20250930091551.3305312-2-sarika.sharma@oss.qualcomm.com -Signed-off-by: Jeff Johnson ---- - drivers/net/wireless/ath/ath12k/dp_rx.c | 70 ++++++++++++++++++++++-- - drivers/net/wireless/ath/ath12k/hal_rx.c | 10 +--- - 2 files changed, 66 insertions(+), 14 deletions(-) - -diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c -index 9048818984f1..0be911b4f316 100644 ---- a/drivers/net/wireless/ath/ath12k/dp_rx.c -+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c -@@ -1,7 +1,7 @@ - // SPDX-License-Identifier: BSD-3-Clause-Clear - /* - * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. -- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - - #include -@@ -3690,6 +3690,48 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, - return 0; - } - -+static int ath12k_dp_h_msdu_buffer_type(struct ath12k_base *ab, -+ struct list_head *list, -+ struct hal_reo_dest_ring *desc) -+{ -+ struct ath12k_rx_desc_info *desc_info; -+ struct ath12k_skb_rxcb *rxcb; -+ struct sk_buff *msdu; -+ u64 desc_va; -+ -+ desc_va = (u64)le32_to_cpu(desc->buf_va_hi) << 32 | -+ le32_to_cpu(desc->buf_va_lo); -+ desc_info = (struct ath12k_rx_desc_info *)(uintptr_t)desc_va; -+ if (!desc_info) { -+ u32 cookie; -+ -+ cookie = le32_get_bits(desc->buf_addr_info.info1, -+ BUFFER_ADDR_INFO1_SW_COOKIE); -+ desc_info = ath12k_dp_get_rx_desc(ab, cookie); -+ if (!desc_info) { -+ ath12k_warn(ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", -+ cookie); -+ return -EINVAL; -+ } -+ } -+ -+ if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) { -+ ath12k_warn(ab, "rx exception, magic check failed with value: %u\n", -+ desc_info->magic); -+ return -EINVAL; -+ } -+ -+ msdu = desc_info->skb; -+ desc_info->skb = NULL; -+ list_add_tail(&desc_info->list, list); -+ rxcb = ATH12K_SKB_RXCB(msdu); -+ dma_unmap_single(ab->dev, rxcb->paddr, msdu->len + skb_tailroom(msdu), -+ DMA_FROM_DEVICE); -+ dev_kfree_skb_any(msdu); -+ -+ return 0; -+} -+ - int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - int budget) - { -@@ -3734,6 +3776,26 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - drop = false; - ab->device_stats.err_ring_pkts++; - -+ hw_link_id = le32_get_bits(reo_desc->info0, -+ HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); -+ device_id = hw_links[hw_link_id].device_id; -+ partner_ab = ath12k_ag_to_ab(ag, device_id); -+ -+ /* Below case is added to handle data packet from un-associated clients. -+ * As it is expected that AST lookup will fail for -+ * un-associated station's data packets. -+ */ -+ if (le32_get_bits(reo_desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE) == -+ HAL_REO_DEST_RING_BUFFER_TYPE_MSDU) { -+ if (!ath12k_dp_h_msdu_buffer_type(partner_ab, -+ &rx_desc_used_list[device_id], -+ reo_desc)) { -+ num_buffs_reaped[device_id]++; -+ tot_n_bufs_reaped++; -+ } -+ goto next_desc; -+ } -+ - ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr, - &desc_bank); - if (ret) { -@@ -3742,11 +3804,6 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - continue; - } - -- hw_link_id = le32_get_bits(reo_desc->info0, -- HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); -- device_id = hw_links[hw_link_id].device_id; -- partner_ab = ath12k_ag_to_ab(ag, device_id); -- - pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params, - hw_links[hw_link_id].pdev_idx); - ar = partner_ab->pdevs[pdev_id].ar; -@@ -3795,6 +3852,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - } - } - -+next_desc: - if (tot_n_bufs_reaped >= quota) { - tot_n_bufs_reaped = quota; - goto exit; -diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c -index 48aa48c48606..805c31e4243d 100644 ---- a/drivers/net/wireless/ath/ath12k/hal_rx.c -+++ b/drivers/net/wireless/ath/ath12k/hal_rx.c -@@ -1,7 +1,7 @@ - // SPDX-License-Identifier: BSD-3-Clause-Clear - /* - * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. -- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - - #include "debug.h" -@@ -320,7 +320,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab, - { - enum hal_reo_dest_ring_push_reason push_reason; - enum hal_reo_dest_ring_error_code err_code; -- u32 cookie, val; -+ u32 cookie; - - push_reason = le32_get_bits(desc->info0, - HAL_REO_DEST_RING_INFO0_PUSH_REASON); -@@ -335,12 +335,6 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab, - return -EINVAL; - } - -- val = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE); -- if (val != HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) { -- ath12k_warn(ab, "expected buffer type link_desc"); -- return -EINVAL; -- } -- - ath12k_hal_rx_reo_ent_paddr_get(ab, &desc->buf_addr_info, paddr, &cookie); - *desc_bank = u32_get_bits(cookie, DP_LINK_DESC_BANK_MASK); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf b/SPECS/kernel-rt/0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf deleted file mode 100644 index 8c6c8bc28..000000000 --- a/SPECS/kernel-rt/0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf +++ /dev/null @@ -1,74 +0,0 @@ -From 78d9b7401b4a248cdc778a216257ed5df6f33467 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 08:18:23 -0700 -Subject: [PATCH 043/100] KVM: x86: Acquire SRCU in WRMSR fastpath iff - instruction needs to be skipped - -Acquire SRCU in the WRMSR fastpath if and only if an instruction needs to -be skipped, i.e. only if the fastpath succeeds. The reasoning in commit -3f2739bd1e0b ("KVM: x86: Acquire SRCU read lock when handling fastpath MSR -writes") about "avoid having to play whack-a-mole" seems sound, but in -hindsight unconditionally acquiring SRCU does more harm than good. - -While acquiring/releasing SRCU isn't slow per se, the things that are -_protected_ by kvm->srcu are generally safe to access only in the "slow" -VM-Exit path. E.g. accessing memslots in generic helpers is never safe, -because accessing guest memory with IRQs disabled is unless unsafe (except -when kvm_vcpu_read_guest_atomic() is used, but that API should never be -used in emulation helpers). - -In other words, playing whack-a-mole is actually desirable in this case, -because every access to an asset protected by kvm->srcu warrants further -scrutiny. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 21 ++++++++------------- - 1 file changed, 8 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 63ca9185d133..69c668f4d2b6 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2158,10 +2158,8 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { - u32 msr = kvm_rcx_read(vcpu); - u64 data; -- fastpath_t ret; - bool handled; -- -- kvm_vcpu_srcu_read_lock(vcpu); -+ int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -@@ -2177,19 +2175,16 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - break; - } - -- if (handled) { -- if (!kvm_skip_emulated_instruction(vcpu)) -- ret = EXIT_FASTPATH_EXIT_USERSPACE; -- else -- ret = EXIT_FASTPATH_REENTER_GUEST; -- trace_kvm_msr_write(msr, data); -- } else { -- ret = EXIT_FASTPATH_NONE; -- } -+ if (!handled) -+ return EXIT_FASTPATH_NONE; - -+ kvm_vcpu_srcu_read_lock(vcpu); -+ r = kvm_skip_emulated_instruction(vcpu); - kvm_vcpu_srcu_read_unlock(vcpu); - -- return ret; -+ trace_kvm_msr_write(msr, data); -+ -+ return r ? EXIT_FASTPATH_REENTER_GUEST : EXIT_FASTPATH_EXIT_USERSPACE; - } - EXPORT_SYMBOL_GPL(handle_fastpath_set_msr_irqoff); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi b/SPECS/kernel-rt/0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi deleted file mode 100644 index 85c388341..000000000 --- a/SPECS/kernel-rt/0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi +++ /dev/null @@ -1,36 +0,0 @@ -From f84be478f482ad9a7816adafbe48750b9b0fe9f0 Mon Sep 17 00:00:00 2001 -From: Zeng Guang -Date: Wed, 31 Jan 2024 21:52:30 +0800 -Subject: [PATCH 43/44] KVM: x86: Advise NMI Source to user space - -NMI Source can program unique values into the NMI vector at the originating -site which is generally ignored on handling of NMI interrupt, and repurpose -it to identify the different event source. It allows the software NMI exception -handler to call corresponding function reliably and efficiently without checking -all sources. - -The CPUID bit definition to support NMI Source: -CPUID.(EAX=07H.ECX=1):EAX.NMI_SOURCE[bit 20] - -Advertise NMI Source to user space for virtualization support. - -Signed-off-by: Zeng Guang ---- - arch/x86/kvm/cpuid.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index 15217805c007..0a4f27ce2b06 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -1001,6 +1001,7 @@ void kvm_set_cpu_caps(void) - F(AMX_FP16), - F(AVX_IFMA), - F(LAM), -+ F(NMI_SOURCE), - ); - - kvm_cpu_cap_init(CPUID_7_1_EDX, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0043-ntfs3-fix-uninit-memory-after-failed-mi_read-in-mi_f.patch b/SPECS/kernel-rt/0043-ntfs3-fix-uninit-memory-after-failed-mi_read-in-mi_f.patch deleted file mode 100644 index 6dcb229e6..000000000 --- a/SPECS/kernel-rt/0043-ntfs3-fix-uninit-memory-after-failed-mi_read-in-mi_f.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 73d642bb9276696a16adb9b5cd5ce7e810e41ae6 Mon Sep 17 00:00:00 2001 -From: Raphael Pinsonneault-Thibeault -Date: Sun, 12 Oct 2025 16:16:34 -0400 -Subject: [PATCH 43/51] ntfs3: fix uninit memory after failed mi_read in - mi_format_new -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fix a KMSAN un-init bug found by syzkaller. - -ntfs_get_bh() expects a buffer from sb_getblk(), that buffer may not be -uptodate. We do not bring the buffer uptodate before setting it as -uptodate. If the buffer were to not be uptodate, it could mean adding a -buffer with un-init data to the mi record. Attempting to load that record -will trigger KMSAN. - -Avoid this by setting the buffer as uptodate, if it’s not already, by -overwriting it. - -Reported-by: syzbot+7a2ba6b7b66340cff225@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=7a2ba6b7b66340cff225 -Tested-by: syzbot+7a2ba6b7b66340cff225@syzkaller.appspotmail.com -Fixes: 4342306f0f0d5 ("fs/ntfs3: Add file operations and implementation") -Signed-off-by: Raphael Pinsonneault-Thibeault -Signed-off-by: Konstantin Komarov ---- - fs/ntfs3/fsntfs.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c -index c7a2f191254d..488bdefc1ad0 100644 ---- a/fs/ntfs3/fsntfs.c -+++ b/fs/ntfs3/fsntfs.c -@@ -1349,7 +1349,13 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, - } - if (buffer_locked(bh)) - __wait_on_buffer(bh); -- set_buffer_uptodate(bh); -+ -+ lock_buffer(bh); -+ if (!buffer_uptodate(bh)) { -+ memset(bh->b_data, 0, blocksize); -+ set_buffer_uptodate(bh); -+ } -+ unlock_buffer(bh); - } else { - bh = ntfs_bread(sb, block); - if (!bh) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0043-x86-fred-Enable-FRED-by-default.nmi b/SPECS/kernel-rt/0043-x86-fred-Enable-FRED-by-default.nmi new file mode 100644 index 000000000..07e6478dc --- /dev/null +++ b/SPECS/kernel-rt/0043-x86-fred-Enable-FRED-by-default.nmi @@ -0,0 +1,31 @@ +From fe4c0a9c022d16919105f76a56ee67958ef97555 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Fri, 14 Feb 2025 10:31:18 -0800 +Subject: [PATCH 43/44] x86/fred: Enable FRED by default + +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kernel/cpu/common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +index a2a251549fa15..40ad8a394cdc8 100644 +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -1721,7 +1721,7 @@ static void __init cpu_parse_early_param(void) + + /* Minimize the gap between FRED is available and available but disabled. */ + arglen = cmdline_find_option(boot_command_line, "fred", arg, sizeof(arg)); +- if (arglen != 2 || strncmp(arg, "on", 2)) ++ if (arglen == 3 && !strncmp(arg, "off", 3)) + setup_clear_cpu_cap(X86_FEATURE_FRED); + + arglen = cmdline_find_option(boot_command_line, "clearcpuid", arg, sizeof(arg)); +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0044-EDAC-ieh-Fix-a-compile-error.nmi b/SPECS/kernel-rt/0044-EDAC-ieh-Fix-a-compile-error.nmi deleted file mode 100644 index 5f53780fc..000000000 --- a/SPECS/kernel-rt/0044-EDAC-ieh-Fix-a-compile-error.nmi +++ /dev/null @@ -1,30 +0,0 @@ -From 0ff6292174c874dc857eaba8b8b40e0318329a3f Mon Sep 17 00:00:00 2001 -From: "Yu, Tao1" -Date: Thu, 10 Jul 2025 16:19:06 +0000 -Subject: [PATCH 44/44] EDAC/ieh: Fix a compile error - -NMI add a source vector argument to register_nmi_handler(), -EDAC sould use the new interface. - -Fixes: 17ade7d95513 ("EDAC/ieh: Add I/O device EDAC driver for Intel CPUs with IEH") -Signed-off-by: Tao Yu ---- - drivers/edac/ieh_edac.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/edac/ieh_edac.c b/drivers/edac/ieh_edac.c -index 46eca4bddea8..d108d8730438 100644 ---- a/drivers/edac/ieh_edac.c -+++ b/drivers/edac/ieh_edac.c -@@ -708,7 +708,7 @@ static int register_err_handler(void) - if (has_notification_by(IEH_NMI)) { - init_irq_work(&ieh_irq_work, ieh_irq_work_cb); - rc = register_nmi_handler(NMI_SERR, ieh_nmi_handler, -- 0, IEH_NMI_NAME); -+ 0, IEH_NMI_NAME, 0); - if (rc) { - ieh_printk(KERN_ERR, "Can't register NMI handler\n"); - return rc; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf b/SPECS/kernel-rt/0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf deleted file mode 100644 index 3a0b8862a..000000000 --- a/SPECS/kernel-rt/0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf +++ /dev/null @@ -1,47 +0,0 @@ -From 147e029e5b2a48a857d2e3b3a2e113730fdd6329 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 08:19:22 -0700 -Subject: [PATCH 044/100] KVM: x86: Unconditionally grab data from EDX:EAX in - WRMSR fastpath - -Always grab EDX:EAX in the WRMSR fastpath to deduplicate and simplify the -case statements, and to prepare for handling immediate variants of WRMSRNS -in the fastpath (the data register is explicitly provided in that case). -There's no harm in reading the registers, as their values are always -available, i.e. don't require VMREADs (or similarly slow operations). - -No real functional change intended. - -Cc: Xin Li -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 69c668f4d2b6..e6c221f9b92e 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2156,18 +2156,16 @@ static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) - - fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { -+ u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); -- u64 data; - bool handled; - int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -- data = kvm_read_edx_eax(vcpu); - handled = !handle_fastpath_set_x2apic_icr_irqoff(vcpu, data); - break; - case MSR_IA32_TSC_DEADLINE: -- data = kvm_read_edx_eax(vcpu); - handled = !handle_fastpath_set_tscdeadline(vcpu, data); - break; - default: --- -2.43.0 - diff --git a/SPECS/kernel-rt/0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi b/SPECS/kernel-rt/0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi new file mode 100644 index 000000000..808438267 --- /dev/null +++ b/SPECS/kernel-rt/0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi @@ -0,0 +1,47 @@ +From 86ced05172c8e653565845642f8619f2dcf6ab42 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Fri, 5 Dec 2025 10:40:18 -0800 +Subject: [PATCH 44/44] fixup! KVM: VMX: Handle #MCs on VM-Enter/TD-Enter + outside of the fastpath + +The base code of FRED/NMI_source contains a problemtic commit which has +been fixed in the new code base. Backport the new implementation from +https://lore.kernel.org/all/20251118222328.2265758-3-seanjc@google.com/ + +Received this patch from Chenyi Qiang , applied +manually. + +Signed-off-by: Sohil Mehta +--- + arch/x86/kvm/vmx/vmx.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 2515b8afd2d8f..4491c54b71556 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7255,10 +7255,19 @@ void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu) + if (to_vt(vcpu)->emulation_required) + return; + +- if (vmx_get_exit_reason(vcpu).basic == EXIT_REASON_EXTERNAL_INTERRUPT) ++ switch (vmx_get_exit_reason(vcpu).basic) { ++ case EXIT_REASON_EXTERNAL_INTERRUPT: + handle_external_interrupt_irqoff(vcpu, vmx_get_intr_info(vcpu)); +- else if (vmx_get_exit_reason(vcpu).basic == EXIT_REASON_EXCEPTION_NMI) ++ break; ++ case EXIT_REASON_EXCEPTION_NMI: + handle_exception_irqoff(vcpu, vmx_get_intr_info(vcpu)); ++ break; ++ case EXIT_REASON_MCE_DURING_VMENTRY: ++ kvm_machine_check(); ++ break; ++ default: ++ break; ++ } + } + + /* +-- +2.43.0 + diff --git a/SPECS/kernel-rt/0044-ntfs3-Fix-uninit-buffer-allocated-by-__getname.patch b/SPECS/kernel-rt/0044-ntfs3-Fix-uninit-buffer-allocated-by-__getname.patch deleted file mode 100644 index b4dbcb77f..000000000 --- a/SPECS/kernel-rt/0044-ntfs3-Fix-uninit-buffer-allocated-by-__getname.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0f2007ed423b7f4b46a6e36432b33793ff24a857 Mon Sep 17 00:00:00 2001 -From: Sidharth Seela -Date: Tue, 23 Sep 2025 12:10:16 +0530 -Subject: [PATCH 44/51] ntfs3: Fix uninit buffer allocated by __getname() - -Fix uninit errors caused after buffer allocation given to 'de'; by -initializing the buffer with zeroes. The fix was found by using KMSAN. - -Reported-by: syzbot+332bd4e9d148f11a87dc@syzkaller.appspotmail.com -Fixes: 78ab59fee07f2 ("fs/ntfs3: Rework file operations") -Signed-off-by: Sidharth Seela -Signed-off-by: Konstantin Komarov ---- - fs/ntfs3/inode.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c -index 7a89d31ccc1c..68862da31345 100644 ---- a/fs/ntfs3/inode.c -+++ b/fs/ntfs3/inode.c -@@ -1717,6 +1717,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) - de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!de) - return -ENOMEM; -+ memset(de, 0, PATH_MAX); - - /* Mark rw ntfs as dirty. It will be cleared at umount. */ - ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf b/SPECS/kernel-rt/0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf deleted file mode 100644 index fef20b430..000000000 --- a/SPECS/kernel-rt/0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf +++ /dev/null @@ -1,83 +0,0 @@ -From 4f8df081826ebbd3b171763e977e72f4dbfffa02 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 12:11:20 -0700 -Subject: [PATCH 045/100] KVM: x86: Fold WRMSR fastpath helpers into the main - handler - -Fold the per-MSR WRMSR fastpath helpers into the main handler now that the -IPI path in particular is relatively tiny. In addition to eliminating a -decent amount of boilerplate, this removes the ugly -errno/1/0 => bool -conversion (which is "necessitated" by kvm_x2apic_icr_write_fast()). - -Opportunistically drop the comment about IPIs, as the purpose of the -fastpath is hopefully self-evident, and _if_ it needs more documentation, -the documentation (and rules!) should be placed in a more central location. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 34 +++++----------------------------- - 1 file changed, 5 insertions(+), 29 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index e6c221f9b92e..a4441f036929 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2133,48 +2133,24 @@ static inline bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu) - kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending(); - } - --/* -- * The fast path for frequent and performance sensitive wrmsr emulation, -- * i.e. the sending of IPI, sending IPI early in the VM-Exit flow reduces -- * the latency of virtual IPI by avoiding the expensive bits of transitioning -- * from guest to host, e.g. reacquiring KVM's SRCU lock. In contrast to the -- * other cases which must be called after interrupts are enabled on the host. -- */ --static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data) --{ -- if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic)) -- return 1; -- -- return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); --} -- --static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) --{ -- kvm_set_lapic_tscdeadline_msr(vcpu, data); -- return 0; --} -- - fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { - u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); -- bool handled; - int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -- handled = !handle_fastpath_set_x2apic_icr_irqoff(vcpu, data); -+ if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic) || -+ kvm_x2apic_icr_write_fast(vcpu->arch.apic, data)) -+ return EXIT_FASTPATH_NONE; - break; - case MSR_IA32_TSC_DEADLINE: -- handled = !handle_fastpath_set_tscdeadline(vcpu, data); -+ kvm_set_lapic_tscdeadline_msr(vcpu, data); - break; - default: -- handled = false; -- break; -- } -- -- if (!handled) - return EXIT_FASTPATH_NONE; -+ } - - kvm_vcpu_srcu_read_lock(vcpu); - r = kvm_skip_emulated_instruction(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf b/SPECS/kernel-rt/0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf deleted file mode 100644 index 679bcff47..000000000 --- a/SPECS/kernel-rt/0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf +++ /dev/null @@ -1,139 +0,0 @@ -From 0c532d8388151e10330a0c9b3390f205ac6a3283 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 17:34:40 -0700 -Subject: [PATCH 046/100] KVM: x86/pmu: Move kvm_init_pmu_capability() to pmu.c - -Move kvm_init_pmu_capability() to pmu.c so that future changes can access -variables that have no business being visible outside of pmu.c. -kvm_init_pmu_capability() is called once per module load, there's is zero -reason it needs to be inlined. - -No functional change intended. - -Cc: Dapeng Mi -Cc: Sandipan Das -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ - arch/x86/kvm/pmu.h | 47 +--------------------------------------------- - 2 files changed, 48 insertions(+), 46 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 75e9cfc689f8..eb17d90916ea 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -96,6 +96,53 @@ void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) - #undef __KVM_X86_PMU_OP - } - -+void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) -+{ -+ bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; -+ int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; -+ -+ /* -+ * Hybrid PMUs don't play nice with virtualization without careful -+ * configuration by userspace, and KVM's APIs for reporting supported -+ * vPMU features do not account for hybrid PMUs. Disable vPMU support -+ * for hybrid PMUs until KVM gains a way to let userspace opt-in. -+ */ -+ if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) -+ enable_pmu = false; -+ -+ if (enable_pmu) { -+ perf_get_x86_pmu_capability(&kvm_pmu_cap); -+ -+ /* -+ * WARN if perf did NOT disable hardware PMU if the number of -+ * architecturally required GP counters aren't present, i.e. if -+ * there are a non-zero number of counters, but fewer than what -+ * is architecturally required. -+ */ -+ if (!kvm_pmu_cap.num_counters_gp || -+ WARN_ON_ONCE(kvm_pmu_cap.num_counters_gp < min_nr_gp_ctrs)) -+ enable_pmu = false; -+ else if (is_intel && !kvm_pmu_cap.version) -+ enable_pmu = false; -+ } -+ -+ if (!enable_pmu) { -+ memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); -+ return; -+ } -+ -+ kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); -+ kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp, -+ pmu_ops->MAX_NR_GP_COUNTERS); -+ kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed, -+ KVM_MAX_NR_FIXED_COUNTERS); -+ -+ kvm_pmu_eventsel.INSTRUCTIONS_RETIRED = -+ perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS); -+ kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED = -+ perf_get_hw_event_config(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); -+} -+ - static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 103604c4b33b..71796270918f 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -180,52 +180,7 @@ static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) - extern struct x86_pmu_capability kvm_pmu_cap; - extern struct kvm_pmu_emulated_event_selectors kvm_pmu_eventsel; - --static inline void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) --{ -- bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; -- int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; -- -- /* -- * Hybrid PMUs don't play nice with virtualization without careful -- * configuration by userspace, and KVM's APIs for reporting supported -- * vPMU features do not account for hybrid PMUs. Disable vPMU support -- * for hybrid PMUs until KVM gains a way to let userspace opt-in. -- */ -- if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) -- enable_pmu = false; -- -- if (enable_pmu) { -- perf_get_x86_pmu_capability(&kvm_pmu_cap); -- -- /* -- * WARN if perf did NOT disable hardware PMU if the number of -- * architecturally required GP counters aren't present, i.e. if -- * there are a non-zero number of counters, but fewer than what -- * is architecturally required. -- */ -- if (!kvm_pmu_cap.num_counters_gp || -- WARN_ON_ONCE(kvm_pmu_cap.num_counters_gp < min_nr_gp_ctrs)) -- enable_pmu = false; -- else if (is_intel && !kvm_pmu_cap.version) -- enable_pmu = false; -- } -- -- if (!enable_pmu) { -- memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); -- return; -- } -- -- kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); -- kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp, -- pmu_ops->MAX_NR_GP_COUNTERS); -- kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed, -- KVM_MAX_NR_FIXED_COUNTERS); -- -- kvm_pmu_eventsel.INSTRUCTIONS_RETIRED = -- perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS); -- kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED = -- perf_get_hw_event_config(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); --} -+void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); - - static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) - { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf b/SPECS/kernel-rt/0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf deleted file mode 100644 index b167d9aeb..000000000 --- a/SPECS/kernel-rt/0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf +++ /dev/null @@ -1,145 +0,0 @@ -From 973ca934c1905e7bf212d8e99c560b9490d2dce3 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 10:17:46 -0700 -Subject: [PATCH 047/100] KVM: x86/pmu: Add wrappers for counting emulated - instructions/branches - -Add wrappers for triggering instruction retired and branch retired PMU -events in anticipation of reworking the internal mechanisms to track -which PMCs need to be evaluated, e.g. to avoid having to walk and check -every PMC. - -Opportunistically bury "struct kvm_pmu_emulated_event_selectors" in pmu.c. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 22 ++++++++++++++++++---- - arch/x86/kvm/pmu.h | 9 ++------- - arch/x86/kvm/vmx/nested.c | 2 +- - arch/x86/kvm/x86.c | 6 +++--- - 4 files changed, 24 insertions(+), 15 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index eb17d90916ea..e1911b366c43 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -29,8 +29,11 @@ - struct x86_pmu_capability __read_mostly kvm_pmu_cap; - EXPORT_SYMBOL_GPL(kvm_pmu_cap); - --struct kvm_pmu_emulated_event_selectors __read_mostly kvm_pmu_eventsel; --EXPORT_SYMBOL_GPL(kvm_pmu_eventsel); -+struct kvm_pmu_emulated_event_selectors { -+ u64 INSTRUCTIONS_RETIRED; -+ u64 BRANCH_INSTRUCTIONS_RETIRED; -+}; -+static struct kvm_pmu_emulated_event_selectors __read_mostly kvm_pmu_eventsel; - - /* Precise Distribution of Instructions Retired (PDIR) */ - static const struct x86_cpu_id vmx_pebs_pdir_cpu[] = { -@@ -907,7 +910,7 @@ static inline bool cpl_is_matched(struct kvm_pmc *pmc) - select_user; - } - --void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) -+static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - { - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -@@ -944,7 +947,18 @@ void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - kvm_pmu_incr_counter(pmc); - } - } --EXPORT_SYMBOL_GPL(kvm_pmu_trigger_event); -+ -+void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu) -+{ -+ kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+} -+EXPORT_SYMBOL_GPL(kvm_pmu_instruction_retired); -+ -+void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu) -+{ -+ kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+} -+EXPORT_SYMBOL_GPL(kvm_pmu_branch_retired); - - static bool is_masked_filter_valid(const struct kvm_x86_pmu_event_filter *filter) - { -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 71796270918f..c4dbdc0e6fc5 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -23,11 +23,6 @@ - - #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED - --struct kvm_pmu_emulated_event_selectors { -- u64 INSTRUCTIONS_RETIRED; -- u64 BRANCH_INSTRUCTIONS_RETIRED; --}; -- - struct kvm_pmu_ops { - struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu, - unsigned int idx, u64 *mask); -@@ -178,7 +173,6 @@ static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) - } - - extern struct x86_pmu_capability kvm_pmu_cap; --extern struct kvm_pmu_emulated_event_selectors kvm_pmu_eventsel; - - void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); - -@@ -227,7 +221,8 @@ void kvm_pmu_init(struct kvm_vcpu *vcpu); - void kvm_pmu_cleanup(struct kvm_vcpu *vcpu); - void kvm_pmu_destroy(struct kvm_vcpu *vcpu); - int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp); --void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel); -+void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu); -+void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index b8ea1969113d..db2fd4eedc90 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3690,7 +3690,7 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) - return 1; - } - -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+ kvm_pmu_branch_retired(vcpu); - - if (CC(evmptrld_status == EVMPTRLD_VMFAIL)) - return nested_vmx_failInvalid(vcpu); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a4441f036929..f2b2eaaec6f8 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -8824,7 +8824,7 @@ int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu) - if (unlikely(!r)) - return 0; - -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+ kvm_pmu_instruction_retired(vcpu); - - /* - * rflags is the old, "raw" value of the flags. The new value has -@@ -9158,9 +9158,9 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - */ - if (!ctxt->have_exception || - exception_type(ctxt->exception.vector) == EXCPT_TRAP) { -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+ kvm_pmu_instruction_retired(vcpu); - if (ctxt->is_branch) -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+ kvm_pmu_branch_retired(vcpu); - kvm_rip_write(vcpu, ctxt->eip); - if (r && (ctxt->tf || (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP))) - r = kvm_vcpu_do_singlestep(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0047-wifi-ath11k-fix-peer-HE-MCS-assignment.patch b/SPECS/kernel-rt/0047-wifi-ath11k-fix-peer-HE-MCS-assignment.patch deleted file mode 100644 index 4f98c0af1..000000000 --- a/SPECS/kernel-rt/0047-wifi-ath11k-fix-peer-HE-MCS-assignment.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 5808c982fe02b6b3ba6b0d02de2c429a9e9c05bb Mon Sep 17 00:00:00 2001 -From: Baochen Qiang -Date: Fri, 17 Oct 2025 09:49:00 +0800 -Subject: [PATCH 47/51] wifi: ath11k: fix peer HE MCS assignment - -In ath11k_wmi_send_peer_assoc_cmd(), peer's transmit MCS is sent to -firmware as receive MCS while peer's receive MCS sent as transmit MCS, -which goes against firmwire's definition. - -While connecting to a misbehaved AP that advertises 0xffff (meaning not -supported) for 160 MHz transmit MCS map, firmware crashes due to 0xffff -is assigned to he_mcs->rx_mcs_set field. - - Ext Tag: HE Capabilities - [...] - Supported HE-MCS and NSS Set - [...] - Rx and Tx MCS Maps 160 MHz - [...] - Tx HE-MCS Map 160 MHz: 0xffff - -Swap the assignment to fix this issue. - -As the HE rate control mask is meant to limit our own transmit MCS, it -needs to go via he_mcs->rx_mcs_set field. With the aforementioned swapping -done, change is needed as well to apply it to the peer's receive MCS. - -Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41 -Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 - -Fixes: 61fe43e7216d ("ath11k: add support for setting fixed HE rate/gi/ltf") -Signed-off-by: Baochen Qiang -Reviewed-by: Vasanthakumar Thiagarajan -Link: https://patch.msgid.link/20251017-ath11k-mcs-assignment-v1-2-da40825c1783@oss.qualcomm.com -Signed-off-by: Jeff Johnson ---- - drivers/net/wireless/ath/ath11k/mac.c | 4 ++-- - drivers/net/wireless/ath/ath11k/wmi.c | 7 +++++-- - 2 files changed, 7 insertions(+), 4 deletions(-) - -diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c -index 0e41b5a91d66..c3353bf8f16d 100644 ---- a/drivers/net/wireless/ath/ath11k/mac.c -+++ b/drivers/net/wireless/ath/ath11k/mac.c -@@ -2522,10 +2522,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, - he_tx_mcs = v; - } - v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); -+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; - - v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160); -- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; - - arg->peer_he_mcs_count++; -@@ -2535,10 +2535,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, - - default: - v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); -+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; - - v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); -- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; - - arg->peer_he_mcs_count++; -diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c -index e3b444333dee..d2972f081262 100644 ---- a/drivers/net/wireless/ath/ath11k/wmi.c -+++ b/drivers/net/wireless/ath/ath11k/wmi.c -@@ -2088,8 +2088,11 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, - FIELD_PREP(WMI_TLV_LEN, - sizeof(*he_mcs) - TLV_HDR_SIZE); - -- he_mcs->rx_mcs_set = param->peer_he_tx_mcs_set[i]; -- he_mcs->tx_mcs_set = param->peer_he_rx_mcs_set[i]; -+ /* firmware interprets mcs->rx_mcs_set field as peer's -+ * RX capability -+ */ -+ he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i]; -+ he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i]; - ptr += sizeof(*he_mcs); - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf b/SPECS/kernel-rt/0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf deleted file mode 100644 index 5f6344198..000000000 --- a/SPECS/kernel-rt/0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf +++ /dev/null @@ -1,194 +0,0 @@ -From 2af921402abbff2c7480f7f0cdfdc66cf0e087ed Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 12:19:15 -0700 -Subject: [PATCH 048/100] KVM: x86/pmu: Calculate set of to-be-emulated PMCs at - time of WRMSRs - -Calculate and track PMCs that are counting instructions/branches retired -when the PMC's event selector (or fixed counter control) is modified -instead evaluating the event selector on-demand. Immediately recalc a -PMC's configuration on writes to avoid false negatives/positives when -KVM skips an emulated WRMSR, which is guaranteed to occur before the -main run loop processes KVM_REQ_PMU. - -Out of an abundance of caution, and because it's relatively cheap, recalc -reprogrammed PMCs in kvm_pmu_handle_event() as well. Recalculating in -response to KVM_REQ_PMU _should_ be unnecessary, but for now be paranoid -to avoid introducing easily-avoidable bugs in edge cases. The code can be -removed in the future if necessary, e.g. in the unlikely event that the -overhead of recalculating to-be-emulated PMCs is noticeable. - -Note! Deliberately don't check the PMU event filters, as doing so could -result in KVM consuming stale information. - -Tracking which PMCs are counting branches/instructions will allow grabbing -SRCU in the fastpath VM-Exit handlers if and only if a PMC event might be -triggered (to consult the event filters), and will also allow the upcoming -mediated PMU to do the right thing with respect to counting instructions -(the mediated PMU won't be able to update PMCs in the VM-Exit fastpath). - -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 3 ++ - arch/x86/kvm/pmu.c | 75 ++++++++++++++++++++++++--------- - arch/x86/kvm/pmu.h | 4 ++ - 3 files changed, 61 insertions(+), 21 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 7fb2bdcf42a8..95d7d727db03 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -579,6 +579,9 @@ struct kvm_pmu { - DECLARE_BITMAP(all_valid_pmc_idx, X86_PMC_IDX_MAX); - DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX); - -+ DECLARE_BITMAP(pmc_counting_instructions, X86_PMC_IDX_MAX); -+ DECLARE_BITMAP(pmc_counting_branches, X86_PMC_IDX_MAX); -+ - u64 ds_area; - u64 pebs_enable; - u64 pebs_enable_rsvd; -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e1911b366c43..b0f0275a2c2e 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -542,6 +542,47 @@ static int reprogram_counter(struct kvm_pmc *pmc) - eventsel & ARCH_PERFMON_EVENTSEL_INT); - } - -+static bool pmc_is_event_match(struct kvm_pmc *pmc, u64 eventsel) -+{ -+ /* -+ * Ignore checks for edge detect (all events currently emulated by KVM -+ * are always rising edges), pin control (unsupported by modern CPUs), -+ * and counter mask and its invert flag (KVM doesn't emulate multiple -+ * events in a single clock cycle). -+ * -+ * Note, the uppermost nibble of AMD's mask overlaps Intel's IN_TX (bit -+ * 32) and IN_TXCP (bit 33), as well as two reserved bits (bits 35:34). -+ * Checking the "in HLE/RTM transaction" flags is correct as the vCPU -+ * can't be in a transaction if KVM is emulating an instruction. -+ * -+ * Checking the reserved bits might be wrong if they are defined in the -+ * future, but so could ignoring them, so do the simple thing for now. -+ */ -+ return !((pmc->eventsel ^ eventsel) & AMD64_RAW_EVENT_MASK_NB); -+} -+ -+void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc) -+{ -+ bitmap_clear(pmu->pmc_counting_instructions, pmc->idx, 1); -+ bitmap_clear(pmu->pmc_counting_branches, pmc->idx, 1); -+ -+ /* -+ * Do NOT consult the PMU event filters, as the filters must be checked -+ * at the time of emulation to ensure KVM uses fresh information, e.g. -+ * omitting a PMC from a bitmap could result in a missed event if the -+ * filter is changed to allow counting the event. -+ */ -+ if (!pmc_speculative_in_use(pmc)) -+ return; -+ -+ if (pmc_is_event_match(pmc, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED)) -+ bitmap_set(pmu->pmc_counting_instructions, pmc->idx, 1); -+ -+ if (pmc_is_event_match(pmc, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED)) -+ bitmap_set(pmu->pmc_counting_branches, pmc->idx, 1); -+} -+EXPORT_SYMBOL_GPL(kvm_pmu_recalc_pmc_emulation); -+ - void kvm_pmu_handle_event(struct kvm_vcpu *vcpu) - { - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); -@@ -577,6 +618,9 @@ void kvm_pmu_handle_event(struct kvm_vcpu *vcpu) - */ - if (unlikely(pmu->need_cleanup)) - kvm_pmu_cleanup(vcpu); -+ -+ kvm_for_each_pmc(pmu, pmc, bit, bitmap) -+ kvm_pmu_recalc_pmc_emulation(pmu, pmc); - } - - int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx) -@@ -910,7 +954,8 @@ static inline bool cpl_is_matched(struct kvm_pmc *pmc) - select_user; - } - --static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) -+static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, -+ const unsigned long *event_pmcs) - { - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -@@ -919,29 +964,17 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - - BUILD_BUG_ON(sizeof(pmu->global_ctrl) * BITS_PER_BYTE != X86_PMC_IDX_MAX); - -+ if (bitmap_empty(event_pmcs, X86_PMC_IDX_MAX)) -+ return; -+ - if (!kvm_pmu_has_perf_global_ctrl(pmu)) -- bitmap_copy(bitmap, pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX); -- else if (!bitmap_and(bitmap, pmu->all_valid_pmc_idx, -+ bitmap_copy(bitmap, event_pmcs, X86_PMC_IDX_MAX); -+ else if (!bitmap_and(bitmap, event_pmcs, - (unsigned long *)&pmu->global_ctrl, X86_PMC_IDX_MAX)) - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- /* -- * Ignore checks for edge detect (all events currently emulated -- * but KVM are always rising edges), pin control (unsupported -- * by modern CPUs), and counter mask and its invert flag (KVM -- * doesn't emulate multiple events in a single clock cycle). -- * -- * Note, the uppermost nibble of AMD's mask overlaps Intel's -- * IN_TX (bit 32) and IN_TXCP (bit 33), as well as two reserved -- * bits (bits 35:34). Checking the "in HLE/RTM transaction" -- * flags is correct as the vCPU can't be in a transaction if -- * KVM is emulating an instruction. Checking the reserved bits -- * might be wrong if they are defined in the future, but so -- * could ignoring them, so do the simple thing for now. -- */ -- if (((pmc->eventsel ^ eventsel) & AMD64_RAW_EVENT_MASK_NB) || -- !pmc_event_is_allowed(pmc) || !cpl_is_matched(pmc)) -+ if (!pmc_event_is_allowed(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); -@@ -950,13 +983,13 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - - void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu) - { -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+ kvm_pmu_trigger_event(vcpu, vcpu_to_pmu(vcpu)->pmc_counting_instructions); - } - EXPORT_SYMBOL_GPL(kvm_pmu_instruction_retired); - - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu) - { -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+ kvm_pmu_trigger_event(vcpu, vcpu_to_pmu(vcpu)->pmc_counting_branches); - } - EXPORT_SYMBOL_GPL(kvm_pmu_branch_retired); - -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index c4dbdc0e6fc5..c6e34db08264 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -176,8 +176,12 @@ extern struct x86_pmu_capability kvm_pmu_cap; - - void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); - -+void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc); -+ - static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) - { -+ kvm_pmu_recalc_pmc_emulation(pmc_to_pmu(pmc), pmc); -+ - set_bit(pmc->idx, pmc_to_pmu(pmc)->reprogram_pmi); - kvm_make_request(KVM_REQ_PMU, pmc->vcpu); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0048-wifi-rtl818x-rtl8187-Fix-potential-buffer-underflow-.patch b/SPECS/kernel-rt/0048-wifi-rtl818x-rtl8187-Fix-potential-buffer-underflow-.patch deleted file mode 100644 index c58a92fc9..000000000 --- a/SPECS/kernel-rt/0048-wifi-rtl818x-rtl8187-Fix-potential-buffer-underflow-.patch +++ /dev/null @@ -1,85 +0,0 @@ -From f60a0d857b51613bc16174202c14190b67e67ab7 Mon Sep 17 00:00:00 2001 -From: Seungjin Bae -Date: Mon, 17 Nov 2025 20:32:59 -0500 -Subject: [PATCH 48/51] wifi: rtl818x: rtl8187: Fix potential buffer underflow - in rtl8187_rx_cb() - -The rtl8187_rx_cb() calculates the rx descriptor header address -by subtracting its size from the skb tail pointer. -However, it does not validate if the received packet -(skb->len from urb->actual_length) is large enough to contain this -header. - -If a truncated packet is received, this will lead to a buffer -underflow, reading memory before the start of the skb data area, -and causing a kernel panic. - -Add length checks for both rtl8187 and rtl8187b descriptor headers -before attempting to access them, dropping the packet cleanly if the -check fails. - -Fixes: 6f7853f3cbe4 ("rtl8187: change rtl8187_dev.c to support RTL8187B (part 2)") -Signed-off-by: Seungjin Bae -Signed-off-by: Ping-Ke Shih -Link: https://patch.msgid.link/20251118013258.1789949-2-eeodqql09@gmail.com ---- - .../wireless/realtek/rtl818x/rtl8187/dev.c | 27 +++++++++++++------ - 1 file changed, 19 insertions(+), 8 deletions(-) - -diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c -index 0c5c66401daa..7aa2da0cd63c 100644 ---- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c -+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c -@@ -338,14 +338,16 @@ static void rtl8187_rx_cb(struct urb *urb) - spin_unlock_irqrestore(&priv->rx_queue.lock, f); - skb_put(skb, urb->actual_length); - -- if (unlikely(urb->status)) { -- dev_kfree_skb_irq(skb); -- return; -- } -+ if (unlikely(urb->status)) -+ goto free_skb; - - if (!priv->is_rtl8187b) { -- struct rtl8187_rx_hdr *hdr = -- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); -+ struct rtl8187_rx_hdr *hdr; -+ -+ if (skb->len < sizeof(struct rtl8187_rx_hdr)) -+ goto free_skb; -+ -+ hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - flags = le32_to_cpu(hdr->flags); - /* As with the RTL8187B below, the AGC is used to calculate - * signal strength. In this case, the scaling -@@ -355,8 +357,12 @@ static void rtl8187_rx_cb(struct urb *urb) - rx_status.antenna = (hdr->signal >> 7) & 1; - rx_status.mactime = le64_to_cpu(hdr->mac_time); - } else { -- struct rtl8187b_rx_hdr *hdr = -- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); -+ struct rtl8187b_rx_hdr *hdr; -+ -+ if (skb->len < sizeof(struct rtl8187b_rx_hdr)) -+ goto free_skb; -+ -+ hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - /* The Realtek datasheet for the RTL8187B shows that the RX - * header contains the following quantities: signal quality, - * RSSI, AGC, the received power in dB, and the measured SNR. -@@ -409,6 +415,11 @@ static void rtl8187_rx_cb(struct urb *urb) - skb_unlink(skb, &priv->rx_queue); - dev_kfree_skb_irq(skb); - } -+ return; -+ -+free_skb: -+ dev_kfree_skb_irq(skb); -+ return; - } - - static int rtl8187_init_urbs(struct ieee80211_hw *dev) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf b/SPECS/kernel-rt/0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf deleted file mode 100644 index fb06bdc2c..000000000 --- a/SPECS/kernel-rt/0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf +++ /dev/null @@ -1,85 +0,0 @@ -From 7f1ae2a996dee94172025d61038addf3b98a74ea Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:33:11 -0700 -Subject: [PATCH 049/100] KVM: x86/pmu: Rename pmc_speculative_in_use() to - pmc_is_locally_enabled() - -Rename pmc_speculative_in_use() to pmc_is_locally_enabled() to better -capture what it actually tracks, and to show its relationship to -pmc_is_globally_enabled(). While neither AMD nor Intel refer to event -selectors or the fixed counter control MSR as "local", it's the obvious -name to pair with "global". - -As for "speculative", there's absolutely nothing speculative about the -checks. E.g. for PMUs without PERF_GLOBAL_CTRL, from the guest's -perspective, the counters are "in use" without any qualifications. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 6 +++--- - arch/x86/kvm/pmu.h | 2 +- - arch/x86/kvm/vmx/pmu_intel.c | 2 +- - 3 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index b0f0275a2c2e..e73c2a44028b 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -493,7 +493,7 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc) - - static bool pmc_event_is_allowed(struct kvm_pmc *pmc) - { -- return pmc_is_globally_enabled(pmc) && pmc_speculative_in_use(pmc) && -+ return pmc_is_globally_enabled(pmc) && pmc_is_locally_enabled(pmc) && - check_pmu_event_filter(pmc); - } - -@@ -572,7 +572,7 @@ void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc) - * omitting a PMC from a bitmap could result in a missed event if the - * filter is changed to allow counting the event. - */ -- if (!pmc_speculative_in_use(pmc)) -+ if (!pmc_is_locally_enabled(pmc)) - return; - - if (pmc_is_event_match(pmc, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED)) -@@ -907,7 +907,7 @@ void kvm_pmu_cleanup(struct kvm_vcpu *vcpu) - pmu->pmc_in_use, X86_PMC_IDX_MAX); - - kvm_for_each_pmc(pmu, pmc, i, bitmask) { -- if (pmc->perf_event && !pmc_speculative_in_use(pmc)) -+ if (pmc->perf_event && !pmc_is_locally_enabled(pmc)) - pmc_stop_counter(pmc); - } - -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index c6e34db08264..5c3939e91f1d 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -160,7 +160,7 @@ static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr) - return NULL; - } - --static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) -+static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); - -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index e8b37a38fbba..b2a2c4ebf448 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -762,7 +762,7 @@ void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu) - int bit, hw_idx; - - kvm_for_each_pmc(pmu, pmc, bit, (unsigned long *)&pmu->global_ctrl) { -- if (!pmc_speculative_in_use(pmc) || -+ if (!pmc_is_locally_enabled(pmc) || - !pmc_is_globally_enabled(pmc) || !pmc->perf_event) - continue; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf b/SPECS/kernel-rt/0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf deleted file mode 100644 index 00b1a77ad..000000000 --- a/SPECS/kernel-rt/0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf +++ /dev/null @@ -1,57 +0,0 @@ -From c263d7ea7b358d134940a20445c26ee01702af71 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:36:47 -0700 -Subject: [PATCH 050/100] KVM: x86/pmu: Open code pmc_event_is_allowed() in its - callers - -Open code pmc_event_is_allowed() in its callers, as kvm_pmu_trigger_event() -only needs to check the event filter (both global and local enables are -consulted outside of the loop). - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 12 ++++-------- - 1 file changed, 4 insertions(+), 8 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e73c2a44028b..a495ab5d0556 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -491,12 +491,6 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc) - return is_fixed_event_allowed(filter, pmc->idx); - } - --static bool pmc_event_is_allowed(struct kvm_pmc *pmc) --{ -- return pmc_is_globally_enabled(pmc) && pmc_is_locally_enabled(pmc) && -- check_pmu_event_filter(pmc); --} -- - static int reprogram_counter(struct kvm_pmc *pmc) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -@@ -507,7 +501,8 @@ static int reprogram_counter(struct kvm_pmc *pmc) - - emulate_overflow = pmc_pause_counter(pmc); - -- if (!pmc_event_is_allowed(pmc)) -+ if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -+ !check_pmu_event_filter(pmc)) - return 0; - - if (emulate_overflow) -@@ -974,7 +969,8 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!pmc_event_is_allowed(pmc) || !cpl_is_matched(pmc)) -+ if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -+ !check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf b/SPECS/kernel-rt/0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf deleted file mode 100644 index 996cc5400..000000000 --- a/SPECS/kernel-rt/0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf +++ /dev/null @@ -1,34 +0,0 @@ -From b8f68b83d57e6128be521f34bc581866cf685623 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:38:11 -0700 -Subject: [PATCH 051/100] KVM: x86/pmu: Drop redundant check on PMC being - globally enabled for emulation - -When triggering PMC events in response to emulation, drop the redundant -checks on a PMC being globally and locally enabled, as the passed in bitmap -contains only PMCs that are locally enabled (and counting the right event), -and the local copy of the bitmap has already been masked with global_ctrl. - -No true functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index a495ab5d0556..bdcd9c6f0ec0 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -969,7 +969,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -+ if (!pmc_is_locally_enabled(pmc) || - !check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) - continue; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf b/SPECS/kernel-rt/0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf deleted file mode 100644 index 44d8b524e..000000000 --- a/SPECS/kernel-rt/0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf +++ /dev/null @@ -1,31 +0,0 @@ -From d5a6332c6e3593d85d144069d9d7e637c0eb1c27 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:39:32 -0700 -Subject: [PATCH 052/100] KVM: x86/pmu: Drop redundant check on PMC being - locally enabled for emulation - -Drop the check on a PMC being locally enabled when triggering emulated -events, as the bitmap of passed-in PMCs only contains locally enabled PMCs. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index bdcd9c6f0ec0..422af7734846 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -969,8 +969,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!pmc_is_locally_enabled(pmc) || -- !check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) -+ if (!check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf b/SPECS/kernel-rt/0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf deleted file mode 100644 index 223bc6369..000000000 --- a/SPECS/kernel-rt/0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf +++ /dev/null @@ -1,50 +0,0 @@ -From 0b1ea8b95321e1d446235a8fcbea199a91581d5c Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:58:49 -0700 -Subject: [PATCH 053/100] KVM: x86/pmu: Rename check_pmu_event_filter() to - pmc_is_event_allowed() - -Rename check_pmu_event_filter() to make its polarity more obvious, and to -connect the dots to is_gp_event_allowed() and is_fixed_event_allowed(). - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 422af7734846..e75671b6e88c 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -476,7 +476,7 @@ static bool is_fixed_event_allowed(struct kvm_x86_pmu_event_filter *filter, - return true; - } - --static bool check_pmu_event_filter(struct kvm_pmc *pmc) -+static bool pmc_is_event_allowed(struct kvm_pmc *pmc) - { - struct kvm_x86_pmu_event_filter *filter; - struct kvm *kvm = pmc->vcpu->kvm; -@@ -502,7 +502,7 @@ static int reprogram_counter(struct kvm_pmc *pmc) - emulate_overflow = pmc_pause_counter(pmc); - - if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -- !check_pmu_event_filter(pmc)) -+ !pmc_is_event_allowed(pmc)) - return 0; - - if (emulate_overflow) -@@ -969,7 +969,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) -+ if (!pmc_is_event_allowed(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf b/SPECS/kernel-rt/0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf deleted file mode 100644 index 7d4818794..000000000 --- a/SPECS/kernel-rt/0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf +++ /dev/null @@ -1,106 +0,0 @@ -From aa7ec2aa6714a53494483673831ad443cc3eeab6 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:53:51 -0700 -Subject: [PATCH 054/100] KVM: x86: Push acquisition of SRCU in fastpath into - kvm_pmu_trigger_event() - -Acquire SRCU in the VM-Exit fastpath if and only if KVM needs to check the -PMU event filter, to further trim the amount of code that is executed with -SRCU protection in the fastpath. Counter-intuitively, holding SRCU can do -more harm than good due to masking potential bugs, and introducing a new -SRCU-protected asset to code reachable via kvm_skip_emulated_instruction() -would be quite notable, i.e. definitely worth auditing. - -E.g. the primary user of kvm->srcu is KVM's memslots, accessing memslots -all but guarantees guest memory may be accessed, accessing guest memory -can fault, and page faults might sleep, which isn't allowed while IRQs are -disabled. Not acquiring SRCU means the (hypothetical) illegal sleep would -be flagged when running with PROVE_RCU=y, even if DEBUG_ATOMIC_SLEEP=n. - -Note, performance is NOT a motivating factor, as SRCU lock/unlock only -adds ~15 cycles of latency to fastpath VM-Exits. I.e. overhead isn't a -concern _if_ SRCU protection needs to be extended beyond PMU events, e.g. -to honor userspace MSR filters. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 4 +++- - arch/x86/kvm/x86.c | 18 +++++------------- - 2 files changed, 8 insertions(+), 14 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e75671b6e88c..3206412a35a1 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -955,7 +955,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); - struct kvm_pmc *pmc; -- int i; -+ int i, idx; - - BUILD_BUG_ON(sizeof(pmu->global_ctrl) * BITS_PER_BYTE != X86_PMC_IDX_MAX); - -@@ -968,12 +968,14 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - (unsigned long *)&pmu->global_ctrl, X86_PMC_IDX_MAX)) - return; - -+ idx = srcu_read_lock(&vcpu->kvm->srcu); - kvm_for_each_pmc(pmu, pmc, i, bitmap) { - if (!pmc_is_event_allowed(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); - } -+ srcu_read_unlock(&vcpu->kvm->srcu, idx); - } - - void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index f2b2eaaec6f8..a56f83b40a55 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2137,7 +2137,6 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { - u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); -- int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -@@ -2152,13 +2151,12 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - return EXIT_FASTPATH_NONE; - } - -- kvm_vcpu_srcu_read_lock(vcpu); -- r = kvm_skip_emulated_instruction(vcpu); -- kvm_vcpu_srcu_read_unlock(vcpu); -- - trace_kvm_msr_write(msr, data); - -- return r ? EXIT_FASTPATH_REENTER_GUEST : EXIT_FASTPATH_EXIT_USERSPACE; -+ if (!kvm_skip_emulated_instruction(vcpu)) -+ return EXIT_FASTPATH_EXIT_USERSPACE; -+ -+ return EXIT_FASTPATH_REENTER_GUEST; - } - EXPORT_SYMBOL_GPL(handle_fastpath_set_msr_irqoff); - -@@ -11251,13 +11249,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_halt); - - fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu) - { -- int ret; -- -- kvm_vcpu_srcu_read_lock(vcpu); -- ret = kvm_emulate_halt(vcpu); -- kvm_vcpu_srcu_read_unlock(vcpu); -- -- if (!ret) -+ if (!kvm_emulate_halt(vcpu)) - return EXIT_FASTPATH_EXIT_USERSPACE; - - if (kvm_vcpu_running(vcpu)) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf b/SPECS/kernel-rt/0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf deleted file mode 100644 index abeea302d..000000000 --- a/SPECS/kernel-rt/0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf +++ /dev/null @@ -1,82 +0,0 @@ -From f2e53da05e049ba69aef496b4383055ab9c85983 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 09:00:58 -0700 -Subject: [PATCH 055/100] KVM: x86: Add a fastpath handler for INVD - -Add a fastpath handler for INVD so that the common fastpath logic can be -trivially tested on both Intel and AMD. Under KVM, INVD is always: -(a) intercepted, (b) available to the guest, and (c) emulated as a nop, -with no side effects. Combined with INVD not having any inputs or outputs, -i.e. no register constraints, INVD is the perfect instruction for -exercising KVM's fastpath as it can be inserted into practically any -guest-side code stream. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/svm.c | 2 ++ - arch/x86/kvm/vmx/vmx.c | 2 ++ - arch/x86/kvm/x86.c | 9 +++++++++ - arch/x86/kvm/x86.h | 1 + - 4 files changed, 14 insertions(+) - -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 829d9d46718d..f7e1e665a826 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4200,6 +4200,8 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) - return handle_fastpath_set_msr_irqoff(vcpu); - case SVM_EXIT_HLT: - return handle_fastpath_hlt(vcpu); -+ case SVM_EXIT_INVD: -+ return handle_fastpath_invd(vcpu); - default: - break; - } -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index aa157fe5b7b3..95765db52992 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7175,6 +7175,8 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu, - return handle_fastpath_preemption_timer(vcpu, force_immediate_exit); - case EXIT_REASON_HLT: - return handle_fastpath_hlt(vcpu); -+ case EXIT_REASON_INVD: -+ return handle_fastpath_invd(vcpu); - default: - return EXIT_FASTPATH_NONE; - } -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a56f83b40a55..5af2c5aed0f2 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2086,6 +2086,15 @@ int kvm_emulate_invd(struct kvm_vcpu *vcpu) - } - EXPORT_SYMBOL_GPL(kvm_emulate_invd); - -+fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu) -+{ -+ if (!kvm_emulate_invd(vcpu)) -+ return EXIT_FASTPATH_EXIT_USERSPACE; -+ -+ return EXIT_FASTPATH_REENTER_GUEST; -+} -+EXPORT_SYMBOL_GPL(handle_fastpath_invd); -+ - int kvm_handle_invalid_op(struct kvm_vcpu *vcpu) - { - kvm_queue_exception(vcpu, UD_VECTOR); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index bcfd9b719ada..46220b04cdf2 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -439,6 +439,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - int emulation_type, void *insn, int insn_len); - fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu); - fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu); -+fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu); - - extern struct kvm_caps kvm_caps; - extern struct kvm_host_values kvm_host; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0056-perf-Skip-pmu_ctx-based-on-event_type.perf b/SPECS/kernel-rt/0056-perf-Skip-pmu_ctx-based-on-event_type.perf deleted file mode 100644 index a07fe9992..000000000 --- a/SPECS/kernel-rt/0056-perf-Skip-pmu_ctx-based-on-event_type.perf +++ /dev/null @@ -1,282 +0,0 @@ -From ba1d8614d882bf13222c12c2603d0d0be4caea70 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:42 +0000 -Subject: [PATCH 056/100] perf: Skip pmu_ctx based on event_type - -To optimize the cgroup context switch, the perf_event_pmu_context -iteration skips the PMUs without cgroup events. A bool cgroup was -introduced to indicate the case. It can work, but this way is hard to -extend for other cases, e.g. skipping non-mediated PMUs. It doesn't -make sense to keep adding bool variables. - -Pass the event_type instead of the specific bool variable. Check both -the event_type and related pmu_ctx variables to decide whether skipping -a PMU. - -Event flags, e.g., EVENT_CGROUP, should be cleard in the ctx->is_active. -Add EVENT_FLAGS to indicate such event flags. - -No functional change. - -Signed-off-by: Kan Liang -Tested-by: Yongwei Ma -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - kernel/events/core.c | 74 ++++++++++++++++++++++++-------------------- - 1 file changed, 40 insertions(+), 34 deletions(-) - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 0ff00d0b59ea..3054d6a866ac 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -164,7 +164,7 @@ enum event_type_t { - /* see ctx_resched() for details */ - EVENT_CPU = 0x10, - EVENT_CGROUP = 0x20, -- -+ EVENT_FLAGS = EVENT_CGROUP, - /* compound helpers */ - EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED, - EVENT_TIME_FROZEN = EVENT_TIME | EVENT_FROZEN, -@@ -778,27 +778,37 @@ do { \ - ___p; \ - }) - --#define for_each_epc(_epc, _ctx, _pmu, _cgroup) \ -+static bool perf_skip_pmu_ctx(struct perf_event_pmu_context *pmu_ctx, -+ enum event_type_t event_type) -+{ -+ if ((event_type & EVENT_CGROUP) && !pmu_ctx->nr_cgroups) -+ return true; -+ return false; -+} -+ -+#define for_each_epc(_epc, _ctx, _pmu, _event_type) \ - list_for_each_entry(_epc, &((_ctx)->pmu_ctx_list), pmu_ctx_entry) \ -- if (_cgroup && !_epc->nr_cgroups) \ -+ if (perf_skip_pmu_ctx(_epc, _event_type)) \ - continue; \ - else if (_pmu && _epc->pmu != _pmu) \ - continue; \ - else - --static void perf_ctx_disable(struct perf_event_context *ctx, bool cgroup) -+static void perf_ctx_disable(struct perf_event_context *ctx, -+ enum event_type_t event_type) - { - struct perf_event_pmu_context *pmu_ctx; - -- for_each_epc(pmu_ctx, ctx, NULL, cgroup) -+ for_each_epc(pmu_ctx, ctx, NULL, event_type) - perf_pmu_disable(pmu_ctx->pmu); - } - --static void perf_ctx_enable(struct perf_event_context *ctx, bool cgroup) -+static void perf_ctx_enable(struct perf_event_context *ctx, -+ enum event_type_t event_type) - { - struct perf_event_pmu_context *pmu_ctx; - -- for_each_epc(pmu_ctx, ctx, NULL, cgroup) -+ for_each_epc(pmu_ctx, ctx, NULL, event_type) - perf_pmu_enable(pmu_ctx->pmu); - } - -@@ -963,8 +973,7 @@ static void perf_cgroup_switch(struct task_struct *task) - return; - - WARN_ON_ONCE(cpuctx->ctx.nr_cgroups == 0); -- -- perf_ctx_disable(&cpuctx->ctx, true); -+ perf_ctx_disable(&cpuctx->ctx, EVENT_CGROUP); - - ctx_sched_out(&cpuctx->ctx, NULL, EVENT_ALL|EVENT_CGROUP); - /* -@@ -980,7 +989,7 @@ static void perf_cgroup_switch(struct task_struct *task) - */ - ctx_sched_in(&cpuctx->ctx, NULL, EVENT_ALL|EVENT_CGROUP); - -- perf_ctx_enable(&cpuctx->ctx, true); -+ perf_ctx_enable(&cpuctx->ctx, EVENT_CGROUP); - } - - static int perf_cgroup_ensure_storage(struct perf_event *event, -@@ -2904,11 +2913,11 @@ static void ctx_resched(struct perf_cpu_context *cpuctx, - - event_type &= EVENT_ALL; - -- for_each_epc(epc, &cpuctx->ctx, pmu, false) -+ for_each_epc(epc, &cpuctx->ctx, pmu, 0) - perf_pmu_disable(epc->pmu); - - if (task_ctx) { -- for_each_epc(epc, task_ctx, pmu, false) -+ for_each_epc(epc, task_ctx, pmu, 0) - perf_pmu_disable(epc->pmu); - - task_ctx_sched_out(task_ctx, pmu, event_type); -@@ -2928,11 +2937,11 @@ static void ctx_resched(struct perf_cpu_context *cpuctx, - - perf_event_sched_in(cpuctx, task_ctx, pmu); - -- for_each_epc(epc, &cpuctx->ctx, pmu, false) -+ for_each_epc(epc, &cpuctx->ctx, pmu, 0) - perf_pmu_enable(epc->pmu); - - if (task_ctx) { -- for_each_epc(epc, task_ctx, pmu, false) -+ for_each_epc(epc, task_ctx, pmu, 0) - perf_pmu_enable(epc->pmu); - } - } -@@ -3481,11 +3490,10 @@ static void - ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type) - { - struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ enum event_type_t active_type = event_type & ~EVENT_FLAGS; - struct perf_event_pmu_context *pmu_ctx; - int is_active = ctx->is_active; -- bool cgroup = event_type & EVENT_CGROUP; - -- event_type &= ~EVENT_CGROUP; - - lockdep_assert_held(&ctx->lock); - -@@ -3516,7 +3524,7 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - * see __load_acquire() in perf_event_time_now() - */ - barrier(); -- ctx->is_active &= ~event_type; -+ ctx->is_active &= ~active_type; - - if (!(ctx->is_active & EVENT_ALL)) { - /* -@@ -3537,7 +3545,7 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - - is_active ^= ctx->is_active; /* changed bits */ - -- for_each_epc(pmu_ctx, ctx, pmu, cgroup) -+ for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_out(pmu_ctx, is_active); - } - -@@ -3693,7 +3701,7 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next) - raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING); - if (context_equiv(ctx, next_ctx)) { - -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - - /* PMIs are disabled; ctx->nr_no_switch_fast is stable. */ - if (local_read(&ctx->nr_no_switch_fast) || -@@ -3717,7 +3725,7 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next) - - perf_ctx_sched_task_cb(ctx, task, false); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - - /* - * RCU_INIT_POINTER here is safe because we've not -@@ -3741,13 +3749,13 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next) - - if (do_switch) { - raw_spin_lock(&ctx->lock); -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - - inside_switch: - perf_ctx_sched_task_cb(ctx, task, false); - task_ctx_sched_out(ctx, NULL, EVENT_ALL); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - raw_spin_unlock(&ctx->lock); - } - } -@@ -4056,11 +4064,9 @@ static void - ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type) - { - struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ enum event_type_t active_type = event_type & ~EVENT_FLAGS; - struct perf_event_pmu_context *pmu_ctx; - int is_active = ctx->is_active; -- bool cgroup = event_type & EVENT_CGROUP; -- -- event_type &= ~EVENT_CGROUP; - - lockdep_assert_held(&ctx->lock); - -@@ -4078,7 +4084,7 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - barrier(); - } - -- ctx->is_active |= (event_type | EVENT_TIME); -+ ctx->is_active |= active_type | EVENT_TIME; - if (ctx->task) { - if (!(is_active & EVENT_ALL)) - cpuctx->task_ctx = ctx; -@@ -4093,13 +4099,13 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - * in order to give them the best chance of going on. - */ - if (is_active & EVENT_PINNED) { -- for_each_epc(pmu_ctx, ctx, pmu, cgroup) -+ for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_in(pmu_ctx, EVENT_PINNED); - } - - /* Then walk through the lower prio flexible groups */ - if (is_active & EVENT_FLEXIBLE) { -- for_each_epc(pmu_ctx, ctx, pmu, cgroup) -+ for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_in(pmu_ctx, EVENT_FLEXIBLE); - } - } -@@ -4116,11 +4122,11 @@ static void perf_event_context_sched_in(struct task_struct *task) - - if (cpuctx->task_ctx == ctx) { - perf_ctx_lock(cpuctx, ctx); -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - - perf_ctx_sched_task_cb(ctx, task, true); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - perf_ctx_unlock(cpuctx, ctx); - goto rcu_unlock; - } -@@ -4133,7 +4139,7 @@ static void perf_event_context_sched_in(struct task_struct *task) - if (!ctx->nr_events) - goto unlock; - -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - /* - * We want to keep the following priority order: - * cpu pinned (that don't need to move), task pinned, -@@ -4143,7 +4149,7 @@ static void perf_event_context_sched_in(struct task_struct *task) - * events, no need to flip the cpuctx's events around. - */ - if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree)) { -- perf_ctx_disable(&cpuctx->ctx, false); -+ perf_ctx_disable(&cpuctx->ctx, 0); - ctx_sched_out(&cpuctx->ctx, NULL, EVENT_FLEXIBLE); - } - -@@ -4152,9 +4158,9 @@ static void perf_event_context_sched_in(struct task_struct *task) - perf_ctx_sched_task_cb(cpuctx->task_ctx, task, true); - - if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree)) -- perf_ctx_enable(&cpuctx->ctx, false); -+ perf_ctx_enable(&cpuctx->ctx, 0); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - - unlock: - perf_ctx_unlock(cpuctx, ctx); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0057-perf-Add-generic-exclude_guest-support.perf b/SPECS/kernel-rt/0057-perf-Add-generic-exclude_guest-support.perf deleted file mode 100644 index 3be1c66d3..000000000 --- a/SPECS/kernel-rt/0057-perf-Add-generic-exclude_guest-support.perf +++ /dev/null @@ -1,64 +0,0 @@ -From f9f422924163d86939bac97108712ba5117fad24 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:45 +0000 -Subject: [PATCH 057/100] perf: Add generic exclude_guest support - -Only KVM knows the exact time when a guest is entering/exiting. Expose -two interfaces to KVM to switch the ownership of the PMU resources. - -All the pinned events must be scheduled in first. Extend the -perf_event_sched_in() helper to support extra flag, e.g., EVENT_GUEST. - -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - kernel/events/core.c | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 3054d6a866ac..d9c17f5f5195 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -2872,14 +2872,15 @@ static void task_ctx_sched_out(struct perf_event_context *ctx, - - static void perf_event_sched_in(struct perf_cpu_context *cpuctx, - struct perf_event_context *ctx, -- struct pmu *pmu) -+ struct pmu *pmu, -+ enum event_type_t event_type) - { -- ctx_sched_in(&cpuctx->ctx, pmu, EVENT_PINNED); -+ ctx_sched_in(&cpuctx->ctx, pmu, EVENT_PINNED | event_type); - if (ctx) -- ctx_sched_in(ctx, pmu, EVENT_PINNED); -- ctx_sched_in(&cpuctx->ctx, pmu, EVENT_FLEXIBLE); -+ ctx_sched_in(ctx, pmu, EVENT_PINNED | event_type); -+ ctx_sched_in(&cpuctx->ctx, pmu, EVENT_FLEXIBLE | event_type); - if (ctx) -- ctx_sched_in(ctx, pmu, EVENT_FLEXIBLE); -+ ctx_sched_in(ctx, pmu, EVENT_FLEXIBLE | event_type); - } - - /* -@@ -2935,7 +2936,7 @@ static void ctx_resched(struct perf_cpu_context *cpuctx, - else if (event_type & EVENT_PINNED) - ctx_sched_out(&cpuctx->ctx, pmu, EVENT_FLEXIBLE); - -- perf_event_sched_in(cpuctx, task_ctx, pmu); -+ perf_event_sched_in(cpuctx, task_ctx, pmu, 0); - - for_each_epc(epc, &cpuctx->ctx, pmu, 0) - perf_pmu_enable(epc->pmu); -@@ -4153,7 +4154,7 @@ static void perf_event_context_sched_in(struct task_struct *task) - ctx_sched_out(&cpuctx->ctx, NULL, EVENT_FLEXIBLE); - } - -- perf_event_sched_in(cpuctx, ctx, NULL); -+ perf_event_sched_in(cpuctx, ctx, NULL, 0); - - perf_ctx_sched_task_cb(cpuctx->task_ctx, task, true); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf b/SPECS/kernel-rt/0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf deleted file mode 100644 index ddb51ef4a..000000000 --- a/SPECS/kernel-rt/0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf +++ /dev/null @@ -1,46 +0,0 @@ -From 7bcd131ab587cb29bd8eefd9dc46e310e9d986ea Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 10:01:17 -0700 -Subject: [PATCH 058/100] perf: Move security_perf_event_free() call to - __free_event() - -Move the freeing of any security state associated with a perf event from -_free_event() to __free_event(), i.e. invoke security_perf_event_free() in -the error paths for perf_event_alloc(). This will allow adding potential -error paths in perf_event_alloc() that can occur after allocating security -state. - -Note, kfree() and thus security_perf_event_free() is a nop if -event->security is NULL, i.e. calling security_perf_event_free() even if -security_perf_event_alloc() fails or is never reached is functionality ok. - -Signed-off-by: Sean Christopherson ---- - kernel/events/core.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index d9c17f5f5195..3af2ec8641ca 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -5602,6 +5602,8 @@ static void __free_event(struct perf_event *event) - { - struct pmu *pmu = event->pmu; - -+ security_perf_event_free(event); -+ - if (event->attach_state & PERF_ATTACH_CALLCHAIN) - put_callchain_buffers(); - -@@ -5665,8 +5667,6 @@ static void _free_event(struct perf_event *event) - - unaccount_event(event); - -- security_perf_event_free(event); -- - if (event->rb) { - /* - * Can happen when we close an event with re-directed output. --- -2.43.0 - diff --git a/SPECS/kernel-rt/0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf b/SPECS/kernel-rt/0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf deleted file mode 100644 index 1653e3b46..000000000 --- a/SPECS/kernel-rt/0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf +++ /dev/null @@ -1,226 +0,0 @@ -From 09c2cfcf7086c69998f7e5b07fc8d6336ed53b9d Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:41 +0000 -Subject: [PATCH 059/100] perf: Add APIs to create/release mediated guest vPMUs - -Currently, exposing PMU capabilities to a KVM guest is done by emulating -guest PMCs via host perf events, i.e. by having KVM be "just" another user -of perf. As a result, the guest and host are effectively competing for -resources, and emulating guest accesses to vPMU resources requires -expensive actions (expensive relative to the native instruction). The -overhead and resource competition results in degraded guest performance -and ultimately very poor vPMU accuracy. - -To address the issues with the perf-emulated vPMU, introduce a "mediated -vPMU", where the data plane (PMCs and enable/disable knobs) is exposed -directly to the guest, but the control plane (event selectors and access -to fixed counters) is managed by KVM (via MSR interceptions). To allow -host perf usage of the PMU to (partially) co-exist with KVM/guest usage -of the PMU, KVM and perf will coordinate to a world switch between host -perf context and guest vPMU context near VM-Enter/VM-Exit. - -Add two exported APIs, perf_{create,release}_mediated_pmu(), to allow KVM -to create and release a mediated PMU instance (per VM). Because host perf -context will be deactivated while the guest is running, mediated PMU usage -will be mutually exclusive with perf analysis of the guest, i.e. perf -events that do NOT exclude the guest will not behave as expected. - -To avoid silent failure of !exclude_guest perf events, disallow creating a -mediated PMU if there are active !exclude_guest events, and on the perf -side, disallowing creating new !exclude_guest perf events while there is -at least one active mediated PMU. - -Exempt PMU resources that do not support mediated PMU usage, i.e. that are -outside the scope/view of KVM's vPMU and will not be swapped out while the -guest is running. - -Guard mediated PMU with a new kconfig to help readers identify code paths -that are unique to mediated PMU support, and to allow for adding arch- -specific hooks without stubs. KVM x86 is expected to be the only KVM -architecture to support a mediated PMU in the near future (e.g. arm64 is -trending toward a partitioned PMU implementation), and KVM x86 will select -PERF_GUEST_MEDIATED_PMU unconditionally, i.e. won't need stubs. - -Immediately select PERF_GUEST_MEDIATED_PMU when KVM x86 is enabled so that -all paths are compile tested. Full KVM support is on its way... - -Suggested-by: Sean Christopherson -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: add kconfig and WARNing, rewrite changelog, swizzle patch ordering] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/Kconfig | 1 + - include/linux/perf_event.h | 6 +++ - init/Kconfig | 4 ++ - kernel/events/core.c | 82 ++++++++++++++++++++++++++++++++++++++ - 4 files changed, 93 insertions(+) - -diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig -index 2c86673155c9..ee67357b5e36 100644 ---- a/arch/x86/kvm/Kconfig -+++ b/arch/x86/kvm/Kconfig -@@ -37,6 +37,7 @@ config KVM_X86 - select SCHED_INFO - select PERF_EVENTS - select GUEST_PERF_EVENTS -+ select PERF_GUEST_MEDIATED_PMU - select HAVE_KVM_MSI - select HAVE_KVM_CPU_RELAX_INTERCEPT - select HAVE_KVM_NO_POLL -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index dd9bfe42c011..a430a3ae4c52 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -306,6 +306,7 @@ struct perf_event_pmu_context; - #define PERF_PMU_CAP_AUX_PAUSE 0x0200 - #define PERF_PMU_CAP_AUX_PREFER_LARGE 0x0400 - #define PERF_PMU_CAP_MORE_EXT_REGS 0x0800 -+#define PERF_PMU_CAP_MEDIATED_VPMU 0x1000 - - /** - * pmu::scope -@@ -1918,6 +1919,11 @@ extern int perf_event_account_interrupt(struct perf_event *event); - extern int perf_event_period(struct perf_event *event, u64 value); - extern u64 perf_event_pause(struct perf_event *event, bool reset); - -+#ifdef CONFIG_PERF_GUEST_MEDIATED_PMU -+int perf_create_mediated_pmu(void); -+void perf_release_mediated_pmu(void); -+#endif -+ - #else /* !CONFIG_PERF_EVENTS: */ - - static inline void * -diff --git a/init/Kconfig b/init/Kconfig -index 836320251219..4d1c5eb98a80 100644 ---- a/init/Kconfig -+++ b/init/Kconfig -@@ -1980,6 +1980,10 @@ config GUEST_PERF_EVENTS - bool - depends on HAVE_PERF_EVENTS - -+config PERF_GUEST_MEDIATED_PMU -+ bool -+ depends on GUEST_PERF_EVENTS -+ - config PERF_USE_VMALLOC - bool - help -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 3af2ec8641ca..d9beaad6837c 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -5657,6 +5657,8 @@ static void __free_event(struct perf_event *event) - call_rcu(&event->rcu_head, free_event_rcu); - } - -+static void mediated_pmu_unaccount_event(struct perf_event *event); -+ - DEFINE_FREE(__free_event, struct perf_event *, if (_T) __free_event(_T)) - - /* vs perf_event_alloc() success */ -@@ -5666,6 +5668,7 @@ static void _free_event(struct perf_event *event) - irq_work_sync(&event->pending_disable_irq); - - unaccount_event(event); -+ mediated_pmu_unaccount_event(event); - - if (event->rb) { - /* -@@ -6188,6 +6191,81 @@ u64 perf_event_pause(struct perf_event *event, bool reset) - } - EXPORT_SYMBOL_GPL(perf_event_pause); - -+#ifdef CONFIG_PERF_GUEST_MEDIATED_PMU -+static atomic_t nr_include_guest_events __read_mostly; -+ -+static atomic_t nr_mediated_pmu_vms __read_mostly; -+static DEFINE_MUTEX(perf_mediated_pmu_mutex); -+ -+/* !exclude_guest event of PMU with PERF_PMU_CAP_MEDIATED_VPMU */ -+static inline bool is_include_guest_event(struct perf_event *event) -+{ -+ if ((event->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU) && -+ !event->attr.exclude_guest) -+ return true; -+ -+ return false; -+} -+ -+static int mediated_pmu_account_event(struct perf_event *event) -+{ -+ if (!is_include_guest_event(event)) -+ return 0; -+ -+ guard(mutex)(&perf_mediated_pmu_mutex); -+ -+ if (atomic_read(&nr_mediated_pmu_vms)) -+ return -EOPNOTSUPP; -+ -+ atomic_inc(&nr_include_guest_events); -+ return 0; -+} -+ -+static void mediated_pmu_unaccount_event(struct perf_event *event) -+{ -+ if (!is_include_guest_event(event)) -+ return; -+ -+ atomic_dec(&nr_include_guest_events); -+} -+ -+/* -+ * Currently invoked at VM creation to -+ * - Check whether there are existing !exclude_guest events of PMU with -+ * PERF_PMU_CAP_MEDIATED_VPMU -+ * - Set nr_mediated_pmu_vms to prevent !exclude_guest event creation on -+ * PMUs with PERF_PMU_CAP_MEDIATED_VPMU -+ * -+ * No impact for the PMU without PERF_PMU_CAP_MEDIATED_VPMU. The perf -+ * still owns all the PMU resources. -+ */ -+int perf_create_mediated_pmu(void) -+{ -+ guard(mutex)(&perf_mediated_pmu_mutex); -+ if (atomic_inc_not_zero(&nr_mediated_pmu_vms)) -+ return 0; -+ -+ if (atomic_read(&nr_include_guest_events)) -+ return -EBUSY; -+ -+ atomic_inc(&nr_mediated_pmu_vms); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(perf_create_mediated_pmu); -+ -+void perf_release_mediated_pmu(void) -+{ -+ if (WARN_ON_ONCE(!atomic_read(&nr_mediated_pmu_vms))) -+ return; -+ -+ atomic_dec(&nr_mediated_pmu_vms); -+} -+EXPORT_SYMBOL_GPL(perf_release_mediated_pmu); -+#else -+static int mediated_pmu_account_event(struct perf_event *event) { return 0; } -+static void mediated_pmu_unaccount_event(struct perf_event *event) {} -+#endif -+ - /* - * Holding the top-level event's child_mutex means that any - * descendant process that has inherited this event will block -@@ -13115,6 +13193,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, - if (err) - return ERR_PTR(err); - -+ err = mediated_pmu_account_event(event); -+ if (err) -+ return ERR_PTR(err); -+ - /* symmetric to unaccount_event() in _free_event() */ - account_event(event); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0060-perf-Clean-up-perf-ctx-time.perf b/SPECS/kernel-rt/0060-perf-Clean-up-perf-ctx-time.perf deleted file mode 100644 index ed18cb2cf..000000000 --- a/SPECS/kernel-rt/0060-perf-Clean-up-perf-ctx-time.perf +++ /dev/null @@ -1,223 +0,0 @@ -From 7912f8392baadefe96f23d4bbc26022313774e9c Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:43 +0000 -Subject: [PATCH 060/100] perf: Clean up perf ctx time - -The current perf tracks two timestamps for the normal ctx and cgroup. -The same type of variables and similar codes are used to track the -timestamps. In the following patch, the third timestamp to track the -guest time will be introduced. -To avoid the code duplication, add a new struct perf_time_ctx and factor -out a generic function update_perf_time_ctx(). - -No functional change. - -Suggested-by: Peter Zijlstra (Intel) -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - include/linux/perf_event.h | 13 +++---- - kernel/events/core.c | 70 +++++++++++++++++--------------------- - 2 files changed, 39 insertions(+), 44 deletions(-) - -Index: kernel-staging/include/linux/perf_event.h -=================================================================== ---- kernel-staging.orig/include/linux/perf_event.h -+++ kernel-staging/include/linux/perf_event.h -@@ -1000,6 +1000,11 @@ struct perf_event_groups { - u64 index; - }; - -+struct perf_time_ctx { -+ u64 time; -+ u64 stamp; -+ u64 offset; -+}; - - /** - * struct perf_event_context - event context structure -@@ -1038,9 +1043,7 @@ struct perf_event_context { - /* - * Context clock, runs when context enabled. - */ -- u64 time; -- u64 timestamp; -- u64 timeoffset; -+ struct perf_time_ctx time; - - /* - * These fields let us detect when two contexts have both -@@ -1173,9 +1176,7 @@ struct bpf_perf_event_data_kern { - * This is a per-cpu dynamically allocated data structure. - */ - struct perf_cgroup_info { -- u64 time; -- u64 timestamp; -- u64 timeoffset; -+ struct perf_time_ctx time; - int active; - }; - -Index: kernel-staging/kernel/events/core.c -=================================================================== ---- kernel-staging.orig/kernel/events/core.c -+++ kernel-staging/kernel/events/core.c -@@ -815,6 +815,24 @@ static void perf_ctx_enable(struct perf_ - static void ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type); - static void ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type); - -+static inline void update_perf_time_ctx(struct perf_time_ctx *time, u64 now, bool adv) -+{ -+ if (adv) -+ time->time += now - time->stamp; -+ time->stamp = now; -+ -+ /* -+ * The above: time' = time + (now - timestamp), can be re-arranged -+ * into: time` = now + (time - timestamp), which gives a single value -+ * offset to compute future time without locks on. -+ * -+ * See perf_event_time_now(), which can be used from NMI context where -+ * it's (obviously) not possible to acquire ctx->lock in order to read -+ * both the above values in a consistent manner. -+ */ -+ WRITE_ONCE(time->offset, time->time - time->stamp); -+} -+ - #ifdef CONFIG_CGROUP_PERF - - static inline bool -@@ -856,7 +874,7 @@ static inline u64 perf_cgroup_event_time - struct perf_cgroup_info *t; - - t = per_cpu_ptr(event->cgrp->info, event->cpu); -- return t->time; -+ return t->time.time; - } - - static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now) -@@ -865,22 +883,11 @@ static inline u64 perf_cgroup_event_time - - t = per_cpu_ptr(event->cgrp->info, event->cpu); - if (!__load_acquire(&t->active)) -- return t->time; -- now += READ_ONCE(t->timeoffset); -+ return t->time.time; -+ now += READ_ONCE(t->time.offset); - return now; - } - --static inline void __update_cgrp_time(struct perf_cgroup_info *info, u64 now, bool adv) --{ -- if (adv) -- info->time += now - info->timestamp; -- info->timestamp = now; -- /* -- * see update_context_time() -- */ -- WRITE_ONCE(info->timeoffset, info->time - info->timestamp); --} -- - static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, bool final) - { - struct perf_cgroup *cgrp = cpuctx->cgrp; -@@ -894,7 +901,7 @@ static inline void update_cgrp_time_from - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); - -- __update_cgrp_time(info, now, true); -+ update_perf_time_ctx(&info->time, now, true); - if (final) - __store_release(&info->active, 0); - } -@@ -917,7 +924,7 @@ static inline void update_cgrp_time_from - * Do not update time when cgroup is not active - */ - if (info->active) -- __update_cgrp_time(info, perf_clock(), true); -+ update_perf_time_ctx(&info->time, perf_clock(), true); - } - - static inline void -@@ -941,7 +948,7 @@ perf_cgroup_set_timestamp(struct perf_cp - for (css = &cgrp->css; css; css = css->parent) { - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); -- __update_cgrp_time(info, ctx->timestamp, false); -+ update_perf_time_ctx(&info->time, ctx->time.stamp, false); - __store_release(&info->active, 1); - } - } -@@ -1562,20 +1569,7 @@ static void __update_context_time(struct - - lockdep_assert_held(&ctx->lock); - -- if (adv) -- ctx->time += now - ctx->timestamp; -- ctx->timestamp = now; -- -- /* -- * The above: time' = time + (now - timestamp), can be re-arranged -- * into: time` = now + (time - timestamp), which gives a single value -- * offset to compute future time without locks on. -- * -- * See perf_event_time_now(), which can be used from NMI context where -- * it's (obviously) not possible to acquire ctx->lock in order to read -- * both the above values in a consistent manner. -- */ -- WRITE_ONCE(ctx->timeoffset, ctx->time - ctx->timestamp); -+ update_perf_time_ctx(&ctx->time, now, adv); - } - - static void update_context_time(struct perf_event_context *ctx) -@@ -1593,7 +1587,7 @@ static u64 perf_event_time(struct perf_e - if (is_cgroup_event(event)) - return perf_cgroup_event_time(event); - -- return ctx->time; -+ return ctx->time.time; - } - - static u64 perf_event_time_now(struct perf_event *event, u64 now) -@@ -1607,9 +1601,9 @@ static u64 perf_event_time_now(struct pe - return perf_cgroup_event_time_now(event, now); - - if (!(__load_acquire(&ctx->is_active) & EVENT_TIME)) -- return ctx->time; -+ return ctx->time.time; - -- now += READ_ONCE(ctx->timeoffset); -+ now += READ_ONCE(ctx->time.offset); - return now; - } - -@@ -12087,7 +12081,7 @@ static void task_clock_event_update(stru - static void task_clock_event_start(struct perf_event *event, int flags) - { - event->hw.state = 0; -- local64_set(&event->hw.prev_count, event->ctx->time); -+ local64_set(&event->hw.prev_count, event->ctx->time.time); - perf_swevent_start_hrtimer(event); - } - -@@ -12096,7 +12090,7 @@ static void task_clock_event_stop(struct - event->hw.state = PERF_HES_STOPPED; - perf_swevent_cancel_hrtimer(event); - if (flags & PERF_EF_UPDATE) -- task_clock_event_update(event, event->ctx->time); -+ task_clock_event_update(event, event->ctx->time.time); - } - - static int task_clock_event_add(struct perf_event *event, int flags) -@@ -12116,8 +12110,8 @@ static void task_clock_event_del(struct - static void task_clock_event_read(struct perf_event *event) - { - u64 now = perf_clock(); -- u64 delta = now - event->ctx->timestamp; -- u64 time = event->ctx->time + delta; -+ u64 delta = now - event->ctx->time.stamp; -+ u64 time = event->ctx->time.time + delta; - - task_clock_event_update(event, time); - } diff --git a/SPECS/kernel-rt/0061-perf-Add-a-EVENT_GUEST-flag.perf b/SPECS/kernel-rt/0061-perf-Add-a-EVENT_GUEST-flag.perf deleted file mode 100644 index a6e65e37c..000000000 --- a/SPECS/kernel-rt/0061-perf-Add-a-EVENT_GUEST-flag.perf +++ /dev/null @@ -1,531 +0,0 @@ -From cdb7cd281af6aaa5242da9b7ca41af74cc76972b Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:44 +0000 -Subject: [PATCH 061/100] perf: Add a EVENT_GUEST flag - -Current perf doesn't explicitly schedule out all exclude_guest events -while the guest is running. There is no problem with the current -emulated vPMU. Because perf owns all the PMU counters. It can mask the -counter which is assigned to an exclude_guest event when a guest is -running (Intel way), or set the corresponding HOSTONLY bit in evsentsel -(AMD way). The counter doesn't count when a guest is running. - -However, either way doesn't work with the introduced mediated vPMU. -A guest owns all the PMU counters when it's running. The host should not -mask any counters. The counter may be used by the guest. The evsentsel -may be overwritten. - -Perf should explicitly schedule out all exclude_guest events to release -the PMU resources when entering a guest, and resume the counting when -exiting the guest. - -It's possible that an exclude_guest event is created when a guest is -running. The new event should not be scheduled in as well. - -The ctx time is shared among different PMUs. The time cannot be stopped -when a guest is running. It is required to calculate the time for events -from other PMUs, e.g., uncore events. Add timeguest to track the guest -run time. For an exclude_guest event, the elapsed time equals -the ctx time - guest time. -Cgroup has dedicated times. Use the same method to deduct the guest time -from the cgroup time as well. - -Co-developed-by: Peter Zijlstra (Intel) -Signed-off-by: Peter Zijlstra (Intel) -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: massage comments] -Signed-off-by: Sean Christopherson ---- - include/linux/perf_event.h | 6 + - kernel/events/core.c | 230 +++++++++++++++++++++++++++++-------- - 2 files changed, 185 insertions(+), 51 deletions(-) - -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index 2b03fd042149..dfe33bc72180 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1045,6 +1045,11 @@ struct perf_event_context { - */ - struct perf_time_ctx time; - -+ /* -+ * Context clock, runs when in the guest mode. -+ */ -+ struct perf_time_ctx timeguest; -+ - /* - * These fields let us detect when two contexts have both - * been cloned (inherited) from a common ancestor. -@@ -1177,6 +1182,7 @@ struct bpf_perf_event_data_kern { - */ - struct perf_cgroup_info { - struct perf_time_ctx time; -+ struct perf_time_ctx timeguest; - int active; - }; - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index d31b186dc012..0b36899f5939 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -164,7 +164,19 @@ enum event_type_t { - /* see ctx_resched() for details */ - EVENT_CPU = 0x10, - EVENT_CGROUP = 0x20, -- EVENT_FLAGS = EVENT_CGROUP, -+ -+ /* -+ * EVENT_GUEST is set when scheduling in/out events between the host -+ * and a guest with a mediated vPMU. Among other things, EVENT_GUEST -+ * is used: -+ * -+ * - In for_each_epc() to skip PMUs that don't support events in a -+ * MEDIATED_VPMU guest, i.e. don't need to be context switched. -+ * - To indicate the start/end point of the events in a guest. Guest -+ * running time is deducted for host-only (exclude_guest) events. -+ */ -+ EVENT_GUEST = 0x40, -+ EVENT_FLAGS = EVENT_CGROUP | EVENT_GUEST, - /* compound helpers */ - EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED, - EVENT_TIME_FROZEN = EVENT_TIME | EVENT_FROZEN, -@@ -457,6 +469,11 @@ static cpumask_var_t perf_online_pkg_mask; - static cpumask_var_t perf_online_sys_mask; - static struct kmem_cache *perf_event_cache; - -+static __always_inline bool is_guest_mediated_pmu_loaded(void) -+{ -+ return false; -+} -+ - /* - * perf event paranoia level: - * -1 - not paranoid at all -@@ -783,6 +800,9 @@ static bool perf_skip_pmu_ctx(struct perf_event_pmu_context *pmu_ctx, - { - if ((event_type & EVENT_CGROUP) && !pmu_ctx->nr_cgroups) - return true; -+ if ((event_type & EVENT_GUEST) && -+ !(pmu_ctx->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU)) -+ return true; - return false; - } - -@@ -833,6 +853,39 @@ static inline void update_perf_time_ctx(struct perf_time_ctx *time, u64 now, boo - WRITE_ONCE(time->offset, time->time - time->stamp); - } - -+static_assert(offsetof(struct perf_event_context, timeguest) - -+ offsetof(struct perf_event_context, time) == -+ sizeof(struct perf_time_ctx)); -+ -+#define T_TOTAL 0 -+#define T_GUEST 1 -+ -+static inline u64 __perf_event_time_ctx(struct perf_event *event, -+ struct perf_time_ctx *times) -+{ -+ u64 time = times[T_TOTAL].time; -+ -+ if (event->attr.exclude_guest) -+ time -= times[T_GUEST].time; -+ -+ return time; -+} -+ -+static inline u64 __perf_event_time_ctx_now(struct perf_event *event, -+ struct perf_time_ctx *times, -+ u64 now) -+{ -+ if (is_guest_mediated_pmu_loaded() && event->attr.exclude_guest) { -+ /* -+ * (now + times[total].offset) - (now + times[guest].offset) := -+ * times[total].offset - times[guest].offset -+ */ -+ return READ_ONCE(times[T_TOTAL].offset) - READ_ONCE(times[T_GUEST].offset); -+ } -+ -+ return now + READ_ONCE(times[T_TOTAL].offset); -+} -+ - #ifdef CONFIG_CGROUP_PERF - - static inline bool -@@ -869,12 +922,16 @@ static inline int is_cgroup_event(struct perf_event *event) - return event->cgrp != NULL; - } - -+static_assert(offsetof(struct perf_cgroup_info, timeguest) - -+ offsetof(struct perf_cgroup_info, time) == -+ sizeof(struct perf_time_ctx)); -+ - static inline u64 perf_cgroup_event_time(struct perf_event *event) - { - struct perf_cgroup_info *t; - - t = per_cpu_ptr(event->cgrp->info, event->cpu); -- return t->time.time; -+ return __perf_event_time_ctx(event, &t->time); - } - - static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now) -@@ -883,9 +940,21 @@ static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now) - - t = per_cpu_ptr(event->cgrp->info, event->cpu); - if (!__load_acquire(&t->active)) -- return t->time.time; -- now += READ_ONCE(t->time.offset); -- return now; -+ return __perf_event_time_ctx(event, &t->time); -+ -+ return __perf_event_time_ctx_now(event, &t->time, now); -+} -+ -+static inline void __update_cgrp_guest_time(struct perf_cgroup_info *info, u64 now, bool adv) -+{ -+ update_perf_time_ctx(&info->timeguest, now, adv); -+} -+ -+static inline void update_cgrp_time(struct perf_cgroup_info *info, u64 now) -+{ -+ update_perf_time_ctx(&info->time, now, true); -+ if (is_guest_mediated_pmu_loaded()) -+ __update_cgrp_guest_time(info, now, true); - } - - static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, bool final) -@@ -901,7 +970,7 @@ static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); - -- update_perf_time_ctx(&info->time, now, true); -+ update_cgrp_time(info, now); - if (final) - __store_release(&info->active, 0); - } -@@ -924,11 +993,11 @@ static inline void update_cgrp_time_from_event(struct perf_event *event) - * Do not update time when cgroup is not active - */ - if (info->active) -- update_perf_time_ctx(&info->time, perf_clock(), true); -+ update_cgrp_time(info, perf_clock()); - } - - static inline void --perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx) -+perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx, bool guest) - { - struct perf_event_context *ctx = &cpuctx->ctx; - struct perf_cgroup *cgrp = cpuctx->cgrp; -@@ -948,8 +1017,12 @@ perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx) - for (css = &cgrp->css; css; css = css->parent) { - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); -- update_perf_time_ctx(&info->time, ctx->time.stamp, false); -- __store_release(&info->active, 1); -+ if (guest) { -+ __update_cgrp_guest_time(info, ctx->time.stamp, false); -+ } else { -+ update_perf_time_ctx(&info->time, ctx->time.stamp, false); -+ __store_release(&info->active, 1); -+ } - } - } - -@@ -1153,7 +1226,7 @@ static inline int perf_cgroup_connect(pid_t pid, struct perf_event *event, - } - - static inline void --perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx) -+perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx, bool guest) - { - } - -@@ -1565,16 +1638,24 @@ static void perf_unpin_context(struct perf_event_context *ctx) - */ - static void __update_context_time(struct perf_event_context *ctx, bool adv) - { -- u64 now = perf_clock(); -+ lockdep_assert_held(&ctx->lock); -+ -+ update_perf_time_ctx(&ctx->time, perf_clock(), adv); -+} - -+static void __update_context_guest_time(struct perf_event_context *ctx, bool adv) -+{ - lockdep_assert_held(&ctx->lock); - -- update_perf_time_ctx(&ctx->time, now, adv); -+ /* must be called after __update_context_time(); */ -+ update_perf_time_ctx(&ctx->timeguest, ctx->time.stamp, adv); - } - - static void update_context_time(struct perf_event_context *ctx) - { - __update_context_time(ctx, true); -+ if (is_guest_mediated_pmu_loaded()) -+ __update_context_guest_time(ctx, true); - } - - static u64 perf_event_time(struct perf_event *event) -@@ -1587,7 +1668,7 @@ static u64 perf_event_time(struct perf_event *event) - if (is_cgroup_event(event)) - return perf_cgroup_event_time(event); - -- return ctx->time.time; -+ return __perf_event_time_ctx(event, &ctx->time); - } - - static u64 perf_event_time_now(struct perf_event *event, u64 now) -@@ -1601,10 +1682,9 @@ static u64 perf_event_time_now(struct perf_event *event, u64 now) - return perf_cgroup_event_time_now(event, now); - - if (!(__load_acquire(&ctx->is_active) & EVENT_TIME)) -- return ctx->time.time; -+ return __perf_event_time_ctx(event, &ctx->time); - -- now += READ_ONCE(ctx->time.offset); -- return now; -+ return __perf_event_time_ctx_now(event, &ctx->time, now); - } - - static enum event_type_t get_event_type(struct perf_event *event) -@@ -2427,20 +2507,23 @@ group_sched_out(struct perf_event *group_event, struct perf_event_context *ctx) - } - - static inline void --__ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx, bool final) -+__ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx, -+ bool final, enum event_type_t event_type) - { - if (ctx->is_active & EVENT_TIME) { - if (ctx->is_active & EVENT_FROZEN) - return; -+ - update_context_time(ctx); -- update_cgrp_time_from_cpuctx(cpuctx, final); -+ /* vPMU should not stop time */ -+ update_cgrp_time_from_cpuctx(cpuctx, !(event_type & EVENT_GUEST) && final); - } - } - - static inline void - ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx) - { -- __ctx_time_update(cpuctx, ctx, false); -+ __ctx_time_update(cpuctx, ctx, false, 0); - } - - /* -@@ -3512,7 +3595,7 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - * - * would only update time for the pinned events. - */ -- __ctx_time_update(cpuctx, ctx, ctx == &cpuctx->ctx); -+ __ctx_time_update(cpuctx, ctx, ctx == &cpuctx->ctx, event_type); - - /* - * CPU-release for the below ->is_active store, -@@ -3538,7 +3621,18 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - cpuctx->task_ctx = NULL; - } - -- is_active ^= ctx->is_active; /* changed bits */ -+ if (event_type & EVENT_GUEST) { -+ /* -+ * Schedule out all exclude_guest events of PMU -+ * with PERF_PMU_CAP_MEDIATED_VPMU. -+ */ -+ is_active = EVENT_ALL; -+ __update_context_guest_time(ctx, false); -+ perf_cgroup_set_timestamp(cpuctx, true); -+ barrier(); -+ } else { -+ is_active ^= ctx->is_active; /* changed bits */ -+ } - - for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_out(pmu_ctx, is_active); -@@ -3997,10 +4091,15 @@ static inline void group_update_userpage(struct perf_event *group_event) - event_update_userpage(event); - } - -+struct merge_sched_data { -+ int can_add_hw; -+ enum event_type_t event_type; -+}; -+ - static int merge_sched_in(struct perf_event *event, void *data) - { - struct perf_event_context *ctx = event->ctx; -- int *can_add_hw = data; -+ struct merge_sched_data *msd = data; - - if (event->state <= PERF_EVENT_STATE_OFF) - return 0; -@@ -4008,13 +4107,22 @@ static int merge_sched_in(struct perf_event *event, void *data) - if (!event_filter_match(event)) - return 0; - -- if (group_can_go_on(event, *can_add_hw)) { -+ /* -+ * Don't schedule in any host events from PMU with -+ * PERF_PMU_CAP_MEDIATED_VPMU, while a guest is running. -+ */ -+ if (is_guest_mediated_pmu_loaded() && -+ event->pmu_ctx->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU && -+ !(msd->event_type & EVENT_GUEST)) -+ return 0; -+ -+ if (group_can_go_on(event, msd->can_add_hw)) { - if (!group_sched_in(event, ctx)) - list_add_tail(&event->active_list, get_event_list(event)); - } - - if (event->state == PERF_EVENT_STATE_INACTIVE) { -- *can_add_hw = 0; -+ msd->can_add_hw = 0; - if (event->attr.pinned) { - perf_cgroup_event_disable(event, ctx); - perf_event_set_state(event, PERF_EVENT_STATE_ERROR); -@@ -4037,11 +4145,15 @@ static int merge_sched_in(struct perf_event *event, void *data) - - static void pmu_groups_sched_in(struct perf_event_context *ctx, - struct perf_event_groups *groups, -- struct pmu *pmu) -+ struct pmu *pmu, -+ enum event_type_t event_type) - { -- int can_add_hw = 1; -+ struct merge_sched_data msd = { -+ .can_add_hw = 1, -+ .event_type = event_type, -+ }; - visit_groups_merge(ctx, groups, smp_processor_id(), pmu, -- merge_sched_in, &can_add_hw); -+ merge_sched_in, &msd); - } - - static void __pmu_ctx_sched_in(struct perf_event_pmu_context *pmu_ctx, -@@ -4050,9 +4162,9 @@ static void __pmu_ctx_sched_in(struct perf_event_pmu_context *pmu_ctx, - struct perf_event_context *ctx = pmu_ctx->ctx; - - if (event_type & EVENT_PINNED) -- pmu_groups_sched_in(ctx, &ctx->pinned_groups, pmu_ctx->pmu); -+ pmu_groups_sched_in(ctx, &ctx->pinned_groups, pmu_ctx->pmu, event_type); - if (event_type & EVENT_FLEXIBLE) -- pmu_groups_sched_in(ctx, &ctx->flexible_groups, pmu_ctx->pmu); -+ pmu_groups_sched_in(ctx, &ctx->flexible_groups, pmu_ctx->pmu, event_type); - } - - static void -@@ -4069,9 +4181,11 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - return; - - if (!(is_active & EVENT_TIME)) { -+ /* EVENT_TIME should be active while the guest runs */ -+ WARN_ON_ONCE(event_type & EVENT_GUEST); - /* start ctx time */ - __update_context_time(ctx, false); -- perf_cgroup_set_timestamp(cpuctx); -+ perf_cgroup_set_timestamp(cpuctx, false); - /* - * CPU-release for the below ->is_active store, - * see __load_acquire() in perf_event_time_now() -@@ -4087,7 +4201,23 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - WARN_ON_ONCE(cpuctx->task_ctx != ctx); - } - -- is_active ^= ctx->is_active; /* changed bits */ -+ if (event_type & EVENT_GUEST) { -+ /* -+ * Schedule in the required exclude_guest events of PMU -+ * with PERF_PMU_CAP_MEDIATED_VPMU. -+ */ -+ is_active = event_type & EVENT_ALL; -+ -+ /* -+ * Update ctx time to set the new start time for -+ * the exclude_guest events. -+ */ -+ update_context_time(ctx); -+ update_cgrp_time_from_cpuctx(cpuctx, false); -+ barrier(); -+ } else { -+ is_active ^= ctx->is_active; /* changed bits */ -+ } - - /* - * First go through the list and put on any pinned groups -@@ -4095,13 +4225,13 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - */ - if (is_active & EVENT_PINNED) { - for_each_epc(pmu_ctx, ctx, pmu, event_type) -- __pmu_ctx_sched_in(pmu_ctx, EVENT_PINNED); -+ __pmu_ctx_sched_in(pmu_ctx, EVENT_PINNED | (event_type & EVENT_GUEST)); - } - - /* Then walk through the lower prio flexible groups */ - if (is_active & EVENT_FLEXIBLE) { - for_each_epc(pmu_ctx, ctx, pmu, event_type) -- __pmu_ctx_sched_in(pmu_ctx, EVENT_FLEXIBLE); -+ __pmu_ctx_sched_in(pmu_ctx, EVENT_FLEXIBLE | (event_type & EVENT_GUEST)); - } - } - -@@ -6628,22 +6758,22 @@ void perf_event_update_userpage(struct perf_event *event) - goto unlock; - - /* -- * compute total_time_enabled, total_time_running -- * based on snapshot values taken when the event -- * was last scheduled in. -+ * Disable preemption to guarantee consistent time stamps are stored to -+ * the user page. -+ */ -+ preempt_disable(); -+ -+ /* -+ * Compute total_time_enabled, total_time_running based on snapshot -+ * values taken when the event was last scheduled in. - * -- * we cannot simply called update_context_time() -- * because of locking issue as we can be called in -- * NMI context -+ * We cannot simply call update_context_time() because doing so would -+ * lead to deadlock when called from NMI context. - */ - calc_timer_values(event, &now, &enabled, &running); - - userpg = rb->user_page; -- /* -- * Disable preemption to guarantee consistent time stamps are stored to -- * the user page. -- */ -- preempt_disable(); -+ - ++userpg->lock; - barrier(); - userpg->index = perf_event_index(event); -@@ -7943,13 +8073,11 @@ static void perf_output_read(struct perf_output_handle *handle, - u64 read_format = event->attr.read_format; - - /* -- * compute total_time_enabled, total_time_running -- * based on snapshot values taken when the event -- * was last scheduled in. -+ * Compute total_time_enabled, total_time_running based on snapshot -+ * values taken when the event was last scheduled in. - * -- * we cannot simply called update_context_time() -- * because of locking issue as we are called in -- * NMI context -+ * We cannot simply call update_context_time() because doing so would -+ * lead to deadlock when called from NMI context. - */ - if (read_format & PERF_FORMAT_TOTAL_TIMES) - calc_timer_values(event, &now, &enabled, &running); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf b/SPECS/kernel-rt/0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf deleted file mode 100644 index d2f0b8002..000000000 --- a/SPECS/kernel-rt/0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf +++ /dev/null @@ -1,125 +0,0 @@ -From 4cdfd036c183754b28cc9a0e9bdae165ebbfefcb Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Tue, 13 May 2025 10:23:53 -0700 -Subject: [PATCH 062/100] perf: Add APIs to load/put guest mediated PMU context - -Add exported APIs to load/put a guest mediated PMU context. KVM will -load the guest PMU shortly before VM-Enter, and put the guest PMU shortly -after VM-Exit. - -On the perf side of things, schedule out all exclude_guest events when the -guest context is loaded, and schedule them back in when the guest context -is put. I.e. yield the hardware PMU resources to the guest, by way of KVM. - -Note, perf is only responsible for managing host context. KVM is -responsible for loading/storing guest state to/from hardware. - -Suggested-by: Sean Christopherson -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: shuffle patches around, write changelog] -Signed-off-by: Sean Christopherson ---- - include/linux/perf_event.h | 2 ++ - kernel/events/core.c | 61 ++++++++++++++++++++++++++++++++++++++ - 2 files changed, 63 insertions(+) - -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index dfe33bc72180..57341843be12 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1929,6 +1929,8 @@ extern u64 perf_event_pause(struct perf_event *event, bool reset); - #ifdef CONFIG_PERF_GUEST_MEDIATED_PMU - int perf_create_mediated_pmu(void); - void perf_release_mediated_pmu(void); -+void perf_load_guest_context(unsigned long data); -+void perf_put_guest_context(void); - #endif - - #else /* !CONFIG_PERF_EVENTS: */ -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 0b36899f5939..45a8b3f0a6f3 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -469,10 +469,19 @@ static cpumask_var_t perf_online_pkg_mask; - static cpumask_var_t perf_online_sys_mask; - static struct kmem_cache *perf_event_cache; - -+#ifdef CONFIG_PERF_GUEST_MEDIATED_PMU -+static DEFINE_PER_CPU(bool, guest_ctx_loaded); -+ -+static __always_inline bool is_guest_mediated_pmu_loaded(void) -+{ -+ return __this_cpu_read(guest_ctx_loaded); -+} -+#else - static __always_inline bool is_guest_mediated_pmu_loaded(void) - { - return false; - } -+#endif - - /* - * perf event paranoia level: -@@ -6385,6 +6394,58 @@ void perf_release_mediated_pmu(void) - atomic_dec(&nr_mediated_pmu_vms); - } - EXPORT_SYMBOL_GPL(perf_release_mediated_pmu); -+ -+/* When loading a guest's mediated PMU, schedule out all exclude_guest events. */ -+void perf_load_guest_context(unsigned long data) -+{ -+ struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ -+ lockdep_assert_irqs_disabled(); -+ -+ guard(perf_ctx_lock)(cpuctx, cpuctx->task_ctx); -+ -+ if (WARN_ON_ONCE(__this_cpu_read(guest_ctx_loaded))) -+ return; -+ -+ perf_ctx_disable(&cpuctx->ctx, EVENT_GUEST); -+ ctx_sched_out(&cpuctx->ctx, NULL, EVENT_GUEST); -+ if (cpuctx->task_ctx) { -+ perf_ctx_disable(cpuctx->task_ctx, EVENT_GUEST); -+ task_ctx_sched_out(cpuctx->task_ctx, NULL, EVENT_GUEST); -+ } -+ -+ perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); -+ if (cpuctx->task_ctx) -+ perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); -+ -+ __this_cpu_write(guest_ctx_loaded, true); -+} -+EXPORT_SYMBOL_GPL(perf_load_guest_context); -+ -+void perf_put_guest_context(void) -+{ -+ struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ -+ lockdep_assert_irqs_disabled(); -+ -+ guard(perf_ctx_lock)(cpuctx, cpuctx->task_ctx); -+ -+ if (WARN_ON_ONCE(!__this_cpu_read(guest_ctx_loaded))) -+ return; -+ -+ perf_ctx_disable(&cpuctx->ctx, EVENT_GUEST); -+ if (cpuctx->task_ctx) -+ perf_ctx_disable(cpuctx->task_ctx, EVENT_GUEST); -+ -+ perf_event_sched_in(cpuctx, cpuctx->task_ctx, NULL, EVENT_GUEST); -+ -+ if (cpuctx->task_ctx) -+ perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); -+ perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); -+ -+ __this_cpu_write(guest_ctx_loaded, false); -+} -+EXPORT_SYMBOL_GPL(perf_put_guest_context); - #else - static int mediated_pmu_account_event(struct perf_event *event) { return 0; } - static void mediated_pmu_unaccount_event(struct perf_event *event) {} --- -2.43.0 - diff --git a/SPECS/kernel-rt/0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf b/SPECS/kernel-rt/0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf deleted file mode 100644 index 3d5b3e65a..000000000 --- a/SPECS/kernel-rt/0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf +++ /dev/null @@ -1,260 +0,0 @@ -From 73de4b308edcc8a9de03ccfd91b1d796685d5d00 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 07:35:40 -0700 -Subject: [PATCH 063/100] perf: core/x86: Register a new vector for handling - mediated guest PMIs - -Wire up system vector 0xf5 for handling PMIs (i.e. interrupts delivered -through the LVTPC) while running KVM guests with a mediated PMU. Perf -currently delivers all PMIs as NMIs, e.g. so that events that trigger while -IRQs are disabled aren't delayed and generate useless records, but due to -the multiplexing of NMIs throughout the system, correctly identifying NMIs -for a mediated PMU is practically infeasible. - -To (greatly) simplify identifying guest mediated PMU PMIs, perf will -switch the CPU's LVTPC between PERF_GUEST_MEDIATED_PMI_VECTOR and NMI when -guest PMU context is loaded/put. I.e. PMIs that are generated by the CPU -while the guest is active will be identified purely based on the IRQ -vector. - -Route the vector through perf, e.g. as opposed to letting KVM attach a -handler directly a la posted interrupt notification vectors, as perf owns -the LVTPC and thus is the rightful owner of PERF_GUEST_MEDIATED_PMI_VECTOR. -Functionally, having KVM directly own the vector would be fine (both KVM -and perf will be completely aware of when a mediated PMU is active), but -would lead to an undesirable split in ownership: perf would be responsible -for installing the vector, but not handling the resulting IRQs. - -Add a new perf_guest_info_callbacks hook (and static call) to allow KVM to -register its handler with perf when running guests with mediated PMUs. - -Note, because KVM always runs guests with host IRQs enabled, there is no -danger of a PMI being delayed from the guest's perspective due to using a -regular IRQ instead of an NMI. - -Signed-off-by: Sean Christopherson ---- - arch/x86/entry/entry_fred.c | 1 + - arch/x86/include/asm/hardirq.h | 3 +++ - arch/x86/include/asm/idtentry.h | 6 ++++++ - arch/x86/include/asm/irq_vectors.h | 4 +++- - arch/x86/kernel/idt.c | 3 +++ - arch/x86/kernel/irq.c | 19 +++++++++++++++++++ - include/linux/perf_event.h | 8 ++++++++ - kernel/events/core.c | 9 +++++++-- - .../beauty/arch/x86/include/asm/irq_vectors.h | 3 ++- - virt/kvm/kvm_main.c | 3 +++ - 10 files changed, 55 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c -index f004a4dc74c2..d80861a4cd00 100644 ---- a/arch/x86/entry/entry_fred.c -+++ b/arch/x86/entry/entry_fred.c -@@ -114,6 +114,7 @@ static idtentry_t sysvec_table[NR_SYSTEM_VECTORS] __ro_after_init = { - - SYSVEC(IRQ_WORK_VECTOR, irq_work), - -+ SYSVEC(PERF_GUEST_MEDIATED_PMI_VECTOR, perf_guest_mediated_pmi_handler), - SYSVEC(POSTED_INTR_VECTOR, kvm_posted_intr_ipi), - SYSVEC(POSTED_INTR_WAKEUP_VECTOR, kvm_posted_intr_wakeup_ipi), - SYSVEC(POSTED_INTR_NESTED_VECTOR, kvm_posted_intr_nested_ipi), -diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h -index f00c09ffe6a9..f221d001e4ed 100644 ---- a/arch/x86/include/asm/hardirq.h -+++ b/arch/x86/include/asm/hardirq.h -@@ -18,6 +18,9 @@ typedef struct { - unsigned int kvm_posted_intr_ipis; - unsigned int kvm_posted_intr_wakeup_ipis; - unsigned int kvm_posted_intr_nested_ipis; -+#endif -+#ifdef CONFIG_GUEST_PERF_EVENTS -+ unsigned int perf_guest_mediated_pmis; - #endif - unsigned int x86_platform_ipis; /* arch dependent */ - unsigned int apic_perf_irqs; -diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h -index a4ec27c67988..5109c24b3c1c 100644 ---- a/arch/x86/include/asm/idtentry.h -+++ b/arch/x86/include/asm/idtentry.h -@@ -751,6 +751,12 @@ DECLARE_IDTENTRY_SYSVEC(POSTED_INTR_NESTED_VECTOR, sysvec_kvm_posted_intr_nested - # define fred_sysvec_kvm_posted_intr_nested_ipi NULL - #endif - -+# ifdef CONFIG_GUEST_PERF_EVENTS -+DECLARE_IDTENTRY_SYSVEC(PERF_GUEST_MEDIATED_PMI_VECTOR, sysvec_perf_guest_mediated_pmi_handler); -+#else -+# define fred_sysvec_perf_guest_mediated_pmi_handler NULL -+#endif -+ - # ifdef CONFIG_X86_POSTED_MSI - DECLARE_IDTENTRY_SYSVEC(POSTED_MSI_NOTIFICATION_VECTOR, sysvec_posted_msi_notification); - #else -diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h -index 47051871b436..85253fc8e384 100644 ---- a/arch/x86/include/asm/irq_vectors.h -+++ b/arch/x86/include/asm/irq_vectors.h -@@ -77,7 +77,9 @@ - */ - #define IRQ_WORK_VECTOR 0xf6 - --/* 0xf5 - unused, was UV_BAU_MESSAGE */ -+/* IRQ vector for PMIs when running a guest with a mediated PMU. */ -+#define PERF_GUEST_MEDIATED_PMI_VECTOR 0xf5 -+ - #define DEFERRED_ERROR_VECTOR 0xf4 - - /* Vector on which hypervisor callbacks will be delivered */ -diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c -index f445bec516a0..260456588756 100644 ---- a/arch/x86/kernel/idt.c -+++ b/arch/x86/kernel/idt.c -@@ -158,6 +158,9 @@ static const __initconst struct idt_data apic_idts[] = { - INTG(POSTED_INTR_WAKEUP_VECTOR, asm_sysvec_kvm_posted_intr_wakeup_ipi), - INTG(POSTED_INTR_NESTED_VECTOR, asm_sysvec_kvm_posted_intr_nested_ipi), - # endif -+#ifdef CONFIG_GUEST_PERF_EVENTS -+ INTG(PERF_GUEST_MEDIATED_PMI_VECTOR, asm_sysvec_perf_guest_mediated_pmi_handler), -+#endif - # ifdef CONFIG_IRQ_WORK - INTG(IRQ_WORK_VECTOR, asm_sysvec_irq_work), - # endif -diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c -index 10721a125226..e9050e69717e 100644 ---- a/arch/x86/kernel/irq.c -+++ b/arch/x86/kernel/irq.c -@@ -191,6 +191,13 @@ int arch_show_interrupts(struct seq_file *p, int prec) - irq_stats(j)->kvm_posted_intr_wakeup_ipis); - seq_puts(p, " Posted-interrupt wakeup event\n"); - #endif -+#ifdef CONFIG_GUEST_PERF_EVENTS -+ seq_printf(p, "%*s: ", prec, "VPMI"); -+ for_each_online_cpu(j) -+ seq_printf(p, "%10u ", -+ irq_stats(j)->perf_guest_mediated_pmis); -+ seq_puts(p, " Perf Guest Mediated PMI\n"); -+#endif - #ifdef CONFIG_X86_POSTED_MSI - seq_printf(p, "%*s: ", prec, "PMN"); - for_each_online_cpu(j) -@@ -348,6 +355,18 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_x86_platform_ipi) - } - #endif - -+#ifdef CONFIG_GUEST_PERF_EVENTS -+/* -+ * Handler for PERF_GUEST_MEDIATED_PMI_VECTOR. -+ */ -+DEFINE_IDTENTRY_SYSVEC(sysvec_perf_guest_mediated_pmi_handler) -+{ -+ apic_eoi(); -+ inc_irq_stat(perf_guest_mediated_pmis); -+ perf_guest_handle_mediated_pmi(); -+} -+#endif -+ - #if IS_ENABLED(CONFIG_KVM) - static void dummy_handler(void) {} - static void (*kvm_posted_intr_wakeup_handler)(void) = dummy_handler; -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index 57341843be12..63a16f8e74a8 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1681,6 +1681,8 @@ struct perf_guest_info_callbacks { - unsigned int (*state)(void); - unsigned long (*get_ip)(void); - unsigned int (*handle_intel_pt_intr)(void); -+ -+ void (*handle_mediated_pmi)(void); - }; - - #ifdef CONFIG_GUEST_PERF_EVENTS -@@ -1690,6 +1692,7 @@ extern struct perf_guest_info_callbacks __rcu *perf_guest_cbs; - DECLARE_STATIC_CALL(__perf_guest_state, *perf_guest_cbs->state); - DECLARE_STATIC_CALL(__perf_guest_get_ip, *perf_guest_cbs->get_ip); - DECLARE_STATIC_CALL(__perf_guest_handle_intel_pt_intr, *perf_guest_cbs->handle_intel_pt_intr); -+DECLARE_STATIC_CALL(__perf_guest_handle_mediated_pmi, *perf_guest_cbs->handle_mediated_pmi); - - static inline unsigned int perf_guest_state(void) - { -@@ -1706,6 +1709,11 @@ static inline unsigned int perf_guest_handle_intel_pt_intr(void) - return static_call(__perf_guest_handle_intel_pt_intr)(); - } - -+static inline void perf_guest_handle_mediated_pmi(void) -+{ -+ static_call(__perf_guest_handle_mediated_pmi)(); -+} -+ - extern void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs); - extern void perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs); - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 45a8b3f0a6f3..23f9e68b89ad 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -7633,6 +7633,7 @@ struct perf_guest_info_callbacks __rcu *perf_guest_cbs; - DEFINE_STATIC_CALL_RET0(__perf_guest_state, *perf_guest_cbs->state); - DEFINE_STATIC_CALL_RET0(__perf_guest_get_ip, *perf_guest_cbs->get_ip); - DEFINE_STATIC_CALL_RET0(__perf_guest_handle_intel_pt_intr, *perf_guest_cbs->handle_intel_pt_intr); -+DEFINE_STATIC_CALL_RET0(__perf_guest_handle_mediated_pmi, *perf_guest_cbs->handle_mediated_pmi); - - void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) - { -@@ -7647,6 +7648,10 @@ void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) - if (cbs->handle_intel_pt_intr) - static_call_update(__perf_guest_handle_intel_pt_intr, - cbs->handle_intel_pt_intr); -+ -+ if (cbs->handle_mediated_pmi) -+ static_call_update(__perf_guest_handle_mediated_pmi, -+ cbs->handle_mediated_pmi); - } - EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks); - -@@ -7658,8 +7663,8 @@ void perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) - rcu_assign_pointer(perf_guest_cbs, NULL); - static_call_update(__perf_guest_state, (void *)&__static_call_return0); - static_call_update(__perf_guest_get_ip, (void *)&__static_call_return0); -- static_call_update(__perf_guest_handle_intel_pt_intr, -- (void *)&__static_call_return0); -+ static_call_update(__perf_guest_handle_intel_pt_intr, (void *)&__static_call_return0); -+ static_call_update(__perf_guest_handle_mediated_pmi, (void *)&__static_call_return0); - synchronize_rcu(); - } - EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); -diff --git a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h -index 47051871b436..6e1d5b955aae 100644 ---- a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h -+++ b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h -@@ -77,7 +77,8 @@ - */ - #define IRQ_WORK_VECTOR 0xf6 - --/* 0xf5 - unused, was UV_BAU_MESSAGE */ -+#define PERF_GUEST_MEDIATED_PMI_VECTOR 0xf5 -+ - #define DEFERRED_ERROR_VECTOR 0xf4 - - /* Vector on which hypervisor callbacks will be delivered */ -diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index 6c07dd423458..ecafab2e17d9 100644 ---- a/virt/kvm/kvm_main.c -+++ b/virt/kvm/kvm_main.c -@@ -6426,11 +6426,14 @@ static struct perf_guest_info_callbacks kvm_guest_cbs = { - .state = kvm_guest_state, - .get_ip = kvm_guest_get_ip, - .handle_intel_pt_intr = NULL, -+ .handle_mediated_pmi = NULL, - }; - - void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void)) - { - kvm_guest_cbs.handle_intel_pt_intr = pt_intr_handler; -+ kvm_guest_cbs.handle_mediated_pmi = NULL; -+ - perf_register_guest_info_callbacks(&kvm_guest_cbs); - } - void kvm_unregister_perf_callbacks(void) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf b/SPECS/kernel-rt/0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf deleted file mode 100644 index fca9bdd41..000000000 --- a/SPECS/kernel-rt/0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf +++ /dev/null @@ -1,118 +0,0 @@ -From bb41c19e08c1b4bdbef275667c7cecfc458c6e30 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 9 Jul 2025 09:05:45 -0700 -Subject: [PATCH 064/100] perf/x86: Switch LVTPC to/from mediated PMI vector on - guest load/put context - -Add arch hooks to the mediated vPMU load/put APIs, and use the hooks to -switch PMIs to the dedicated mediated PMU IRQ vector on load, and back to -perf's standard NMI when the guest context is put. I.e. route PMIs to -PERF_GUEST_MEDIATED_PMI_VECTOR when the guest context is active, and to -NMIs while the host context is active. - -While running with guest context loaded, ignore all NMIs (in perf). Any -NMI that arrives while the LVTPC points at the mediated PMU IRQ vector -can't possibly be due to a host perf event. - -Signed-off-by: Xiong Zhang -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: use arch hook instead of per-PMU callback] -Signed-off-by: Sean Christopherson ---- - arch/x86/events/core.c | 27 +++++++++++++++++++++++++++ - include/linux/perf_event.h | 3 +++ - kernel/events/core.c | 4 ++++ - 3 files changed, 34 insertions(+) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 56d197a56d59..dc143693ce98 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -55,6 +55,8 @@ DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { - .pmu = &pmu, - }; - -+static DEFINE_PER_CPU(bool, x86_guest_ctx_loaded); -+ - DEFINE_STATIC_KEY_FALSE(rdpmc_never_available_key); - DEFINE_STATIC_KEY_FALSE(rdpmc_always_available_key); - DEFINE_STATIC_KEY_FALSE(perf_is_hybrid); -@@ -1866,6 +1868,16 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs) - u64 finish_clock; - int ret; - -+ /* -+ * Ignore all NMIs when a guest's mediated PMU context is loaded. Any -+ * such NMI can't be due to a PMI as the CPU's LVTPC is switched to/from -+ * the dedicated mediated PMI IRQ vector while host events are quiesced. -+ * Attempting to handle a PMI while the guest's context is loaded will -+ * generate false positives and clobber guest state. -+ */ -+ if (this_cpu_read(x86_guest_ctx_loaded)) -+ return NMI_DONE; -+ - /* - * All PMUs/events that share this PMI handler should make sure to - * increment active_events for their events. -@@ -2841,6 +2853,21 @@ static struct pmu pmu = { - .filter = x86_pmu_filter, - }; - -+void arch_perf_load_guest_context(unsigned long data) -+{ -+ u32 masked = data & APIC_LVT_MASKED; -+ -+ apic_write(APIC_LVTPC, -+ APIC_DM_FIXED | PERF_GUEST_MEDIATED_PMI_VECTOR | masked); -+ this_cpu_write(x86_guest_ctx_loaded, true); -+} -+ -+void arch_perf_put_guest_context(void) -+{ -+ this_cpu_write(x86_guest_ctx_loaded, false); -+ apic_write(APIC_LVTPC, APIC_DM_NMI); -+} -+ - void arch_perf_update_userpage(struct perf_event *event, - struct perf_event_mmap_page *userpg, u64 now) - { -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index 63a16f8e74a8..012dbc9de139 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1850,6 +1850,9 @@ static inline unsigned long perf_arch_guest_misc_flags(struct pt_regs *regs) - # define perf_arch_guest_misc_flags(regs) perf_arch_guest_misc_flags(regs) - #endif - -+extern void arch_perf_load_guest_context(unsigned long data); -+extern void arch_perf_put_guest_context(void); -+ - static inline bool needs_branch_stack(struct perf_event *event) - { - return event->attr.branch_sample_type != 0; -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 23f9e68b89ad..e69108914c80 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -6414,6 +6414,8 @@ void perf_load_guest_context(unsigned long data) - task_ctx_sched_out(cpuctx->task_ctx, NULL, EVENT_GUEST); - } - -+ arch_perf_load_guest_context(data); -+ - perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); - if (cpuctx->task_ctx) - perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); -@@ -6439,6 +6441,8 @@ void perf_put_guest_context(void) - - perf_event_sched_in(cpuctx, cpuctx->task_ctx, NULL, EVENT_GUEST); - -+ arch_perf_put_guest_context(); -+ - if (cpuctx->task_ctx) - perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); - perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf b/SPECS/kernel-rt/0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf deleted file mode 100644 index ff5a6c603..000000000 --- a/SPECS/kernel-rt/0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf +++ /dev/null @@ -1,39 +0,0 @@ -From ac602f207da73491aad5025c65c4b07fee9d8caa Mon Sep 17 00:00:00 2001 -From: Sandipan Das -Date: Mon, 24 Mar 2025 17:30:52 +0000 -Subject: [PATCH 065/100] perf/x86/core: Do not set bit width for unavailable - counters - -Not all x86 processors have fixed counters. It may also be the case that -a processor has only fixed counters and no general-purpose counters. Set -the bit widths corresponding to each counter type only if such counters -are available. - -Fixes: b3d9468a8bd2 ("perf, x86: Expose perf capability to other modules") -Signed-off-by: Sandipan Das -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/events/core.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index dc143693ce98..6036be2afe0a 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -3239,8 +3239,8 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) - cap->version = x86_pmu.version; - cap->num_counters_gp = x86_pmu_num_counters(NULL); - cap->num_counters_fixed = x86_pmu_num_counters_fixed(NULL); -- cap->bit_width_gp = x86_pmu.cntval_bits; -- cap->bit_width_fixed = x86_pmu.cntval_bits; -+ cap->bit_width_gp = cap->num_counters_gp ? x86_pmu.cntval_bits : 0; -+ cap->bit_width_fixed = cap->num_counters_fixed ? x86_pmu.cntval_bits : 0; - cap->events_mask = (unsigned int)x86_pmu.events_maskl; - cap->events_mask_len = x86_pmu.events_mask_len; - cap->pebs_ept = x86_pmu.pebs_ept; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf b/SPECS/kernel-rt/0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf deleted file mode 100644 index 6e444de76..000000000 --- a/SPECS/kernel-rt/0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf +++ /dev/null @@ -1,44 +0,0 @@ -From f0f9e833e53e50f397f7ae7db590a841d53fd19c Mon Sep 17 00:00:00 2001 -From: Mingwei Zhang -Date: Mon, 24 Mar 2025 17:30:53 +0000 -Subject: [PATCH 066/100] perf/x86/core: Plumb mediated PMU capability from - x86_pmu to x86_pmu_cap - -Plumb mediated PMU capability to x86_pmu_cap in order to let any kernel -entity such as KVM know that host PMU support mediated PMU mode and has -the implementation. - -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/events/core.c | 1 + - arch/x86/include/asm/perf_event.h | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 6036be2afe0a..68d0bea0f62e 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -3244,6 +3244,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) - cap->events_mask = (unsigned int)x86_pmu.events_maskl; - cap->events_mask_len = x86_pmu.events_mask_len; - cap->pebs_ept = x86_pmu.pebs_ept; -+ cap->mediated = !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU); - } - EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability); - -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index e723666c1de2..a3ef52e943b0 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -305,6 +305,7 @@ struct x86_pmu_capability { - unsigned int events_mask; - int events_mask_len; - unsigned int pebs_ept :1; -+ unsigned int mediated :1; - }; - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf b/SPECS/kernel-rt/0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf deleted file mode 100644 index 2db51f01d..000000000 --- a/SPECS/kernel-rt/0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf +++ /dev/null @@ -1,45 +0,0 @@ -From 56db0f02cf340a054a08db9482339290189eac16 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:31:13 +0000 -Subject: [PATCH 067/100] perf/x86/intel: Support PERF_PMU_CAP_MEDIATED_VPMU - -Apply the PERF_PMU_CAP_MEDIATED_VPMU for Intel core PMU. It only indicates -that the perf side of core PMU is ready to support the mediated vPMU. -Besides the capability, the hypervisor, a.k.a. KVM, still needs to check -the PMU version and other PMU features/capabilities to decide whether to -enable support mediated vPMUs. - -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: massage changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/events/intel/core.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index b6ed211695ff..f0cd578a14e1 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5675,6 +5675,8 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) - else - pmu->intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS; - -+ pmu->pmu.capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; -+ - intel_pmu_check_event_constraints_all(&pmu->pmu); - - intel_pmu_check_extra_regs(pmu->extra_regs); -@@ -7293,6 +7295,9 @@ __init int intel_pmu_init(void) - pr_cont(" AnyThread deprecated, "); - } - -+ /* The perf side of core PMU is ready to support the mediated vPMU. */ -+ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; -+ - /* - * Many features on and after V6 require dynamic constraint, - * e.g., Arch PEBS, ACR. --- -2.43.0 - diff --git a/SPECS/kernel-rt/0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf b/SPECS/kernel-rt/0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf deleted file mode 100644 index 710b360e5..000000000 --- a/SPECS/kernel-rt/0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf +++ /dev/null @@ -1,39 +0,0 @@ -From e8342617d4c8bd471926a482e2b039461cdc0862 Mon Sep 17 00:00:00 2001 -From: Sandipan Das -Date: Mon, 24 Mar 2025 17:31:14 +0000 -Subject: [PATCH 068/100] perf/x86/amd: Support PERF_PMU_CAP_MEDIATED_VPMU for - AMD host - -Apply the PERF_PMU_CAP_MEDIATED_VPMU flag for version 2 and later -implementations of the core PMU. Aside from having Global Control and -Status registers, virtualizing the PMU using the mediated model requires -an interface to set or clear the overflow bits in the Global Status MSRs -while restoring or saving the PMU context of a vCPU. - -PerfMonV2-capable hardware has additional MSRs for this purpose, namely -PerfCntrGlobalStatusSet and PerfCntrGlobalStatusClr, thereby making it -suitable for use with mediated vPMU. - -Signed-off-by: Sandipan Das -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/events/amd/core.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c -index b20661b8621d..8179fb5f1ee3 100644 ---- a/arch/x86/events/amd/core.c -+++ b/arch/x86/events/amd/core.c -@@ -1433,6 +1433,8 @@ static int __init amd_core_pmu_init(void) - - amd_pmu_global_cntr_mask = x86_pmu.cntr_mask64; - -+ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; -+ - /* Update PMC handling functions */ - x86_pmu.enable_all = amd_pmu_v2_enable_all; - x86_pmu.disable_all = amd_pmu_v2_disable_all; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf b/SPECS/kernel-rt/0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf deleted file mode 100644 index f57899436..000000000 --- a/SPECS/kernel-rt/0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf +++ /dev/null @@ -1,58 +0,0 @@ -From ae551c391dfe878f8fdfe386cfb0b884a0ee4e10 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 16 May 2025 07:09:39 -0700 -Subject: [PATCH 069/100] KVM: VMX: Setup canonical VMCS config prior to - kvm_x86_vendor_init() - -Setup the golden VMCS config during vmx_init(), before the call to -kvm_x86_vendor_init(), instead of waiting until the callback to do -hardware setup. setup_vmcs_config() only touches VMX state, i.e. doesn't -poke anything in kvm.ko, and has no runtime dependencies beyond -hv_init_evmcs(). - -Setting the VMCS config early on will allow referencing VMCS and VMX -capabilities at any point during setup, e.g. to check for PERF_GLOBAL_CTRL -save/load support during mediated PMU initialization. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 13 +++++++++---- - 1 file changed, 9 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 95765db52992..ed10013dac95 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -8335,8 +8335,6 @@ __init int vmx_hardware_setup(void) - - vmx_setup_user_return_msrs(); - -- if (setup_vmcs_config(&vmcs_config, &vmx_capability) < 0) -- return -EIO; - - if (boot_cpu_has(X86_FEATURE_NX)) - kvm_enable_efer_bits(EFER_NX); -@@ -8560,11 +8558,18 @@ int __init vmx_init(void) - return -EOPNOTSUPP; - - /* -- * Note, hv_init_evmcs() touches only VMX knobs, i.e. there's nothing -- * to unwind if a later step fails. -+ * Note, VMCS and eVMCS configuration only touch VMX knobs/variables, -+ * i.e. there's nothing to unwind if a later step fails. - */ - hv_init_evmcs(); - -+ /* -+ * Parse the VMCS config and VMX capabilities before anything else, so -+ * that the information is available to all setup flows. -+ */ -+ if (setup_vmcs_config(&vmcs_config, &vmx_capability) < 0) -+ return -EIO; -+ - r = kvm_x86_vendor_init(&vt_init_ops); - if (r) - return r; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf b/SPECS/kernel-rt/0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf deleted file mode 100644 index e3357d928..000000000 --- a/SPECS/kernel-rt/0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf +++ /dev/null @@ -1,43 +0,0 @@ -From 20223d7e37fe68853f3ce86876d015d8ca4b4e27 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Mon, 4 Aug 2025 13:24:23 -0700 -Subject: [PATCH 070/100] KVM: SVM: Check pmu->version, not enable_pmu, when - getting PMC MSRs - -Gate access to PMC MSRs based on pmu->version, not on kvm->arch.enable_pmu, -to more accurately reflect KVM's behavior. This is a glorified nop, as -pmu->version and pmu->nr_arch_gp_counters can only be non-zero if -amd_pmu_refresh() is reached, kvm_pmu_refresh() invokes amd_pmu_refresh() -if and only if kvm->arch.enable_pmu is true, and amd_pmu_refresh() forces -pmu->version to be 1 or 2. - -I.e. the following holds true: - - !pmu->nr_arch_gp_counters || kvm->arch.enable_pmu == (pmu->version > 0) - -and so the only way for amd_pmu_get_pmc() to return a non-NULL value is if -both kvm->arch.enable_pmu and pmu->version evaluate to true. - -No real functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 288f7f2a46f2..7b8577f3c57a 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -41,7 +41,7 @@ static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr, - struct kvm_vcpu *vcpu = pmu_to_vcpu(pmu); - unsigned int idx; - -- if (!vcpu->kvm->arch.enable_pmu) -+ if (!pmu->version) - return NULL; - - switch (msr) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf b/SPECS/kernel-rt/0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf deleted file mode 100644 index c0f5146c3..000000000 --- a/SPECS/kernel-rt/0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf +++ /dev/null @@ -1,118 +0,0 @@ -From 1b7729987d29af6aaf929efc0ab417c87176a6eb Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 17:46:44 -0700 -Subject: [PATCH 071/100] KVM: Add a simplified wrapper for registering perf - callbacks - -Add a parameter-less API for registering perf callbacks in anticipation of -introducing another x86-only parameter for handling mediated PMU PMIs. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/arm64/kvm/arm.c | 2 +- - arch/loongarch/kvm/main.c | 2 +- - arch/riscv/kvm/main.c | 2 +- - arch/x86/kvm/x86.c | 2 +- - include/linux/kvm_host.h | 11 +++++++++-- - virt/kvm/kvm_main.c | 5 +++-- - 6 files changed, 16 insertions(+), 8 deletions(-) - -diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c -index 888f7c7abf54..6c604b5214f2 100644 ---- a/arch/arm64/kvm/arm.c -+++ b/arch/arm64/kvm/arm.c -@@ -2328,7 +2328,7 @@ static int __init init_subsystems(void) - if (err) - goto out; - -- kvm_register_perf_callbacks(NULL); -+ kvm_register_perf_callbacks(); - - out: - if (err) -diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c -index 80ea63d465b8..f62326fe29fa 100644 ---- a/arch/loongarch/kvm/main.c -+++ b/arch/loongarch/kvm/main.c -@@ -394,7 +394,7 @@ static int kvm_loongarch_env_init(void) - } - - kvm_init_gcsr_flag(); -- kvm_register_perf_callbacks(NULL); -+ kvm_register_perf_callbacks(); - - /* Register LoongArch IPI interrupt controller interface. */ - ret = kvm_loongarch_register_ipi_device(); -diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c -index 67c876de74ef..cbe842c2f615 100644 ---- a/arch/riscv/kvm/main.c -+++ b/arch/riscv/kvm/main.c -@@ -159,7 +159,7 @@ static int __init riscv_kvm_init(void) - kvm_info("AIA available with %d guest external interrupts\n", - kvm_riscv_aia_nr_hgei); - -- kvm_register_perf_callbacks(NULL); -+ kvm_register_perf_callbacks(); - - rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); - if (rc) { -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 5af2c5aed0f2..d80bbd5e0859 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9689,7 +9689,7 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - set_hv_tscchange_cb(kvm_hyperv_tsc_notifier); - #endif - -- kvm_register_perf_callbacks(ops->handle_intel_pt_intr); -+ __kvm_register_perf_callbacks(ops->handle_intel_pt_intr, NULL); - - if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && tdp_mmu_enabled) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SW_PROTECTED_VM); -diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h -index 15656b7fba6c..20c50eaa0089 100644 ---- a/include/linux/kvm_host.h -+++ b/include/linux/kvm_host.h -@@ -1731,10 +1731,17 @@ static inline bool kvm_arch_intc_initialized(struct kvm *kvm) - #ifdef CONFIG_GUEST_PERF_EVENTS - unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu); - --void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void)); -+void __kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void), -+ void (*mediated_pmi_handler)(void)); -+ -+static inline void kvm_register_perf_callbacks(void) -+{ -+ __kvm_register_perf_callbacks(NULL, NULL); -+} -+ - void kvm_unregister_perf_callbacks(void); - #else --static inline void kvm_register_perf_callbacks(void *ign) {} -+static inline void kvm_register_perf_callbacks(void) {} - static inline void kvm_unregister_perf_callbacks(void) {} - #endif /* CONFIG_GUEST_PERF_EVENTS */ - -diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index ecafab2e17d9..d477a7fda0ae 100644 ---- a/virt/kvm/kvm_main.c -+++ b/virt/kvm/kvm_main.c -@@ -6429,10 +6429,11 @@ static struct perf_guest_info_callbacks kvm_guest_cbs = { - .handle_mediated_pmi = NULL, - }; - --void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void)) -+void __kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void), -+ void (*mediated_pmi_handler)(void)) - { - kvm_guest_cbs.handle_intel_pt_intr = pt_intr_handler; -- kvm_guest_cbs.handle_mediated_pmi = NULL; -+ kvm_guest_cbs.handle_mediated_pmi = mediated_pmi_handler; - - perf_register_guest_info_callbacks(&kvm_guest_cbs); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf b/SPECS/kernel-rt/0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf deleted file mode 100644 index 30b594ac9..000000000 --- a/SPECS/kernel-rt/0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf +++ /dev/null @@ -1,72 +0,0 @@ -From bee18b05e80661bc9fa45d1d2ea5796ae76ac8de Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 17:38:40 -0700 -Subject: [PATCH 072/100] KVM: x86/pmu: Snapshot host (i.e. perf's) reported - PMU capabilities - -Take a snapshot of the unadulterated PMU capabilities provided by perf so -that KVM can compare guest vPMU capabilities against hardware capabilities -when determining whether or not to intercept PMU MSRs (and RDPMC). - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 15 ++++++++++----- - 1 file changed, 10 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 3206412a35a1..0f3e011824ed 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -26,6 +26,10 @@ - /* This is enough to filter the vast majority of currently defined events. */ - #define KVM_PMU_EVENT_FILTER_MAX_EVENTS 300 - -+/* Unadultered PMU capabilities of the host, i.e. of hardware. */ -+static struct x86_pmu_capability __read_mostly kvm_host_pmu; -+ -+/* KVM's PMU capabilities, i.e. the intersection of KVM and hardware support. */ - struct x86_pmu_capability __read_mostly kvm_pmu_cap; - EXPORT_SYMBOL_GPL(kvm_pmu_cap); - -@@ -104,6 +108,8 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; - int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; - -+ perf_get_x86_pmu_capability(&kvm_host_pmu); -+ - /* - * Hybrid PMUs don't play nice with virtualization without careful - * configuration by userspace, and KVM's APIs for reporting supported -@@ -114,18 +120,16 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - enable_pmu = false; - - if (enable_pmu) { -- perf_get_x86_pmu_capability(&kvm_pmu_cap); -- - /* - * WARN if perf did NOT disable hardware PMU if the number of - * architecturally required GP counters aren't present, i.e. if - * there are a non-zero number of counters, but fewer than what - * is architecturally required. - */ -- if (!kvm_pmu_cap.num_counters_gp || -- WARN_ON_ONCE(kvm_pmu_cap.num_counters_gp < min_nr_gp_ctrs)) -+ if (!kvm_host_pmu.num_counters_gp || -+ WARN_ON_ONCE(kvm_host_pmu.num_counters_gp < min_nr_gp_ctrs)) - enable_pmu = false; -- else if (is_intel && !kvm_pmu_cap.version) -+ else if (is_intel && !kvm_host_pmu.version) - enable_pmu = false; - } - -@@ -134,6 +138,7 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - return; - } - -+ memcpy(&kvm_pmu_cap, &kvm_host_pmu, sizeof(kvm_host_pmu)); - kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); - kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp, - pmu_ops->MAX_NR_GP_COUNTERS); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf b/SPECS/kernel-rt/0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf deleted file mode 100644 index 4855f1c3b..000000000 --- a/SPECS/kernel-rt/0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf +++ /dev/null @@ -1,202 +0,0 @@ -From 231dc35b989bd5c6fb4a8b412f2d838ec82b3353 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 4 Aug 2025 14:03:05 -0700 -Subject: [PATCH 073/100] KVM: x86/pmu: Start stubbing in mediated PMU support - -Introduce enable_mediated_pmu as a global variable, with the intent of -exposing it to userspace a vendor module parameter, to control and reflect -mediated vPMU support. Wire up the perf plumbing to create+release a -mediated PMU, but defer exposing the parameter to userspace until KVM -support for a mediated PMUs is fully landed. - -To (a) minimize compatibility issues, (b) to give userspace a chance to -opt out of the restrictive side-effects of perf_create_mediated_pmu(), -and (c) to avoid adding new dependencies between enabling an in-kernel -irqchip and a mediated vPMU, defer "creating" a mediated PMU in perf -until the first vCPU is created. - -Regarding userspace compatibility, an alternative solution would be to -make the mediated PMU fully opt-in, e.g. to avoid unexpected failure due -to perf_create_mediated_pmu() failing. Ironically, that approach creates -an even bigger compatibility issue, as turning on enable_mediated_pmu -would silently break VMMs that don't utilize KVM_CAP_PMU_CAPABILITY (well, -silently until the guest tried to access PMU assets). - -Regarding an in-kernel irqchip, create a mediated PMU if and only if the -VM has an in-kernel local APIC, as the mediated PMU will take a hard -dependency on forwarding PMIs to the guest without bouncing through host -userspace. Silently "drop" the PMU instead of rejecting KVM_CREATE_VCPU, -as KVM's existing vPMU support doesn't function correctly if the local -APIC is emulated by userspace, e.g. PMIs will never be delivered. I.e. -it's far, far more likely that rejecting KVM_CREATE_VCPU would cause -problems, e.g. for tests or userspace daemons that just want to probe -basic KVM functionality. - -Note! Deliberately make mediated PMU creation "sticky", i.e. don't unwind -it on failure to create a vCPU. Practically speaking, there's no harm to -having a VM with a mediated PMU and no vCPUs. To avoid an "impossible" VM -setup, reject KVM_CAP_PMU_CAPABILITY if a mediated PMU has been created, -i.e. don't let userspace disable PMU support after failed vCPU creation -(with PMU support enabled). - -Defer vendor specific requirements and constraints to the future. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 1 + - arch/x86/kvm/pmu.c | 4 ++++ - arch/x86/kvm/pmu.h | 7 +++++++ - arch/x86/kvm/x86.c | 37 +++++++++++++++++++++++++++++++-- - arch/x86/kvm/x86.h | 1 + - 5 files changed, 48 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 95d7d727db03..83682be4c088 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -1471,6 +1471,7 @@ struct kvm_arch { - - bool bus_lock_detection_enabled; - bool enable_pmu; -+ bool created_mediated_pmu; - - u32 notify_window; - u32 notify_vmexit_flags; -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 0f3e011824ed..4d4bb9b17412 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -133,6 +133,10 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - enable_pmu = false; - } - -+ if (!enable_pmu || !enable_mediated_pmu || !kvm_host_pmu.mediated || -+ !pmu_ops->is_mediated_pmu_supported(&kvm_host_pmu)) -+ enable_mediated_pmu = false; -+ - if (!enable_pmu) { - memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); - return; -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 5c3939e91f1d..a5c7c026b919 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -37,6 +37,8 @@ struct kvm_pmu_ops { - void (*deliver_pmi)(struct kvm_vcpu *vcpu); - void (*cleanup)(struct kvm_vcpu *vcpu); - -+ bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); -+ - const u64 EVENTSEL_EVENT; - const int MAX_NR_GP_COUNTERS; - const int MIN_NR_GP_COUNTERS; -@@ -58,6 +60,11 @@ static inline bool kvm_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) - return pmu->version > 1; - } - -+static inline bool kvm_vcpu_has_mediated_pmu(struct kvm_vcpu *vcpu) -+{ -+ return enable_mediated_pmu && vcpu_to_pmu(vcpu)->version; -+} -+ - /* - * KVM tracks all counters in 64-bit bitmaps, with general purpose counters - * mapped to bits 31:0 and fixed counters mapped to 63:32, e.g. fixed counter 0 -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index d80bbd5e0859..396d1aa81732 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -187,6 +187,10 @@ bool __read_mostly enable_pmu = true; - EXPORT_SYMBOL_GPL(enable_pmu); - module_param(enable_pmu, bool, 0444); - -+/* Enable/disabled mediated PMU virtualization. */ -+bool __read_mostly enable_mediated_pmu; -+EXPORT_SYMBOL_GPL(enable_mediated_pmu); -+ - bool __read_mostly eager_page_split = true; - module_param(eager_page_split, bool, 0644); - -@@ -6542,7 +6546,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, - break; - - mutex_lock(&kvm->lock); -- if (!kvm->created_vcpus) { -+ if (!kvm->created_vcpus && !kvm->arch.created_mediated_pmu) { - kvm->arch.enable_pmu = !(cap->args[0] & KVM_PMU_CAP_DISABLE); - r = 0; - } -@@ -12174,8 +12178,13 @@ static int sync_regs(struct kvm_vcpu *vcpu) - return 0; - } - -+#define PERF_MEDIATED_PMU_MSG \ -+ "Failed to enable mediated vPMU, try disabling system wide perf events and nmi_watchdog.\n" -+ - int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) - { -+ int r; -+ - if (kvm_check_tsc_unstable() && kvm->created_vcpus) - pr_warn_once("SMP vm created on host with unstable TSC; " - "guest TSC will not be reliable\n"); -@@ -12186,7 +12195,29 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) - if (id >= kvm->arch.max_vcpu_ids) - return -EINVAL; - -- return kvm_x86_call(vcpu_precreate)(kvm); -+ /* -+ * Note, any actions done by .vcpu_create() must be idempotent with -+ * respect to creating multiple vCPUs, and therefore are not undone if -+ * creating a vCPU fails (including failure during pre-create). -+ */ -+ r = kvm_x86_call(vcpu_precreate)(kvm); -+ if (r) -+ return r; -+ -+ if (enable_mediated_pmu && kvm->arch.enable_pmu && -+ !kvm->arch.created_mediated_pmu) { -+ if (irqchip_in_kernel(kvm)) { -+ r = perf_create_mediated_pmu(); -+ if (r) { -+ pr_warn_ratelimited(PERF_MEDIATED_PMU_MSG); -+ return r; -+ } -+ kvm->arch.created_mediated_pmu = true; -+ } else { -+ kvm->arch.enable_pmu = false; -+ } -+ } -+ return 0; - } - - int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) -@@ -12818,6 +12849,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm) - __x86_set_memory_region(kvm, TSS_PRIVATE_MEMSLOT, 0, 0); - mutex_unlock(&kvm->slots_lock); - } -+ if (kvm->arch.created_mediated_pmu) -+ perf_release_mediated_pmu(); - kvm_destroy_vcpus(kvm); - kvm_free_msr_filter(srcu_dereference_check(kvm->arch.msr_filter, &kvm->srcu, 1)); - #ifdef CONFIG_KVM_IOAPIC -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 46220b04cdf2..bd1149768acc 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -445,6 +445,7 @@ extern struct kvm_caps kvm_caps; - extern struct kvm_host_values kvm_host; - - extern bool enable_pmu; -+extern bool enable_mediated_pmu; - - /* - * Get a filtered version of KVM's supported XCR0 that strips out dynamic --- -2.43.0 - diff --git a/SPECS/kernel-rt/0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf b/SPECS/kernel-rt/0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf deleted file mode 100644 index 650679db7..000000000 --- a/SPECS/kernel-rt/0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf +++ /dev/null @@ -1,91 +0,0 @@ -From dac0c96ac12dabd83741644c885efb613af2eda5 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:54 +0000 -Subject: [PATCH 074/100] KVM: x86/pmu: Implement Intel mediated PMU - requirements and constraints - -Implement Intel PMU requirements and constraints for mediated PMU support. -Require host PMU version 4+ so that PERF_GLOBAL_STATUS_SET can be used to -precisely load the guest's status value into hardware, and require full- -width writes so that KVM can precisely load guest counter values. - -Disable PEBS and LBRs if mediated PMU support is enabled, as they won't be -supported in the initial implementation. - -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: split to separate patch, add full-width writes dependency] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/capabilities.h | 3 ++- - arch/x86/kvm/vmx/pmu_intel.c | 17 +++++++++++++++++ - arch/x86/kvm/vmx/vmx.c | 3 ++- - 3 files changed, 21 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 5316c27f6099..854e54c352f8 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -389,7 +389,8 @@ static inline bool vmx_pt_mode_is_host_guest(void) - - static inline bool vmx_pebs_supported(void) - { -- return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept; -+ return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept && -+ !enable_mediated_pmu; - } - - static inline bool cpu_has_notify_vmexit(void) -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index b2a2c4ebf448..c264a60791a5 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -776,6 +776,20 @@ void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu) - } - } - -+static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pmu) -+{ -+ u64 host_perf_cap = 0; -+ -+ if (boot_cpu_has(X86_FEATURE_PDCM)) -+ rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap); -+ -+ /* -+ * Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width -+ * writes so that KVM can precisely load guest counter values. -+ */ -+ return host_pmu->version >= 4 && host_perf_cap & PMU_CAP_FW_WRITES; -+} -+ - struct kvm_pmu_ops intel_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = intel_msr_idx_to_pmc, -@@ -787,6 +801,9 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = { - .reset = intel_pmu_reset, - .deliver_pmi = intel_pmu_deliver_pmi, - .cleanup = intel_pmu_cleanup, -+ -+ .is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported, -+ - .EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = 1, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index ed10013dac95..8c6343494e62 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7795,7 +7795,8 @@ static __init u64 vmx_get_perf_capabilities(void) - if (boot_cpu_has(X86_FEATURE_PDCM)) - rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap); - -- if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR)) { -+ if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) && -+ !enable_mediated_pmu) { - x86_perf_get_lbr(&vmx_lbr_caps); - - /* --- -2.43.0 - diff --git a/SPECS/kernel-rt/0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf b/SPECS/kernel-rt/0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf deleted file mode 100644 index 969ebbab3..000000000 --- a/SPECS/kernel-rt/0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf +++ /dev/null @@ -1,46 +0,0 @@ -From b6dad8e5f2fe290dfc3c85b62b78d8b95e06fdeb Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 14 May 2025 10:59:09 -0700 -Subject: [PATCH 075/100] KVM: x86/pmu: Implement AMD mediated PMU requirements - -Require host PMU version 2+ for AMD mediated PMU support, as -PERF_GLOBAL_CTRL and friends are hard requirements for the mediated PMU. - -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: extract to separate patch, write changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 7b8577f3c57a..96be2c3e0d65 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -227,6 +227,11 @@ static void amd_pmu_init(struct kvm_vcpu *vcpu) - } - } - -+static bool amd_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pmu) -+{ -+ return host_pmu->version >= 2; -+} -+ - struct kvm_pmu_ops amd_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = amd_msr_idx_to_pmc, -@@ -236,6 +241,9 @@ struct kvm_pmu_ops amd_pmu_ops __initdata = { - .set_msr = amd_pmu_set_msr, - .refresh = amd_pmu_refresh, - .init = amd_pmu_init, -+ -+ .is_mediated_pmu_supported = amd_pmu_is_mediated_pmu_supported, -+ - .EVENTSEL_EVENT = AMD64_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_AMD_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = AMD64_NUM_COUNTERS, --- -2.43.0 - diff --git a/SPECS/kernel-rt/0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf b/SPECS/kernel-rt/0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf deleted file mode 100644 index 01a3a3ab3..000000000 --- a/SPECS/kernel-rt/0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf +++ /dev/null @@ -1,78 +0,0 @@ -From 6d5e6fc135a4215da8c17be8b0bc71afed54e07a Mon Sep 17 00:00:00 2001 -From: Xiong Zhang -Date: Tue, 13 May 2025 17:47:23 -0700 -Subject: [PATCH 076/100] KVM: x86/pmu: Register PMI handler for mediated vPMU - -Register a dedicated PMI handler with perf's callback when mediated PMU -support is enabled. Perf routes PMIs that arrive while guest context is -loaded to the provided callback, by modifying the CPU's LVTPC to point at -a dedicated mediated PMI IRQ vector. - -WARN upon receipt of a mediated PMI if there is no active vCPU, or if the -vCPU doesn't have a mediated PMU. Even if a PMI manages to skid past -VM-Exit, it should never be delayed all the way beyond unloading the vCPU. -And while running vCPUs without a mediated PMU, the LVTPC should never be -wired up to the mediated PMI IRQ vector, i.e. should always be routed -through perf's NMI handler. - -Signed-off-by: Xiong Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 10 ++++++++++ - arch/x86/kvm/pmu.h | 2 ++ - arch/x86/kvm/x86.c | 3 ++- - 3 files changed, 14 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 4d4bb9b17412..680523e9d11e 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -155,6 +155,16 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - perf_get_hw_event_config(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); - } - -+void kvm_handle_guest_mediated_pmi(void) -+{ -+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu(); -+ -+ if (WARN_ON_ONCE(!vcpu || !kvm_vcpu_has_mediated_pmu(vcpu))) -+ return; -+ -+ kvm_make_request(KVM_REQ_PMI, vcpu); -+} -+ - static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index a5c7c026b919..9849c2bb720d 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -46,6 +46,8 @@ struct kvm_pmu_ops { - - void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops); - -+void kvm_handle_guest_mediated_pmi(void); -+ - static inline bool kvm_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) - { - /* -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 396d1aa81732..2c34dd3f0222 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9693,7 +9693,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - set_hv_tscchange_cb(kvm_hyperv_tsc_notifier); - #endif - -- __kvm_register_perf_callbacks(ops->handle_intel_pt_intr, NULL); -+ __kvm_register_perf_callbacks(ops->handle_intel_pt_intr, -+ enable_mediated_pmu ? kvm_handle_guest_mediated_pmi : NULL); - - if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && tdp_mmu_enabled) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SW_PROTECTED_VM); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf b/SPECS/kernel-rt/0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf deleted file mode 100644 index 0b01b39a6..000000000 --- a/SPECS/kernel-rt/0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf +++ /dev/null @@ -1,56 +0,0 @@ -From cb3aa7500be47c492e240ffbc193f252ff7d8752 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:56 +0000 -Subject: [PATCH 077/100] KVM: x86: Rename vmx_vmentry/vmexit_ctrl() helpers - -Rename the two helpers vmx_vmentry/vmexit_ctrl() to -vmx_get_initial_vmentry/vmexit_ctrl() to represent their real meaning. - -No functional change intended. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 8c6343494e62..7b0b51809f0e 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4304,7 +4304,7 @@ static u32 vmx_pin_based_exec_ctrl(struct vcpu_vmx *vmx) - return pin_based_exec_ctrl; - } - --static u32 vmx_vmentry_ctrl(void) -+static u32 vmx_get_initial_vmentry_ctrl(void) - { - u32 vmentry_ctrl = vmcs_config.vmentry_ctrl; - -@@ -4321,7 +4321,7 @@ static u32 vmx_vmentry_ctrl(void) - return vmentry_ctrl; - } - --static u32 vmx_vmexit_ctrl(void) -+static u32 vmx_get_initial_vmexit_ctrl(void) - { - u32 vmexit_ctrl = vmcs_config.vmexit_ctrl; - -@@ -4686,10 +4686,10 @@ static void init_vmcs(struct vcpu_vmx *vmx) - if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT) - vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat); - -- vm_exit_controls_set(vmx, vmx_vmexit_ctrl()); -+ vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl()); - - /* 22.2.1, 20.8.1 */ -- vm_entry_controls_set(vmx, vmx_vmentry_ctrl()); -+ vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl()); - - vmx->vcpu.arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits(); - vmcs_writel(CR0_GUEST_HOST_MASK, ~vmx->vcpu.arch.cr0_guest_owned_bits); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf b/SPECS/kernel-rt/0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf deleted file mode 100644 index 3ce8456b6..000000000 --- a/SPECS/kernel-rt/0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf +++ /dev/null @@ -1,130 +0,0 @@ -From 950f2db5d51aa3fc7071b92d0d9107ab762b4df1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:58 +0000 -Subject: [PATCH 078/100] KVM: x86/pmu: Move PMU_CAP_{FW_WRITES,LBR_FMT} into - msr-index.h header - -Move PMU_CAP_{FW_WRITES,LBR_FMT} into msr-index.h and rename them with -PERF_CAP prefix to keep consistent with other perf capabilities macros. - -No functional change intended. - -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/msr-index.h | 3 +++ - arch/x86/kvm/vmx/capabilities.h | 3 --- - arch/x86/kvm/vmx/pmu_intel.c | 6 +++--- - arch/x86/kvm/vmx/vmx.c | 12 ++++++------ - 4 files changed, 12 insertions(+), 12 deletions(-) - -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 84a1cbe6ab30..1570487e7213 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -315,9 +315,12 @@ - #define PERF_CAP_PT_IDX 16 - - #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 -+ -+#define PERF_CAP_LBR_FMT 0x3f - #define PERF_CAP_PEBS_TRAP BIT_ULL(6) - #define PERF_CAP_ARCH_REG BIT_ULL(7) - #define PERF_CAP_PEBS_FORMAT 0xf00 -+#define PERF_CAP_FW_WRITES BIT_ULL(13) - #define PERF_CAP_PEBS_BASELINE BIT_ULL(14) - #define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17) - #define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 854e54c352f8..26ff606ff139 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -20,9 +20,6 @@ extern int __read_mostly pt_mode; - #define PT_MODE_SYSTEM 0 - #define PT_MODE_HOST_GUEST 1 - --#define PMU_CAP_FW_WRITES (1ULL << 13) --#define PMU_CAP_LBR_FMT 0x3f -- - struct nested_vmx_msrs { - /* - * We only store the "true" versions of the VMX capability MSRs. We -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index c264a60791a5..5a9841d75117 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -138,7 +138,7 @@ static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu) - - static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu) - { -- return (vcpu_get_perf_capabilities(vcpu) & PMU_CAP_FW_WRITES) != 0; -+ return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0; - } - - static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr) -@@ -588,7 +588,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - - perf_capabilities = vcpu_get_perf_capabilities(vcpu); - if (intel_pmu_lbr_is_compatible(vcpu) && -- (perf_capabilities & PMU_CAP_LBR_FMT)) -+ (perf_capabilities & PERF_CAP_LBR_FMT)) - memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps)); - else - lbr_desc->records.nr = 0; -@@ -787,7 +787,7 @@ static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_ - * Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width - * writes so that KVM can precisely load guest counter values. - */ -- return host_pmu->version >= 4 && host_perf_cap & PMU_CAP_FW_WRITES; -+ return host_pmu->version >= 4 && host_perf_cap & PERF_CAP_FW_WRITES; - } - - struct kvm_pmu_ops intel_pmu_ops __initdata = { -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 7b0b51809f0e..93b87f9e6dfd 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2127,7 +2127,7 @@ u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated) - (host_initiated || guest_cpu_cap_has(vcpu, X86_FEATURE_BUS_LOCK_DETECT))) - debugctl |= DEBUGCTLMSR_BUS_LOCK_DETECT; - -- if ((kvm_caps.supported_perf_cap & PMU_CAP_LBR_FMT) && -+ if ((kvm_caps.supported_perf_cap & PERF_CAP_LBR_FMT) && - (host_initiated || intel_pmu_lbr_is_enabled(vcpu))) - debugctl |= DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI; - -@@ -2412,9 +2412,9 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - vmx->pt_desc.guest.addr_a[index / 2] = data; - break; - case MSR_IA32_PERF_CAPABILITIES: -- if (data & PMU_CAP_LBR_FMT) { -- if ((data & PMU_CAP_LBR_FMT) != -- (kvm_caps.supported_perf_cap & PMU_CAP_LBR_FMT)) -+ if (data & PERF_CAP_LBR_FMT) { -+ if ((data & PERF_CAP_LBR_FMT) != -+ (kvm_caps.supported_perf_cap & PERF_CAP_LBR_FMT)) - return 1; - if (!cpuid_model_is_consistent(vcpu)) - return 1; -@@ -7786,7 +7786,7 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - - static __init u64 vmx_get_perf_capabilities(void) - { -- u64 perf_cap = PMU_CAP_FW_WRITES; -+ u64 perf_cap = PERF_CAP_FW_WRITES; - u64 host_perf_cap = 0; - - if (!enable_pmu) -@@ -7807,7 +7807,7 @@ static __init u64 vmx_get_perf_capabilities(void) - if (!vmx_lbr_caps.has_callstack) - memset(&vmx_lbr_caps, 0, sizeof(vmx_lbr_caps)); - else if (vmx_lbr_caps.nr) -- perf_cap |= host_perf_cap & PMU_CAP_LBR_FMT; -+ perf_cap |= host_perf_cap & PERF_CAP_LBR_FMT; - } - - if (vmx_pebs_supported()) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf b/SPECS/kernel-rt/0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf deleted file mode 100644 index 749209671..000000000 --- a/SPECS/kernel-rt/0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf +++ /dev/null @@ -1,229 +0,0 @@ -From b40fc5e290947f2498a4ed3640e7c41aa843545b Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 8 Jul 2025 15:57:44 -0700 -Subject: [PATCH 079/100] KVM: x86: Rework KVM_REQ_MSR_FILTER_CHANGED into a - generic RECALC_INTERCEPTS - -Rework the MSR_FILTER_CHANGED request into a more generic RECALC_INTERCEPTS -request, and expand the responsibilities of vendor code to recalculate all -intercepts that vary based on userspace input, e.g. instruction intercepts -that are tied to guest CPUID. - -Providing a generic recalc request will allow the upcoming mediated PMU -support to trigger a recalc when PMU features, e.g. PERF_CAPABILITIES, are -set by userspace, without having to make multiple calls to/from PMU code. -As a bonus, using a request will effectively coalesce recalcs, e.g. will -reduce the number of recalcs for normal usage from 3+ to 1 (vCPU create, -set CPUID, set PERF_CAPABILITIES (Intel only), set filter). - -The downside is that MSR filter changes that are done in isolation will do -a small amount of unnecessary work, but that's already a relatively slow -path, and the cost of recalculating instruction intercepts is negligible. - -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm-x86-ops.h | 2 +- - arch/x86/include/asm/kvm_host.h | 4 ++-- - arch/x86/kvm/svm/svm.c | 8 ++++---- - arch/x86/kvm/vmx/main.c | 14 +++++++------- - arch/x86/kvm/vmx/vmx.c | 9 +++++++-- - arch/x86/kvm/vmx/x86_ops.h | 2 +- - arch/x86/kvm/x86.c | 15 +++++++-------- - 7 files changed, 29 insertions(+), 25 deletions(-) - -diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h -index 18a5c3119e1a..7c240e23bd52 100644 ---- a/arch/x86/include/asm/kvm-x86-ops.h -+++ b/arch/x86/include/asm/kvm-x86-ops.h -@@ -138,7 +138,7 @@ KVM_X86_OP(check_emulate_instruction) - KVM_X86_OP(apic_init_signal_blocked) - KVM_X86_OP_OPTIONAL(enable_l2_tlb_flush) - KVM_X86_OP_OPTIONAL(migrate_timers) --KVM_X86_OP(recalc_msr_intercepts) -+KVM_X86_OP(recalc_intercepts) - KVM_X86_OP(complete_emulated_msr) - KVM_X86_OP(vcpu_deliver_sipi_vector) - KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons); -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 83682be4c088..ecd0634599ff 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -120,7 +120,7 @@ - #define KVM_REQ_TLB_FLUSH_GUEST \ - KVM_ARCH_REQ_FLAGS(27, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) - #define KVM_REQ_APF_READY KVM_ARCH_REQ(28) --#define KVM_REQ_MSR_FILTER_CHANGED KVM_ARCH_REQ(29) -+#define KVM_REQ_RECALC_INTERCEPTS KVM_ARCH_REQ(29) - #define KVM_REQ_UPDATE_CPU_DIRTY_LOGGING \ - KVM_ARCH_REQ_FLAGS(30, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) - #define KVM_REQ_MMU_FREE_OBSOLETE_ROOTS \ -@@ -1912,7 +1912,7 @@ struct kvm_x86_ops { - int (*enable_l2_tlb_flush)(struct kvm_vcpu *vcpu); - - void (*migrate_timers)(struct kvm_vcpu *vcpu); -- void (*recalc_msr_intercepts)(struct kvm_vcpu *vcpu); -+ void (*recalc_intercepts)(struct kvm_vcpu *vcpu); - int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); - - void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index f7e1e665a826..3d9dcc66a407 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -1077,7 +1077,7 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) - } - } - --static void svm_recalc_intercepts_after_set_cpuid(struct kvm_vcpu *vcpu) -+static void svm_recalc_intercepts(struct kvm_vcpu *vcpu) - { - svm_recalc_instruction_intercepts(vcpu); - svm_recalc_msr_intercepts(vcpu); -@@ -1225,7 +1225,7 @@ static void init_vmcb(struct kvm_vcpu *vcpu) - - svm_hv_init_vmcb(vmcb); - -- svm_recalc_intercepts_after_set_cpuid(vcpu); -+ svm_recalc_intercepts(vcpu); - - vmcb_mark_all_dirty(vmcb); - -@@ -4479,7 +4479,7 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - if (sev_guest(vcpu->kvm)) - sev_vcpu_after_set_cpuid(svm); - -- svm_recalc_intercepts_after_set_cpuid(vcpu); -+ svm_recalc_intercepts(vcpu); - } - - static bool svm_has_wbinvd_exit(void) -@@ -5181,7 +5181,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { - - .apic_init_signal_blocked = svm_apic_init_signal_blocked, - -- .recalc_msr_intercepts = svm_recalc_msr_intercepts, -+ .recalc_intercepts = svm_recalc_intercepts, - .complete_emulated_msr = svm_complete_emulated_msr, - - .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, -diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c -index dbab1c15b0cd..68dcafd177a8 100644 ---- a/arch/x86/kvm/vmx/main.c -+++ b/arch/x86/kvm/vmx/main.c -@@ -188,18 +188,18 @@ static int vt_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - return vmx_get_msr(vcpu, msr_info); - } - --static void vt_recalc_msr_intercepts(struct kvm_vcpu *vcpu) -+static void vt_recalc_intercepts(struct kvm_vcpu *vcpu) - { - /* -- * TDX doesn't allow VMM to configure interception of MSR accesses. -- * TDX guest requests MSR accesses by calling TDVMCALL. The MSR -- * filters will be applied when handling the TDVMCALL for RDMSR/WRMSR -- * if the userspace has set any. -+ * TDX doesn't allow VMM to configure interception of instructions or -+ * MSR accesses. TDX guest requests MSR accesses by calling TDVMCALL. -+ * The MSR filters will be applied when handling the TDVMCALL for -+ * RDMSR/WRMSR if the userspace has set any. - */ - if (is_td_vcpu(vcpu)) - return; - -- vmx_recalc_msr_intercepts(vcpu); -+ vmx_recalc_intercepts(vcpu); - } - - static int vt_complete_emulated_msr(struct kvm_vcpu *vcpu, int err) -@@ -995,7 +995,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { - .apic_init_signal_blocked = vt_op(apic_init_signal_blocked), - .migrate_timers = vmx_migrate_timers, - -- .recalc_msr_intercepts = vt_op(recalc_msr_intercepts), -+ .recalc_intercepts = vt_op(recalc_intercepts), - .complete_emulated_msr = vt_op(complete_emulated_msr), - - .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 93b87f9e6dfd..2244ca074e9d 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4068,7 +4068,7 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) - } - } - --void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) -+static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - if (!cpu_has_vmx_msr_bitmap()) - return; -@@ -4121,6 +4121,11 @@ void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - */ - } - -+void vmx_recalc_intercepts(struct kvm_vcpu *vcpu) -+{ -+ vmx_recalc_msr_intercepts(vcpu); -+} -+ - static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu, - int vector) - { -@@ -7778,7 +7783,7 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - ~FEAT_CTL_SGX_LC_ENABLED; - - /* Recalc MSR interception to account for feature changes. */ -- vmx_recalc_msr_intercepts(vcpu); -+ vmx_recalc_intercepts(vcpu); - - /* Refresh #PF interception to account for MAXPHYADDR changes. */ - vmx_update_exception_bitmap(vcpu); -diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h -index 2b3424f638db..2c590ff44ced 100644 ---- a/arch/x86/kvm/vmx/x86_ops.h -+++ b/arch/x86/kvm/vmx/x86_ops.h -@@ -52,7 +52,7 @@ void vmx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, - int trig_mode, int vector); - void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu); - bool vmx_has_emulated_msr(struct kvm *kvm, u32 index); --void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu); -+void vmx_recalc_intercepts(struct kvm_vcpu *vcpu); - void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu); - void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu); - int vmx_get_feature_msr(u32 msr, u64 *data); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 2c34dd3f0222..69f5d9deb75f 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -6742,7 +6742,11 @@ static int kvm_vm_ioctl_set_msr_filter(struct kvm *kvm, - - kvm_free_msr_filter(old_filter); - -- kvm_make_all_cpus_request(kvm, KVM_REQ_MSR_FILTER_CHANGED); -+ /* -+ * Recalc MSR intercepts as userspace may want to intercept accesses to -+ * MSRs that KVM would otherwise pass through to the guest. -+ */ -+ kvm_make_all_cpus_request(kvm, KVM_REQ_RECALC_INTERCEPTS); - - return 0; - } -@@ -10765,13 +10769,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) - if (kvm_check_request(KVM_REQ_APF_READY, vcpu)) - kvm_check_async_pf_completion(vcpu); - -- /* -- * Recalc MSR intercepts as userspace may want to intercept -- * accesses to MSRs that KVM would otherwise pass through to -- * the guest. -- */ -- if (kvm_check_request(KVM_REQ_MSR_FILTER_CHANGED, vcpu)) -- kvm_x86_call(recalc_msr_intercepts)(vcpu); -+ if (kvm_check_request(KVM_REQ_RECALC_INTERCEPTS, vcpu)) -+ kvm_x86_call(recalc_intercepts)(vcpu); - - if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu)) - kvm_x86_call(update_cpu_dirty_logging)(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf b/SPECS/kernel-rt/0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf deleted file mode 100644 index 63e6584ec..000000000 --- a/SPECS/kernel-rt/0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf +++ /dev/null @@ -1,71 +0,0 @@ -From 940fa63d1e69037d664f6317434d6cce6cb2d7af Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 8 Jul 2025 16:00:22 -0700 -Subject: [PATCH 080/100] KVM: x86: Use KVM_REQ_RECALC_INTERCEPTS to react to - CPUID updates - -Defer recalculating MSR and instruction intercepts after a CPUID update -via RECALC_INTERCEPTS to converge on RECALC_INTERCEPTS as the "official" -mechanism for triggering recalcs. As a bonus, because KVM does a "recalc" -during vCPU creation, and every functional VMM sets CPUID at least once, -for all intents and purposes this saves at least one recalc. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/cpuid.c | 2 ++ - arch/x86/kvm/svm/svm.c | 4 +--- - arch/x86/kvm/vmx/vmx.c | 3 --- - 3 files changed, 3 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index e2836a255b16..cc16e28bfab2 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -448,6 +448,8 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - * adjustments to the reserved GPA bits. - */ - kvm_mmu_after_set_cpuid(vcpu); -+ -+ kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); - } - - int cpuid_query_maxphyaddr(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 3d9dcc66a407..ef7dffc54dca 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -1225,7 +1225,7 @@ static void init_vmcb(struct kvm_vcpu *vcpu) - - svm_hv_init_vmcb(vmcb); - -- svm_recalc_intercepts(vcpu); -+ kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); - - vmcb_mark_all_dirty(vmcb); - -@@ -4478,8 +4478,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - - if (sev_guest(vcpu->kvm)) - sev_vcpu_after_set_cpuid(svm); -- -- svm_recalc_intercepts(vcpu); - } - - static bool svm_has_wbinvd_exit(void) -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 2244ca074e9d..6094de4855d6 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7782,9 +7782,6 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - vmx->msr_ia32_feature_control_valid_bits &= - ~FEAT_CTL_SGX_LC_ENABLED; - -- /* Recalc MSR interception to account for feature changes. */ -- vmx_recalc_intercepts(vcpu); -- - /* Refresh #PF interception to account for MAXPHYADDR changes. */ - vmx_update_exception_bitmap(vcpu); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf b/SPECS/kernel-rt/0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf deleted file mode 100644 index a5e14d09c..000000000 --- a/SPECS/kernel-rt/0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf +++ /dev/null @@ -1,75 +0,0 @@ -From b698b7cf5519ae476e4597381f0a9f75cb404162 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:59 +0000 -Subject: [PATCH 081/100] KVM: VMX: Add helpers to toggle/change a bit in VMCS - execution controls - -Expand the VMCS controls builder macros to generate helpers to change a -bit to the desired value, and use the new helpers when toggling APICv -related controls. - -No functional change intended. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -[sean: rewrite changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 20 +++++++------------- - arch/x86/kvm/vmx/vmx.h | 8 ++++++++ - 2 files changed, 15 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 6094de4855d6..baea4a9cf74f 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4356,19 +4356,13 @@ void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) - - pin_controls_set(vmx, vmx_pin_based_exec_ctrl(vmx)); - -- if (kvm_vcpu_apicv_active(vcpu)) { -- secondary_exec_controls_setbit(vmx, -- SECONDARY_EXEC_APIC_REGISTER_VIRT | -- SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); -- if (enable_ipiv) -- tertiary_exec_controls_setbit(vmx, TERTIARY_EXEC_IPI_VIRT); -- } else { -- secondary_exec_controls_clearbit(vmx, -- SECONDARY_EXEC_APIC_REGISTER_VIRT | -- SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); -- if (enable_ipiv) -- tertiary_exec_controls_clearbit(vmx, TERTIARY_EXEC_IPI_VIRT); -- } -+ secondary_exec_controls_changebit(vmx, -+ SECONDARY_EXEC_APIC_REGISTER_VIRT | -+ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY, -+ kvm_vcpu_apicv_active(vcpu)); -+ if (enable_ipiv) -+ tertiary_exec_controls_changebit(vmx, TERTIARY_EXEC_IPI_VIRT, -+ kvm_vcpu_apicv_active(vcpu)); - - vmx_update_msr_bitmap_x2apic(vcpu); - } -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index d3389baf3ab3..a4e5bcd1d023 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -608,6 +608,14 @@ static __always_inline void lname##_controls_clearbit(struct vcpu_vmx *vmx, u##b - { \ - BUILD_BUG_ON(!(val & (KVM_REQUIRED_VMX_##uname | KVM_OPTIONAL_VMX_##uname))); \ - lname##_controls_set(vmx, lname##_controls_get(vmx) & ~val); \ -+} \ -+static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u##bits val, \ -+ bool set) \ -+{ \ -+ if (set) \ -+ lname##_controls_setbit(vmx, val); \ -+ else \ -+ lname##_controls_clearbit(vmx, val); \ - } - BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) - BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf b/SPECS/kernel-rt/0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf deleted file mode 100644 index 9657d75b6..000000000 --- a/SPECS/kernel-rt/0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf +++ /dev/null @@ -1,130 +0,0 @@ -From 0930af415779b92c44a0dc2a9647cf1ba8a83852 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:00 +0000 -Subject: [PATCH 082/100] KVM: x86/pmu: Disable RDPMC interception for - compatible mediated vPMU - -Disable RDPMC interception for vCPUs with a mediated vPMU that is -compatible with the host PMU, i.e. that doesn't require KVM emulation of -RDPMC to honor the guest's vCPU model. With a mediated vPMU, all guest -state accessible via RDPMC is loaded into hardware while the guest is -running. - -Adust RDPMC interception only for non-TDX guests, as the TDX module is -responsible for managing RDPMC intercepts based on the TD configuration. - -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sandipan Das -Signed-off-by: Sandipan Das -Signed-off-by: Dapeng Mi -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 26 ++++++++++++++++++++++++++ - arch/x86/kvm/pmu.h | 1 + - arch/x86/kvm/svm/svm.c | 5 +++++ - arch/x86/kvm/vmx/vmx.c | 7 +++++++ - arch/x86/kvm/x86.c | 1 + - 5 files changed, 40 insertions(+) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 680523e9d11e..674f42d083a9 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -712,6 +712,32 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data) - return 0; - } - -+bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu)) -+ return true; -+ -+ /* -+ * VMware allows access to these Pseduo-PMCs even when read via RDPMC -+ * in Ring3 when CR4.PCE=0. -+ */ -+ if (enable_vmware_backdoor) -+ return true; -+ -+ /* -+ * Note! Check *host* PMU capabilities, not KVM's PMU capabilities, as -+ * KVM's capabilities are constrained based on KVM support, i.e. KVM's -+ * capabilities themselves may be a subset of hardware capabilities. -+ */ -+ return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp || -+ pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed || -+ pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) || -+ pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1); -+} -+EXPORT_SYMBOL_GPL(kvm_need_rdpmc_intercept); -+ - void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu) - { - if (lapic_in_kernel(vcpu)) { -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 9849c2bb720d..506c203587ea 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -238,6 +238,7 @@ void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu); - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); -+bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu); - - extern struct kvm_pmu_ops intel_pmu_ops; - extern struct kvm_pmu_ops amd_pmu_ops; -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index ef7dffc54dca..2d42962b47aa 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -1075,6 +1075,11 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) - svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; - } - } -+ -+ if (kvm_need_rdpmc_intercept(vcpu)) -+ svm_set_intercept(svm, INTERCEPT_RDPMC); -+ else -+ svm_clr_intercept(svm, INTERCEPT_RDPMC); - } - - static void svm_recalc_intercepts(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index baea4a9cf74f..2f7db32710e3 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4121,8 +4121,15 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - */ - } - -+static void vmx_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) -+{ -+ exec_controls_changebit(to_vmx(vcpu), CPU_BASED_RDPMC_EXITING, -+ kvm_need_rdpmc_intercept(vcpu)); -+} -+ - void vmx_recalc_intercepts(struct kvm_vcpu *vcpu) - { -+ vmx_recalc_instruction_intercepts(vcpu); - vmx_recalc_msr_intercepts(vcpu); - } - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 69f5d9deb75f..b8014435c988 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -3793,6 +3793,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - - vcpu->arch.perf_capabilities = data; - kvm_pmu_refresh(vcpu); -+ kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); - break; - case MSR_IA32_PRED_CMD: { - u64 reserved_bits = ~(PRED_CMD_IBPB | PRED_CMD_SBPB); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf b/SPECS/kernel-rt/0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf deleted file mode 100644 index a5b3371cc..000000000 --- a/SPECS/kernel-rt/0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf +++ /dev/null @@ -1,276 +0,0 @@ -From 64c78cd9fec12e5974afe503733aff6edb1e6b02 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:01 +0000 -Subject: [PATCH 083/100] KVM: x86/pmu: Load/save GLOBAL_CTRL via entry/exit - fields for mediated PMU - -When running a guest with a mediated PMU, context switch PERF_GLOBAL_CTRL -via the dedicated VMCS fields for both host and guest. For the host, -always zero GLOBAL_CTRL on exit as the guest's state will still be loaded -in hardware (KVM will context switch the bulk of PMU state outside of the -inner run loop). For the guest, use the dedicated fields to atomically -load and save PERF_GLOBAL_CTRL on all entry/exits. - -Note, VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL was introduced by Sapphire -Rapids, and is expected to be supported on all CPUs with PMU v4+. WARN if -that expectation is not met. Alternatively, KVM could manually save -PERF_GLOBAL_CTRL via the MSR save list, but the associated complexity and -runtime overhead is unjustified given that the feature should always be -available on relevant CPUs. - -To minimize VM-Entry latency, propagate IA32_PERF_GLOBAL_CTRL to the VMCS -on-demand. But to minimize complexity, read IA32_PERF_GLOBAL_CTRL out of -the VMCS on all non-failing VM-Exits. I.e. partially cache the MSR. -KVM could track GLOBAL_CTRL as an EXREG and defer all reads, but writes -are rare, i.e. the dirty tracking for an EXREG is unnecessary, and it's -not obvious that shaving ~15-20 cycles per exit is meaningful given the -total overhead associated with mediated PMU context switches. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm-x86-pmu-ops.h | 2 ++ - arch/x86/include/asm/vmx.h | 1 + - arch/x86/kvm/pmu.c | 13 +++++++++-- - arch/x86/kvm/pmu.h | 3 ++- - arch/x86/kvm/vmx/capabilities.h | 5 +++++ - arch/x86/kvm/vmx/pmu_intel.c | 19 +++++++++++++++- - arch/x86/kvm/vmx/vmx.c | 31 +++++++++++++++++++++++++- - arch/x86/kvm/vmx/vmx.h | 3 ++- - 8 files changed, 71 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h -index 9159bf1a4730..ad2cc82abf79 100644 ---- a/arch/x86/include/asm/kvm-x86-pmu-ops.h -+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h -@@ -23,5 +23,7 @@ KVM_X86_PMU_OP_OPTIONAL(reset) - KVM_X86_PMU_OP_OPTIONAL(deliver_pmi) - KVM_X86_PMU_OP_OPTIONAL(cleanup) - -+KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl) -+ - #undef KVM_X86_PMU_OP - #undef KVM_X86_PMU_OP_OPTIONAL -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index cca7d6641287..af71666c3a37 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -106,6 +106,7 @@ - #define VM_EXIT_CLEAR_BNDCFGS 0x00800000 - #define VM_EXIT_PT_CONCEAL_PIP 0x01000000 - #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 -+#define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 - - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 674f42d083a9..a4fe0e76df79 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -103,7 +103,7 @@ void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) - #undef __KVM_X86_PMU_OP - } - --void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) -+void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops) - { - bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; - int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; -@@ -137,6 +137,9 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - !pmu_ops->is_mediated_pmu_supported(&kvm_host_pmu)) - enable_mediated_pmu = false; - -+ if (!enable_mediated_pmu) -+ pmu_ops->write_global_ctrl = NULL; -+ - if (!enable_pmu) { - memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); - return; -@@ -831,6 +834,9 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - diff = pmu->global_ctrl ^ data; - pmu->global_ctrl = data; - reprogram_counters(pmu, diff); -+ -+ if (kvm_vcpu_has_mediated_pmu(vcpu)) -+ kvm_pmu_call(write_global_ctrl)(data); - } - break; - case MSR_CORE_PERF_GLOBAL_OVF_CTRL: -@@ -921,8 +927,11 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu) - * in the global controls). Emulate that behavior when refreshing the - * PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL. - */ -- if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) -+ if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) { - pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0); -+ if (kvm_vcpu_has_mediated_pmu(vcpu)) -+ kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); -+ } - } - - void kvm_pmu_init(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 506c203587ea..2ff469334c1a 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -38,6 +38,7 @@ struct kvm_pmu_ops { - void (*cleanup)(struct kvm_vcpu *vcpu); - - bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); -+ void (*write_global_ctrl)(u64 global_ctrl); - - const u64 EVENTSEL_EVENT; - const int MAX_NR_GP_COUNTERS; -@@ -183,7 +184,7 @@ static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc) - - extern struct x86_pmu_capability kvm_pmu_cap; - --void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); -+void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops); - - void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc); - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 26ff606ff139..874c6dd34665 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -100,6 +100,11 @@ static inline bool cpu_has_load_perf_global_ctrl(void) - return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; - } - -+static inline bool cpu_has_save_perf_global_ctrl(void) -+{ -+ return vmcs_config.vmexit_ctrl & VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL; -+} -+ - static inline bool cpu_has_vmx_mpx(void) - { - return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS; -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 5a9841d75117..79aad6355fa3 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -787,7 +787,23 @@ static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_ - * Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width - * writes so that KVM can precisely load guest counter values. - */ -- return host_pmu->version >= 4 && host_perf_cap & PERF_CAP_FW_WRITES; -+ if (host_pmu->version < 4 || !(host_perf_cap & PERF_CAP_FW_WRITES)) -+ return false; -+ -+ /* -+ * All CPUs that support a mediated PMU are expected to support loading -+ * and saving PERF_GLOBAL_CTRL via dedicated VMCS fields. -+ */ -+ if (WARN_ON_ONCE(!cpu_has_load_perf_global_ctrl() || -+ !cpu_has_save_perf_global_ctrl())) -+ return false; -+ -+ return true; -+} -+ -+static void intel_pmu_write_global_ctrl(u64 global_ctrl) -+{ -+ vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, global_ctrl); - } - - struct kvm_pmu_ops intel_pmu_ops __initdata = { -@@ -803,6 +819,7 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = { - .cleanup = intel_pmu_cleanup, - - .is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported, -+ .write_global_ctrl = intel_pmu_write_global_ctrl, - - .EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 2f7db32710e3..1233a0afb31e 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4115,6 +4115,18 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, - !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); - -+ if (enable_mediated_pmu) { -+ bool is_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu); -+ struct vcpu_vmx *vmx = to_vmx(vcpu); -+ -+ vm_entry_controls_changebit(vmx, -+ VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -+ -+ vm_exit_controls_changebit(vmx, -+ VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -+ } -+ - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be - * filtered by userspace. -@@ -4282,6 +4294,16 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) - - if (cpu_has_load_ia32_efer()) - vmcs_write64(HOST_IA32_EFER, kvm_host.efer); -+ -+ /* -+ * When running a guest with a mediated PMU, guest state is resident in -+ * hardware after VM-Exit. Zero PERF_GLOBAL_CTRL on exit so that host -+ * activity doesn't bleed into the guest counters. When running with -+ * an emulated PMU, PERF_GLOBAL_CTRL is dynamically computed on every -+ * entry/exit to merge guest and host PMU usage. -+ */ -+ if (enable_mediated_pmu) -+ vmcs_write64(HOST_IA32_PERF_GLOBAL_CTRL, 0); - } - - void set_cr4_guest_host_mask(struct vcpu_vmx *vmx) -@@ -4349,7 +4371,8 @@ static u32 vmx_get_initial_vmexit_ctrl(void) - VM_EXIT_CLEAR_IA32_RTIT_CTL); - /* Loading of EFER and PERF_GLOBAL_CTRL are toggled dynamically */ - return vmexit_ctrl & -- ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER); -+ ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER | -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL); - } - - void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) -@@ -7087,6 +7110,9 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx) - struct perf_guest_switch_msr *msrs; - struct kvm_pmu *pmu = vcpu_to_pmu(&vmx->vcpu); - -+ if (kvm_vcpu_has_mediated_pmu(&vmx->vcpu)) -+ return; -+ - pmu->host_cross_mapped_mask = 0; - if (pmu->pebs_enable & pmu->global_ctrl) - intel_pmu_cross_mapped_check(pmu); -@@ -7407,6 +7433,9 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) - - vmx->loaded_vmcs->launched = 1; - -+ if (!msr_write_intercepted(vmx, MSR_CORE_PERF_GLOBAL_CTRL)) -+ vcpu_to_pmu(vcpu)->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL); -+ - vmx_recover_nmi_blocking(vmx); - vmx_complete_interrupts(vmx); - -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index a4e5bcd1d023..7eb57f5cb975 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -506,7 +506,8 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_LOAD_IA32_EFER | \ - VM_EXIT_CLEAR_BNDCFGS | \ - VM_EXIT_PT_CONCEAL_PIP | \ -- VM_EXIT_CLEAR_IA32_RTIT_CTL) -+ VM_EXIT_CLEAR_IA32_RTIT_CTL | \ -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ - (PIN_BASED_EXT_INTR_MASK | \ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf b/SPECS/kernel-rt/0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf deleted file mode 100644 index 58169c51d..000000000 --- a/SPECS/kernel-rt/0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf +++ /dev/null @@ -1,88 +0,0 @@ -From 009e272d43d7524424506e6939f14963bb4db36b Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Tue, 13 May 2025 13:53:29 -0700 -Subject: [PATCH 084/100] KVM: x86/pmu: Use BIT_ULL() instead of open coded - equivalents - -Replace a variety of "1ull << N" and "(u64)1 << N" snippets with BIT_ULL() -in the PMU code. - -No functional change intended. - -Signed-off-by: Dapeng Mi -[sean: split to separate patch, write changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 4 ++-- - arch/x86/kvm/vmx/pmu_intel.c | 15 ++++++--------- - 2 files changed, 8 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 96be2c3e0d65..b777c3743304 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -199,11 +199,11 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.num_counters_gp); - - if (pmu->version > 1) { -- pmu->global_ctrl_rsvd = ~((1ull << pmu->nr_arch_gp_counters) - 1); -+ pmu->global_ctrl_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1); - pmu->global_status_rsvd = pmu->global_ctrl_rsvd; - } - -- pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1; -+ pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(48) - 1; - pmu->reserved_bits = 0xfffffff000280000ull; - pmu->raw_event_mask = AMD64_RAW_EVENT_MASK; - /* not applicable to AMD; but clean them to prevent any fall out */ -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 79aad6355fa3..7592a5fb7675 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -536,11 +536,10 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.num_counters_gp); - eax.split.bit_width = min_t(int, eax.split.bit_width, - kvm_pmu_cap.bit_width_gp); -- pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1; -+ pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(eax.split.bit_width) - 1; - eax.split.mask_length = min_t(int, eax.split.mask_length, - kvm_pmu_cap.events_mask_len); -- pmu->available_event_types = ~entry->ebx & -- ((1ull << eax.split.mask_length) - 1); -+ pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1); - - if (pmu->version == 1) { - pmu->nr_arch_fixed_counters = 0; -@@ -549,16 +548,15 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.num_counters_fixed); - edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, - kvm_pmu_cap.bit_width_fixed); -- pmu->counter_bitmask[KVM_PMC_FIXED] = -- ((u64)1 << edx.split.bit_width_fixed) - 1; -+ pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1; - } - - intel_pmu_enable_fixed_counter_bits(pmu, INTEL_FIXED_0_KERNEL | - INTEL_FIXED_0_USER | - INTEL_FIXED_0_ENABLE_PMI); - -- counter_rsvd = ~(((1ull << pmu->nr_arch_gp_counters) - 1) | -- (((1ull << pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX)); -+ counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) | -+ ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX)); - pmu->global_ctrl_rsvd = counter_rsvd; - - /* -@@ -603,8 +601,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - pmu->pebs_data_cfg_rsvd = ~0xff00000full; - intel_pmu_enable_fixed_counter_bits(pmu, ICL_FIXED_0_ADAPTIVE); - } else { -- pmu->pebs_enable_rsvd = -- ~((1ull << pmu->nr_arch_gp_counters) - 1); -+ pmu->pebs_enable_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1); - } - } - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf b/SPECS/kernel-rt/0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf deleted file mode 100644 index 6f4433b7e..000000000 --- a/SPECS/kernel-rt/0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf +++ /dev/null @@ -1,65 +0,0 @@ -From 69ff2f2b422e4fb4fbc4b0e80078a5011faf0c5c Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 14:21:22 -0700 -Subject: [PATCH 085/100] KVM: x86/pmu: Move initialization of valid PMCs - bitmask to common x86 - -Move all initialization of all_valid_pmc_idx to common code, as the logic -is 100% common to Intel and AMD, and KVM heavily relies on Intel and AMD -having the same semantics. E.g. the fact that AMD doesn't support fixed -counters doesn't allow KVM to use all_valid_pmc_idx[63:32] for other -purposes. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 4 ++++ - arch/x86/kvm/svm/pmu.c | 1 - - arch/x86/kvm/vmx/pmu_intel.c | 5 ----- - 3 files changed, 4 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index a4fe0e76df79..4246e1d2cfcc 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -932,6 +932,10 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu) - if (kvm_vcpu_has_mediated_pmu(vcpu)) - kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); - } -+ -+ bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); -+ bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX, -+ pmu->nr_arch_fixed_counters); - } - - void kvm_pmu_init(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index b777c3743304..9ffd44a5d474 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -209,7 +209,6 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu) - /* not applicable to AMD; but clean them to prevent any fall out */ - pmu->counter_bitmask[KVM_PMC_FIXED] = 0; - pmu->nr_arch_fixed_counters = 0; -- bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); - } - - static void amd_pmu_init(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 7592a5fb7675..3ca8859b8091 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -579,11 +579,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED); - } - -- bitmap_set(pmu->all_valid_pmc_idx, -- 0, pmu->nr_arch_gp_counters); -- bitmap_set(pmu->all_valid_pmc_idx, -- INTEL_PMC_MAX_GENERIC, pmu->nr_arch_fixed_counters); -- - perf_capabilities = vcpu_get_perf_capabilities(vcpu); - if (intel_pmu_lbr_is_compatible(vcpu) && - (perf_capabilities & PERF_CAP_LBR_FMT)) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf b/SPECS/kernel-rt/0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf deleted file mode 100644 index 61c0c8a3b..000000000 --- a/SPECS/kernel-rt/0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf +++ /dev/null @@ -1,91 +0,0 @@ -From 267ad7a219b29998a24f05cfff06ea6b95b4808b Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 14:30:55 -0700 -Subject: [PATCH 086/100] KVM: x86/pmu: Restrict GLOBAL_{CTRL,STATUS}, fixed - PMCs, and PEBS to PMU v2+ - -Restrict support for GLOBAL_CTRL, GLOBAL_STATUS, fixed PMCs, and PEBS to -v2 or later vPMUs. The SDM explicitly states that GLOBAL_{CTRL,STATUS} and -fixed counters were introduced with PMU v2, and PEBS has hard dependencies -on fixed counters and the bitmap MSR layouts established by PMU v2. - -Reported-by: Dapeng Mi -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/pmu_intel.c | 51 ++++++++++++++++++------------------ - 1 file changed, 25 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 3ca8859b8091..04db3e0f0f2d 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -541,16 +541,33 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.events_mask_len); - pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1); - -- if (pmu->version == 1) { -- pmu->nr_arch_fixed_counters = 0; -- } else { -- pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed, -- kvm_pmu_cap.num_counters_fixed); -- edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, -- kvm_pmu_cap.bit_width_fixed); -- pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1; -+ entry = kvm_find_cpuid_entry_index(vcpu, 7, 0); -+ if (entry && -+ (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) && -+ (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) { -+ pmu->reserved_bits ^= HSW_IN_TX; -+ pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED); - } - -+ perf_capabilities = vcpu_get_perf_capabilities(vcpu); -+ if (intel_pmu_lbr_is_compatible(vcpu) && -+ (perf_capabilities & PERF_CAP_LBR_FMT)) -+ memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps)); -+ else -+ lbr_desc->records.nr = 0; -+ -+ if (lbr_desc->records.nr) -+ bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1); -+ -+ if (pmu->version == 1) -+ return; -+ -+ pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed, -+ kvm_pmu_cap.num_counters_fixed); -+ edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, -+ kvm_pmu_cap.bit_width_fixed); -+ pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1; -+ - intel_pmu_enable_fixed_counter_bits(pmu, INTEL_FIXED_0_KERNEL | - INTEL_FIXED_0_USER | - INTEL_FIXED_0_ENABLE_PMI); -@@ -571,24 +588,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - pmu->global_status_rsvd &= - ~MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI; - -- entry = kvm_find_cpuid_entry_index(vcpu, 7, 0); -- if (entry && -- (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) && -- (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) { -- pmu->reserved_bits ^= HSW_IN_TX; -- pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED); -- } -- -- perf_capabilities = vcpu_get_perf_capabilities(vcpu); -- if (intel_pmu_lbr_is_compatible(vcpu) && -- (perf_capabilities & PERF_CAP_LBR_FMT)) -- memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps)); -- else -- lbr_desc->records.nr = 0; -- -- if (lbr_desc->records.nr) -- bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1); -- - if (perf_capabilities & PERF_CAP_PEBS_FORMAT) { - if (perf_capabilities & PERF_CAP_PEBS_BASELINE) { - pmu->pebs_enable_rsvd = counter_rsvd; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf b/SPECS/kernel-rt/0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf deleted file mode 100644 index 6ee85ade1..000000000 --- a/SPECS/kernel-rt/0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf +++ /dev/null @@ -1,296 +0,0 @@ -From ec95386c5f989f70ace870007dbba0237ad17312 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:03 +0000 -Subject: [PATCH 087/100] KVM: x86/pmu: Disable interception of select PMU MSRs - for mediated vPMUs - -For vCPUs with a mediated vPMU, disable interception of counter MSRs for -PMCs that are exposed to the guest, and for GLOBAL_CTRL and related MSRs -if they are fully supported according to the vCPU model, i.e. if the MSRs -and all bits supported by hardware exist from the guest's point of view. - -Do NOT passthrough event selector or fixed counter control MSRs, so that -KVM can enforce userspace-defined event filters, e.g. to prevent use of -AnyThread events (which is unfortunately a setting in the fixed counter -control MSR). - -Defer support for nested passthrough of mediated PMU MSRs to the future, -as the logic for nested MSR interception is unfortunately vendor specific. - -Suggested-by: Sean Christopherson -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sandipan Das -Signed-off-by: Sandipan Das -Signed-off-by: Dapeng Mi -[sean: squash patches, massage changelog, refresh VMX MSRs on filter change] -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/msr-index.h | 1 + - arch/x86/kvm/pmu.c | 34 ++++++++++++------ - arch/x86/kvm/pmu.h | 1 + - arch/x86/kvm/svm/svm.c | 36 +++++++++++++++++++ - arch/x86/kvm/vmx/pmu_intel.c | 13 ------- - arch/x86/kvm/vmx/pmu_intel.h | 15 ++++++++ - arch/x86/kvm/vmx/vmx.c | 59 ++++++++++++++++++++++++++------ - 7 files changed, 124 insertions(+), 35 deletions(-) - -Index: kernel-staging/arch/x86/kvm/pmu.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/pmu.c -+++ kernel-staging/arch/x86/kvm/pmu.c -@@ -715,18 +715,14 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, - return 0; - } - --bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu) -+bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu) - { - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); - - if (!kvm_vcpu_has_mediated_pmu(vcpu)) - return true; - -- /* -- * VMware allows access to these Pseduo-PMCs even when read via RDPMC -- * in Ring3 when CR4.PCE=0. -- */ -- if (enable_vmware_backdoor) -+ if (!kvm_pmu_has_perf_global_ctrl(pmu)) - return true; - - /* -@@ -735,7 +731,22 @@ bool kvm_need_rdpmc_intercept(struct kvm - * capabilities themselves may be a subset of hardware capabilities. - */ - return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp || -- pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed || -+ pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed; -+} -+EXPORT_SYMBOL_GPL(kvm_need_perf_global_ctrl_intercept); -+ -+bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ /* -+ * VMware allows access to these Pseduo-PMCs even when read via RDPMC -+ * in Ring3 when CR4.PCE=0. -+ */ -+ if (enable_vmware_backdoor) -+ return true; -+ -+ return kvm_need_perf_global_ctrl_intercept(vcpu) || - pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) || - pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1); - } -@@ -932,11 +943,12 @@ void kvm_pmu_refresh(struct kvm_vcpu *vc - * in the global controls). Emulate that behavior when refreshing the - * PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL. - */ -- if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) { -+ if (pmu->nr_arch_gp_counters && -+ (kvm_pmu_has_perf_global_ctrl(pmu) || kvm_vcpu_has_mediated_pmu(vcpu))) - pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0); -- if (kvm_vcpu_has_mediated_pmu(vcpu)) -- kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); -- } -+ -+ if (kvm_vcpu_has_mediated_pmu(vcpu)) -+ kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); - - bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); - bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX, -Index: kernel-staging/arch/x86/kvm/pmu.h -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/pmu.h -+++ kernel-staging/arch/x86/kvm/pmu.h -@@ -239,6 +239,7 @@ void kvm_pmu_instruction_retired(struct - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); -+bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu); - bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu); - - extern struct kvm_pmu_ops intel_pmu_ops; -Index: kernel-staging/arch/x86/kvm/svm/svm.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/svm/svm.c -+++ kernel-staging/arch/x86/kvm/svm/svm.c -@@ -778,6 +778,40 @@ void svm_vcpu_free_msrpm(void *msrpm) - __free_pages(virt_to_page(msrpm), get_order(MSRPM_SIZE)); - } - -+static void svm_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) -+{ -+ bool intercept = !kvm_vcpu_has_mediated_pmu(vcpu); -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ int i; -+ -+ if (!enable_mediated_pmu) -+ return; -+ -+ /* Legacy counters are always available for AMD CPUs with a PMU. */ -+ for (i = 0; i < min(pmu->nr_arch_gp_counters, AMD64_NUM_COUNTERS); i++) -+ svm_set_intercept_for_msr(vcpu, MSR_K7_PERFCTR0 + i, -+ MSR_TYPE_RW, intercept); -+ -+ intercept |= !guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE); -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) -+ svm_set_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i, -+ MSR_TYPE_RW, intercept); -+ -+ for ( ; i < kvm_pmu_cap.num_counters_gp; i++) -+ svm_enable_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i, -+ MSR_TYPE_RW); -+ -+ intercept = kvm_need_perf_global_ctrl_intercept(vcpu); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL, -+ MSR_TYPE_RW, intercept); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, -+ MSR_TYPE_RW, intercept); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, -+ MSR_TYPE_RW, intercept); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET, -+ MSR_TYPE_RW, intercept); -+} -+ - static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - struct vcpu_svm *svm = to_svm(vcpu); -@@ -835,6 +869,8 @@ static void svm_recalc_msr_intercepts(st - if (sev_es_guest(vcpu->kvm)) - sev_es_recalc_msr_intercepts(vcpu); - -+ svm_recalc_pmu_msr_intercepts(vcpu); -+ - /* - * x2APIC intercepts are modified on-demand and cannot be filtered by - * userspace. -Index: kernel-staging/arch/x86/kvm/vmx/pmu_intel.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/vmx/pmu_intel.c -+++ kernel-staging/arch/x86/kvm/vmx/pmu_intel.c -@@ -128,19 +128,6 @@ static struct kvm_pmc *intel_rdpmc_ecx_t - return &counters[array_index_nospec(idx, num_counters)]; - } - --static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu) --{ -- if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM)) -- return 0; -- -- return vcpu->arch.perf_capabilities; --} -- --static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu) --{ -- return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0; --} -- - static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr) - { - if (!fw_writes_is_enabled(pmu_to_vcpu(pmu))) -Index: kernel-staging/arch/x86/kvm/vmx/pmu_intel.h -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/vmx/pmu_intel.h -+++ kernel-staging/arch/x86/kvm/vmx/pmu_intel.h -@@ -4,6 +4,21 @@ - - #include - -+#include "cpuid.h" -+ -+static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu) -+{ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM)) -+ return 0; -+ -+ return vcpu->arch.perf_capabilities; -+} -+ -+static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu) -+{ -+ return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0; -+} -+ - bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu); - int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); - -Index: kernel-staging/arch/x86/kvm/vmx/vmx.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/vmx/vmx.c -+++ kernel-staging/arch/x86/kvm/vmx/vmx.c -@@ -4068,6 +4068,53 @@ void pt_update_intercept_for_msr(struct - } - } - -+static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) -+{ -+ bool has_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu); -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct vcpu_vmx *vmx = to_vmx(vcpu); -+ bool intercept = !has_mediated_pmu; -+ int i; -+ -+ if (!enable_mediated_pmu) -+ return; -+ -+ vm_entry_controls_changebit(vmx, VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, -+ has_mediated_pmu); -+ -+ vm_exit_controls_changebit(vmx, VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, -+ has_mediated_pmu); -+ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i, -+ MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, MSR_TYPE_RW, -+ intercept || !fw_writes_is_enabled(vcpu)); -+ } -+ for ( ; i < kvm_pmu_cap.num_counters_gp; i++) { -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i, -+ MSR_TYPE_RW, true); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, -+ MSR_TYPE_RW, true); -+ } -+ -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i, -+ MSR_TYPE_RW, intercept); -+ for ( ; i < kvm_pmu_cap.num_counters_fixed; i++) -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i, -+ MSR_TYPE_RW, true); -+ -+ intercept = kvm_need_perf_global_ctrl_intercept(vcpu); -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_STATUS, -+ MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -+ MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL, -+ MSR_TYPE_RW, intercept); -+} -+ - static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - if (!cpu_has_vmx_msr_bitmap()) -@@ -4115,17 +4162,7 @@ static void vmx_recalc_msr_intercepts(st - vmx_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, - !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); - -- if (enable_mediated_pmu) { -- bool is_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu); -- struct vcpu_vmx *vmx = to_vmx(vcpu); -- -- vm_entry_controls_changebit(vmx, -- VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -- -- vm_exit_controls_changebit(vmx, -- VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | -- VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -- } -+ vmx_recalc_pmu_msr_intercepts(vcpu); - - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be diff --git a/SPECS/kernel-rt/0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf b/SPECS/kernel-rt/0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf deleted file mode 100644 index 1903f1f63..000000000 --- a/SPECS/kernel-rt/0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf +++ /dev/null @@ -1,55 +0,0 @@ -From d7cb98e68c98e985d87a26f79a892cbc787bec94 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:07 +0000 -Subject: [PATCH 088/100] KVM: x86/pmu: Bypass perf checks when emulating - mediated PMU counter accesses - -When emulating a PMC counter read or write for a mediated PMU, bypass the -perf checks and emulated_counter logic as the counters aren't proxied -through perf, i.e. pmc->counter always holds the guest's up-to-date value, -and thus there's no need to defer emulated overflow checks. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: split from event filtering change, write shortlog+changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 5 +++++ - arch/x86/kvm/pmu.h | 3 +++ - 2 files changed, 8 insertions(+) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 817ef852bdf9..082d2905882b 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -377,6 +377,11 @@ static void pmc_update_sample_period(struct kvm_pmc *pmc) - - void pmc_write_counter(struct kvm_pmc *pmc, u64 val) - { -+ if (kvm_vcpu_has_mediated_pmu(pmc->vcpu)) { -+ pmc->counter = val & pmc_bitmask(pmc); -+ return; -+ } -+ - /* - * Drop any unconsumed accumulated counts, the WRMSR is a write, not a - * read-modify-write. Adjust the counter value so that its value is -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 356b08e92bc9..9a199109d672 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -111,6 +111,9 @@ static inline u64 pmc_read_counter(struct kvm_pmc *pmc) - { - u64 counter, enabled, running; - -+ if (kvm_vcpu_has_mediated_pmu(pmc->vcpu)) -+ return pmc->counter & pmc_bitmask(pmc); -+ - counter = pmc->counter + pmc->emulated_counter; - - if (pmc->perf_event && !pmc->is_paused) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf b/SPECS/kernel-rt/0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf deleted file mode 100644 index 87baa99ac..000000000 --- a/SPECS/kernel-rt/0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf +++ /dev/null @@ -1,101 +0,0 @@ -From a9ca9d609170d9ee2f4954bf7b489b440d4cc6e0 Mon Sep 17 00:00:00 2001 -From: Mingwei Zhang -Date: Mon, 24 Mar 2025 17:31:06 +0000 -Subject: [PATCH 089/100] KVM: x86/pmu: Introduce eventsel_hw to prepare for - pmu event filtering - -Introduce eventsel_hw and fixed_ctr_ctrl_hw to store the actual HW value in -PMU event selector MSRs. In mediated PMU checks events before allowing the -event values written to the PMU MSRs. However, to match the HW behavior, -when PMU event checks fails, KVM should allow guest to read the value back. - -This essentially requires an extra variable to separate the guest requested -value from actual PMU MSR value. Note this only applies to event selectors. - -Signed-off-by: Mingwei Zhang -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 2 ++ - arch/x86/kvm/pmu.c | 7 +++++-- - arch/x86/kvm/svm/pmu.c | 1 + - arch/x86/kvm/vmx/pmu_intel.c | 2 ++ - 4 files changed, 10 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index ecd0634599ff..31b2bdbf8ea6 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -528,6 +528,7 @@ struct kvm_pmc { - */ - u64 emulated_counter; - u64 eventsel; -+ u64 eventsel_hw; - struct perf_event *perf_event; - struct kvm_vcpu *vcpu; - /* -@@ -556,6 +557,7 @@ struct kvm_pmu { - unsigned nr_arch_fixed_counters; - unsigned available_event_types; - u64 fixed_ctr_ctrl; -+ u64 fixed_ctr_ctrl_hw; - u64 fixed_ctr_ctrl_rsvd; - u64 global_ctrl; - u64 global_status; -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 082d2905882b..e39ae37f0280 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -890,11 +890,14 @@ static void kvm_pmu_reset(struct kvm_vcpu *vcpu) - pmc->counter = 0; - pmc->emulated_counter = 0; - -- if (pmc_is_gp(pmc)) -+ if (pmc_is_gp(pmc)) { - pmc->eventsel = 0; -+ pmc->eventsel_hw = 0; -+ } - } - -- pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status = 0; -+ pmu->fixed_ctr_ctrl = pmu->fixed_ctr_ctrl_hw = 0; -+ pmu->global_ctrl = pmu->global_status = 0; - - kvm_pmu_call(reset)(vcpu); - } -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 9ffd44a5d474..9641ef5d0dd7 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -165,6 +165,7 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - data &= ~pmu->reserved_bits; - if (data != pmc->eventsel) { - pmc->eventsel = data; -+ pmc->eventsel_hw = data; - kvm_pmu_request_counter_reprogram(pmc); - } - return 0; -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 8005a1728dec..6835b2fde047 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -61,6 +61,7 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data) - int i; - - pmu->fixed_ctr_ctrl = data; -+ pmu->fixed_ctr_ctrl_hw = data; - for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { - u8 new_ctrl = fixed_ctrl_field(data, i); - u8 old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i); -@@ -430,6 +431,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - - if (data != pmc->eventsel) { - pmc->eventsel = data; -+ pmc->eventsel_hw = data; - kvm_pmu_request_counter_reprogram(pmc); - } - break; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf b/SPECS/kernel-rt/0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf deleted file mode 100644 index 39001fdf2..000000000 --- a/SPECS/kernel-rt/0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf +++ /dev/null @@ -1,69 +0,0 @@ -From 43a7fefcd23f3e1430fbcb02863ba2f3f0ea026f Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:07 +0000 -Subject: [PATCH 090/100] KVM: x86/pmu: Reprogram mediated PMU event selectors - on event filter updates - -Refresh the event selectors that are programmed into hardware when a PMC -is "reprogrammed" for a mediated PMU, i.e. if userspace changes the PMU -event filters - -Note, KVM doesn't utilize the reprogramming infrastructure to handle -counter overflow for mediated PMUs, as there's no need to reprogram a -non-existent perf event. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: add a helper to document behavior, split patch and rewrite changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e39ae37f0280..b4c6a7704a01 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -518,6 +518,25 @@ static bool pmc_is_event_allowed(struct kvm_pmc *pmc) - return is_fixed_event_allowed(filter, pmc->idx); - } - -+static void kvm_mediated_pmu_refresh_event_filter(struct kvm_pmc *pmc) -+{ -+ bool allowed = pmc_is_event_allowed(pmc); -+ struct kvm_pmu *pmu = pmc_to_pmu(pmc); -+ -+ if (pmc_is_gp(pmc)) { -+ pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; -+ if (allowed) -+ pmc->eventsel_hw |= pmc->eventsel & -+ ARCH_PERFMON_EVENTSEL_ENABLE; -+ } else { -+ u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf); -+ -+ pmu->fixed_ctr_ctrl_hw &= ~mask; -+ if (allowed) -+ pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask; -+ } -+} -+ - static int reprogram_counter(struct kvm_pmc *pmc) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -@@ -526,6 +545,11 @@ static int reprogram_counter(struct kvm_pmc *pmc) - bool emulate_overflow; - u8 fixed_ctr_ctrl; - -+ if (kvm_vcpu_has_mediated_pmu(pmu_to_vcpu(pmu))) { -+ kvm_mediated_pmu_refresh_event_filter(pmc); -+ return 0; -+ } -+ - emulate_overflow = pmc_pause_counter(pmc); - - if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || --- -2.43.0 - diff --git a/SPECS/kernel-rt/0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf b/SPECS/kernel-rt/0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf deleted file mode 100644 index 5de637397..000000000 --- a/SPECS/kernel-rt/0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf +++ /dev/null @@ -1,43 +0,0 @@ -From 7554705da0e56ce2626c25add280522c2620f30e Mon Sep 17 00:00:00 2001 -From: Sandipan Das -Date: Mon, 24 Mar 2025 17:31:08 +0000 -Subject: [PATCH 091/100] KVM: x86/pmu: Always stuff GuestOnly=1,HostOnly=0 for - mediated PMCs on AMD - -On AMD platforms, there is no way to restore PerfCntrGlobalCtl at -VM-Entry or clear it at VM-Exit. Since the register states will be -restored before entering and saved after exiting guest context, the -counters can keep ticking and even overflow leading to chaos while -still in host context. - -To avoid this, intecept event selectors, which is already done by mediated -PMU. In addition, always set the GuestOnly bit and clear the HostOnly bit -for PMU selectors on AMD. Doing so allows the counters run only in guest -context even if their enable bits are still set after VM exit and before -host/guest PMU context switch. - -Signed-off-by: Sandipan Das -Signed-off-by: Mingwei Zhang -[sean: massage shortlog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 9641ef5d0dd7..a5e70a4e7647 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -165,7 +165,8 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - data &= ~pmu->reserved_bits; - if (data != pmc->eventsel) { - pmc->eventsel = data; -- pmc->eventsel_hw = data; -+ pmc->eventsel_hw = (data & ~AMD64_EVENTSEL_HOSTONLY) | -+ AMD64_EVENTSEL_GUESTONLY; - kvm_pmu_request_counter_reprogram(pmc); - } - return 0; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf b/SPECS/kernel-rt/0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf deleted file mode 100644 index 5fed52aa2..000000000 --- a/SPECS/kernel-rt/0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf +++ /dev/null @@ -1,393 +0,0 @@ -From fd815369165deaa89dc8f6f7ad917205f6138859 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:09 +0000 -Subject: [PATCH 092/100] KVM: x86/pmu: Load/put mediated PMU context when - entering/exiting guest - -Implement the PMU "world switch" between host perf and guest mediated PMU. -When loading guest state, call into perf to switch from host to guest, and -then load guest state into hardware, and then reverse those actions when -putting guest state. - -On the KVM side, when loading guest state, zero PERF_GLOBAL_CTRL to ensure -all counters are disabled, then load selectors and counters, and finally -call into vendor code to load control/status information. While VMX and -SVM use different mechanisms to avoid counting host activity while guest -controls are loaded, both implementations require PERF_GLOBAL_CTRL to be -zeroed when the event selectors are in flux. - -When putting guest state, reverse the order, and save and zero controls -and status prior to saving+zeroing selectors and counters. Defer clearing -PERF_GLOBAL_CTRL to vendor code, as only SVM needs to manually clear the -MSR; VMX configures PERF_GLOBAL_CTRL to be atomically cleared by the CPU -on VM-Exit. - -Handle the difference in MSR layouts between Intel and AMD by communicating -the bases and stride via kvm_pmu_ops. Because KVM requires Intel v4 (and -full-width writes) and AMD v2, the MSRs to load/save are constant for a -given vendor, i.e. do not vary based on the guest PMU, and do not vary -based on host PMU (because KVM will simply disable mediated PMU support if -the necessary MSRs are unsupported). - -Except for retrieving the guest's PERF_GLOBAL_CTRL, which needs to be read -before invoking any fastpath handler (spoiler alert), perform the context -switch around KVM's inner run loop. State only needs to be synchronized -from hardware before KVM can access the software "caches". - -Note, VMX already grabs the guest's PERF_GLOBAL_CTRL immediately after -VM-Exit, as hardware saves value into the VMCS. - -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sandipan Das -Signed-off-by: Sandipan Das -Signed-off-by: Dapeng Mi -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm-x86-pmu-ops.h | 2 + - arch/x86/include/asm/msr-index.h | 1 + - arch/x86/kvm/pmu.c | 111 +++++++++++++++++++++++++ - arch/x86/kvm/pmu.h | 10 +++ - arch/x86/kvm/svm/pmu.c | 34 ++++++++ - arch/x86/kvm/svm/svm.c | 3 + - arch/x86/kvm/vmx/pmu_intel.c | 45 ++++++++++ - arch/x86/kvm/x86.c | 4 + - 8 files changed, 210 insertions(+) - -diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h -index ad2cc82abf79..f0aa6996811f 100644 ---- a/arch/x86/include/asm/kvm-x86-pmu-ops.h -+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h -@@ -24,6 +24,8 @@ KVM_X86_PMU_OP_OPTIONAL(deliver_pmi) - KVM_X86_PMU_OP_OPTIONAL(cleanup) - - KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl) -+KVM_X86_PMU_OP(mediated_load) -+KVM_X86_PMU_OP(mediated_put) - - #undef KVM_X86_PMU_OP - #undef KVM_X86_PMU_OP_OPTIONAL -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 156c9709a9d9..6b22abdc856a 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -1205,6 +1205,7 @@ - #define MSR_CORE_PERF_GLOBAL_STATUS 0x0000038e - #define MSR_CORE_PERF_GLOBAL_CTRL 0x0000038f - #define MSR_CORE_PERF_GLOBAL_OVF_CTRL 0x00000390 -+#define MSR_CORE_PERF_GLOBAL_STATUS_SET 0x00000391 - - #define MSR_PERF_METRICS 0x00000329 - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index b4c6a7704a01..77042cad3155 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -1234,3 +1234,114 @@ int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp) - kfree(filter); - return r; - } -+ -+static __always_inline u32 fixed_counter_msr(u32 idx) -+{ -+ return kvm_pmu_ops.FIXED_COUNTER_BASE + idx * kvm_pmu_ops.MSR_STRIDE; -+} -+ -+static __always_inline u32 gp_counter_msr(u32 idx) -+{ -+ return kvm_pmu_ops.GP_COUNTER_BASE + idx * kvm_pmu_ops.MSR_STRIDE; -+} -+ -+static __always_inline u32 gp_eventsel_msr(u32 idx) -+{ -+ return kvm_pmu_ops.GP_EVENTSEL_BASE + idx * kvm_pmu_ops.MSR_STRIDE; -+} -+ -+static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct kvm_pmc *pmc; -+ u32 i; -+ -+ /* -+ * No need to zero out unexposed GP/fixed counters/selectors since RDPMC -+ * is intercepted if hardware has counters that aren't visible to the -+ * guest (KVM will inject #GP as appropriate). -+ */ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ pmc = &pmu->gp_counters[i]; -+ -+ wrmsrl(gp_counter_msr(i), pmc->counter); -+ wrmsrl(gp_eventsel_msr(i), pmc->eventsel_hw); -+ } -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { -+ pmc = &pmu->fixed_counters[i]; -+ -+ wrmsrl(fixed_counter_msr(i), pmc->counter); -+ } -+} -+ -+void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu) -+{ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu) || -+ KVM_BUG_ON(!lapic_in_kernel(vcpu), vcpu->kvm)) -+ return; -+ -+ lockdep_assert_irqs_disabled(); -+ -+ perf_load_guest_context(kvm_lapic_get_reg(vcpu->arch.apic, APIC_LVTPC)); -+ -+ /* -+ * Disable all counters before loading event selectors and PMCs so that -+ * KVM doesn't enable or load guest counters while host events are -+ * active. VMX will enable/disabled counters at VM-Enter/VM-Exit by -+ * atomically loading PERF_GLOBAL_CONTROL. SVM effectively performs -+ * the switch by configuring all events to be GUEST_ONLY. -+ */ -+ wrmsrl(kvm_pmu_ops.PERF_GLOBAL_CTRL, 0); -+ -+ kvm_pmu_load_guest_pmcs(vcpu); -+ -+ kvm_pmu_call(mediated_load)(vcpu); -+} -+ -+static void kvm_pmu_put_guest_pmcs(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct kvm_pmc *pmc; -+ u32 i; -+ -+ /* -+ * Clear selectors and counters to ensure hardware doesn't count using -+ * guest controls when the host (perf) restores its state. -+ */ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ pmc = &pmu->gp_counters[i]; -+ -+ pmc->counter = rdpmc(i); -+ if (pmc->counter) -+ wrmsrl(gp_counter_msr(i), 0); -+ if (pmc->eventsel_hw) -+ wrmsrl(gp_eventsel_msr(i), 0); -+ } -+ -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { -+ pmc = &pmu->fixed_counters[i]; -+ -+ pmc->counter = rdpmc(INTEL_PMC_FIXED_RDPMC_BASE | i); -+ if (pmc->counter) -+ wrmsrl(fixed_counter_msr(i), 0); -+ } -+} -+ -+void kvm_mediated_pmu_put(struct kvm_vcpu *vcpu) -+{ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu) || -+ KVM_BUG_ON(!lapic_in_kernel(vcpu), vcpu->kvm)) -+ return; -+ -+ lockdep_assert_irqs_disabled(); -+ -+ /* -+ * Defer handling of PERF_GLOBAL_CTRL to vendor code. On Intel, it's -+ * atomically cleared on VM-Exit, i.e. doesn't need to be clear here. -+ */ -+ kvm_pmu_call(mediated_put)(vcpu); -+ -+ kvm_pmu_put_guest_pmcs(vcpu); -+ -+ perf_put_guest_context(); -+} -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 9a199109d672..25b583da9ee2 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -38,11 +38,19 @@ struct kvm_pmu_ops { - void (*cleanup)(struct kvm_vcpu *vcpu); - - bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); -+ void (*mediated_load)(struct kvm_vcpu *vcpu); -+ void (*mediated_put)(struct kvm_vcpu *vcpu); - void (*write_global_ctrl)(u64 global_ctrl); - - const u64 EVENTSEL_EVENT; - const int MAX_NR_GP_COUNTERS; - const int MIN_NR_GP_COUNTERS; -+ -+ const u32 PERF_GLOBAL_CTRL; -+ const u32 GP_EVENTSEL_BASE; -+ const u32 GP_COUNTER_BASE; -+ const u32 FIXED_COUNTER_BASE; -+ const u32 MSR_STRIDE; - }; - - void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops); -@@ -240,6 +248,8 @@ void kvm_pmu_destroy(struct kvm_vcpu *vcpu); - int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp); - void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu); - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); -+void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu); -+void kvm_mediated_pmu_put(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); - bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu); -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index a5e70a4e7647..c03720b30785 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -233,6 +233,32 @@ static bool amd_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pm - return host_pmu->version >= 2; - } - -+static void amd_mediated_pmu_load(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ u64 global_status; -+ -+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, global_status); -+ /* Clear host global_status MSR if non-zero. */ -+ if (global_status) -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, global_status); -+ -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET, pmu->global_status); -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, pmu->global_ctrl); -+} -+ -+static void amd_mediated_pmu_put(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0); -+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, pmu->global_status); -+ -+ /* Clear global status bits if non-zero */ -+ if (pmu->global_status) -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, pmu->global_status); -+} -+ - struct kvm_pmu_ops amd_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = amd_msr_idx_to_pmc, -@@ -244,8 +270,16 @@ struct kvm_pmu_ops amd_pmu_ops __initdata = { - .init = amd_pmu_init, - - .is_mediated_pmu_supported = amd_pmu_is_mediated_pmu_supported, -+ .mediated_load = amd_mediated_pmu_load, -+ .mediated_put = amd_mediated_pmu_put, - - .EVENTSEL_EVENT = AMD64_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_AMD_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = AMD64_NUM_COUNTERS, -+ -+ .PERF_GLOBAL_CTRL = MSR_AMD64_PERF_CNTR_GLOBAL_CTL, -+ .GP_EVENTSEL_BASE = MSR_F15H_PERF_CTL0, -+ .GP_COUNTER_BASE = MSR_F15H_PERF_CTR0, -+ .FIXED_COUNTER_BASE = 0, -+ .MSR_STRIDE = 2, - }; -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index add50b64256c..ca6f453cc160 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4416,6 +4416,9 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) - - vcpu->arch.regs_avail &= ~SVM_REGS_LAZY_LOAD_SET; - -+ if (!msr_write_intercepted(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL)) -+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, vcpu_to_pmu(vcpu)->global_ctrl); -+ - /* - * We need to handle MC intercepts here before the vcpu has a chance to - * change the physical cpu -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 6835b2fde047..19fd6d359e59 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -786,6 +786,43 @@ static void intel_pmu_write_global_ctrl(u64 global_ctrl) - vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, global_ctrl); - } - -+ -+static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ u64 global_status, toggle; -+ -+ rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status); -+ toggle = pmu->global_status ^ global_status; -+ if (global_status & toggle) -+ wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, global_status & toggle); -+ if (pmu->global_status & toggle) -+ wrmsrq(MSR_CORE_PERF_GLOBAL_STATUS_SET, pmu->global_status & toggle); -+ -+ wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, pmu->fixed_ctr_ctrl_hw); -+} -+ -+static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ /* MSR_CORE_PERF_GLOBAL_CTRL is already saved at VM-exit. */ -+ rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, pmu->global_status); -+ -+ /* Clear hardware MSR_CORE_PERF_GLOBAL_STATUS MSR, if non-zero. */ -+ if (pmu->global_status) -+ wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, pmu->global_status); -+ -+ /* -+ * Clear hardware FIXED_CTR_CTRL MSR to avoid information leakage and -+ * also to avoid accidentally enabling fixed counters (based on guest -+ * state) while running in the host, e.g. when setting global ctrl. -+ */ -+ if (pmu->fixed_ctr_ctrl_hw) -+ wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0); -+} -+ -+ - struct kvm_pmu_ops intel_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = intel_msr_idx_to_pmc, -@@ -799,9 +836,17 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = { - .cleanup = intel_pmu_cleanup, - - .is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported, -+ .mediated_load = intel_mediated_pmu_load, -+ .mediated_put = intel_mediated_pmu_put, - .write_global_ctrl = intel_pmu_write_global_ctrl, - - .EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = 1, -+ -+ .PERF_GLOBAL_CTRL = MSR_CORE_PERF_GLOBAL_CTRL, -+ .GP_EVENTSEL_BASE = MSR_P6_EVNTSEL0, -+ .GP_COUNTER_BASE = MSR_IA32_PMC0, -+ .FIXED_COUNTER_BASE = MSR_CORE_PERF_FIXED_CTR0, -+ .MSR_STRIDE = 1, - }; -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index b8014435c988..7fb94ef64e18 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -10906,6 +10906,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) - run_flags |= KVM_RUN_LOAD_DEBUGCTL; - vcpu->arch.host_debugctl = debug_ctl; - -+ kvm_mediated_pmu_load(vcpu); -+ - guest_timing_enter_irqoff(); - - for (;;) { -@@ -10936,6 +10938,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) - ++vcpu->stat.exits; - } - -+ kvm_mediated_pmu_put(vcpu); -+ - /* - * Do this here before restoring debug registers on the host. And - * since we do this before handling the vmexit, a DR access vmexit --- -2.43.0 - diff --git a/SPECS/kernel-rt/0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf b/SPECS/kernel-rt/0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf deleted file mode 100644 index bda48b72e..000000000 --- a/SPECS/kernel-rt/0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf +++ /dev/null @@ -1,76 +0,0 @@ -From 5269e5df9fd7e611d8e30484f5ef9a06ebb4da67 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 16:26:16 -0700 -Subject: [PATCH 093/100] KVM: x86/pmu: Disallow emulation in the fastpath if - mediated PMCs are active - -Don't handle exits in the fastpath if emulation is required, i.e. if an -instruction needs to be skipped, the mediated PMU is enabled, and one or -more PMCs is counting instructions. With the mediated PMU, KVM's cache of -PMU state is inconsistent with respect to hardware until KVM exits the -inner run loop (when the mediated PMU is "put"). - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.h | 10 ++++++++++ - arch/x86/kvm/x86.c | 9 +++++++++ - 2 files changed, 19 insertions(+) - -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 25b583da9ee2..0925246731cb 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -234,6 +234,16 @@ static inline bool pmc_is_globally_enabled(struct kvm_pmc *pmc) - return test_bit(pmc->idx, (unsigned long *)&pmu->global_ctrl); - } - -+static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ return !kvm_vcpu_has_mediated_pmu(vcpu) || -+ !bitmap_intersects(pmu->pmc_counting_instructions, -+ (unsigned long *)&pmu->global_ctrl, -+ X86_PMC_IDX_MAX); -+} -+ - void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu); - void kvm_pmu_handle_event(struct kvm_vcpu *vcpu); - int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 7fb94ef64e18..6bdf7ef0b535 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2092,6 +2092,9 @@ EXPORT_SYMBOL_GPL(kvm_emulate_invd); - - fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu) - { -+ if (!kvm_pmu_is_fastpath_emulation_allowed(vcpu)) -+ return EXIT_FASTPATH_NONE; -+ - if (!kvm_emulate_invd(vcpu)) - return EXIT_FASTPATH_EXIT_USERSPACE; - -@@ -2151,6 +2154,9 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); - -+ if (!kvm_pmu_is_fastpath_emulation_allowed(vcpu)) -+ return EXIT_FASTPATH_NONE; -+ - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): - if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic) || -@@ -11267,6 +11273,9 @@ EXPORT_SYMBOL_GPL(kvm_emulate_halt); - - fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu) - { -+ if (!kvm_pmu_is_fastpath_emulation_allowed(vcpu)) -+ return EXIT_FASTPATH_NONE; -+ - if (!kvm_emulate_halt(vcpu)) - return EXIT_FASTPATH_EXIT_USERSPACE; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf b/SPECS/kernel-rt/0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf deleted file mode 100644 index f21ff298f..000000000 --- a/SPECS/kernel-rt/0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf +++ /dev/null @@ -1,75 +0,0 @@ -From bdba07134ecf40e31f5052fcaabbb9a596bd89d1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:10 +0000 -Subject: [PATCH 094/100] KVM: x86/pmu: Handle emulated instruction for - mediated vPMU - -Mediated vPMU needs to accumulate the emulated instructions into counter -and load the counter into HW at vm-entry. - -Moreover, if the accumulation leads to counter overflow, KVM needs to -update GLOBAL_STATUS and inject PMI into guest as well. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 39 +++++++++++++++++++++++++++++++++++++-- - 1 file changed, 37 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 77042cad3155..ddab1630a978 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -1018,10 +1018,45 @@ void kvm_pmu_destroy(struct kvm_vcpu *vcpu) - kvm_pmu_reset(vcpu); - } - -+static bool pmc_is_pmi_enabled(struct kvm_pmc *pmc) -+{ -+ u8 fixed_ctr_ctrl; -+ -+ if (pmc_is_gp(pmc)) -+ return pmc->eventsel & ARCH_PERFMON_EVENTSEL_INT; -+ -+ fixed_ctr_ctrl = fixed_ctrl_field(pmc_to_pmu(pmc)->fixed_ctr_ctrl, -+ pmc->idx - KVM_FIXED_PMC_BASE_IDX); -+ return fixed_ctr_ctrl & INTEL_FIXED_0_ENABLE_PMI; -+} -+ - static void kvm_pmu_incr_counter(struct kvm_pmc *pmc) - { -- pmc->emulated_counter++; -- kvm_pmu_request_counter_reprogram(pmc); -+ struct kvm_vcpu *vcpu = pmc->vcpu; -+ -+ /* -+ * For perf-based PMUs, accumulate software-emulated events separately -+ * from pmc->counter, as pmc->counter is offset by the count of the -+ * associated perf event. Request reprogramming, which will consult -+ * both emulated and hardware-generated events to detect overflow. -+ */ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu)) { -+ pmc->emulated_counter++; -+ kvm_pmu_request_counter_reprogram(pmc); -+ return; -+ } -+ -+ /* -+ * For mediated PMUs, pmc->counter is updated when the vCPU's PMU is -+ * put, and will be loaded into hardware when the PMU is loaded. Simply -+ * increment the counter and signal overflow if it wraps to zero. -+ */ -+ pmc->counter = (pmc->counter + 1) & pmc_bitmask(pmc); -+ if (!pmc->counter) { -+ pmc_to_pmu(pmc)->global_status |= BIT_ULL(pmc->idx); -+ if (pmc_is_pmi_enabled(pmc)) -+ kvm_make_request(KVM_REQ_PMI, vcpu); -+ } - } - - static inline bool cpl_is_matched(struct kvm_pmc *pmc) --- -2.43.0 - diff --git a/SPECS/kernel-rt/0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf b/SPECS/kernel-rt/0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf deleted file mode 100644 index 69a25d4a4..000000000 --- a/SPECS/kernel-rt/0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf +++ /dev/null @@ -1,74 +0,0 @@ -From 8ce94c1009c52f79d3719ab1a62281a455ba6174 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:11 +0000 -Subject: [PATCH 095/100] KVM: nVMX: Add macros to simplify nested MSR - interception setting - -Add macros nested_vmx_merge_msr_bitmaps_xxx() to simplify nested MSR -interception setting. No function change intended. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/nested.c | 35 +++++++++++++++++++---------------- - 1 file changed, 19 insertions(+), 16 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index db2fd4eedc90..47f1f0c7d3a7 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -614,6 +614,19 @@ static inline void nested_vmx_set_intercept_for_msr(struct vcpu_vmx *vmx, - msr_bitmap_l0, msr); - } - -+#define nested_vmx_merge_msr_bitmaps(msr, type) \ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, \ -+ msr_bitmap_l0, msr, type) -+ -+#define nested_vmx_merge_msr_bitmaps_read(msr) \ -+ nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_R) -+ -+#define nested_vmx_merge_msr_bitmaps_write(msr) \ -+ nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_W) -+ -+#define nested_vmx_merge_msr_bitmaps_rw(msr) \ -+ nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_RW) -+ - /* - * Merge L0's and L1's MSR bitmap, return false to indicate that - * we do not use the hardware. -@@ -697,23 +710,13 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - * other runtime changes to vmcs01's bitmap, e.g. dynamic pass-through. - */ - #ifdef CONFIG_X86_64 -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_FS_BASE, MSR_TYPE_RW); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_GS_BASE, MSR_TYPE_RW); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_KERNEL_GS_BASE, MSR_TYPE_RW); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_FS_BASE); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_GS_BASE); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_KERNEL_GS_BASE); - #endif -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_IA32_SPEC_CTRL, MSR_TYPE_RW); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_IA32_PRED_CMD, MSR_TYPE_W); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_IA32_FLUSH_CMD, MSR_TYPE_W); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_SPEC_CTRL); -+ nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD); -+ nested_vmx_merge_msr_bitmaps_write(MSR_IA32_FLUSH_CMD); - - nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, - MSR_IA32_APERF, MSR_TYPE_R); --- -2.43.0 - diff --git a/SPECS/kernel-rt/0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf b/SPECS/kernel-rt/0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf deleted file mode 100644 index 73e6f8cba..000000000 --- a/SPECS/kernel-rt/0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf +++ /dev/null @@ -1,70 +0,0 @@ -From 9e783fa42339f5f089b6e4c9972d92066fd9175b Mon Sep 17 00:00:00 2001 -From: Mingwei Zhang -Date: Mon, 24 Mar 2025 17:31:12 +0000 -Subject: [PATCH 096/100] KVM: nVMX: Disable PMU MSR interception as - appropriate while running L2 - -Merge KVM's PMU MSR interception bitmaps with those of L1, i.e. merge the -bitmaps of vmcs01 and vmcs12, e.g. so that KVM doesn't interpose on MSR -accesses unnecessarily if L1 exposes a mediated PMU (or equivalent) to L2. - -Signed-off-by: Mingwei Zhang -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -[sean: rewrite changelog and comment, omit MSRs that are always intercepted] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/nested.c | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 47f1f0c7d3a7..b986a6fb684c 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -627,6 +627,34 @@ static inline void nested_vmx_set_intercept_for_msr(struct vcpu_vmx *vmx, - #define nested_vmx_merge_msr_bitmaps_rw(msr) \ - nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_RW) - -+static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu, -+ unsigned long *msr_bitmap_l1, -+ unsigned long *msr_bitmap_l0) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct vcpu_vmx *vmx = to_vmx(vcpu); -+ int i; -+ -+ /* -+ * Skip the merges if the vCPU doesn't have a mediated PMU MSR, i.e. if -+ * none of the MSRs can possibly be passed through to L1. -+ */ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu)) -+ return; -+ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_PERFCTR0 + i); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_PMC0 + i); -+ } -+ -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) -+ nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_FIXED_CTR0 + i); -+ -+ nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL); -+ nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS); -+ nested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL); -+} -+ - /* - * Merge L0's and L1's MSR bitmap, return false to indicate that - * we do not use the hardware. -@@ -724,6 +752,8 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, - MSR_IA32_MPERF, MSR_TYPE_R); - -+ nested_vmx_merge_pmu_msr_bitmaps(vcpu, msr_bitmap_l1, msr_bitmap_l0); -+ - kvm_vcpu_unmap(vcpu, &map); - - vmx->nested.force_msr_bitmap_recalc = false; --- -2.43.0 - diff --git a/SPECS/kernel-rt/0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf b/SPECS/kernel-rt/0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf deleted file mode 100644 index f64e1656d..000000000 --- a/SPECS/kernel-rt/0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf +++ /dev/null @@ -1,56 +0,0 @@ -From 6a79b2c24060d637b19cee73dbb314b6a2b26d6f Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 5 Aug 2025 17:33:47 -0700 -Subject: [PATCH 097/100] KVM: nSVM: Disable PMU MSR interception as - appropriate while running L2 - -Add MSRs that might be passed through to L1 when running with a mediated -PMU to the nested SVM's set of to-be-merged MSR indices, i.e. disable -interception of PMU MSRs when running L2 if both KVM (L0) and L1 disable -interception. There is no need for KVM to interpose on such MSR accesses, -e.g. if L1 exposes a mediated PMU (or equivalent) to L2. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/nested.c | 18 +++++++++++++++++- - 1 file changed, 17 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c -index b7fd2e869998..de2b9db2d0ba 100644 ---- a/arch/x86/kvm/svm/nested.c -+++ b/arch/x86/kvm/svm/nested.c -@@ -194,7 +194,7 @@ void recalc_intercepts(struct vcpu_svm *svm) - * Hardcode the capacity of the array based on the maximum number of _offsets_. - * MSRs are batched together, so there are fewer offsets than MSRs. - */ --static int nested_svm_msrpm_merge_offsets[7] __ro_after_init; -+static int nested_svm_msrpm_merge_offsets[10] __ro_after_init; - static int nested_svm_nr_msrpm_merge_offsets __ro_after_init; - typedef unsigned long nsvm_msrpm_merge_t; - -@@ -222,6 +222,22 @@ int __init nested_svm_init_msrpm_merge_offsets(void) - MSR_IA32_LASTBRANCHTOIP, - MSR_IA32_LASTINTFROMIP, - MSR_IA32_LASTINTTOIP, -+ -+ MSR_K7_PERFCTR0, -+ MSR_K7_PERFCTR1, -+ MSR_K7_PERFCTR2, -+ MSR_K7_PERFCTR3, -+ MSR_F15H_PERF_CTR0, -+ MSR_F15H_PERF_CTR1, -+ MSR_F15H_PERF_CTR2, -+ MSR_F15H_PERF_CTR3, -+ MSR_F15H_PERF_CTR4, -+ MSR_F15H_PERF_CTR5, -+ -+ MSR_AMD64_PERF_CNTR_GLOBAL_CTL, -+ MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, -+ MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, -+ MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET, - }; - int i, j; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf b/SPECS/kernel-rt/0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf deleted file mode 100644 index e8155afed..000000000 --- a/SPECS/kernel-rt/0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf +++ /dev/null @@ -1,119 +0,0 @@ -From 73ca5e5868cb9cdd409a333e60deca6fd1f08ccc Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:15 +0000 -Subject: [PATCH 098/100] KVM: x86/pmu: Expose enable_mediated_pmu parameter to - user space - -Expose enable_mediated_pmu parameter to user space, i.e. allow userspace -to enable/disable mediated vPMU support. - -Document the mediated versus perf-based behavior as part of the -kernel-parameters.txt entry, and opportunistically add an entry for the -core enable_pmu param as well. - -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - .../admin-guide/kernel-parameters.txt | 49 +++++++++++++++++++ - arch/x86/kvm/svm/svm.c | 2 + - arch/x86/kvm/vmx/vmx.c | 2 + - 3 files changed, 53 insertions(+) - -diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt -index 747a55abf494..b28b2daa8a4e 100644 ---- a/Documentation/admin-guide/kernel-parameters.txt -+++ b/Documentation/admin-guide/kernel-parameters.txt -@@ -2903,6 +2903,26 @@ - - Default is Y (on). - -+ kvm.enable_pmu=[KVM,X86] -+ If enabled, KVM will virtualize PMU functionality based -+ on the virtual CPU model defined by userspace. This -+ can be overridden on a per-VM basis via -+ KVM_CAP_PMU_CAPABILITY. -+ -+ If disabled, KVM will not virtualize PMU functionality, -+ e.g. MSRs, PMCs, PMIs, etc., even if userspace defines -+ a virtual CPU model that contains PMU assets. -+ -+ Note, KVM's vPMU support implicitly requires running -+ with an in-kernel local APIC, e.g. to deliver PMIs to -+ the guest. Running without an in-kernel local APIC is -+ not supported, though KVM will allow such a combination -+ (with severely degraded functionality). -+ -+ See also enable_mediated_pmu. -+ -+ Default is Y (on). -+ - kvm.enable_virt_at_load=[KVM,ARM64,LOONGARCH,MIPS,RISCV,X86] - If enabled, KVM will enable virtualization in hardware - when KVM is loaded, and disable virtualization when KVM -@@ -2949,6 +2969,35 @@ - If the value is 0 (the default), KVM will pick a period based - on the ratio, such that a page is zapped after 1 hour on average. - -+ kvm-{amd,intel}.enable_mediated_pmu=[KVM,AMD,INTEL] -+ If enabled, KVM will provide a mediated virtual PMU, -+ instead of the default perf-based virtual PMU (if -+ kvm.enable_pmu is true and PMU is enumerated via the -+ virtual CPU model). -+ -+ With a perf-based vPMU, KVM operates as a user of perf, -+ i.e. emulates guest PMU counters using perf events. -+ KVM-created perf events are managed by perf as regular -+ (guest-only) events, e.g. are scheduled in/out, contend -+ for hardware resources, etc. Using a perf-based vPMU -+ allows guest and host usage of the PMU to co-exist, but -+ incurs non-trivial overhead and can result in silently -+ dropped guest events (due to resource contention). -+ -+ With a mediated vPMU, hardware PMU state is context -+ switched around the world switch to/from the guest. -+ KVM mediates which events the guest can utilize, but -+ gives the guest direct access to all other PMU assets -+ when possible (KVM may intercept some accesses if the -+ virtual CPU model provides a subset of hardware PMU -+ functionality). Using a mediated vPMU significantly -+ reduces PMU virtualization overhead and eliminates lost -+ guest events, but is mutually exclusive with using perf -+ to profile KVM guests and adds latency to most VM-Exits -+ (to context switch PMU state). -+ -+ Default is N (off). -+ - kvm-amd.nested= [KVM,AMD] Control nested virtualization feature in - KVM/SVM. Default is 1 (enabled). - -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index ca6f453cc160..2797c3ab7854 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -178,6 +178,8 @@ module_param(intercept_smi, bool, 0444); - bool vnmi = true; - module_param(vnmi, bool, 0444); - -+module_param(enable_mediated_pmu, bool, 0444); -+ - static bool svm_gp_erratum_intercept = true; - - static u8 rsm_ins_bytes[] = "\x0f\xaa"; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 85bd82d41f94..4a4691beba55 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -151,6 +151,8 @@ module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO); - extern bool __read_mostly allow_smaller_maxphyaddr; - module_param(allow_smaller_maxphyaddr, bool, S_IRUGO); - -+module_param(enable_mediated_pmu, bool, 0444); -+ - #define KVM_VM_CR0_ALWAYS_OFF (X86_CR0_NW | X86_CR0_CD) - #define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST X86_CR0_NE - #define KVM_VM_CR0_ALWAYS_ON \ --- -2.43.0 - diff --git a/SPECS/kernel-rt/0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf b/SPECS/kernel-rt/0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf deleted file mode 100644 index 76f8287a9..000000000 --- a/SPECS/kernel-rt/0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf +++ /dev/null @@ -1,47 +0,0 @@ -From a20f48706a03d7c6b606b37193caee67aa366b69 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 1 Aug 2025 10:21:34 -0700 -Subject: [PATCH 099/100] KVM: x86/pmu: Elide WRMSRs when loading guest PMCs if - values already match - -When loading a mediated PMU state, elide the WRMSRs to load PMCs with the -guest's value if the value in hardware already matches the guest's value. -For the relatively common case where neither the guest nor the host is -actively using the PMU, i.e. when all/many counters are '0', eliding the -WRMSRs reduces the latency of handling VM-Exit by a measurable amount -(WRMSR is significantly more expensive than RDPMC). - -As measured by KVM-Unit-Tests' CPUID VM-Exit testcase, this provides a -a ~25% reduction in latency (4k => 3k cycles) on Intel Emerald Rapids, -and a ~13% reduction (6.2k => 5.3k cycles) on AMD Turing. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index ddab1630a978..0e5048ae86fa 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -1299,13 +1299,15 @@ static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu) - for (i = 0; i < pmu->nr_arch_gp_counters; i++) { - pmc = &pmu->gp_counters[i]; - -- wrmsrl(gp_counter_msr(i), pmc->counter); -+ if (pmc->counter != rdpmc(i)) -+ wrmsrl(gp_counter_msr(i), pmc->counter); - wrmsrl(gp_eventsel_msr(i), pmc->eventsel_hw); - } - for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { - pmc = &pmu->fixed_counters[i]; - -- wrmsrl(fixed_counter_msr(i), pmc->counter); -+ if (pmc->counter != rdpmc(INTEL_PMC_FIXED_RDPMC_BASE | i)) -+ wrmsrl(fixed_counter_msr(i), pmc->counter); - } - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68254.patch b/SPECS/kernel-rt/CVE-2025-68254.patch deleted file mode 100644 index 633f966ec..000000000 --- a/SPECS/kernel-rt/CVE-2025-68254.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 2744a2ac9d9e08c0f04bf0b8b751dea2fac256c5 Mon Sep 17 00:00:00 2001 -From: Navaneeth K -Date: Thu, 20 Nov 2025 16:35:20 +0000 -Subject: [PATCH 11/16] staging: rtl8723bs: fix out-of-bounds read in OnBeacon - ESR IE parsing - -The Extended Supported Rates (ESR) IE handling in OnBeacon accessed -*(p + 1 + ielen) and *(p + 2 + ielen) without verifying that these -offsets lie within the received frame buffer. A malformed beacon with -an ESR IE positioned at the end of the buffer could cause an -out-of-bounds read, potentially triggering a kernel panic. - -Add a boundary check to ensure that the ESR IE body and the subsequent -bytes are within the limits of the frame before attempting to access -them. - -This prevents OOB reads caused by malformed beacon frames. - -Signed-off-by: Navaneeth K -Cc: stable -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -index 7d451cd5835a..32aa168bcb07 100644 ---- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -@@ -579,9 +579,11 @@ unsigned int OnBeacon(struct adapter *padapter, union recv_frame *precv_frame) - - p = rtw_get_ie(pframe + sizeof(struct ieee80211_hdr_3addr) + _BEACON_IE_OFFSET_, WLAN_EID_EXT_SUPP_RATES, &ielen, precv_frame->u.hdr.len - sizeof(struct ieee80211_hdr_3addr) - _BEACON_IE_OFFSET_); - if (p && ielen > 0) { -- if ((*(p + 1 + ielen) == 0x2D) && (*(p + 2 + ielen) != 0x2D)) -- /* Invalid value 0x2D is detected in Extended Supported Rates (ESR) IE. Try to fix the IE length to avoid failed Beacon parsing. */ -- *(p + 1) = ielen - 1; -+ if (p + 2 + ielen < pframe + len) { -+ if ((*(p + 1 + ielen) == 0x2D) && (*(p + 2 + ielen) != 0x2D)) -+ /* Invalid value 0x2D is detected in Extended Supported Rates (ESR) IE. Try to fix the IE length to avoid failed Beacon parsing. */ -+ *(p + 1) = ielen - 1; -+ } - } - - if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68255.patch b/SPECS/kernel-rt/CVE-2025-68255.patch deleted file mode 100644 index 08a9183fa..000000000 --- a/SPECS/kernel-rt/CVE-2025-68255.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 648f7276946334bcfa9025b6003649cc18a62bb5 Mon Sep 17 00:00:00 2001 -From: Navaneeth K -Date: Thu, 20 Nov 2025 16:33:08 +0000 -Subject: [PATCH 03/16] staging: rtl8723bs: fix stack buffer overflow in - OnAssocReq IE parsing - -The Supported Rates IE length from an incoming Association Request frame -was used directly as the memcpy() length when copying into a fixed-size -16-byte stack buffer (supportRate). A malicious station can advertise an -IE length larger than 16 bytes, causing a stack buffer overflow. - -Clamp ie_len to the buffer size before copying the Supported Rates IE, -and correct the bounds check when merging Extended Supported Rates to -prevent a second potential overflow. - -This prevents kernel stack corruption triggered by malformed association -requests. - -Signed-off-by: Navaneeth K -Cc: stable -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -index bc980d21d50e..7d451cd5835a 100644 ---- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -@@ -1033,6 +1033,9 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) - status = WLAN_STATUS_CHALLENGE_FAIL; - goto OnAssocReqFail; - } else { -+ if (ie_len > sizeof(supportRate)) -+ ie_len = sizeof(supportRate); -+ - memcpy(supportRate, p+2, ie_len); - supportRateNum = ie_len; - -@@ -1040,7 +1043,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) - pkt_len - WLAN_HDR_A3_LEN - ie_offset); - if (p) { - -- if (supportRateNum <= sizeof(supportRate)) { -+ if (supportRateNum + ie_len <= sizeof(supportRate)) { - memcpy(supportRate+supportRateNum, p+2, ie_len); - supportRateNum += ie_len; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68256.patch b/SPECS/kernel-rt/CVE-2025-68256.patch deleted file mode 100644 index ea7f8bff3..000000000 --- a/SPECS/kernel-rt/CVE-2025-68256.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 3cfddd5d4ae1dd9ad31ac72fa869504cc0e335b5 Mon Sep 17 00:00:00 2001 -From: Navaneeth K -Date: Thu, 20 Nov 2025 16:23:52 +0000 -Subject: [PATCH 04/16] staging: rtl8723bs: fix out-of-bounds read in - rtw_get_ie() parser - -The Information Element (IE) parser rtw_get_ie() trusted the length -byte of each IE without validating that the IE body (len bytes after -the 2-byte header) fits inside the remaining frame buffer. A malformed -frame can advertise an IE length larger than the available data, causing -the parser to increment its pointer beyond the buffer end. This results -in out-of-bounds reads or, depending on the pattern, an infinite loop. - -Fix by validating that (offset + 2 + len) does not exceed the limit -before accepting the IE or advancing to the next element. - -This prevents OOB reads and ensures the parser terminates safely on -malformed frames. - -Signed-off-by: Navaneeth K -Cc: stable -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c -index 53d4c113b19c..df35c616e71f 100644 ---- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c -+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c -@@ -140,22 +140,24 @@ u8 *rtw_get_ie(u8 *pbuf, signed int index, signed int *len, signed int limit) - signed int tmp, i; - u8 *p; - -- if (limit < 1) -+ if (limit < 2) - return NULL; - - p = pbuf; - i = 0; - *len = 0; -- while (1) { -+ while (i + 2 <= limit) { -+ tmp = *(p + 1); -+ if (i + 2 + tmp > limit) -+ break; -+ - if (*p == index) { -- *len = *(p + 1); -+ *len = tmp; - return p; - } -- tmp = *(p + 1); -+ - p += (tmp + 2); - i += (tmp + 2); -- if (i >= limit) -- break; - } - return NULL; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68259.patch b/SPECS/kernel-rt/CVE-2025-68259.patch deleted file mode 100644 index 8c27e1639..000000000 --- a/SPECS/kernel-rt/CVE-2025-68259.patch +++ /dev/null @@ -1,203 +0,0 @@ -From 48ba05e336e086025ade1c23e913177fb4756e7d Mon Sep 17 00:00:00 2001 -From: Omar Sandoval -Date: Tue, 4 Nov 2025 09:55:26 -0800 -Subject: [PATCH 10/16] KVM: SVM: Don't skip unrelated instruction if INT3/INTO - is replaced - -When re-injecting a soft interrupt from an INT3, INT0, or (select) INTn -instruction, discard the exception and retry the instruction if the code -stream is changed (e.g. by a different vCPU) between when the CPU -executes the instruction and when KVM decodes the instruction to get the -next RIP. - -As effectively predicted by commit 6ef88d6e36c2 ("KVM: SVM: Re-inject -INT3/INTO instead of retrying the instruction"), failure to verify that -the correct INTn instruction was decoded can effectively clobber guest -state due to decoding the wrong instruction and thus specifying the -wrong next RIP. - -The bug most often manifests as "Oops: int3" panics on static branch -checks in Linux guests. Enabling or disabling a static branch in Linux -uses the kernel's "text poke" code patching mechanism. To modify code -while other CPUs may be executing that code, Linux (temporarily) -replaces the first byte of the original instruction with an int3 (opcode -0xcc), then patches in the new code stream except for the first byte, -and finally replaces the int3 with the first byte of the new code -stream. If a CPU hits the int3, i.e. executes the code while it's being -modified, then the guest kernel must look up the RIP to determine how to -handle the #BP, e.g. by emulating the new instruction. If the RIP is -incorrect, then this lookup fails and the guest kernel panics. - -The bug reproduces almost instantly by hacking the guest kernel to -repeatedly check a static branch[1] while running a drgn script[2] on -the host to constantly swap out the memory containing the guest's TSS. - -[1]: https://gist.github.com/osandov/44d17c51c28c0ac998ea0334edf90b5a -[2]: https://gist.github.com/osandov/10e45e45afa29b11e0c7209247afc00b - -Fixes: 6ef88d6e36c2 ("KVM: SVM: Re-inject INT3/INTO instead of retrying the instruction") -Cc: stable@vger.kernel.org -Co-developed-by: Sean Christopherson -Signed-off-by: Omar Sandoval -Link: https://patch.msgid.link/1cc6dcdf36e3add7ee7c8d90ad58414eeb6c3d34.1762278762.git.osandov@fb.com -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 9 +++++++++ - arch/x86/kvm/svm/svm.c | 24 +++++++++++++----------- - arch/x86/kvm/x86.c | 21 +++++++++++++++++++++ - 3 files changed, 43 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 12e7dfb0e8c4..06f1b2cedaeb 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -2135,6 +2135,11 @@ u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); - * the gfn, i.e. retrying the instruction will hit a - * !PRESENT fault, which results in a new shadow page - * and sends KVM back to square one. -+ * -+ * EMULTYPE_SKIP_SOFT_INT - Set in combination with EMULTYPE_SKIP to only skip -+ * an instruction if it could generate a given software -+ * interrupt, which must be encoded via -+ * EMULTYPE_SET_SOFT_INT_VECTOR(). - */ - #define EMULTYPE_NO_DECODE (1 << 0) - #define EMULTYPE_TRAP_UD (1 << 1) -@@ -2145,6 +2150,10 @@ u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); - #define EMULTYPE_PF (1 << 6) - #define EMULTYPE_COMPLETE_USER_EXIT (1 << 7) - #define EMULTYPE_WRITE_PF_TO_SP (1 << 8) -+#define EMULTYPE_SKIP_SOFT_INT (1 << 9) -+ -+#define EMULTYPE_SET_SOFT_INT_VECTOR(v) ((u32)((v) & 0xff) << 16) -+#define EMULTYPE_GET_SOFT_INT_VECTOR(e) (((e) >> 16) & 0xff) - - static inline bool kvm_can_emulate_event_vectoring(int emul_type) - { -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 4e97f51a4126..b85db1d65046 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -282,6 +282,7 @@ static void svm_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) - } - - static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, -+ int emul_type, - bool commit_side_effects) - { - struct vcpu_svm *svm = to_svm(vcpu); -@@ -303,7 +304,7 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, - if (unlikely(!commit_side_effects)) - old_rflags = svm->vmcb->save.rflags; - -- if (!kvm_emulate_instruction(vcpu, EMULTYPE_SKIP)) -+ if (!kvm_emulate_instruction(vcpu, emul_type)) - return 0; - - if (unlikely(!commit_side_effects)) -@@ -321,11 +322,13 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, - - static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) - { -- return __svm_skip_emulated_instruction(vcpu, true); -+ return __svm_skip_emulated_instruction(vcpu, EMULTYPE_SKIP, true); - } - --static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu) -+static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu, u8 vector) - { -+ const int emul_type = EMULTYPE_SKIP | EMULTYPE_SKIP_SOFT_INT | -+ EMULTYPE_SET_SOFT_INT_VECTOR(vector); - unsigned long rip, old_rip = kvm_rip_read(vcpu); - struct vcpu_svm *svm = to_svm(vcpu); - -@@ -341,7 +344,7 @@ static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu) - * in use, the skip must not commit any side effects such as clearing - * the interrupt shadow or RFLAGS.RF. - */ -- if (!__svm_skip_emulated_instruction(vcpu, !nrips)) -+ if (!__svm_skip_emulated_instruction(vcpu, emul_type, !nrips)) - return -EIO; - - rip = kvm_rip_read(vcpu); -@@ -377,7 +380,7 @@ static void svm_inject_exception(struct kvm_vcpu *vcpu) - kvm_deliver_exception_payload(vcpu, ex); - - if (kvm_exception_is_soft(ex->vector) && -- svm_update_soft_interrupt_rip(vcpu)) -+ svm_update_soft_interrupt_rip(vcpu, ex->vector)) - return; - - svm->vmcb->control.event_inj = ex->vector -@@ -3705,11 +3708,12 @@ static bool svm_set_vnmi_pending(struct kvm_vcpu *vcpu) - - static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) - { -+ struct kvm_queued_interrupt *intr = &vcpu->arch.interrupt; - struct vcpu_svm *svm = to_svm(vcpu); - u32 type; - -- if (vcpu->arch.interrupt.soft) { -- if (svm_update_soft_interrupt_rip(vcpu)) -+ if (intr->soft) { -+ if (svm_update_soft_interrupt_rip(vcpu, intr->nr)) - return; - - type = SVM_EVTINJ_TYPE_SOFT; -@@ -3717,12 +3721,10 @@ static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) - type = SVM_EVTINJ_TYPE_INTR; - } - -- trace_kvm_inj_virq(vcpu->arch.interrupt.nr, -- vcpu->arch.interrupt.soft, reinjected); -+ trace_kvm_inj_virq(intr->nr, intr->soft, reinjected); - ++vcpu->stat.irq_injections; - -- svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr | -- SVM_EVTINJ_VALID | type; -+ svm->vmcb->control.event_inj = intr->nr | SVM_EVTINJ_VALID | type; - } - - void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a152b00efcce..dd4fdbfa7901 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9305,6 +9305,23 @@ static bool is_vmware_backdoor_opcode(struct x86_emulate_ctxt *ctxt) - return false; - } - -+static bool is_soft_int_instruction(struct x86_emulate_ctxt *ctxt, -+ int emulation_type) -+{ -+ u8 vector = EMULTYPE_GET_SOFT_INT_VECTOR(emulation_type); -+ -+ switch (ctxt->b) { -+ case 0xcc: -+ return vector == BP_VECTOR; -+ case 0xcd: -+ return vector == ctxt->src.val; -+ case 0xce: -+ return vector == OF_VECTOR; -+ default: -+ return false; -+ } -+} -+ - /* - * Decode an instruction for emulation. The caller is responsible for handling - * code breakpoints. Note, manually detecting code breakpoints is unnecessary -@@ -9415,6 +9432,10 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - * injecting single-step #DBs. - */ - if (emulation_type & EMULTYPE_SKIP) { -+ if (emulation_type & EMULTYPE_SKIP_SOFT_INT && -+ !is_soft_int_instruction(ctxt, emulation_type)) -+ return 0; -+ - if (ctxt->mode != X86EMUL_MODE_PROT64) - ctxt->eip = (u32)ctxt->_eip; - else --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68261.patch b/SPECS/kernel-rt/CVE-2025-68261.patch deleted file mode 100644 index b622bd2a3..000000000 --- a/SPECS/kernel-rt/CVE-2025-68261.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 6d247f2c40f1d8fb51c7a5b76a282bdec27b4e2f Mon Sep 17 00:00:00 2001 -From: Alexey Nepomnyashih -Date: Tue, 4 Nov 2025 09:33:25 +0000 -Subject: [PATCH 09/16] ext4: add i_data_sem protection in - ext4_destroy_inline_data_nolock() - -Fix a race between inline data destruction and block mapping. - -The function ext4_destroy_inline_data_nolock() changes the inode data -layout by clearing EXT4_INODE_INLINE_DATA and setting EXT4_INODE_EXTENTS. -At the same time, another thread may execute ext4_map_blocks(), which -tests EXT4_INODE_EXTENTS to decide whether to call ext4_ext_map_blocks() -or ext4_ind_map_blocks(). - -Without i_data_sem protection, ext4_ind_map_blocks() may receive inode -with EXT4_INODE_EXTENTS flag and triggering assert. - -kernel BUG at fs/ext4/indirect.c:546! -EXT4-fs (loop2): unmounting filesystem. -invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI -Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 -RIP: 0010:ext4_ind_map_blocks.cold+0x2b/0x5a fs/ext4/indirect.c:546 - -Call Trace: - - ext4_map_blocks+0xb9b/0x16f0 fs/ext4/inode.c:681 - _ext4_get_block+0x242/0x590 fs/ext4/inode.c:822 - ext4_block_write_begin+0x48b/0x12c0 fs/ext4/inode.c:1124 - ext4_write_begin+0x598/0xef0 fs/ext4/inode.c:1255 - ext4_da_write_begin+0x21e/0x9c0 fs/ext4/inode.c:3000 - generic_perform_write+0x259/0x5d0 mm/filemap.c:3846 - ext4_buffered_write_iter+0x15b/0x470 fs/ext4/file.c:285 - ext4_file_write_iter+0x8e0/0x17f0 fs/ext4/file.c:679 - call_write_iter include/linux/fs.h:2271 [inline] - do_iter_readv_writev+0x212/0x3c0 fs/read_write.c:735 - do_iter_write+0x186/0x710 fs/read_write.c:861 - vfs_iter_write+0x70/0xa0 fs/read_write.c:902 - iter_file_splice_write+0x73b/0xc90 fs/splice.c:685 - do_splice_from fs/splice.c:763 [inline] - direct_splice_actor+0x10f/0x170 fs/splice.c:950 - splice_direct_to_actor+0x33a/0xa10 fs/splice.c:896 - do_splice_direct+0x1a9/0x280 fs/splice.c:1002 - do_sendfile+0xb13/0x12c0 fs/read_write.c:1255 - __do_sys_sendfile64 fs/read_write.c:1323 [inline] - __se_sys_sendfile64 fs/read_write.c:1309 [inline] - __x64_sys_sendfile64+0x1cf/0x210 fs/read_write.c:1309 - do_syscall_x64 arch/x86/entry/common.c:51 [inline] - do_syscall_64+0x35/0x80 arch/x86/entry/common.c:81 - entry_SYSCALL_64_after_hwframe+0x6e/0xd8 - -Fixes: c755e251357a ("ext4: fix deadlock between inline_data and ext4_expand_extra_isize_ea()") -Cc: stable@vger.kernel.org # v4.11+ -Signed-off-by: Alexey Nepomnyashih -Message-ID: <20251104093326.697381-1-sdl@nppct.ru> -Signed-off-by: Theodore Ts'o ---- - fs/ext4/inline.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c -index 1b094a4f3866..ef7821f7fd26 100644 ---- a/fs/ext4/inline.c -+++ b/fs/ext4/inline.c -@@ -446,9 +446,13 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, - if (!ei->i_inline_off) - return 0; - -+ down_write(&ei->i_data_sem); -+ - error = ext4_get_inode_loc(inode, &is.iloc); -- if (error) -+ if (error) { -+ up_write(&ei->i_data_sem); - return error; -+ } - - error = ext4_xattr_ibody_find(inode, &i, &is); - if (error) -@@ -487,6 +491,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, - brelse(is.iloc.bh); - if (error == -ENODATA) - error = 0; -+ up_write(&ei->i_data_sem); - return error; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68262.patch b/SPECS/kernel-rt/CVE-2025-68262.patch deleted file mode 100644 index 94c68b7aa..000000000 --- a/SPECS/kernel-rt/CVE-2025-68262.patch +++ /dev/null @@ -1,85 +0,0 @@ -From aa3da9b470ef096347e14c1df537c4430c672027 Mon Sep 17 00:00:00 2001 -From: Giovanni Cabiddu -Date: Thu, 20 Nov 2025 16:26:09 +0000 -Subject: [PATCH 08/16] crypto: zstd - fix double-free in per-CPU stream - cleanup - -The crypto/zstd module has a double-free bug that occurs when multiple -tfms are allocated and freed. - -The issue happens because zstd_streams (per-CPU contexts) are freed in -zstd_exit() during every tfm destruction, rather than being managed at -the module level. When multiple tfms exist, each tfm exit attempts to -free the same shared per-CPU streams, resulting in a double-free. - -This leads to a stack trace similar to: - - BUG: Bad page state in process kworker/u16:1 pfn:106fd93 - page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x106fd93 - flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) - page_type: 0xffffffff() - raw: 0017ffffc0000000 dead000000000100 dead000000000122 0000000000000000 - raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000 - page dumped because: nonzero entire_mapcount - Modules linked in: ... - CPU: 3 UID: 0 PID: 2506 Comm: kworker/u16:1 Kdump: loaded Tainted: G B - Hardware name: ... - Workqueue: btrfs-delalloc btrfs_work_helper - Call Trace: - - dump_stack_lvl+0x5d/0x80 - bad_page+0x71/0xd0 - free_unref_page_prepare+0x24e/0x490 - free_unref_page+0x60/0x170 - crypto_acomp_free_streams+0x5d/0xc0 - crypto_acomp_exit_tfm+0x23/0x50 - crypto_destroy_tfm+0x60/0xc0 - ... - -Change the lifecycle management of zstd_streams to free the streams only -once during module cleanup. - -Fixes: f5ad93ffb541 ("crypto: zstd - convert to acomp") -Cc: stable@vger.kernel.org -Signed-off-by: Giovanni Cabiddu -Reviewed-by: Suman Kumar Chakraborty -Signed-off-by: Herbert Xu ---- - crypto/zstd.c | 7 +------ - 1 file changed, 1 insertion(+), 6 deletions(-) - -diff --git a/crypto/zstd.c b/crypto/zstd.c -index ac318d333b68..32a339b74f34 100644 ---- a/crypto/zstd.c -+++ b/crypto/zstd.c -@@ -75,11 +75,6 @@ static int zstd_init(struct crypto_acomp *acomp_tfm) - return ret; - } - --static void zstd_exit(struct crypto_acomp *acomp_tfm) --{ -- crypto_acomp_free_streams(&zstd_streams); --} -- - static int zstd_compress_one(struct acomp_req *req, struct zstd_ctx *ctx, - const void *src, void *dst, unsigned int *dlen) - { -@@ -297,7 +292,6 @@ static struct acomp_alg zstd_acomp = { - .cra_module = THIS_MODULE, - }, - .init = zstd_init, -- .exit = zstd_exit, - .compress = zstd_compress, - .decompress = zstd_decompress, - }; -@@ -310,6 +304,7 @@ static int __init zstd_mod_init(void) - static void __exit zstd_mod_fini(void) - { - crypto_unregister_acomp(&zstd_acomp); -+ crypto_acomp_free_streams(&zstd_streams); - } - - module_init(zstd_mod_init); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68263.patch b/SPECS/kernel-rt/CVE-2025-68263.patch deleted file mode 100644 index d90eb1ea4..000000000 --- a/SPECS/kernel-rt/CVE-2025-68263.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 949657704ce45da9a394966c0e19c5f383f192fc Mon Sep 17 00:00:00 2001 -From: Qianchang Zhao -Date: Wed, 26 Nov 2025 12:24:18 +0900 -Subject: [PATCH 02/16] ksmbd: ipc: fix use-after-free in ipc_msg_send_request - -ipc_msg_send_request() waits for a generic netlink reply using an -ipc_msg_table_entry on the stack. The generic netlink handler -(handle_generic_event()/handle_response()) fills entry->response under -ipc_msg_table_lock, but ipc_msg_send_request() used to validate and free -entry->response without holding the same lock. - -Under high concurrency this allows a race where handle_response() is -copying data into entry->response while ipc_msg_send_request() has just -freed it, leading to a slab-use-after-free reported by KASAN in -handle_generic_event(): - - BUG: KASAN: slab-use-after-free in handle_generic_event+0x3c4/0x5f0 [ksmbd] - Write of size 12 at addr ffff888198ee6e20 by task pool/109349 - ... - Freed by task: - kvfree - ipc_msg_send_request [ksmbd] - ksmbd_rpc_open -> ksmbd_session_rpc_open [ksmbd] - -Fix by: -- Taking ipc_msg_table_lock in ipc_msg_send_request() while validating - entry->response, freeing it when invalid, and removing the entry from - ipc_msg_table. -- Returning the final entry->response pointer to the caller only after - the hash entry is removed under the lock. -- Returning NULL in the error path, preserving the original API - semantics. - -This makes all accesses to entry->response consistent with -handle_response(), which already updates and fills the response buffer -under ipc_msg_table_lock, and closes the race that allowed the UAF. - -Cc: stable@vger.kernel.org -Reported-by: Qianchang Zhao -Reported-by: Zhitong Liu -Signed-off-by: Qianchang Zhao -Acked-by: Namjae Jeon -Signed-off-by: Steve French ---- - fs/smb/server/transport_ipc.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c -index 2c08cccfa680..2dbabe2d8005 100644 ---- a/fs/smb/server/transport_ipc.c -+++ b/fs/smb/server/transport_ipc.c -@@ -553,12 +553,16 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle - up_write(&ipc_msg_table_lock); - - ret = ipc_msg_send(msg); -- if (ret) -+ if (ret) { -+ down_write(&ipc_msg_table_lock); - goto out; -+ } - - ret = wait_event_interruptible_timeout(entry.wait, - entry.response != NULL, - IPC_WAIT_TIMEOUT); -+ -+ down_write(&ipc_msg_table_lock); - if (entry.response) { - ret = ipc_validate_msg(&entry); - if (ret) { -@@ -567,7 +571,6 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle - } - } - out: -- down_write(&ipc_msg_table_lock); - hash_del(&entry.ipc_table_hlist); - up_write(&ipc_msg_table_lock); - return entry.response; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68264.patch b/SPECS/kernel-rt/CVE-2025-68264.patch deleted file mode 100644 index b67af4abb..000000000 --- a/SPECS/kernel-rt/CVE-2025-68264.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 3a19a33f01c018e91e4a04519d9b6ff73f1c5a34 Mon Sep 17 00:00:00 2001 -From: Deepanshu Kartikey -Date: Mon, 20 Oct 2025 11:39:36 +0530 -Subject: [PATCH 13/16] ext4: refresh inline data size before write operations - -The cached ei->i_inline_size can become stale between the initial size -check and when ext4_update_inline_data()/ext4_create_inline_data() use -it. Although ext4_get_max_inline_size() reads the correct value at the -time of the check, concurrent xattr operations can modify i_inline_size -before ext4_write_lock_xattr() is acquired. - -This causes ext4_update_inline_data() and ext4_create_inline_data() to -work with stale capacity values, leading to a BUG_ON() crash in -ext4_write_inline_data(): - - kernel BUG at fs/ext4/inline.c:1331! - BUG_ON(pos + len > EXT4_I(inode)->i_inline_size); - -The race window: -1. ext4_get_max_inline_size() reads i_inline_size = 60 (correct) -2. Size check passes for 50-byte write -3. [Another thread adds xattr, i_inline_size changes to 40] -4. ext4_write_lock_xattr() acquires lock -5. ext4_update_inline_data() uses stale i_inline_size = 60 -6. Attempts to write 50 bytes but only 40 bytes actually available -7. BUG_ON() triggers - -Fix this by recalculating i_inline_size via ext4_find_inline_data_nolock() -immediately after acquiring xattr_sem. This ensures ext4_update_inline_data() -and ext4_create_inline_data() work with current values that are protected -from concurrent modifications. - -This is similar to commit a54c4613dac1 ("ext4: fix race writing to an -inline_data file while its xattrs are changing") which fixed i_inline_off -staleness. This patch addresses the related i_inline_size staleness issue. - -Reported-by: syzbot+f3185be57d7e8dda32b8@syzkaller.appspotmail.com -Link: https://syzkaller.appspot.com/bug?extid=f3185be57d7e8dda32b8 -Cc: stable@kernel.org -Signed-off-by: Deepanshu Kartikey -Message-ID: <20251020060936.474314-1-kartikey406@gmail.com> -Signed-off-by: Theodore Ts'o ---- - fs/ext4/inline.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c -index ef7821f7fd26..1f6bc05593df 100644 ---- a/fs/ext4/inline.c -+++ b/fs/ext4/inline.c -@@ -418,7 +418,12 @@ static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, - return -ENOSPC; - - ext4_write_lock_xattr(inode, &no_expand); -- -+ /* -+ * ei->i_inline_size may have changed since the initial check -+ * if other xattrs were added. Recalculate to ensure -+ * ext4_update_inline_data() validates against current capacity. -+ */ -+ (void) ext4_find_inline_data_nolock(inode); - if (ei->i_inline_off) - ret = ext4_update_inline_data(handle, inode, len); - else --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68265.patch b/SPECS/kernel-rt/CVE-2025-68265.patch deleted file mode 100644 index 4bda50400..000000000 --- a/SPECS/kernel-rt/CVE-2025-68265.patch +++ /dev/null @@ -1,93 +0,0 @@ -From dab5fec018f0974102e33357731bde1fdf207406 Mon Sep 17 00:00:00 2001 -From: Keith Busch -Date: Tue, 4 Nov 2025 14:48:30 -0800 -Subject: [PATCH 01/16] nvme: fix admin request_queue lifetime - -The namespaces can access the controller's admin request_queue, and -stale references on the namespaces may exist after tearing down the -controller. Ensure the admin request_queue is active by moving the -controller's 'put' to after all controller references have been released -to ensure no one is can access the request_queue. This fixes a reported -use-after-free bug: - - BUG: KASAN: slab-use-after-free in blk_queue_enter+0x41c/0x4a0 - Read of size 8 at addr ffff88c0a53819f8 by task nvme/3287 - CPU: 67 UID: 0 PID: 3287 Comm: nvme Tainted: G E 6.13.2-ga1582f1a031e #15 - Tainted: [E]=UNSIGNED_MODULE - Hardware name: Jabil /EGS 2S MB1, BIOS 1.00 06/18/2025 - Call Trace: - - dump_stack_lvl+0x4f/0x60 - print_report+0xc4/0x620 - ? _raw_spin_lock_irqsave+0x70/0xb0 - ? _raw_read_unlock_irqrestore+0x30/0x30 - ? blk_queue_enter+0x41c/0x4a0 - kasan_report+0xab/0xe0 - ? blk_queue_enter+0x41c/0x4a0 - blk_queue_enter+0x41c/0x4a0 - ? __irq_work_queue_local+0x75/0x1d0 - ? blk_queue_start_drain+0x70/0x70 - ? irq_work_queue+0x18/0x20 - ? vprintk_emit.part.0+0x1cc/0x350 - ? wake_up_klogd_work_func+0x60/0x60 - blk_mq_alloc_request+0x2b7/0x6b0 - ? __blk_mq_alloc_requests+0x1060/0x1060 - ? __switch_to+0x5b7/0x1060 - nvme_submit_user_cmd+0xa9/0x330 - nvme_user_cmd.isra.0+0x240/0x3f0 - ? force_sigsegv+0xe0/0xe0 - ? nvme_user_cmd64+0x400/0x400 - ? vfs_fileattr_set+0x9b0/0x9b0 - ? cgroup_update_frozen_flag+0x24/0x1c0 - ? cgroup_leave_frozen+0x204/0x330 - ? nvme_ioctl+0x7c/0x2c0 - blkdev_ioctl+0x1a8/0x4d0 - ? blkdev_common_ioctl+0x1930/0x1930 - ? fdget+0x54/0x380 - __x64_sys_ioctl+0x129/0x190 - do_syscall_64+0x5b/0x160 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - RIP: 0033:0x7f765f703b0b - Code: ff ff ff 85 c0 79 9b 49 c7 c4 ff ff ff ff 5b 5d 4c 89 e0 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d dd 52 0f 00 f7 d8 64 89 01 48 - RSP: 002b:00007ffe2cefe808 EFLAGS: 00000202 ORIG_RAX: 0000000000000010 - RAX: ffffffffffffffda RBX: 00007ffe2cefe860 RCX: 00007f765f703b0b - RDX: 00007ffe2cefe860 RSI: 00000000c0484e41 RDI: 0000000000000003 - RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000000 - R10: 00007f765f611d50 R11: 0000000000000202 R12: 0000000000000003 - R13: 00000000c0484e41 R14: 0000000000000001 R15: 00007ffe2cefea60 - - -Reported-by: Casey Chen -Reviewed-by: Christoph Hellwig -Reviewed-by: Hannes Reinecke -Reviewed-by: Ming Lei -Reviewed-by: Chaitanya Kulkarni -Signed-off-by: Keith Busch ---- - drivers/nvme/host/core.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c -index 5714d4993282..28c598008124 100644 ---- a/drivers/nvme/host/core.c -+++ b/drivers/nvme/host/core.c -@@ -4896,7 +4896,6 @@ void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl) - */ - nvme_stop_keep_alive(ctrl); - blk_mq_destroy_queue(ctrl->admin_q); -- blk_put_queue(ctrl->admin_q); - if (ctrl->ops->flags & NVME_F_FABRICS) { - blk_mq_destroy_queue(ctrl->fabrics_q); - blk_put_queue(ctrl->fabrics_q); -@@ -5040,6 +5039,8 @@ static void nvme_free_ctrl(struct device *dev) - container_of(dev, struct nvme_ctrl, ctrl_device); - struct nvme_subsystem *subsys = ctrl->subsys; - -+ if (ctrl->admin_q) -+ blk_put_queue(ctrl->admin_q); - if (!subsys || ctrl->instance != subsys->instance) - ida_free(&nvme_instance_ida, ctrl->instance); - nvme_free_cels(ctrl); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68281.patch b/SPECS/kernel-rt/CVE-2025-68281.patch deleted file mode 100644 index 2105677bc..000000000 --- a/SPECS/kernel-rt/CVE-2025-68281.patch +++ /dev/null @@ -1,36 +0,0 @@ -From b657ebe7d692c906e63577986cf754d38ec5610f Mon Sep 17 00:00:00 2001 -From: Niranjan H Y -Date: Mon, 10 Nov 2025 20:56:46 +0530 -Subject: [PATCH 07/16] ASoC: SDCA: bug fix while parsing - mipi-sdca-control-cn-list - -"struct sdca_control" declares "values" field as integer array. -But the memory allocated to it is of char array. This causes -crash for sdca_parse_function API. This patch addresses the -issue by allocating correct data size. - -Signed-off-by: Niranjan H Y -Reviewed-by: Charles Keepax -Link: https://patch.msgid.link/20251110152646.192-1-niranjan.hy@ti.com -Signed-off-by: Mark Brown ---- - sound/soc/sdca/sdca_functions.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c -index 13f68f7b6dd6..0ccb6775f4de 100644 ---- a/sound/soc/sdca/sdca_functions.c -+++ b/sound/soc/sdca/sdca_functions.c -@@ -894,7 +894,8 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti - return ret; - } - -- control->values = devm_kzalloc(dev, hweight64(control->cn_list), GFP_KERNEL); -+ control->values = devm_kcalloc(dev, hweight64(control->cn_list), -+ sizeof(int), GFP_KERNEL); - if (!control->values) - return -ENOMEM; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68323.patch b/SPECS/kernel-rt/CVE-2025-68323.patch deleted file mode 100644 index db3cd6eb8..000000000 --- a/SPECS/kernel-rt/CVE-2025-68323.patch +++ /dev/null @@ -1,136 +0,0 @@ -From b9320969035ba986d397638c4fdbfc247140f217 Mon Sep 17 00:00:00 2001 -From: Duoming Zhou -Date: Tue, 25 Nov 2025 18:36:27 +0800 -Subject: [PATCH 16/16] usb: typec: ucsi: fix use-after-free caused by - uec->work - -The delayed work uec->work is scheduled in gaokun_ucsi_probe() -but never properly canceled in gaokun_ucsi_remove(). This creates -use-after-free scenarios where the ucsi and gaokun_ucsi structure -are freed after ucsi_destroy() completes execution, while the -gaokun_ucsi_register_worker() might be either currently executing -or still pending in the work queue. The already-freed gaokun_ucsi -or ucsi structure may then be accessed. - -Furthermore, the race window is 3 seconds, which is sufficiently -long to make this bug easily reproducible. The following is the -trace captured by KASAN: - -================================================================== -BUG: KASAN: slab-use-after-free in __run_timers+0x5ec/0x630 -Write of size 8 at addr ffff00000ec28cc8 by task swapper/0/0 -... -Call trace: - show_stack+0x18/0x24 (C) - dump_stack_lvl+0x78/0x90 - print_report+0x114/0x580 - kasan_report+0xa4/0xf0 - __asan_report_store8_noabort+0x20/0x2c - __run_timers+0x5ec/0x630 - run_timer_softirq+0xe8/0x1cc - handle_softirqs+0x294/0x720 - __do_softirq+0x14/0x20 - ____do_softirq+0x10/0x1c - call_on_irq_stack+0x30/0x48 - do_softirq_own_stack+0x1c/0x28 - __irq_exit_rcu+0x27c/0x364 - irq_exit_rcu+0x10/0x1c - el1_interrupt+0x40/0x60 - el1h_64_irq_handler+0x18/0x24 - el1h_64_irq+0x6c/0x70 - arch_local_irq_enable+0x4/0x8 (P) - do_idle+0x334/0x458 - cpu_startup_entry+0x60/0x70 - rest_init+0x158/0x174 - start_kernel+0x2f8/0x394 - __primary_switched+0x8c/0x94 - -Allocated by task 72 on cpu 0 at 27.510341s: - kasan_save_stack+0x2c/0x54 - kasan_save_track+0x24/0x5c - kasan_save_alloc_info+0x40/0x54 - __kasan_kmalloc+0xa0/0xb8 - __kmalloc_node_track_caller_noprof+0x1c0/0x588 - devm_kmalloc+0x7c/0x1c8 - gaokun_ucsi_probe+0xa0/0x840 auxiliary_bus_probe+0x94/0xf8 - really_probe+0x17c/0x5b8 - __driver_probe_device+0x158/0x2c4 - driver_probe_device+0x10c/0x264 - __device_attach_driver+0x168/0x2d0 - bus_for_each_drv+0x100/0x188 - __device_attach+0x174/0x368 - device_initial_probe+0x14/0x20 - bus_probe_device+0x120/0x150 - device_add+0xb3c/0x10fc - __auxiliary_device_add+0x88/0x130 -... - -Freed by task 73 on cpu 1 at 28.910627s: - kasan_save_stack+0x2c/0x54 - kasan_save_track+0x24/0x5c - __kasan_save_free_info+0x4c/0x74 - __kasan_slab_free+0x60/0x8c - kfree+0xd4/0x410 - devres_release_all+0x140/0x1f0 - device_unbind_cleanup+0x20/0x190 - device_release_driver_internal+0x344/0x460 - device_release_driver+0x18/0x24 - bus_remove_device+0x198/0x274 - device_del+0x310/0xa84 -... - -The buggy address belongs to the object at ffff00000ec28c00 - which belongs to the cache kmalloc-512 of size 512 -The buggy address is located 200 bytes inside of - freed 512-byte region -The buggy address belongs to the physical page: -page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x4ec28 -head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 -flags: 0x3fffe0000000040(head|node=0|zone=0|lastcpupid=0x1ffff) -page_type: f5(slab) -raw: 03fffe0000000040 ffff000008801c80 dead000000000122 0000000000000000 -raw: 0000000000000000 0000000080100010 00000000f5000000 0000000000000000 -head: 03fffe0000000040 ffff000008801c80 dead000000000122 0000000000000000 -head: 0000000000000000 0000000080100010 00000000f5000000 0000000000000000 -head: 03fffe0000000002 fffffdffc03b0a01 00000000ffffffff 00000000ffffffff -head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000004 -page dumped because: kasan: bad access detected - -Memory state around the buggy address: - ffff00000ec28b80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc - ffff00000ec28c00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ->ffff00000ec28c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb - ^ - ffff00000ec28d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb - ffff00000ec28d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb -================================================================== - -Add disable_delayed_work_sync() in gaokun_ucsi_remove() to ensure -that uec->work is properly canceled and prevented from executing -after the ucsi and gaokun_ucsi structure have been deallocated. - -Fixes: 00327d7f2c8c ("usb: typec: ucsi: add Huawei Matebook E Go ucsi driver") -Cc: stable -Signed-off-by: Duoming Zhou -Reviewed-by: Heikki Krogerus -Link: https://patch.msgid.link/cc31e12ef9ffbf86676585b02233165fd33f0d8e.1764065838.git.duoming@zju.edu.cn -Signed-off-by: Greg Kroah-Hartman ---- - drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c -index 7b5222081bbb..d6d82be35675 100644 ---- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c -+++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c -@@ -502,6 +502,7 @@ static void gaokun_ucsi_remove(struct auxiliary_device *adev) - { - struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); - -+ disable_delayed_work_sync(&uec->work); - gaokun_ec_unregister_notify(uec->ec, &uec->nb); - ucsi_unregister(uec->ucsi); - ucsi_destroy(uec->ucsi); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68325.patch b/SPECS/kernel-rt/CVE-2025-68325.patch deleted file mode 100644 index 0ac23abeb..000000000 --- a/SPECS/kernel-rt/CVE-2025-68325.patch +++ /dev/null @@ -1,154 +0,0 @@ -From 6d2cb43e5e4a6d4f72e046d2caa4f402124feea7 Mon Sep 17 00:00:00 2001 -From: Xiang Mei -Date: Thu, 27 Nov 2025 17:14:14 -0700 -Subject: [PATCH 14/16] net/sched: sch_cake: Fix incorrect qlen reduction in - cake_drop -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In cake_drop(), qdisc_tree_reduce_backlog() is used to update the qlen -and backlog of the qdisc hierarchy. Its caller, cake_enqueue(), assumes -that the parent qdisc will enqueue the current packet. However, this -assumption breaks when cake_enqueue() returns NET_XMIT_CN: the parent -qdisc stops enqueuing current packet, leaving the tree qlen/backlog -accounting inconsistent. This mismatch can lead to a NULL dereference -(e.g., when the parent Qdisc is qfq_qdisc). - -This patch computes the qlen/backlog delta in a more robust way by -observing the difference before and after the series of cake_drop() -calls, and then compensates the qdisc tree accounting if cake_enqueue() -returns NET_XMIT_CN. - -To ensure correct compensation when ACK thinning is enabled, a new -variable is introduced to keep qlen unchanged. - -Fixes: 15de71d06a40 ("net/sched: Make cake_enqueue return NET_XMIT_CN when past buffer_limit") -Signed-off-by: Xiang Mei -Reviewed-by: Toke Høiland-Jørgensen -Link: https://patch.msgid.link/20251128001415.377823-1-xmei5@asu.edu -Signed-off-by: Paolo Abeni ---- - net/sched/sch_cake.c | 58 ++++++++++++++++++++++++-------------------- - 1 file changed, 32 insertions(+), 26 deletions(-) - -diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c -index 32bacfc314c2..d325a90cde9e 100644 ---- a/net/sched/sch_cake.c -+++ b/net/sched/sch_cake.c -@@ -1597,7 +1597,6 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free) - - qdisc_drop_reason(skb, sch, to_free, SKB_DROP_REASON_QDISC_OVERLIMIT); - sch->q.qlen--; -- qdisc_tree_reduce_backlog(sch, 1, len); - - cake_heapify(q, 0); - -@@ -1743,14 +1742,14 @@ static void cake_reconfigure(struct Qdisc *sch); - static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - struct sk_buff **to_free) - { -+ u32 idx, tin, prev_qlen, prev_backlog, drop_id; - struct cake_sched_data *q = qdisc_priv(sch); -- int len = qdisc_pkt_len(skb); -- int ret; -+ int len = qdisc_pkt_len(skb), ret; - struct sk_buff *ack = NULL; - ktime_t now = ktime_get(); - struct cake_tin_data *b; - struct cake_flow *flow; -- u32 idx, tin; -+ bool same_flow = false; - - /* choose flow to insert into */ - idx = cake_classify(sch, &b, skb, q->flow_mode, &ret); -@@ -1823,6 +1822,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - consume_skb(skb); - } else { - /* not splitting */ -+ int ack_pkt_len = 0; -+ - cobalt_set_enqueue_time(skb, now); - get_cobalt_cb(skb)->adjusted_len = cake_overhead(q, skb); - flow_queue_add(flow, skb); -@@ -1833,13 +1834,13 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - if (ack) { - b->ack_drops++; - sch->qstats.drops++; -- b->bytes += qdisc_pkt_len(ack); -- len -= qdisc_pkt_len(ack); -+ ack_pkt_len = qdisc_pkt_len(ack); -+ b->bytes += ack_pkt_len; - q->buffer_used += skb->truesize - ack->truesize; - if (q->rate_flags & CAKE_FLAG_INGRESS) - cake_advance_shaper(q, b, ack, now, true); - -- qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(ack)); -+ qdisc_tree_reduce_backlog(sch, 1, ack_pkt_len); - consume_skb(ack); - } else { - sch->q.qlen++; -@@ -1848,11 +1849,11 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - - /* stats */ - b->packets++; -- b->bytes += len; -- b->backlogs[idx] += len; -- b->tin_backlog += len; -- sch->qstats.backlog += len; -- q->avg_window_bytes += len; -+ b->bytes += len - ack_pkt_len; -+ b->backlogs[idx] += len - ack_pkt_len; -+ b->tin_backlog += len - ack_pkt_len; -+ sch->qstats.backlog += len - ack_pkt_len; -+ q->avg_window_bytes += len - ack_pkt_len; - } - - if (q->overflow_timeout) -@@ -1927,24 +1928,29 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - if (q->buffer_used > q->buffer_max_used) - q->buffer_max_used = q->buffer_used; - -- if (q->buffer_used > q->buffer_limit) { -- bool same_flow = false; -- u32 dropped = 0; -- u32 drop_id; -+ if (q->buffer_used <= q->buffer_limit) -+ return NET_XMIT_SUCCESS; - -- while (q->buffer_used > q->buffer_limit) { -- dropped++; -- drop_id = cake_drop(sch, to_free); -+ prev_qlen = sch->q.qlen; -+ prev_backlog = sch->qstats.backlog; - -- if ((drop_id >> 16) == tin && -- (drop_id & 0xFFFF) == idx) -- same_flow = true; -- } -- b->drop_overlimit += dropped; -+ while (q->buffer_used > q->buffer_limit) { -+ drop_id = cake_drop(sch, to_free); -+ if ((drop_id >> 16) == tin && -+ (drop_id & 0xFFFF) == idx) -+ same_flow = true; -+ } -+ -+ prev_qlen -= sch->q.qlen; -+ prev_backlog -= sch->qstats.backlog; -+ b->drop_overlimit += prev_qlen; - -- if (same_flow) -- return NET_XMIT_CN; -+ if (same_flow) { -+ qdisc_tree_reduce_backlog(sch, prev_qlen - 1, -+ prev_backlog - len); -+ return NET_XMIT_CN; - } -+ qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog); - return NET_XMIT_SUCCESS; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68333.patch b/SPECS/kernel-rt/CVE-2025-68333.patch deleted file mode 100644 index 0c2bc6e52..000000000 --- a/SPECS/kernel-rt/CVE-2025-68333.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 8c4125e4f8e5d21f655964e52355ae29c369170d Mon Sep 17 00:00:00 2001 -From: Zqiang -Date: Thu, 13 Nov 2025 19:43:55 +0800 -Subject: [PATCH 17/51] sched_ext: Fix possible deadlock in the - deferred_irq_workfn() - -For PREEMPT_RT=y kernels, the deferred_irq_workfn() is executed in -the per-cpu irq_work/* task context and not disable-irq, if the rq -returned by container_of() is current CPU's rq, the following scenarios -may occur: - -lock(&rq->__lock); - - lock(&rq->__lock); - -This commit use IRQ_WORK_INIT_HARD() to replace init_irq_work() to -initialize rq->scx.deferred_irq_work, make the deferred_irq_workfn() -is always invoked in hard-irq context. - -Signed-off-by: Zqiang -Signed-off-by: Tejun Heo ---- - kernel/sched/ext.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c -index 1e4740de66c2..16a7ae9b29ae 100644 ---- a/kernel/sched/ext.c -+++ b/kernel/sched/ext.c -@@ -5377,7 +5377,7 @@ void __init init_sched_ext_class(void) - BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_kick_if_idle, GFP_KERNEL, n)); - BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_preempt, GFP_KERNEL, n)); - BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_wait, GFP_KERNEL, n)); -- init_irq_work(&rq->scx.deferred_irq_work, deferred_irq_workfn); -+ rq->scx.deferred_irq_work = IRQ_WORK_INIT_HARD(deferred_irq_workfn); - init_irq_work(&rq->scx.kick_cpus_irq_work, kick_cpus_irq_workfn); - - if (cpu_online(cpu)) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68336.patch b/SPECS/kernel-rt/CVE-2025-68336.patch deleted file mode 100644 index 90d508203..000000000 --- a/SPECS/kernel-rt/CVE-2025-68336.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 0e5be7270b58b9bcee83584be1eac03cd0845eba Mon Sep 17 00:00:00 2001 -From: Alexander Sverdlin -Date: Fri, 19 Sep 2025 11:12:38 +0200 -Subject: [PATCH 18/51] locking/spinlock/debug: Fix data-race in - do_raw_write_lock - -KCSAN reports: - -BUG: KCSAN: data-race in do_raw_write_lock / do_raw_write_lock - -write (marked) to 0xffff800009cf504c of 4 bytes by task 1102 on cpu 1: - do_raw_write_lock+0x120/0x204 - _raw_write_lock_irq - do_exit - call_usermodehelper_exec_async - ret_from_fork - -read to 0xffff800009cf504c of 4 bytes by task 1103 on cpu 0: - do_raw_write_lock+0x88/0x204 - _raw_write_lock_irq - do_exit - call_usermodehelper_exec_async - ret_from_fork - -value changed: 0xffffffff -> 0x00000001 - -Reported by Kernel Concurrency Sanitizer on: -CPU: 0 PID: 1103 Comm: kworker/u4:1 6.1.111 - -Commit 1a365e822372 ("locking/spinlock/debug: Fix various data races") has -adressed most of these races, but seems to be not consistent/not complete. - ->From do_raw_write_lock() only debug_write_lock_after() part has been -converted to WRITE_ONCE(), but not debug_write_lock_before() part. -Do it now. - -Fixes: 1a365e822372 ("locking/spinlock/debug: Fix various data races") -Reported-by: Adrian Freihofer -Signed-off-by: Alexander Sverdlin -Signed-off-by: Boqun Feng -Signed-off-by: Peter Zijlstra (Intel) -Reviewed-by: Paul E. McKenney -Acked-by: Waiman Long -Cc: stable@vger.kernel.org ---- - kernel/locking/spinlock_debug.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c -index 87b03d2e41db..2338b3adfb55 100644 ---- a/kernel/locking/spinlock_debug.c -+++ b/kernel/locking/spinlock_debug.c -@@ -184,8 +184,8 @@ void do_raw_read_unlock(rwlock_t *lock) - static inline void debug_write_lock_before(rwlock_t *lock) - { - RWLOCK_BUG_ON(lock->magic != RWLOCK_MAGIC, lock, "bad magic"); -- RWLOCK_BUG_ON(lock->owner == current, lock, "recursion"); -- RWLOCK_BUG_ON(lock->owner_cpu == raw_smp_processor_id(), -+ RWLOCK_BUG_ON(READ_ONCE(lock->owner) == current, lock, "recursion"); -+ RWLOCK_BUG_ON(READ_ONCE(lock->owner_cpu) == raw_smp_processor_id(), - lock, "cpu recursion"); - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68337.patch b/SPECS/kernel-rt/CVE-2025-68337.patch deleted file mode 100644 index 19b159468..000000000 --- a/SPECS/kernel-rt/CVE-2025-68337.patch +++ /dev/null @@ -1,96 +0,0 @@ -From f5085db4ff19b30506acecd97e8db9f55f819488 Mon Sep 17 00:00:00 2001 -From: Ye Bin -Date: Sat, 25 Oct 2025 15:26:57 +0800 -Subject: [PATCH 27/51] jbd2: avoid bug_on in jbd2_journal_get_create_access() - when file system corrupted - -There's issue when file system corrupted: -------------[ cut here ]------------ -kernel BUG at fs/jbd2/transaction.c:1289! -Oops: invalid opcode: 0000 [#1] SMP KASAN PTI -CPU=5 UID=0 PID=2031 Comm: mkdir Not tainted 6.18.0-rc1-next -RIP=0010=jbd2_journal_get_create_access+0x3b6/0x4d0 -RSP=0018:ffff888117aafa30 EFLAGS=00010202 -RAX=0000000000000000 RBX=ffff88811a86b000 RCX=ffffffff89a63534 -RDX=1ffff110200ec602 RSI=0000000000000004 RDI=ffff888100763010 -RBP=ffff888100763000 R08=0000000000000001 R09=ffff888100763028 -R10=0000000000000003 R11=0000000000000000 R12=0000000000000000 -R13=ffff88812c432000 R14=ffff88812c608000 R15=ffff888120bfc000 -CS=0010 DS=0000 ES=0000 CR0=0000000080050033 -CR2=00007f91d6970c99 CR3=00000001159c4000 CR4=00000000000006f0 -Call Trace: - - __ext4_journal_get_create_access+0x42/0x170 - ext4_getblk+0x319/0x6f0 - ext4_bread+0x11/0x100 - ext4_append+0x1e6/0x4a0 - ext4_init_new_dir+0x145/0x1d0 - ext4_mkdir+0x326/0x920 - vfs_mkdir+0x45c/0x740 - do_mkdirat+0x234/0x2f0 - __x64_sys_mkdir+0xd6/0x120 - do_syscall_64+0x5f/0xfa0 - entry_SYSCALL_64_after_hwframe+0x76/0x7e - -The above issue occurs with us in errors=continue mode when accompanied by -storage failures. There have been many inconsistencies in the file system -data. -In the case of file system data inconsistency, for example, if the block -bitmap of a referenced block is not set, it can lead to the situation where -a block being committed is allocated and used again. As a result, the -following condition will not be satisfied then trigger BUG_ON. Of course, -it is entirely possible to construct a problematic image that can trigger -this BUG_ON through specific operations. In fact, I have constructed such -an image and easily reproduced this issue. -Therefore, J_ASSERT() holds true only under ideal conditions, but it may -not necessarily be satisfied in exceptional scenarios. Using J_ASSERT() -directly in abnormal situations would cause the system to crash, which is -clearly not what we want. So here we directly trigger a JBD abort instead -of immediately invoking BUG_ON. - -Fixes: 470decc613ab ("[PATCH] jbd2: initial copy of files from jbd") -Signed-off-by: Ye Bin -Reviewed-by: Jan Kara -Message-ID: <20251025072657.307851-1-yebin@huaweicloud.com> -Signed-off-by: Theodore Ts'o -Cc: stable@kernel.org ---- - fs/jbd2/transaction.c | 19 ++++++++++++++----- - 1 file changed, 14 insertions(+), 5 deletions(-) - -diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c -index 3e510564de6e..9ce626ac947e 100644 ---- a/fs/jbd2/transaction.c -+++ b/fs/jbd2/transaction.c -@@ -1284,14 +1284,23 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) - * committing transaction's lists, but it HAS to be in Forget state in - * that case: the transaction must have deleted the buffer for it to be - * reused here. -+ * In the case of file system data inconsistency, for example, if the -+ * block bitmap of a referenced block is not set, it can lead to the -+ * situation where a block being committed is allocated and used again. -+ * As a result, the following condition will not be satisfied, so here -+ * we directly trigger a JBD abort instead of immediately invoking -+ * bugon. - */ - spin_lock(&jh->b_state_lock); -- J_ASSERT_JH(jh, (jh->b_transaction == transaction || -- jh->b_transaction == NULL || -- (jh->b_transaction == journal->j_committing_transaction && -- jh->b_jlist == BJ_Forget))); -+ if (!(jh->b_transaction == transaction || jh->b_transaction == NULL || -+ (jh->b_transaction == journal->j_committing_transaction && -+ jh->b_jlist == BJ_Forget)) || jh->b_next_transaction != NULL) { -+ err = -EROFS; -+ spin_unlock(&jh->b_state_lock); -+ jbd2_journal_abort(journal, err); -+ goto out; -+ } - -- J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - J_ASSERT_JH(jh, buffer_locked(jh2bh(jh))); - - if (jh->b_transaction == NULL) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68345.patch b/SPECS/kernel-rt/CVE-2025-68345.patch deleted file mode 100644 index 48c3ba7d0..000000000 --- a/SPECS/kernel-rt/CVE-2025-68345.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 304d6abf76904b1c7c581343dcf0c00235d7b21c Mon Sep 17 00:00:00 2001 -From: Denis Arefev -Date: Tue, 2 Dec 2025 13:13:36 +0300 -Subject: [PATCH 19/51] ALSA: hda: cs35l41: Fix NULL pointer dereference in - cs35l41_hda_read_acpi() - -The acpi_get_first_physical_node() function can return NULL, in which -case the get_device() function also returns NULL, but this value is -then dereferenced without checking,so add a check to prevent a crash. - -Found by Linux Verification Center (linuxtesting.org) with SVACE. - -Fixes: 7b2f3eb492da ("ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems") -Cc: stable@vger.kernel.org -Signed-off-by: Denis Arefev -Reviewed-by: Richard Fitzgerald -Signed-off-by: Takashi Iwai -Link: https://patch.msgid.link/20251202101338.11437-1-arefev@swemel.ru ---- - sound/hda/codecs/side-codecs/cs35l41_hda.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c -index 0ef77fae0402..8d0cfe0a7ecb 100644 ---- a/sound/hda/codecs/side-codecs/cs35l41_hda.c -+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c -@@ -1917,6 +1917,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i - - cs35l41->dacpi = adev; - physdev = get_device(acpi_get_first_physical_node(adev)); -+ if (!physdev) -+ return -ENODEV; - - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68346.patch b/SPECS/kernel-rt/CVE-2025-68346.patch deleted file mode 100644 index 21949e06b..000000000 --- a/SPECS/kernel-rt/CVE-2025-68346.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 69a1cc4950f8628a85e213ef8f4c437965921a27 Mon Sep 17 00:00:00 2001 -From: Junrui Luo -Date: Fri, 28 Nov 2025 12:06:31 +0800 -Subject: [PATCH 20/51] ALSA: dice: fix buffer overflow in - detect_stream_formats() - -The function detect_stream_formats() reads the stream_count value directly -from a FireWire device without validating it. This can lead to -out-of-bounds writes when a malicious device provides a stream_count value -greater than MAX_STREAMS. - -Fix by applying the same validation to both TX and RX stream counts in -detect_stream_formats(). - -Reported-by: Yuhao Jiang -Reported-by: Junrui Luo -Fixes: 58579c056c1c ("ALSA: dice: use extended protocol to detect available stream formats") -Cc: stable@vger.kernel.org -Reviewed-by: Takashi Sakamoto -Signed-off-by: Junrui Luo -Link: https://patch.msgid.link/SYBPR01MB7881B043FC68B4C0DA40B73DAFDCA@SYBPR01MB7881.ausprd01.prod.outlook.com -Signed-off-by: Takashi Iwai ---- - sound/firewire/dice/dice-extension.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c -index 02f4a8318e38..48bfb3ad93ce 100644 ---- a/sound/firewire/dice/dice-extension.c -+++ b/sound/firewire/dice/dice-extension.c -@@ -116,7 +116,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) - break; - - base_offset += EXT_APP_STREAM_ENTRIES; -- stream_count = be32_to_cpu(reg[0]); -+ stream_count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); - err = read_stream_entries(dice, section_addr, base_offset, - stream_count, mode, - dice->tx_pcm_chs, -@@ -125,7 +125,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) - break; - - base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE; -- stream_count = be32_to_cpu(reg[1]); -+ stream_count = min_t(unsigned int, be32_to_cpu(reg[1]), MAX_STREAMS); - err = read_stream_entries(dice, section_addr, base_offset, - stream_count, - mode, dice->rx_pcm_chs, --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68347.patch b/SPECS/kernel-rt/CVE-2025-68347.patch deleted file mode 100644 index bb7e4dd60..000000000 --- a/SPECS/kernel-rt/CVE-2025-68347.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 8022191f0a098bca7795638e8b8849032ea28152 Mon Sep 17 00:00:00 2001 -From: Junrui Luo -Date: Wed, 3 Dec 2025 12:27:03 +0800 -Subject: [PATCH 21/51] ALSA: firewire-motu: fix buffer overflow in hwdep read - for DSP events - -The DSP event handling code in hwdep_read() could write more bytes to -the user buffer than requested, when a user provides a buffer smaller -than the event header size (8 bytes). - -Fix by using min_t() to clamp the copy size, This ensures we never copy -more than the user requested. - -Reported-by: Yuhao Jiang -Reported-by: Junrui Luo -Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") -Signed-off-by: Junrui Luo -Link: https://patch.msgid.link/SYBPR01MB78810656377E79E58350D951AFD9A@SYBPR01MB7881.ausprd01.prod.outlook.com -Signed-off-by: Takashi Iwai ---- - sound/firewire/motu/motu-hwdep.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c -index fa2685665db3..e594765747d5 100644 ---- a/sound/firewire/motu/motu-hwdep.c -+++ b/sound/firewire/motu/motu-hwdep.c -@@ -83,10 +83,11 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, - event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; - event.motu_register_dsp_change.count = - (consumed - sizeof(event.motu_register_dsp_change)) / 4; -- if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) -+ if (copy_to_user(buf, &event, -+ min_t(long, count, sizeof(event.motu_register_dsp_change)))) - return -EFAULT; - -- count = consumed; -+ count = min_t(long, count, consumed); - } else { - spin_unlock_irq(&motu->lock); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68348.patch b/SPECS/kernel-rt/CVE-2025-68348.patch deleted file mode 100644 index 0dfa149fe..000000000 --- a/SPECS/kernel-rt/CVE-2025-68348.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 75c42bfca8ba40b5ee59d6e66bd6ff64789e301c Mon Sep 17 00:00:00 2001 -From: Shaurya Rane -Date: Thu, 4 Dec 2025 23:42:59 +0530 -Subject: [PATCH 22/51] block: fix memory leak in __blkdev_issue_zero_pages - -Move the fatal signal check before bio_alloc() to prevent a memory -leak when BLKDEV_ZERO_KILLABLE is set and a fatal signal is pending. - -Previously, the bio was allocated before checking for a fatal signal. -If a signal was pending, the code would break out of the loop without -freeing or chaining the just-allocated bio, causing a memory leak. - -This matches the pattern already used in __blkdev_issue_write_zeroes() -where the signal check precedes the allocation. - -Fixes: bf86bcdb4012 ("blk-lib: check for kill signal in ioctl BLKZEROOUT") -Reported-by: syzbot+527a7e48a3d3d315d862@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=527a7e48a3d3d315d862 -Signed-off-by: Shaurya Rane -Reviewed-by: Keith Busch -Tested-by: syzbot+527a7e48a3d3d315d862@syzkaller.appspotmail.com -Signed-off-by: Jens Axboe ---- - block/blk-lib.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/block/blk-lib.c b/block/blk-lib.c -index 4c9f20a689f7..8cb2987db786 100644 ---- a/block/blk-lib.c -+++ b/block/blk-lib.c -@@ -200,13 +200,13 @@ static void __blkdev_issue_zero_pages(struct block_device *bdev, - unsigned int nr_vecs = __blkdev_sectors_to_bio_pages(nr_sects); - struct bio *bio; - -- bio = bio_alloc(bdev, nr_vecs, REQ_OP_WRITE, gfp_mask); -- bio->bi_iter.bi_sector = sector; -- - if ((flags & BLKDEV_ZERO_KILLABLE) && - fatal_signal_pending(current)) - break; - -+ bio = bio_alloc(bdev, nr_vecs, REQ_OP_WRITE, gfp_mask); -+ bio->bi_iter.bi_sector = sector; -+ - do { - unsigned int len, added; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68349.patch b/SPECS/kernel-rt/CVE-2025-68349.patch deleted file mode 100644 index f7e1577f9..000000000 --- a/SPECS/kernel-rt/CVE-2025-68349.patch +++ /dev/null @@ -1,38 +0,0 @@ -From c80547245308a4267488800b321de344d8f469ca Mon Sep 17 00:00:00 2001 -From: Jonathan Curley -Date: Wed, 12 Nov 2025 18:02:42 +0000 -Subject: [PATCH 04/51] NFSv4/pNFS: Clear NFS_INO_LAYOUTCOMMIT in - pnfs_mark_layout_stateid_invalid - -Fixes a crash when layout is null during this call stack: - -write_inode - -> nfs4_write_inode - -> pnfs_layoutcommit_inode - -pnfs_set_layoutcommit relies on the lseg refcount to keep the layout -around. Need to clear NFS_INO_LAYOUTCOMMIT otherwise we might attempt -to reference a null layout. - -Fixes: fe1cf9469d7bc ("pNFS: Clear all layout segment state in pnfs_mark_layout_stateid_invalid") -Signed-off-by: Jonathan Curley -Signed-off-by: Trond Myklebust ---- - fs/nfs/pnfs.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c -index a3135b5af7ee..7ce2e840217c 100644 ---- a/fs/nfs/pnfs.c -+++ b/fs/nfs/pnfs.c -@@ -464,6 +464,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo, - struct pnfs_layout_segment *lseg, *next; - - set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags); -+ clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(lo->plh_inode)->flags); - list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) - pnfs_clear_lseg_state(lseg, lseg_list); - pnfs_clear_layoutreturn_info(lo); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68353.patch b/SPECS/kernel-rt/CVE-2025-68353.patch deleted file mode 100644 index 3409beeb4..000000000 --- a/SPECS/kernel-rt/CVE-2025-68353.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 40b6c2110b3bcbc801775dcb517c609c4fa1eab4 Mon Sep 17 00:00:00 2001 -From: Antoine Tenart -Date: Wed, 26 Nov 2025 11:26:25 +0100 -Subject: [PATCH 23/51] net: vxlan: prevent NULL deref in vxlan_xmit_one - -Neither sock4 nor sock6 pointers are guaranteed to be non-NULL in -vxlan_xmit_one, e.g. if the iface is brought down. This can lead to the -following NULL dereference: - - BUG: kernel NULL pointer dereference, address: 0000000000000010 - Oops: Oops: 0000 [#1] SMP NOPTI - RIP: 0010:vxlan_xmit_one+0xbb3/0x1580 - Call Trace: - vxlan_xmit+0x429/0x610 - dev_hard_start_xmit+0x55/0xa0 - __dev_queue_xmit+0x6d0/0x7f0 - ip_finish_output2+0x24b/0x590 - ip_output+0x63/0x110 - -Mentioned commits changed the code path in vxlan_xmit_one and as a side -effect the sock4/6 pointer validity checks in vxlan(6)_get_route were -lost. Fix this by adding back checks. - -Since both commits being fixed were released in the same version (v6.7) -and are strongly related, bundle the fixes in a single commit. - -Reported-by: Liang Li -Fixes: 6f19b2c136d9 ("vxlan: use generic function for tunnel IPv4 route lookup") -Fixes: 2aceb896ee18 ("vxlan: use generic function for tunnel IPv6 route lookup") -Cc: Beniamino Galvani -Signed-off-by: Antoine Tenart -Reviewed-by: Ido Schimmel -Tested-by: Ido Schimmel -Link: https://patch.msgid.link/20251126102627.74223-1-atenart@kernel.org -Signed-off-by: Jakub Kicinski ---- - drivers/net/vxlan/vxlan_core.c | 18 +++++++++++++++--- - 1 file changed, 15 insertions(+), 3 deletions(-) - -diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c -index dab864bc733c..df9f37df282a 100644 ---- a/drivers/net/vxlan/vxlan_core.c -+++ b/drivers/net/vxlan/vxlan_core.c -@@ -2348,7 +2348,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - int addr_family; - __u8 tos, ttl; - int ifindex; -- int err; -+ int err = 0; - u32 flags = vxlan->cfg.flags; - bool use_cache; - bool udp_sum = false; -@@ -2453,12 +2453,18 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - - rcu_read_lock(); - if (addr_family == AF_INET) { -- struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); -+ struct vxlan_sock *sock4; - u16 ipcb_flags = 0; - struct rtable *rt; - __be16 df = 0; - __be32 saddr; - -+ sock4 = rcu_dereference(vxlan->vn4_sock); -+ if (unlikely(!sock4)) { -+ reason = SKB_DROP_REASON_DEV_READY; -+ goto tx_error; -+ } -+ - if (!ifindex) - ifindex = sock4->sock->sk->sk_bound_dev_if; - -@@ -2533,10 +2539,16 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - ipcb_flags); - #if IS_ENABLED(CONFIG_IPV6) - } else { -- struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); -+ struct vxlan_sock *sock6; - struct in6_addr saddr; - u16 ip6cb_flags = 0; - -+ sock6 = rcu_dereference(vxlan->vn6_sock); -+ if (unlikely(!sock6)) { -+ reason = SKB_DROP_REASON_DEV_READY; -+ goto tx_error; -+ } -+ - if (!ifindex) - ifindex = sock6->sock->sk->sk_bound_dev_if; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68354.patch b/SPECS/kernel-rt/CVE-2025-68354.patch deleted file mode 100644 index aebdd714b..000000000 --- a/SPECS/kernel-rt/CVE-2025-68354.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 698e858cd9b3e1a76716eb31e3a1128f23e9db61 Mon Sep 17 00:00:00 2001 -From: sparkhuang -Date: Thu, 27 Nov 2025 10:57:16 +0800 -Subject: [PATCH 28/51] regulator: core: Protect regulator_supply_alias_list - with regulator_list_mutex - -regulator_supply_alias_list was accessed without any locking in -regulator_supply_alias(), regulator_register_supply_alias(), and -regulator_unregister_supply_alias(). Concurrent registration, -unregistration and lookups can race, leading to: - -1 use-after-free if an alias entry is removed while being read, -2 duplicate entries when two threads register the same alias, -3 inconsistent alias mappings observed by consumers. - -Protect all traversals, insertions and deletions on -regulator_supply_alias_list with the existing regulator_list_mutex. - -Fixes: a06ccd9c3785f ("regulator: core: Add ability to create a lookup alias for supply") -Signed-off-by: sparkhuang -Reviewed-by: Charles Keepax -Link: https://patch.msgid.link/20251127025716.5440-1-huangshaobo3@xiaomi.com -Signed-off-by: Mark Brown ---- - drivers/regulator/core.c | 32 ++++++++++++++++++++------------ - 1 file changed, 20 insertions(+), 12 deletions(-) - -diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c -index 554d83c4af0c..9c7c65063839 100644 ---- a/drivers/regulator/core.c -+++ b/drivers/regulator/core.c -@@ -1942,6 +1942,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply) - { - struct regulator_supply_alias *map; - -+ mutex_lock(®ulator_list_mutex); - map = regulator_find_supply_alias(*dev, *supply); - if (map) { - dev_dbg(*dev, "Mapping supply %s to %s,%s\n", -@@ -1950,6 +1951,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply) - *dev = map->alias_dev; - *supply = map->alias_supply; - } -+ mutex_unlock(®ulator_list_mutex); - } - - static int regulator_match(struct device *dev, const void *data) -@@ -2492,22 +2494,26 @@ int regulator_register_supply_alias(struct device *dev, const char *id, - const char *alias_id) - { - struct regulator_supply_alias *map; -+ struct regulator_supply_alias *new_map; - -- map = regulator_find_supply_alias(dev, id); -- if (map) -- return -EEXIST; -- -- map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL); -- if (!map) -+ new_map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL); -+ if (!new_map) - return -ENOMEM; - -- map->src_dev = dev; -- map->src_supply = id; -- map->alias_dev = alias_dev; -- map->alias_supply = alias_id; -- -- list_add(&map->list, ®ulator_supply_alias_list); -+ mutex_lock(®ulator_list_mutex); -+ map = regulator_find_supply_alias(dev, id); -+ if (map) { -+ mutex_unlock(®ulator_list_mutex); -+ kfree(new_map); -+ return -EEXIST; -+ } - -+ new_map->src_dev = dev; -+ new_map->src_supply = id; -+ new_map->alias_dev = alias_dev; -+ new_map->alias_supply = alias_id; -+ list_add(&new_map->list, ®ulator_supply_alias_list); -+ mutex_unlock(®ulator_list_mutex); - pr_info("Adding alias for supply %s,%s -> %s,%s\n", - id, dev_name(dev), alias_id, dev_name(alias_dev)); - -@@ -2527,11 +2533,13 @@ void regulator_unregister_supply_alias(struct device *dev, const char *id) - { - struct regulator_supply_alias *map; - -+ mutex_lock(®ulator_list_mutex); - map = regulator_find_supply_alias(dev, id); - if (map) { - list_del(&map->list); - kfree(map); - } -+ mutex_unlock(®ulator_list_mutex); - } - EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68358.patch b/SPECS/kernel-rt/CVE-2025-68358.patch deleted file mode 100644 index 455384a0b..000000000 --- a/SPECS/kernel-rt/CVE-2025-68358.patch +++ /dev/null @@ -1,251 +0,0 @@ -From b1848a20aec6b8b9d692098f47b19dc2c7a6e6b9 Mon Sep 17 00:00:00 2001 -From: Boris Burkov -Date: Wed, 1 Oct 2025 17:20:22 -0700 -Subject: [PATCH 25/51] btrfs: fix racy bitfield write in - btrfs_clear_space_info_full() - -From the memory-barriers.txt document regarding memory barrier ordering -guarantees: - - (*) These guarantees do not apply to bitfields, because compilers often - generate code to modify these using non-atomic read-modify-write - sequences. Do not attempt to use bitfields to synchronize parallel - algorithms. - - (*) Even in cases where bitfields are protected by locks, all fields - in a given bitfield must be protected by one lock. If two fields - in a given bitfield are protected by different locks, the compiler's - non-atomic read-modify-write sequences can cause an update to one - field to corrupt the value of an adjacent field. - -btrfs_space_info has a bitfield sharing an underlying word consisting of -the fields full, chunk_alloc, and flush: - -struct btrfs_space_info { - struct btrfs_fs_info * fs_info; /* 0 8 */ - struct btrfs_space_info * parent; /* 8 8 */ - ... - int clamp; /* 172 4 */ - unsigned int full:1; /* 176: 0 4 */ - unsigned int chunk_alloc:1; /* 176: 1 4 */ - unsigned int flush:1; /* 176: 2 4 */ - ... - -Therefore, to be safe from parallel read-modify-writes losing a write to -one of the bitfield members protected by a lock, all writes to all the -bitfields must use the lock. They almost universally do, except for -btrfs_clear_space_info_full() which iterates over the space_infos and -writes out found->full = 0 without a lock. - -Imagine that we have one thread completing a transaction in which we -finished deleting a block_group and are thus calling -btrfs_clear_space_info_full() while simultaneously the data reclaim -ticket infrastructure is running do_async_reclaim_data_space(): - - T1 T2 -btrfs_commit_transaction - btrfs_clear_space_info_full - data_sinfo->full = 0 - READ: full:0, chunk_alloc:0, flush:1 - do_async_reclaim_data_space(data_sinfo) - spin_lock(&space_info->lock); - if(list_empty(tickets)) - space_info->flush = 0; - READ: full: 0, chunk_alloc:0, flush:1 - MOD/WRITE: full: 0, chunk_alloc:0, flush:0 - spin_unlock(&space_info->lock); - return; - MOD/WRITE: full:0, chunk_alloc:0, flush:1 - -and now data_sinfo->flush is 1 but the reclaim worker has exited. This -breaks the invariant that flush is 0 iff there is no work queued or -running. Once this invariant is violated, future allocations that go -into __reserve_bytes() will add tickets to space_info->tickets but will -see space_info->flush is set to 1 and not queue the work. After this, -they will block forever on the resulting ticket, as it is now impossible -to kick the worker again. - -I also confirmed by looking at the assembly of the affected kernel that -it is doing RMW operations. For example, to set the flush (3rd) bit to 0, -the assembly is: - andb $0xfb,0x60(%rbx) -and similarly for setting the full (1st) bit to 0: - andb $0xfe,-0x20(%rax) - -So I think this is really a bug on practical systems. I have observed -a number of systems in this exact state, but am currently unable to -reproduce it. - -Rather than leaving this footgun lying around for the future, take -advantage of the fact that there is room in the struct anyway, and that -it is already quite large and simply change the three bitfield members to -bools. This avoids writes to space_info->full having any effect on -writes to space_info->flush, regardless of locking. - -Fixes: 957780eb2788 ("Btrfs: introduce ticketed enospc infrastructure") -Reviewed-by: Qu Wenruo -Signed-off-by: Boris Burkov -Reviewed-by: David Sterba -Signed-off-by: David Sterba ---- - fs/btrfs/block-group.c | 6 +++--- - fs/btrfs/space-info.c | 22 +++++++++++----------- - fs/btrfs/space-info.h | 6 +++--- - 3 files changed, 17 insertions(+), 17 deletions(-) - -diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c -index 499a9edf0ca3..a368d6ac98ed 100644 ---- a/fs/btrfs/block-group.c -+++ b/fs/btrfs/block-group.c -@@ -4215,7 +4215,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, - mutex_unlock(&fs_info->chunk_mutex); - } else { - /* Proceed with allocation */ -- space_info->chunk_alloc = 1; -+ space_info->chunk_alloc = true; - wait_for_alloc = false; - spin_unlock(&space_info->lock); - } -@@ -4264,7 +4264,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, - spin_lock(&space_info->lock); - if (ret < 0) { - if (ret == -ENOSPC) -- space_info->full = 1; -+ space_info->full = true; - else - goto out; - } else { -@@ -4274,7 +4274,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, - - space_info->force_alloc = CHUNK_ALLOC_NO_FORCE; - out: -- space_info->chunk_alloc = 0; -+ space_info->chunk_alloc = false; - spin_unlock(&space_info->lock); - mutex_unlock(&fs_info->chunk_mutex); - -diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c -index 0481c693ac2e..b05ab1122a42 100644 ---- a/fs/btrfs/space-info.c -+++ b/fs/btrfs/space-info.c -@@ -192,7 +192,7 @@ void btrfs_clear_space_info_full(struct btrfs_fs_info *info) - struct btrfs_space_info *found; - - list_for_each_entry(found, head, list) -- found->full = 0; -+ found->full = false; - } - - /* -@@ -372,7 +372,7 @@ void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info, - space_info->bytes_readonly += block_group->bytes_super; - btrfs_space_info_update_bytes_zone_unusable(space_info, block_group->zone_unusable); - if (block_group->length > 0) -- space_info->full = 0; -+ space_info->full = false; - btrfs_try_granting_tickets(info, space_info); - spin_unlock(&space_info->lock); - -@@ -1146,7 +1146,7 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info) - spin_lock(&space_info->lock); - to_reclaim = btrfs_calc_reclaim_metadata_size(fs_info, space_info); - if (!to_reclaim) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1158,7 +1158,7 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info) - flush_space(fs_info, space_info, to_reclaim, flush_state, false); - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1201,7 +1201,7 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info) - flush_state = FLUSH_DELAYED_ITEMS_NR; - commit_cycles--; - } else { -- space_info->flush = 0; -+ space_info->flush = false; - } - } else { - flush_state = FLUSH_DELAYED_ITEMS_NR; -@@ -1383,7 +1383,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1394,7 +1394,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - flush_space(fs_info, space_info, U64_MAX, ALLOC_CHUNK_FORCE, false); - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1411,7 +1411,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - data_flush_states[flush_state], false); - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1428,7 +1428,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - if (maybe_fail_all_tickets(fs_info, space_info)) - flush_state = 0; - else -- space_info->flush = 0; -+ space_info->flush = false; - } else { - flush_state = 0; - } -@@ -1444,7 +1444,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - - aborted_fs: - maybe_fail_all_tickets(fs_info, space_info); -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - } - -@@ -1825,7 +1825,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info, - */ - maybe_clamp_preempt(fs_info, space_info); - -- space_info->flush = 1; -+ space_info->flush = true; - trace_btrfs_trigger_flush(fs_info, - space_info->flags, - orig_bytes, flush, -diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h -index 679f22efb407..a846f63585c9 100644 ---- a/fs/btrfs/space-info.h -+++ b/fs/btrfs/space-info.h -@@ -142,11 +142,11 @@ struct btrfs_space_info { - flushing. The value is >> clamp, so turns - out to be a 2^clamp divisor. */ - -- unsigned int full:1; /* indicates that we cannot allocate any more -+ bool full; /* indicates that we cannot allocate any more - chunks for this space */ -- unsigned int chunk_alloc:1; /* set if we are allocating a chunk */ -+ bool chunk_alloc; /* set if we are allocating a chunk */ - -- unsigned int flush:1; /* set if we are trying to make space */ -+ bool flush; /* set if we are trying to make space */ - - unsigned int force_alloc; /* set if we need to force a chunk - alloc for this space */ --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68359.patch b/SPECS/kernel-rt/CVE-2025-68359.patch deleted file mode 100644 index 7eb082868..000000000 --- a/SPECS/kernel-rt/CVE-2025-68359.patch +++ /dev/null @@ -1,150 +0,0 @@ -From 0a25d5c140f5359ada727cdb3f5f378599fea799 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miquel=20Sabat=C3=A9=20Sol=C3=A0?= -Date: Wed, 1 Oct 2025 20:05:03 +0200 -Subject: [PATCH 30/51] btrfs: fix double free of qgroup record after failure - to add delayed ref head -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In the previous code it was possible to incur into a double kfree() -scenario when calling add_delayed_ref_head(). This could happen if the -record was reported to already exist in the -btrfs_qgroup_trace_extent_nolock() call, but then there was an error -later on add_delayed_ref_head(). In this case, since -add_delayed_ref_head() returned an error, the caller went to free the -record. Since add_delayed_ref_head() couldn't set this kfree'd pointer -to NULL, then kfree() would have acted on a non-NULL 'record' object -which was pointing to memory already freed by the callee. - -The problem comes from the fact that the responsibility to kfree the -object is on both the caller and the callee at the same time. Hence, the -fix for this is to shift the ownership of the 'qrecord' object out of -the add_delayed_ref_head(). That is, we will never attempt to kfree() -the given object inside of this function, and will expect the caller to -act on the 'qrecord' object on its own. The only exception where the -'qrecord' object cannot be kfree'd is if it was inserted into the -tracing logic, for which we already have the 'qrecord_inserted_ret' -boolean to account for this. Hence, the caller has to kfree the object -only if add_delayed_ref_head() reports not to have inserted it on the -tracing logic. - -As a side-effect of the above, we must guarantee that -'qrecord_inserted_ret' is properly initialized at the start of the -function, not at the end, and then set when an actual insert -happens. This way we avoid 'qrecord_inserted_ret' having an invalid -value on an early exit. - -The documentation from the add_delayed_ref_head() has also been updated -to reflect on the exact ownership of the 'qrecord' object. - -Fixes: 6ef8fbce0104 ("btrfs: fix missing error handling when adding delayed ref with qgroups enabled") -Reviewed-by: Filipe Manana -Signed-off-by: Miquel Sabaté Solà -Signed-off-by: Filipe Manana -Signed-off-by: David Sterba ---- - fs/btrfs/delayed-ref.c | 43 ++++++++++++++++++++++++++++++++---------- - 1 file changed, 33 insertions(+), 10 deletions(-) - -diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c -index ca382c5b186f..39c7ad123167 100644 ---- a/fs/btrfs/delayed-ref.c -+++ b/fs/btrfs/delayed-ref.c -@@ -798,9 +798,13 @@ static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, - } - - /* -- * helper function to actually insert a head node into the rbtree. -- * this does all the dirty work in terms of maintaining the correct -- * overall modification count. -+ * Helper function to actually insert a head node into the xarray. This does all -+ * the dirty work in terms of maintaining the correct overall modification -+ * count. -+ * -+ * The caller is responsible for calling kfree() on @qrecord. More specifically, -+ * if this function reports that it did not insert it as noted in -+ * @qrecord_inserted_ret, then it's safe to call kfree() on it. - * - * Returns an error pointer in case of an error. - */ -@@ -814,7 +818,14 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_head *existing; - struct btrfs_delayed_ref_root *delayed_refs; - const unsigned long index = (head_ref->bytenr >> fs_info->sectorsize_bits); -- bool qrecord_inserted = false; -+ -+ /* -+ * If 'qrecord_inserted_ret' is provided, then the first thing we need -+ * to do is to initialize it to false just in case we have an exit -+ * before trying to insert the record. -+ */ -+ if (qrecord_inserted_ret) -+ *qrecord_inserted_ret = false; - - delayed_refs = &trans->transaction->delayed_refs; - lockdep_assert_held(&delayed_refs->lock); -@@ -833,6 +844,12 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - - /* Record qgroup extent info if provided */ - if (qrecord) { -+ /* -+ * Setting 'qrecord' but not 'qrecord_inserted_ret' will likely -+ * result in a memory leakage. -+ */ -+ ASSERT(qrecord_inserted_ret != NULL); -+ - int ret; - - ret = btrfs_qgroup_trace_extent_nolock(fs_info, delayed_refs, qrecord, -@@ -840,12 +857,10 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - if (ret) { - /* Clean up if insertion fails or item exists. */ - xa_release(&delayed_refs->dirty_extents, index); -- /* Caller responsible for freeing qrecord on error. */ - if (ret < 0) - return ERR_PTR(ret); -- kfree(qrecord); -- } else { -- qrecord_inserted = true; -+ } else if (qrecord_inserted_ret) { -+ *qrecord_inserted_ret = true; - } - } - -@@ -888,8 +903,6 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - delayed_refs->num_heads++; - delayed_refs->num_heads_ready++; - } -- if (qrecord_inserted_ret) -- *qrecord_inserted_ret = qrecord_inserted; - - return head_ref; - } -@@ -1049,6 +1062,14 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, - xa_release(&delayed_refs->head_refs, index); - spin_unlock(&delayed_refs->lock); - ret = PTR_ERR(new_head_ref); -+ -+ /* -+ * It's only safe to call kfree() on 'qrecord' if -+ * add_delayed_ref_head() has _not_ inserted it for -+ * tracing. Otherwise we need to handle this here. -+ */ -+ if (!qrecord_reserved || qrecord_inserted) -+ goto free_head_ref; - goto free_record; - } - head_ref = new_head_ref; -@@ -1071,6 +1092,8 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, - - if (qrecord_inserted) - return btrfs_qgroup_trace_extent_post(trans, record, generic_ref->bytenr); -+ -+ kfree(record); - return 0; - - free_record: --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68363.patch b/SPECS/kernel-rt/CVE-2025-68363.patch deleted file mode 100644 index dda9678f2..000000000 --- a/SPECS/kernel-rt/CVE-2025-68363.patch +++ /dev/null @@ -1,65 +0,0 @@ -From dc2df6d0a3b828a3f8d487b75f1a769c7345ca3e Mon Sep 17 00:00:00 2001 -From: Martin KaFai Lau -Date: Wed, 12 Nov 2025 15:23:30 -0800 -Subject: [PATCH 09/51] bpf: Check skb->transport_header is set in - bpf_skb_check_mtu - -The bpf_skb_check_mtu helper needs to use skb->transport_header when -the BPF_MTU_CHK_SEGS flag is used: - - bpf_skb_check_mtu(skb, ifindex, &mtu_len, 0, BPF_MTU_CHK_SEGS) - -The transport_header is not always set. There is a WARN_ON_ONCE -report when CONFIG_DEBUG_NET is enabled + skb->gso_size is set + -bpf_prog_test_run is used: - -WARNING: CPU: 1 PID: 2216 at ./include/linux/skbuff.h:3071 - skb_gso_validate_network_len - bpf_skb_check_mtu - bpf_prog_3920e25740a41171_tc_chk_segs_flag # A test in the next patch - bpf_test_run - bpf_prog_test_run_skb - -For a normal ingress skb (not test_run), skb_reset_transport_header -is performed but there is plan to avoid setting it as described in -commit 2170a1f09148 ("net: no longer reset transport_header in __netif_receive_skb_core()"). - -This patch fixes the bpf helper by checking -skb_transport_header_was_set(). The check is done just before -skb->transport_header is used, to avoid breaking the existing bpf prog. -The WARN_ON_ONCE is limited to bpf_prog_test_run, so targeting bpf-next. - -Fixes: 34b2021cc616 ("bpf: Add BPF-helper for MTU checking") -Cc: Jesper Dangaard Brouer -Reported-by: Kaiyan Mei -Reported-by: Yinhao Hu -Signed-off-by: Martin KaFai Lau -Link: https://lore.kernel.org/r/20251112232331.1566074-1-martin.lau@linux.dev -Signed-off-by: Alexei Starovoitov ---- - net/core/filter.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/net/core/filter.c b/net/core/filter.c -index b20d5fecdbc9..47366ec94e58 100644 ---- a/net/core/filter.c -+++ b/net/core/filter.c -@@ -6414,9 +6414,12 @@ BPF_CALL_5(bpf_skb_check_mtu, struct sk_buff *, skb, - */ - if (skb_is_gso(skb)) { - ret = BPF_MTU_CHK_RET_SUCCESS; -- if (flags & BPF_MTU_CHK_SEGS && -- !skb_gso_validate_network_len(skb, mtu)) -- ret = BPF_MTU_CHK_RET_SEGS_TOOBIG; -+ if (flags & BPF_MTU_CHK_SEGS) { -+ if (!skb_transport_header_was_set(skb)) -+ return -EINVAL; -+ if (!skb_gso_validate_network_len(skb, mtu)) -+ ret = BPF_MTU_CHK_RET_SEGS_TOOBIG; -+ } - } - out: - *mtu_len = mtu; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68366.patch b/SPECS/kernel-rt/CVE-2025-68366.patch deleted file mode 100644 index 104ecfd0d..000000000 --- a/SPECS/kernel-rt/CVE-2025-68366.patch +++ /dev/null @@ -1,67 +0,0 @@ -From f06abb6afac3a44f47c0a309729af7fd22a1eb50 Mon Sep 17 00:00:00 2001 -From: Zheng Qixing -Date: Mon, 10 Nov 2025 20:49:20 +0800 -Subject: [PATCH 05/51] nbd: defer config unlock in nbd_genl_connect - -There is one use-after-free warning when running NBD_CMD_CONNECT and -NBD_CLEAR_SOCK: - -nbd_genl_connect - nbd_alloc_and_init_config // config_refs=1 - nbd_start_device // config_refs=2 - set NBD_RT_HAS_CONFIG_REF open nbd // config_refs=3 - recv_work done // config_refs=2 - NBD_CLEAR_SOCK // config_refs=1 - close nbd // config_refs=0 - refcount_inc -> uaf - -------------[ cut here ]------------ -refcount_t: addition on 0; use-after-free. -WARNING: CPU: 24 PID: 1014 at lib/refcount.c:25 refcount_warn_saturate+0x12e/0x290 - nbd_genl_connect+0x16d0/0x1ab0 - genl_family_rcv_msg_doit+0x1f3/0x310 - genl_rcv_msg+0x44a/0x790 - -The issue can be easily reproduced by adding a small delay before -refcount_inc(&nbd->config_refs) in nbd_genl_connect(): - - mutex_unlock(&nbd->config_lock); - if (!ret) { - set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags); -+ printk("before sleep\n"); -+ mdelay(5 * 1000); -+ printk("after sleep\n"); - refcount_inc(&nbd->config_refs); - nbd_connect_reply(info, nbd->index); - } - -Fixes: e46c7287b1c2 ("nbd: add a basic netlink interface") -Signed-off-by: Zheng Qixing -Reviewed-by: Yu Kuai -Signed-off-by: Jens Axboe ---- - drivers/block/nbd.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c -index ad39ab95ea66..99fde2be65f9 100644 ---- a/drivers/block/nbd.c -+++ b/drivers/block/nbd.c -@@ -2241,12 +2241,13 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) - - ret = nbd_start_device(nbd); - out: -- mutex_unlock(&nbd->config_lock); - if (!ret) { - set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags); - refcount_inc(&nbd->config_refs); - nbd_connect_reply(info, nbd->index); - } -+ mutex_unlock(&nbd->config_lock); -+ - nbd_config_put(nbd); - if (put_dev) - nbd_put(nbd); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68368.patch b/SPECS/kernel-rt/CVE-2025-68368.patch deleted file mode 100644 index 93fbaebf3..000000000 --- a/SPECS/kernel-rt/CVE-2025-68368.patch +++ /dev/null @@ -1,174 +0,0 @@ -From aaa0398c24a0d4ea8ce04f089d6b0a3f0b0d293b Mon Sep 17 00:00:00 2001 -From: Li Nan -Date: Mon, 3 Nov 2025 20:57:54 +0800 -Subject: [PATCH 32/51] md: init bioset in mddev_init - -IO operations may be needed before md_run(), such as updating metadata -after writing sysfs. Without bioset, this triggers a NULL pointer -dereference as below: - - BUG: kernel NULL pointer dereference, address: 0000000000000020 - Call Trace: - md_update_sb+0x658/0xe00 - new_level_store+0xc5/0x120 - md_attr_store+0xc9/0x1e0 - sysfs_kf_write+0x6f/0xa0 - kernfs_fop_write_iter+0x141/0x2a0 - vfs_write+0x1fc/0x5a0 - ksys_write+0x79/0x180 - __x64_sys_write+0x1d/0x30 - x64_sys_call+0x2818/0x2880 - do_syscall_64+0xa9/0x580 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -Reproducer -``` - mdadm -CR /dev/md0 -l1 -n2 /dev/sd[cd] - echo inactive > /sys/block/md0/md/array_state - echo 10 > /sys/block/md0/md/new_level -``` - -mddev_init() can only be called once per mddev, no need to test if bioset -has been initialized anymore. - -Link: https://lore.kernel.org/linux-raid/20251103125757.1405796-3-linan666@huaweicloud.com -Fixes: d981ed841930 ("md: Add new_level sysfs interface") -Signed-off-by: Li Nan -Reviewed-by: Xiao Ni -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 68 +++++++++++++++++++++++-------------------------- - 1 file changed, 32 insertions(+), 36 deletions(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index 4e033c26fdd4..b260aff7882c 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -679,6 +679,7 @@ static void no_op(struct percpu_ref *r) {} - - int mddev_init(struct mddev *mddev) - { -+ int err = 0; - - if (percpu_ref_init(&mddev->active_io, active_io_release, - PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) -@@ -686,10 +687,23 @@ int mddev_init(struct mddev *mddev) - - if (percpu_ref_init(&mddev->writes_pending, no_op, - PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) { -- percpu_ref_exit(&mddev->active_io); -- return -ENOMEM; -+ err = -ENOMEM; -+ goto exit_acitve_io; - } - -+ err = bioset_init(&mddev->bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -+ if (err) -+ goto exit_writes_pending; -+ -+ err = bioset_init(&mddev->sync_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -+ if (err) -+ goto exit_bio_set; -+ -+ err = bioset_init(&mddev->io_clone_set, BIO_POOL_SIZE, -+ offsetof(struct md_io_clone, bio_clone), 0); -+ if (err) -+ goto exit_sync_set; -+ - /* We want to start with the refcount at zero */ - percpu_ref_put(&mddev->writes_pending); - -@@ -719,11 +733,24 @@ int mddev_init(struct mddev *mddev) - INIT_WORK(&mddev->del_work, mddev_delayed_delete); - - return 0; -+ -+exit_sync_set: -+ bioset_exit(&mddev->sync_set); -+exit_bio_set: -+ bioset_exit(&mddev->bio_set); -+exit_writes_pending: -+ percpu_ref_exit(&mddev->writes_pending); -+exit_acitve_io: -+ percpu_ref_exit(&mddev->active_io); -+ return err; - } - EXPORT_SYMBOL_GPL(mddev_init); - - void mddev_destroy(struct mddev *mddev) - { -+ bioset_exit(&mddev->bio_set); -+ bioset_exit(&mddev->sync_set); -+ bioset_exit(&mddev->io_clone_set); - percpu_ref_exit(&mddev->active_io); - percpu_ref_exit(&mddev->writes_pending); - } -@@ -6212,29 +6239,9 @@ int md_run(struct mddev *mddev) - nowait = nowait && bdev_nowait(rdev->bdev); - } - -- if (!bioset_initialized(&mddev->bio_set)) { -- err = bioset_init(&mddev->bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -- if (err) -- return err; -- } -- if (!bioset_initialized(&mddev->sync_set)) { -- err = bioset_init(&mddev->sync_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -- if (err) -- goto exit_bio_set; -- } -- -- if (!bioset_initialized(&mddev->io_clone_set)) { -- err = bioset_init(&mddev->io_clone_set, BIO_POOL_SIZE, -- offsetof(struct md_io_clone, bio_clone), 0); -- if (err) -- goto exit_sync_set; -- } -- - pers = get_pers(mddev->level, mddev->clevel); -- if (!pers) { -- err = -EINVAL; -- goto abort; -- } -+ if (!pers) -+ return -EINVAL; - if (mddev->level != pers->head.id) { - mddev->level = pers->head.id; - mddev->new_level = pers->head.id; -@@ -6245,8 +6252,7 @@ int md_run(struct mddev *mddev) - pers->start_reshape == NULL) { - /* This personality cannot handle reshaping... */ - put_pers(pers); -- err = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - if (pers->sync_request) { -@@ -6373,12 +6379,6 @@ int md_run(struct mddev *mddev) - mddev->private = NULL; - put_pers(pers); - mddev->bitmap_ops->destroy(mddev); --abort: -- bioset_exit(&mddev->io_clone_set); --exit_sync_set: -- bioset_exit(&mddev->sync_set); --exit_bio_set: -- bioset_exit(&mddev->bio_set); - return err; - } - EXPORT_SYMBOL_GPL(md_run); -@@ -6599,10 +6599,6 @@ static void __md_stop(struct mddev *mddev) - mddev->private = NULL; - put_pers(pers); - clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); -- -- bioset_exit(&mddev->bio_set); -- bioset_exit(&mddev->sync_set); -- bioset_exit(&mddev->io_clone_set); - } - - void md_stop(struct mddev *mddev) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68371.patch b/SPECS/kernel-rt/CVE-2025-68371.patch deleted file mode 100644 index c9be703f4..000000000 --- a/SPECS/kernel-rt/CVE-2025-68371.patch +++ /dev/null @@ -1,90 +0,0 @@ -From db94ca4006fcd8604d4e687d1417ff2a7d9391e7 Mon Sep 17 00:00:00 2001 -From: Mike McGowen -Date: Thu, 6 Nov 2025 10:38:20 -0600 -Subject: [PATCH 33/51] scsi: smartpqi: Fix device resources accessed after - device removal - -Correct possible race conditions during device removal. - -Previously, a scheduled work item to reset a LUN could still execute -after the device was removed, leading to use-after-free and other -resource access issues. - -This race condition occurs because the abort handler may schedule a LUN -reset concurrently with device removal via sdev_destroy(), leading to -use-after-free and improper access to freed resources. - - - Check in the device reset handler if the device is still present in - the controller's SCSI device list before running; if not, the reset - is skipped. - - - Cancel any pending TMF work that has not started in sdev_destroy(). - - - Ensure device freeing in sdev_destroy() is done while holding the - LUN reset mutex to avoid races with ongoing resets. - -Fixes: 2d80f4054f7f ("scsi: smartpqi: Update deleting a LUN via sysfs") -Reviewed-by: Scott Teel -Reviewed-by: Scott Benesh -Signed-off-by: Mike McGowen -Signed-off-by: Don Brace -Link: https://patch.msgid.link/20251106163823.786828-3-don.brace@microchip.com -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/smartpqi/smartpqi_init.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c -index 125944941601..9f4f6e74f3e3 100644 ---- a/drivers/scsi/smartpqi/smartpqi_init.c -+++ b/drivers/scsi/smartpqi/smartpqi_init.c -@@ -6409,10 +6409,22 @@ static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev - - static int pqi_device_reset_handler(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun, struct scsi_cmnd *scmd, u8 scsi_opcode) - { -+ unsigned long flags; - int rc; - - mutex_lock(&ctrl_info->lun_reset_mutex); - -+ spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); -+ if (pqi_find_scsi_dev(ctrl_info, device->bus, device->target, device->lun) == NULL) { -+ dev_warn(&ctrl_info->pci_dev->dev, -+ "skipping reset of scsi %d:%d:%d:%u, device has been removed\n", -+ ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun); -+ spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); -+ mutex_unlock(&ctrl_info->lun_reset_mutex); -+ return 0; -+ } -+ spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); -+ - dev_err(&ctrl_info->pci_dev->dev, - "resetting scsi %d:%d:%d:%u SCSI cmd at %p due to cmd opcode 0x%02x\n", - ctrl_info->scsi_host->host_no, device->bus, device->target, lun, scmd, scsi_opcode); -@@ -6593,7 +6605,9 @@ static void pqi_sdev_destroy(struct scsi_device *sdev) - { - struct pqi_ctrl_info *ctrl_info; - struct pqi_scsi_dev *device; -+ struct pqi_tmf_work *tmf_work; - int mutex_acquired; -+ unsigned int lun; - unsigned long flags; - - ctrl_info = shost_to_hba(sdev->host); -@@ -6620,8 +6634,13 @@ static void pqi_sdev_destroy(struct scsi_device *sdev) - - mutex_unlock(&ctrl_info->scan_mutex); - -+ for (lun = 0, tmf_work = device->tmf_work; lun < PQI_MAX_LUNS_PER_DEVICE; lun++, tmf_work++) -+ cancel_work_sync(&tmf_work->work_struct); -+ -+ mutex_lock(&ctrl_info->lun_reset_mutex); - pqi_dev_info(ctrl_info, "removed", device); - pqi_free_device(device); -+ mutex_unlock(&ctrl_info->lun_reset_mutex); - } - - static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68372.patch b/SPECS/kernel-rt/CVE-2025-68372.patch deleted file mode 100644 index 15aad0b19..000000000 --- a/SPECS/kernel-rt/CVE-2025-68372.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 96303d7f8485aca392dadff87901e54df5d61498 Mon Sep 17 00:00:00 2001 -From: Zheng Qixing -Date: Sat, 8 Nov 2025 15:02:02 +0800 -Subject: [PATCH 51/51] nbd: defer config put in recv_work - -There is one uaf issue in recv_work when running NBD_CLEAR_SOCK and -NBD_CMD_RECONFIGURE: - nbd_genl_connect // conf_ref=2 (connect and recv_work A) - nbd_open // conf_ref=3 - recv_work A done // conf_ref=2 - NBD_CLEAR_SOCK // conf_ref=1 - nbd_genl_reconfigure // conf_ref=2 (trigger recv_work B) - close nbd // conf_ref=1 - recv_work B - config_put // conf_ref=0 - atomic_dec(&config->recv_threads); -> UAF - -Or only running NBD_CLEAR_SOCK: - nbd_genl_connect // conf_ref=2 - nbd_open // conf_ref=3 - NBD_CLEAR_SOCK // conf_ref=2 - close nbd - nbd_release - config_put // conf_ref=1 - recv_work - config_put // conf_ref=0 - atomic_dec(&config->recv_threads); -> UAF - -Commit 87aac3a80af5 ("nbd: call nbd_config_put() before notifying the -waiter") moved nbd_config_put() to run before waking up the waiter in -recv_work, in order to ensure that nbd_start_device_ioctl() would not -be woken up while nbd->task_recv was still uncleared. - -However, in nbd_start_device_ioctl(), after being woken up it explicitly -calls flush_workqueue() to make sure all current works are finished. -Therefore, there is no need to move the config put ahead of the wakeup. - -Move nbd_config_put() to the end of recv_work, so that the reference is -held for the whole lifetime of the worker thread. This makes sure the -config cannot be freed while recv_work is still running, even if clear -+ reconfigure interleave. - -In addition, we don't need to worry about recv_work dropping the last -nbd_put (which causes deadlock): - -path A (netlink with NBD_CFLAG_DESTROY_ON_DISCONNECT): - connect // nbd_refs=1 (trigger recv_work) - open nbd // nbd_refs=2 - NBD_CLEAR_SOCK - close nbd - nbd_release - nbd_disconnect_and_put - flush_workqueue // recv_work done - nbd_config_put - nbd_put // nbd_refs=1 - nbd_put // nbd_refs=0 - queue_work - -path B (netlink without NBD_CFLAG_DESTROY_ON_DISCONNECT): - connect // nbd_refs=2 (trigger recv_work) - open nbd // nbd_refs=3 - NBD_CLEAR_SOCK // conf_refs=2 - close nbd - nbd_release - nbd_config_put // conf_refs=1 - nbd_put // nbd_refs=2 - recv_work done // conf_refs=0, nbd_refs=1 - rmmod // nbd_refs=0 - -Reported-by: syzbot+56fbf4c7ddf65e95c7cc@syzkaller.appspotmail.com -Closes: https://lore.kernel.org/all/6907edce.a70a0220.37351b.0014.GAE@google.com/T/ -Fixes: 87aac3a80af5 ("nbd: make the config put is called before the notifying the waiter") -Depends-on: e2daec488c57 ("nbd: Fix hungtask when nbd_config_put") -Signed-off-by: Zheng Qixing -Signed-off-by: Jens Axboe ---- - drivers/block/nbd.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c -index 99fde2be65f9..9ba4b20b80ba 100644 ---- a/drivers/block/nbd.c -+++ b/drivers/block/nbd.c -@@ -1024,9 +1024,9 @@ static void recv_work(struct work_struct *work) - nbd_mark_nsock_dead(nbd, nsock, 1); - mutex_unlock(&nsock->tx_lock); - -- nbd_config_put(nbd); - atomic_dec(&config->recv_threads); - wake_up(&config->recv_wq); -+ nbd_config_put(nbd); - kfree(args); - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68373-2.patch b/SPECS/kernel-rt/CVE-2025-68373-2.patch deleted file mode 100644 index 03003a60d..000000000 --- a/SPECS/kernel-rt/CVE-2025-68373-2.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 61589ec93c17b6993ec2541e2647b0e6df015185 Mon Sep 17 00:00:00 2001 -From: Xiao Ni -Date: Wed, 29 Oct 2025 14:34:19 +0800 -Subject: [PATCH 37/51] md: avoid repeated calls to del_gendisk - -There is a uaf problem which is found by case 23rdev-lifetime: - -Oops: general protection fault, probably for non-canonical address 0xdead000000000122 -RIP: 0010:bdi_unregister+0x4b/0x170 -Call Trace: - - __del_gendisk+0x356/0x3e0 - mddev_unlock+0x351/0x360 - rdev_attr_store+0x217/0x280 - kernfs_fop_write_iter+0x14a/0x210 - vfs_write+0x29e/0x550 - ksys_write+0x74/0xf0 - do_syscall_64+0xbb/0x380 - entry_SYSCALL_64_after_hwframe+0x77/0x7f -RIP: 0033:0x7ff5250a177e - -The sequence is: -1. rdev remove path gets reconfig_mutex -2. rdev remove path release reconfig_mutex in mddev_unlock -3. md stop calls do_md_stop and sets MD_DELETED -4. rdev remove path calls del_gendisk because MD_DELETED is set -5. md stop path release reconfig_mutex and calls del_gendisk again - -So there is a race condition we should resolve. This patch adds a -flag MD_DO_DELETE to avoid the race condition. - -Link: https://lore.kernel.org/linux-raid/20251029063419.21700-1-xni@redhat.com -Fixes: 9e59d609763f ("md: call del_gendisk in control path") -Signed-off-by: Xiao Ni -Suggested-by: Yu Kuai -Reviewed-by: Li Nan -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 3 ++- - drivers/md/md.h | 1 + - 2 files changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index 5aa08663b9bb..55262f7bdf72 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -914,7 +914,8 @@ void mddev_unlock(struct mddev *mddev) - * do_md_stop. dm raid only uses md_stop to stop. So dm raid - * doesn't need to check MD_DELETED when getting reconfig lock - */ -- if (test_bit(MD_DELETED, &mddev->flags)) { -+ if (test_bit(MD_DELETED, &mddev->flags) && -+ !test_and_set_bit(MD_DO_DELETE, &mddev->flags)) { - kobject_del(&mddev->kobj); - del_gendisk(mddev->gendisk); - } -diff --git a/drivers/md/md.h b/drivers/md/md.h -index 51af29a03079..e0bc35543217 100644 ---- a/drivers/md/md.h -+++ b/drivers/md/md.h -@@ -353,6 +353,7 @@ enum mddev_flags { - MD_HAS_MULTIPLE_PPLS, - MD_NOT_READY, - MD_BROKEN, -+ MD_DO_DELETE, - MD_DELETED, - }; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68373.patch b/SPECS/kernel-rt/CVE-2025-68373.patch deleted file mode 100644 index bb066a182..000000000 --- a/SPECS/kernel-rt/CVE-2025-68373.patch +++ /dev/null @@ -1,55 +0,0 @@ -From ceb9af48dda3ef7c2542123c15646819e37dc69d Mon Sep 17 00:00:00 2001 -From: Xiao Ni -Date: Sun, 28 Sep 2025 09:24:24 +0800 -Subject: [PATCH 36/51] md: delete mddev kobj before deleting gendisk kobj - -In sync del gendisk path, it deletes gendisk first and the directory -/sys/block/md is removed. Then it releases mddev kobj in a delayed work. -If we enable debug log in sysfs_remove_group, we can see the debug log -'sysfs group bitmap not found for kobject md'. It's the reason that the -parent kobj has been deleted, so it can't find parent directory. - -In creating path, it allocs gendisk first, then adds mddev kobj. So it -should delete mddev kobj before deleting gendisk. - -Before commit 9e59d609763f ("md: call del_gendisk in control path"), it -releases mddev kobj first. If the kobj hasn't been deleted, it does clean -job and deletes the kobj. Then it calls del_gendisk and releases gendisk -kobj. So it doesn't need to call kobject_del to delete mddev kobj. After -this patch, in sync del gendisk path, the sequence changes. So it needs -to call kobject_del to delete mddev kobj. - -After this patch, the sequence is: -1. kobject del mddev kobj -2. del_gendisk deletes gendisk kobj -3. mddev_delayed_delete releases mddev kobj -4. md_kobj_release releases gendisk kobj - -Link: https://lore.kernel.org/linux-raid/20250928012424.61370-1-xni@redhat.com -Fixes: 9e59d609763f ("md: call del_gendisk in control path") -Signed-off-by: Xiao Ni -Reviewed-by: Li Nan -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index b260aff7882c..5aa08663b9bb 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -914,8 +914,10 @@ void mddev_unlock(struct mddev *mddev) - * do_md_stop. dm raid only uses md_stop to stop. So dm raid - * doesn't need to check MD_DELETED when getting reconfig lock - */ -- if (test_bit(MD_DELETED, &mddev->flags)) -+ if (test_bit(MD_DELETED, &mddev->flags)) { -+ kobject_del(&mddev->kobj); - del_gendisk(mddev->gendisk); -+ } - } - } - EXPORT_SYMBOL_GPL(mddev_unlock); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68374.patch b/SPECS/kernel-rt/CVE-2025-68374.patch deleted file mode 100644 index 6fc45f42c..000000000 --- a/SPECS/kernel-rt/CVE-2025-68374.patch +++ /dev/null @@ -1,109 +0,0 @@ -From ae3bcdd6bd49bedc7047b5ba709fba63a171a85a Mon Sep 17 00:00:00 2001 -From: Yun Zhou -Date: Wed, 15 Oct 2025 16:32:27 +0800 -Subject: [PATCH 39/51] md: fix rcu protection in md_wakeup_thread - -We attempted to use RCU to protect the pointer 'thread', but directly -passed the value when calling md_wakeup_thread(). This means that the -RCU pointer has been acquired before rcu_read_lock(), which renders -rcu_read_lock() ineffective and could lead to a use-after-free. - -Link: https://lore.kernel.org/linux-raid/20251015083227.1079009-1-yun.zhou@windriver.com -Fixes: 446931543982 ("md: protect md_thread with rcu") -Signed-off-by: Yun Zhou -Reviewed-by: Li Nan -Reviewed-by: Yu Kuai -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 14 ++++++-------- - drivers/md/md.h | 8 +++++++- - 2 files changed, 13 insertions(+), 9 deletions(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index 55262f7bdf72..ec9d6e29e2d1 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -100,7 +100,7 @@ static int remove_and_add_spares(struct mddev *mddev, - struct md_rdev *this); - static void mddev_detach(struct mddev *mddev); - static void export_rdev(struct md_rdev *rdev, struct mddev *mddev); --static void md_wakeup_thread_directly(struct md_thread __rcu *thread); -+static void md_wakeup_thread_directly(struct md_thread __rcu **thread); - - /* - * Default number of read corrections we'll attempt on an rdev -@@ -5012,7 +5012,7 @@ static void stop_sync_thread(struct mddev *mddev, bool locked) - * Thread might be blocked waiting for metadata update which will now - * never happen - */ -- md_wakeup_thread_directly(mddev->sync_thread); -+ md_wakeup_thread_directly(&mddev->sync_thread); - if (work_pending(&mddev->sync_work)) - flush_work(&mddev->sync_work); - -@@ -8193,22 +8193,21 @@ static int md_thread(void *arg) - return 0; - } - --static void md_wakeup_thread_directly(struct md_thread __rcu *thread) -+static void md_wakeup_thread_directly(struct md_thread __rcu **thread) - { - struct md_thread *t; - - rcu_read_lock(); -- t = rcu_dereference(thread); -+ t = rcu_dereference(*thread); - if (t) - wake_up_process(t->tsk); - rcu_read_unlock(); - } - --void md_wakeup_thread(struct md_thread __rcu *thread) -+void __md_wakeup_thread(struct md_thread __rcu *thread) - { - struct md_thread *t; - -- rcu_read_lock(); - t = rcu_dereference(thread); - if (t) { - pr_debug("md: waking up MD thread %s.\n", t->tsk->comm); -@@ -8216,9 +8215,8 @@ void md_wakeup_thread(struct md_thread __rcu *thread) - if (wq_has_sleeper(&t->wqueue)) - wake_up(&t->wqueue); - } -- rcu_read_unlock(); - } --EXPORT_SYMBOL(md_wakeup_thread); -+EXPORT_SYMBOL(__md_wakeup_thread); - - struct md_thread *md_register_thread(void (*run) (struct md_thread *), - struct mddev *mddev, const char *name) -diff --git a/drivers/md/md.h b/drivers/md/md.h -index e0bc35543217..0a1a227f106b 100644 ---- a/drivers/md/md.h -+++ b/drivers/md/md.h -@@ -879,6 +879,12 @@ struct md_io_clone { - - #define THREAD_WAKEUP 0 - -+#define md_wakeup_thread(thread) do { \ -+ rcu_read_lock(); \ -+ __md_wakeup_thread(thread); \ -+ rcu_read_unlock(); \ -+} while (0) -+ - static inline void safe_put_page(struct page *p) - { - if (p) put_page(p); -@@ -892,7 +898,7 @@ extern struct md_thread *md_register_thread( - struct mddev *mddev, - const char *name); - extern void md_unregister_thread(struct mddev *mddev, struct md_thread __rcu **threadp); --extern void md_wakeup_thread(struct md_thread __rcu *thread); -+extern void __md_wakeup_thread(struct md_thread __rcu *thread); - extern void md_check_recovery(struct mddev *mddev); - extern void md_reap_sync_thread(struct mddev *mddev); - extern enum sync_action md_sync_action(struct mddev *mddev); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68375.patch b/SPECS/kernel-rt/CVE-2025-68375.patch deleted file mode 100644 index 10d9ee902..000000000 --- a/SPECS/kernel-rt/CVE-2025-68375.patch +++ /dev/null @@ -1,93 +0,0 @@ -From e45840cde6f163484bf568430d0ef58b0ed13bfc Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Wed, 29 Oct 2025 18:21:26 +0800 -Subject: [PATCH 11/51] perf/x86: Fix NULL event access and potential PEBS - record loss - -When intel_pmu_drain_pebs_icl() is called to drain PEBS records, the -perf_event_overflow() could be called to process the last PEBS record. - -While perf_event_overflow() could trigger the interrupt throttle and -stop all events of the group, like what the below call-chain shows. - -perf_event_overflow() - -> __perf_event_overflow() - ->__perf_event_account_interrupt() - -> perf_event_throttle_group() - -> perf_event_throttle() - -> event->pmu->stop() - -> x86_pmu_stop() - -The side effect of stopping the events is that all corresponding event -pointers in cpuc->events[] array are cleared to NULL. - -Assume there are two PEBS events (event a and event b) in a group. When -intel_pmu_drain_pebs_icl() calls perf_event_overflow() to process the -last PEBS record of PEBS event a, interrupt throttle is triggered and -all pointers of event a and event b are cleared to NULL. Then -intel_pmu_drain_pebs_icl() tries to process the last PEBS record of -event b and encounters NULL pointer access. - -To avoid this issue, move cpuc->events[] clearing from x86_pmu_stop() -to x86_pmu_del(). It's safe since cpuc->active_mask or -cpuc->pebs_enabled is always checked before access the event pointer -from cpuc->events[]. - -Closes: https://lore.kernel.org/oe-lkp/202507042103.a15d2923-lkp@intel.com -Fixes: 9734e25fbf5a ("perf: Fix the throttle logic for a group") -Reported-by: kernel test robot -Suggested-by: Peter Zijlstra -Signed-off-by: Dapeng Mi -Signed-off-by: Peter Zijlstra (Intel) -Link: https://patch.msgid.link/20251029102136.61364-3-dapeng1.mi@linux.intel.com ---- - arch/x86/events/core.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index add8fc04baf0..d28a05ebac16 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1454,6 +1454,7 @@ static void x86_pmu_enable(struct pmu *pmu) - hwc->state |= PERF_HES_ARCH; - - x86_pmu_stop(event, PERF_EF_UPDATE); -+ cpuc->events[hwc->idx] = NULL; - } - - /* -@@ -1475,6 +1476,7 @@ static void x86_pmu_enable(struct pmu *pmu) - * if cpuc->enabled = 0, then no wrmsr as - * per x86_pmu_enable_event() - */ -+ cpuc->events[hwc->idx] = event; - x86_pmu_start(event, PERF_EF_RELOAD); - } - cpuc->n_added = 0; -@@ -1640,7 +1642,6 @@ static void x86_pmu_start(struct perf_event *event, int flags) - - event->hw.state = 0; - -- cpuc->events[idx] = event; - __set_bit(idx, cpuc->active_mask); - static_call(x86_pmu_enable)(event); - perf_event_update_userpage(event); -@@ -1719,7 +1720,6 @@ void x86_pmu_stop(struct perf_event *event, int flags) - if (test_bit(hwc->idx, cpuc->active_mask)) { - static_call(x86_pmu_disable)(event); - __clear_bit(hwc->idx, cpuc->active_mask); -- cpuc->events[hwc->idx] = NULL; - WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); - hwc->state |= PERF_HES_STOPPED; - } -@@ -1757,6 +1757,7 @@ static void x86_pmu_del(struct perf_event *event, int flags) - * Not a TXN, therefore cleanup properly. - */ - x86_pmu_stop(event, PERF_EF_UPDATE); -+ cpuc->events[event->hw.idx] = NULL; - - for (i = 0; i < cpuc->n_events; i++) { - if (event == cpuc->event_list[i]) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68378.patch b/SPECS/kernel-rt/CVE-2025-68378.patch deleted file mode 100644 index e27b975e0..000000000 --- a/SPECS/kernel-rt/CVE-2025-68378.patch +++ /dev/null @@ -1,107 +0,0 @@ -From eb89fe28dc12b280026c630e5085076fcac932ea Mon Sep 17 00:00:00 2001 -From: Arnaud Lecomte -Date: Sat, 25 Oct 2025 19:29:41 +0000 -Subject: [PATCH 49/51] bpf: Fix stackmap overflow check in __bpf_get_stackid() - -Syzkaller reported a KASAN slab-out-of-bounds write in __bpf_get_stackid() -when copying stack trace data. The issue occurs when the perf trace - contains more stack entries than the stack map bucket can hold, - leading to an out-of-bounds write in the bucket's data array. - -Fixes: ee2a098851bf ("bpf: Adjust BPF stack helper functions to accommodate skip > 0") -Reported-by: syzbot+c9b724fbb41cf2538b7b@syzkaller.appspotmail.com -Signed-off-by: Arnaud Lecomte -Signed-off-by: Andrii Nakryiko -Acked-by: Yonghong Song -Acked-by: Song Liu -Link: https://lore.kernel.org/bpf/20251025192941.1500-1-contact@arnaud-lcm.com - -Closes: https://syzkaller.appspot.com/bug?extid=c9b724fbb41cf2538b7b ---- - kernel/bpf/stackmap.c | 37 ++++++++++++++++++++++++++++++------- - 1 file changed, 30 insertions(+), 7 deletions(-) - -diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c -index 3615c06b7dfa..87a3e09c072a 100644 ---- a/kernel/bpf/stackmap.c -+++ b/kernel/bpf/stackmap.c -@@ -42,6 +42,28 @@ static inline int stack_map_data_size(struct bpf_map *map) - sizeof(struct bpf_stack_build_id) : sizeof(u64); - } - -+/** -+ * stack_map_calculate_max_depth - Calculate maximum allowed stack trace depth -+ * @size: Size of the buffer/map value in bytes -+ * @elem_size: Size of each stack trace element -+ * @flags: BPF stack trace flags (BPF_F_USER_STACK, BPF_F_USER_BUILD_ID, ...) -+ * -+ * Return: Maximum number of stack trace entries that can be safely stored -+ */ -+static u32 stack_map_calculate_max_depth(u32 size, u32 elem_size, u64 flags) -+{ -+ u32 skip = flags & BPF_F_SKIP_FIELD_MASK; -+ u32 max_depth; -+ u32 curr_sysctl_max_stack = READ_ONCE(sysctl_perf_event_max_stack); -+ -+ max_depth = size / elem_size; -+ max_depth += skip; -+ if (max_depth > curr_sysctl_max_stack) -+ return curr_sysctl_max_stack; -+ -+ return max_depth; -+} -+ - static int prealloc_elems_and_freelist(struct bpf_stack_map *smap) - { - u64 elem_size = sizeof(struct stack_map_bucket) + -@@ -229,8 +251,8 @@ static long __bpf_get_stackid(struct bpf_map *map, - { - struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); - struct stack_map_bucket *bucket, *new_bucket, *old_bucket; -+ u32 hash, id, trace_nr, trace_len, i, max_depth; - u32 skip = flags & BPF_F_SKIP_FIELD_MASK; -- u32 hash, id, trace_nr, trace_len, i; - bool user = flags & BPF_F_USER_STACK; - u64 *ips; - bool hash_matches; -@@ -239,7 +261,8 @@ static long __bpf_get_stackid(struct bpf_map *map, - /* skipping more than usable stack trace */ - return -EFAULT; - -- trace_nr = trace->nr - skip; -+ max_depth = stack_map_calculate_max_depth(map->value_size, stack_map_data_size(map), flags); -+ trace_nr = min_t(u32, trace->nr - skip, max_depth - skip); - trace_len = trace_nr * sizeof(u64); - ips = trace->ip + skip; - hash = jhash2((u32 *)ips, trace_len / sizeof(u32), 0); -@@ -371,15 +394,11 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, - return -EFAULT; - - nr_kernel = count_kernel_ip(trace); -+ __u64 nr = trace->nr; /* save original */ - - if (kernel) { -- __u64 nr = trace->nr; -- - trace->nr = nr_kernel; - ret = __bpf_get_stackid(map, trace, flags); -- -- /* restore nr */ -- trace->nr = nr; - } else { /* user */ - u64 skip = flags & BPF_F_SKIP_FIELD_MASK; - -@@ -390,6 +409,10 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, - flags = (flags & ~BPF_F_SKIP_FIELD_MASK) | skip; - ret = __bpf_get_stackid(map, trace, flags); - } -+ -+ /* restore nr */ -+ trace->nr = nr; -+ - return ret; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68379.patch b/SPECS/kernel-rt/CVE-2025-68379.patch deleted file mode 100644 index c519d206d..000000000 --- a/SPECS/kernel-rt/CVE-2025-68379.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 1f4b41aa489a5328e4d7c8ebc49ca038d828f294 Mon Sep 17 00:00:00 2001 -From: Zhu Yanjun -Date: Mon, 27 Oct 2025 14:52:03 -0700 -Subject: [PATCH 10/51] RDMA/rxe: Fix null deref on srq->rq.queue after resize - failure - -A NULL pointer dereference can occur in rxe_srq_chk_attr() when -ibv_modify_srq() is invoked twice in succession under certain error -conditions. The first call may fail in rxe_queue_resize(), which leads -rxe_srq_from_attr() to set srq->rq.queue = NULL. The second call then -triggers a crash (null deref) when accessing -srq->rq.queue->buf->index_mask. - -Call Trace: - -rxe_modify_srq+0x170/0x480 [rdma_rxe] -? __pfx_rxe_modify_srq+0x10/0x10 [rdma_rxe] -? uverbs_try_lock_object+0x4f/0xa0 [ib_uverbs] -? rdma_lookup_get_uobject+0x1f0/0x380 [ib_uverbs] -ib_uverbs_modify_srq+0x204/0x290 [ib_uverbs] -? __pfx_ib_uverbs_modify_srq+0x10/0x10 [ib_uverbs] -? tryinc_node_nr_active+0xe6/0x150 -? uverbs_fill_udata+0xed/0x4f0 [ib_uverbs] -ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0x2c0/0x470 [ib_uverbs] -? __pfx_ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0x10/0x10 [ib_uverbs] -? uverbs_fill_udata+0xed/0x4f0 [ib_uverbs] -ib_uverbs_run_method+0x55a/0x6e0 [ib_uverbs] -? __pfx_ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0x10/0x10 [ib_uverbs] -ib_uverbs_cmd_verbs+0x54d/0x800 [ib_uverbs] -? __pfx_ib_uverbs_cmd_verbs+0x10/0x10 [ib_uverbs] -? __pfx___raw_spin_lock_irqsave+0x10/0x10 -? __pfx_do_vfs_ioctl+0x10/0x10 -? ioctl_has_perm.constprop.0.isra.0+0x2c7/0x4c0 -? __pfx_ioctl_has_perm.constprop.0.isra.0+0x10/0x10 -ib_uverbs_ioctl+0x13e/0x220 [ib_uverbs] -? __pfx_ib_uverbs_ioctl+0x10/0x10 [ib_uverbs] -__x64_sys_ioctl+0x138/0x1c0 -do_syscall_64+0x82/0x250 -? fdget_pos+0x58/0x4c0 -? ksys_write+0xf3/0x1c0 -? __pfx_ksys_write+0x10/0x10 -? do_syscall_64+0xc8/0x250 -? __pfx_vm_mmap_pgoff+0x10/0x10 -? fget+0x173/0x230 -? fput+0x2a/0x80 -? ksys_mmap_pgoff+0x224/0x4c0 -? do_syscall_64+0xc8/0x250 -? do_user_addr_fault+0x37b/0xfe0 -? clear_bhb_loop+0x50/0xa0 -? clear_bhb_loop+0x50/0xa0 -? clear_bhb_loop+0x50/0xa0 -entry_SYSCALL_64_after_hwframe+0x76/0x7e - -Fixes: 8700e3e7c485 ("Soft RoCE driver") -Tested-by: Liu Yi -Signed-off-by: Zhu Yanjun -Link: https://patch.msgid.link/20251027215203.1321-1-yanjun.zhu@linux.dev -Signed-off-by: Leon Romanovsky ---- - drivers/infiniband/sw/rxe/rxe_srq.c | 7 +------ - 1 file changed, 1 insertion(+), 6 deletions(-) - -diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c -index 3661cb627d28..2a234f26ac10 100644 ---- a/drivers/infiniband/sw/rxe/rxe_srq.c -+++ b/drivers/infiniband/sw/rxe/rxe_srq.c -@@ -171,7 +171,7 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, - udata, mi, &srq->rq.producer_lock, - &srq->rq.consumer_lock); - if (err) -- goto err_free; -+ return err; - - srq->rq.max_wr = attr->max_wr; - } -@@ -180,11 +180,6 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, - srq->limit = attr->srq_limit; - - return 0; -- --err_free: -- rxe_queue_cleanup(q); -- srq->rq.queue = NULL; -- return err; - } - - void rxe_srq_cleanup(struct rxe_pool_elem *elem) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68724.patch b/SPECS/kernel-rt/CVE-2025-68724.patch deleted file mode 100644 index 3c35eee5a..000000000 --- a/SPECS/kernel-rt/CVE-2025-68724.patch +++ /dev/null @@ -1,58 +0,0 @@ -From d6a0faaefb15d8f072edb31a1c76441dc49b45f0 Mon Sep 17 00:00:00 2001 -From: Thorsten Blum -Date: Mon, 13 Oct 2025 13:40:10 +0200 -Subject: [PATCH 46/51] crypto: asymmetric_keys - prevent overflow in - asymmetric_key_generate_id - -Use check_add_overflow() to guard against potential integer overflows -when adding the binary blob lengths and the size of an asymmetric_key_id -structure and return ERR_PTR(-EOVERFLOW) accordingly. This prevents a -possible buffer overflow when copying data from potentially malicious -X.509 certificate fields that can be arbitrarily large, such as ASN.1 -INTEGER serial numbers, issuer names, etc. - -Fixes: 7901c1a8effb ("KEYS: Implement binary asymmetric key ID handling") -Signed-off-by: Thorsten Blum -Reviewed-by: Lukas Wunner -Signed-off-by: Herbert Xu ---- - crypto/asymmetric_keys/asymmetric_type.c | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c -index ba2d9d1ea235..348966ea2175 100644 ---- a/crypto/asymmetric_keys/asymmetric_type.c -+++ b/crypto/asymmetric_keys/asymmetric_type.c -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -141,12 +142,17 @@ struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, - size_t len_2) - { - struct asymmetric_key_id *kid; -- -- kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, -- GFP_KERNEL); -+ size_t kid_sz; -+ size_t len; -+ -+ if (check_add_overflow(len_1, len_2, &len)) -+ return ERR_PTR(-EOVERFLOW); -+ if (check_add_overflow(sizeof(struct asymmetric_key_id), len, &kid_sz)) -+ return ERR_PTR(-EOVERFLOW); -+ kid = kmalloc(kid_sz, GFP_KERNEL); - if (!kid) - return ERR_PTR(-ENOMEM); -- kid->len = len_1 + len_2; -+ kid->len = len; - memcpy(kid->data, val_1, len_1); - memcpy(kid->data + len_1, val_2, len_2); - return kid; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68725.patch b/SPECS/kernel-rt/CVE-2025-68725.patch deleted file mode 100644 index dba15828b..000000000 --- a/SPECS/kernel-rt/CVE-2025-68725.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 7b4c169fc288f3e11fabc60de37408991505f87e Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Mon, 20 Oct 2025 09:54:41 +0200 -Subject: [PATCH 50/51] bpf: Do not let BPF test infra emit invalid GSO types - to stack - -Yinhao et al. reported that their fuzzer tool was able to trigger a -skb_warn_bad_offload() from netif_skb_features() -> gso_features_check(). -When a BPF program - triggered via BPF test infra - pushes the packet -to the loopback device via bpf_clone_redirect() then mentioned offload -warning can be seen. GSO-related features are then rightfully disabled. - -We get into this situation due to convert___skb_to_skb() setting -gso_segs and gso_size but not gso_type. Technically, it makes sense -that this warning triggers since the GSO properties are malformed due -to the gso_type. Potentially, the gso_type could be marked non-trustworthy -through setting it at least to SKB_GSO_DODGY without any other specific -assumptions, but that also feels wrong given we should not go further -into the GSO engine in the first place. - -The checks were added in 121d57af308d ("gso: validate gso_type in GSO -handlers") because there were malicious (syzbot) senders that combine -a protocol with a non-matching gso_type. If we would want to drop such -packets, gso_features_check() currently only returns feature flags via -netif_skb_features(), so one location for potentially dropping such skbs -could be validate_xmit_unreadable_skb(), but then otoh it would be -an additional check in the fast-path for a very corner case. Given -bpf_clone_redirect() is the only place where BPF test infra could emit -such packets, lets reject them right there. - -Fixes: 850a88cc4096 ("bpf: Expose __sk_buff wire_len/gso_segs to BPF_PROG_TEST_RUN") -Fixes: cf62089b0edd ("bpf: Add gso_size to __sk_buff") -Reported-by: Yinhao Hu -Reported-by: Kaiyan Mei -Reported-by: Dongliang Mu -Signed-off-by: Daniel Borkmann -Signed-off-by: Martin KaFai Lau -Link: https://patch.msgid.link/20251020075441.127980-1-daniel@iogearbox.net ---- - net/bpf/test_run.c | 5 +++++ - net/core/filter.c | 7 +++++++ - 2 files changed, 12 insertions(+) - -diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c -index 9728dbd4c66c..848be4ce7226 100644 ---- a/net/bpf/test_run.c -+++ b/net/bpf/test_run.c -@@ -950,6 +950,11 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) - - if (__skb->gso_segs > GSO_MAX_SEGS) - return -EINVAL; -+ -+ /* Currently GSO type is zero/unset. If this gets extended with -+ * a small list of accepted GSO types in future, the filter for -+ * an unset GSO type in bpf_clone_redirect() can be lifted. -+ */ - skb_shinfo(skb)->gso_segs = __skb->gso_segs; - skb_shinfo(skb)->gso_size = __skb->gso_size; - skb_shinfo(skb)->hwtstamps.hwtstamp = __skb->hwtstamp; -diff --git a/net/core/filter.c b/net/core/filter.c -index 47366ec94e58..5185e1245c5d 100644 ---- a/net/core/filter.c -+++ b/net/core/filter.c -@@ -2458,6 +2458,13 @@ BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags) - if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))) - return -EINVAL; - -+ /* BPF test infra's convert___skb_to_skb() can create type-less -+ * GSO packets. gso_features_check() will detect this as a bad -+ * offload. However, lets not leak them out in the first place. -+ */ -+ if (unlikely(skb_is_gso(skb) && !skb_shinfo(skb)->gso_type)) -+ return -EBADMSG; -+ - dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex); - if (unlikely(!dev)) - return -EINVAL; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68730.patch b/SPECS/kernel-rt/CVE-2025-68730.patch deleted file mode 100644 index d1f7c20d8..000000000 --- a/SPECS/kernel-rt/CVE-2025-68730.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 58a027fb9bf73f41dccbae40ea2827d5adaf8b49 Mon Sep 17 00:00:00 2001 -From: Jacek Lawrynowicz -Date: Thu, 25 Sep 2025 16:51:14 +0200 -Subject: [PATCH 14/51] accel/ivpu: Fix page fault in - ivpu_bo_unbind_all_bos_from_context() - -Don't add BO to the vdev->bo_list in ivpu_gem_create_object(). -When failure happens inside drm_gem_shmem_create(), the BO is not -fully created and ivpu_gem_bo_free() callback will not be called -causing a deleted BO to be left on the list. - -Fixes: 8d88e4cdce4f ("accel/ivpu: Use GEM shmem helper for all buffers") -Signed-off-by: Jacek Lawrynowicz -Signed-off-by: Maciej Falkowski -Reviewed-by: Karol Wachowski -Signed-off-by: Karol Wachowski -Link: https://lore.kernel.org/r/20250925145114.1446283-1-maciej.falkowski@linux.intel.com ---- - drivers/accel/ivpu/ivpu_gem.c | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c -index 4c0898102697..7b997ae83a77 100644 ---- a/drivers/accel/ivpu/ivpu_gem.c -+++ b/drivers/accel/ivpu/ivpu_gem.c -@@ -182,10 +182,12 @@ struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t siz - struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) - { -+ struct ivpu_device *vdev = to_ivpu_device(dev); - struct device *attach_dev = dev->dev; - struct dma_buf_attachment *attach; - struct sg_table *sgt; - struct drm_gem_object *obj; -+ struct ivpu_bo *bo; - int ret; - - attach = dma_buf_attach(dma_buf, attach_dev); -@@ -209,6 +211,14 @@ struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, - obj->import_attach = attach; - obj->resv = dma_buf->resv; - -+ bo = to_ivpu_bo(obj); -+ -+ mutex_lock(&vdev->bo_list_lock); -+ list_add_tail(&bo->bo_list_node, &vdev->bo_list); -+ mutex_unlock(&vdev->bo_list_lock); -+ -+ ivpu_dbg(vdev, BO, "import: bo %8p size %9zu\n", bo, ivpu_bo_size(bo)); -+ - return obj; - - fail_unmap: -@@ -246,7 +256,7 @@ static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 fla - list_add_tail(&bo->bo_list_node, &vdev->bo_list); - mutex_unlock(&vdev->bo_list_lock); - -- ivpu_dbg_bo(vdev, bo, "alloc"); -+ ivpu_dbg(vdev, BO, " alloc: bo %8p size %9llu\n", bo, size); - - return bo; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68732.patch b/SPECS/kernel-rt/CVE-2025-68732.patch deleted file mode 100644 index b85b3eeb9..000000000 --- a/SPECS/kernel-rt/CVE-2025-68732.patch +++ /dev/null @@ -1,53 +0,0 @@ -From f090523b6281ef5ccedf4198a1bb83dbe8ae5930 Mon Sep 17 00:00:00 2001 -From: Mainak Sen -Date: Mon, 7 Jul 2025 18:17:39 +0900 -Subject: [PATCH 13/51] gpu: host1x: Fix race in syncpt alloc/free - -Fix race condition between host1x_syncpt_alloc() -and host1x_syncpt_put() by using kref_put_mutex() -instead of kref_put() + manual mutex locking. - -This ensures no thread can acquire the -syncpt_mutex after the refcount drops to zero -but before syncpt_release acquires it. -This prevents races where syncpoints could -be allocated while still being cleaned up -from a previous release. - -Remove explicit mutex locking in syncpt_release -as kref_put_mutex() handles this atomically. - -Signed-off-by: Mainak Sen -Fixes: f5ba33fb9690 ("gpu: host1x: Reserve VBLANK syncpoints at initialization") -Signed-off-by: Mikko Perttunen -Signed-off-by: Thierry Reding -Link: https://lore.kernel.org/r/20250707-host1x-syncpt-race-fix-v1-1-28b0776e70bc@nvidia.com ---- - drivers/gpu/host1x/syncpt.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c -index f63d14a57a1d..acc7d82e0585 100644 ---- a/drivers/gpu/host1x/syncpt.c -+++ b/drivers/gpu/host1x/syncpt.c -@@ -345,8 +345,6 @@ static void syncpt_release(struct kref *ref) - - sp->locked = false; - -- mutex_lock(&sp->host->syncpt_mutex); -- - host1x_syncpt_base_free(sp->base); - kfree(sp->name); - sp->base = NULL; -@@ -369,7 +367,7 @@ void host1x_syncpt_put(struct host1x_syncpt *sp) - if (!sp) - return; - -- kref_put(&sp->ref, syncpt_release); -+ kref_put_mutex(&sp->ref, syncpt_release, &sp->host->syncpt_mutex); - } - EXPORT_SYMBOL(host1x_syncpt_put); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68733.patch b/SPECS/kernel-rt/CVE-2025-68733.patch deleted file mode 100644 index d46b24edf..000000000 --- a/SPECS/kernel-rt/CVE-2025-68733.patch +++ /dev/null @@ -1,96 +0,0 @@ -From ad55184080e5f96a9a37d6c82c2a36a8b228005b Mon Sep 17 00:00:00 2001 -From: Konstantin Andreev -Date: Tue, 17 Jun 2025 00:32:16 +0300 -Subject: [PATCH 16/51] smack: fix bug: unprivileged task can create labels - -If an unprivileged task is allowed to relabel itself -(/smack/relabel-self is not empty), -it can freely create new labels by writing their -names into own /proc/PID/attr/smack/current - -This occurs because do_setattr() imports -the provided label in advance, -before checking "relabel-self" list. - -This change ensures that the "relabel-self" list -is checked before importing the label. - -Fixes: 38416e53936e ("Smack: limited capability for changing process label") -Signed-off-by: Konstantin Andreev -Signed-off-by: Casey Schaufler ---- - security/smack/smack_lsm.c | 41 +++++++++++++++++++++++++------------- - 1 file changed, 27 insertions(+), 14 deletions(-) - -diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c -index fc340a6f0dde..834458b40895 100644 ---- a/security/smack/smack_lsm.c -+++ b/security/smack/smack_lsm.c -@@ -3731,8 +3731,8 @@ static int do_setattr(u64 attr, void *value, size_t size) - struct task_smack *tsp = smack_cred(current_cred()); - struct cred *new; - struct smack_known *skp; -- struct smack_known_list_elem *sklep; -- int rc; -+ char *labelstr; -+ int rc = 0; - - if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) - return -EPERM; -@@ -3743,28 +3743,41 @@ static int do_setattr(u64 attr, void *value, size_t size) - if (attr != LSM_ATTR_CURRENT) - return -EOPNOTSUPP; - -- skp = smk_import_entry(value, size); -- if (IS_ERR(skp)) -- return PTR_ERR(skp); -+ labelstr = smk_parse_smack(value, size); -+ if (IS_ERR(labelstr)) -+ return PTR_ERR(labelstr); - - /* - * No process is ever allowed the web ("@") label - * and the star ("*") label. - */ -- if (skp == &smack_known_web || skp == &smack_known_star) -- return -EINVAL; -+ if (labelstr[1] == '\0' /* '@', '*' */) { -+ const char c = labelstr[0]; -+ -+ if (c == *smack_known_web.smk_known || -+ c == *smack_known_star.smk_known) { -+ rc = -EPERM; -+ goto free_labelstr; -+ } -+ } - - if (!smack_privileged(CAP_MAC_ADMIN)) { -- rc = -EPERM; -+ const struct smack_known_list_elem *sklep; - list_for_each_entry(sklep, &tsp->smk_relabel, list) -- if (sklep->smk_label == skp) { -- rc = 0; -- break; -- } -- if (rc) -- return rc; -+ if (strcmp(sklep->smk_label->smk_known, labelstr) == 0) -+ goto free_labelstr; -+ rc = -EPERM; - } - -+free_labelstr: -+ kfree(labelstr); -+ if (rc) -+ return -EPERM; -+ -+ skp = smk_import_entry(value, size); -+ if (IS_ERR(skp)) -+ return PTR_ERR(skp); -+ - new = prepare_creds(); - if (new == NULL) - return -ENOMEM; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68736.patch b/SPECS/kernel-rt/CVE-2025-68736.patch deleted file mode 100644 index ffe051d07..000000000 --- a/SPECS/kernel-rt/CVE-2025-68736.patch +++ /dev/null @@ -1,166 +0,0 @@ -From bc3496008e6039fed140466b7c6f7d86920b4437 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= -Date: Wed, 7 Jan 2026 19:28:19 -0800 -Subject: [PATCH 12/51] landlock: Fix handling of disconnected directories -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Disconnected files or directories can appear when they are visible and -opened from a bind mount, but have been renamed or moved from the source -of the bind mount in a way that makes them inaccessible from the mount -point (i.e. out of scope). - -Previously, access rights tied to files or directories opened through a -disconnected directory were collected by walking the related hierarchy -down to the root of the filesystem, without taking into account the -mount point because it couldn't be found. This could lead to -inconsistent access results, potential access right widening, and -hard-to-debug renames, especially since such paths cannot be printed. - -For a sandboxed task to create a disconnected directory, it needs to -have write access (i.e. FS_MAKE_REG, FS_REMOVE_FILE, and FS_REFER) to -the underlying source of the bind mount, and read access to the related -mount point. Because a sandboxed task cannot acquire more access -rights than those defined by its Landlock domain, this could lead to -inconsistent access rights due to missing permissions that should be -inherited from the mount point hierarchy, while inheriting permissions -from the filesystem hierarchy hidden by this mount point instead. - -Landlock now handles files and directories opened from disconnected -directories by taking into account the filesystem hierarchy when the -mount point is not found in the hierarchy walk, and also always taking -into account the mount point from which these disconnected directories -were opened. This ensures that a rename is not allowed if it would -widen access rights [1]. - -The rationale is that, even if disconnected hierarchies might not be -visible or accessible to a sandboxed task, relying on the collected -access rights from them improves the guarantee that access rights will -not be widened during a rename because of the access right comparison -between the source and the destination (see LANDLOCK_ACCESS_FS_REFER). -It may look like this would grant more access on disconnected files and -directories, but the security policies are always enforced for all the -evaluated hierarchies. This new behavior should be less surprising to -users and safer from an access control perspective. - -Remove a wrong WARN_ON_ONCE() canary in collect_domain_accesses() and -fix the related comment. - -Because opened files have their access rights stored in the related file -security properties, there is no impact for disconnected or unlinked -files. - -Cc: Christian Brauner -Cc: Günther Noack -Cc: Song Liu -Reported-by: Tingmao Wang -Closes: https://lore.kernel.org/r/027d5190-b37a-40a8-84e9-4ccbc352bcdf@maowtm.org -Closes: https://lore.kernel.org/r/09b24128f86973a6022e6aa8338945fcfb9a33e4.1749925391.git.m@maowtm.org -Fixes: b91c3e4ea756 ("landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER") -Fixes: cb2c7d1a1776 ("landlock: Support filesystem access-control") -Link: https://lore.kernel.org/r/b0f46246-f2c5-42ca-93ce-0d629702a987@maowtm.org [1] -Reviewed-by: Tingmao Wang -Link: https://lore.kernel.org/r/20251128172200.760753-2-mic@digikod.net -Signed-off-by: Mickaël Salaün ---- - security/landlock/errata/abi-1.h | 16 +++++++++++++ - security/landlock/fs.c | 40 ++++++++++++++++++++++---------- - 2 files changed, 44 insertions(+), 12 deletions(-) - create mode 100644 security/landlock/errata/abi-1.h - -diff --git a/security/landlock/errata/abi-1.h b/security/landlock/errata/abi-1.h -new file mode 100644 -index 000000000000..e8a2bff2e5b6 ---- /dev/null -+++ b/security/landlock/errata/abi-1.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+ -+/** -+ * DOC: erratum_3 -+ * -+ * Erratum 3: Disconnected directory handling -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This fix addresses an issue with disconnected directories that occur when a -+ * directory is moved outside the scope of a bind mount. The change ensures -+ * that evaluated access rights include both those from the disconnected file -+ * hierarchy down to its filesystem root and those from the related mount point -+ * hierarchy. This prevents access right widening through rename or link -+ * actions. -+ */ -+LANDLOCK_ERRATUM(3) -diff --git a/security/landlock/fs.c b/security/landlock/fs.c -index c04f8879ad03..725440836771 100644 ---- a/security/landlock/fs.c -+++ b/security/landlock/fs.c -@@ -909,21 +909,31 @@ static bool is_access_to_paths_allowed( - break; - } - } -+ - if (unlikely(IS_ROOT(walker_path.dentry))) { -- /* -- * Stops at disconnected root directories. Only allows -- * access to internal filesystems (e.g. nsfs, which is -- * reachable through /proc//ns/). -- */ -- if (walker_path.mnt->mnt_flags & MNT_INTERNAL) { -+ if (likely(walker_path.mnt->mnt_flags & MNT_INTERNAL)) { -+ /* -+ * Stops and allows access when reaching disconnected root -+ * directories that are part of internal filesystems (e.g. nsfs, -+ * which is reachable through /proc//ns/). -+ */ - allowed_parent1 = true; - allowed_parent2 = true; -+ break; - } -- break; -+ -+ /* -+ * We reached a disconnected root directory from a bind mount. -+ * Let's continue the walk with the mount point we missed. -+ */ -+ dput(walker_path.dentry); -+ walker_path.dentry = walker_path.mnt->mnt_root; -+ dget(walker_path.dentry); -+ } else { -+ parent_dentry = dget_parent(walker_path.dentry); -+ dput(walker_path.dentry); -+ walker_path.dentry = parent_dentry; - } -- parent_dentry = dget_parent(walker_path.dentry); -- dput(walker_path.dentry); -- walker_path.dentry = parent_dentry; - } - path_put(&walker_path); - -@@ -1021,6 +1031,9 @@ static access_mask_t maybe_remove(const struct dentry *const dentry) - * file. While walking from @dir to @mnt_root, we record all the domain's - * allowed accesses in @layer_masks_dom. - * -+ * Because of disconnected directories, this walk may not reach @mnt_dir. In -+ * this case, the walk will continue to @mnt_dir after this call. -+ * - * This is similar to is_access_to_paths_allowed() but much simpler because it - * only handles walking on the same mount point and only checks one set of - * accesses. -@@ -1062,8 +1075,11 @@ static bool collect_domain_accesses( - break; - } - -- /* We should not reach a root other than @mnt_root. */ -- if (dir == mnt_root || WARN_ON_ONCE(IS_ROOT(dir))) -+ /* -+ * Stops at the mount point or the filesystem root for a disconnected -+ * directory. -+ */ -+ if (dir == mnt_root || unlikely(IS_ROOT(dir))) - break; - - parent_dentry = dget_parent(dir); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68740.patch b/SPECS/kernel-rt/CVE-2025-68740.patch deleted file mode 100644 index 6187120bf..000000000 --- a/SPECS/kernel-rt/CVE-2025-68740.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 0429f67c4e1b72e706819940e4d51d9edbbaeeb5 Mon Sep 17 00:00:00 2001 -From: Zhao Yipeng -Date: Thu, 20 Nov 2025 15:18:05 +0800 -Subject: [PATCH 38/51] ima: Handle error code returned by - ima_filter_rule_match() - -In ima_match_rules(), if ima_filter_rule_match() returns -ENOENT due to -the rule being NULL, the function incorrectly skips the 'if (!rc)' check -and sets 'result = true'. The LSM rule is considered a match, causing -extra files to be measured by IMA. - -This issue can be reproduced in the following scenario: -After unloading the SELinux policy module via 'semodule -d', if an IMA -measurement is triggered before ima_lsm_rules is updated, -in ima_match_rules(), the first call to ima_filter_rule_match() returns --ESTALE. This causes the code to enter the 'if (rc == -ESTALE && -!rule_reinitialized)' block, perform ima_lsm_copy_rule() and retry. In -ima_lsm_copy_rule(), since the SELinux module has been removed, the rule -becomes NULL, and the second call to ima_filter_rule_match() returns --ENOENT. This bypasses the 'if (!rc)' check and results in a false match. - -Call trace: - selinux_audit_rule_match+0x310/0x3b8 - security_audit_rule_match+0x60/0xa0 - ima_match_rules+0x2e4/0x4a0 - ima_match_policy+0x9c/0x1e8 - ima_get_action+0x48/0x60 - process_measurement+0xf8/0xa98 - ima_bprm_check+0x98/0xd8 - security_bprm_check+0x5c/0x78 - search_binary_handler+0x6c/0x318 - exec_binprm+0x58/0x1b8 - bprm_execve+0xb8/0x130 - do_execveat_common.isra.0+0x1a8/0x258 - __arm64_sys_execve+0x48/0x68 - invoke_syscall+0x50/0x128 - el0_svc_common.constprop.0+0xc8/0xf0 - do_el0_svc+0x24/0x38 - el0_svc+0x44/0x200 - el0t_64_sync_handler+0x100/0x130 - el0t_64_sync+0x3c8/0x3d0 - -Fix this by changing 'if (!rc)' to 'if (rc <= 0)' to ensure that error -codes like -ENOENT do not bypass the check and accidentally result in a -successful match. - -Fixes: 4af4662fa4a9d ("integrity: IMA policy") -Signed-off-by: Zhao Yipeng -Reviewed-by: Roberto Sassu -Signed-off-by: Mimi Zohar ---- - security/integrity/ima/ima_policy.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c -index 128fab897930..db6d55af5a80 100644 ---- a/security/integrity/ima/ima_policy.c -+++ b/security/integrity/ima/ima_policy.c -@@ -674,7 +674,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, - goto retry; - } - } -- if (!rc) { -+ if (rc <= 0) { - result = false; - goto out; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68741.patch b/SPECS/kernel-rt/CVE-2025-68741.patch deleted file mode 100644 index 428374b9c..000000000 --- a/SPECS/kernel-rt/CVE-2025-68741.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 99c7757fe51f5f72bacacd88c7909ed9844f9de6 Mon Sep 17 00:00:00 2001 -From: Zilin Guan -Date: Thu, 13 Nov 2025 15:12:46 +0000 -Subject: [PATCH 31/51] scsi: qla2xxx: Fix improper freeing of purex item - -In qla2xxx_process_purls_iocb(), an item is allocated via -qla27xx_copy_multiple_pkt(), which internally calls -qla24xx_alloc_purex_item(). - -The qla24xx_alloc_purex_item() function may return a pre-allocated item -from a per-adapter pool for small allocations, instead of dynamically -allocating memory with kzalloc(). - -An error handling path in qla2xxx_process_purls_iocb() incorrectly uses -kfree() to release the item. If the item was from the pre-allocated -pool, calling kfree() on it is a bug that can lead to memory corruption. - -Fix this by using the correct deallocation function, -qla24xx_free_purex_item(), which properly handles both dynamically -allocated and pre-allocated items. - -Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe") -Signed-off-by: Zilin Guan -Reviewed-by: Himanshu Madhani -Link: https://patch.msgid.link/20251113151246.762510-1-zilin@seu.edu.cn -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/qla2xxx/qla_nvme.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c -index 316594aa40cc..42eb65a62f1f 100644 ---- a/drivers/scsi/qla2xxx/qla_nvme.c -+++ b/drivers/scsi/qla2xxx/qla_nvme.c -@@ -1292,7 +1292,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp) - a.reason = FCNVME_RJT_RC_LOGIC; - a.explanation = FCNVME_RJT_EXP_NONE; - xmt_reject = true; -- kfree(item); -+ qla24xx_free_purex_item(item); - goto out; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68742.patch b/SPECS/kernel-rt/CVE-2025-68742.patch deleted file mode 100644 index c28490007..000000000 --- a/SPECS/kernel-rt/CVE-2025-68742.patch +++ /dev/null @@ -1,87 +0,0 @@ -From e1789aa8bb9c5635ce265730122a9e7d7a68243b Mon Sep 17 00:00:00 2001 -From: Pu Lehui -Date: Sat, 15 Nov 2025 10:23:43 +0000 -Subject: [PATCH 40/51] bpf: Fix invalid prog->stats access when - update_effective_progs fails - -Syzkaller triggers an invalid memory access issue following fault -injection in update_effective_progs. The issue can be described as -follows: - -__cgroup_bpf_detach - update_effective_progs - compute_effective_progs - bpf_prog_array_alloc <-- fault inject - purge_effective_progs - /* change to dummy_bpf_prog */ - array->items[index] = &dummy_bpf_prog.prog - ----softirq start--- -__do_softirq - ... - __cgroup_bpf_run_filter_skb - __bpf_prog_run_save_cb - bpf_prog_run - stats = this_cpu_ptr(prog->stats) - /* invalid memory access */ - flags = u64_stats_update_begin_irqsave(&stats->syncp) ----softirq end--- - - static_branch_dec(&cgroup_bpf_enabled_key[atype]) - -The reason is that fault injection caused update_effective_progs to fail -and then changed the original prog into dummy_bpf_prog.prog in -purge_effective_progs. Then a softirq came, and accessing the members of -dummy_bpf_prog.prog in the softirq triggers invalid mem access. - -To fix it, skip updating stats when stats is NULL. - -Fixes: 492ecee892c2 ("bpf: enable program stats") -Signed-off-by: Pu Lehui -Link: https://lore.kernel.org/r/20251115102343.2200727-1-pulehui@huaweicloud.com -Signed-off-by: Alexei Starovoitov ---- - include/linux/filter.h | 12 +++++++----- - kernel/bpf/syscall.c | 3 +++ - 2 files changed, 10 insertions(+), 5 deletions(-) - -diff --git a/include/linux/filter.h b/include/linux/filter.h -index 152f2fc7b65a..70e2f5051676 100644 ---- a/include/linux/filter.h -+++ b/include/linux/filter.h -@@ -709,11 +709,13 @@ static __always_inline u32 __bpf_prog_run(const struct bpf_prog *prog, - ret = dfunc(ctx, prog->insnsi, prog->bpf_func); - - duration = sched_clock() - start; -- stats = this_cpu_ptr(prog->stats); -- flags = u64_stats_update_begin_irqsave(&stats->syncp); -- u64_stats_inc(&stats->cnt); -- u64_stats_add(&stats->nsecs, duration); -- u64_stats_update_end_irqrestore(&stats->syncp, flags); -+ if (likely(prog->stats)) { -+ stats = this_cpu_ptr(prog->stats); -+ flags = u64_stats_update_begin_irqsave(&stats->syncp); -+ u64_stats_inc(&stats->cnt); -+ u64_stats_add(&stats->nsecs, duration); -+ u64_stats_update_end_irqrestore(&stats->syncp, flags); -+ } - } else { - ret = dfunc(ctx, prog->insnsi, prog->bpf_func); - } -diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c -index 0fbfa8532c39..c7b4f597a293 100644 ---- a/kernel/bpf/syscall.c -+++ b/kernel/bpf/syscall.c -@@ -2406,6 +2406,9 @@ void notrace bpf_prog_inc_misses_counter(struct bpf_prog *prog) - struct bpf_prog_stats *stats; - unsigned int flags; - -+ if (unlikely(!prog->stats)) -+ return; -+ - stats = this_cpu_ptr(prog->stats); - flags = u64_stats_update_begin_irqsave(&stats->syncp); - u64_stats_inc(&stats->misses); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68743.patch b/SPECS/kernel-rt/CVE-2025-68743.patch deleted file mode 100644 index 46547393c..000000000 --- a/SPECS/kernel-rt/CVE-2025-68743.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 344804c70dbf3af503917e969c083c088fc9ec5b Mon Sep 17 00:00:00 2001 -From: Nuno Das Neves -Date: Wed, 7 Jan 2026 18:35:30 -0800 -Subject: [PATCH 45/51] mshv: Fix create memory region overlap check - -The current check is incorrect; it only checks if the beginning or end -of a region is within an existing region. This doesn't account for -userspace specifying a region that begins before and ends after an -existing region. - -Change the logic to a range intersection check against gfns and uaddrs -for each region. - -Remove mshv_partition_region_by_uaddr() as it is no longer used. - -Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") -Reported-by: Michael Kelley -Closes: https://lore.kernel.org/linux-hyperv/SN6PR02MB41575BE0406D3AB22E1D7DB5D4C2A@SN6PR02MB4157.namprd02.prod.outlook.com/ -Signed-off-by: Nuno Das Neves -Reviewed-by: Michael Kelley -Signed-off-by: Wei Liu -(cherry picked from commit ba9eb9b86d232854e983203dc2fb1ba18e316681) ---- - drivers/hv/mshv_root_main.c | 31 +++++++++++-------------------- - 1 file changed, 11 insertions(+), 20 deletions(-) - -diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c -index cad09ff5f94d..999c73d19de2 100644 ---- a/drivers/hv/mshv_root_main.c -+++ b/drivers/hv/mshv_root_main.c -@@ -1198,21 +1198,6 @@ mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn) - return NULL; - } - --static struct mshv_mem_region * --mshv_partition_region_by_uaddr(struct mshv_partition *partition, u64 uaddr) --{ -- struct mshv_mem_region *region; -- -- hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) { -- if (uaddr >= region->start_uaddr && -- uaddr < region->start_uaddr + -- (region->nr_pages << HV_HYP_PAGE_SHIFT)) -- return region; -- } -- -- return NULL; --} -- - /* - * NB: caller checks and makes sure mem->size is page aligned - * Returns: 0 with regionpp updated on success, or -errno -@@ -1222,15 +1207,21 @@ static int mshv_partition_create_region(struct mshv_partition *partition, - struct mshv_mem_region **regionpp, - bool is_mmio) - { -- struct mshv_mem_region *region; -+ struct mshv_mem_region *region, *rg; - u64 nr_pages = HVPFN_DOWN(mem->size); - - /* Reject overlapping regions */ -- if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) || -- mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1) || -- mshv_partition_region_by_uaddr(partition, mem->userspace_addr) || -- mshv_partition_region_by_uaddr(partition, mem->userspace_addr + mem->size - 1)) -+ hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) { -+ u64 rg_size = rg->nr_pages << HV_HYP_PAGE_SHIFT; -+ -+ if ((mem->guest_pfn + nr_pages <= rg->start_gfn || -+ rg->start_gfn + rg->nr_pages <= mem->guest_pfn) && -+ (mem->userspace_addr + mem->size <= rg->start_uaddr || -+ rg->start_uaddr + rg_size <= mem->userspace_addr)) -+ continue; -+ - return -EEXIST; -+ } - - region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages); - if (!region) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68744.patch b/SPECS/kernel-rt/CVE-2025-68744.patch deleted file mode 100644 index eb2513fd3..000000000 --- a/SPECS/kernel-rt/CVE-2025-68744.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 324978028345fa41753231006bc6ff774893375f Mon Sep 17 00:00:00 2001 -From: Leon Hwang -Date: Wed, 5 Nov 2025 23:14:06 +0800 -Subject: [PATCH 06/51] bpf: Free special fields when update [lru_,]percpu_hash - maps - -As [lru_,]percpu_hash maps support BPF_KPTR_{REF,PERCPU}, missing -calls to 'bpf_obj_free_fields()' in 'pcpu_copy_value()' could cause the -memory referenced by BPF_KPTR_{REF,PERCPU} fields to be held until the -map gets freed. - -Fix this by calling 'bpf_obj_free_fields()' after -'copy_map_value[,_long]()' in 'pcpu_copy_value()'. - -Fixes: 65334e64a493 ("bpf: Support kptrs in percpu hashmap and percpu LRU hashmap") -Signed-off-by: Leon Hwang -Acked-by: Yonghong Song -Link: https://lore.kernel.org/r/20251105151407.12723-2-leon.hwang@linux.dev -Signed-off-by: Alexei Starovoitov ---- - kernel/bpf/hashtab.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c -index 71f9931ac64c..f3bc22ea503f 100644 ---- a/kernel/bpf/hashtab.c -+++ b/kernel/bpf/hashtab.c -@@ -939,15 +939,21 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) - static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, - void *value, bool onallcpus) - { -+ void *ptr; -+ - if (!onallcpus) { - /* copy true value_size bytes */ -- copy_map_value(&htab->map, this_cpu_ptr(pptr), value); -+ ptr = this_cpu_ptr(pptr); -+ copy_map_value(&htab->map, ptr, value); -+ bpf_obj_free_fields(htab->map.record, ptr); - } else { - u32 size = round_up(htab->map.value_size, 8); - int off = 0, cpu; - - for_each_possible_cpu(cpu) { -- copy_map_value_long(&htab->map, per_cpu_ptr(pptr, cpu), value + off); -+ ptr = per_cpu_ptr(pptr, cpu); -+ copy_map_value_long(&htab->map, ptr, value + off); -+ bpf_obj_free_fields(htab->map.record, ptr); - off += size; - } - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68745.patch b/SPECS/kernel-rt/CVE-2025-68745.patch deleted file mode 100644 index b880f1423..000000000 --- a/SPECS/kernel-rt/CVE-2025-68745.patch +++ /dev/null @@ -1,106 +0,0 @@ -From df107b79cd30292576fb7a0494bd6c64cde9721b Mon Sep 17 00:00:00 2001 -From: Tony Battersby -Date: Mon, 10 Nov 2025 11:07:37 -0500 -Subject: [PATCH 03/51] scsi: qla2xxx: Clear cmds after chip reset - -Commit aefed3e5548f ("scsi: qla2xxx: target: Fix offline port handling -and host reset handling") caused two problems: - -1. Commands sent to FW, after chip reset got stuck and never freed as FW - is not going to respond to them anymore. - -2. BUG_ON(cmd->sg_mapped) in qlt_free_cmd(). Commit 26f9ce53817a - ("scsi: qla2xxx: Fix missed DMA unmap for aborted commands") - attempted to fix this, but introduced another bug under different - circumstances when two different CPUs were racing to call - qlt_unmap_sg() at the same time: BUG_ON(!valid_dma_direction(dir)) in - dma_unmap_sg_attrs(). - -So revert "scsi: qla2xxx: Fix missed DMA unmap for aborted commands" and -partially revert "scsi: qla2xxx: target: Fix offline port handling and -host reset handling" at __qla2x00_abort_all_cmds. - -Fixes: aefed3e5548f ("scsi: qla2xxx: target: Fix offline port handling and host reset handling") -Fixes: 26f9ce53817a ("scsi: qla2xxx: Fix missed DMA unmap for aborted commands") -Co-developed-by: Dmitry Bogdanov -Signed-off-by: Dmitry Bogdanov -Signed-off-by: Tony Battersby -Link: https://patch.msgid.link/0e7e5d26-e7a0-42d1-8235-40eeb27f3e98@cybernetics.com -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/qla2xxx/qla_os.c | 20 ++++++++++++++++++-- - drivers/scsi/qla2xxx/qla_target.c | 5 +---- - drivers/scsi/qla2xxx/qla_target.h | 1 + - 3 files changed, 20 insertions(+), 6 deletions(-) - -diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c -index 4460421834cb..b515ed301940 100644 ---- a/drivers/scsi/qla2xxx/qla_os.c -+++ b/drivers/scsi/qla2xxx/qla_os.c -@@ -1881,10 +1881,26 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res) - continue; - } - cmd = (struct qla_tgt_cmd *)sp; -- cmd->aborted = 1; -+ -+ if (cmd->sg_mapped) -+ qlt_unmap_sg(vha, cmd); -+ -+ if (cmd->state == QLA_TGT_STATE_NEED_DATA) { -+ cmd->aborted = 1; -+ cmd->write_data_transferred = 0; -+ cmd->state = QLA_TGT_STATE_DATA_IN; -+ ha->tgt.tgt_ops->handle_data(cmd); -+ } else { -+ ha->tgt.tgt_ops->free_cmd(cmd); -+ } - break; - case TYPE_TGT_TMCMD: -- /* Skip task management functions. */ -+ /* -+ * Currently, only ABTS response gets on the -+ * outstanding_cmds[] -+ */ -+ ha->tgt.tgt_ops->free_mcmd( -+ (struct qla_tgt_mgmt_cmd *) sp); - break; - default: - break; -diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c -index 1e81582085e3..4c6aff59fe3f 100644 ---- a/drivers/scsi/qla2xxx/qla_target.c -+++ b/drivers/scsi/qla2xxx/qla_target.c -@@ -2443,7 +2443,7 @@ static int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm) - return -1; - } - --static void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) -+void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) - { - struct qla_hw_data *ha; - struct qla_qpair *qpair; -@@ -3773,9 +3773,6 @@ int qlt_abort_cmd(struct qla_tgt_cmd *cmd) - - spin_lock_irqsave(&cmd->cmd_lock, flags); - if (cmd->aborted) { -- if (cmd->sg_mapped) -- qlt_unmap_sg(vha, cmd); -- - spin_unlock_irqrestore(&cmd->cmd_lock, flags); - /* - * It's normal to see 2 calls in this path: -diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h -index 15a59c125c53..c483966d0a84 100644 ---- a/drivers/scsi/qla2xxx/qla_target.h -+++ b/drivers/scsi/qla2xxx/qla_target.h -@@ -1058,6 +1058,7 @@ extern int qlt_abort_cmd(struct qla_tgt_cmd *); - extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); - extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); - extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); -+extern void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd); - extern void qlt_async_event(uint16_t, struct scsi_qla_host *, uint16_t *); - extern void qlt_enable_vha(struct scsi_qla_host *); - extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68749.patch b/SPECS/kernel-rt/CVE-2025-68749.patch deleted file mode 100644 index 9c6d4431f..000000000 --- a/SPECS/kernel-rt/CVE-2025-68749.patch +++ /dev/null @@ -1,51 +0,0 @@ -From bcc739c9ef5136a1cfbcf34623882561694d47a0 Mon Sep 17 00:00:00 2001 -From: Tomasz Rusinowicz -Date: Wed, 29 Oct 2025 08:14:51 +0100 -Subject: [PATCH 01/51] accel/ivpu: Fix race condition when unbinding BOs - -Fix 'Memory manager not clean during takedown' warning that occurs -when ivpu_gem_bo_free() removes the BO from the BOs list before it -gets unmapped. Then file_priv_unbind() triggers a warning in -drm_mm_takedown() during context teardown. - -Protect the unmapping sequence with bo_list_lock to ensure the BO is -always fully unmapped when removed from the list. This ensures the BO -is either fully unmapped at context teardown time or present on the -list and unmapped by file_priv_unbind(). - -Fixes: 48aea7f2a2ef ("accel/ivpu: Fix locking in ivpu_bo_remove_all_bos_from_context()") -Signed-off-by: Tomasz Rusinowicz -Reviewed-by: Jeff Hugo -Signed-off-by: Karol Wachowski -Link: https://patch.msgid.link/20251029071451.184243-1-karol.wachowski@linux.intel.com ---- - drivers/accel/ivpu/ivpu_gem.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c -index 59cfcf3eaded..4c0898102697 100644 ---- a/drivers/accel/ivpu/ivpu_gem.c -+++ b/drivers/accel/ivpu/ivpu_gem.c -@@ -283,14 +283,18 @@ static void ivpu_gem_bo_free(struct drm_gem_object *obj) - - mutex_lock(&vdev->bo_list_lock); - list_del(&bo->bo_list_node); -- mutex_unlock(&vdev->bo_list_lock); - - drm_WARN_ON(&vdev->drm, !drm_gem_is_imported(&bo->base.base) && - !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ)); - drm_WARN_ON(&vdev->drm, ivpu_bo_size(bo) == 0); - drm_WARN_ON(&vdev->drm, bo->base.vaddr); - -+ ivpu_bo_lock(bo); - ivpu_bo_unbind_locked(bo); -+ ivpu_bo_unlock(bo); -+ -+ mutex_unlock(&vdev->bo_list_lock); -+ - drm_WARN_ON(&vdev->drm, bo->mmu_mapped); - drm_WARN_ON(&vdev->drm, bo->ctx); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68752.patch b/SPECS/kernel-rt/CVE-2025-68752.patch deleted file mode 100644 index 44494c6ba..000000000 --- a/SPECS/kernel-rt/CVE-2025-68752.patch +++ /dev/null @@ -1,49 +0,0 @@ -From a0dfcb10317fec90540dcf3a1569d585fd595903 Mon Sep 17 00:00:00 2001 -From: Michal Schmidt -Date: Wed, 26 Nov 2025 10:48:49 +0100 -Subject: [PATCH 33/38] iavf: Implement settime64 with -EOPNOTSUPP - -ptp_clock_settime() assumes every ptp_clock has implemented settime64(). -Stub it with -EOPNOTSUPP to prevent a NULL dereference. - -The fix is similar to commit 329d050bbe63 ("gve: Implement settime64 -with -EOPNOTSUPP"). - -Fixes: d734223b2f0d ("iavf: add initial framework for registering PTP clock") -Signed-off-by: Michal Schmidt -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Tim Hostetler -Link: https://patch.msgid.link/20251126094850.2842557-1-mschmidt@redhat.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/ethernet/intel/iavf/iavf_ptp.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/drivers/net/ethernet/intel/iavf/iavf_ptp.c b/drivers/net/ethernet/intel/iavf/iavf_ptp.c -index b4d5eda2e84f..9cbd8c154031 100644 ---- a/drivers/net/ethernet/intel/iavf/iavf_ptp.c -+++ b/drivers/net/ethernet/intel/iavf/iavf_ptp.c -@@ -252,6 +252,12 @@ static int iavf_ptp_gettimex64(struct ptp_clock_info *info, - return iavf_read_phc_indirect(adapter, ts, sts); - } - -+static int iavf_ptp_settime64(struct ptp_clock_info *info, -+ const struct timespec64 *ts) -+{ -+ return -EOPNOTSUPP; -+} -+ - /** - * iavf_ptp_cache_phc_time - Cache PHC time for performing timestamp extension - * @adapter: private adapter structure -@@ -320,6 +326,7 @@ static int iavf_ptp_register_clock(struct iavf_adapter *adapter) - KBUILD_MODNAME, dev_name(dev)); - ptp_info->owner = THIS_MODULE; - ptp_info->gettimex64 = iavf_ptp_gettimex64; -+ ptp_info->settime64 = iavf_ptp_settime64; - ptp_info->do_aux_work = iavf_ptp_do_aux_work; - - clock = ptp_clock_register(ptp_info, dev); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68753.patch b/SPECS/kernel-rt/CVE-2025-68753.patch deleted file mode 100644 index 1412236ab..000000000 --- a/SPECS/kernel-rt/CVE-2025-68753.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 7cd04bc7f7197c72a6dcdf9aa05a281e9cacaf7f Mon Sep 17 00:00:00 2001 -From: Junrui Luo -Date: Tue, 9 Dec 2025 13:16:41 +0800 -Subject: [PATCH 31/38] ALSA: firewire-motu: add bounds check in put_user loop - for DSP events - -In the DSP event handling code, a put_user() loop copies event data. -When the user buffer size is not aligned to 4 bytes, it could overwrite -beyond the buffer boundary. - -Fix by adding a bounds check before put_user(). - -Suggested-by: Takashi Iwai -Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") -Signed-off-by: Junrui Luo -Link: https://patch.msgid.link/SYBPR01MB788112C72AF8A1C8C448B4B8AFA3A@SYBPR01MB7881.ausprd01.prod.outlook.com -Signed-off-by: Takashi Iwai ---- - sound/firewire/motu/motu-hwdep.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c -index fa2685665db3..c13086fbb041 100644 ---- a/sound/firewire/motu/motu-hwdep.c -+++ b/sound/firewire/motu/motu-hwdep.c -@@ -75,7 +75,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, - while (consumed < count && - snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { - ptr = (u32 __user *)(buf + consumed); -- if (put_user(ev, ptr)) -+ if (consumed + sizeof(ev) > count || put_user(ev, ptr)) - return -EFAULT; - consumed += sizeof(ev); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68756.patch b/SPECS/kernel-rt/CVE-2025-68756.patch deleted file mode 100644 index 8bf587a8c..000000000 --- a/SPECS/kernel-rt/CVE-2025-68756.patch +++ /dev/null @@ -1,164 +0,0 @@ -From 1008e1348b94fbead6292fa3074f45b6eaa5c97f Mon Sep 17 00:00:00 2001 -From: Mohamed Khalfella -Date: Fri, 5 Dec 2025 13:17:02 -0800 -Subject: [PATCH 30/38] block: Use RCU in blk_mq_[un]quiesce_tagset() instead - of set->tag_list_lock - -blk_mq_{add,del}_queue_tag_set() functions add and remove queues from -tagset, the functions make sure that tagset and queues are marked as -shared when two or more queues are attached to the same tagset. -Initially a tagset starts as unshared and when the number of added -queues reaches two, blk_mq_add_queue_tag_set() marks it as shared along -with all the queues attached to it. When the number of attached queues -drops to 1 blk_mq_del_queue_tag_set() need to mark both the tagset and -the remaining queues as unshared. - -Both functions need to freeze current queues in tagset before setting on -unsetting BLK_MQ_F_TAG_QUEUE_SHARED flag. While doing so, both functions -hold set->tag_list_lock mutex, which makes sense as we do not want -queues to be added or deleted in the process. This used to work fine -until commit 98d81f0df70c ("nvme: use blk_mq_[un]quiesce_tagset") -made the nvme driver quiesce tagset instead of quiscing individual -queues. blk_mq_quiesce_tagset() does the job and quiesce the queues in -set->tag_list while holding set->tag_list_lock also. - -This results in deadlock between two threads with these stacktraces: - - __schedule+0x47c/0xbb0 - ? timerqueue_add+0x66/0xb0 - schedule+0x1c/0xa0 - schedule_preempt_disabled+0xa/0x10 - __mutex_lock.constprop.0+0x271/0x600 - blk_mq_quiesce_tagset+0x25/0xc0 - nvme_dev_disable+0x9c/0x250 - nvme_timeout+0x1fc/0x520 - blk_mq_handle_expired+0x5c/0x90 - bt_iter+0x7e/0x90 - blk_mq_queue_tag_busy_iter+0x27e/0x550 - ? __blk_mq_complete_request_remote+0x10/0x10 - ? __blk_mq_complete_request_remote+0x10/0x10 - ? __call_rcu_common.constprop.0+0x1c0/0x210 - blk_mq_timeout_work+0x12d/0x170 - process_one_work+0x12e/0x2d0 - worker_thread+0x288/0x3a0 - ? rescuer_thread+0x480/0x480 - kthread+0xb8/0xe0 - ? kthread_park+0x80/0x80 - ret_from_fork+0x2d/0x50 - ? kthread_park+0x80/0x80 - ret_from_fork_asm+0x11/0x20 - - __schedule+0x47c/0xbb0 - ? xas_find+0x161/0x1a0 - schedule+0x1c/0xa0 - blk_mq_freeze_queue_wait+0x3d/0x70 - ? destroy_sched_domains_rcu+0x30/0x30 - blk_mq_update_tag_set_shared+0x44/0x80 - blk_mq_exit_queue+0x141/0x150 - del_gendisk+0x25a/0x2d0 - nvme_ns_remove+0xc9/0x170 - nvme_remove_namespaces+0xc7/0x100 - nvme_remove+0x62/0x150 - pci_device_remove+0x23/0x60 - device_release_driver_internal+0x159/0x200 - unbind_store+0x99/0xa0 - kernfs_fop_write_iter+0x112/0x1e0 - vfs_write+0x2b1/0x3d0 - ksys_write+0x4e/0xb0 - do_syscall_64+0x5b/0x160 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -The top stacktrace is showing nvme_timeout() called to handle nvme -command timeout. timeout handler is trying to disable the controller and -as a first step, it needs to blk_mq_quiesce_tagset() to tell blk-mq not -to call queue callback handlers. The thread is stuck waiting for -set->tag_list_lock as it tries to walk the queues in set->tag_list. - -The lock is held by the second thread in the bottom stack which is -waiting for one of queues to be frozen. The queue usage counter will -drop to zero after nvme_timeout() finishes, and this will not happen -because the thread will wait for this mutex forever. - -Given that [un]quiescing queue is an operation that does not need to -sleep, update blk_mq_[un]quiesce_tagset() to use RCU instead of taking -set->tag_list_lock, update blk_mq_{add,del}_queue_tag_set() to use RCU -safe list operations. Also, delete INIT_LIST_HEAD(&q->tag_set_list) -in blk_mq_del_queue_tag_set() because we can not re-initialize it while -the list is being traversed under RCU. The deleted queue will not be -added/deleted to/from a tagset and it will be freed in blk_free_queue() -after the end of RCU grace period. - -Signed-off-by: Mohamed Khalfella -Fixes: 98d81f0df70c ("nvme: use blk_mq_[un]quiesce_tagset") -Reviewed-by: Ming Lei -Reviewed-by: Bart Van Assche -Signed-off-by: Jens Axboe ---- - block/blk-mq.c | 17 ++++++++--------- - 1 file changed, 8 insertions(+), 9 deletions(-) - -diff --git a/block/blk-mq.c b/block/blk-mq.c -index 19f62b070ca9..63b01c416d42 100644 ---- a/block/blk-mq.c -+++ b/block/blk-mq.c -@@ -335,12 +335,12 @@ void blk_mq_quiesce_tagset(struct blk_mq_tag_set *set) - { - struct request_queue *q; - -- mutex_lock(&set->tag_list_lock); -- list_for_each_entry(q, &set->tag_list, tag_set_list) { -+ rcu_read_lock(); -+ list_for_each_entry_rcu(q, &set->tag_list, tag_set_list) { - if (!blk_queue_skip_tagset_quiesce(q)) - blk_mq_quiesce_queue_nowait(q); - } -- mutex_unlock(&set->tag_list_lock); -+ rcu_read_unlock(); - - blk_mq_wait_quiesce_done(set); - } -@@ -350,12 +350,12 @@ void blk_mq_unquiesce_tagset(struct blk_mq_tag_set *set) - { - struct request_queue *q; - -- mutex_lock(&set->tag_list_lock); -- list_for_each_entry(q, &set->tag_list, tag_set_list) { -+ rcu_read_lock(); -+ list_for_each_entry_rcu(q, &set->tag_list, tag_set_list) { - if (!blk_queue_skip_tagset_quiesce(q)) - blk_mq_unquiesce_queue(q); - } -- mutex_unlock(&set->tag_list_lock); -+ rcu_read_unlock(); - } - EXPORT_SYMBOL_GPL(blk_mq_unquiesce_tagset); - -@@ -4303,7 +4303,7 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) - struct blk_mq_tag_set *set = q->tag_set; - - mutex_lock(&set->tag_list_lock); -- list_del(&q->tag_set_list); -+ list_del_rcu(&q->tag_set_list); - if (list_is_singular(&set->tag_list)) { - /* just transitioned to unshared */ - set->flags &= ~BLK_MQ_F_TAG_QUEUE_SHARED; -@@ -4311,7 +4311,6 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) - blk_mq_update_tag_set_shared(set, false); - } - mutex_unlock(&set->tag_list_lock); -- INIT_LIST_HEAD(&q->tag_set_list); - } - - static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, -@@ -4330,7 +4329,7 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, - } - if (set->flags & BLK_MQ_F_TAG_QUEUE_SHARED) - queue_set_hctx_shared(q, true); -- list_add_tail(&q->tag_set_list, &set->tag_list); -+ list_add_tail_rcu(&q->tag_set_list, &set->tag_list); - - mutex_unlock(&set->tag_list_lock); - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68759.patch b/SPECS/kernel-rt/CVE-2025-68759.patch deleted file mode 100644 index d72512995..000000000 --- a/SPECS/kernel-rt/CVE-2025-68759.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 8537f8719ea3f8a8697d95ca396abf177e15bbba Mon Sep 17 00:00:00 2001 -From: Abdun Nihaal -Date: Fri, 14 Nov 2025 15:15:26 +0530 -Subject: [PATCH 29/38] wifi: rtl818x: Fix potential memory leaks in - rtl8180_init_rx_ring() - -In rtl8180_init_rx_ring(), memory is allocated for skb packets and DMA -allocations in a loop. When an allocation fails, the previously -successful allocations are not freed on exit. - -Fix that by jumping to err_free_rings label on error, which calls -rtl8180_free_rx_ring() to free the allocations. Remove the free of -rx_ring in rtl8180_init_rx_ring() error path, and set the freed -priv->rx_buf entry to null, to avoid double free. - -Fixes: f653211197f3 ("Add rtl8180 wireless driver") -Signed-off-by: Abdun Nihaal -Reviewed-by: Ping-Ke Shih -Signed-off-by: Ping-Ke Shih -Link: https://patch.msgid.link/20251114094527.79842-1-nihaal@cse.iitm.ac.in ---- - drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) - -diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c -index 2905baea6239..070c0431c482 100644 ---- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c -+++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c -@@ -1023,9 +1023,6 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) - dma_addr_t *mapping; - entry = priv->rx_ring + priv->rx_ring_sz*i; - if (!skb) { -- dma_free_coherent(&priv->pdev->dev, -- priv->rx_ring_sz * 32, -- priv->rx_ring, priv->rx_ring_dma); - wiphy_err(dev->wiphy, "Cannot allocate RX skb\n"); - return -ENOMEM; - } -@@ -1037,9 +1034,7 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) - - if (dma_mapping_error(&priv->pdev->dev, *mapping)) { - kfree_skb(skb); -- dma_free_coherent(&priv->pdev->dev, -- priv->rx_ring_sz * 32, -- priv->rx_ring, priv->rx_ring_dma); -+ priv->rx_buf[i] = NULL; - wiphy_err(dev->wiphy, "Cannot map DMA for RX skb\n"); - return -ENOMEM; - } -@@ -1130,7 +1125,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) - - ret = rtl8180_init_rx_ring(dev); - if (ret) -- return ret; -+ goto err_free_rings; - - for (i = 0; i < (dev->queues + 1); i++) - if ((ret = rtl8180_init_tx_ring(dev, i, 16))) --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68762.patch b/SPECS/kernel-rt/CVE-2025-68762.patch deleted file mode 100644 index f809f4af5..000000000 --- a/SPECS/kernel-rt/CVE-2025-68762.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 5cfe3b59e6cfec1452878ce5a0390ff00bef2ca1 Mon Sep 17 00:00:00 2001 -From: Breno Leitao -Date: Thu, 27 Nov 2025 07:30:15 -0800 -Subject: [PATCH 28/38] net: netpoll: initialize work queue before error checks - -Prevent a kernel warning when netconsole setup fails on devices with -IFF_DISABLE_NETPOLL flag. The warning (at kernel/workqueue.c:4242 in -__flush_work) occurs because the cleanup path tries to cancel an -uninitialized work queue. - -When __netpoll_setup() encounters a device with IFF_DISABLE_NETPOLL, -it fails early and calls skb_pool_flush() for cleanup. This function -calls cancel_work_sync(&np->refill_wq), but refill_wq hasn't been -initialized yet, triggering the warning. - -Move INIT_WORK() to the beginning of __netpoll_setup(), ensuring the -work queue is properly initialized before any potential failure points. -This allows the cleanup path to safely cancel the work queue regardless -of where the setup fails. - -Fixes: 248f6571fd4c5 ("netpoll: Optimize skb refilling on critical path") -Signed-off-by: Breno Leitao -Link: https://patch.msgid.link/20251127-netpoll_fix_init_work-v1-1-65c07806d736@debian.org -Signed-off-by: Jakub Kicinski ---- - net/core/netpoll.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/net/core/netpoll.c b/net/core/netpoll.c -index be5658ff74ee..27f573d2c5e3 100644 ---- a/net/core/netpoll.c -+++ b/net/core/netpoll.c -@@ -554,6 +554,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) - int err; - - skb_queue_head_init(&np->skb_pool); -+ INIT_WORK(&np->refill_wq, refill_skbs_work_handler); - - if (ndev->priv_flags & IFF_DISABLE_NETPOLL) { - np_err(np, "%s doesn't support polling, aborting\n", -@@ -592,7 +593,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) - - /* fill up the skb queue */ - refill_skbs(np); -- INIT_WORK(&np->refill_wq, refill_skbs_work_handler); - - /* last thing to do is link it to the net device structure */ - rcu_assign_pointer(ndev->npinfo, npinfo); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68764.patch b/SPECS/kernel-rt/CVE-2025-68764.patch deleted file mode 100644 index 9a26d8274..000000000 --- a/SPECS/kernel-rt/CVE-2025-68764.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 123e370c0d704fea3440f903d59f9ffd6147d111 Mon Sep 17 00:00:00 2001 -From: Trond Myklebust -Date: Fri, 28 Nov 2025 14:22:44 -0500 -Subject: [PATCH 27/38] NFS: Automounted filesystems should inherit - ro,noexec,nodev,sync flags - -When a filesystem is being automounted, it needs to preserve the -user-set superblock mount options, such as the "ro" flag. - -Reported-by: Li Lingfeng -Link: https://lore.kernel.org/all/20240604112636.236517-3-lilingfeng@huaweicloud.com/ -Fixes: f2aedb713c28 ("NFS: Add fs_context support.") -Signed-off-by: Trond Myklebust ---- - fs/nfs/namespace.c | 6 ++++++ - fs/nfs/super.c | 4 ---- - 2 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c -index 7f1ec9c67ff2..c74e45a89500 100644 ---- a/fs/nfs/namespace.c -+++ b/fs/nfs/namespace.c -@@ -149,6 +149,7 @@ struct vfsmount *nfs_d_automount(struct path *path) - struct vfsmount *mnt = ERR_PTR(-ENOMEM); - struct nfs_server *server = NFS_SB(path->dentry->d_sb); - struct nfs_client *client = server->nfs_client; -+ unsigned long s_flags = path->dentry->d_sb->s_flags; - int timeout = READ_ONCE(nfs_mountpoint_expiry_timeout); - int ret; - -@@ -174,6 +175,11 @@ struct vfsmount *nfs_d_automount(struct path *path) - fc->net_ns = get_net(client->cl_net); - } - -+ /* Inherit the flags covered by NFS_SB_MASK */ -+ fc->sb_flags_mask |= NFS_SB_MASK; -+ fc->sb_flags &= ~NFS_SB_MASK; -+ fc->sb_flags |= s_flags & NFS_SB_MASK; -+ - /* for submounts we want the same server; referrals will reassign */ - memcpy(&ctx->nfs_server._address, &client->cl_addr, client->cl_addrlen); - ctx->nfs_server.addrlen = client->cl_addrlen; -diff --git a/fs/nfs/super.c b/fs/nfs/super.c -index 72dee6f3050e..cf4ab3a5e690 100644 ---- a/fs/nfs/super.c -+++ b/fs/nfs/super.c -@@ -1334,10 +1334,6 @@ int nfs_get_tree_common(struct fs_context *fc) - if (server->flags & NFS_MOUNT_NOAC) - fc->sb_flags |= SB_SYNCHRONOUS; - -- if (ctx->clone_data.sb) -- if (ctx->clone_data.sb->s_flags & SB_SYNCHRONOUS) -- fc->sb_flags |= SB_SYNCHRONOUS; -- - /* Get a superblock - note that we may end up sharing one that already exists */ - fc->s_fs_info = server; - s = sget_fc(fc, compare_super, nfs_set_super); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68768-1.patch b/SPECS/kernel-rt/CVE-2025-68768-1.patch deleted file mode 100644 index 773578a8d..000000000 --- a/SPECS/kernel-rt/CVE-2025-68768-1.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 6df6d25354ec54477df1b9f13457666db545849e Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Sat, 6 Dec 2025 17:09:39 -0800 -Subject: [PATCH 24/38] inet: frags: avoid theoretical race in ip_frag_reinit() - -In ip_frag_reinit() we want to move the frag timeout timer into -the future. If the timer fires in the meantime we inadvertently -scheduled it again, and since the timer assumes a ref on frag_queue -we need to acquire one to balance things out. - -This is technically racy, we should have acquired the reference -_before_ we touch the timer, it may fire again before we take the ref. -Avoid this entire dance by using mod_timer_pending() which only modifies -the timer if its pending (and which exists since Linux v2.6.30) - -Note that this was the only place we ever took a ref on frag_queue -since Eric's conversion to RCU. So we could potentially replace -the whole refcnt field with an atomic flag and a bit more RCU. - -Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") -Reviewed-by: Eric Dumazet -Link: https://patch.msgid.link/20251207010942.1672972-2-kuba@kernel.org -Signed-off-by: Jakub Kicinski ---- - net/ipv4/inet_fragment.c | 4 +++- - net/ipv4/ip_fragment.c | 4 +--- - 2 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c -index 470ab17ceb51..d310742a23dd 100644 ---- a/net/ipv4/inet_fragment.c -+++ b/net/ipv4/inet_fragment.c -@@ -327,7 +327,9 @@ static struct inet_frag_queue *inet_frag_alloc(struct fqdir *fqdir, - - timer_setup(&q->timer, f->frag_expire, 0); - spin_lock_init(&q->lock); -- /* One reference for the timer, one for the hash table. */ -+ /* One reference for the timer, one for the hash table. -+ * We never take any extra references, only decrement this field. -+ */ - refcount_set(&q->refcnt, 2); - - return q; -diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c -index f7012479713b..d7bccdc9dc69 100644 ---- a/net/ipv4/ip_fragment.c -+++ b/net/ipv4/ip_fragment.c -@@ -242,10 +242,8 @@ static int ip_frag_reinit(struct ipq *qp) - { - unsigned int sum_truesize = 0; - -- if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { -- refcount_inc(&qp->q.refcnt); -+ if (!mod_timer_pending(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) - return -ETIMEDOUT; -- } - - sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, - SKB_DROP_REASON_FRAG_TOO_FAR); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68768-2.patch b/SPECS/kernel-rt/CVE-2025-68768-2.patch deleted file mode 100644 index ca32abed7..000000000 --- a/SPECS/kernel-rt/CVE-2025-68768-2.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 562944a68129db7e84836c708ca15ea8fcd3205c Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Sat, 6 Dec 2025 17:09:40 -0800 -Subject: [PATCH 25/38] inet: frags: add inet_frag_queue_flush() - -Instead of exporting inet_frag_rbtree_purge() which requires that -caller takes care of memory accounting, add a new helper. We will -need to call it from a few places in the next patch. - -Reviewed-by: Eric Dumazet -Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org -Signed-off-by: Jakub Kicinski ---- - include/net/inet_frag.h | 5 ++--- - net/ipv4/inet_fragment.c | 15 ++++++++++++--- - net/ipv4/ip_fragment.c | 6 +----- - 3 files changed, 15 insertions(+), 11 deletions(-) - -diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h -index 0eccd9c3a883..3ffaceee7bbc 100644 ---- a/include/net/inet_frag.h -+++ b/include/net/inet_frag.h -@@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q, int *refs); - void inet_frag_destroy(struct inet_frag_queue *q); - struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key); - --/* Free all skbs in the queue; return the sum of their truesizes. */ --unsigned int inet_frag_rbtree_purge(struct rb_root *root, -- enum skb_drop_reason reason); -+void inet_frag_queue_flush(struct inet_frag_queue *q, -+ enum skb_drop_reason reason); - - static inline void inet_frag_putn(struct inet_frag_queue *q, int refs) - { -diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c -index d310742a23dd..41b2f0373fc9 100644 ---- a/net/ipv4/inet_fragment.c -+++ b/net/ipv4/inet_fragment.c -@@ -263,8 +263,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head) - kmem_cache_free(f->frags_cachep, q); - } - --unsigned int inet_frag_rbtree_purge(struct rb_root *root, -- enum skb_drop_reason reason) -+static unsigned int -+inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason) - { - struct rb_node *p = rb_first(root); - unsigned int sum = 0; -@@ -284,7 +284,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root, - } - return sum; - } --EXPORT_SYMBOL(inet_frag_rbtree_purge); -+ -+void inet_frag_queue_flush(struct inet_frag_queue *q, -+ enum skb_drop_reason reason) -+{ -+ unsigned int sum; -+ -+ sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); -+ sub_frag_mem_limit(q->fqdir, sum); -+} -+EXPORT_SYMBOL(inet_frag_queue_flush); - - void inet_frag_destroy(struct inet_frag_queue *q) - { -diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c -index d7bccdc9dc69..32f1c1a46ba7 100644 ---- a/net/ipv4/ip_fragment.c -+++ b/net/ipv4/ip_fragment.c -@@ -240,14 +240,10 @@ static int ip_frag_too_far(struct ipq *qp) - - static int ip_frag_reinit(struct ipq *qp) - { -- unsigned int sum_truesize = 0; -- - if (!mod_timer_pending(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) - return -ETIMEDOUT; - -- sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, -- SKB_DROP_REASON_FRAG_TOO_FAR); -- sub_frag_mem_limit(qp->q.fqdir, sum_truesize); -+ inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR); - - qp->q.flags = 0; - qp->q.len = 0; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68768-3.patch b/SPECS/kernel-rt/CVE-2025-68768-3.patch deleted file mode 100644 index 7610b34c8..000000000 --- a/SPECS/kernel-rt/CVE-2025-68768-3.patch +++ /dev/null @@ -1,181 +0,0 @@ -From c5a20b5a005fa8f9a23df5755403253c8d925e6f Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Sat, 6 Dec 2025 17:09:41 -0800 -Subject: [PATCH 26/38] inet: frags: flush pending skbs in fqdir_pre_exit() - -We have been seeing occasional deadlocks on pernet_ops_rwsem since -September in NIPA. The stuck task was usually modprobe (often loading -a driver like ipvlan), trying to take the lock as a Writer. -lockdep does not track readers for rwsems so the read wasn't obvious -from the reports. - -On closer inspection the Reader holding the lock was conntrack looping -forever in nf_conntrack_cleanup_net_list(). Based on past experience -with occasional NIPA crashes I looked thru the tests which run before -the crash and noticed that the crash follows ip_defrag.sh. An immediate -red flag. Scouring thru (de)fragmentation queues reveals skbs sitting -around, holding conntrack references. - -The problem is that since conntrack depends on nf_defrag_ipv6, -nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its -netns exit hooks run _after_ conntrack's netns exit hook. - -Flush all fragment queue SKBs during fqdir_pre_exit() to release -conntrack references before conntrack cleanup runs. Also flush -the queues in timer expiry handlers when they discover fqdir->dead -is set, in case packet sneaks in while we're running the pre_exit -flush. - -The commit under Fixes is not exactly the culprit, but I think -previously the timer firing would eventually unblock the spinning -conntrack. - -Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units") -Reviewed-by: Eric Dumazet -Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org -Signed-off-by: Jakub Kicinski ---- - include/net/inet_frag.h | 13 +------------ - include/net/ipv6_frag.h | 9 ++++++--- - net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++ - net/ipv4/ip_fragment.c | 12 +++++++----- - 4 files changed, 50 insertions(+), 20 deletions(-) - -diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h -index 3ffaceee7bbc..365925c9d262 100644 ---- a/include/net/inet_frag.h -+++ b/include/net/inet_frag.h -@@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *); - - int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net); - --static inline void fqdir_pre_exit(struct fqdir *fqdir) --{ -- /* Prevent creation of new frags. -- * Pairs with READ_ONCE() in inet_frag_find(). -- */ -- WRITE_ONCE(fqdir->high_thresh, 0); -- -- /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() -- * and ip6frag_expire_frag_queue(). -- */ -- WRITE_ONCE(fqdir->dead, true); --} -+void fqdir_pre_exit(struct fqdir *fqdir); - void fqdir_exit(struct fqdir *fqdir); - - void inet_frag_kill(struct inet_frag_queue *q, int *refs); -diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h -index 38ef66826939..41d9fc6965f9 100644 ---- a/include/net/ipv6_frag.h -+++ b/include/net/ipv6_frag.h -@@ -69,9 +69,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) - int refs = 1; - - rcu_read_lock(); -- /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ -- if (READ_ONCE(fq->q.fqdir->dead)) -- goto out_rcu_unlock; - spin_lock(&fq->q.lock); - - if (fq->q.flags & INET_FRAG_COMPLETE) -@@ -80,6 +77,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) - fq->q.flags |= INET_FRAG_DROP; - inet_frag_kill(&fq->q, &refs); - -+ /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ -+ if (READ_ONCE(fq->q.fqdir->dead)) { -+ inet_frag_queue_flush(&fq->q, 0); -+ goto out; -+ } -+ - dev = dev_get_by_index_rcu(net, fq->iif); - if (!dev) - goto out; -diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c -index 41b2f0373fc9..4683560a5fb2 100644 ---- a/net/ipv4/inet_fragment.c -+++ b/net/ipv4/inet_fragment.c -@@ -218,6 +218,41 @@ static int __init inet_frag_wq_init(void) - - pure_initcall(inet_frag_wq_init); - -+void fqdir_pre_exit(struct fqdir *fqdir) -+{ -+ struct inet_frag_queue *fq; -+ struct rhashtable_iter hti; -+ -+ /* Prevent creation of new frags. -+ * Pairs with READ_ONCE() in inet_frag_find(). -+ */ -+ WRITE_ONCE(fqdir->high_thresh, 0); -+ -+ /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() -+ * and ip6frag_expire_frag_queue(). -+ */ -+ WRITE_ONCE(fqdir->dead, true); -+ -+ rhashtable_walk_enter(&fqdir->rhashtable, &hti); -+ rhashtable_walk_start(&hti); -+ -+ while ((fq = rhashtable_walk_next(&hti))) { -+ if (IS_ERR(fq)) { -+ if (PTR_ERR(fq) != -EAGAIN) -+ break; -+ continue; -+ } -+ spin_lock_bh(&fq->lock); -+ if (!(fq->flags & INET_FRAG_COMPLETE)) -+ inet_frag_queue_flush(fq, 0); -+ spin_unlock_bh(&fq->lock); -+ } -+ -+ rhashtable_walk_stop(&hti); -+ rhashtable_walk_exit(&hti); -+} -+EXPORT_SYMBOL(fqdir_pre_exit); -+ - void fqdir_exit(struct fqdir *fqdir) - { - INIT_WORK(&fqdir->destroy_work, fqdir_work_fn); -@@ -290,6 +325,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, - { - unsigned int sum; - -+ reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT; - sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); - sub_frag_mem_limit(q->fqdir, sum); - } -diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c -index 32f1c1a46ba7..56b0f738d2f2 100644 ---- a/net/ipv4/ip_fragment.c -+++ b/net/ipv4/ip_fragment.c -@@ -134,11 +134,6 @@ static void ip_expire(struct timer_list *t) - net = qp->q.fqdir->net; - - rcu_read_lock(); -- -- /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ -- if (READ_ONCE(qp->q.fqdir->dead)) -- goto out_rcu_unlock; -- - spin_lock(&qp->q.lock); - - if (qp->q.flags & INET_FRAG_COMPLETE) -@@ -146,6 +141,13 @@ static void ip_expire(struct timer_list *t) - - qp->q.flags |= INET_FRAG_DROP; - inet_frag_kill(&qp->q, &refs); -+ -+ /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ -+ if (READ_ONCE(qp->q.fqdir->dead)) { -+ inet_frag_queue_flush(&qp->q, 0); -+ goto out; -+ } -+ - __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); - __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68791.patch b/SPECS/kernel-rt/CVE-2025-68791.patch deleted file mode 100644 index 8c78b18ec..000000000 --- a/SPECS/kernel-rt/CVE-2025-68791.patch +++ /dev/null @@ -1,74 +0,0 @@ -From c49f1aa60797aa137c9c61564b8de707bd7777b8 Mon Sep 17 00:00:00 2001 -From: Cheng Ding -Date: Tue, 21 Oct 2025 22:46:42 +0200 -Subject: [PATCH 23/38] fuse: missing copy_finish in fuse-over-io-uring - argument copies - -Fix a possible reference count leak of payload pages during -fuse argument copies. - -[Joanne: simplified error cleanup] - -Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support") -Cc: stable@vger.kernel.org # v6.14 -Signed-off-by: Cheng Ding -Signed-off-by: Bernd Schubert -Reviewed-by: Joanne Koong -Signed-off-by: Miklos Szeredi ---- - fs/fuse/dev.c | 2 +- - fs/fuse/dev_uring.c | 5 ++++- - fs/fuse/fuse_dev_i.h | 1 + - 3 files changed, 6 insertions(+), 2 deletions(-) - -diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c -index 612d4da6d7d9..6c296d4fa2f2 100644 ---- a/fs/fuse/dev.c -+++ b/fs/fuse/dev.c -@@ -827,7 +827,7 @@ void fuse_copy_init(struct fuse_copy_state *cs, bool write, - } - - /* Unmap and put previous page of userspace buffer */ --static void fuse_copy_finish(struct fuse_copy_state *cs) -+void fuse_copy_finish(struct fuse_copy_state *cs) - { - if (cs->currbuf) { - struct pipe_buffer *buf = cs->currbuf; -diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c -index 33e9c19cbc3c..f7bd3395ae91 100644 ---- a/fs/fuse/dev_uring.c -+++ b/fs/fuse/dev_uring.c -@@ -598,7 +598,9 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, - cs.is_uring = true; - cs.req = req; - -- return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); -+ err = fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); -+ fuse_copy_finish(&cs); -+ return err; - } - - /* -@@ -649,6 +651,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, - /* copy the payload */ - err = fuse_copy_args(&cs, num_args, args->in_pages, - (struct fuse_arg *)in_args, 0); -+ fuse_copy_finish(&cs); - if (err) { - pr_info_ratelimited("%s fuse_copy_args failed\n", __func__); - return err; -diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h -index 5a9bd771a319..fcebacd061e2 100644 ---- a/fs/fuse/fuse_dev_i.h -+++ b/fs/fuse/fuse_dev_i.h -@@ -53,6 +53,7 @@ void fuse_dev_end_requests(struct list_head *head); - - void fuse_copy_init(struct fuse_copy_state *cs, bool write, - struct iov_iter *iter); -+void fuse_copy_finish(struct fuse_copy_state *cs); - int fuse_copy_args(struct fuse_copy_state *cs, unsigned int numargs, - unsigned int argpages, struct fuse_arg *args, - int zeroing); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68805.patch b/SPECS/kernel-rt/CVE-2025-68805.patch deleted file mode 100644 index f549c42e2..000000000 --- a/SPECS/kernel-rt/CVE-2025-68805.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 4948bfda68ea9e1fd5e4fe6e07dca9e69aefabc3 Mon Sep 17 00:00:00 2001 -From: Joanne Koong -Date: Tue, 25 Nov 2025 10:13:47 -0800 -Subject: [PATCH 22/38] fuse: fix io-uring list corruption for terminated - non-committed requests - -When a request is terminated before it has been committed, the request -is not removed from the queue's list. This leaves a dangling list entry -that leads to list corruption and use-after-free issues. - -Remove the request from the queue's list for terminated non-committed -requests. - -Signed-off-by: Joanne Koong -Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support") -Cc: stable@vger.kernel.org -Reviewed-by: Bernd Schubert -Signed-off-by: Miklos Szeredi ---- - fs/fuse/dev_uring.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c -index 249b210becb1..33e9c19cbc3c 100644 ---- a/fs/fuse/dev_uring.c -+++ b/fs/fuse/dev_uring.c -@@ -85,6 +85,7 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, - lockdep_assert_not_held(&queue->lock); - spin_lock(&queue->lock); - ent->fuse_req = NULL; -+ list_del_init(&req->list); - if (test_bit(FR_BACKGROUND, &req->flags)) { - queue->active_background--; - spin_lock(&fc->bg_lock); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68807.patch b/SPECS/kernel-rt/CVE-2025-68807.patch deleted file mode 100644 index a4e453580..000000000 --- a/SPECS/kernel-rt/CVE-2025-68807.patch +++ /dev/null @@ -1,199 +0,0 @@ -From 763f409df079c83b8f0ccbdc8dd22a381ca05cb9 Mon Sep 17 00:00:00 2001 -From: Ming Lei -Date: Fri, 12 Dec 2025 22:35:00 +0800 -Subject: [PATCH 21/38] block: fix race between wbt_enable_default and IO - submission - -When wbt_enable_default() is moved out of queue freezing in elevator_change(), -it can cause the wbt inflight counter to become negative (-1), leading to hung -tasks in the writeback path. Tasks get stuck in wbt_wait() because the counter -is in an inconsistent state. - -The issue occurs because wbt_enable_default() could race with IO submission, -allowing the counter to be decremented before proper initialization. This manifests -as: - - rq_wait[0]: - inflight: -1 - has_waiters: True - -rwb_enabled() checks the state, which can be updated exactly between wbt_wait() -(rq_qos_throttle()) and wbt_track()(rq_qos_track()), then the inflight counter -will become negative. - -And results in hung task warnings like: - task:kworker/u24:39 state:D stack:0 pid:14767 - Call Trace: - rq_qos_wait+0xb4/0x150 - wbt_wait+0xa9/0x100 - __rq_qos_throttle+0x24/0x40 - blk_mq_submit_bio+0x672/0x7b0 - ... - -Fix this by: - -1. Splitting wbt_enable_default() into: - - __wbt_enable_default(): Returns true if wbt_init() should be called - - wbt_enable_default(): Wrapper for existing callers (no init) - - wbt_init_enable_default(): New function that checks and inits WBT - -2. Using wbt_init_enable_default() in blk_register_queue() to ensure - proper initialization during queue registration - -3. Move wbt_init() out of wbt_enable_default() which is only for enabling - disabled wbt from bfq and iocost, and wbt_init() isn't needed. Then the - original lock warning can be avoided. - -4. Removing the ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT flag and its handling - code since it's no longer needed - -This ensures WBT is properly initialized before any IO can be submitted, -preventing the counter from going negative. - -Cc: Nilay Shroff -Cc: Yu Kuai -Cc: Guangwu Zhang -Fixes: 78c271344b6f ("block: move wbt_enable_default() out of queue freezing from sched ->exit()") -Signed-off-by: Ming Lei -Reviewed-by: Nilay Shroff -Signed-off-by: Jens Axboe ---- - block/bfq-iosched.c | 2 +- - block/blk-sysfs.c | 2 +- - block/blk-wbt.c | 20 ++++++++++++++++---- - block/blk-wbt.h | 5 +++++ - block/elevator.c | 5 ----- - block/elevator.h | 1 - - 6 files changed, 23 insertions(+), 12 deletions(-) - -diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c -index 4a8d3d96bfe4..6e54b1d3d8bc 100644 ---- a/block/bfq-iosched.c -+++ b/block/bfq-iosched.c -@@ -7181,7 +7181,7 @@ static void bfq_exit_queue(struct elevator_queue *e) - - blk_stat_disable_accounting(bfqd->queue); - blk_queue_flag_clear(QUEUE_FLAG_DISABLE_WBT_DEF, bfqd->queue); -- set_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT, &e->flags); -+ wbt_enable_default(bfqd->queue->disk); - - kfree(bfqd); - } -diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c -index 9e292d42df60..aaf77f729c5a 100644 ---- a/block/blk-sysfs.c -+++ b/block/blk-sysfs.c -@@ -935,7 +935,7 @@ int blk_register_queue(struct gendisk *disk) - elevator_set_default(q); - - blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); -- wbt_enable_default(disk); -+ wbt_init_enable_default(disk); - - /* Now everything is ready and send out KOBJ_ADD uevent */ - kobject_uevent(&disk->queue_kobj, KOBJ_ADD); -diff --git a/block/blk-wbt.c b/block/blk-wbt.c -index eb8037bae0bd..0974875f77bd 100644 ---- a/block/blk-wbt.c -+++ b/block/blk-wbt.c -@@ -699,7 +699,7 @@ static void wbt_requeue(struct rq_qos *rqos, struct request *rq) - /* - * Enable wbt if defaults are configured that way - */ --void wbt_enable_default(struct gendisk *disk) -+static bool __wbt_enable_default(struct gendisk *disk) - { - struct request_queue *q = disk->queue; - struct rq_qos *rqos; -@@ -716,19 +716,31 @@ void wbt_enable_default(struct gendisk *disk) - if (enable && RQWB(rqos)->enable_state == WBT_STATE_OFF_DEFAULT) - RQWB(rqos)->enable_state = WBT_STATE_ON_DEFAULT; - mutex_unlock(&disk->rqos_state_mutex); -- return; -+ return false; - } - mutex_unlock(&disk->rqos_state_mutex); - - /* Queue not registered? Maybe shutting down... */ - if (!blk_queue_registered(q)) -- return; -+ return false; - - if (queue_is_mq(q) && enable) -- wbt_init(disk); -+ return true; -+ return false; -+} -+ -+void wbt_enable_default(struct gendisk *disk) -+{ -+ __wbt_enable_default(disk); - } - EXPORT_SYMBOL_GPL(wbt_enable_default); - -+void wbt_init_enable_default(struct gendisk *disk) -+{ -+ if (__wbt_enable_default(disk)) -+ WARN_ON_ONCE(wbt_init(disk)); -+} -+ - u64 wbt_default_latency_nsec(struct request_queue *q) - { - /* -diff --git a/block/blk-wbt.h b/block/blk-wbt.h -index e5fc653b9b76..925f22475738 100644 ---- a/block/blk-wbt.h -+++ b/block/blk-wbt.h -@@ -5,6 +5,7 @@ - #ifdef CONFIG_BLK_WBT - - int wbt_init(struct gendisk *disk); -+void wbt_init_enable_default(struct gendisk *disk); - void wbt_disable_default(struct gendisk *disk); - void wbt_enable_default(struct gendisk *disk); - -@@ -16,6 +17,10 @@ u64 wbt_default_latency_nsec(struct request_queue *); - - #else - -+static inline void wbt_init_enable_default(struct gendisk *disk) -+{ -+} -+ - static inline void wbt_disable_default(struct gendisk *disk) - { - } -diff --git a/block/elevator.c b/block/elevator.c -index e2ebfbf107b3..40cf859b5f53 100644 ---- a/block/elevator.c -+++ b/block/elevator.c -@@ -640,14 +640,9 @@ static int elevator_change_done(struct request_queue *q, - int ret = 0; - - if (ctx->old) { -- bool enable_wbt = test_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT, -- &ctx->old->flags); -- - elv_unregister_queue(q, ctx->old); - blk_mq_free_sched_tags(ctx->old->et, q->tag_set); - kobject_put(&ctx->old->kobj); -- if (enable_wbt) -- wbt_enable_default(q->disk); - } - if (ctx->new) { - ret = elv_register_queue(q, ctx->new, !ctx->no_uevent); -diff --git a/block/elevator.h b/block/elevator.h -index c4d20155065e..63a3e38b9440 100644 ---- a/block/elevator.h -+++ b/block/elevator.h -@@ -132,7 +132,6 @@ struct elevator_queue - - #define ELEVATOR_FLAG_REGISTERED 0 - #define ELEVATOR_FLAG_DYING 1 --#define ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT 2 - - /* - * block elevator interface --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-68823.patch b/SPECS/kernel-rt/CVE-2025-68823.patch deleted file mode 100644 index 139200f21..000000000 --- a/SPECS/kernel-rt/CVE-2025-68823.patch +++ /dev/null @@ -1,113 +0,0 @@ -From 01e1404951461d78c37d49ad15eddb75267fa7e3 Mon Sep 17 00:00:00 2001 -From: Ming Lei -Date: Fri, 12 Dec 2025 22:34:15 +0800 -Subject: [PATCH 20/38] ublk: fix deadlock when reading partition table - -When one process(such as udev) opens ublk block device (e.g., to read -the partition table via bdev_open()), a deadlock[1] can occur: - -1. bdev_open() grabs disk->open_mutex -2. The process issues read I/O to ublk backend to read partition table -3. In __ublk_complete_rq(), blk_update_request() or blk_mq_end_request() - runs bio->bi_end_io() callbacks -4. If this triggers fput() on file descriptor of ublk block device, the - work may be deferred to current task's task work (see fput() implementation) -5. This eventually calls blkdev_release() from the same context -6. blkdev_release() tries to grab disk->open_mutex again -7. Deadlock: same task waiting for a mutex it already holds - -The fix is to run blk_update_request() and blk_mq_end_request() with bottom -halves disabled. This forces blkdev_release() to run in kernel work-queue -context instead of current task work context, and allows ublk server to make -forward progress, and avoids the deadlock. - -Fixes: 71f28f3136af ("ublk_drv: add io_uring based userspace block driver") -Link: https://github.com/ublk-org/ublksrv/issues/170 [1] -Signed-off-by: Ming Lei -Reviewed-by: Caleb Sander Mateos -[axboe: rewrite comment in ublk] -Signed-off-by: Jens Axboe ---- - drivers/block/ublk_drv.c | 32 ++++++++++++++++++++++++++++---- - 1 file changed, 28 insertions(+), 4 deletions(-) - -diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c -index 021274a339b5..89699fc09559 100644 ---- a/drivers/block/ublk_drv.c -+++ b/drivers/block/ublk_drv.c -@@ -1150,6 +1150,13 @@ static inline struct ublk_uring_cmd_pdu *ublk_get_uring_cmd_pdu( - return io_uring_cmd_to_pdu(ioucmd, struct ublk_uring_cmd_pdu); - } - -+static void ublk_end_request(struct request *req, blk_status_t error) -+{ -+ local_bh_disable(); -+ blk_mq_end_request(req, error); -+ local_bh_enable(); -+} -+ - /* todo: handle partial completion */ - static inline void __ublk_complete_rq(struct request *req) - { -@@ -1157,6 +1164,7 @@ static inline void __ublk_complete_rq(struct request *req) - struct ublk_io *io = &ubq->ios[req->tag]; - unsigned int unmapped_bytes; - blk_status_t res = BLK_STS_OK; -+ bool requeue; - - /* failed read IO if nothing is read */ - if (!io->res && req_op(req) == REQ_OP_READ) -@@ -1188,14 +1196,30 @@ static inline void __ublk_complete_rq(struct request *req) - if (unlikely(unmapped_bytes < io->res)) - io->res = unmapped_bytes; - -- if (blk_update_request(req, BLK_STS_OK, io->res)) -+ /* -+ * Run bio->bi_end_io() with softirqs disabled. If the final fput -+ * happens off this path, then that will prevent ublk's blkdev_release() -+ * from being called on current's task work, see fput() implementation. -+ * -+ * Otherwise, ublk server may not provide forward progress in case of -+ * reading the partition table from bdev_open() with disk->open_mutex -+ * held, and causes dead lock as we could already be holding -+ * disk->open_mutex here. -+ * -+ * Preferably we would not be doing IO with a mutex held that is also -+ * used for release, but this work-around will suffice for now. -+ */ -+ local_bh_disable(); -+ requeue = blk_update_request(req, BLK_STS_OK, io->res); -+ local_bh_enable(); -+ if (requeue) - blk_mq_requeue_request(req, true); - else if (likely(!blk_should_fake_timeout(req->q))) - __blk_mq_end_request(req, BLK_STS_OK); - - return; - exit: -- blk_mq_end_request(req, res); -+ ublk_end_request(req, res); - } - - static struct io_uring_cmd *__ublk_prep_compl_io_cmd(struct ublk_io *io, -@@ -1235,7 +1259,7 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq, - if (ublk_nosrv_dev_should_queue_io(ubq->dev)) - blk_mq_requeue_request(rq, false); - else -- blk_mq_end_request(rq, BLK_STS_IOERR); -+ ublk_end_request(rq, BLK_STS_IOERR); - } - - static void -@@ -1259,7 +1283,7 @@ static bool ublk_auto_buf_reg(const struct ublk_queue *ubq, struct request *req, - ublk_auto_buf_reg_fallback(ubq, io); - return true; - } -- blk_mq_end_request(req, BLK_STS_IOERR); -+ ublk_end_request(req, BLK_STS_IOERR); - return false; - } - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71070-1.patch b/SPECS/kernel-rt/CVE-2025-71070-1.patch deleted file mode 100644 index 20ff60db4..000000000 --- a/SPECS/kernel-rt/CVE-2025-71070-1.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 61375d91c986cd67a1f859b931820e109a662b73 Mon Sep 17 00:00:00 2001 -From: Caleb Sander Mateos -Date: Wed, 17 Sep 2025 19:49:40 -0600 -Subject: [PATCH 17/38] ublk: add helpers to check ublk_device flags - -Introduce ublk_device analogues of the ublk_queue flag helpers: -- ublk_support_zero_copy() -> ublk_dev_support_user_copy() -- ublk_support_auto_buf_reg() -> ublk_dev_support_auto_buf_reg() -- ublk_support_user_copy() -> ublk_dev_support_user_copy() -- ublk_need_map_io() -> ublk_dev_need_map_io() -- ublk_need_req_ref() -> ublk_dev_need_req_ref() -- ublk_need_get_data() -> ublk_dev_need_get_data() - -These will be used in subsequent changes to avoid accessing the -ublk_queue just for the flags, and instead use the ublk_device. - -Signed-off-by: Caleb Sander Mateos -Reviewed-by: Ming Lei -Signed-off-by: Jens Axboe ---- - drivers/block/ublk_drv.c | 34 ++++++++++++++++++++++++++++++++++ - 1 file changed, 34 insertions(+) - -diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c -index 67d4a867aec4..9a770d91e840 100644 ---- a/drivers/block/ublk_drv.c -+++ b/drivers/block/ublk_drv.c -@@ -664,22 +664,44 @@ static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq) - return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY; - } - -+static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY; -+} -+ - static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq) - { - return ubq->flags & UBLK_F_AUTO_BUF_REG; - } - -+static inline bool ublk_dev_support_auto_buf_reg(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_AUTO_BUF_REG; -+} -+ - static inline bool ublk_support_user_copy(const struct ublk_queue *ubq) - { - return ubq->flags & UBLK_F_USER_COPY; - } - -+static inline bool ublk_dev_support_user_copy(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_USER_COPY; -+} -+ - static inline bool ublk_need_map_io(const struct ublk_queue *ubq) - { - return !ublk_support_user_copy(ubq) && !ublk_support_zero_copy(ubq) && - !ublk_support_auto_buf_reg(ubq); - } - -+static inline bool ublk_dev_need_map_io(const struct ublk_device *ub) -+{ -+ return !ublk_dev_support_user_copy(ub) && -+ !ublk_dev_support_zero_copy(ub) && -+ !ublk_dev_support_auto_buf_reg(ub); -+} -+ - static inline bool ublk_need_req_ref(const struct ublk_queue *ubq) - { - /* -@@ -697,6 +719,13 @@ static inline bool ublk_need_req_ref(const struct ublk_queue *ubq) - ublk_support_auto_buf_reg(ubq); - } - -+static inline bool ublk_dev_need_req_ref(const struct ublk_device *ub) -+{ -+ return ublk_dev_support_user_copy(ub) || -+ ublk_dev_support_zero_copy(ub) || -+ ublk_dev_support_auto_buf_reg(ub); -+} -+ - static inline void ublk_init_req_ref(const struct ublk_queue *ubq, - struct ublk_io *io) - { -@@ -728,6 +757,11 @@ static inline bool ublk_need_get_data(const struct ublk_queue *ubq) - return ubq->flags & UBLK_F_NEED_GET_DATA; - } - -+static inline bool ublk_dev_need_get_data(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_NEED_GET_DATA; -+} -+ - /* Called in slow path only, keep it noinline for trace purpose */ - static noinline struct ublk_device *ublk_get_device(struct ublk_device *ub) - { --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71070-2.patch b/SPECS/kernel-rt/CVE-2025-71070-2.patch deleted file mode 100644 index 4ff315c75..000000000 --- a/SPECS/kernel-rt/CVE-2025-71070-2.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 04329cce6eb616853738bee0b4aedfe8d84452cd Mon Sep 17 00:00:00 2001 -From: Caleb Sander Mateos -Date: Fri, 12 Dec 2025 17:19:49 -0700 -Subject: [PATCH 18/38] ublk: clean up user copy references on ublk server exit - -If a ublk server process releases a ublk char device file, any requests -dispatched to the ublk server but not yet completed will retain a ref -value of UBLK_REFCOUNT_INIT. Before commit e63d2228ef83 ("ublk: simplify -aborting ublk request"), __ublk_fail_req() would decrement the reference -count before completing the failed request. However, that commit -optimized __ublk_fail_req() to call __ublk_complete_rq() directly -without decrementing the request reference count. -The leaked reference count incorrectly allows user copy and zero copy -operations on the completed ublk request. It also triggers the -WARN_ON_ONCE(refcount_read(&io->ref)) warnings in ublk_queue_reinit() -and ublk_deinit_queue(). -Commit c5c5eb24ed61 ("ublk: avoid ublk_io_release() called after ublk -char dev is closed") already fixed the issue for ublk devices using -UBLK_F_SUPPORT_ZERO_COPY or UBLK_F_AUTO_BUF_REG. However, the reference -count leak also affects UBLK_F_USER_COPY, the other reference-counted -data copy mode. Fix the condition in ublk_check_and_reset_active_ref() -to include all reference-counted data copy modes. This ensures that any -ublk requests still owned by the ublk server when it exits have their -reference counts reset to 0. - -Signed-off-by: Caleb Sander Mateos -Fixes: e63d2228ef83 ("ublk: simplify aborting ublk request") -Reviewed-by: Ming Lei -Signed-off-by: Jens Axboe ---- - drivers/block/ublk_drv.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c -index 9a770d91e840..021274a339b5 100644 ---- a/drivers/block/ublk_drv.c -+++ b/drivers/block/ublk_drv.c -@@ -1634,8 +1634,7 @@ static bool ublk_check_and_reset_active_ref(struct ublk_device *ub) - { - int i, j; - -- if (!(ub->dev_info.flags & (UBLK_F_SUPPORT_ZERO_COPY | -- UBLK_F_AUTO_BUF_REG))) -+ if (!ublk_dev_need_req_ref(ub)) - return false; - - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71074.patch b/SPECS/kernel-rt/CVE-2025-71074.patch deleted file mode 100644 index ce60285fa..000000000 --- a/SPECS/kernel-rt/CVE-2025-71074.patch +++ /dev/null @@ -1,136 +0,0 @@ -From 332c42437dca12e7139e5c58747cdcae44c78e27 Mon Sep 17 00:00:00 2001 -From: Al Viro -Date: Fri, 14 Nov 2025 02:18:22 -0500 -Subject: [PATCH 19/38] functionfs: fix the open/removal races - -ffs_epfile_open() can race with removal, ending up with file->private_data -pointing to freed object. - -There is a total count of opened files on functionfs (both ep0 and -dynamic ones) and when it hits zero, dynamic files get removed. -Unfortunately, that removal can happen while another thread is -in ffs_epfile_open(), but has not incremented the count yet. -In that case open will succeed, leaving us with UAF on any subsequent -read() or write(). - -The root cause is that ffs->opened is misused; atomic_dec_and_test() vs. -atomic_add_return() is not a good idea, when object remains visible all -along. - -To untangle that - * serialize openers on ffs->mutex (both for ep0 and for dynamic files) - * have dynamic ones use atomic_inc_not_zero() and fail if we had -zero ->opened; in that case the file we are opening is doomed. - * have the inodes of dynamic files marked on removal (from the -callback of simple_recursive_removal()) - clear ->i_private there. - * have open of dynamic ones verify they hadn't been already removed, -along with checking that state is FFS_ACTIVE. - -Reviewed-by: Greg Kroah-Hartman -Signed-off-by: Al Viro ---- - drivers/usb/gadget/function/f_fs.c | 53 ++++++++++++++++++++++++------ - 1 file changed, 43 insertions(+), 10 deletions(-) - -diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c -index 04058261cdd0..ce94a5101537 100644 ---- a/drivers/usb/gadget/function/f_fs.c -+++ b/drivers/usb/gadget/function/f_fs.c -@@ -640,13 +640,22 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, - - static int ffs_ep0_open(struct inode *inode, struct file *file) - { -- struct ffs_data *ffs = inode->i_private; -+ struct ffs_data *ffs = inode->i_sb->s_fs_info; -+ int ret; - -- if (ffs->state == FFS_CLOSING) -- return -EBUSY; -+ /* Acquire mutex */ -+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); -+ if (ret < 0) -+ return ret; - -- file->private_data = ffs; - ffs_data_opened(ffs); -+ if (ffs->state == FFS_CLOSING) { -+ ffs_data_closed(ffs); -+ mutex_unlock(&ffs->mutex); -+ return -EBUSY; -+ } -+ mutex_unlock(&ffs->mutex); -+ file->private_data = ffs; - - return stream_open(inode, file); - } -@@ -1193,14 +1202,33 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) - static int - ffs_epfile_open(struct inode *inode, struct file *file) - { -- struct ffs_epfile *epfile = inode->i_private; -+ struct ffs_data *ffs = inode->i_sb->s_fs_info; -+ struct ffs_epfile *epfile; -+ int ret; - -- if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) -+ /* Acquire mutex */ -+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); -+ if (ret < 0) -+ return ret; -+ -+ if (!atomic_inc_not_zero(&ffs->opened)) { -+ mutex_unlock(&ffs->mutex); -+ return -ENODEV; -+ } -+ /* -+ * we want the state to be FFS_ACTIVE; FFS_ACTIVE alone is -+ * not enough, though - we might have been through FFS_CLOSING -+ * and back to FFS_ACTIVE, with our file already removed. -+ */ -+ epfile = smp_load_acquire(&inode->i_private); -+ if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) { -+ mutex_unlock(&ffs->mutex); -+ ffs_data_closed(ffs); - return -ENODEV; -+ } -+ mutex_unlock(&ffs->mutex); - - file->private_data = epfile; -- ffs_data_opened(epfile->ffs); -- - return stream_open(inode, file); - } - -@@ -1332,7 +1360,7 @@ static void ffs_dmabuf_put(struct dma_buf_attachment *attach) - static int - ffs_epfile_release(struct inode *inode, struct file *file) - { -- struct ffs_epfile *epfile = inode->i_private; -+ struct ffs_epfile *epfile = file->private_data; - struct ffs_dmabuf_priv *priv, *tmp; - struct ffs_data *ffs = epfile->ffs; - -@@ -2352,6 +2380,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs) - return 0; - } - -+static void clear_one(struct dentry *dentry) -+{ -+ smp_store_release(&dentry->d_inode->i_private, NULL); -+} -+ - static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) - { - struct ffs_epfile *epfile = epfiles; -@@ -2359,7 +2392,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) - for (; count; --count, ++epfile) { - BUG_ON(mutex_is_locked(&epfile->mutex)); - if (epfile->dentry) { -- simple_recursive_removal(epfile->dentry, NULL); -+ simple_recursive_removal(epfile->dentry, clear_one); - epfile->dentry = NULL; - } - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71090.patch b/SPECS/kernel-rt/CVE-2025-71090.patch deleted file mode 100644 index 16f48d069..000000000 --- a/SPECS/kernel-rt/CVE-2025-71090.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 7f986c0e5ce17634353dcdc1d2578804b358b269 Mon Sep 17 00:00:00 2001 -From: Chuck Lever -Date: Mon, 1 Dec 2025 17:09:55 -0500 -Subject: [PATCH 16/38] nfsd: fix nfsd_file reference leak in - nfsd4_add_rdaccess_to_wrdeleg() - -nfsd4_add_rdaccess_to_wrdeleg() unconditionally overwrites -fp->fi_fds[O_RDONLY] with a newly acquired nfsd_file. However, if -the client already has a SHARE_ACCESS_READ open from a previous OPEN -operation, this action overwrites the existing pointer without -releasing its reference, orphaning the previous reference. - -Additionally, the function originally stored the same nfsd_file -pointer in both fp->fi_fds[O_RDONLY] and fp->fi_rdeleg_file with -only a single reference. When put_deleg_file() runs, it clears -fi_rdeleg_file and calls nfs4_file_put_access() to release the file. - -However, nfs4_file_put_access() only releases fi_fds[O_RDONLY] when -the fi_access[O_RDONLY] counter drops to zero. If another READ open -exists on the file, the counter remains elevated and the nfsd_file -reference from the delegation is never released. This potentially -causes open conflicts on that file. - -Then, on server shutdown, these leaks cause __nfsd_file_cache_purge() -to encounter files with an elevated reference count that cannot be -cleaned up, ultimately triggering a BUG() in kmem_cache_destroy() -because there are still nfsd_file objects allocated in that cache. - -Fixes: e7a8ebc305f2 ("NFSD: Offer write delegation for OPEN with OPEN4_SHARE_ACCESS_WRITE") -Cc: stable@vger.kernel.org -Reviewed-by: Jeff Layton -Signed-off-by: Chuck Lever ---- - fs/nfsd/nfs4state.c | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c -index 87e2a4a3f3a7..aa6b017ec176 100644 ---- a/fs/nfsd/nfs4state.c -+++ b/fs/nfsd/nfs4state.c -@@ -1218,8 +1218,10 @@ static void put_deleg_file(struct nfs4_file *fp) - - if (nf) - nfsd_file_put(nf); -- if (rnf) -+ if (rnf) { -+ nfsd_file_put(rnf); - nfs4_file_put_access(fp, NFS4_SHARE_ACCESS_READ); -+ } - } - - static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp) -@@ -6187,10 +6189,14 @@ nfsd4_add_rdaccess_to_wrdeleg(struct svc_rqst *rqstp, struct nfsd4_open *open, - fp = stp->st_stid.sc_file; - spin_lock(&fp->fi_lock); - __nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ); -- fp = stp->st_stid.sc_file; -- fp->fi_fds[O_RDONLY] = nf; -- fp->fi_rdeleg_file = nf; -+ if (!fp->fi_fds[O_RDONLY]) { -+ fp->fi_fds[O_RDONLY] = nf; -+ nf = NULL; -+ } -+ fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]); - spin_unlock(&fp->fi_lock); -+ if (nf) -+ nfsd_file_put(nf); - } - return true; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71115.patch b/SPECS/kernel-rt/CVE-2025-71115.patch deleted file mode 100644 index d30c2410e..000000000 --- a/SPECS/kernel-rt/CVE-2025-71115.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 16ecd5094ab8e04c3f208f31ff7d3a33f2e22f87 Mon Sep 17 00:00:00 2001 -From: Johannes Berg -Date: Wed, 24 Sep 2025 11:32:13 +0200 -Subject: [PATCH 15/38] um: init cpu_tasks[] earlier - -This is currently done in uml_finishsetup(), but e.g. with -KCOV enabled we'll crash because some init code can call -into e.g. memparse(), which has coverage annotations, and -then the checks in check_kcov_mode() crash because current -is NULL. - -Simply initialize the cpu_tasks[] array statically, which -fixes the crash. For the later SMP work, it seems to have -not really caused any problems yet, but initialize all of -the entries anyway. - -Link: https://patch.msgid.link/20250924113214.c76cd74d0583.I974f691ebb1a2b47915bd2b04cc38e5263b9447f@changeid -Signed-off-by: Johannes Berg ---- - arch/um/kernel/process.c | 4 +++- - arch/um/kernel/um_arch.c | 2 -- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c -index 9c9c66dc45f0..13d461712c99 100644 ---- a/arch/um/kernel/process.c -+++ b/arch/um/kernel/process.c -@@ -43,7 +43,9 @@ - * cares about its entry, so it's OK if another processor is modifying its - * entry. - */ --struct task_struct *cpu_tasks[NR_CPUS]; -+struct task_struct *cpu_tasks[NR_CPUS] = { -+ [0 ... NR_CPUS - 1] = &init_task, -+}; - EXPORT_SYMBOL(cpu_tasks); - - void free_stack(unsigned long stack, int order) -diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c -index 2f5ee045bc7a..f2edd95d7663 100644 ---- a/arch/um/kernel/um_arch.c -+++ b/arch/um/kernel/um_arch.c -@@ -242,8 +242,6 @@ static struct notifier_block panic_exit_notifier = { - - void uml_finishsetup(void) - { -- cpu_tasks[0] = &init_task; -- - atomic_notifier_chain_register(&panic_notifier_list, - &panic_exit_notifier); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71117.patch b/SPECS/kernel-rt/CVE-2025-71117.patch deleted file mode 100644 index 20576bf06..000000000 --- a/SPECS/kernel-rt/CVE-2025-71117.patch +++ /dev/null @@ -1,219 +0,0 @@ -From 0052e8a55c69e9476b1f36764f4c70f5155c93a3 Mon Sep 17 00:00:00 2001 -From: Bart Van Assche -Date: Fri, 14 Nov 2025 13:04:07 -0800 -Subject: [PATCH 10/38] block: Remove queue freezing from several sysfs store - callbacks - -Freezing the request queue from inside sysfs store callbacks may cause a -deadlock in combination with the dm-multipath driver and the -queue_if_no_path option. Additionally, freezing the request queue slows -down system boot on systems where sysfs attributes are set synchronously. - -Fix this by removing the blk_mq_freeze_queue() / blk_mq_unfreeze_queue() -calls from the store callbacks that do not strictly need these callbacks. -Add the __data_racy annotation to request_queue.rq_timeout to suppress -KCSAN data race reports about the rq_timeout reads. - -This patch may cause a small delay in applying the new settings. - -For all the attributes affected by this patch, I/O will complete -correctly whether the old or the new value of the attribute is used. - -This patch affects the following sysfs attributes: -* io_poll_delay -* io_timeout -* nomerges -* read_ahead_kb -* rq_affinity - -Here is an example of a deadlock triggered by running test srp/002 -if this patch is not applied: - -task:multipathd -Call Trace: - - __schedule+0x8c1/0x1bf0 - schedule+0xdd/0x270 - schedule_preempt_disabled+0x1c/0x30 - __mutex_lock+0xb89/0x1650 - mutex_lock_nested+0x1f/0x30 - dm_table_set_restrictions+0x823/0xdf0 - __bind+0x166/0x590 - dm_swap_table+0x2a7/0x490 - do_resume+0x1b1/0x610 - dev_suspend+0x55/0x1a0 - ctl_ioctl+0x3a5/0x7e0 - dm_ctl_ioctl+0x12/0x20 - __x64_sys_ioctl+0x127/0x1a0 - x64_sys_call+0xe2b/0x17d0 - do_syscall_64+0x96/0x3a0 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -task:(udev-worker) -Call Trace: - - __schedule+0x8c1/0x1bf0 - schedule+0xdd/0x270 - blk_mq_freeze_queue_wait+0xf2/0x140 - blk_mq_freeze_queue_nomemsave+0x23/0x30 - queue_ra_store+0x14e/0x290 - queue_attr_store+0x23e/0x2c0 - sysfs_kf_write+0xde/0x140 - kernfs_fop_write_iter+0x3b2/0x630 - vfs_write+0x4fd/0x1390 - ksys_write+0xfd/0x230 - __x64_sys_write+0x76/0xc0 - x64_sys_call+0x276/0x17d0 - do_syscall_64+0x96/0x3a0 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - - -Cc: Christoph Hellwig -Cc: Ming Lei -Cc: Nilay Shroff -Cc: Martin Wilck -Cc: Benjamin Marzinski -Cc: stable@vger.kernel.org -Fixes: af2814149883 ("block: freeze the queue in queue_attr_store") -Signed-off-by: Bart Van Assche -Reviewed-by: Nilay Shroff -Signed-off-by: Jens Axboe ---- - block/blk-sysfs.c | 26 ++++++++------------------ - include/linux/blkdev.h | 2 +- - 2 files changed, 9 insertions(+), 19 deletions(-) - -diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c -index 9b03261b3e04..9e292d42df60 100644 ---- a/block/blk-sysfs.c -+++ b/block/blk-sysfs.c -@@ -146,21 +146,22 @@ queue_ra_store(struct gendisk *disk, const char *page, size_t count) - { - unsigned long ra_kb; - ssize_t ret; -- unsigned int memflags; - struct request_queue *q = disk->queue; - - ret = queue_var_store(&ra_kb, page, count); - if (ret < 0) - return ret; - /* -- * ->ra_pages is protected by ->limits_lock because it is usually -- * calculated from the queue limits by queue_limits_commit_update. -+ * The ->ra_pages change below is protected by ->limits_lock because it -+ * is usually calculated from the queue limits by -+ * queue_limits_commit_update(). -+ * -+ * bdi->ra_pages reads are not serialized against bdi->ra_pages writes. -+ * Use WRITE_ONCE() to write bdi->ra_pages once. - */ - mutex_lock(&q->limits_lock); -- memflags = blk_mq_freeze_queue(q); -- disk->bdi->ra_pages = ra_kb >> (PAGE_SHIFT - 10); -+ WRITE_ONCE(disk->bdi->ra_pages, ra_kb >> (PAGE_SHIFT - 10)); - mutex_unlock(&q->limits_lock); -- blk_mq_unfreeze_queue(q, memflags); - - return ret; - } -@@ -378,21 +379,18 @@ static ssize_t queue_nomerges_store(struct gendisk *disk, const char *page, - size_t count) - { - unsigned long nm; -- unsigned int memflags; - struct request_queue *q = disk->queue; - ssize_t ret = queue_var_store(&nm, page, count); - - if (ret < 0) - return ret; - -- memflags = blk_mq_freeze_queue(q); - blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, q); - blk_queue_flag_clear(QUEUE_FLAG_NOXMERGES, q); - if (nm == 2) - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, q); - else if (nm) - blk_queue_flag_set(QUEUE_FLAG_NOXMERGES, q); -- blk_mq_unfreeze_queue(q, memflags); - - return ret; - } -@@ -412,7 +410,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count) - #ifdef CONFIG_SMP - struct request_queue *q = disk->queue; - unsigned long val; -- unsigned int memflags; - - ret = queue_var_store(&val, page, count); - if (ret < 0) -@@ -424,7 +421,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count) - * are accessed individually using atomic test_bit operation. So we - * don't grab any lock while updating these flags. - */ -- memflags = blk_mq_freeze_queue(q); - if (val == 2) { - blk_queue_flag_set(QUEUE_FLAG_SAME_COMP, q); - blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, q); -@@ -435,7 +431,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count) - blk_queue_flag_clear(QUEUE_FLAG_SAME_COMP, q); - blk_queue_flag_clear(QUEUE_FLAG_SAME_FORCE, q); - } -- blk_mq_unfreeze_queue(q, memflags); - #endif - return ret; - } -@@ -449,11 +444,9 @@ static ssize_t queue_poll_delay_store(struct gendisk *disk, const char *page, - static ssize_t queue_poll_store(struct gendisk *disk, const char *page, - size_t count) - { -- unsigned int memflags; - ssize_t ret = count; - struct request_queue *q = disk->queue; - -- memflags = blk_mq_freeze_queue(q); - if (!(q->limits.features & BLK_FEAT_POLL)) { - ret = -EINVAL; - goto out; -@@ -462,7 +455,6 @@ static ssize_t queue_poll_store(struct gendisk *disk, const char *page, - pr_info_ratelimited("writes to the poll attribute are ignored.\n"); - pr_info_ratelimited("please use driver specific parameters instead.\n"); - out: -- blk_mq_unfreeze_queue(q, memflags); - return ret; - } - -@@ -475,7 +467,7 @@ static ssize_t queue_io_timeout_show(struct gendisk *disk, char *page) - static ssize_t queue_io_timeout_store(struct gendisk *disk, const char *page, - size_t count) - { -- unsigned int val, memflags; -+ unsigned int val; - int err; - struct request_queue *q = disk->queue; - -@@ -483,9 +475,7 @@ static ssize_t queue_io_timeout_store(struct gendisk *disk, const char *page, - if (err || val == 0) - return -EINVAL; - -- memflags = blk_mq_freeze_queue(q); - blk_queue_rq_timeout(q, msecs_to_jiffies(val)); -- blk_mq_unfreeze_queue(q, memflags); - - return count; - } -diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h -index 37fa7169fa9f..a73ed5d85c22 100644 ---- a/include/linux/blkdev.h -+++ b/include/linux/blkdev.h -@@ -485,7 +485,7 @@ struct request_queue { - */ - unsigned long queue_flags; - -- unsigned int rq_timeout; -+ unsigned int __data_racy rq_timeout; - - unsigned int queue_depth; - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71128.patch b/SPECS/kernel-rt/CVE-2025-71128.patch deleted file mode 100644 index 061128e94..000000000 --- a/SPECS/kernel-rt/CVE-2025-71128.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 687b69709d0498240643fde5aa6481b40389f88a Mon Sep 17 00:00:00 2001 -From: Frode Nordahl -Date: Sat, 13 Dec 2025 10:13:36 +0000 -Subject: [PATCH 11/38] erspan: Initialize options_len before referencing - options. - -The struct ip_tunnel_info has a flexible array member named -options that is protected by a counted_by(options_len) -attribute. - -The compiler will use this information to enforce runtime bounds -checking deployed by FORTIFY_SOURCE string helpers. - -As laid out in the GCC documentation, the counter must be -initialized before the first reference to the flexible array -member. - -After scanning through the files that use struct ip_tunnel_info -and also refer to options or options_len, it appears the normal -case is to use the ip_tunnel_info_opts_set() helper. - -Said helper would initialize options_len properly before copying -data into options, however in the GRE ERSPAN code a partial -update is done, preventing the use of the helper function. - -Before this change the handling of ERSPAN traffic in GRE tunnels -would cause a kernel panic when the kernel is compiled with -GCC 15+ and having FORTIFY_SOURCE configured: - -memcpy: detected buffer overflow: 4 byte write of buffer size 0 - -Call Trace: - - __fortify_panic+0xd/0xf - erspan_rcv.cold+0x68/0x83 - ? ip_route_input_slow+0x816/0x9d0 - gre_rcv+0x1b2/0x1c0 - gre_rcv+0x8e/0x100 - ? raw_v4_input+0x2a0/0x2b0 - ip_protocol_deliver_rcu+0x1ea/0x210 - ip_local_deliver_finish+0x86/0x110 - ip_local_deliver+0x65/0x110 - ? ip_rcv_finish_core+0xd6/0x360 - ip_rcv+0x186/0x1a0 - -Cc: stable@vger.kernel.org -Link: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-counted_005fby-variable-attribute -Reported-at: https://launchpad.net/bugs/2129580 -Fixes: bb5e62f2d547 ("net: Add options as a flexible array to struct ip_tunnel_info") -Signed-off-by: Frode Nordahl -Reviewed-by: Simon Horman -Link: https://patch.msgid.link/20251213101338.4693-1-fnordahl@ubuntu.com -Signed-off-by: Paolo Abeni ---- - net/ipv4/ip_gre.c | 6 ++++-- - net/ipv6/ip6_gre.c | 6 ++++-- - 2 files changed, 8 insertions(+), 4 deletions(-) - -diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c -index f5b9004d6938..f8a8a524a199 100644 ---- a/net/ipv4/ip_gre.c -+++ b/net/ipv4/ip_gre.c -@@ -330,6 +330,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, - if (!tun_dst) - return PACKET_REJECT; - -+ /* MUST set options_len before referencing options */ -+ info = &tun_dst->u.tun_info; -+ info->options_len = sizeof(*md); -+ - /* skb can be uncloned in __iptunnel_pull_header, so - * old pkt_md is no longer valid and we need to reset - * it -@@ -344,10 +348,8 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, - memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE : - ERSPAN_V2_MDSIZE); - -- info = &tun_dst->u.tun_info; - __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, - info->key.tun_flags); -- info->options_len = sizeof(*md); - } - - skb_reset_mac_header(skb); -diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c -index 74d49dd6124d..cc1757eaf942 100644 ---- a/net/ipv6/ip6_gre.c -+++ b/net/ipv6/ip6_gre.c -@@ -535,6 +535,10 @@ static int ip6erspan_rcv(struct sk_buff *skb, - if (!tun_dst) - return PACKET_REJECT; - -+ /* MUST set options_len before referencing options */ -+ info = &tun_dst->u.tun_info; -+ info->options_len = sizeof(*md); -+ - /* skb can be uncloned in __iptunnel_pull_header, so - * old pkt_md is no longer valid and we need to reset - * it -@@ -543,7 +547,6 @@ static int ip6erspan_rcv(struct sk_buff *skb, - skb_network_header_len(skb); - pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len + - sizeof(*ershdr)); -- info = &tun_dst->u.tun_info; - md = ip_tunnel_info_opts(info); - md->version = ver; - md2 = &md->u.md2; -@@ -551,7 +554,6 @@ static int ip6erspan_rcv(struct sk_buff *skb, - ERSPAN_V2_MDSIZE); - __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, - info->key.tun_flags); -- info->options_len = sizeof(*md); - - ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); - --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71139-1.patch b/SPECS/kernel-rt/CVE-2025-71139-1.patch deleted file mode 100644 index 83995468e..000000000 --- a/SPECS/kernel-rt/CVE-2025-71139-1.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 3cc295f54779c5e6aa1412d1990100ba8fe14ae8 Mon Sep 17 00:00:00 2001 -From: Pingfan Liu -Date: Tue, 16 Dec 2025 09:48:51 +0800 -Subject: [PATCH 12/38] kernel/kexec: change the prototype of - kimage_map_segment() - -The kexec segment index will be required to extract the corresponding -information for that segment in kimage_map_segment(). Additionally, -kexec_segment already holds the kexec relocation destination address and -size. Therefore, the prototype of kimage_map_segment() can be changed. - -Link: https://lkml.kernel.org/r/20251216014852.8737-1-piliu@redhat.com -Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation") -Signed-off-by: Pingfan Liu -Acked-by: Baoquan He -Cc: Mimi Zohar -Cc: Roberto Sassu -Cc: Alexander Graf -Cc: Steven Chen -Cc: -Signed-off-by: Andrew Morton ---- - include/linux/kexec.h | 4 ++-- - kernel/kexec_core.c | 9 ++++++--- - security/integrity/ima/ima_kexec.c | 4 +--- - 3 files changed, 9 insertions(+), 8 deletions(-) - -diff --git a/include/linux/kexec.h b/include/linux/kexec.h -index 39fe3e6cd282..6c8abb2534cf 100644 ---- a/include/linux/kexec.h -+++ b/include/linux/kexec.h -@@ -527,7 +527,7 @@ extern bool kexec_file_dbg_print; - #define kexec_dprintk(fmt, arg...) \ - do { if (kexec_file_dbg_print) pr_info(fmt, ##arg); } while (0) - --extern void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size); -+extern void *kimage_map_segment(struct kimage *image, int idx); - extern void kimage_unmap_segment(void *buffer); - #else /* !CONFIG_KEXEC_CORE */ - struct pt_regs; -@@ -537,7 +537,7 @@ static inline void __crash_kexec(struct pt_regs *regs) { } - static inline void crash_kexec(struct pt_regs *regs) { } - static inline int kexec_should_crash(struct task_struct *p) { return 0; } - static inline int kexec_crash_loaded(void) { return 0; } --static inline void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size) -+static inline void *kimage_map_segment(struct kimage *image, int idx) - { return NULL; } - static inline void kimage_unmap_segment(void *buffer) { } - #define kexec_in_progress false -diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c -index 31203f0bacaf..8af0891732d0 100644 ---- a/kernel/kexec_core.c -+++ b/kernel/kexec_core.c -@@ -961,17 +961,20 @@ int kimage_load_segment(struct kimage *image, int idx) - return result; - } - --void *kimage_map_segment(struct kimage *image, -- unsigned long addr, unsigned long size) -+void *kimage_map_segment(struct kimage *image, int idx) - { -+ unsigned long addr, size, eaddr; - unsigned long src_page_addr, dest_page_addr = 0; -- unsigned long eaddr = addr + size; - kimage_entry_t *ptr, entry; - struct page **src_pages; - unsigned int npages; - void *vaddr = NULL; - int i; - -+ addr = image->segment[idx].mem; -+ size = image->segment[idx].memsz; -+ eaddr = addr + size; -+ - /* - * Collect the source pages and map them in a contiguous VA range. - */ -diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c -index 7362f68f2d8b..5beb69edd12f 100644 ---- a/security/integrity/ima/ima_kexec.c -+++ b/security/integrity/ima/ima_kexec.c -@@ -250,9 +250,7 @@ void ima_kexec_post_load(struct kimage *image) - if (!image->ima_buffer_addr) - return; - -- ima_kexec_buffer = kimage_map_segment(image, -- image->ima_buffer_addr, -- image->ima_buffer_size); -+ ima_kexec_buffer = kimage_map_segment(image, image->ima_segment_index); - if (!ima_kexec_buffer) { - pr_err("Could not map measurements buffer.\n"); - return; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71139-2.patch b/SPECS/kernel-rt/CVE-2025-71139-2.patch deleted file mode 100644 index 341adfba8..000000000 --- a/SPECS/kernel-rt/CVE-2025-71139-2.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 68cb92cb11c6bd015aa8b638624f54f269ab2657 Mon Sep 17 00:00:00 2001 -From: Pingfan Liu -Date: Tue, 16 Dec 2025 09:48:52 +0800 -Subject: [PATCH 13/38] kernel/kexec: fix IMA when allocation happens in CMA - area - -*** Bug description *** - -When I tested kexec with the latest kernel, I ran into the following warning: - -[ 40.712410] ------------[ cut here ]------------ -[ 40.712576] WARNING: CPU: 2 PID: 1562 at kernel/kexec_core.c:1001 kimage_map_segment+0x144/0x198 -[...] -[ 40.816047] Call trace: -[ 40.818498] kimage_map_segment+0x144/0x198 (P) -[ 40.823221] ima_kexec_post_load+0x58/0xc0 -[ 40.827246] __do_sys_kexec_file_load+0x29c/0x368 -[...] -[ 40.855423] ---[ end trace 0000000000000000 ]--- - -*** How to reproduce *** - -This bug is only triggered when the kexec target address is allocated in -the CMA area. If no CMA area is reserved in the kernel, use the "cma=" -option in the kernel command line to reserve one. - -*** Root cause *** -The commit 07d24902977e ("kexec: enable CMA based contiguous -allocation") allocates the kexec target address directly on the CMA area -to avoid copying during the jump. In this case, there is no IND_SOURCE -for the kexec segment. But the current implementation of -kimage_map_segment() assumes that IND_SOURCE pages exist and map them -into a contiguous virtual address by vmap(). - -*** Solution *** -If IMA segment is allocated in the CMA area, use its page_address() -directly. - -Link: https://lkml.kernel.org/r/20251216014852.8737-2-piliu@redhat.com -Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation") -Signed-off-by: Pingfan Liu -Acked-by: Baoquan He -Cc: Alexander Graf -Cc: Steven Chen -Cc: Mimi Zohar -Cc: Roberto Sassu -Cc: -Signed-off-by: Andrew Morton ---- - kernel/kexec_core.c | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c -index 8af0891732d0..2f1c62c817e6 100644 ---- a/kernel/kexec_core.c -+++ b/kernel/kexec_core.c -@@ -968,13 +968,17 @@ void *kimage_map_segment(struct kimage *image, int idx) - kimage_entry_t *ptr, entry; - struct page **src_pages; - unsigned int npages; -+ struct page *cma; - void *vaddr = NULL; - int i; - -+ cma = image->segment_cma[idx]; -+ if (cma) -+ return page_address(cma); -+ - addr = image->segment[idx].mem; - size = image->segment[idx].memsz; - eaddr = addr + size; -- - /* - * Collect the source pages and map them in a contiguous VA range. - */ -@@ -1015,7 +1019,8 @@ void *kimage_map_segment(struct kimage *image, int idx) - - void kimage_unmap_segment(void *segment_buffer) - { -- vunmap(segment_buffer); -+ if (is_vmalloc_addr(segment_buffer)) -+ vunmap(segment_buffer); - } - - struct kexec_load_limit { --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71142.patch b/SPECS/kernel-rt/CVE-2025-71142.patch deleted file mode 100644 index 32a0fe88b..000000000 --- a/SPECS/kernel-rt/CVE-2025-71142.patch +++ /dev/null @@ -1,112 +0,0 @@ -From ef88484b98a0cb6edcbccdebc0eac1062ae97acc Mon Sep 17 00:00:00 2001 -From: Chen Ridong -Date: Thu, 18 Dec 2025 01:59:50 +0000 -Subject: [PATCH 14/38] cpuset: fix warning when disabling remote partition - -A warning was triggered as follows: - -WARNING: kernel/cgroup/cpuset.c:1651 at remote_partition_disable+0xf7/0x110 -RIP: 0010:remote_partition_disable+0xf7/0x110 -RSP: 0018:ffffc90001947d88 EFLAGS: 00000206 -RAX: 0000000000007fff RBX: ffff888103b6e000 RCX: 0000000000006f40 -RDX: 0000000000006f00 RSI: ffffc90001947da8 RDI: ffff888103b6e000 -RBP: ffff888103b6e000 R08: 0000000000000000 R09: 0000000000000000 -R10: 0000000000000001 R11: ffff88810b2e2728 R12: ffffc90001947da8 -R13: 0000000000000000 R14: ffffc90001947da8 R15: ffff8881081f1c00 -CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 -CR2: 00007f55c8bbe0b2 CR3: 000000010b14c000 CR4: 00000000000006f0 -Call Trace: - - update_prstate+0x2d3/0x580 - cpuset_partition_write+0x94/0xf0 - kernfs_fop_write_iter+0x147/0x200 - vfs_write+0x35d/0x500 - ksys_write+0x66/0xe0 - do_syscall_64+0x6b/0x390 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 -RIP: 0033:0x7f55c8cd4887 - -Reproduction steps (on a 16-CPU machine): - - # cd /sys/fs/cgroup/ - # mkdir A1 - # echo +cpuset > A1/cgroup.subtree_control - # echo "0-14" > A1/cpuset.cpus.exclusive - # mkdir A1/A2 - # echo "0-14" > A1/A2/cpuset.cpus.exclusive - # echo "root" > A1/A2/cpuset.cpus.partition - # echo 0 > /sys/devices/system/cpu/cpu15/online - # echo member > A1/A2/cpuset.cpus.partition - -When CPU 15 is offlined, subpartitions_cpus gets cleared because no CPUs -remain available for the top_cpuset, forcing partitions to share CPUs with -the top_cpuset. In this scenario, disabling the remote partition triggers -a warning stating that effective_xcpus is not a subset of -subpartitions_cpus. Partitions should be invalidated in this case to -inform users that the partition is now invalid(cpus are shared with -top_cpuset). - -To fix this issue: -1. Only emit the warning only if subpartitions_cpus is not empty and the - effective_xcpus is not a subset of subpartitions_cpus. -2. During the CPU hotplug process, invalidate partitions if - subpartitions_cpus is empty. - -Fixes: f62a5d39368e ("cgroup/cpuset: Remove remote_partition_check() & make update_cpumasks_hier() handle remote partition") -Signed-off-by: Chen Ridong -Reviewed-by: Waiman Long -Signed-off-by: Tejun Heo ---- - kernel/cgroup/cpuset.c | 21 ++++++++++++++++----- - 1 file changed, 16 insertions(+), 5 deletions(-) - -diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c -index fd890b34a840..bf380f11b874 100644 ---- a/kernel/cgroup/cpuset.c -+++ b/kernel/cgroup/cpuset.c -@@ -1496,7 +1496,14 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp) - bool isolcpus_updated; - - WARN_ON_ONCE(!is_remote_partition(cs)); -- WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus)); -+ /* -+ * When a CPU is offlined, top_cpuset may end up with no available CPUs, -+ * which should clear subpartitions_cpus. We should not emit a warning for this -+ * scenario: the hierarchy is updated from top to bottom, so subpartitions_cpus -+ * may already be cleared when disabling the partition. -+ */ -+ WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus) && -+ !cpumask_empty(subpartitions_cpus)); - - spin_lock_irq(&callback_lock); - list_del_init(&cs->remote_sibling); -@@ -3844,8 +3851,9 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) - if (remote || (is_partition_valid(cs) && is_partition_valid(parent))) - compute_partition_effective_cpumask(cs, &new_cpus); - -- if (remote && cpumask_empty(&new_cpus) && -- partition_is_populated(cs, NULL)) { -+ if (remote && (cpumask_empty(subpartitions_cpus) || -+ (cpumask_empty(&new_cpus) && -+ partition_is_populated(cs, NULL)))) { - cs->prs_err = PERR_HOTPLUG; - remote_partition_disable(cs, tmp); - compute_effective_cpumask(&new_cpus, cs, parent); -@@ -3858,9 +3866,12 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) - * 1) empty effective cpus but not valid empty partition. - * 2) parent is invalid or doesn't grant any cpus to child - * partitions. -+ * 3) subpartitions_cpus is empty. - */ -- if (is_local_partition(cs) && (!is_partition_valid(parent) || -- tasks_nocpu_error(parent, cs, &new_cpus))) -+ if (is_local_partition(cs) && -+ (!is_partition_valid(parent) || -+ tasks_nocpu_error(parent, cs, &new_cpus) || -+ cpumask_empty(subpartitions_cpus))) - partcmd = partcmd_invalidate; - /* - * On the other hand, an invalid partition root may be transitioned --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2025-71161.patch b/SPECS/kernel-rt/CVE-2025-71161.patch deleted file mode 100644 index a2ba95045..000000000 --- a/SPECS/kernel-rt/CVE-2025-71161.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 4398ba78106304ff9c60518b9d86e17cbc41089e Mon Sep 17 00:00:00 2001 -From: Mikulas Patocka -Date: Fri, 14 Nov 2025 16:54:01 +0100 -Subject: [PATCH 08/38] dm-verity: disable recursive forward error correction - -There are two problems with the recursive correction: - -1. It may cause denial-of-service. In fec_read_bufs, there is a loop that -has 253 iterations. For each iteration, we may call verity_hash_for_block -recursively. There is a limit of 4 nested recursions - that means that -there may be at most 253^4 (4 billion) iterations. Red Hat QE team -actually created an image that pushes dm-verity to this limit - and this -image just makes the udev-worker process get stuck in the 'D' state. - -2. It doesn't work. In fec_read_bufs we store data into the variable -"fio->bufs", but fio bufs is shared between recursive invocations, if -"verity_hash_for_block" invoked correction recursively, it would -overwrite partially filled fio->bufs. - -Signed-off-by: Mikulas Patocka -Reported-by: Guangwu Zhang -Reviewed-by: Sami Tolvanen -Reviewed-by: Eric Biggers ---- - drivers/md/dm-verity-fec.c | 4 +--- - drivers/md/dm-verity-fec.h | 3 --- - drivers/md/dm-verity-target.c | 2 +- - 3 files changed, 2 insertions(+), 7 deletions(-) - -diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c -index 72047b47a7a0..e41bde1d3b15 100644 ---- a/drivers/md/dm-verity-fec.c -+++ b/drivers/md/dm-verity-fec.c -@@ -413,10 +413,8 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io, - if (!verity_fec_is_enabled(v)) - return -EOPNOTSUPP; - -- if (fio->level >= DM_VERITY_FEC_MAX_RECURSION) { -- DMWARN_LIMIT("%s: FEC: recursion too deep", v->data_dev->name); -+ if (fio->level) - return -EIO; -- } - - fio->level++; - -diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h -index 09123a612953..ec37e607cb3f 100644 ---- a/drivers/md/dm-verity-fec.h -+++ b/drivers/md/dm-verity-fec.h -@@ -23,9 +23,6 @@ - #define DM_VERITY_FEC_BUF_MAX \ - (1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS)) - --/* maximum recursion level for verity_fec_decode */ --#define DM_VERITY_FEC_MAX_RECURSION 4 -- - #define DM_VERITY_OPT_FEC_DEV "use_fec_from_device" - #define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks" - #define DM_VERITY_OPT_FEC_START "fec_start" -diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c -index 66a00a8ccb39..c8695c079cfe 100644 ---- a/drivers/md/dm-verity-target.c -+++ b/drivers/md/dm-verity-target.c -@@ -1690,7 +1690,7 @@ static struct target_type verity_target = { - .name = "verity", - /* Note: the LSMs depend on the singleton and immutable features */ - .features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE, -- .version = {1, 12, 0}, -+ .version = {1, 13, 0}, - .module = THIS_MODULE, - .ctr = verity_ctr, - .dtr = verity_dtr, --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-22981.patch b/SPECS/kernel-rt/CVE-2026-22981.patch deleted file mode 100644 index ce2aee1a8..000000000 --- a/SPECS/kernel-rt/CVE-2026-22981.patch +++ /dev/null @@ -1,275 +0,0 @@ -From 0c2dcb673e8d902de98f3a24c07e788dcf14c35c Mon Sep 17 00:00:00 2001 -From: Emil Tantilov -Date: Thu, 20 Nov 2025 16:12:15 -0800 -Subject: [PATCH 07/38] idpf: detach and close netdevs while handling a reset - -Protect the reset path from callbacks by setting the netdevs to detached -state and close any netdevs in UP state until the reset handling has -completed. During a reset, the driver will de-allocate resources for the -vport, and there is no guarantee that those will recover, which is why the -existing vport_ctrl_lock does not provide sufficient protection. - -idpf_detach_and_close() is called right before reset handling. If the -reset handling succeeds, the netdevs state is recovered via call to -idpf_attach_and_open(). If the reset handling fails the netdevs remain -down. The detach/down calls are protected with RTNL lock to avoid racing -with callbacks. On the recovery side the attach can be done without -holding the RTNL lock as there are no callbacks expected at that point, -due to detach/close always being done first in that flow. - -The previous logic restoring the netdevs state based on the -IDPF_VPORT_UP_REQUESTED flag in the init task is not needed anymore, hence -the removal of idpf_set_vport_state(). The IDPF_VPORT_UP_REQUESTED is -still being used to restore the state of the netdevs following the reset, -but has no use outside of the reset handling flow. - -idpf_init_hard_reset() is converted to void, since it was used as such and -there is no error handling being done based on its return value. - -Before this change, invoking hard and soft resets simultaneously will -cause the driver to lose the vport state: -ip -br a - UP -echo 1 > /sys/class/net/ens801f0/device/reset& \ -ethtool -L ens801f0 combined 8 -ip -br a - DOWN -ip link set up -ip -br a - DOWN - -Also in case of a failure in the reset path, the netdev is left -exposed to external callbacks, while vport resources are not -initialized, leading to a crash on subsequent ifup/down: -[408471.398966] idpf 0000:83:00.0: HW reset detected -[408471.411744] idpf 0000:83:00.0: Device HW Reset initiated -[408472.277901] idpf 0000:83:00.0: The driver was unable to contact the device's firmware. Check that the FW is running. Driver state= 0x2 -[408508.125551] BUG: kernel NULL pointer dereference, address: 0000000000000078 -[408508.126112] #PF: supervisor read access in kernel mode -[408508.126687] #PF: error_code(0x0000) - not-present page -[408508.127256] PGD 2aae2f067 P4D 0 -[408508.127824] Oops: Oops: 0000 [#1] SMP NOPTI -... -[408508.130871] RIP: 0010:idpf_stop+0x39/0x70 [idpf] -... -[408508.139193] Call Trace: -[408508.139637] -[408508.140077] __dev_close_many+0xbb/0x260 -[408508.140533] __dev_change_flags+0x1cf/0x280 -[408508.140987] netif_change_flags+0x26/0x70 -[408508.141434] dev_change_flags+0x3d/0xb0 -[408508.141878] devinet_ioctl+0x460/0x890 -[408508.142321] inet_ioctl+0x18e/0x1d0 -[408508.142762] ? _copy_to_user+0x22/0x70 -[408508.143207] sock_do_ioctl+0x3d/0xe0 -[408508.143652] sock_ioctl+0x10e/0x330 -[408508.144091] ? find_held_lock+0x2b/0x80 -[408508.144537] __x64_sys_ioctl+0x96/0xe0 -[408508.144979] do_syscall_64+0x79/0x3d0 -[408508.145415] entry_SYSCALL_64_after_hwframe+0x76/0x7e -[408508.145860] RIP: 0033:0x7f3e0bb4caff - -Fixes: 0fe45467a104 ("idpf: add create vport and netdev configuration") -Signed-off-by: Emil Tantilov -Reviewed-by: Madhu Chittim -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen ---- - drivers/net/ethernet/intel/idpf/idpf_lib.c | 121 ++++++++++++--------- - 1 file changed, 72 insertions(+), 49 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index 7e22790aa9a8..99c7ffe118e5 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -727,6 +727,65 @@ static int idpf_init_mac_addr(struct idpf_vport *vport, - return 0; - } - -+static void idpf_detach_and_close(struct idpf_adapter *adapter) -+{ -+ int max_vports = adapter->max_vports; -+ -+ for (int i = 0; i < max_vports; i++) { -+ struct net_device *netdev = adapter->netdevs[i]; -+ -+ /* If the interface is in detached state, that means the -+ * previous reset was not handled successfully for this -+ * vport. -+ */ -+ if (!netif_device_present(netdev)) -+ continue; -+ -+ /* Hold RTNL to protect racing with callbacks */ -+ rtnl_lock(); -+ netif_device_detach(netdev); -+ if (netif_running(netdev)) { -+ set_bit(IDPF_VPORT_UP_REQUESTED, -+ adapter->vport_config[i]->flags); -+ dev_close(netdev); -+ } -+ rtnl_unlock(); -+ } -+} -+ -+static void idpf_attach_and_open(struct idpf_adapter *adapter) -+{ -+ int max_vports = adapter->max_vports; -+ -+ for (int i = 0; i < max_vports; i++) { -+ struct idpf_vport *vport = adapter->vports[i]; -+ struct idpf_vport_config *vport_config; -+ struct net_device *netdev; -+ -+ /* In case of a critical error in the init task, the vport -+ * will be freed. Only continue to restore the netdevs -+ * if the vport is allocated. -+ */ -+ if (!vport) -+ continue; -+ -+ /* No need for RTNL on attach as this function is called -+ * following detach and dev_close(). We do take RTNL for -+ * dev_open() below as it can race with external callbacks -+ * following the call to netif_device_attach(). -+ */ -+ netdev = adapter->netdevs[i]; -+ netif_device_attach(netdev); -+ vport_config = adapter->vport_config[vport->idx]; -+ if (test_and_clear_bit(IDPF_VPORT_UP_REQUESTED, -+ vport_config->flags)) { -+ rtnl_lock(); -+ dev_open(netdev, NULL); -+ rtnl_unlock(); -+ } -+ } -+} -+ - /** - * idpf_cfg_netdev - Allocate, configure and register a netdev - * @vport: main vport structure -@@ -1036,10 +1095,11 @@ static void idpf_vport_dealloc(struct idpf_vport *vport) - idpf_idc_deinit_vport_aux_device(vport->vdev_info); - - idpf_deinit_mac_addr(vport); -- idpf_vport_stop(vport, true); - -- if (!test_bit(IDPF_HR_RESET_IN_PROG, adapter->flags)) -+ if (!test_bit(IDPF_HR_RESET_IN_PROG, adapter->flags)) { -+ idpf_vport_stop(vport, true); - idpf_decfg_netdev(vport); -+ } - if (test_bit(IDPF_REMOVE_IN_PROG, adapter->flags)) - idpf_del_all_mac_filters(vport); - -@@ -1523,7 +1583,6 @@ void idpf_init_task(struct work_struct *work) - struct idpf_vport_config *vport_config; - struct idpf_vport_max_q max_q; - struct idpf_adapter *adapter; -- struct idpf_netdev_priv *np; - struct idpf_vport *vport; - u16 num_default_vports; - struct pci_dev *pdev; -@@ -1581,12 +1640,6 @@ void idpf_init_task(struct work_struct *work) - if (err) - goto handle_err; - -- /* Once state is put into DOWN, driver is ready for dev_open */ -- np = netdev_priv(vport->netdev); -- np->state = __IDPF_VPORT_DOWN; -- if (test_and_clear_bit(IDPF_VPORT_UP_REQUESTED, vport_config->flags)) -- idpf_vport_open(vport, true); -- - /* Spawn and return 'idpf_init_task' work queue until all the - * default vports are created - */ -@@ -1767,27 +1820,6 @@ static int idpf_check_reset_complete(struct idpf_hw *hw, - return -EBUSY; - } - --/** -- * idpf_set_vport_state - Set the vport state to be after the reset -- * @adapter: Driver specific private structure -- */ --static void idpf_set_vport_state(struct idpf_adapter *adapter) --{ -- u16 i; -- -- for (i = 0; i < adapter->max_vports; i++) { -- struct idpf_netdev_priv *np; -- -- if (!adapter->netdevs[i]) -- continue; -- -- np = netdev_priv(adapter->netdevs[i]); -- if (np->state == __IDPF_VPORT_UP) -- set_bit(IDPF_VPORT_UP_REQUESTED, -- adapter->vport_config[i]->flags); -- } --} -- - /** - * idpf_init_hard_reset - Initiate a hardware reset - * @adapter: Driver specific private structure -@@ -1796,28 +1828,17 @@ static void idpf_set_vport_state(struct idpf_adapter *adapter) - * reallocate. Also reinitialize the mailbox. Return 0 on success, - * negative on failure. - */ --static int idpf_init_hard_reset(struct idpf_adapter *adapter) -+static void idpf_init_hard_reset(struct idpf_adapter *adapter) - { - struct idpf_reg_ops *reg_ops = &adapter->dev_ops.reg_ops; - struct device *dev = &adapter->pdev->dev; -- struct net_device *netdev; - int err; -- u16 i; - -+ idpf_detach_and_close(adapter); - mutex_lock(&adapter->vport_ctrl_lock); - - dev_info(dev, "Device HW Reset initiated\n"); - -- /* Avoid TX hangs on reset */ -- for (i = 0; i < adapter->max_vports; i++) { -- netdev = adapter->netdevs[i]; -- if (!netdev) -- continue; -- -- netif_carrier_off(netdev); -- netif_tx_disable(netdev); -- } -- - /* Prepare for reset */ - if (test_and_clear_bit(IDPF_HR_DRV_LOAD, adapter->flags)) { - reg_ops->trigger_reset(adapter, IDPF_HR_DRV_LOAD); -@@ -1826,7 +1847,6 @@ static int idpf_init_hard_reset(struct idpf_adapter *adapter) - - idpf_idc_issue_reset_event(adapter->cdev_info); - -- idpf_set_vport_state(adapter); - idpf_vc_core_deinit(adapter); - if (!is_reset) - reg_ops->trigger_reset(adapter, IDPF_HR_FUNC_RESET); -@@ -1873,11 +1893,14 @@ static int idpf_init_hard_reset(struct idpf_adapter *adapter) - unlock_mutex: - mutex_unlock(&adapter->vport_ctrl_lock); - -- /* Wait until all vports are created to init RDMA CORE AUX */ -- if (!err) -- err = idpf_idc_init(adapter); -- -- return err; -+ /* Attempt to restore netdevs and initialize RDMA CORE AUX device, -+ * provided vc_core_init succeeded. It is still possible that -+ * vports are not allocated at this point if the init task failed. -+ */ -+ if (!err) { -+ idpf_attach_and_open(adapter); -+ idpf_idc_init(adapter); -+ } - } - - /** --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-22985-1.patch b/SPECS/kernel-rt/CVE-2026-22985-1.patch deleted file mode 100644 index 7e973d18a..000000000 --- a/SPECS/kernel-rt/CVE-2026-22985-1.patch +++ /dev/null @@ -1,383 +0,0 @@ -From 636d193e216844e1987a1f2a6b4bfd973c2f3661 Mon Sep 17 00:00:00 2001 -From: Sreedevi Joshi -Date: Mon, 24 Nov 2025 12:47:48 -0600 -Subject: [PATCH 37/38] idpf: Fix RSS LUT NULL pointer crash on early ethtool - operations - -The RSS LUT is not initialized until the interface comes up, causing -the following NULL pointer crash when ethtool operations like rxhash on/off -are performed before the interface is brought up for the first time. - -Move RSS LUT initialization from ndo_open to vport creation to ensure LUT -is always available. This enables RSS configuration via ethtool before -bringing the interface up. Simplify LUT management by maintaining all -changes in the driver's soft copy and programming zeros to the indirection -table when rxhash is disabled. Defer HW programming until the interface -comes up if it is down during rxhash and LUT configuration changes. - -Steps to reproduce: -** Load idpf driver; interfaces will be created - modprobe idpf -** Before bringing the interfaces up, turn rxhash off - ethtool -K eth2 rxhash off - -[89408.371875] BUG: kernel NULL pointer dereference, address: 0000000000000000 -[89408.371908] #PF: supervisor read access in kernel mode -[89408.371924] #PF: error_code(0x0000) - not-present page -[89408.371940] PGD 0 P4D 0 -[89408.371953] Oops: Oops: 0000 [#1] SMP NOPTI - -[89408.372052] RIP: 0010:memcpy_orig+0x16/0x130 -[89408.372310] Call Trace: -[89408.372317] -[89408.372326] ? idpf_set_features+0xfc/0x180 [idpf] -[89408.372363] __netdev_update_features+0x295/0xde0 -[89408.372384] ethnl_set_features+0x15e/0x460 -[89408.372406] genl_family_rcv_msg_doit+0x11f/0x180 -[89408.372429] genl_rcv_msg+0x1ad/0x2b0 -[89408.372446] ? __pfx_ethnl_set_features+0x10/0x10 -[89408.372465] ? __pfx_genl_rcv_msg+0x10/0x10 -[89408.372482] netlink_rcv_skb+0x58/0x100 -[89408.372502] genl_rcv+0x2c/0x50 -[89408.372516] netlink_unicast+0x289/0x3e0 -[89408.372533] netlink_sendmsg+0x215/0x440 -[89408.372551] __sys_sendto+0x234/0x240 -[89408.372571] __x64_sys_sendto+0x28/0x30 -[89408.372585] x64_sys_call+0x1909/0x1da0 -[89408.372604] do_syscall_64+0x7a/0xfa0 -[89408.373140] ? clear_bhb_loop+0x60/0xb0 -[89408.373647] entry_SYSCALL_64_after_hwframe+0x76/0x7e -[89408.378887] - - -Fixes: a251eee62133 ("idpf: add SRIOV support and other ndo_ops") -Signed-off-by: Sreedevi Joshi -Reviewed-by: Sridhar Samudrala -Reviewed-by: Emil Tantilov -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Paul Menzel -Reviewed-by: Simon Horman -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen ---- - drivers/net/ethernet/intel/idpf/idpf.h | 2 - - drivers/net/ethernet/intel/idpf/idpf_lib.c | 87 +++++++++---------- - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 36 +++----- - drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 +- - .../net/ethernet/intel/idpf/idpf_virtchnl.c | 9 +- - 5 files changed, 64 insertions(+), 74 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h -index aafbb280c2e7..4fcf2be878e5 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf.h -+++ b/drivers/net/ethernet/intel/idpf/idpf.h -@@ -397,14 +397,12 @@ enum idpf_user_flags { - * @rss_key: RSS hash key - * @rss_lut_size: Size of RSS lookup table - * @rss_lut: RSS lookup table -- * @cached_lut: Used to restore previously init RSS lut - */ - struct idpf_rss_data { - u16 rss_key_size; - u8 *rss_key; - u16 rss_lut_size; - u32 *rss_lut; -- u32 *cached_lut; - }; - - /** -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index 99c7ffe118e5..2021dd6059af 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -1045,7 +1045,7 @@ static void idpf_vport_rel(struct idpf_vport *vport) - u16 idx = vport->idx; - - vport_config = adapter->vport_config[vport->idx]; -- idpf_deinit_rss(vport); -+ idpf_deinit_rss_lut(vport); - rss_data = &vport_config->user_config.rss_data; - kfree(rss_data->rss_key); - rss_data->rss_key = NULL; -@@ -1194,6 +1194,7 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - u16 idx = adapter->next_vport; - struct idpf_vport *vport; - u16 num_max_q; -+ int err; - - if (idx == IDPF_NO_FREE_SLOT) - return NULL; -@@ -1244,10 +1245,11 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - - idpf_vport_init(vport, max_q); - -- /* This alloc is done separate from the LUT because it's not strictly -- * dependent on how many queues we have. If we change number of queues -- * and soft reset we'll need a new LUT but the key can remain the same -- * for as long as the vport exists. -+ /* LUT and key are both initialized here. Key is not strictly dependent -+ * on how many queues we have. If we change number of queues and soft -+ * reset is initiated, LUT will be freed and a new LUT will be allocated -+ * as per the updated number of queues during vport bringup. However, -+ * the key remains the same for as long as the vport exists. - */ - rss_data = &adapter->vport_config[idx]->user_config.rss_data; - rss_data->rss_key = kzalloc(rss_data->rss_key_size, GFP_KERNEL); -@@ -1257,6 +1259,11 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - /* Initialize default rss key */ - netdev_rss_key_fill((void *)rss_data->rss_key, rss_data->rss_key_size); - -+ /* Initialize default rss LUT */ -+ err = idpf_init_rss_lut(vport); -+ if (err) -+ goto free_rss_key; -+ - /* fill vport slot in the adapter struct */ - adapter->vports[idx] = vport; - adapter->vport_ids[idx] = idpf_get_vport_id(vport); -@@ -1267,6 +1274,8 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - - return vport; - -+free_rss_key: -+ kfree(rss_data->rss_key); - free_vector_idxs: - kfree(vport->q_vector_idxs); - free_vport: -@@ -1527,9 +1536,22 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - - idpf_restore_features(vport); - -+ vport_config = adapter->vport_config[vport->idx]; -+ rss_data = &vport_config->user_config.rss_data; -+ -+ if (!rss_data->rss_lut) { -+ err = idpf_init_rss_lut(vport); -+ if (err) { -+ dev_err(&adapter->pdev->dev, -+ "Failed to initialize RSS LUT for vport %u: %d\n", -+ vport->vport_id, err); -+ goto disable_vport; -+ } -+ } -+ - err = idpf_config_rss(vport); - if (err) { -- dev_err(&adapter->pdev->dev, "Failed to initialize RSS for vport %u: %d\n", -+ dev_err(&adapter->pdev->dev, "Failed to configure RSS for vport %u: %d\n", - vport->vport_id, err); - goto disable_vport; - } -@@ -1538,7 +1560,7 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - if (err) { - dev_err(&adapter->pdev->dev, "Failed to complete interface up for vport %u: %d\n", - vport->vport_id, err); -- goto deinit_rss; -+ goto disable_vport; - } - - if (rtnl) -@@ -1546,8 +1568,6 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - - return 0; - --deinit_rss: -- idpf_deinit_rss(vport); - disable_vport: - idpf_send_disable_vport_msg(vport); - disable_queues: -@@ -2172,40 +2192,6 @@ static void idpf_set_rx_mode(struct net_device *netdev) - dev_err(dev, "Failed to set promiscuous mode: %d\n", err); - } - --/** -- * idpf_vport_manage_rss_lut - disable/enable RSS -- * @vport: the vport being changed -- * -- * In the event of disable request for RSS, this function will zero out RSS -- * LUT, while in the event of enable request for RSS, it will reconfigure RSS -- * LUT with the default LUT configuration. -- */ --static int idpf_vport_manage_rss_lut(struct idpf_vport *vport) --{ -- bool ena = idpf_is_feature_ena(vport, NETIF_F_RXHASH); -- struct idpf_rss_data *rss_data; -- u16 idx = vport->idx; -- int lut_size; -- -- rss_data = &vport->adapter->vport_config[idx]->user_config.rss_data; -- lut_size = rss_data->rss_lut_size * sizeof(u32); -- -- if (ena) { -- /* This will contain the default or user configured LUT */ -- memcpy(rss_data->rss_lut, rss_data->cached_lut, lut_size); -- } else { -- /* Save a copy of the current LUT to be restored later if -- * requested. -- */ -- memcpy(rss_data->cached_lut, rss_data->rss_lut, lut_size); -- -- /* Zero out the current LUT to disable */ -- memset(rss_data->rss_lut, 0, lut_size); -- } -- -- return idpf_config_rss(vport); --} -- - /** - * idpf_set_features - set the netdev feature flags - * @netdev: ptr to the netdev being adjusted -@@ -2231,10 +2217,19 @@ static int idpf_set_features(struct net_device *netdev, - } - - if (changed & NETIF_F_RXHASH) { -+ struct idpf_netdev_priv *np = netdev_priv(netdev); -+ - netdev->features ^= NETIF_F_RXHASH; -- err = idpf_vport_manage_rss_lut(vport); -- if (err) -- goto unlock_mutex; -+ -+ /* If the interface is not up when changing the rxhash, update -+ * to the HW is skipped. The updated LUT will be committed to -+ * the HW when the interface is brought up. -+ */ -+ if (np->state == __IDPF_VPORT_UP) { -+ err = idpf_config_rss(vport); -+ if (err) -+ goto unlock_mutex; -+ } - } - - if (changed & NETIF_F_GRO_HW) { -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -index a11ec2b8c5d1..1f61d2107e56 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -@@ -4232,57 +4232,47 @@ void idpf_fill_dflt_rss_lut(struct idpf_vport *vport) - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; - -- for (i = 0; i < rss_data->rss_lut_size; i++) { -+ for (i = 0; i < rss_data->rss_lut_size; i++) - rss_data->rss_lut[i] = i % num_active_rxq; -- rss_data->cached_lut[i] = rss_data->rss_lut[i]; -- } - } - - /** -- * idpf_init_rss - Allocate and initialize RSS resources -+ * idpf_init_rss_lut - Allocate and initialize RSS LUT - * @vport: virtual port - * -- * Return 0 on success, negative on failure -+ * Return: 0 on success, negative on failure - */ --int idpf_init_rss(struct idpf_vport *vport) -+int idpf_init_rss_lut(struct idpf_vport *vport) - { - struct idpf_adapter *adapter = vport->adapter; - struct idpf_rss_data *rss_data; -- u32 lut_size; - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -+ if (!rss_data->rss_lut) { -+ u32 lut_size; - -- lut_size = rss_data->rss_lut_size * sizeof(u32); -- rss_data->rss_lut = kzalloc(lut_size, GFP_KERNEL); -- if (!rss_data->rss_lut) -- return -ENOMEM; -- -- rss_data->cached_lut = kzalloc(lut_size, GFP_KERNEL); -- if (!rss_data->cached_lut) { -- kfree(rss_data->rss_lut); -- rss_data->rss_lut = NULL; -- -- return -ENOMEM; -+ lut_size = rss_data->rss_lut_size * sizeof(u32); -+ rss_data->rss_lut = kzalloc(lut_size, GFP_KERNEL); -+ if (!rss_data->rss_lut) -+ return -ENOMEM; - } - - /* Fill the default RSS lut values */ - idpf_fill_dflt_rss_lut(vport); - -- return idpf_config_rss(vport); -+ return 0; - } - - /** -- * idpf_deinit_rss - Release RSS resources -+ * idpf_deinit_rss_lut - Release RSS LUT - * @vport: virtual port - */ --void idpf_deinit_rss(struct idpf_vport *vport) -+void idpf_deinit_rss_lut(struct idpf_vport *vport) - { - struct idpf_adapter *adapter = vport->adapter; - struct idpf_rss_data *rss_data; - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -- kfree(rss_data->cached_lut); -- rss_data->cached_lut = NULL; - kfree(rss_data->rss_lut); - rss_data->rss_lut = NULL; - } -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -index a28cfb792bca..932209427df0 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -@@ -1010,8 +1010,8 @@ int idpf_vport_intr_init(struct idpf_vport *vport); - void idpf_vport_intr_ena(struct idpf_vport *vport); - void idpf_fill_dflt_rss_lut(struct idpf_vport *vport); - int idpf_config_rss(struct idpf_vport *vport); --int idpf_init_rss(struct idpf_vport *vport); --void idpf_deinit_rss(struct idpf_vport *vport); -+int idpf_init_rss_lut(struct idpf_vport *vport); -+void idpf_deinit_rss_lut(struct idpf_vport *vport); - int idpf_rx_bufs_init_all(struct idpf_vport *vport); - void idpf_rx_add_frag(struct idpf_rx_buf *rx_buf, struct sk_buff *skb, - unsigned int size); -diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -index c1f34381333d..9c7efee04b45 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -@@ -2476,6 +2476,10 @@ int idpf_send_get_stats_msg(struct idpf_vport *vport) - * @vport: virtual port data structure - * @get: flag to set or get rss look up table - * -+ * When rxhash is disabled, RSS LUT will be configured with zeros. If rxhash -+ * is enabled, the LUT values stored in driver's soft copy will be used to setup -+ * the HW. -+ * - * Returns 0 on success, negative on failure. - */ - int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get) -@@ -2486,10 +2490,12 @@ int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get) - struct idpf_rss_data *rss_data; - int buf_size, lut_buf_size; - ssize_t reply_sz; -+ bool rxhash_ena; - int i; - - rss_data = - &vport->adapter->vport_config[vport->idx]->user_config.rss_data; -+ rxhash_ena = idpf_is_feature_ena(vport, NETIF_F_RXHASH); - buf_size = struct_size(rl, lut, rss_data->rss_lut_size); - rl = kzalloc(buf_size, GFP_KERNEL); - if (!rl) -@@ -2511,7 +2517,8 @@ int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get) - } else { - rl->lut_entries = cpu_to_le16(rss_data->rss_lut_size); - for (i = 0; i < rss_data->rss_lut_size; i++) -- rl->lut[i] = cpu_to_le32(rss_data->rss_lut[i]); -+ rl->lut[i] = rxhash_ena ? -+ cpu_to_le32(rss_data->rss_lut[i]) : 0; - - xn_params.vc_op = VIRTCHNL2_OP_SET_RSS_LUT; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-22985-2.patch b/SPECS/kernel-rt/CVE-2026-22985-2.patch deleted file mode 100644 index 2b11bea56..000000000 --- a/SPECS/kernel-rt/CVE-2026-22985-2.patch +++ /dev/null @@ -1,269 +0,0 @@ -From e5d21c479823dd370a76d5bd95e0fe27c423d783 Mon Sep 17 00:00:00 2001 -From: Emil Tantilov -Date: Tue, 25 Nov 2025 14:36:24 -0800 -Subject: [PATCH 38/38] idpf: convert vport state to bitmap - -Convert vport state to a bitmap and remove the DOWN state which is -redundant in the existing logic. There are no functional changes aside -from the use of bitwise operations when setting and checking the states. -Removed the double underscore to be consistent with the naming of other -bitmaps in the header and renamed current_state to vport_is_up to match -the meaning of the new variable. - -Reviewed-by: Przemek Kitszel -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Chittim Madhu -Signed-off-by: Emil Tantilov -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen -Link: https://patch.msgid.link/20251125223632.1857532-6-anthony.l.nguyen@intel.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/ethernet/intel/idpf/idpf.h | 12 ++++----- - .../net/ethernet/intel/idpf/idpf_ethtool.c | 10 ++++---- - drivers/net/ethernet/intel/idpf/idpf_lib.c | 25 ++++++++++--------- - .../ethernet/intel/idpf/idpf_singleq_txrx.c | 2 +- - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 2 +- - .../net/ethernet/intel/idpf/idpf_virtchnl.c | 4 +-- - 6 files changed, 27 insertions(+), 28 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h -index 4fcf2be878e5..49e0f5b49830 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf.h -+++ b/drivers/net/ethernet/intel/idpf/idpf.h -@@ -130,14 +130,12 @@ enum idpf_cap_field { - - /** - * enum idpf_vport_state - Current vport state -- * @__IDPF_VPORT_DOWN: Vport is down -- * @__IDPF_VPORT_UP: Vport is up -- * @__IDPF_VPORT_STATE_LAST: Must be last, number of states -+ * @IDPF_VPORT_UP: Vport is up -+ * @IDPF_VPORT_STATE_NBITS: Must be last, number of states - */ - enum idpf_vport_state { -- __IDPF_VPORT_DOWN, -- __IDPF_VPORT_UP, -- __IDPF_VPORT_STATE_LAST, -+ IDPF_VPORT_UP, -+ IDPF_VPORT_STATE_NBITS - }; - - /** -@@ -161,7 +159,7 @@ struct idpf_netdev_priv { - u16 vport_idx; - u16 max_tx_hdr_size; - u16 tx_max_bufs; -- enum idpf_vport_state state; -+ DECLARE_BITMAP(state, IDPF_VPORT_STATE_NBITS); - struct rtnl_link_stats64 netstats; - spinlock_t stats_lock; - }; -diff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c -index 0eb812ac19c2..f84e247399a7 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c -@@ -386,7 +386,7 @@ static int idpf_get_rxfh(struct net_device *netdev, - } - - rss_data = &adapter->vport_config[np->vport_idx]->user_config.rss_data; -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - rxfh->hfunc = ETH_RSS_HASH_TOP; -@@ -436,7 +436,7 @@ static int idpf_set_rxfh(struct net_device *netdev, - } - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && -@@ -1167,7 +1167,7 @@ static void idpf_get_ethtool_stats(struct net_device *netdev, - idpf_vport_ctrl_lock(netdev); - vport = idpf_netdev_to_vport(netdev); - -- if (np->state != __IDPF_VPORT_UP) { -+ if (!test_bit(IDPF_VPORT_UP, np->state)) { - idpf_vport_ctrl_unlock(netdev); - - return; -@@ -1319,7 +1319,7 @@ static int idpf_get_q_coalesce(struct net_device *netdev, - idpf_vport_ctrl_lock(netdev); - vport = idpf_netdev_to_vport(netdev); - -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - if (q_num >= vport->num_rxq && q_num >= vport->num_txq) { -@@ -1507,7 +1507,7 @@ static int idpf_set_coalesce(struct net_device *netdev, - idpf_vport_ctrl_lock(netdev); - vport = idpf_netdev_to_vport(netdev); - -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - for (i = 0; i < vport->num_txq; i++) { -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index 2021dd6059af..f440446aa615 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -517,7 +517,7 @@ static int idpf_del_mac_filter(struct idpf_vport *vport, - } - spin_unlock_bh(&vport_config->mac_filter_list_lock); - -- if (np->state == __IDPF_VPORT_UP) { -+ if (test_bit(IDPF_VPORT_UP, np->state)) { - int err; - - err = idpf_add_del_mac_filters(vport, np, false, async); -@@ -588,7 +588,7 @@ static int idpf_add_mac_filter(struct idpf_vport *vport, - if (err) - return err; - -- if (np->state == __IDPF_VPORT_UP) -+ if (test_bit(IDPF_VPORT_UP, np->state)) - err = idpf_add_del_mac_filters(vport, np, true, async); - - return err; -@@ -949,7 +949,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); - -- if (np->state <= __IDPF_VPORT_DOWN) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - return; - - if (rtnl) -@@ -975,7 +975,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl) - idpf_vport_intr_deinit(vport); - idpf_vport_queues_rel(vport); - idpf_vport_intr_rel(vport); -- np->state = __IDPF_VPORT_DOWN; -+ clear_bit(IDPF_VPORT_UP, np->state); - - if (rtnl) - rtnl_unlock(); -@@ -1409,7 +1409,7 @@ static int idpf_up_complete(struct idpf_vport *vport) - netif_tx_start_all_queues(vport->netdev); - } - -- np->state = __IDPF_VPORT_UP; -+ set_bit(IDPF_VPORT_UP, np->state); - - return 0; - } -@@ -1452,9 +1452,11 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); - struct idpf_adapter *adapter = vport->adapter; -+ struct idpf_vport_config *vport_config; -+ struct idpf_rss_data *rss_data; - int err; - -- if (np->state != __IDPF_VPORT_DOWN) -+ if (test_bit(IDPF_VPORT_UP, np->state)) - return -EBUSY; - - if (rtnl) -@@ -1963,7 +1965,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - enum idpf_vport_reset_cause reset_cause) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); -- enum idpf_vport_state current_state = np->state; -+ bool vport_is_up = test_bit(IDPF_VPORT_UP, np->state); - struct idpf_adapter *adapter = vport->adapter; - struct idpf_vport *new_vport; - int err; -@@ -2014,7 +2016,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - goto free_vport; - } - -- if (current_state <= __IDPF_VPORT_DOWN) { -+ if (!vport_is_up) { - idpf_send_delete_queues_msg(vport); - } else { - set_bit(IDPF_VPORT_DEL_QUEUES, vport->flags); -@@ -2050,7 +2052,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - !netif_is_rxfh_configured(vport->netdev)) - idpf_fill_dflt_rss_lut(vport); - -- if (current_state == __IDPF_VPORT_UP) -+ if (vport_is_up) - err = idpf_vport_open(vport, false); - - goto free_vport; -@@ -2060,8 +2062,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - vport->num_rxq, vport->num_bufq); - - err_open: -- if (current_state == __IDPF_VPORT_UP) -- idpf_vport_open(vport, false); -+ if (vport_is_up) - - free_vport: - kfree(new_vport); -@@ -2225,7 +2226,7 @@ static int idpf_set_features(struct net_device *netdev, - * to the HW is skipped. The updated LUT will be committed to - * the HW when the interface is brought up. - */ -- if (np->state == __IDPF_VPORT_UP) { -+ if (test_bit(IDPF_VPORT_UP, np->state)) { - err = idpf_config_rss(vport); - if (err) - goto unlock_mutex; -diff --git a/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c -index b19b462e0bb6..06199fde3b57 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c -@@ -571,7 +571,7 @@ static bool idpf_tx_singleq_clean(struct idpf_tx_queue *tx_q, int napi_budget, - np = netdev_priv(tx_q->netdev); - nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx); - -- dont_wake = np->state != __IDPF_VPORT_UP || -+ dont_wake = !test_bit(IDPF_VPORT_UP, np->state) || - !netif_carrier_ok(tx_q->netdev); - __netif_txq_completed_wake(nq, ss.packets, ss.bytes, - IDPF_DESC_UNUSED(tx_q), IDPF_TX_WAKE_THRESH, -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -index 1f61d2107e56..50a6f59fb1ee 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -@@ -1889,7 +1889,7 @@ static bool idpf_tx_clean_complq(struct idpf_compl_queue *complq, int budget, - /* Update BQL */ - nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx); - -- dont_wake = !complq_ok || np->state != __IDPF_VPORT_UP || -+ dont_wake = !complq_ok || !test_bit(IDPF_VPORT_UP, np->state) || - !netif_carrier_ok(tx_q->netdev); - /* Check if the TXQ needs to and can be restarted */ - __netif_txq_completed_wake(nq, tx_q->cleaned_pkts, tx_q->cleaned_bytes, -diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -index 9c7efee04b45..96ee48860cbf 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -@@ -68,7 +68,7 @@ static void idpf_handle_event_link(struct idpf_adapter *adapter, - - vport->link_up = v2e->link_status; - -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - return; - - if (vport->link_up) { -@@ -2432,7 +2432,7 @@ int idpf_send_get_stats_msg(struct idpf_vport *vport) - - - /* Don't send get_stats message if the link is down */ -- if (np->state <= __IDPF_VPORT_DOWN) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - return 0; - - stats_msg.vport_id = cpu_to_le32(vport->vport_id); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-22987.patch b/SPECS/kernel-rt/CVE-2026-22987.patch deleted file mode 100644 index c410d7e88..000000000 --- a/SPECS/kernel-rt/CVE-2026-22987.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 4797daed31f7dc17a7e067572833f33b6c700b9e Mon Sep 17 00:00:00 2001 -From: Shivani Gupta -Date: Mon, 5 Jan 2026 00:59:05 +0000 -Subject: [PATCH 06/38] net/sched: act_api: avoid dereferencing ERR_PTR in - tcf_idrinfo_destroy - -syzbot reported a crash in tc_act_in_hw() during netns teardown where -tcf_idrinfo_destroy() passed an ERR_PTR(-EBUSY) value as a tc_action -pointer, leading to an invalid dereference. - -Guard against ERR_PTR entries when iterating the action IDR so teardown -does not call tc_act_in_hw() on an error pointer. - -Fixes: 84a7d6797e6a ("net/sched: acp_api: no longer acquire RTNL in tc_action_net_exit()") -Link: https://syzkaller.appspot.com/bug?extid=8f1c492ffa4644ff3826 -Reported-by: syzbot+8f1c492ffa4644ff3826@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=8f1c492ffa4644ff3826 -Signed-off-by: Shivani Gupta -Link: https://patch.msgid.link/20260105005905.243423-1-shivani07g@gmail.com -Signed-off-by: Jakub Kicinski ---- - net/sched/act_api.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/net/sched/act_api.c b/net/sched/act_api.c -index 9e468e463467..7fab81f223a5 100644 ---- a/net/sched/act_api.c -+++ b/net/sched/act_api.c -@@ -940,6 +940,8 @@ void tcf_idrinfo_destroy(const struct tc_action_ops *ops, - int ret; - - idr_for_each_entry_ul(idr, p, tmp, id) { -+ if (IS_ERR(p)) -+ continue; - if (tc_act_in_hw(p) && !mutex_taken) { - rtnl_lock(); - mutex_taken = true; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-22993.patch b/SPECS/kernel-rt/CVE-2026-22993.patch deleted file mode 100644 index 1754ebda9..000000000 --- a/SPECS/kernel-rt/CVE-2026-22993.patch +++ /dev/null @@ -1,102 +0,0 @@ -From a3a2302e6b913bf599866428c7ec7d7a5e38e32c Mon Sep 17 00:00:00 2001 -From: "LIOU, Mei Fan" -Date: Wed, 28 Jan 2026 07:47:07 -0800 -Subject: [PATCH 05/38] idpf: Fix RSS LUT NULL ptr issue after soft reset - -During soft reset, the RSS LUT is freed and not restored unless the -interface is up. If an ethtool command that accesses the rss lut is -attempted immediately after reset, it will result in NULL ptr -dereference. Also, there is no need to reset the rss lut if the soft reset -does not involve queue count change. - -After soft reset, set the RSS LUT to default values based on the updated -queue count only if the reset was a result of a queue count change and -the LUT was not configured by the user. In all other cases, don't touch -the LUT. - -Fixes: 02cbfba1add5 ("idpf: add ethtool callbacks") -Signed-off-by: Sreedevi Joshi -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Sridhar Samudrala -Reviewed-by: Emil Tantilov -Reviewed-by: Simon Horman -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen ---- - drivers/net/ethernet/intel/idpf/idpf_lib.c | 12 +++++------- - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 2 +- - drivers/net/ethernet/intel/idpf/idpf_txrx.h | 1 + - 3 files changed, 7 insertions(+), 8 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index f4b89d222610..7e22790aa9a8 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -1383,7 +1383,6 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); - struct idpf_adapter *adapter = vport->adapter; -- struct idpf_vport_config *vport_config; - int err; - - if (np->state != __IDPF_VPORT_DOWN) -@@ -1468,11 +1467,7 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - - idpf_restore_features(vport); - -- vport_config = adapter->vport_config[vport->idx]; -- if (vport_config->user_config.rss_data.rss_lut) -- err = idpf_config_rss(vport); -- else -- err = idpf_init_rss(vport); -+ err = idpf_config_rss(vport); - if (err) { - dev_err(&adapter->pdev->dev, "Failed to initialize RSS for vport %u: %d\n", - vport->vport_id, err); -@@ -1983,7 +1978,6 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - idpf_vport_stop(vport, false); - } - -- idpf_deinit_rss(vport); - /* We're passing in vport here because we need its wait_queue - * to send a message and it should be getting all the vport - * config data out of the adapter but we need to be careful not -@@ -2009,6 +2003,10 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - if (err) - goto err_open; - -+ if (reset_cause == IDPF_SR_Q_CHANGE && -+ !netif_is_rxfh_configured(vport->netdev)) -+ idpf_fill_dflt_rss_lut(vport); -+ - if (current_state == __IDPF_VPORT_UP) - err = idpf_vport_open(vport, false); - -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -index 92634c4bb369..a11ec2b8c5d1 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -@@ -4223,7 +4223,7 @@ int idpf_config_rss(struct idpf_vport *vport) - * idpf_fill_dflt_rss_lut - Fill the indirection table with the default values - * @vport: virtual port structure - */ --static void idpf_fill_dflt_rss_lut(struct idpf_vport *vport) -+void idpf_fill_dflt_rss_lut(struct idpf_vport *vport) - { - struct idpf_adapter *adapter = vport->adapter; - u16 num_active_rxq = vport->num_rxq; -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -index 52753dff381c..a28cfb792bca 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -@@ -1008,6 +1008,7 @@ void idpf_vport_intr_update_itr_ena_irq(struct idpf_q_vector *q_vector); - void idpf_vport_intr_deinit(struct idpf_vport *vport); - int idpf_vport_intr_init(struct idpf_vport *vport); - void idpf_vport_intr_ena(struct idpf_vport *vport); -+void idpf_fill_dflt_rss_lut(struct idpf_vport *vport); - int idpf_config_rss(struct idpf_vport *vport); - int idpf_init_rss(struct idpf_vport *vport); - void idpf_deinit_rss(struct idpf_vport *vport); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-23004.patch b/SPECS/kernel-rt/CVE-2026-23004.patch deleted file mode 100644 index 4e4a70f7e..000000000 --- a/SPECS/kernel-rt/CVE-2026-23004.patch +++ /dev/null @@ -1,264 +0,0 @@ -From fe885b689ab38fd4005c572f48f9eafb82c9a689 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Mon, 12 Jan 2026 10:38:25 +0000 -Subject: [PATCH 35/38] dst: fix races in rt6_uncached_list_del() and - rt_del_uncached_list() - -syzbot was able to crash the kernel in rt6_uncached_list_flush_dev() -in an interesting way [1] - -Crash happens in list_del_init()/INIT_LIST_HEAD() while writing -list->prev, while the prior write on list->next went well. - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - WRITE_ONCE(list->next, list); // This went well - WRITE_ONCE(list->prev, list); // Crash, @list has been freed. -} - -Issue here is that rt6_uncached_list_del() did not attempt to lock -ul->lock, as list_empty(&rt->dst.rt_uncached) returned -true because the WRITE_ONCE(list->next, list) happened on the other CPU. - -We might use list_del_init_careful() and list_empty_careful(), -or make sure rt6_uncached_list_del() always grabs the spinlock -whenever rt->dst.rt_uncached_list has been set. - -A similar fix is neeed for IPv4. - -[1] - - BUG: KASAN: slab-use-after-free in INIT_LIST_HEAD include/linux/list.h:46 [inline] - BUG: KASAN: slab-use-after-free in list_del_init include/linux/list.h:296 [inline] - BUG: KASAN: slab-use-after-free in rt6_uncached_list_flush_dev net/ipv6/route.c:191 [inline] - BUG: KASAN: slab-use-after-free in rt6_disable_ip+0x633/0x730 net/ipv6/route.c:5020 -Write of size 8 at addr ffff8880294cfa78 by task kworker/u8:14/3450 - -CPU: 0 UID: 0 PID: 3450 Comm: kworker/u8:14 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)} -Tainted: [L]=SOFTLOCKUP -Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 -Workqueue: netns cleanup_net -Call Trace: - - dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 - print_address_description mm/kasan/report.c:378 [inline] - print_report+0xca/0x240 mm/kasan/report.c:482 - kasan_report+0x118/0x150 mm/kasan/report.c:595 - INIT_LIST_HEAD include/linux/list.h:46 [inline] - list_del_init include/linux/list.h:296 [inline] - rt6_uncached_list_flush_dev net/ipv6/route.c:191 [inline] - rt6_disable_ip+0x633/0x730 net/ipv6/route.c:5020 - addrconf_ifdown+0x143/0x18a0 net/ipv6/addrconf.c:3853 - addrconf_notify+0x1bc/0x1050 net/ipv6/addrconf.c:-1 - notifier_call_chain+0x19d/0x3a0 kernel/notifier.c:85 - call_netdevice_notifiers_extack net/core/dev.c:2268 [inline] - call_netdevice_notifiers net/core/dev.c:2282 [inline] - netif_close_many+0x29c/0x410 net/core/dev.c:1785 - unregister_netdevice_many_notify+0xb50/0x2330 net/core/dev.c:12353 - ops_exit_rtnl_list net/core/net_namespace.c:187 [inline] - ops_undo_list+0x3dc/0x990 net/core/net_namespace.c:248 - cleanup_net+0x4de/0x7b0 net/core/net_namespace.c:696 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - - -Allocated by task 803: - kasan_save_stack mm/kasan/common.c:57 [inline] - kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 - unpoison_slab_object mm/kasan/common.c:340 [inline] - __kasan_slab_alloc+0x6c/0x80 mm/kasan/common.c:366 - kasan_slab_alloc include/linux/kasan.h:253 [inline] - slab_post_alloc_hook mm/slub.c:4953 [inline] - slab_alloc_node mm/slub.c:5263 [inline] - kmem_cache_alloc_noprof+0x18d/0x6c0 mm/slub.c:5270 - dst_alloc+0x105/0x170 net/core/dst.c:89 - ip6_dst_alloc net/ipv6/route.c:342 [inline] - icmp6_dst_alloc+0x75/0x460 net/ipv6/route.c:3333 - mld_sendpack+0x683/0xe60 net/ipv6/mcast.c:1844 - mld_send_cr net/ipv6/mcast.c:2154 [inline] - mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - -Freed by task 20: - kasan_save_stack mm/kasan/common.c:57 [inline] - kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 - kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 - poison_slab_object mm/kasan/common.c:253 [inline] - __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 - kasan_slab_free include/linux/kasan.h:235 [inline] - slab_free_hook mm/slub.c:2540 [inline] - slab_free mm/slub.c:6670 [inline] - kmem_cache_free+0x18f/0x8d0 mm/slub.c:6781 - dst_destroy+0x235/0x350 net/core/dst.c:121 - rcu_do_batch kernel/rcu/tree.c:2605 [inline] - rcu_core kernel/rcu/tree.c:2857 [inline] - rcu_cpu_kthread+0xba5/0x1af0 kernel/rcu/tree.c:2945 - smpboot_thread_fn+0x542/0xa60 kernel/smpboot.c:160 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - -Last potentially related work creation: - kasan_save_stack+0x3e/0x60 mm/kasan/common.c:57 - kasan_record_aux_stack+0xbd/0xd0 mm/kasan/generic.c:556 - __call_rcu_common kernel/rcu/tree.c:3119 [inline] - call_rcu+0xee/0x890 kernel/rcu/tree.c:3239 - refdst_drop include/net/dst.h:266 [inline] - skb_dst_drop include/net/dst.h:278 [inline] - skb_release_head_state+0x71/0x360 net/core/skbuff.c:1156 - skb_release_all net/core/skbuff.c:1180 [inline] - __kfree_skb net/core/skbuff.c:1196 [inline] - sk_skb_reason_drop+0xe9/0x170 net/core/skbuff.c:1234 - kfree_skb_reason include/linux/skbuff.h:1322 [inline] - tcf_kfree_skb_list include/net/sch_generic.h:1127 [inline] - __dev_xmit_skb net/core/dev.c:4260 [inline] - __dev_queue_xmit+0x26aa/0x3210 net/core/dev.c:4785 - NF_HOOK_COND include/linux/netfilter.h:307 [inline] - ip6_output+0x340/0x550 net/ipv6/ip6_output.c:247 - NF_HOOK+0x9e/0x380 include/linux/netfilter.h:318 - mld_sendpack+0x8d4/0xe60 net/ipv6/mcast.c:1855 - mld_send_cr net/ipv6/mcast.c:2154 [inline] - mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - -The buggy address belongs to the object at ffff8880294cfa00 - which belongs to the cache ip6_dst_cache of size 232 -The buggy address is located 120 bytes inside of - freed 232-byte region [ffff8880294cfa00, ffff8880294cfae8) - -The buggy address belongs to the physical page: -page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x294cf -memcg:ffff88803536b781 -flags: 0x80000000000000(node=0|zone=1) -page_type: f5(slab) -raw: 0080000000000000 ffff88802ff1c8c0 ffffea0000bf2bc0 dead000000000006 -raw: 0000000000000000 00000000800c000c 00000000f5000000 ffff88803536b781 -page dumped because: kasan: bad access detected -page_owner tracks the page as allocated -page last allocated via order 0, migratetype Unmovable, gfp_mask 0x52820(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP), pid 9, tgid 9 (kworker/0:0), ts 91119585830, free_ts 91088628818 - set_page_owner include/linux/page_owner.h:32 [inline] - post_alloc_hook+0x234/0x290 mm/page_alloc.c:1857 - prep_new_page mm/page_alloc.c:1865 [inline] - get_page_from_freelist+0x28c0/0x2960 mm/page_alloc.c:3915 - __alloc_frozen_pages_noprof+0x181/0x370 mm/page_alloc.c:5210 - alloc_pages_mpol+0xd1/0x380 mm/mempolicy.c:2486 - alloc_slab_page mm/slub.c:3075 [inline] - allocate_slab+0x86/0x3b0 mm/slub.c:3248 - new_slab mm/slub.c:3302 [inline] - ___slab_alloc+0xb10/0x13e0 mm/slub.c:4656 - __slab_alloc+0xc6/0x1f0 mm/slub.c:4779 - __slab_alloc_node mm/slub.c:4855 [inline] - slab_alloc_node mm/slub.c:5251 [inline] - kmem_cache_alloc_noprof+0x101/0x6c0 mm/slub.c:5270 - dst_alloc+0x105/0x170 net/core/dst.c:89 - ip6_dst_alloc net/ipv6/route.c:342 [inline] - icmp6_dst_alloc+0x75/0x460 net/ipv6/route.c:3333 - mld_sendpack+0x683/0xe60 net/ipv6/mcast.c:1844 - mld_send_cr net/ipv6/mcast.c:2154 [inline] - mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 -page last free pid 5859 tgid 5859 stack trace: - reset_page_owner include/linux/page_owner.h:25 [inline] - free_pages_prepare mm/page_alloc.c:1406 [inline] - __free_frozen_pages+0xfe1/0x1170 mm/page_alloc.c:2943 - discard_slab mm/slub.c:3346 [inline] - __put_partials+0x149/0x170 mm/slub.c:3886 - __slab_free+0x2af/0x330 mm/slub.c:5952 - qlink_free mm/kasan/quarantine.c:163 [inline] - qlist_free_all+0x97/0x100 mm/kasan/quarantine.c:179 - kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286 - __kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350 - kasan_slab_alloc include/linux/kasan.h:253 [inline] - slab_post_alloc_hook mm/slub.c:4953 [inline] - slab_alloc_node mm/slub.c:5263 [inline] - kmem_cache_alloc_noprof+0x18d/0x6c0 mm/slub.c:5270 - getname_flags+0xb8/0x540 fs/namei.c:146 - getname include/linux/fs.h:2498 [inline] - do_sys_openat2+0xbc/0x200 fs/open.c:1426 - do_sys_open fs/open.c:1436 [inline] - __do_sys_openat fs/open.c:1452 [inline] - __se_sys_openat fs/open.c:1447 [inline] - __x64_sys_openat+0x138/0x170 fs/open.c:1447 - do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] - do_syscall_64+0xec/0xf80 arch/x86/entry/syscall_64.c:94 - -Fixes: 8d0b94afdca8 ("ipv6: Keep track of DST_NOCACHE routes in case of iface down/unregister") -Fixes: 78df76a065ae ("ipv4: take rt_uncached_lock only if needed") -Reported-by: syzbot+179fc225724092b8b2b2@syzkaller.appspotmail.com -Closes: https://lore.kernel.org/netdev/6964cdf2.050a0220.eaf7.009d.GAE@google.com/T/#u -Signed-off-by: Eric Dumazet -Cc: Martin KaFai Lau -Reviewed-by: David Ahern -Link: https://patch.msgid.link/20260112103825.3810713-1-edumazet@google.com -Signed-off-by: Jakub Kicinski ---- - net/core/dst.c | 1 + - net/ipv4/route.c | 4 ++-- - net/ipv6/route.c | 4 ++-- - 3 files changed, 5 insertions(+), 4 deletions(-) - -diff --git a/net/core/dst.c b/net/core/dst.c -index e9d35f49c9e7..1dae26c51ebe 100644 ---- a/net/core/dst.c -+++ b/net/core/dst.c -@@ -68,6 +68,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops, - dst->lwtstate = NULL; - rcuref_init(&dst->__rcuref, 1); - INIT_LIST_HEAD(&dst->rt_uncached); -+ dst->rt_uncached_list = NULL; - dst->__use = 0; - dst->lastuse = jiffies; - dst->flags = flags; -diff --git a/net/ipv4/route.c b/net/ipv4/route.c -index 2a3ca3efce7f..78c3d330c2b8 100644 ---- a/net/ipv4/route.c -+++ b/net/ipv4/route.c -@@ -1536,9 +1536,9 @@ void rt_add_uncached_list(struct rtable *rt) - - void rt_del_uncached_list(struct rtable *rt) - { -- if (!list_empty(&rt->dst.rt_uncached)) { -- struct uncached_list *ul = rt->dst.rt_uncached_list; -+ struct uncached_list *ul = rt->dst.rt_uncached_list; - -+ if (ul) { - spin_lock_bh(&ul->lock); - list_del_init(&rt->dst.rt_uncached); - spin_unlock_bh(&ul->lock); -diff --git a/net/ipv6/route.c b/net/ipv6/route.c -index 3371f16b7a3e..0bf1070aa8ac 100644 ---- a/net/ipv6/route.c -+++ b/net/ipv6/route.c -@@ -148,9 +148,9 @@ void rt6_uncached_list_add(struct rt6_info *rt) - - void rt6_uncached_list_del(struct rt6_info *rt) - { -- if (!list_empty(&rt->dst.rt_uncached)) { -- struct uncached_list *ul = rt->dst.rt_uncached_list; -+ struct uncached_list *ul = rt->dst.rt_uncached_list; - -+ if (ul) { - spin_lock_bh(&ul->lock); - list_del_init(&rt->dst.rt_uncached); - spin_unlock_bh(&ul->lock); --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-23007.patch b/SPECS/kernel-rt/CVE-2026-23007.patch deleted file mode 100644 index 018d276db..000000000 --- a/SPECS/kernel-rt/CVE-2026-23007.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 9aa2e99ed029c40d4c313539dbea4216480d1fcc Mon Sep 17 00:00:00 2001 -From: Caleb Sander Mateos -Date: Thu, 8 Jan 2026 10:22:10 -0700 -Subject: [PATCH 01/38] block: zero non-PI portion of auto integrity buffer - -The auto-generated integrity buffer for writes needs to be fully -initialized before being passed to the underlying block device, -otherwise the uninitialized memory can be read back by userspace or -anyone with physical access to the storage device. If protection -information is generated, that portion of the integrity buffer is -already initialized. The integrity data is also zeroed if PI generation -is disabled via sysfs or the PI tuple size is 0. However, this misses -the case where PI is generated and the PI tuple size is nonzero, but the -metadata size is larger than the PI tuple. In this case, the remainder -("opaque") of the metadata is left uninitialized. -Generalize the BLK_INTEGRITY_CSUM_NONE check to cover any case when the -metadata is larger than just the PI tuple. - -Signed-off-by: Caleb Sander Mateos -Fixes: c546d6f43833 ("block: only zero non-PI metadata tuples in bio_integrity_prep") -Reviewed-by: Anuj Gupta -Reviewed-by: Christoph Hellwig -Reviewed-by: Martin K. Petersen -Signed-off-by: Jens Axboe ---- - block/bio-integrity-auto.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c -index 687952f63bbb..b8b7587be967 100644 ---- a/block/bio-integrity-auto.c -+++ b/block/bio-integrity-auto.c -@@ -142,7 +142,7 @@ bool bio_integrity_prep(struct bio *bio) - return true; - set_flags = false; - gfp |= __GFP_ZERO; -- } else if (bi->csum_type == BLK_INTEGRITY_CSUM_NONE) -+ } else if (bi->metadata_size > bi->pi_tuple_size) - gfp |= __GFP_ZERO; - break; - default: --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-23008.patch b/SPECS/kernel-rt/CVE-2026-23008.patch deleted file mode 100644 index d50975e0d..000000000 --- a/SPECS/kernel-rt/CVE-2026-23008.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 5c6fe5133c1b989f9bdd64e7327f830d585c9b01 Mon Sep 17 00:00:00 2001 -From: Ian Forbes -Date: Fri, 14 Nov 2025 14:37:03 -0600 -Subject: [PATCH 02/38] drm/vmwgfx: Fix KMS with 3D on HW version 10 - -HW version 10 does not have GB Surfaces so there is no backing buffer for -surface backed FBs. This would result in a nullptr dereference and crash -the driver causing a black screen. - -Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") -Signed-off-by: Ian Forbes -Reviewed-by: Zack Rusin -Signed-off-by: Zack Rusin -Link: https://patch.msgid.link/20251114203703.1946616-1-ian.forbes@broadcom.com ---- - drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c -index 54ea1b513950..535d844191e7 100644 ---- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c -+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c -@@ -763,13 +763,15 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, - return ERR_PTR(ret); - } - -- ttm_bo_reserve(&bo->tbo, false, false, NULL); -- ret = vmw_bo_dirty_add(bo); -- if (!ret && surface && surface->res.func->dirty_alloc) { -- surface->res.coherent = true; -- ret = surface->res.func->dirty_alloc(&surface->res); -+ if (bo) { -+ ttm_bo_reserve(&bo->tbo, false, false, NULL); -+ ret = vmw_bo_dirty_add(bo); -+ if (!ret && surface && surface->res.func->dirty_alloc) { -+ surface->res.coherent = true; -+ ret = surface->res.func->dirty_alloc(&surface->res); -+ } -+ ttm_bo_unreserve(&bo->tbo); - } -- ttm_bo_unreserve(&bo->tbo); - - return &vfb->base; - } --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-23009.patch b/SPECS/kernel-rt/CVE-2026-23009.patch deleted file mode 100644 index 7f6486dad..000000000 --- a/SPECS/kernel-rt/CVE-2026-23009.patch +++ /dev/null @@ -1,95 +0,0 @@ -From e2b288c090387cfd3a7d27054d0b43e4c78206b2 Mon Sep 17 00:00:00 2001 -From: Mathias Nyman -Date: Fri, 16 Jan 2026 01:37:58 +0200 -Subject: [PATCH 03/38] xhci: sideband: don't dereference freed ring when - removing sideband endpoint -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -xhci_sideband_remove_endpoint() incorrecly assumes that the endpoint is -running and has a valid transfer ring. - -Lianqin reported a crash during suspend/wake-up stress testing, and -found the cause to be dereferencing a non-existing transfer ring -'ep->ring' during xhci_sideband_remove_endpoint(). - -The endpoint and its ring may be in unknown state if this function -is called after xHCI was reinitialized in resume (lost power), or if -device is being re-enumerated, disconnected or endpoint already dropped. - -Fix this by both removing unnecessary ring access, and by checking -ep->ring exists before dereferencing it. Also make sure endpoint is -running before attempting to stop it. - -Remove the xhci_initialize_ring_info() call during sideband endpoint -removal as is it only initializes ring structure enqueue, dequeue and -cycle state values to their starting values without changing actual -hardware enqueue, dequeue and cycle state. Leaving them out of sync -is worse than leaving it as it is. The endpoint will get freed in after -this in most usecases. - -If the (audio) class driver want's to reuse the endpoint after offload -then it is up to the class driver to ensure endpoint is properly set up. - -Reported-by: 胡连勤 -Closes: https://lore.kernel.org/linux-usb/TYUPR06MB6217B105B059A7730C4F6EC8D2B9A@TYUPR06MB6217.apcprd06.prod.outlook.com/ -Tested-by: 胡连勤 -Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity") -Cc: stable@vger.kernel.org -Signed-off-by: Mathias Nyman -Link: https://patch.msgid.link/20260115233758.364097-2-mathias.nyman@linux.intel.com -Signed-off-by: Greg Kroah-Hartman ---- - drivers/usb/host/xhci-sideband.c | 1 - - drivers/usb/host/xhci.c | 15 ++++++++++++--- - 2 files changed, 12 insertions(+), 4 deletions(-) - -diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c -index d49f9886dd84..254ce6d168a5 100644 ---- a/drivers/usb/host/xhci-sideband.c -+++ b/drivers/usb/host/xhci-sideband.c -@@ -190,7 +190,6 @@ xhci_sideband_remove_endpoint(struct xhci_sideband *sb, - } - - __xhci_sideband_remove_endpoint(sb, ep); -- xhci_initialize_ring_info(ep->ring); - mutex_unlock(&sb->mutex); - - return 0; -diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c -index 25cd778d3fbd..d2bda5c17a04 100644 ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -2891,16 +2891,25 @@ int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int - gfp_t gfp_flags) - { - struct xhci_command *command; -+ struct xhci_ep_ctx *ep_ctx; - unsigned long flags; -- int ret; -+ int ret = -ENODEV; - - command = xhci_alloc_command(xhci, true, gfp_flags); - if (!command) - return -ENOMEM; - - spin_lock_irqsave(&xhci->lock, flags); -- ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, -- ep->ep_index, suspend); -+ -+ /* make sure endpoint exists and is running before stopping it */ -+ if (ep->ring) { -+ ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); -+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) -+ ret = xhci_queue_stop_endpoint(xhci, command, -+ ep->vdev->slot_id, -+ ep->ep_index, suspend); -+ } -+ - if (ret < 0) { - spin_unlock_irqrestore(&xhci->lock, flags); - goto out; --- -2.43.0 - diff --git a/SPECS/kernel-rt/CVE-2026-23012.patch b/SPECS/kernel-rt/CVE-2026-23012.patch deleted file mode 100644 index 44f3e0396..000000000 --- a/SPECS/kernel-rt/CVE-2026-23012.patch +++ /dev/null @@ -1,105 +0,0 @@ -From ea7b0f04dc71f6fe41ab74a0f9ec13b081366745 Mon Sep 17 00:00:00 2001 -From: SeongJae Park -Date: Tue, 30 Dec 2025 17:23:13 -0800 -Subject: [PATCH 04/38] mm/damon/core: remove call_control in inactive contexts - -If damon_call() is executed against a DAMON context that is not running, -the function returns error while keeping the damon_call_control object -linked to the context's call_controls list. Let's suppose the object is -deallocated after the damon_call(), and yet another damon_call() is -executed against the same context. The function tries to add the new -damon_call_control object to the call_controls list, which still has the -pointer to the previous damon_call_control object, which is deallocated. -As a result, use-after-free happens. - -This can actually be triggered using the DAMON sysfs interface. It is not -easily exploitable since it requires the sysfs write permission and making -a definitely weird file writes, though. Please refer to the report for -more details about the issue reproduction steps. - -Fix the issue by making two changes. Firstly, move the final -kdamond_call() for cancelling all existing damon_call() requests from -terminating DAMON context to be done before the ctx->kdamond reset. This -makes any code that sees NULL ctx->kdamond can safely assume the context -may not access damon_call() requests anymore. Secondly, let damon_call() -to cleanup the damon_call_control objects that were added to the -already-terminated DAMON context, before returning the error. - -Link: https://lkml.kernel.org/r/20251231012315.75835-1-sj@kernel.org -Fixes: 004ded6bee11 ("mm/damon: accept parallel damon_call() requests") -Signed-off-by: SeongJae Park -Reported-by: JaeJoon Jung -Closes: https://lore.kernel.org/20251224094401.20384-1-rgbi3307@gmail.com -Cc: # 6.17.x -Signed-off-by: Andrew Morton ---- - mm/damon/core.c | 33 +++++++++++++++++++++++++++++++-- - 1 file changed, 31 insertions(+), 2 deletions(-) - -diff --git a/mm/damon/core.c b/mm/damon/core.c -index 533c1c2d72f2..7dddb82b0b9a 100644 ---- a/mm/damon/core.c -+++ b/mm/damon/core.c -@@ -1398,6 +1398,35 @@ bool damon_is_running(struct damon_ctx *ctx) - return running; - } - -+/* -+ * damon_call_handle_inactive_ctx() - handle DAMON call request that added to -+ * an inactive context. -+ * @ctx: The inactive DAMON context. -+ * @control: Control variable of the call request. -+ * -+ * This function is called in a case that @control is added to @ctx but @ctx is -+ * not running (inactive). See if @ctx handled @control or not, and cleanup -+ * @control if it was not handled. -+ * -+ * Returns 0 if @control was handled by @ctx, negative error code otherwise. -+ */ -+static int damon_call_handle_inactive_ctx( -+ struct damon_ctx *ctx, struct damon_call_control *control) -+{ -+ struct damon_call_control *c; -+ -+ mutex_lock(&ctx->call_controls_lock); -+ list_for_each_entry(c, &ctx->call_controls, list) { -+ if (c == control) { -+ list_del(&control->list); -+ mutex_unlock(&ctx->call_controls_lock); -+ return -EINVAL; -+ } -+ } -+ mutex_unlock(&ctx->call_controls_lock); -+ return 0; -+} -+ - /** - * damon_call() - Invoke a given function on DAMON worker thread (kdamond). - * @ctx: DAMON context to call the function for. -@@ -1428,7 +1457,7 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control) - list_add_tail(&control->list, &ctx->call_controls); - mutex_unlock(&ctx->call_controls_lock); - if (!damon_is_running(ctx)) -- return -EINVAL; -+ return damon_call_handle_inactive_ctx(ctx, control); - if (control->repeat) - return 0; - wait_for_completion(&control->completion); -@@ -2669,13 +2698,13 @@ static int kdamond_fn(void *data) - if (ctx->ops.cleanup) - ctx->ops.cleanup(ctx); - kfree(ctx->regions_score_histogram); -+ kdamond_call(ctx, true); - - pr_debug("kdamond (%d) finishes\n", current->pid); - mutex_lock(&ctx->kdamond_lock); - ctx->kdamond = NULL; - mutex_unlock(&ctx->kdamond_lock); - -- kdamond_call(ctx, true); - damos_walk_cancel(ctx); - - mutex_lock(&damon_lock); --- -2.43.0 - diff --git a/SPECS/kernel-rt/config b/SPECS/kernel-rt/config index 22e9a0ae9..43f45ce91 100644 --- a/SPECS/kernel-rt/config +++ b/SPECS/kernel-rt/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 6.17.11 Kernel Configuration +# Linux/x86_64 6.18.15 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.2.0" CONFIG_CC_IS_GCC=y @@ -625,6 +625,7 @@ CONFIG_ACPI_PCC=y # CONFIG_ACPI_FFH is not set CONFIG_PMIC_OPREGION=y CONFIG_TPS68470_PMIC_OPREGION=y +CONFIG_BXT_WC_PMIC_OPREGION=y CONFIG_ACPI_PRMT=y CONFIG_X86_PM_TIMER=y @@ -2125,7 +2126,7 @@ CONFIG_BLK_DEV_RAM_SIZE=4096 # CONFIG_ATA_OVER_ETH is not set CONFIG_XEN_BLKDEV_FRONTEND=m # CONFIG_XEN_BLKDEV_BACKEND is not set -CONFIG_VIRTIO_BLK=m +CONFIG_VIRTIO_BLK=y CONFIG_BLK_DEV_RBD=m # CONFIG_BLK_DEV_UBLK is not set @@ -4037,7 +4038,7 @@ CONFIG_LPC_SCH=m CONFIG_MFD_INTEL_LPSS=m CONFIG_MFD_INTEL_LPSS_ACPI=m CONFIG_MFD_INTEL_LPSS_PCI=m -# CONFIG_MFD_INTEL_PMC_BXT is not set +CONFIG_MFD_INTEL_PMC_BXT=m # CONFIG_MFD_IQS62X is not set # CONFIG_MFD_JANZ_CMODIO is not set # CONFIG_MFD_KEMPLD is not set @@ -4097,6 +4098,7 @@ CONFIG_MFD_INTEL_LPSS_PCI=m # CONFIG_MFD_WM8994 is not set # CONFIG_MFD_ATC260X_I2C is not set # CONFIG_RAVE_SP_CORE is not set +CONFIG_INTEL_SOC_PMIC_BXTWC=m # end of Multifunction device drivers CONFIG_REGULATOR=y @@ -5979,11 +5981,25 @@ CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_GADGET=m CONFIG_TYPEC=m +CONFIG_TYPEC_TCPM=m +CONFIG_TYPEC_TCPCI=m +CONFIG_TYPEC_WCOVE=m CONFIG_TYPEC_UCSI=m CONFIG_UCSI_ACPI=m # CONFIG_USB_ROLE_SWITCH is not set CONFIG_USB_MASS_STORAGE=m CONFIG_USB_LIBCOMPOSITE=m + +# +# USB Type-C Multiplexer/DeMultiplexer Switch support +# +CONFIG_TYPEC_MUX_INTEL_PMC=m + +# +# USB Type-C Alternate Mode drivers +# +CONFIG_TYPEC_DP_ALTMODE=m + CONFIG_MMC=m CONFIG_MMC_BLOCK=m CONFIG_MMC_BLOCK_MINORS=16 @@ -7628,6 +7644,9 @@ CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO=y CONFIG_INIT_STACK_NONE=y # CONFIG_INIT_STACK_ALL_PATTERN is not set CONFIG_INIT_STACK_ALL_ZERO=y +CONFIG_KSTACK_ERASE=y +CONFIG_KSTACK_ERASE_METRICS=y +CONFIG_KSTACK_ERASE_RUNTIME_DISABLE=y CONFIG_GCC_PLUGIN_STACKLEAK=y # CONFIG_STACKLEAK_RUNTIME_DISABLE is not set CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y diff --git a/SPECS/kernel-rt/kernel-rt.signatures.json b/SPECS/kernel-rt/kernel-rt.signatures.json index d865810de..618906c18 100644 --- a/SPECS/kernel-rt/kernel-rt.signatures.json +++ b/SPECS/kernel-rt/kernel-rt.signatures.json @@ -1,10 +1,10 @@ { "Signatures": { "emt-ca-20211013.pem": "5ef124b0924cb1047c111a0ecff1ae11e6ad7cac8d1d9b40f98f99334121f0b0", - "config": "ab03ceb1c66752a85629014b622b53e04c35e60f149c455954281f330d3ae07f", + "config": "91ddbaa423cc93f0b59f22189a034892b99dd5806498bd24aa1c905ed596a4ed", "cpupower": "d7518767bf2b1110d146a49c7d42e76b803f45eb8bd14d931aa6d0d346fae985", "cpupower.service": "b057fe9e5d0e8c36f485818286b80e3eba8ff66ff44797940e99b1fd5361bb98", "sha512hmac-openssl.sh": "02ab91329c4be09ee66d759e4d23ac875037c3b56e5a598e32fd1206da06a27f", - "linux-6.17.11.tar.gz": "820dd3cacc1d853becb9d1051c4ba5e75442633378a63c244fdf179d0b28f4ac" + "linux-6.18.15.tar.gz": "9d18995c14c96a269e18777be65e8d7712c47f56f9709bbceca846aae58c7fe6" } } diff --git a/SPECS/kernel-rt/kernel-rt.spec b/SPECS/kernel-rt/kernel-rt.spec index 674df3cff..1e7d32246 100644 --- a/SPECS/kernel-rt/kernel-rt.spec +++ b/SPECS/kernel-rt/kernel-rt.spec @@ -1,572 +1,205 @@ Summary: Preempt RT Linux Kernel Name: kernel-rt -Version: 6.17.11 -Release: 2%{?dist} +Version: 6.18.15 +Release: 1%{?dist} License: GPLv2 Vendor: Intel Corporation Distribution: Edge Microvisor Toolkit Group: System Environment/Kernel URL: https://www.kernel.org/pub/linux/kernel -Source0: https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.17.11.tar.gz +Source0: https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.18.15.tar.gz Source1: config Source3: sha512hmac-openssl.sh Source4: emt-ca-20211013.pem Source5: cpupower Source6: cpupower.service -# Intel not-upstreamed kernel features -# v6.17.11 -#5439375ca698 Linux 6.17.11 +# Intel Kernel Patches +# Series file for v6.18.15 linux kernel +# df0dc1b06fb6b Linux 6.18.15 # security Patch01001: 0001-Add-security.md-file.security -Patch01002: 0001-issei-initial-driver-skeleton.security -Patch01003: 0002-issei-add-firmware-and-host-clients-implementatio.security -Patch01004: 0003-issei-implement-main-thread-and-ham-messages.security -Patch01005: 0004-issei-add-heci-hardware-module.security -Patch01006: 0005-issei-update-MAINTAINERS-file.security -Patch01007: 0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security -# lpss -Patch02001: 0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss -Patch02002: 0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss -Patch02003: 0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss -Patch02004: 0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss -Patch02005: 0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss -Patch02006: 0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss -Patch02007: 0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss -Patch02008: 0008-spi-intel-Add-support-for-128M-component-density.lpss -Patch02009: 0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss -Patch02010: 0011-i2c-designware-Preliminary-SMBus-support.lpss -Patch02011: 0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss +Patch01002: 0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security +Patch01003: 0001-mei-bus-fix-device-leak.security +Patch01004: 0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security +Patch01005: 0003-mei-expose-device-kind-for-ioe-device.security +Patch01006: 0004-mei-virtio-virtualization-frontend-driver.security +Patch01007: 0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security +Patch01008: 0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security +Patch01009: 0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security +Patch01010: 0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security +Patch01011: 0001-issei-initial-driver-skeleton.security +Patch01012: 0002-issei-add-firmware-and-host-clients-implementatio.security +Patch01013: 0003-issei-implement-main-thread-and-ham-messages.security +Patch01014: 0004-issei-add-heci-hardware-module.security +Patch01015: 0005-issei-update-MAINTAINERS-file.security +Patch01016: 0006-issei-host_client-add-dma-allocation-support.security +Patch01017: 0007-issei-add-driver-to-driver-interface.security +# preempt-rt +Patch02001: 0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt +Patch02002: 0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt +Patch02003: 0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt +Patch02004: 0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt +Patch02005: 0005-drm-i915-Drop-the-irqs_disabled-check.rt +Patch02006: 0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt +Patch02007: 0007-drm-i915-Consider-RCU-read-section-as-atomic.rt +Patch02008: 0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt +Patch02009: 0009-sysfs-Add-sys-kernel-realtime-entry.rt +# rapl +Patch03001: 0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl +Patch03002: 0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl +Patch03003: 0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl +Patch03004: 0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl +Patch03005: 0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl +Patch03006: 0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl +Patch03007: 0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl +Patch03008: 0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl +Patch03009: 0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl +Patch03010: 0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl +Patch03011: 0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl +Patch03012: 0016-cpuidle-Update-header-inclusion.rapl +Patch03013: 0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl +# turbo +Patch04001: 0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo +Patch04002: 0003-tools-power-turbostat-Refactor-added-column-header-p.turbo +Patch04003: 0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo +Patch04004: 0005-tools-power-turbostat.8-Update-example.turbo +Patch04005: 0006-tools-power-turbostat-Refactor-floating-point-printo.turbo +Patch04006: 0007-tools-power-turbostat-Remove-dead-code.turbo +Patch04007: 0008-tools-power-turbostat-Add-LLC-stats.turbo +Patch04008: 0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo +Patch04009: 0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo +Patch04010: 0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo +Patch04011: 0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo +Patch04012: 0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo +Patch04013: 0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo +Patch04014: 0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo +Patch04015: 0016-tools-power-turbostat-Enhance-perf-probe.turbo +Patch04016: 0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo +Patch04017: 0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo +Patch04018: 0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo +Patch04019: 0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo +Patch04020: 0021-tools-power-turbostat-version-2025.12.02.turbo +# ethernet +Patch05001: 0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet +Patch05002: 0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet +Patch05003: 0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet +Patch05004: 0002-bpf-add-btf-register-unregister-API.ethernet +Patch05005: 0003-net-core-XDP-metadata-BTF-netlink-API.ethernet +Patch05006: 0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet +Patch05007: 0005-rtnetlink-Add-return-value-check.ethernet +Patch05008: 0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet +Patch05009: 0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet +Patch05010: 0008-igc-Add-BTF-based-metadata-for-XDP.ethernet +Patch05011: 0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet +Patch05012: 0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet +Patch05013: 0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet +Patch05014: 0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet +Patch05015: 0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet +Patch05016: 0014-igc-Remove-XDP-metadata-invalidation.ethernet +Patch05017: 0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet +Patch05018: 0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet +Patch05019: 0003-net-phy-increase-gpy-loopback-test-delay.ethernet +Patch05020: 0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet +Patch05021: 0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet +Patch05022: 0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet +Patch05023: 0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet +Patch05024: 0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet +Patch05025: 0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet +Patch05026: 0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet +Patch05027: 0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet +Patch05028: 0012-net-phylink-Add-module_exit.ethernet +Patch05029: 0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet +Patch05030: 0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet +Patch05031: 0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet +Patch05032: 0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet +Patch05033: 0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet +Patch05034: 0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet +# nmi +Patch06001: 0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi +Patch06002: 0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi +Patch06003: 0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi +Patch06004: 0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi +Patch06005: 0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi +Patch06006: 0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi +Patch06007: 0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi +Patch06008: 0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi +Patch06009: 0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi +Patch06010: 0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi +Patch06011: 0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi +Patch06012: 0012-KVM-VMX-Virtualize-FRED-event_data.nmi +Patch06013: 0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi +Patch06014: 0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi +Patch06015: 0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi +Patch06016: 0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi +Patch06017: 0017-KVM-x86-Advertise-support-for-FRED.nmi +Patch06018: 0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi +Patch06019: 0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi +Patch06020: 0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi +Patch06021: 0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi +Patch06022: 0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi +Patch06023: 0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi +Patch06024: 0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi +Patch06025: 0025-KVM-selftests-Add-fred-exception-tests.nmi +Patch06026: 0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi +Patch06027: 0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi +Patch06028: 0028-x86-fred-Allow-variable-sized-event-frame.nmi +Patch06029: 0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi +Patch06030: 0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi +Patch06031: 0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi +Patch06032: 0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi +Patch06033: 0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi +Patch06034: 0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi +Patch06035: 0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi +Patch06036: 0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi +Patch06037: 0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi +Patch06038: 0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi +Patch06039: 0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi +Patch06040: 0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi +Patch06041: 0041-KVM-VMX-Implement-NMI-source-injection.nmi +Patch06042: 0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi +Patch06043: 0043-x86-fred-Enable-FRED-by-default.nmi +Patch06044: 0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi # drm -Patch03001: 0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm -Patch03002: 0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm -Patch03003: 0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm -Patch03004: 0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm -# sriov -Patch04001: 0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov -Patch04002: 0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov -# edac -Patch05001: 0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac -Patch05002: 0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac -Patch05003: 0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac -Patch05004: 0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac -Patch05005: 0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac -Patch05006: 0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac -Patch05007: 0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac -Patch05008: 0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac -Patch05009: 0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac -Patch05010: 0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac -Patch05011: 0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac -Patch05012: 0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +Patch07001: 0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm +Patch07002: 0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm +Patch07003: 0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm +Patch07004: 0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm +Patch07005: 0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm +Patch07006: 0001-i915-gt-GuC-for-legacy-platform.drm +Patch07007: 0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm +# edcac +Patch08001: 0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac +Patch08002: 0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +Patch08003: 0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac # perf -Patch06001: 0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf -Patch06002: 0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf -Patch06003: 0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf -Patch06004: 0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf -Patch06005: 0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf -Patch06006: 0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf -Patch06007: 0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf -Patch06008: 0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf -Patch06009: 0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf -Patch06010: 0013-perf-x86-intel-Initialize-architectural-PEBS.perf -Patch06011: 0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf -Patch06012: 0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf -Patch06013: 0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf -Patch06014: 0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf -Patch06015: 0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf -Patch06016: 0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf -Patch06017: 0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf -Patch06018: 0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf -Patch06019: 0022-perf-core-Support-to-capture-higher-width-vector-regi.perf -Patch06020: 0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf -Patch06021: 0024-perf-tools-Support-to-show-SSP-register.perf -Patch06022: 0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf -Patch06023: 0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf -Patch06024: 0027-perf-tools-Support-to-capture-more-vector-registers-x.perf -Patch06025: 0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf -Patch06026: 0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf -Patch06027: 0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf -Patch06028: 0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf -Patch06029: 0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf -Patch06030: 0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf -Patch06031: 0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf -Patch06032: 0036-KVM-selftests-Relax-precise-event-count-validation-as.perf -Patch06033: 0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf -Patch06034: 0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf -Patch06035: 0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf -Patch06036: 0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf -Patch06037: 0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf -Patch06038: 0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf -Patch06039: 0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf -Patch06040: 0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf -Patch06041: 0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf -Patch06042: 0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf -Patch06043: 0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf -Patch06044: 0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf -Patch06045: 0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf -Patch06046: 0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf -Patch06047: 0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf -Patch06048: 0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf -Patch06049: 0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf -Patch06050: 0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf -Patch06051: 0056-perf-Skip-pmu_ctx-based-on-event_type.perf -Patch06052: 0057-perf-Add-generic-exclude_guest-support.perf -Patch06053: 0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf -Patch06054: 0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf -Patch06055: 0060-perf-Clean-up-perf-ctx-time.perf -Patch06056: 0061-perf-Add-a-EVENT_GUEST-flag.perf -Patch06057: 0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf -Patch06058: 0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf -Patch06059: 0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf -Patch06060: 0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf -Patch06061: 0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf -Patch06062: 0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf -Patch06063: 0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf -Patch06064: 0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf -Patch06065: 0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf -Patch06066: 0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf -Patch06067: 0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf -Patch06068: 0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf -Patch06069: 0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf -Patch06070: 0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf -Patch06071: 0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf -Patch06072: 0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf -Patch06073: 0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf -Patch06074: 0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf -Patch06075: 0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf -Patch06076: 0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf -Patch06077: 0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf -Patch06078: 0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf -Patch06079: 0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf -Patch06080: 0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf -Patch06081: 0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf -Patch06082: 0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf -Patch06083: 0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf -Patch06084: 0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf -Patch06085: 0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf -Patch06086: 0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf -Patch06087: 0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf -Patch06088: 0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf -Patch06089: 0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf -Patch06090: 0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf -Patch06091: 0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf -Patch06092: 0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf -Patch06093: 0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf -Patch06094: 0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf -Patch06095: 0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf -# cet -Patch07001: 0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet -Patch07002: 0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet -Patch07003: 0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet -Patch07004: 0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet -Patch07005: 0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet -Patch07006: 0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet -Patch07007: 0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet -Patch07008: 0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet -Patch07009: 0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet -Patch07010: 0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet -Patch07011: 0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet -Patch07012: 0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet -Patch07013: 0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet -Patch07014: 0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet -Patch07015: 0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet -Patch07016: 0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet -Patch07017: 0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet -Patch07018: 0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet -Patch07019: 0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet -Patch07020: 0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet -Patch07021: 0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet -Patch07022: 0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet -Patch07023: 0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet -Patch07024: 0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet -# nmi -Patch08001: 0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi -Patch08002: 0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi -Patch08003: 0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi -Patch08004: 0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi -Patch08005: 0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi -Patch08006: 0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi -Patch08007: 0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi -Patch08008: 0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi -Patch08009: 0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi -Patch08010: 0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi -Patch08011: 0011-KVM-VMX-Virtualize-FRED-event_data.nmi -Patch08012: 0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi -Patch08013: 0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi -Patch08014: 0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi -Patch08015: 0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi -Patch08016: 0016-KVM-x86-Advertise-support-for-FRED.nmi -Patch08017: 0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi -Patch08018: 0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi -Patch08019: 0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi -Patch08020: 0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi -Patch08021: 0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi -Patch08022: 0022-x86-fred-Enable-FRED-by-default.nmi -Patch08023: 0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi -Patch08024: 0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi -Patch08025: 0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi -Patch08026: 0026-KVM-selftests-Add-fred-exception-tests.nmi -Patch08027: 0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi -Patch08028: 0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi -Patch08029: 0029-x86-fred-Allow-variable-sized-event-frame.nmi -Patch08030: 0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi -Patch08031: 0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi -Patch08032: 0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi -Patch08033: 0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi -Patch08034: 0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi -Patch08035: 0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi -Patch08036: 0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi -Patch08037: 0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi -Patch08038: 0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi -Patch08039: 0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi -Patch08040: 0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi -Patch08041: 0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi -Patch08042: 0042-KVM-VMX-Implement-NMI-source-injection.nmi -Patch08043: 0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi -Patch08044: 0044-EDAC-ieh-Fix-a-compile-error.nmi -Patch08045: 0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi -# ipu -Patch09001: 0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu -Patch09002: 0002-INT3472-Support-LT6911GXD.ipu -Patch09003: 0003-media-i2c-add-support-for-lt6911gxd.ipu -Patch09004: 0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu -Patch09005: 0005-ipu-bridge-add-CPHY-support.ipu -Patch09006: 0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -Patch09007: 0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu -Patch09008: 0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu -Patch09009: 0009-max9x-add-config-in-makefile-kconfig.ipu -Patch09010: 0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu -Patch09011: 0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu -Patch09012: 0001-IPU7-media-pci-Add-platform-data-config.ipu -Patch09013: 0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -Patch09014: 0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu -Patch09015: 0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu -Patch09016: 0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -Patch09017: 0001-Remove-IPU7-drivers-from-pci-directory.ipu -Patch09018: 0002-patch-staging-add-ipu7-isys-reset-code.ipu -Patch09019: 0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu -Patch09020: 0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu -Patch09021: 0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu -Patch09022: 0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu -Patch09023: 0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu -Patch09024: 0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu -Patch09025: 0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu -Patch09026: 0010-patch-staging-add-IPU8_PCI_ID-support.ipu -Patch09027: 0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu -Patch09028: 0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu -Patch09029: 0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu -Patch09030: 0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -Patch09031: 0015-media-ipu7-update-CDPHY-register-settings.ipu -Patch09032: 0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu -Patch09033: 0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu -Patch09034: 0018-IPU7-PSYS-driver-addition.ipu -Patch09035: 0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu -Patch09036: 0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu -Patch09037: 0021-Update-compilation-path-for-IPU7-drivers.ipu -Patch09038: 0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu -Patch09039: 0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu -Patch09040: 0003-i2c-atr-Add-fwnode-handling.ipu -Patch09041: 0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu -Patch09042: 0005-media-mc-Add-INTERNAL-pad-flag.ipu -Patch09043: 0006-i2c-atr-Remove-COMPILE_TEST-check.ipu -# tbt -Patch10001: 0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt -Patch10002: 0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt -Patch10003: 0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt -# pmc_core -Patch11001: 0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# i3c -Patch12001: 0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c -Patch12002: 0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c -Patch12003: 0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c -Patch12004: 0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c -Patch12005: 0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c -Patch12006: 0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c -Patch12007: 0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c -Patch12008: 0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c -Patch12009: 0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c -Patch12010: 0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# ethernet -Patch13001: 0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet -Patch13002: 0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet -Patch13003: 0003-bpf-add-btf-register-unregister-API.ethernet -Patch13004: 0004-net-core-XDP-metadata-BTF-netlink-API.ethernet -Patch13005: 0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet -Patch13006: 0006-rtnetlink-Add-return-value-check.ethernet -Patch13007: 0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet -Patch13008: 0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet -Patch13009: 0009-igc-Add-BTF-based-metadata-for-XDP.ethernet -Patch13010: 0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet -Patch13011: 0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet -Patch13012: 0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet -Patch13013: 0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet -Patch13014: 0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet -Patch13015: 0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet -Patch13016: 0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet -Patch13017: 0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet -Patch13018: 0001-igc-Remove-XDP-metadata-invalidation.ethernet +Patch09001: 0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf +Patch09002: 0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf +Patch09003: 0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf +Patch09004: 0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf +Patch09005: 0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf +Patch09006: 0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf +Patch09007: 0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf +Patch09008: 0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf +Patch09009: 0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf +Patch09010: 0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf +Patch09011: 0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf +Patch09012: 0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf +Patch09013: 0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf +# pmt +Patch10001: 0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt +Patch10002: 0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt +Patch10003: 0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt +Patch10004: 0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt +Patch10005: 0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt +Patch10006: 0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt +Patch10007: 0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt # audio -Patch14001: 0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio -Patch14002: 0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio -# rt -Patch15001: 0001-mei-gsc-add-dependency-on-Xe-driver.rt -Patch15002: 0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt -Patch15003: 0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt -Patch15004: 0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt -Patch15005: 0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt -Patch15006: 0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt -Patch15007: 0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt -Patch15008: 0006-drm-i915-Drop-the-irqs_disabled-check.rt -Patch15009: 0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt -Patch15010: 0008-drm-i915-Consider-RCU-read-section-as-atomic.rt -Patch15011: 0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt -# thermal -Patch16001: 0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal -Patch16002: 0002-thermal-intel-int340x-Add-support-for-power-slider.thermal -Patch16003: 0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal -Patch16004: 0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal -Patch16005: 0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal -Patch16006: 0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal -Patch16007: 0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal -Patch16008: 0008-thermal-testing-Rearrange-variable-declarations-in.thermal -Patch16009: 0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal -Patch16010: 0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal -Patch16011: 0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal -# uncore-frequency -Patch17001: 0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency -#CVE-2025-68265 -Patch18001: CVE-2025-68265.patch - -#CVE-2025-68263 -Patch18002: CVE-2025-68263.patch - -#CVE-2025-68255 -Patch18003: CVE-2025-68255.patch - -#CVE-2025-68256 -Patch18004: CVE-2025-68256.patch - -#CVE-2025-68281 -Patch18005: CVE-2025-68281.patch - -#CVE-2025-68262 -Patch18006: CVE-2025-68262.patch - -#CVE-2025-68261 -Patch18007: CVE-2025-68261.patch - -#CVE-2025-68259 -Patch18008: CVE-2025-68259.patch - -#CVE-2025-68254 -Patch18009: CVE-2025-68254.patch - -#CVE-2025-68264 -Patch18010: CVE-2025-68264.patch - -#CVE-2025-68325 -Patch18011: CVE-2025-68325.patch - -#CVE-2025-68323 -Patch18012: CVE-2025-68323.patch - -#CVE-2025-68749 -Patch18013: CVE-2025-68749.patch - -#CVE-2025-68745 -Patch18014: CVE-2025-68745.patch - -#CVE-2025-68349 -Patch18015: CVE-2025-68349.patch - -#CVE-2025-68366 -Patch18016: CVE-2025-68366.patch - -#CVE-2025-68744 -Patch18017: CVE-2025-68744.patch - -#CVE-2025-68363 -Patch18018: CVE-2025-68363.patch - -#CVE-2025-68379 -Patch18019: CVE-2025-68379.patch - -#CVE-2025-68375 -Patch18020: CVE-2025-68375.patch - -#CVE-2025-68736 -Patch18021: CVE-2025-68736.patch - -#CVE-2025-68732 -Patch18022: CVE-2025-68732.patch - -#CVE-2025-68730 -Patch18023: CVE-2025-68730.patch - -#CVE-2025-68733 -Patch18024: CVE-2025-68733.patch - -#CVE-2025-68333 -Patch18025: CVE-2025-68333.patch - -#CVE-2025-68336 -Patch18026: CVE-2025-68336.patch - -#CVE-2025-68345 -Patch18027: CVE-2025-68345.patch - -#CVE-2025-68346 -Patch18028: CVE-2025-68346.patch - -#CVE-2025-68347 -Patch18029: CVE-2025-68347.patch - -#CVE-2025-68348 -Patch18030: CVE-2025-68348.patch - -#CVE-2025-68353 -Patch18031: CVE-2025-68353.patch - -#CVE-2025-68358 -Patch18032: CVE-2025-68358.patch - -#CVE-2025-68337 -Patch18033: CVE-2025-68337.patch - -#CVE-2025-68354 -Patch18034: CVE-2025-68354.patch - -#CVE-2025-68359 -Patch18035: CVE-2025-68359.patch - -#CVE-2025-68741 -Patch18036: CVE-2025-68741.patch - -#CVE-2025-68368 -Patch18037: CVE-2025-68368.patch - -#CVE-2025-68371 -Patch18038: CVE-2025-68371.patch - -#CVE-2025-68373 -Patch18039: CVE-2025-68373.patch -Patch18040: CVE-2025-68373-2.patch - -#CVE-2025-68740 -Patch18041: CVE-2025-68740.patch - -#CVE-2025-68374 -Patch18042: CVE-2025-68374.patch - -#CVE-2025-68742 -Patch18043: CVE-2025-68742.patch - -#CVE-2025-68743 -Patch18044: CVE-2025-68743.patch - -#CVE-2025-68724 -Patch18045: CVE-2025-68724.patch - -#CVE-2025-68378 -Patch18046: CVE-2025-68378.patch - -#CVE-2025-68725 -Patch18047: CVE-2025-68725.patch - -#CVE-2025-68372 -Patch18048: CVE-2025-68372.patch - -#CVE-2026-23007 -Patch18049: CVE-2026-23007.patch - -#CVE-2026-23008 -Patch18050: CVE-2026-23008.patch - -#CVE-2026-23009 -Patch18051: CVE-2026-23009.patch - -#CVE-2026-23012 -Patch18052: CVE-2026-23012.patch - -#CVE-2026-22993 -Patch18053: CVE-2026-22993.patch - -#CVE-2026-22987 -Patch18054: CVE-2026-22987.patch - -#CVE-2026-22981 -Patch18055: CVE-2026-22981.patch - -#CVE-2025-71161 -Patch18056: CVE-2025-71161.patch - -#CVE-2025-71117 -Patch18057: CVE-2025-71117.patch - -#CVE-2025-71128 -Patch18058: CVE-2025-71128.patch - -#CVE-2025-71139 -Patch18059: CVE-2025-71139-1.patch -Patch18060: CVE-2025-71139-2.patch - -#CVE-2025-71142 -Patch18061: CVE-2025-71142.patch - -#CVE-2025-71115 -Patch18062: CVE-2025-71115.patch - -#CVE-2025-71090 -Patch18063: CVE-2025-71090.patch - -#CVE-2025-71070 -Patch18064: CVE-2025-71070-1.patch -Patch18065: CVE-2025-71070-2.patch - -#CVE-2025-71074 -Patch18066: CVE-2025-71074.patch - -#CVE-2025-68823 -Patch18067: CVE-2025-68823.patch - -#CVE-2025-68807 -Patch18068: CVE-2025-68807.patch - -#CVE-2025-68805 -Patch18069: CVE-2025-68805.patch - -#CVE-2025-68791 -Patch18070: CVE-2025-68791.patch - -#CVE-2025-68768 -Patch18071: CVE-2025-68768-1.patch -Patch18072: CVE-2025-68768-2.patch -Patch18073: CVE-2025-68768-3.patch - -#CVE-2025-68764 -Patch18074: CVE-2025-68764.patch - -#CVE-2025-68762 -Patch18075: CVE-2025-68762.patch - -#CVE-2025-68759 -Patch18076: CVE-2025-68759.patch - -#CVE-2025-68756 -Patch18077: CVE-2025-68756.patch - -#CVE-2025-68753 -Patch18078: CVE-2025-68753.patch - -#CVE-2025-68752 -Patch18079: CVE-2025-68752.patch - -#CVE-2026-23004 -Patch18080: CVE-2026-23004.patch - -#CVE-2026-22985 -Patch18081: CVE-2026-22985-1.patch -Patch18082: CVE-2026-22985-2.patch +Patch11001: 0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio +Patch11002: 0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio +Patch11003: 0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio +# lpss +Patch12001: 0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss # End of Patch section @@ -716,8 +349,8 @@ manipulation of eBPF programs and maps. %prep %define _default_patch_flags -p1 --fuzz=3 --force -%setup -q -n linux-6.17.11 -%autosetup -p1 -n linux-6.17.11 +%setup -q -n linux-6.18.15 +%autosetup -p1 -n linux-6.18.15 # %patch 0 -p1 make mrproper @@ -988,6 +621,10 @@ ln -sf linux-%{uname_r}.cfg /boot/mariner.cfg %{_sysconfdir}/bash_completion.d/bpftool %changelog +* Mon Mar 16 2026 Lishan Liu - 6.18.15-1 +- Update kernel to 6.18.15-1 +- lts-v6.18.15-emt-260310T050801Z + * Sun Feb 01 2026 Lishan Liu - 6.17.11-2 - Update kernel to 6.17.11-2 - mainline-v6.17.11-emt-overlay-cve-260128T080735Z diff --git a/SPECS/kernel-rt/series b/SPECS/kernel-rt/series index 9b7e283fe..342ec979c 100644 --- a/SPECS/kernel-rt/series +++ b/SPECS/kernel-rt/series @@ -1,553 +1,185 @@ -# v6.17.11 -#5439375ca698 Linux 6.17.11 +# Series file for v6.18.15 linux kernel +# df0dc1b06fb6b Linux 6.18.15 # security 0001-Add-security.md-file.security +0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security +0001-mei-bus-fix-device-leak.security +0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security +0003-mei-expose-device-kind-for-ioe-device.security +0004-mei-virtio-virtualization-frontend-driver.security +0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security +0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security +0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security +0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security 0001-issei-initial-driver-skeleton.security 0002-issei-add-firmware-and-host-clients-implementatio.security 0003-issei-implement-main-thread-and-ham-messages.security 0004-issei-add-heci-hardware-module.security 0005-issei-update-MAINTAINERS-file.security -0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security -# lpss -0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss -0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss -0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss -0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss -0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss -0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss -0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss -0008-spi-intel-Add-support-for-128M-component-density.lpss -0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss -0011-i2c-designware-Preliminary-SMBus-support.lpss -0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss +0006-issei-host_client-add-dma-allocation-support.security +0007-issei-add-driver-to-driver-interface.security +# preempt-rt +0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt +0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt +0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt +0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt +0005-drm-i915-Drop-the-irqs_disabled-check.rt +0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt +0007-drm-i915-Consider-RCU-read-section-as-atomic.rt +0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt +0009-sysfs-Add-sys-kernel-realtime-entry.rt +# rapl +0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl +0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl +0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl +0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl +0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl +0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl +0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl +0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl +0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl +0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl +0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl +0016-cpuidle-Update-header-inclusion.rapl +0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl +# turbo +0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo +0003-tools-power-turbostat-Refactor-added-column-header-p.turbo +0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo +0005-tools-power-turbostat.8-Update-example.turbo +0006-tools-power-turbostat-Refactor-floating-point-printo.turbo +0007-tools-power-turbostat-Remove-dead-code.turbo +0008-tools-power-turbostat-Add-LLC-stats.turbo +0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo +0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo +0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo +0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo +0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo +0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo +0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo +0016-tools-power-turbostat-Enhance-perf-probe.turbo +0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo +0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo +0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo +0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo +0021-tools-power-turbostat-version-2025.12.02.turbo +# ethernet +0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet +0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet +0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet +0002-bpf-add-btf-register-unregister-API.ethernet +0003-net-core-XDP-metadata-BTF-netlink-API.ethernet +0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet +0005-rtnetlink-Add-return-value-check.ethernet +0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet +0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet +0008-igc-Add-BTF-based-metadata-for-XDP.ethernet +0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet +0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet +0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet +0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet +0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet +0014-igc-Remove-XDP-metadata-invalidation.ethernet +0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet +0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet +0003-net-phy-increase-gpy-loopback-test-delay.ethernet +0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet +0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet +0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet +0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet +0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet +0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet +0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet +0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet +0012-net-phylink-Add-module_exit.ethernet +0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet +0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet +0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet +0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet +0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet +0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet +# nmi +0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi +0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi +0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi +0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi +0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi +0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi +0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi +0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi +0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi +0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi +0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi +0012-KVM-VMX-Virtualize-FRED-event_data.nmi +0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi +0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi +0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi +0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi +0017-KVM-x86-Advertise-support-for-FRED.nmi +0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi +0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi +0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi +0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi +0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi +0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi +0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi +0025-KVM-selftests-Add-fred-exception-tests.nmi +0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi +0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi +0028-x86-fred-Allow-variable-sized-event-frame.nmi +0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi +0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi +0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi +0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi +0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi +0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi +0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi +0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi +0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi +0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi +0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi +0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi +0041-KVM-VMX-Implement-NMI-source-injection.nmi +0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi +0043-x86-fred-Enable-FRED-by-default.nmi +0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi # drm -0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm -0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm -0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm +0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm +0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm +0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm 0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm -# sriov -0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov -0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov -# edac -0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac -0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac -0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac -0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac -0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac -0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac -0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac -0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac -0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac -0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac -0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac -0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm +0001-i915-gt-GuC-for-legacy-platform.drm +0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm +# edcac +0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac +0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac # perf -0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf -0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf -0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf -0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf -0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf -0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf -0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf -0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf -0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf -0013-perf-x86-intel-Initialize-architectural-PEBS.perf -0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf -0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf -0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf -0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf -0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf -0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf -0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf -0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf -0022-perf-core-Support-to-capture-higher-width-vector-regi.perf -0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf -0024-perf-tools-Support-to-show-SSP-register.perf -0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf -0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf -0027-perf-tools-Support-to-capture-more-vector-registers-x.perf -0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf -0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf -0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf -0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf -0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf -0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf -0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf -0036-KVM-selftests-Relax-precise-event-count-validation-as.perf -0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf -0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf -0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf -0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf -0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf -0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf -0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf -0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf -0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf -0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf -0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf -0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf -0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf -0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf -0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf -0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf -0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf -0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf -0056-perf-Skip-pmu_ctx-based-on-event_type.perf -0057-perf-Add-generic-exclude_guest-support.perf -0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf -0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf -0060-perf-Clean-up-perf-ctx-time.perf -0061-perf-Add-a-EVENT_GUEST-flag.perf -0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf -0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf -0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf -0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf -0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf -0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf -0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf -0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf -0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf -0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf -0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf -0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf -0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf -0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf -0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf -0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf -0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf -0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf -0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf -0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf -0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf -0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf -0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf -0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf -0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf -0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf -0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf -0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf -0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf -0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf -0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf -0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf -0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf -0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf -0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf -0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf -0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf -0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf -0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf -# cet -0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet -0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet -0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet -0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet -0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet -0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet -0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet -0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet -0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet -0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet -0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet -0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet -0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet -0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet -0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet -0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet -0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet -0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet -0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet -0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet -0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet -0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet -0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet -0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet -# nmi -0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi -0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi -0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi -0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi -0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi -0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi -0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi -0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi -0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi -0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi -0011-KVM-VMX-Virtualize-FRED-event_data.nmi -0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi -0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi -0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi -0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi -0016-KVM-x86-Advertise-support-for-FRED.nmi -0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi -0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi -0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi -0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi -0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi -0022-x86-fred-Enable-FRED-by-default.nmi -0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi -0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi -0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi -0026-KVM-selftests-Add-fred-exception-tests.nmi -0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi -0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi -0029-x86-fred-Allow-variable-sized-event-frame.nmi -0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi -0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi -0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi -0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi -0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi -0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi -0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi -0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi -0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi -0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi -0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi -0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi -0042-KVM-VMX-Implement-NMI-source-injection.nmi -0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi -0044-EDAC-ieh-Fix-a-compile-error.nmi -0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi -# ipu -0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu -0002-INT3472-Support-LT6911GXD.ipu -0003-media-i2c-add-support-for-lt6911gxd.ipu -0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu -0005-ipu-bridge-add-CPHY-support.ipu -0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu -0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu -0009-max9x-add-config-in-makefile-kconfig.ipu -0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu -0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu -0001-IPU7-media-pci-Add-platform-data-config.ipu -0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu -0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu -0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -0001-Remove-IPU7-drivers-from-pci-directory.ipu -0002-patch-staging-add-ipu7-isys-reset-code.ipu -0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu -0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu -0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu -0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu -0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu -0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu -0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu -0010-patch-staging-add-IPU8_PCI_ID-support.ipu -0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu -0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu -0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu -0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -0015-media-ipu7-update-CDPHY-register-settings.ipu -0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu -0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu -0018-IPU7-PSYS-driver-addition.ipu -0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu -0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu -0021-Update-compilation-path-for-IPU7-drivers.ipu -0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu -0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu -0003-i2c-atr-Add-fwnode-handling.ipu -0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu -0005-media-mc-Add-INTERNAL-pad-flag.ipu -0006-i2c-atr-Remove-COMPILE_TEST-check.ipu -# tbt -0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt -0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt -0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt -# pmc_core -0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# i3c -0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c -0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c -0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c -0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c -0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c -0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c -0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c -0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c -0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c -0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# ethernet -0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet -0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet -0003-bpf-add-btf-register-unregister-API.ethernet -0004-net-core-XDP-metadata-BTF-netlink-API.ethernet -0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet -0006-rtnetlink-Add-return-value-check.ethernet -0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet -0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet -0009-igc-Add-BTF-based-metadata-for-XDP.ethernet -0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet -0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet -0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet -0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet -0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet -0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet -0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet -0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet -0001-igc-Remove-XDP-metadata-invalidation.ethernet +0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf +0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf +0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf +0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf +0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf +0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf +0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf +0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf +0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf +0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf +0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf +0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf +0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf +# pmt +0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt +0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt +0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt +0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt +0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt +0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt +0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt # audio -0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio 0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio -# rt -0001-mei-gsc-add-dependency-on-Xe-driver.rt -0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt -0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt -0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt -0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt -0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt -0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt -0006-drm-i915-Drop-the-irqs_disabled-check.rt -0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt -0008-drm-i915-Consider-RCU-read-section-as-atomic.rt -0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt -# thermal -0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal -0002-thermal-intel-int340x-Add-support-for-power-slider.thermal -0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal -0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal -0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal -0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal -0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal -0008-thermal-testing-Rearrange-variable-declarations-in.thermal -0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal -0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal -0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal -# uncore-frequency -0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency -#CVE-2025-68265 -CVE-2025-68265.patch - -#CVE-2025-68263 -CVE-2025-68263.patch - -#CVE-2025-68255 -CVE-2025-68255.patch - -#CVE-2025-68256 -CVE-2025-68256.patch - -#CVE-2025-68281 -CVE-2025-68281.patch - -#CVE-2025-68262 -CVE-2025-68262.patch - -#CVE-2025-68261 -CVE-2025-68261.patch - -#CVE-2025-68259 -CVE-2025-68259.patch - -#CVE-2025-68254 -CVE-2025-68254.patch - -#CVE-2025-68264 -CVE-2025-68264.patch - -#CVE-2025-68325 -CVE-2025-68325.patch - -#CVE-2025-68323 -CVE-2025-68323.patch - -#CVE-2025-68749 -CVE-2025-68749.patch - -#CVE-2025-68745 -CVE-2025-68745.patch - -#CVE-2025-68349 -CVE-2025-68349.patch - -#CVE-2025-68366 -CVE-2025-68366.patch - -#CVE-2025-68744 -CVE-2025-68744.patch - -#CVE-2025-68363 -CVE-2025-68363.patch - -#CVE-2025-68379 -CVE-2025-68379.patch - -#CVE-2025-68375 -CVE-2025-68375.patch - -#CVE-2025-68736 -CVE-2025-68736.patch - -#CVE-2025-68732 -CVE-2025-68732.patch - -#CVE-2025-68730 -CVE-2025-68730.patch - -#CVE-2025-68733 -CVE-2025-68733.patch - -#CVE-2025-68333 -CVE-2025-68333.patch - -#CVE-2025-68336 -CVE-2025-68336.patch - -#CVE-2025-68345 -CVE-2025-68345.patch - -#CVE-2025-68346 -CVE-2025-68346.patch - -#CVE-2025-68347 -CVE-2025-68347.patch - -#CVE-2025-68348 -CVE-2025-68348.patch - -#CVE-2025-68353 -CVE-2025-68353.patch - -#CVE-2025-68358 -CVE-2025-68358.patch - -#CVE-2025-68337 -CVE-2025-68337.patch - -#CVE-2025-68354 -CVE-2025-68354.patch - -#CVE-2025-68359 -CVE-2025-68359.patch - -#CVE-2025-68741 -CVE-2025-68741.patch - -#CVE-2025-68368 -CVE-2025-68368.patch - -#CVE-2025-68371 -CVE-2025-68371.patch - -#CVE-2025-68373 -CVE-2025-68373.patch -CVE-2025-68373-2.patch - -#CVE-2025-68740 -CVE-2025-68740.patch - -#CVE-2025-68374 -CVE-2025-68374.patch - -#CVE-2025-68742 -CVE-2025-68742.patch - -#CVE-2025-68743 -CVE-2025-68743.patch - -#CVE-2025-68724 -CVE-2025-68724.patch - -#CVE-2025-68378 -CVE-2025-68378.patch - -#CVE-2025-68725 -CVE-2025-68725.patch - -#CVE-2025-68372 -CVE-2025-68372.patch - -#CVE-2026-23007 -CVE-2026-23007.patch - -#CVE-2026-23008 -CVE-2026-23008.patch - -#CVE-2026-23009 -CVE-2026-23009.patch - -#CVE-2026-23012 -CVE-2026-23012.patch - -#CVE-2026-22993 -CVE-2026-22993.patch - -#CVE-2026-22987 -CVE-2026-22987.patch - -#CVE-2026-22981 -CVE-2026-22981.patch - -#CVE-2025-71161 -CVE-2025-71161.patch - -#CVE-2025-71117 -CVE-2025-71117.patch - -#CVE-2025-71128 -CVE-2025-71128.patch - -#CVE-2025-71139 -CVE-2025-71139-1.patch -CVE-2025-71139-2.patch - -#CVE-2025-71142 -CVE-2025-71142.patch - -#CVE-2025-71115 -CVE-2025-71115.patch - -#CVE-2025-71090 -CVE-2025-71090.patch - -#CVE-2025-71070 -CVE-2025-71070-1.patch -CVE-2025-71070-2.patch - -#CVE-2025-71074 -CVE-2025-71074.patch - -#CVE-2025-68823 -CVE-2025-68823.patch - -#CVE-2025-68807 -CVE-2025-68807.patch - -#CVE-2025-68805 -CVE-2025-68805.patch - -#CVE-2025-68791 -CVE-2025-68791.patch - -#CVE-2025-68768 -CVE-2025-68768-1.patch -CVE-2025-68768-2.patch -CVE-2025-68768-3.patch - -#CVE-2025-68764 -CVE-2025-68764.patch - -#CVE-2025-68762 -CVE-2025-68762.patch - -#CVE-2025-68759 -CVE-2025-68759.patch - -#CVE-2025-68756 -CVE-2025-68756.patch - -#CVE-2025-68753 -CVE-2025-68753.patch - -#CVE-2025-68752 -CVE-2025-68752.patch - -#CVE-2026-23004 -CVE-2026-23004.patch - -#CVE-2026-22985 -CVE-2026-22985-1.patch -CVE-2026-22985-2.patch - +0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio +0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio +# lpss +0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss diff --git a/SPECS/kernel/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio b/SPECS/kernel/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio index f2781d98b..ccce3904b 100644 --- a/SPECS/kernel/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio +++ b/SPECS/kernel/0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio @@ -1,7 +1,7 @@ -From e8f667cf51d2a63c169c148e9ab807f298b38c9d Mon Sep 17 00:00:00 2001 +From 759ecfe0aec70fff292da099330c5f1794815e74 Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Tue, 11 Nov 2025 18:35:12 +0800 -Subject: [PATCH] ASoC: Intel: sof_rt5682: Add quirk override support +Subject: [PATCH 1/2] ASoC: Intel: sof_rt5682: Add quirk override support adding quirk override support to configure different quirk configuration at runtime. @@ -15,7 +15,7 @@ Signed-off-by: Mark Brown 1 file changed, 10 insertions(+) diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c -index 4994aaccc583..6ca114c30ae1 100644 +index 4994aaccc583a..6ca114c30ae16 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -35,6 +35,10 @@ diff --git a/SPECS/kernel/0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio b/SPECS/kernel/0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio deleted file mode 100644 index fda9f6c36..000000000 --- a/SPECS/kernel/0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio +++ /dev/null @@ -1,75 +0,0 @@ -From 0664046ef563fcf1999732063a467a223544b79f Mon Sep 17 00:00:00 2001 -From: Balamurugan C -Date: Tue, 18 Nov 2025 13:53:41 +0530 -Subject: [PATCH] ASoC: SOF: Intel: hda: Only check SSP MCLK mask in case of - IPC3 - -IPC4 is using the NHLT blob itself and sends the SSP blob from it -directly to the firmware, there is no need for the MCLK quirk -based on the SSP blob since the SSP blob is in use. - -At the same time reword the error, info and debug messages for clarity. - -Signed-off-by: Peter Ujfalusi -Signed-off-by: Balamurugan C ---- - sound/soc/sof/intel/hda.c | 36 ++++++++++++++++++++++-------------- - 1 file changed, 22 insertions(+), 14 deletions(-) - -diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c -index c387efec41e9..46b1892e173b 100644 ---- a/sound/soc/sof/intel/hda.c -+++ b/sound/soc/sof/intel/hda.c -@@ -1403,7 +1403,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) - mach->mach_params.i2s_link_mask) { - const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); - int ssp_num; -- int mclk_mask; - - if (hweight_long(mach->mach_params.i2s_link_mask) > 1 && - !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB)) -@@ -1428,19 +1427,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) - - sof_pdata->tplg_filename = tplg_filename; - -- mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num); -- -- if (mclk_mask < 0) { -- dev_err(sdev->dev, "Invalid MCLK configuration\n"); -- return NULL; -- } -- -- dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask); -- -- if (mclk_mask) { -- dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask); -- sdev->mclk_id_override = true; -- sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; -+ if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) { -+ int mclk_mask = check_nhlt_ssp_mclk_mask(sdev, -+ ssp_num); -+ -+ if (mclk_mask < 0) { -+ dev_err(sdev->dev, -+ "Invalid MCLK configuration for SSP%d\n", -+ ssp_num); -+ return NULL; -+ } -+ -+ if (mclk_mask) { -+ sdev->mclk_id_override = true; -+ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; -+ dev_info(sdev->dev, -+ "SSP%d to use MCLK id %d (mask: %#x)\n", -+ ssp_num, sdev->mclk_id_quirk, mclk_mask); -+ } else { -+ dev_dbg(sdev->dev, -+ "MCLK mask is empty for SSP%d in NHLT\n", -+ ssp_num); -+ } - } - } - --- -2.43.0 - diff --git a/SPECS/kernel/0001-Add-security.md-file.security b/SPECS/kernel/0001-Add-security.md-file.security index 31f700a7d..fed8a5fe7 100644 --- a/SPECS/kernel/0001-Add-security.md-file.security +++ b/SPECS/kernel/0001-Add-security.md-file.security @@ -1,7 +1,7 @@ -From d765be87faa2993a69b07d474d062c37bddb686f Mon Sep 17 00:00:00 2001 +From d8be3f2811d6b2942f2c0ddfeb938db5fa6f18df Mon Sep 17 00:00:00 2001 From: Lili Li Date: Tue, 24 Oct 2023 10:33:36 +0800 -Subject: [PATCH] Add security.md file +Subject: [PATCH 1/2] Add security.md file Add security.md file to open source repository. @@ -13,7 +13,7 @@ Signed-off-by: Lili Li diff --git a/security.md b/security.md new file mode 100644 -index 000000000000..98bca5b7ed9f +index 0000000000000..98bca5b7ed9fa --- /dev/null +++ b/security.md @@ -0,0 +1,5 @@ diff --git a/SPECS/kernel/0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security b/SPECS/kernel/0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security deleted file mode 100644 index 1050ec751..000000000 --- a/SPECS/kernel/0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security +++ /dev/null @@ -1,343 +0,0 @@ -From 69c9d8b08c91b9a8341588e5e20873856a84d255 Mon Sep 17 00:00:00 2001 -From: Adam Pawlicki -Date: Thu, 13 Nov 2025 10:32:40 +0100 -Subject: [PATCH] Add updated TPR (TXT Protected Regions) support to kernel. - -This commit applies a TPR-support patch from Tboot Live Image project. -Its intent is to make kernel aware of the TPR memory and to disable -it before proceeding with system boot so that kernel can establish its -own DMA protection. - -Signed-off-by: AdamX Pawlicki -Signed-off-by: Mateusz Mowka ---- - arch/x86/kernel/tboot.c | 109 +++++++++++++++++++++++++++++++++++- - drivers/iommu/intel/dmar.c | 8 +++ - drivers/iommu/intel/iommu.c | 7 ++- - include/acpi/actbl1.h | 86 ++++++++++++++++++++++++++++ - include/linux/tboot.h | 6 ++ - 5 files changed, 214 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c -index 46b8f1f16676..04929aba6a5c 100644 ---- a/arch/x86/kernel/tboot.c -+++ b/arch/x86/kernel/tboot.c -@@ -18,6 +18,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -453,8 +454,14 @@ struct sha1_hash { - u8 hash[SHA1_SIZE]; - }; - -+struct heap_ext_data_elt { -+ u32 type; -+ u32 size; -+ u8 data[]; -+} __packed; -+ - struct sinit_mle_data { -- u32 version; /* currently 6 */ -+ u32 version; /* currently 9 */ - struct sha1_hash bios_acm_id; - u32 edx_senter_flags; - u64 mseg_valid; -@@ -469,8 +476,18 @@ struct sinit_mle_data { - u32 mdrs_off; - u32 num_vtd_dmars; - u32 vtd_dmars_off; -+ u32 proc_scrtm_status; /* version 8 or later only*/ -+ struct heap_ext_data_elt ext_data_elts[]; - } __packed; - -+#define HEAP_EXTDATA_TYPE_DTPR 14 -+ -+struct acpi_dtpr_serialize_req { -+ u64 sts : 1; -+ u64 ctrl : 1; -+ u64 unused : 62; -+}; -+ - struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl) - { - void *heap_base, *heap_ptr, *config; -@@ -514,3 +531,93 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb - - return dmar_tbl; - } -+ -+struct acpi_table_dtpr *tboot_get_dtpr_table(void) -+{ -+ void *heap_base, *heap_ptr, *config; -+ struct sinit_mle_data *sinit_mle; -+ struct heap_ext_data_elt *elt; -+ u64 sinit_mle_size; -+ -+ if (!tboot_enabled()) -+ return NULL; -+ /* -+ * ACPI tables may not be DMA protected by tboot, so use DMAR copy -+ * SINIT saved in SinitMleData in TXT heap (which is DMA protected) -+ */ -+ -+ /* map config space in order to get heap addr */ -+ config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES * -+ PAGE_SIZE); -+ if (!config) -+ return NULL; -+ -+ /* now map TXT heap */ -+ heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE), -+ *(u64 *)(config + TXTCR_HEAP_SIZE)); -+ iounmap(config); -+ if (!heap_base) -+ return NULL; -+ -+ /* walk heap to SinitMleData */ -+ /* skip BiosData */ -+ heap_ptr = heap_base + *(u64 *)heap_base; -+ /* skip OsMleData */ -+ heap_ptr += *(u64 *)heap_ptr; -+ /* skip OsSinitData */ -+ heap_ptr += *(u64 *)heap_ptr; -+ /* now points to SinitMleDataSize; set to SinitMleData */ -+ sinit_mle_size = *(u64 *)heap_ptr; -+ heap_ptr += sizeof(u64); -+ -+ sinit_mle = (struct sinit_mle_data *)heap_ptr; -+ if (sinit_mle->version < 9) -+ return NULL; -+ -+ elt = sinit_mle->ext_data_elts; -+ while (elt->type != HEAP_EXTDATA_TYPE_DTPR) { -+ elt = (void *)elt + elt->size; -+ if ((u64)elt > (u64)sinit_mle + sinit_mle_size) -+ return NULL; -+ } -+ return (struct acpi_table_dtpr *)elt->data; -+} -+ -+static bool tboot_tpr_enabled; -+void tboot_parse_dtpr_table(void) -+{ -+ struct acpi_table_dtpr *dtpr; -+ struct acpi_dtpr_instance *tpr_inst; -+ u32 *instance_cnt; -+ u32 i, j; -+ -+ if (!tboot_enabled()) -+ return; -+ dtpr = tboot_get_dtpr_table(); -+ if (dtpr == NULL) -+ return; -+ tboot_tpr_enabled = 1; -+ instance_cnt = (u32 *)(dtpr + 1); -+ tpr_inst = (struct acpi_dtpr_instance *)(instance_cnt + 1); -+ for (i = 0; i < *instance_cnt; ++i) { -+ for (j = 0; j < tpr_inst->tpr_cnt; ++j) { -+ uint64_t *base = ioremap(tpr_inst->tpr_array[j].base, 16); -+ -+ if (base != NULL) { -+ pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", -+ i, j, readq(base), readq(base + 1)); -+ *base |= (1 << 4); -+ iounmap(base); -+ } -+ } -+ tpr_inst = (struct acpi_dtpr_instance *)((u8 *)tpr_inst -+ + sizeof(*tpr_inst) + j*sizeof(*(tpr_inst->tpr_array))); -+ } -+ if (tboot_tpr_enabled) -+ pr_debug("TPR protection detected, PMR will be disabled\n"); -+} -+ -+bool tboot_is_tpr_enabled(void) -+{ -+ return tboot_tpr_enabled; -+} -diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c -index ec975c73cfe6..9c6f93cf1b3f 100644 ---- a/drivers/iommu/intel/dmar.c -+++ b/drivers/iommu/intel/dmar.c -@@ -635,6 +635,7 @@ static int __init - parse_dmar_table(void) - { - struct acpi_table_dmar *dmar; -+ struct acpi_table_dtpr *dtpr; - int drhd_count = 0; - int ret; - struct dmar_res_callback cb = { -@@ -670,6 +671,13 @@ parse_dmar_table(void) - return -EINVAL; - } - -+ dtpr = tboot_get_dtpr_table(); -+ if (dtpr) { -+ //TPR is enabled -+ //This will also tell not to establish IOMMU PMRs -+ tboot_parse_dtpr_table(); -+ } -+ - pr_info("Host address width %d\n", dmar->width + 1); - ret = dmar_walk_dmar_table(dmar, &cb); - if (ret == 0 && drhd_count == 0) -diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c -index dff2d895b8ab..ce84b5e0a9a4 100644 ---- a/drivers/iommu/intel/iommu.c -+++ b/drivers/iommu/intel/iommu.c -@@ -3017,6 +3017,11 @@ static __init int tboot_force_iommu(void) - if (!tboot_enabled()) - return 0; - -+ //If TPR is enabled we don't need to force IOMMU, -+ //TPR set by SINIT ACM will take care of DMA protection -+ if (tboot_is_tpr_enabled()) -+ return 0; -+ - if (no_iommu || dmar_disabled) - pr_warn("Forcing Intel-IOMMU to enabled\n"); - -@@ -3074,7 +3079,7 @@ int __init intel_iommu_init(void) - * calling SENTER, but the kernel is expected to reset/tear - * down the PMRs. - */ -- if (intel_iommu_tboot_noforce) { -+ if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) { - for_each_iommu(iommu, drhd) - iommu_disable_protect_mem_regions(iommu); - } -diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h -index 99fd1588ff38..0fc38879c428 100644 ---- a/include/acpi/actbl1.h -+++ b/include/acpi/actbl1.h -@@ -47,6 +47,7 @@ - #define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ - #define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */ - #define ACPI_SIG_MSCT "MSCT" /* Maximum System Characteristics Table */ -+#define ACPI_SIG_DTPR "DTPR" /* TXT DMA Protection Ranges reporting table */ - - #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ - #define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ -@@ -1972,6 +1973,91 @@ struct acpi_ibft_target { - u16 reverse_chap_secret_offset; - }; - -+/******************************************************************************* -+ * -+ * DTPR - DMA TPR Reporting -+ * Version 1 -+ * -+ * Conforms to "Intel TXT DMA Protection Ranges", -+ * Version xxx, April 2021 -+ * -+ ******************************************************************************/ -+ -+struct acpi_table_dtpr { -+ struct acpi_table_header header; -+ u32 flags; // 36 -+}; -+ -+struct acpi_tpr_array { -+ u64 base; -+}; -+ -+struct acpi_dtpr_instance { -+ u32 flags; -+ u32 tpr_cnt; -+ struct acpi_tpr_array tpr_array[]; -+}; -+ -+/******************************************************************************* -+ * TPRn_BASE -+ * -+ * Specifies the start address of TPRn region. TPR region address and size must -+ * be with 1MB resolution. These bits are compared with the result of the -+ * TPRn_LIMIT[63:20] * applied to the incoming address, to determine if an -+ * access fall within the TPRn defined region. -+ ******************************************************************************/ -+struct acpi_dtprn_base_reg { -+ u64 reserved0 : 3; -+ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) -+ u64 enable : 1; // 0 == range enabled, 1 == range disabled -+ u64 reserved1 : 15; -+ // Minimal TPRn_Base resolution is 1MB. -+ // Applied to the incoming address, to determine if an access -+ // fall within the TPRn defined region. -+ // Width is determined by a bus width which can be obtained -+ // via CPUID function 0x80000008. -+ u64 tpr_base_rw : 44; -+ //u64 unused : 1; -+}; -+ -+/******************************************************************************* -+ * TPRn_LIMIT -+ * -+ * This register defines an isolated region of memory that can be enabled -+ * to prohibit certain system agents from accessing memory. When an agent -+ * sends a request upstream, whether snooped or not, a TPR prevents that -+ * transaction from changing the state of memory. -+ ******************************************************************************/ -+ -+struct acpi_dtprn_limit_reg { -+ u64 reserved0 : 3; -+ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) -+ u64 enable : 1; // 0 == range enabled, 1 == range disabled -+ u64 reserved1 : 15; -+ // Minimal TPRn_Limit resolution is 1MB. -+ // These bits define TPR limit address. -+ // Width is determined by a bus width. -+ u64 tpr_limit_rw : 44; -+ //u64 unused : 1; -+}; -+ -+/******************************************************************************* -+ * SERIALIZE_REQUEST -+ * -+ * This register is used to request serialization of non-coherent DMA -+ * transactions. OS shall issue it before changing of TPR settings -+ * (base / size). -+ ******************************************************************************/ -+ -+struct acpi_tpr_serialize_request { -+ u64 sts : 1; // Status of serialization request (RO) -+ // 0 == register idle, 1 == serialization in progress -+ u64 ctrl : 1; // Control field to initiate serialization (RW) -+ // 0 == normal, 1 == initialize serialization -+ // (self-clear to allow multiple serialization requests) -+ u64 unused : 62; -+}; -+ - /* Reset to default packing */ - - #pragma pack() -diff --git a/include/linux/tboot.h b/include/linux/tboot.h -index d2279160ef39..95f784b0bde0 100644 ---- a/include/linux/tboot.h -+++ b/include/linux/tboot.h -@@ -126,6 +126,9 @@ extern void tboot_probe(void); - extern void tboot_shutdown(u32 shutdown_type); - extern struct acpi_table_header *tboot_get_dmar_table( - struct acpi_table_header *dmar_tbl); -+extern struct acpi_table_dtpr *tboot_get_dtpr_table(void); -+extern void tboot_parse_dtpr_table(void); -+extern bool tboot_is_tpr_enabled(void); - - #else - -@@ -135,6 +138,9 @@ extern struct acpi_table_header *tboot_get_dmar_table( - #define tboot_sleep(sleep_state, pm1a_control, pm1b_control) \ - do { } while (0) - #define tboot_get_dmar_table(dmar_tbl) (dmar_tbl) -+#define tboot_get_dtpr_table() 0 -+#define tboot_parse_dtpr_table() do { } while (0) -+#define tboot_is_tpr_enabled() 0 - - #endif /* !CONFIG_INTEL_TXT */ - --- -2.43.0 - diff --git a/SPECS/kernel/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss b/SPECS/kernel/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss deleted file mode 100644 index 0bbe3cb75..000000000 --- a/SPECS/kernel/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss +++ /dev/null @@ -1,72 +0,0 @@ -From 3a57f124f860a2bdaeb36797ef296be6191bcbfb Mon Sep 17 00:00:00 2001 -From: kalladax -Date: Thu, 21 Aug 2025 12:34:05 +0300 -Subject: [PATCH] Added spi_set_cs() for more stable r/w operations in SPI Mode - 3 - -Signed-off-by: kalladax ---- - drivers/spi/spi-pxa2xx.c | 4 ++++ - drivers/spi/spi.c | 3 ++- - include/linux/spi/spi.h | 2 ++ - 3 files changed, 8 insertions(+), 1 deletion(-) - -diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c -index 06711a62fa3d..84e3eba21e77 100644 ---- a/drivers/spi/spi-pxa2xx.c -+++ b/drivers/spi/spi-pxa2xx.c -@@ -1047,6 +1047,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, - if (!pxa25x_ssp_comp(drv_data)) - pxa2xx_spi_write(drv_data, SSTO, TIMOUT_DFLT); - -+ spi_set_cs(spi, false, false); -+ - /* First set CR1 without interrupt and service enables */ - pxa2xx_spi_update(drv_data, SSCR1, change_mask, cr1); - -@@ -1056,6 +1058,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, - /* Restart the SSP */ - pxa_ssp_enable(drv_data->ssp); - -+ spi_set_cs(spi, true, false); -+ - if (is_mmp2_ssp(drv_data)) { - u8 tx_level = read_SSSR_bits(drv_data, SSSR_TFL_MASK) >> 8; - -diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c -index a388f372b27a..31dac99b496e 100644 ---- a/drivers/spi/spi.c -+++ b/drivers/spi/spi.c -@@ -1067,7 +1067,7 @@ static void spi_toggle_csgpiod(struct spi_device *spi, u8 idx, bool enable, bool - spi_delay_exec(&spi->cs_inactive, NULL); - } - --static void spi_set_cs(struct spi_device *spi, bool enable, bool force) -+void spi_set_cs(struct spi_device *spi, bool enable, bool force) - { - bool activate = enable; - u8 idx; -@@ -1120,6 +1120,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) - spi_delay_exec(&spi->cs_inactive, NULL); - } - } -+EXPORT_SYMBOL_GPL(spi_set_cs); - - #ifdef CONFIG_HAS_DMA - static int spi_map_buf_attrs(struct spi_controller *ctlr, struct device *dev, -diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h -index e9ea43234d9a..0dd8b227ab12 100644 ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -828,6 +828,8 @@ extern int spi_controller_resume(struct spi_controller *ctlr); - extern struct spi_message *spi_get_next_queued_message(struct spi_controller *ctlr); - extern void spi_finalize_current_message(struct spi_controller *ctlr); - extern void spi_finalize_current_transfer(struct spi_controller *ctlr); -+/* SPI driver calls this function to assert/deassert the chip select */ -+extern void spi_set_cs(struct spi_device *spi, bool enable, bool force); - - /* Helper calls for driver to timestamp transfer */ - void spi_take_timestamp_pre(struct spi_controller *ctlr, --- -2.43.0 - diff --git a/SPECS/kernel/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss b/SPECS/kernel/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss new file mode 100644 index 000000000..fbae38532 --- /dev/null +++ b/SPECS/kernel/0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss @@ -0,0 +1,72 @@ +From d3d0312fa3d24aa193d72fdfc9cac413e88c51b6 Mon Sep 17 00:00:00 2001 +From: Balamurugan C +Date: Tue, 24 Feb 2026 12:03:21 +0530 +Subject: [PATCH] Added spi_set_cs() for more stable r/w operations in SPI mode + 3 + +Signed-off-by: Balamurugan C +--- + drivers/spi/spi-pxa2xx.c | 4 ++++ + drivers/spi/spi.c | 3 ++- + include/linux/spi/spi.h | 2 ++ + 3 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c +index ec7117a94d5f1..d71304cdb6c79 100644 +--- a/drivers/spi/spi-pxa2xx.c ++++ b/drivers/spi/spi-pxa2xx.c +@@ -1047,6 +1047,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, + if (!pxa25x_ssp_comp(drv_data)) + pxa2xx_spi_write(drv_data, SSTO, TIMOUT_DFLT); + ++ spi_set_cs(spi, false, false); ++ + /* First set CR1 without interrupt and service enables */ + pxa2xx_spi_update(drv_data, SSCR1, change_mask, cr1); + +@@ -1056,6 +1058,8 @@ static int pxa2xx_spi_transfer_one(struct spi_controller *controller, + /* Restart the SSP */ + pxa_ssp_enable(drv_data->ssp); + ++ spi_set_cs(spi, true, false); ++ + if (is_mmp2_ssp(drv_data)) { + u8 tx_level = read_SSSR_bits(drv_data, SSSR_TFL_MASK) >> 8; + +diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c +index e25df9990f82d..2a0862c815480 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1059,7 +1059,7 @@ static void spi_toggle_csgpiod(struct spi_device *spi, u8 idx, bool enable, bool + spi_delay_exec(&spi->cs_inactive, NULL); + } + +-static void spi_set_cs(struct spi_device *spi, bool enable, bool force) ++void spi_set_cs(struct spi_device *spi, bool enable, bool force) + { + bool activate = enable; + u8 idx; +@@ -1116,6 +1116,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) + spi_delay_exec(&spi->cs_inactive, NULL); + } + } ++EXPORT_SYMBOL_GPL(spi_set_cs); + + #ifdef CONFIG_HAS_DMA + static int spi_map_buf_attrs(struct spi_controller *ctlr, struct device *dev, +diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h +index cb2c2df310899..bb97882277619 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -830,6 +830,8 @@ extern int spi_controller_resume(struct spi_controller *ctlr); + extern struct spi_message *spi_get_next_queued_message(struct spi_controller *ctlr); + extern void spi_finalize_current_message(struct spi_controller *ctlr); + extern void spi_finalize_current_transfer(struct spi_controller *ctlr); ++/* SPI driver calls this function to assert/deassert the chip select */ ++extern void spi_set_cs(struct spi_device *spi, bool enable, bool force); + + /* Helper calls for driver to timestamp transfer */ + void spi_take_timestamp_pre(struct spi_controller *ctlr, +-- +2.43.0 + diff --git a/SPECS/kernel/0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac b/SPECS/kernel/0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac deleted file mode 100644 index 564f871e2..000000000 --- a/SPECS/kernel/0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +++ /dev/null @@ -1,54 +0,0 @@ -From 62386e735a1df2dace2fd46af3216633fb73a64a Mon Sep 17 00:00:00 2001 -From: Lili Li -Date: Tue, 11 Mar 2025 11:00:23 +0800 -Subject: [PATCH] EDAC/igen6: Add more Intel Panther Lake-H SoCs support - -Add more Intel Panther Lake-H SoC compute die IDs for EDAC support. - -Reviewed-by: Qiuxu Zhuo -Signed-off-by: Lili Li ---- - drivers/edac/igen6_edac.c | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - -diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c -index 1fcb66d9994a..4e8d71c54987 100644 ---- a/drivers/edac/igen6_edac.c -+++ b/drivers/edac/igen6_edac.c -@@ -275,6 +275,16 @@ static struct work_struct ecclog_work; - #define DID_PTL_H_SKU1 0xb000 - #define DID_PTL_H_SKU2 0xb001 - #define DID_PTL_H_SKU3 0xb002 -+#define DID_PTL_H_SKU4 0xb003 -+#define DID_PTL_H_SKU5 0xb004 -+#define DID_PTL_H_SKU6 0xb005 -+#define DID_PTL_H_SKU7 0xb008 -+#define DID_PTL_H_SKU8 0xb011 -+#define DID_PTL_H_SKU9 0xb014 -+#define DID_PTL_H_SKU10 0xb015 -+#define DID_PTL_H_SKU11 0xb028 -+#define DID_PTL_H_SKU12 0xb029 -+#define DID_PTL_H_SKU13 0xb02a - - /* Compute die IDs for Wildcat Lake with IBECC */ - #define DID_WCL_SKU1 0xfd00 -@@ -637,6 +647,16 @@ static struct pci_device_id igen6_pci_tbl[] = { - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&mtl_p_cfg }, -+ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&mtl_p_cfg }, - { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, - { }, - }; --- -2.43.0 - diff --git a/SPECS/kernel/0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac b/SPECS/kernel/0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac new file mode 100644 index 000000000..91db3404a --- /dev/null +++ b/SPECS/kernel/0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac @@ -0,0 +1,40 @@ +From 9e49f45e679c30933899b12bc043e8cebda29da1 Mon Sep 17 00:00:00 2001 +From: Qiuxu Zhuo +Date: Mon, 24 Nov 2025 14:54:56 +0800 +Subject: [PATCH 1/3] EDAC/igen6: Add two Intel Amston Lake SoCs support + +Intel Amston Lake SoCs with IBECC (In-Band ECC) capability share the same +IBECC registers as Alder Lake-N SoCs. Add two new compute die IDs for +Amston Lake SoC products to enable EDAC support. + +Tested-by: Jianfeng Gao +Signed-off-by: Qiuxu Zhuo +--- + drivers/edac/igen6_edac.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 61c0e285bd9b6..092397a154d99 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -246,6 +246,8 @@ static struct work_struct ecclog_work; + + /* Compute did IDs for Amston Lake with IBECC */ + #define DID_ASL_SKU1 0x464a ++#define DID_ASL_SKU2 0x4646 ++#define DID_ASL_SKU3 0x4652 + + /* Compute die IDs for Raptor Lake-P with IBECC */ + #define DID_RPL_P_SKU1 0xa706 +@@ -618,6 +620,8 @@ static struct pci_device_id igen6_pci_tbl[] = { + { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_AZB_SKU1), (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_ASL_SKU1), (kernel_ulong_t)&adl_n_cfg }, ++ { PCI_VDEVICE(INTEL, DID_ASL_SKU2), (kernel_ulong_t)&adl_n_cfg }, ++ { PCI_VDEVICE(INTEL, DID_ASL_SKU3), (kernel_ulong_t)&adl_n_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), (kernel_ulong_t)&rpl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), (kernel_ulong_t)&rpl_p_cfg }, +-- +2.43.0 + diff --git a/SPECS/kernel/0001-IPU7-media-pci-Add-platform-data-config.ipu b/SPECS/kernel/0001-IPU7-media-pci-Add-platform-data-config.ipu deleted file mode 100644 index 5eedf04f2..000000000 --- a/SPECS/kernel/0001-IPU7-media-pci-Add-platform-data-config.ipu +++ /dev/null @@ -1,30 +0,0 @@ -From 1f44f78a75b6651770fcfbaca9b3e8f3f8b781bc Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 13 Oct 2025 10:54:32 +0800 -Subject: [PATCH] IPU7: media: pci: Add platform data config. - -Adding missing platform data config in IPU Kconfig. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/Kconfig | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index a92a7fabeac9..f6b1d7915f83 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -19,3 +19,9 @@ config IPU_BRIDGE - - Microsoft Surface models (except Surface Pro 3) - - The Lenovo Miix line (for example the 510, 520, 710 and 720) - - Dell 7285 -+ -+config VIDEO_INTEL_IPU_USE_PLATFORMDATA -+ bool "Enable platform data" -+ default y -+ help -+ Enable platform data in IPU. -\ No newline at end of file --- -2.43.0 - diff --git a/SPECS/kernel/0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi b/SPECS/kernel/0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi deleted file mode 100644 index aae4ecedb..000000000 --- a/SPECS/kernel/0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi +++ /dev/null @@ -1,66 +0,0 @@ -From 1ab8bf60a090e5bbdf64d8b3b7762aef308db022 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 26 Jun 2025 10:16:01 -0700 -Subject: [PATCH 01/44] KVM: VMX: Add host MSR read/write helpers to - consolidate preemption handling - -Add host MSR read/write helpers to consolidate preemption handling to -prepare for adding FRED RSP0 access functions without duplicating the -preemption handling code. - -Signed-off-by: Xin Li (Intel) -Link: https://lore.kernel.org/r/20250626171601.2293914-1-xin@zytor.com -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 25 +++++++++++++++++++------ - 1 file changed, 19 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 779f73d09e68..bec76fbd0b09 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1346,22 +1346,35 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx) - } - - #ifdef CONFIG_X86_64 --static u64 vmx_read_guest_kernel_gs_base(struct vcpu_vmx *vmx) -+static u64 vmx_read_guest_host_msr(struct vcpu_vmx *vmx, u32 msr, u64 *cache) - { - preempt_disable(); - if (vmx->vt.guest_state_loaded) -- rdmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base); -+ *cache = read_msr(msr); - preempt_enable(); -- return vmx->msr_guest_kernel_gs_base; -+ return *cache; - } - --static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) -+static void vmx_write_guest_host_msr(struct vcpu_vmx *vmx, u32 msr, u64 data, -+ u64 *cache) - { - preempt_disable(); - if (vmx->vt.guest_state_loaded) -- wrmsrq(MSR_KERNEL_GS_BASE, data); -+ wrmsrns(msr, data); - preempt_enable(); -- vmx->msr_guest_kernel_gs_base = data; -+ *cache = data; -+} -+ -+static u64 vmx_read_guest_kernel_gs_base(struct vcpu_vmx *vmx) -+{ -+ return vmx_read_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, -+ &vmx->msr_guest_kernel_gs_base); -+} -+ -+static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) -+{ -+ vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data, -+ &vmx->msr_guest_kernel_gs_base); - } - #endif - --- -2.43.0 - diff --git a/SPECS/kernel/0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi b/SPECS/kernel/0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi new file mode 100644 index 000000000..c03133546 --- /dev/null +++ b/SPECS/kernel/0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi @@ -0,0 +1,220 @@ +From f577c87f393a52df70ba2b905ee3806ea54802d2 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 15:51:20 -0700 +Subject: [PATCH 01/44] KVM: VMX: Enable support for secondary VM exit controls + +Introduce infrastructure to support secondary VM exit controls. + +Always load the controls when supported by hardware, though all control +bits remain clear in this patch. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Changes in v4: +* Fix clearing VM_EXIT_ACTIVATE_SECONDARY_CONTROLS (Chao Gao). +* Check VM exit/entry consistency based on the new macro from Sean + Christopherson. + +Change in v3: +* Do FRED controls consistency checks in the VM exit/entry consistency + check framework (Sean Christopherson). + +Change in v2: +* Always load the secondary VM exit controls (Sean Christopherson). +--- + arch/x86/include/asm/msr-index.h | 1 + + arch/x86/include/asm/vmx.h | 3 +++ + arch/x86/kvm/vmx/capabilities.h | 9 ++++++++- + arch/x86/kvm/vmx/vmcs.h | 1 + + arch/x86/kvm/vmx/vmx.c | 29 +++++++++++++++++++++++++++-- + arch/x86/kvm/vmx/vmx.h | 7 ++++++- + 6 files changed, 46 insertions(+), 4 deletions(-) + +diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h +index 9e1720d73244f..baf5e16484185 100644 +--- a/arch/x86/include/asm/msr-index.h ++++ b/arch/x86/include/asm/msr-index.h +@@ -1225,6 +1225,7 @@ + #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 + #define MSR_IA32_VMX_VMFUNC 0x00000491 + #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 ++#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 + + /* Resctrl MSRs: */ + /* - Intel: */ +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index c85c500195239..1f60c04d11fbd 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -107,6 +107,7 @@ + #define VM_EXIT_PT_CONCEAL_PIP 0x01000000 + #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 + #define VM_EXIT_LOAD_CET_STATE 0x10000000 ++#define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 + + #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff + +@@ -262,6 +263,8 @@ enum vmcs_field { + SHARED_EPT_POINTER = 0x0000203C, + PID_POINTER_TABLE = 0x00002042, + PID_POINTER_TABLE_HIGH = 0x00002043, ++ SECONDARY_VM_EXIT_CONTROLS = 0x00002044, ++ SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, + GUEST_PHYSICAL_ADDRESS = 0x00002400, + GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, + VMCS_LINK_POINTER = 0x00002800, +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index 02aadb9d730e7..6bd67c40ca3b8 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -55,8 +55,9 @@ struct vmcs_config { + u32 cpu_based_exec_ctrl; + u32 cpu_based_2nd_exec_ctrl; + u64 cpu_based_3rd_exec_ctrl; +- u32 vmexit_ctrl; + u32 vmentry_ctrl; ++ u32 vmexit_ctrl; ++ u64 vmexit_2nd_ctrl; + u64 misc; + struct nested_vmx_msrs nested; + }; +@@ -141,6 +142,12 @@ static inline bool cpu_has_tertiary_exec_ctrls(void) + CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; + } + ++static inline bool cpu_has_secondary_vmexit_ctrls(void) ++{ ++ return vmcs_config.vmexit_ctrl & ++ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; ++} ++ + static inline bool cpu_has_vmx_virtualize_apic_accesses(void) + { + return vmcs_config.cpu_based_2nd_exec_ctrl & +diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h +index b256253146588..ae152a9d19631 100644 +--- a/arch/x86/kvm/vmx/vmcs.h ++++ b/arch/x86/kvm/vmx/vmcs.h +@@ -47,6 +47,7 @@ struct vmcs_host_state { + struct vmcs_controls_shadow { + u32 vm_entry; + u32 vm_exit; ++ u64 secondary_vm_exit; + u32 pin; + u32 exec; + u32 secondary_exec; +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 91b6f2f3edc2a..f27d29c836298 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -2595,8 +2595,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + u32 _cpu_based_exec_control = 0; + u32 _cpu_based_2nd_exec_control = 0; + u64 _cpu_based_3rd_exec_control = 0; +- u32 _vmexit_control = 0; + u32 _vmentry_control = 0; ++ u32 _vmexit_control = 0; ++ u64 _vmexit2_control = 0; + u64 basic_msr; + u64 misc_msr; + +@@ -2617,6 +2618,12 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + { VM_ENTRY_LOAD_CET_STATE, VM_EXIT_LOAD_CET_STATE }, + }; + ++ struct { ++ u32 entry_control; ++ u64 exit_control; ++ } const vmcs_entry_exit2_pairs[] = { ++ }; ++ + memset(vmcs_conf, 0, sizeof(*vmcs_conf)); + + if (adjust_vmx_controls(KVM_REQUIRED_VMX_CPU_BASED_VM_EXEC_CONTROL, +@@ -2703,10 +2710,19 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + &_vmentry_control)) + return -EIO; + ++ if (_vmexit_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) ++ _vmexit2_control = ++ adjust_vmx_controls64(KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS, ++ MSR_IA32_VMX_EXIT_CTLS2); ++ + if (vmx_check_entry_exit_pairs(vmcs_entry_exit_pairs, + _vmentry_control, _vmexit_control)) + return -EIO; + ++ if (vmx_check_entry_exit_pairs(vmcs_entry_exit2_pairs, ++ _vmentry_control, _vmexit2_control)) ++ return -EIO; ++ + /* + * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they + * can't be used due to an errata where VM Exit may incorrectly clear +@@ -2755,8 +2771,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control; + vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control; + vmcs_conf->cpu_based_3rd_exec_ctrl = _cpu_based_3rd_exec_control; +- vmcs_conf->vmexit_ctrl = _vmexit_control; + vmcs_conf->vmentry_ctrl = _vmentry_control; ++ vmcs_conf->vmexit_ctrl = _vmexit_control; ++ vmcs_conf->vmexit_2nd_ctrl = _vmexit2_control; + vmcs_conf->misc = misc_msr; + + #if IS_ENABLED(CONFIG_HYPERV) +@@ -4410,6 +4427,11 @@ static u32 vmx_get_initial_vmexit_ctrl(void) + ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER); + } + ++static u64 vmx_secondary_vmexit_ctrl(void) ++{ ++ return vmcs_config.vmexit_2nd_ctrl; ++} ++ + void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) + { + struct vcpu_vmx *vmx = to_vmx(vcpu); +@@ -4752,6 +4774,9 @@ static void init_vmcs(struct vcpu_vmx *vmx) + + vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl()); + ++ if (cpu_has_secondary_vmexit_ctrls()) ++ secondary_vm_exit_controls_set(vmx, vmx_secondary_vmexit_ctrl()); ++ + /* 22.2.1, 20.8.1 */ + vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl()); + +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index ea93121029f91..b2724aab48d2b 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -511,7 +511,11 @@ static inline u8 vmx_get_rvi(void) + VM_EXIT_CLEAR_BNDCFGS | \ + VM_EXIT_PT_CONCEAL_PIP | \ + VM_EXIT_CLEAR_IA32_RTIT_CTL | \ +- VM_EXIT_LOAD_CET_STATE) ++ VM_EXIT_LOAD_CET_STATE | \ ++ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) ++ ++#define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) ++#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) + + #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ + (PIN_BASED_EXT_INTR_MASK | \ +@@ -624,6 +628,7 @@ static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u## + } + BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) + BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) ++BUILD_CONTROLS_SHADOW(secondary_vm_exit, SECONDARY_VM_EXIT_CONTROLS, 64) + BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL, 32) + BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL, 32) + BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL, 32) +-- +2.43.0 + diff --git a/SPECS/kernel/0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet b/SPECS/kernel/0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet deleted file mode 100644 index 7638eec22..000000000 --- a/SPECS/kernel/0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet +++ /dev/null @@ -1,223 +0,0 @@ -From bbc92ecd075fa66a2f3488e8b52d1e23c11d987e Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:32 -0700 -Subject: [PATCH] KVM: x86: Rename kvm_{g,s}et_msr()* to show that they emulate - guest accesses - -Rename - kvm_{g,s}et_msr_with_filter() - kvm_{g,s}et_msr() -to - kvm_emulate_msr_{read,write} - __kvm_emulate_msr_{read,write} - -to make it more obvious that KVM uses these helpers to emulate guest -behaviors, i.e., host_initiated == false in these helpers. - -Suggested-by: Sean Christopherson -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Signed-off-by: Sean Christopherson -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 8 ++++---- - arch/x86/kvm/smm.c | 4 ++-- - arch/x86/kvm/vmx/nested.c | 14 +++++++------- - arch/x86/kvm/x86.c | 28 +++++++++++++++------------- - 4 files changed, 28 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 9215d5cda609..b3fff43e0510 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -2156,11 +2156,11 @@ void kvm_prepare_event_vectoring_exit(struct kvm_vcpu *vcpu, gpa_t gpa); - - void kvm_enable_efer_bits(u64); - bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer); --int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data); --int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data); -+int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); -+int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); - int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiated); --int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data); --int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data); -+int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); -+int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); - int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu); - int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg); - int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu); -diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c -index 9864c057187d..5dd8a1646800 100644 ---- a/arch/x86/kvm/smm.c -+++ b/arch/x86/kvm/smm.c -@@ -529,7 +529,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, - - vcpu->arch.smbase = smstate->smbase; - -- if (kvm_set_msr(vcpu, MSR_EFER, smstate->efer & ~EFER_LMA)) -+ if (__kvm_emulate_msr_write(vcpu, MSR_EFER, smstate->efer & ~EFER_LMA)) - return X86EMUL_UNHANDLEABLE; - - rsm_load_seg_64(vcpu, &smstate->tr, VCPU_SREG_TR); -@@ -620,7 +620,7 @@ int emulator_leave_smm(struct x86_emulate_ctxt *ctxt) - - /* And finally go back to 32-bit mode. */ - efer = 0; -- kvm_set_msr(vcpu, MSR_EFER, efer); -+ __kvm_emulate_msr_write(vcpu, MSR_EFER, efer); - } - #endif - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 1c8319916255..33e54402908e 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -1030,7 +1030,7 @@ static u32 nested_vmx_load_msr(struct kvm_vcpu *vcpu, u64 gpa, u32 count) - __func__, i, e.index, e.reserved); - goto fail; - } -- if (kvm_set_msr_with_filter(vcpu, e.index, e.value)) { -+ if (kvm_emulate_msr_write(vcpu, e.index, e.value)) { - pr_debug_ratelimited( - "%s cannot write MSR (%u, 0x%x, 0x%llx)\n", - __func__, i, e.index, e.value); -@@ -1066,7 +1066,7 @@ static bool nested_vmx_get_vmexit_msr_value(struct kvm_vcpu *vcpu, - } - } - -- if (kvm_get_msr_with_filter(vcpu, msr_index, data)) { -+ if (kvm_emulate_msr_read(vcpu, msr_index, data)) { - pr_debug_ratelimited("%s cannot read MSR (0x%x)\n", __func__, - msr_index); - return false; -@@ -2803,8 +2803,8 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - - if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL) && - kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu)) && -- WARN_ON_ONCE(kvm_set_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -- vmcs12->guest_ia32_perf_global_ctrl))) { -+ WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -+ vmcs12->guest_ia32_perf_global_ctrl))) { - *entry_failure_code = ENTRY_FAIL_DEFAULT; - return -EINVAL; - } -@@ -4791,8 +4791,8 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - } - if ((vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL) && - kvm_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu))) -- WARN_ON_ONCE(kvm_set_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -- vmcs12->host_ia32_perf_global_ctrl)); -+ WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -+ vmcs12->host_ia32_perf_global_ctrl)); - - /* Set L1 segment info according to Intel SDM - 27.5.2 Loading Host Segment and Descriptor-Table Registers */ -@@ -4970,7 +4970,7 @@ static void nested_vmx_restore_host_state(struct kvm_vcpu *vcpu) - goto vmabort; - } - -- if (kvm_set_msr_with_filter(vcpu, h.index, h.value)) { -+ if (kvm_emulate_msr_write(vcpu, h.index, h.value)) { - pr_debug_ratelimited( - "%s WRMSR failed (%u, 0x%x, 0x%llx)\n", - __func__, j, h.index, h.value); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 2bcfdcd45b59..dbf9685db5b3 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1943,33 +1943,33 @@ static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu, - __kvm_get_msr); - } - --int kvm_get_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ)) - return KVM_MSR_RET_FILTERED; - return kvm_get_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_get_msr_with_filter); -+EXPORT_SYMBOL_GPL(kvm_emulate_msr_read); - --int kvm_set_msr_with_filter(struct kvm_vcpu *vcpu, u32 index, u64 data) -+int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE)) - return KVM_MSR_RET_FILTERED; - return kvm_set_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_set_msr_with_filter); -+EXPORT_SYMBOL_GPL(kvm_emulate_msr_write); - --int kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) - { - return kvm_get_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_get_msr); -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_read); - --int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data) -+int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) - { - return kvm_set_msr_ignored_check(vcpu, index, data, false); - } --EXPORT_SYMBOL_GPL(kvm_set_msr); -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_write); - - static void complete_userspace_rdmsr(struct kvm_vcpu *vcpu) - { -@@ -2050,7 +2050,8 @@ static int __kvm_emulate_rdmsr(struct kvm_vcpu *vcpu, u32 msr, int reg, - u64 data; - int r; - -- r = kvm_get_msr_with_filter(vcpu, msr, &data); -+ r = kvm_emulate_msr_read(vcpu, msr, &data); -+ - if (!r) { - trace_kvm_msr_read(msr, data); - -@@ -2090,7 +2091,8 @@ static int __kvm_emulate_wrmsr(struct kvm_vcpu *vcpu, u32 msr, u64 data) - { - int r; - -- r = kvm_set_msr_with_filter(vcpu, msr, data); -+ r = kvm_emulate_msr_write(vcpu, msr, data); -+ - if (!r) { - trace_kvm_msr_write(msr, data); - } else { -@@ -8378,7 +8380,7 @@ static int emulator_get_msr_with_filter(struct x86_emulate_ctxt *ctxt, - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - int r; - -- r = kvm_get_msr_with_filter(vcpu, msr_index, pdata); -+ r = kvm_emulate_msr_read(vcpu, msr_index, pdata); - if (r < 0) - return X86EMUL_UNHANDLEABLE; - -@@ -8401,7 +8403,7 @@ static int emulator_set_msr_with_filter(struct x86_emulate_ctxt *ctxt, - struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); - int r; - -- r = kvm_set_msr_with_filter(vcpu, msr_index, data); -+ r = kvm_emulate_msr_write(vcpu, msr_index, data); - if (r < 0) - return X86EMUL_UNHANDLEABLE; - -@@ -8421,7 +8423,7 @@ static int emulator_set_msr_with_filter(struct x86_emulate_ctxt *ctxt, - static int emulator_get_msr(struct x86_emulate_ctxt *ctxt, - u32 msr_index, u64 *pdata) - { -- return kvm_get_msr(emul_to_vcpu(ctxt), msr_index, pdata); -+ return __kvm_emulate_msr_read(emul_to_vcpu(ctxt), msr_index, pdata); - } - - static int emulator_check_rdpmc_early(struct x86_emulate_ctxt *ctxt, u32 pmc) --- -2.43.0 - diff --git a/SPECS/kernel/0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf b/SPECS/kernel/0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf deleted file mode 100644 index a58cb46a4..000000000 --- a/SPECS/kernel/0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf +++ /dev/null @@ -1,76 +0,0 @@ -From aa22513ad69e7f0001a05cdcf34cf3effc5be5bd Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 10 Oct 2025 08:52:39 +0800 -Subject: [PATCH] KVM: x86/pmu: Fix the warning in - perf_get_x86_pmu_capability() - -When load KVM module in Intel hybrid platforms, the warning below is -observed. - -<4>[ 10.973827] ------------[ cut here ]------------ -<4>[ 10.973841] WARNING: arch/x86/events/core.c:3089 at -perf_get_x86_pmu_capability+0xd/0xc0, CPU#15: (udev-worker)/386 -... -<4>[ 10.974028] Call Trace: -<4>[ 10.974030] -<4>[ 10.974033] ? kvm_init_pmu_capability+0x2b/0x190 [kvm] -<4>[ 10.974154] kvm_x86_vendor_init+0x1b0/0x1a40 [kvm] -<4>[ 10.974248] vmx_init+0xdb/0x260 [kvm_intel] -<4>[ 10.974278] ? __pfx_vt_init+0x10/0x10 [kvm_intel] -<4>[ 10.974296] vt_init+0x12/0x9d0 [kvm_intel] -<4>[ 10.974309] ? __pfx_vt_init+0x10/0x10 [kvm_intel] -<4>[ 10.974322] do_one_initcall+0x60/0x3f0 -<4>[ 10.974335] do_init_module+0x97/0x2b0 -<4>[ 10.974345] load_module+0x2d08/0x2e30 -<4>[ 10.974349] ? __kernel_read+0x158/0x2f0 -<4>[ 10.974370] ? kernel_read_file+0x2b1/0x320 -<4>[ 10.974381] init_module_from_file+0x96/0xe0 -<4>[ 10.974384] ? init_module_from_file+0x96/0xe0 -<4>[ 10.974399] idempotent_init_module+0x117/0x330 -<4>[ 10.974415] __x64_sys_finit_module+0x73/0xe0 - -The root cause is the helper perf_get_x86_pmu_capability() is called -unconditionally but it's supposed to be called only on non-hybrid -platforms. - -This patch fixes this warning by only calling -perf_get_x86_pmu_capability() on non-hybrid platforms. - -Reported-by: Chaitanya Kumar Borah -Closes: https://lore.kernel.org/all/70b64347-2aca-4511-af78-a767d5fa8226@intel.com/ -Fixes: 51f34b1e650f ("KVM: x86/pmu: Snapshot host (i.e. perf's) reported PMU capabilities") -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi ---- - arch/x86/kvm/pmu.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 0e5048ae86fa..07784b95767d 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -108,16 +108,18 @@ void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops) - bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; - int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; - -- perf_get_x86_pmu_capability(&kvm_host_pmu); -- - /* - * Hybrid PMUs don't play nice with virtualization without careful - * configuration by userspace, and KVM's APIs for reporting supported - * vPMU features do not account for hybrid PMUs. Disable vPMU support - * for hybrid PMUs until KVM gains a way to let userspace opt-in. - */ -- if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) -+ if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) { - enable_pmu = false; -+ memset(&kvm_host_pmu, 0, sizeof(kvm_host_pmu)); -+ } else { -+ perf_get_x86_pmu_capability(&kvm_host_pmu); -+ } - - if (enable_pmu) { - /* --- -2.43.0 - diff --git a/SPECS/kernel/0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss b/SPECS/kernel/0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss deleted file mode 100644 index 222a054e2..000000000 --- a/SPECS/kernel/0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss +++ /dev/null @@ -1,32 +0,0 @@ -From d46104a8fa52726a96700faba6f781b1a42edcf7 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Wed, 5 Apr 2023 16:49:57 +0300 -Subject: [PATCH 01/16] PCI: Apply ASPM L1 latency quirk to Intel DG2 Audio - endpoints as well - -In order to reduce power consumption these links can go into L1 when -idle but they too have the latency set to < 1us so we need to apply the -same quirk we use with the GPU endpoints of DG2. - -Signed-off-by: Mika Westerberg ---- - drivers/pci/quirks.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c -index d97335a40193..afdaa2a0629b 100644 ---- a/drivers/pci/quirks.c -+++ b/drivers/pci/quirks.c -@@ -6229,6 +6229,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f85, aspm_l1_acceptable_latency - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f86, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f87, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f88, aspm_l1_acceptable_latency); -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f90, aspm_l1_acceptable_latency); -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f91, aspm_l1_acceptable_latency); -+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f92, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5690, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5691, aspm_l1_acceptable_latency); - DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5692, aspm_l1_acceptable_latency); --- -2.43.0 - diff --git a/SPECS/kernel/0001-Remove-IPU7-drivers-from-pci-directory.ipu b/SPECS/kernel/0001-Remove-IPU7-drivers-from-pci-directory.ipu deleted file mode 100644 index 777748657..000000000 --- a/SPECS/kernel/0001-Remove-IPU7-drivers-from-pci-directory.ipu +++ /dev/null @@ -1,21461 +0,0 @@ -From 76561db385e3dc5efdf4621c9244930ed2b34bea Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 27 Jan 2026 13:58:42 +0800 -Subject: [PATCH 01/21] Remove IPU7 drivers from pci directory - -Latest IPU7 drivers are located in drivers/staging/media/ipu7 -This is to avoid confusion from having multiple IPU7 -drivers exist in kernel 6.17. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/ipu7/Kconfig | 45 - - drivers/media/pci/intel/ipu7/Makefile | 37 - - .../pci/intel/ipu7/abi/ipu7_fw_boot_abi.h | 163 - - .../pci/intel/ipu7/abi/ipu7_fw_common_abi.h | 175 - - .../pci/intel/ipu7/abi/ipu7_fw_config_abi.h | 19 - - .../intel/ipu7/abi/ipu7_fw_insys_config_abi.h | 19 - - .../pci/intel/ipu7/abi/ipu7_fw_isys_abi.h | 459 --- - .../pci/intel/ipu7/abi/ipu7_fw_msg_abi.h | 465 --- - .../intel/ipu7/abi/ipu7_fw_psys_config_abi.h | 24 - - .../pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h | 49 - - drivers/media/pci/intel/ipu7/ipu7-boot.c | 431 --- - drivers/media/pci/intel/ipu7/ipu7-boot.h | 25 - - drivers/media/pci/intel/ipu7/ipu7-bus.c | 158 - - drivers/media/pci/intel/ipu7/ipu7-bus.h | 69 - - .../media/pci/intel/ipu7/ipu7-buttress-regs.h | 461 --- - drivers/media/pci/intel/ipu7/ipu7-buttress.c | 1193 ------- - drivers/media/pci/intel/ipu7/ipu7-buttress.h | 77 - - drivers/media/pci/intel/ipu7/ipu7-cpd.c | 277 -- - drivers/media/pci/intel/ipu7/ipu7-cpd.h | 16 - - drivers/media/pci/intel/ipu7/ipu7-dma.c | 478 --- - drivers/media/pci/intel/ipu7/ipu7-dma.h | 46 - - drivers/media/pci/intel/ipu7/ipu7-fw-isys.c | 389 --- - drivers/media/pci/intel/ipu7/ipu7-fw-isys.h | 42 - - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 1034 ------ - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.h | 16 - - .../pci/intel/ipu7/ipu7-isys-csi2-regs.h | 1197 ------- - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c | 544 ---- - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h | 64 - - .../media/pci/intel/ipu7/ipu7-isys-queue.c | 1198 ------- - .../media/pci/intel/ipu7/ipu7-isys-queue.h | 75 - - .../media/pci/intel/ipu7/ipu7-isys-subdev.c | 348 -- - .../media/pci/intel/ipu7/ipu7-isys-subdev.h | 56 - - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c | 693 ---- - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h | 70 - - .../media/pci/intel/ipu7/ipu7-isys-video.c | 1311 -------- - .../media/pci/intel/ipu7/ipu7-isys-video.h | 125 - - drivers/media/pci/intel/ipu7/ipu7-isys.c | 1625 ---------- - drivers/media/pci/intel/ipu7/ipu7-isys.h | 187 -- - drivers/media/pci/intel/ipu7/ipu7-mmu.c | 854 ----- - drivers/media/pci/intel/ipu7/ipu7-mmu.h | 414 --- - .../media/pci/intel/ipu7/ipu7-platform-regs.h | 82 - - drivers/media/pci/intel/ipu7/ipu7-syscom.c | 79 - - drivers/media/pci/intel/ipu7/ipu7-syscom.h | 35 - - drivers/media/pci/intel/ipu7/ipu7.c | 2864 ----------------- - drivers/media/pci/intel/ipu7/ipu7.h | 259 -- - drivers/media/pci/intel/ipu7/psys/Makefile | 18 - - drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 1545 --------- - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.c | 603 ---- - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.h | 42 - - drivers/media/pci/intel/ipu7/psys/ipu7-psys.c | 398 --- - drivers/media/pci/intel/ipu7/psys/ipu7-psys.h | 184 -- - 51 files changed, 21037 deletions(-) - delete mode 100644 drivers/media/pci/intel/ipu7/Kconfig - delete mode 100644 drivers/media/pci/intel/ipu7/Makefile - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-platform-regs.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.h - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7.c - delete mode 100644 drivers/media/pci/intel/ipu7/ipu7.h - delete mode 100644 drivers/media/pci/intel/ipu7/psys/Makefile - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu-psys.c - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.c - delete mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.h - -diff --git a/drivers/media/pci/intel/ipu7/Kconfig b/drivers/media/pci/intel/ipu7/Kconfig -deleted file mode 100644 -index 91954fdadc8b..000000000000 ---- a/drivers/media/pci/intel/ipu7/Kconfig -+++ /dev/null -@@ -1,45 +0,0 @@ --config VIDEO_INTEL_IPU7 -- tristate "Intel IPU7 driver" -- depends on ACPI || COMPILE_TEST -- depends on VIDEO_DEV -- depends on X86 && HAS_DMA -- depends on IPU_BRIDGE || !IPU_BRIDGE -- # -- # This driver incorrectly tries to override the dma_ops. It should -- # never have done that, but for now keep it working on architectures -- # that use dma ops -- # -- depends on ARCH_HAS_DMA_OPS -- select AUXILIARY_BUS -- select IOMMU_IOVA -- select VIDEO_V4L2_SUBDEV_API -- select MEDIA_CONTROLLER -- select VIDEOBUF2_DMA_SG -- select V4L2_FWNODE -- help -- This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs -- and used for capturing images and video from camera sensors. -- -- To compile this driver, say Y here! It contains 3 modules - -- intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. -- --config VIDEO_INTEL_IPU7_MGC -- bool "Compile for IPU7 MGC driver" -- depends on VIDEO_INTEL_IPU7 -- help -- If selected, MGC device nodes would be created. -- -- Recommended for driver developers only. -- -- If you want to the MGC devices exposed to user as media entity, -- you must select this option, otherwise no. -- --config VIDEO_INTEL_IPU7_ISYS_RESET -- bool "IPU7 ISYS RESET" -- depends on VIDEO_INTEL_IPU7 -- default n -- help -- This option enables IPU7 ISYS reset feature to support -- HDMI-MIPI converter hot-plugging. -- -- If doubt, say N here. -diff --git a/drivers/media/pci/intel/ipu7/Makefile b/drivers/media/pci/intel/ipu7/Makefile -deleted file mode 100644 -index 50417017da3a..000000000000 ---- a/drivers/media/pci/intel/ipu7/Makefile -+++ /dev/null -@@ -1,37 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --# Copyright (c) 2017 - 2025 Intel Corporation. -- --is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) --ifeq ($(is_kernel_lt_6_10), 1) --ifneq ($(EXTERNAL_BUILD), 1) --src := $(srctree)/$(src) --endif --endif -- --intel-ipu7-objs += ipu7.o \ -- ipu7-bus.o \ -- ipu7-dma.o \ -- ipu7-mmu.o \ -- ipu7-buttress.o \ -- ipu7-cpd.o \ -- ipu7-syscom.o \ -- ipu7-boot.o -- --obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7.o -- --intel-ipu7-isys-objs += ipu7-isys.o \ -- ipu7-isys-csi2.o \ -- ipu7-isys-csi-phy.o \ -- ipu7-fw-isys.o \ -- ipu7-isys-video.o \ -- ipu7-isys-queue.o \ -- ipu7-isys-subdev.o -- --ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --intel-ipu7-isys-objs += ipu7-isys-tpg.o --endif --obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o -- --obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ -- --ccflags-y += -I$(src)/ -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -deleted file mode 100644 -index a1519c4fe661..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -+++ /dev/null -@@ -1,163 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_BOOT_ABI_H --#define IPU7_FW_BOOT_ABI_H -- --#include "ipu7_fw_common_abi.h" --#include "ipu7_fw_syscom_abi.h" -- --#define IA_GOFO_FWLOG_SEVERITY_CRIT (0U) --#define IA_GOFO_FWLOG_SEVERITY_ERROR (1U) --#define IA_GOFO_FWLOG_SEVERITY_WARNING (2U) --#define IA_GOFO_FWLOG_SEVERITY_INFO (3U) --#define IA_GOFO_FWLOG_SEVERITY_DEBUG (4U) --#define IA_GOFO_FWLOG_SEVERITY_VERBOSE (5U) --#define IA_GOFO_FWLOG_MAX_LOGGER_SOURCES (64U) -- --#define LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK BIT(0) --#define LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK BIT(1) --#define LOGGER_CONFIG_CHANNEL_ENABLE_ALL_BITMASK \ -- (LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK | \ -- LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK) -- --struct ia_gofo_logger_config { -- u8 use_source_severity; -- u8 source_severity[IA_GOFO_FWLOG_MAX_LOGGER_SOURCES]; -- u8 use_channels_enable_bitmask; -- u8 channels_enable_bitmask; -- u8 padding[1]; -- ia_gofo_addr_t hw_printf_buffer_base_addr; -- u32 hw_printf_buffer_size_bytes; --}; -- --#pragma pack(push, 1) -- --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP \ -- ((u32)IA_GOFO_FW_BOOT_ID_MAX) --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET (0U) --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PS_OFFSET \ -- ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ -- (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) --#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) --#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) --#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) --#define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP) --#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) --#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -- (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) -- --#define IA_GOFO_BOOT_RESERVED_SIZE (58U) --#define IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE (IA_GOFO_BOOT_RESERVED_SIZE) --#define IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS \ -- (sizeof(ia_gofo_addr_t) + sizeof(ia_gofo_addr_t) + sizeof(u32)) -- --enum ia_gofo_buttress_reg_id { -- IA_GOFO_FW_BOOT_CONFIG_ID = 0, -- IA_GOFO_FW_BOOT_STATE_ID = 1, -- IA_GOFO_FW_BOOT_RESERVED1_ID = IA_GOFO_FW_BOOT_STATE_ID, -- IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2, -- IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3, -- IA_GOFO_FW_BOOT_RESERVED0_ID = IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID, -- IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID = 4, -- IA_GOFO_FW_BOOT_ID_MAX --}; -- --enum ia_gofo_boot_uc_tile_frequency_units { -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ = 0, -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_HZ = 1, -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_N --}; -- --#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ -- (0xdead0000U == ((boot_state) & 0xffff0000U)) -- --struct ia_gofo_boot_config { -- u32 length; -- struct ia_gofo_version_s config_version; -- struct ia_gofo_msg_version_list client_version_support; -- ia_gofo_addr_t pkg_dir; -- ia_gofo_addr_t subsys_config; -- u32 uc_tile_frequency; -- u16 checksum; -- u8 uc_tile_frequency_units; -- u8 padding[1]; -- u32 reserved[IA_GOFO_BOOT_RESERVED_SIZE]; -- struct syscom_config_s syscom_context_config; --}; -- --struct ia_gofo_secondary_boot_config { -- u32 length; -- struct ia_gofo_version_s config_version; -- struct ia_gofo_msg_version_list client_version_support; -- u8 reserved1[IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS]; -- u16 checksum; -- u8 padding[2]; -- u32 reserved2[IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE]; -- struct syscom_config_s syscom_context_config; --}; -- --#pragma pack(pop) -- --#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U --#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U --#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U --#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U --#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U --#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U --#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U --#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U --#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U -- --enum ia_gofo_boot_state { -- IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, -- IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, -- IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, -- IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, -- IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, -- IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, -- IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, -- IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, -- IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, -- IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, -- IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, -- IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, -- IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, -- IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, -- IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, -- IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, -- IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, -- IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = -- IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DIRTY = -- IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DTAG = -- IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_CACHE = -- IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_DOUBLE_EXCEPTION = -- IA_GOFO_DOUBLE_EXCEPTION_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = -- IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, -- IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, -- IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, -- IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = -- 0xdead1012U, -- IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, -- IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, -- IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, -- IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, -- IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, -- IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, -- IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, -- IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -deleted file mode 100644 -index 7bb6fac585a3..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -+++ /dev/null -@@ -1,175 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_COMMOM_ABI_H --#define IPU7_FW_COMMOM_ABI_H -- --#include -- --#pragma pack(push, 1) --typedef u32 ia_gofo_addr_t; -- --#define IA_GOFO_ADDR_NULL (0U) -- --struct ia_gofo_version_s { -- u8 patch; -- u8 subminor; -- u8 minor; -- u8 major; --}; -- --#define IA_GOFO_MSG_VERSION_INIT(major_val, minor_val, subminor_val, patch_val)\ -- {.major = (major_val), .minor = (minor_val), .subminor = \ -- (subminor_val), .patch = (patch_val)} -- --#define IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES (3U) --#define IA_GOFO_MSG_RESERVED_SIZE (3U) -- --struct ia_gofo_msg_version_list { -- u8 num_versions; -- u8 reserved[IA_GOFO_MSG_RESERVED_SIZE]; -- struct ia_gofo_version_s versions[IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES]; --}; -- --#pragma pack(pop) -- --#define TLV_TYPE_PADDING (0U) -- --#pragma pack(push, 1) -- --#define IA_GOFO_ABI_BITS_PER_BYTE (8U) -- --struct ia_gofo_tlv_header { -- u16 tlv_type; -- u16 tlv_len32; --}; -- --struct ia_gofo_tlv_list { -- u16 num_elems; -- u16 head_offset; --}; -- --#define TLV_ITEM_ALIGNMENT ((u32)sizeof(u32)) --#define TLV_MSG_ALIGNMENT ((u32)sizeof(u64)) --#define TLV_LIST_ALIGNMENT TLV_ITEM_ALIGNMENT --#pragma pack(pop) -- --#define IA_GOFO_MODULO(dividend, divisor) ((dividend) % (divisor)) -- --#define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) --#define IA_GOFO_MSG_ERR_OK (0U) --#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) --#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) --#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) -- --#pragma pack(push, 1) --struct ia_gofo_msg_err { -- u32 err_group; -- u32 err_code; -- u32 err_detail[IA_GOFO_MSG_ERR_MAX_DETAILS]; --}; -- --#pragma pack(pop) -- --#define IA_GOFO_MSG_ERR_GROUP_APP_EXT_START (16U) --#define IA_GOFO_MSG_ERR_GROUP_MAX (31U) --#define IA_GOFO_MSG_ERR_GROUP_INTERNAL_START (IA_GOFO_MSG_ERR_GROUP_MAX + 1U) --#define IA_GOFO_MSG_ERR_GROUP_RESERVED IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED --#define IA_GOFO_MSG_ERR_GROUP_GENERAL 1 -- --enum ia_gofo_msg_err_general { -- IA_GOFO_MSG_ERR_GENERAL_OK = IA_GOFO_MSG_ERR_OK, -- IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_SMALL = 1, -- IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_LARGE = 2, -- IA_GOFO_MSG_ERR_GENERAL_DEVICE_STATE = 3, -- IA_GOFO_MSG_ERR_GENERAL_ALIGNMENT = 4, -- IA_GOFO_MSG_ERR_GENERAL_INDIRECT_REF_PTR_INVALID = 5, -- IA_GOFO_MSG_ERR_GENERAL_INVALID_MSG_TYPE = 6, -- IA_GOFO_MSG_ERR_GENERAL_SYSCOM_FAIL = 7, -- IA_GOFO_MSG_ERR_GENERAL_N --}; -- --#pragma pack(push, 1) --#define IA_GOFO_MSG_TYPE_RESERVED 0 --#define IA_GOFO_MSG_TYPE_INDIRECT 1 --#define IA_GOFO_MSG_TYPE_LOG 2 --#define IA_GOFO_MSG_TYPE_GENERAL_ERR 3 -- --struct ia_gofo_msg_header { -- struct ia_gofo_tlv_header tlv_header; -- struct ia_gofo_tlv_list msg_options; -- u64 user_token; --}; -- --struct ia_gofo_msg_header_ack { -- struct ia_gofo_msg_header header; -- struct ia_gofo_msg_err err; -- --}; -- --struct ia_gofo_msg_general_err { -- struct ia_gofo_msg_header_ack header; --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --enum ia_gofo_msg_link_streaming_mode { -- IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF = 0, -- IA_GOFO_MSG_LINK_STREAMING_MODE_DOFF = 1, -- IA_GOFO_MSG_LINK_STREAMING_MODE_BCLM = 2, -- IA_GOFO_MSG_LINK_STREAMING_MODE_BCSM_FIX = 3, -- IA_GOFO_MSG_LINK_STREAMING_MODE_N --}; -- --enum ia_gofo_soc_pbk_instance_id { -- IA_GOFO_SOC_PBK_ID0 = 0, -- IA_GOFO_SOC_PBK_ID1 = 1, -- IA_GOFO_SOC_PBK_ID_N --}; -- --#define IA_GOFO_MSG_LINK_PBK_MAX_SLOTS (2U) -- --struct ia_gofo_msg_indirect { -- struct ia_gofo_msg_header header; -- struct ia_gofo_tlv_header ref_header; -- ia_gofo_addr_t ref_msg_ptr; --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --#define IA_GOFO_MSG_LOG_MAX_PARAMS (4U) --#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) -- --#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) --#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) -- --struct ia_gofo_msg_log_info { -- u16 log_counter; -- u8 msg_parameter_types; -- /* [0:0] is_out_of_order, [1:3] logger_channel, [4:7] reserved */ -- u8 logger_opts; -- u32 fmt_id; -- u32 params[IA_GOFO_MSG_LOG_MAX_PARAMS]; --}; -- --struct ia_gofo_msg_log_info_ts { -- u64 msg_ts; -- struct ia_gofo_msg_log_info log_info; --}; -- --struct ia_gofo_msg_log { -- struct ia_gofo_msg_header header; -- struct ia_gofo_msg_log_info_ts log_info_ts; --}; -- --#pragma pack(pop) -- --#define IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID (0U) --#define IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID (1U) --#define IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID (2U) -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -deleted file mode 100644 -index c3f62aaedd86..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -+++ /dev/null -@@ -1,19 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2021 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_CONFIG_ABI_H --#define IPU7_FW_CONFIG_ABI_H -- --#include -- --#define IPU_CONFIG_ABI_WDT_TIMER_DISABLED 0U --#define IPU_CONFIG_ABI_CMD_TIMER_DISABLED 0U -- --struct ipu7_wdt_abi { -- u32 wdt_timer1_us; -- u32 wdt_timer2_us; --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -deleted file mode 100644 -index f161a605c500..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -+++ /dev/null -@@ -1,19 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2021 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_INSYS_CONFIG_ABI_H --#define IPU7_FW_INSYS_CONFIG_ABI_H -- --#include "ipu7_fw_boot_abi.h" --#include "ipu7_fw_config_abi.h" --#include "ipu7_fw_isys_abi.h" -- --struct ipu7_insys_config { -- u32 timeout_val_ms; -- struct ia_gofo_logger_config logger_config; -- struct ipu7_wdt_abi wdt_config; --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -deleted file mode 100644 -index 45db85eb13ec..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -+++ /dev/null -@@ -1,459 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_ISYS_ABI_H --#define IPU7_FW_ISYS_ABI_H -- --#include "ipu7_fw_common_abi.h" --#include "ipu7_fw_isys_abi.h" -- --#define IPU_INSYS_MAX_OUTPUT_QUEUES (3U) --#define IPU_INSYS_STREAM_ID_MAX (16U) -- --#define IPU_INSYS_MAX_INPUT_QUEUES (IPU_INSYS_STREAM_ID_MAX + 1U) --#define IPU_INSYS_OUTPUT_FIRST_QUEUE (0U) --#define IPU_INSYS_OUTPUT_LAST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES - 1U) --#define IPU_INSYS_OUTPUT_MSG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE) --#define IPU_INSYS_OUTPUT_LOG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE + 1U) --#define IPU_INSYS_OUTPUT_RESERVED_QUEUE (IPU_INSYS_OUTPUT_LAST_QUEUE) --#define IPU_INSYS_INPUT_FIRST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES) --#define IPU_INSYS_INPUT_LAST_QUEUE \ -- (IPU_INSYS_INPUT_FIRST_QUEUE + IPU_INSYS_MAX_INPUT_QUEUES - 1U) --#define IPU_INSYS_INPUT_DEV_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE) --#define IPU_INSYS_INPUT_MSG_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE + 1U) --#define IPU_INSYS_INPUT_MSG_MAX_QUEUE (IPU_INSYS_MAX_INPUT_QUEUES - 1U) -- --#define MAX_OPINS_FOR_SINGLE_IPINS (3U) --#define DEV_SEND_QUEUE_SIZE (IPU_INSYS_STREAM_ID_MAX) -- --#define PIN_PLANES_MAX (4U) -- --#define INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_INPUT \ -- INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES -- --typedef u64 ipu7_insys_return_token; -- --enum ipu7_insys_resp_type { -- IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0, -- IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1, -- IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2, -- IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3, -- IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4, -- IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5, -- IPU_INSYS_RESP_TYPE_PIN_DATA_READY = 6, -- IPU_INSYS_RESP_TYPE_FRAME_SOF = 7, -- IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, -- IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, -- IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, -- IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, -- N_IPU_INSYS_RESP_TYPE --}; -- --enum ipu7_insys_send_type { -- IPU_INSYS_SEND_TYPE_STREAM_OPEN = 0, -- IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1, -- IPU_INSYS_SEND_TYPE_STREAM_CAPTURE = 2, -- IPU_INSYS_SEND_TYPE_STREAM_ABORT = 3, -- IPU_INSYS_SEND_TYPE_STREAM_FLUSH = 4, -- IPU_INSYS_SEND_TYPE_STREAM_CLOSE = 5, -- N_IPU_INSYS_SEND_TYPE --}; -- --enum ipu7_insys_mipi_vc { -- IPU_INSYS_MIPI_VC_0 = 0, -- IPU_INSYS_MIPI_VC_1 = 1, -- IPU_INSYS_MIPI_VC_2 = 2, -- IPU_INSYS_MIPI_VC_3 = 3, -- IPU_INSYS_MIPI_VC_4 = 4, -- IPU_INSYS_MIPI_VC_5 = 5, -- IPU_INSYS_MIPI_VC_6 = 6, -- IPU_INSYS_MIPI_VC_7 = 7, -- IPU_INSYS_MIPI_VC_8 = 8, -- IPU_INSYS_MIPI_VC_9 = 9, -- IPU_INSYS_MIPI_VC_10 = 10, -- IPU_INSYS_MIPI_VC_11 = 11, -- IPU_INSYS_MIPI_VC_12 = 12, -- IPU_INSYS_MIPI_VC_13 = 13, -- IPU_INSYS_MIPI_VC_14 = 14, -- IPU_INSYS_MIPI_VC_15 = 15, -- N_IPU_INSYS_MIPI_VC --}; -- --enum ipu7_insys_mipi_port { -- IPU_INSYS_MIPI_PORT_0 = 0, -- IPU_INSYS_MIPI_PORT_1 = 1, -- IPU_INSYS_MIPI_PORT_2 = 2, -- IPU_INSYS_MIPI_PORT_3 = 3, -- IPU_INSYS_MIPI_PORT_4 = 4, -- IPU_INSYS_MIPI_PORT_5 = 5, -- NA_IPU_INSYS_MIPI_PORT --}; -- --enum ipu7_insys_frame_format_type { -- IPU_INSYS_FRAME_FORMAT_NV11 = 0, -- IPU_INSYS_FRAME_FORMAT_NV12 = 1, -- IPU_INSYS_FRAME_FORMAT_NV12_16 = 2, -- IPU_INSYS_FRAME_FORMAT_NV12_TILEY = 3, -- IPU_INSYS_FRAME_FORMAT_NV16 = 4, -- IPU_INSYS_FRAME_FORMAT_NV21 = 5, -- IPU_INSYS_FRAME_FORMAT_NV61 = 6, -- IPU_INSYS_FRAME_FORMAT_YV12 = 7, -- IPU_INSYS_FRAME_FORMAT_YV16 = 8, -- IPU_INSYS_FRAME_FORMAT_YUV420 = 9, -- IPU_INSYS_FRAME_FORMAT_YUV420_10 = 10, -- IPU_INSYS_FRAME_FORMAT_YUV420_12 = 11, -- IPU_INSYS_FRAME_FORMAT_YUV420_14 = 12, -- IPU_INSYS_FRAME_FORMAT_YUV420_16 = 13, -- IPU_INSYS_FRAME_FORMAT_YUV422 = 14, -- IPU_INSYS_FRAME_FORMAT_YUV422_16 = 15, -- IPU_INSYS_FRAME_FORMAT_UYVY = 16, -- IPU_INSYS_FRAME_FORMAT_YUYV = 17, -- IPU_INSYS_FRAME_FORMAT_YUV444 = 18, -- IPU_INSYS_FRAME_FORMAT_YUV_LINE = 19, -- IPU_INSYS_FRAME_FORMAT_RAW8 = 20, -- IPU_INSYS_FRAME_FORMAT_RAW10 = 21, -- IPU_INSYS_FRAME_FORMAT_RAW12 = 22, -- IPU_INSYS_FRAME_FORMAT_RAW14 = 23, -- IPU_INSYS_FRAME_FORMAT_RAW16 = 24, -- IPU_INSYS_FRAME_FORMAT_RGB565 = 25, -- IPU_INSYS_FRAME_FORMAT_PLANAR_RGB888 = 26, -- IPU_INSYS_FRAME_FORMAT_RGBA888 = 27, -- IPU_INSYS_FRAME_FORMAT_QPLANE6 = 28, -- IPU_INSYS_FRAME_FORMAT_BINARY_8 = 29, -- IPU_INSYS_FRAME_FORMAT_Y_8 = 30, -- IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, -- IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, -- IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, -- N_IPU_INSYS_FRAME_FORMAT --}; -- --#define IPU_INSYS_FRAME_FORMAT_RAW (IPU_INSYS_FRAME_FORMAT_RAW16) --#define N_IPU_INSYS_MIPI_DATA_TYPE 0x40 -- --enum ipu7_insys_mipi_dt_rename_mode { -- IPU_INSYS_MIPI_DT_NO_RENAME = 0, -- IPU_INSYS_MIPI_DT_RENAMED_MODE = 1, -- N_IPU_INSYS_MIPI_DT_MODE --}; -- --#define IPU_INSYS_SEND_MSG_ENABLED 1U --#define IPU_INSYS_SEND_MSG_DISABLED 0U -- --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6) --#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7) --#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_RESP ( \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED) --#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_IRQ ( \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED | \ -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED) -- --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7) --#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8) --#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9) --#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK) --#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \ -- IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK) -- --#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0) --#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1) --#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2) --#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3) --#define IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4) --#define IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5) --#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \ -- IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \ -- IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \ -- IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY) --#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \ -- IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \ -- IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \ -- IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY) -- --enum ipu7_insys_output_link_dest { -- IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, -- IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, -- IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 --}; -- --enum ipu7_insys_dpcm_type { -- IPU_INSYS_DPCM_TYPE_DISABLED = 0, -- IPU_INSYS_DPCM_TYPE_10_8_10 = 1, -- IPU_INSYS_DPCM_TYPE_12_8_12 = 2, -- IPU_INSYS_DPCM_TYPE_12_10_12 = 3, -- N_IPU_INSYS_DPCM_TYPE --}; -- --enum ipu7_insys_dpcm_predictor { -- IPU_INSYS_DPCM_PREDICTOR_1 = 0, -- IPU_INSYS_DPCM_PREDICTOR_2 = 1, -- N_IPU_INSYS_DPCM_PREDICTOR --}; -- --enum ipu7_insys_send_queue_token_flag { -- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, -- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 --}; -- --#pragma pack(push, 1) --struct ipu7_insys_resolution { -- u32 width; -- u32 height; --}; -- --struct ipu7_insys_capture_output_pin_payload { -- u64 user_token; -- ia_gofo_addr_t addr; -- u8 pad[4]; --}; -- --struct ipu7_insys_output_link { -- u32 buffer_lines; -- u16 foreign_key; -- u16 granularity_pointer_update; -- u8 msg_link_streaming_mode; -- u8 pbk_id; -- u8 pbk_slot_id; -- u8 dest; -- u8 use_sw_managed; -- u8 is_snoop; -- u8 pad[2]; --}; -- --struct ipu7_insys_output_cropping { -- u16 line_top; -- u16 line_bottom; --#ifdef IPU8_INSYS_NEW_ABI -- u16 column_left; -- u16 column_right; --#endif --}; -- --struct ipu7_insys_output_dpcm { -- u8 enable; -- u8 type; -- u8 predictor; -- u8 pad; --}; -- --#ifdef IPU8_INSYS_NEW_ABI --enum ipu_insys_cfa_dim { -- IPU_INSYS_CFA_DIM_2x2 = 0, -- IPU_INSYS_CFA_DIM_4x4 = 1, -- N_IPU_INSYS_CFA_DIM --}; -- --#define IPU_INSYS_MAX_BINNING_FACTOR (4U) --#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) --#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) --#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) --#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) -- --struct ipu7_insys_upipe_output_pin { -- ia_gofo_addr_t opaque_pin_cfg; -- u16 plane_offset_1; -- u16 plane_offset_2; -- u8 single_uob_fifo; -- u8 shared_uob_fifo; -- u8 pad[2]; --}; -- --struct ipu7_insys_capture_output_pin_cfg { -- struct ipu7_insys_capture_output_pin_payload pin_payload; -- ia_gofo_addr_t upipe_capture_cfg; --}; -- --#endif --struct ipu7_insys_output_pin { -- struct ipu7_insys_output_link link; -- struct ipu7_insys_output_cropping crop; -- struct ipu7_insys_output_dpcm dpcm; --#ifdef IPU8_INSYS_NEW_ABI -- struct ipu7_insys_upipe_output_pin upipe_pin_cfg; --#endif -- u32 stride; -- u16 ft; --#ifdef IPU8_INSYS_NEW_ABI -- u8 upipe_enable; --#endif -- u8 send_irq; -- u8 input_pin_id; -- u8 early_ack_en; --#ifdef IPU8_INSYS_NEW_ABI -- u8 cfa_dim; -- u8 binning_factor; --#else -- u8 pad[3]; --#endif --}; -- --struct ipu7_insys_input_pin { -- struct ipu7_insys_resolution input_res; -- u16 sync_msg_map; -- u8 dt; -- u8 disable_mipi_unpacking; -- u8 dt_rename_mode; -- u8 mapped_dt; -- u8 pad[2]; --}; -- --struct ipu7_insys_stream_cfg { -- struct ipu7_insys_input_pin input_pins[4]; -- struct ipu7_insys_output_pin output_pins[4]; -- u16 stream_msg_map; -- u8 port_id; -- u8 vc; -- u8 nof_input_pins; -- u8 nof_output_pins; -- u8 pad[2]; --}; -- --struct ipu7_insys_buffset { --#ifdef IPU8_INSYS_NEW_ABI -- struct ipu7_insys_capture_output_pin_cfg output_pins[4]; --#else -- struct ipu7_insys_capture_output_pin_payload output_pins[4]; --#endif -- u8 capture_msg_map; -- u8 frame_id; -- u8 skip_frame; -- u8 pad[5]; --}; -- --struct ipu7_insys_resp { -- u64 buf_id; -- struct ipu7_insys_capture_output_pin_payload pin; -- struct ia_gofo_msg_err error_info; -- u32 timestamp[2]; -- u8 type; -- u8 msg_link_streaming_mode; -- u8 stream_id; -- u8 pin_id; -- u8 frame_id; -- u8 skip_frame; -- u8 pad[2]; --}; -- --struct ipu7_insys_resp_queue_token { -- struct ipu7_insys_resp resp_info; --}; -- --struct ipu7_insys_send_queue_token { -- u64 buf_handle; -- ia_gofo_addr_t addr; -- u16 stream_id; -- u8 send_type; -- u8 flag; --}; -- --#pragma pack(pop) -- --enum insys_msg_err_stream { -- INSYS_MSG_ERR_STREAM_OK = IA_GOFO_MSG_ERR_OK, -- INSYS_MSG_ERR_STREAM_STREAM_ID = 1, -- INSYS_MSG_ERR_STREAM_MAX_OPINS = 2, -- INSYS_MSG_ERR_STREAM_MAX_IPINS = 3, -- INSYS_MSG_ERR_STREAM_STREAM_MESSAGES_MAP = 4, -- INSYS_MSG_ERR_STREAM_SYNC_MESSAGES_MAP = 5, -- INSYS_MSG_ERR_STREAM_SENSOR_TYPE = 6, -- INSYS_MSG_ERR_STREAM_FOREIGN_KEY = 7, -- INSYS_MSG_ERR_STREAM_STREAMING_MODE = 8, -- INSYS_MSG_ERR_STREAM_DPCM_EN = 9, -- INSYS_MSG_ERR_STREAM_DPCM_TYPE = 10, -- INSYS_MSG_ERR_STREAM_DPCM_PREDICTOR = 11, -- INSYS_MSG_ERR_STREAM_GRANULARITY_POINTER_UPDATE = 12, -- INSYS_MSG_ERR_STREAM_MPF_LUT_ENTRY_RESOURCES_BUSY = 13, -- INSYS_MSG_ERR_STREAM_MPF_DEV_ID = 14, -- INSYS_MSG_ERR_STREAM_BUFFER_LINES = 15, -- INSYS_MSG_ERR_STREAM_IPIN_ID = 16, -- INSYS_MSG_ERR_STREAM_DATA_TYPE = 17, -- INSYS_MSG_ERR_STREAM_STREAMING_PROTOCOL_STATE = 18, -- INSYS_MSG_ERR_STREAM_SYSCOM_FLUSH = 19, -- INSYS_MSG_ERR_STREAM_MIPI_VC = 20, -- INSYS_MSG_ERR_STREAM_STREAM_SRC = 21, -- INSYS_MSG_ERR_STREAM_PBK_ID = 22, -- INSYS_MSG_ERR_STREAM_CMD_QUEUE_DEALLOCATE = 23, -- INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES = 24, -- INSYS_MSG_ERR_STREAM_IPIN_CONFIGURATION = 25, -- INSYS_MSG_ERR_STREAM_INVALID_STATE = 26, -- INSYS_MSG_ERR_STREAM_SW_MANAGED = 27, -- INSYS_MSG_ERR_STREAM_PBK_SLOT_ID = 28, -- INSYS_MSG_ERR_STREAM_FLUSH_TIMEOUT = 29, -- INSYS_MSG_ERR_STREAM_IPIN_WIDTH = 30, -- INSYS_MSG_ERR_STREAM_IPIN_HEIGHT = 31, -- INSYS_MSG_ERR_STREAM_OUTPUT_PIN_EARLY_ACK_EN = 32, -- INSYS_MSG_ERR_STREAM_INCONSISTENT_PARAMS = 33, -- INSYS_MSG_ERR_STREAM_PLANE_COUNT = 34, -- INSYS_MSG_ERR_STREAM_FRAME_FORMAT_TYPE = 35, -- INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36, -- INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37, -- INSYS_MSG_ERR_STREAM_CLOSED = 38, -- INSYS_MSG_ERR_STREAM_N --}; -- --enum insys_msg_err_capture { -- INSYS_MSG_ERR_CAPTURE_OK = IA_GOFO_MSG_ERR_OK, -- INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1, -- INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2, -- INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3, -- INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4, -- INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5, -- INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6, -- INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7, -- INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8, -- INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9, -- INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10, -- INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11, -- INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12, -- INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20, -- INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21, -- INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22, -- INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23, -- INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24, -- INSYS_MSG_ERR_CAPTURE_N --}; -- --enum insys_msg_err_groups { -- INSYS_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -- INSYS_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -- INSYS_MSG_ERR_GROUP_STREAM = 2, -- INSYS_MSG_ERR_GROUP_CAPTURE = 3, -- INSYS_MSG_ERR_GROUP_N, --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -deleted file mode 100644 -index 8a78dd0936df..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -+++ /dev/null -@@ -1,465 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_MSG_ABI_H --#define IPU7_FW_MSG_ABI_H -- --#include "ipu7_fw_common_abi.h" -- --#pragma pack(push, 1) --enum ipu7_msg_type { -- IPU_MSG_TYPE_RESERVED = IA_GOFO_MSG_TYPE_RESERVED, -- IPU_MSG_TYPE_INDIRECT = IA_GOFO_MSG_TYPE_INDIRECT, -- IPU_MSG_TYPE_DEV_LOG = IA_GOFO_MSG_TYPE_LOG, -- IPU_MSG_TYPE_GENERAL_ERR = IA_GOFO_MSG_TYPE_GENERAL_ERR, -- IPU_MSG_TYPE_DEV_OPEN = 4, -- IPU_MSG_TYPE_DEV_OPEN_ACK = 5, -- IPU_MSG_TYPE_GRAPH_OPEN = 6, -- IPU_MSG_TYPE_GRAPH_OPEN_ACK = 7, -- IPU_MSG_TYPE_TASK_REQ = 8, -- IPU_MSG_TYPE_TASK_DONE = 9, -- IPU_MSG_TYPE_GRAPH_CLOSE = 10, -- IPU_MSG_TYPE_GRAPH_CLOSE_ACK = 11, -- IPU_MSG_TYPE_DEV_CLOSE = 12, -- IPU_MSG_TYPE_DEV_CLOSE_ACK = 13, -- IPU_MSG_TYPE_TERM_EVENT = 14, -- IPU_MSG_TYPE_N, --}; -- --#define IPU_MSG_MAX_NODE_TERMS (64U) --#define IPU_MSG_MAX_FRAGS (7U) -- --enum ipu7_msg_node_type { -- IPU_MSG_NODE_TYPE_PAD = 0, -- IPU_MSG_NODE_TYPE_BASE, -- IPU_MSG_NODE_TYPE_N --}; -- --#define IPU_MSG_NODE_MAX_DEVICES (128U) --#define DEB_NUM_UINT32 (IPU_MSG_NODE_MAX_DEVICES / (sizeof(u32) * 8U)) -- --typedef u32 ipu7_msg_teb_t[2]; --typedef u32 ipu7_msg_deb_t[DEB_NUM_UINT32]; -- --#define IPU_MSG_NODE_MAX_ROUTE_ENABLES (128U) --#define RBM_NUM_UINT32 (IPU_MSG_NODE_MAX_ROUTE_ENABLES / (sizeof(u32) * 8U)) -- --typedef u32 ipu7_msg_rbm_t[RBM_NUM_UINT32]; -- --enum ipu7_msg_node_profile_type { -- IPU_MSG_NODE_PROFILE_TYPE_PAD = 0, -- IPU_MSG_NODE_PROFILE_TYPE_BASE, -- IPU_MSG_NODE_PROFILE_TYPE_CB, -- IPU_MSG_NODE_PROFILE_TYPE_N --}; -- --struct ipu7_msg_node_profile { -- struct ia_gofo_tlv_header tlv_header; -- ipu7_msg_teb_t teb; --}; -- --struct ipu7_msg_cb_profile { -- struct ipu7_msg_node_profile profile_base; -- ipu7_msg_deb_t deb; -- ipu7_msg_rbm_t rbm; -- ipu7_msg_rbm_t reb; --}; -- --#define IPU_MSG_NODE_MAX_PROFILES (2U) --#define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) --#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) -- --#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) --#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) --#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) -- --struct ipu7_msg_node { -- struct ia_gofo_tlv_header tlv_header; -- u8 node_rsrc_id; -- u8 node_ctx_id; -- u8 num_frags; -- u8 reserved[1]; -- struct ia_gofo_tlv_list profiles_list; -- struct ia_gofo_tlv_list terms_list; -- struct ia_gofo_tlv_list node_options; --}; -- --enum ipu7_msg_node_option_types { -- IPU_MSG_NODE_OPTION_TYPES_PADDING = 0, -- IPU_MSG_NODE_OPTION_TYPES_N --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) -- --enum ipu7_msg_link_type { -- IPU_MSG_LINK_TYPE_PAD = 0, -- IPU_MSG_LINK_TYPE_GENERIC = 1, -- IPU_MSG_LINK_TYPE_N --}; -- --enum ipu7_msg_link_option_types { -- IPU_MSG_LINK_OPTION_TYPES_PADDING = 0, -- IPU_MSG_LINK_OPTION_TYPES_CMPRS = 1, -- IPU_MSG_LINK_OPTION_TYPES_N --}; -- --enum ipu7_msg_link_cmprs_option_bit_depth { -- IPU_MSG_LINK_CMPRS_OPTION_8BPP = 0, -- IPU_MSG_LINK_CMPRS_OPTION_10BPP = 1, -- IPU_MSG_LINK_CMPRS_OPTION_12BPP = 2, --}; -- --#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM (128U) --#define IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE (5U) --#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_NUM_MAX \ -- (IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM - 1U) -- --struct ipu7_msg_link_cmprs_plane_desc { -- u8 plane_enable; -- u8 cmprs_enable; -- u8 encoder_plane_id; -- u8 decoder_plane_id; -- u8 cmprs_is_lossy; -- u8 cmprs_is_footprint; -- u8 bit_depth; -- u8 space_saving_numerator; -- u32 pixels_offset; -- u32 ts_offset; -- u32 tile_row_to_tile_row_stride; -- u32 rows_of_tiles; -- u32 lossy_cfg[IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE]; --}; -- --#define IPU_MSG_LINK_CMPRS_MAX_PLANES (2U) --#define IPU_MSG_LINK_CMPRS_NO_ALIGN_INTERVAL (0U) --#define IPU_MSG_LINK_CMPRS_MIN_ALIGN_INTERVAL (16U) --#define IPU_MSG_LINK_CMPRS_MAX_ALIGN_INTERVAL (1024U) --struct ipu7_msg_link_cmprs_option { -- struct ia_gofo_tlv_header header; -- u32 cmprs_buf_size; -- u16 align_interval; -- u8 reserved[2]; -- struct ipu7_msg_link_cmprs_plane_desc plane_descs[2]; --}; -- --struct ipu7_msg_link_ep { -- u8 node_ctx_id; -- u8 term_id; --}; -- --struct ipu7_msg_link_ep_pair { -- struct ipu7_msg_link_ep ep_src; -- struct ipu7_msg_link_ep ep_dst; --}; -- --#define IPU_MSG_LINK_FOREIGN_KEY_NONE (65535U) --#define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) --#define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) --#define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) --#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) -- --struct ipu7_msg_link { -- struct ia_gofo_tlv_header tlv_header; -- struct ipu7_msg_link_ep_pair endpoints; -- u16 foreign_key; -- u8 streaming_mode; -- u8 pbk_id; -- u8 pbk_slot_id; -- u8 delayed_link; -- u8 reserved[2]; -- struct ia_gofo_tlv_list link_options; --}; -- --#pragma pack(pop) -- --enum ipu7_msg_dev_state { -- IPU_MSG_DEV_STATE_CLOSED = 0, -- IPU_MSG_DEV_STATE_OPEN_WAIT = 1, -- IPU_MSG_DEV_STATE_OPEN = 2, -- IPU_MSG_DEV_STATE_CLOSE_WAIT = 3, -- IPU_MSG_DEV_STATE_N --}; -- --enum ipu7_msg_graph_state { -- IPU_MSG_GRAPH_STATE_CLOSED = 0, -- IPU_MSG_GRAPH_STATE_OPEN_WAIT = 1, -- IPU_MSG_GRAPH_STATE_OPEN = 2, -- IPU_MSG_GRAPH_STATE_CLOSE_WAIT = 3, -- IPU_MSG_GRAPH_STATE_N --}; -- --enum ipu7_msg_task_state { -- IPU_MSG_TASK_STATE_DONE = 0, -- IPU_MSG_TASK_STATE_WAIT_DONE = 1, -- IPU_MSG_TASK_STATE_N --}; -- --enum ipu7_msg_err_groups { -- IPU_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -- IPU_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -- IPU_MSG_ERR_GROUP_DEVICE = 2, -- IPU_MSG_ERR_GROUP_GRAPH = 3, -- IPU_MSG_ERR_GROUP_TASK = 4, -- IPU_MSG_ERR_GROUP_N, --}; -- --#pragma pack(push, 1) --struct ipu7_msg_task { -- struct ia_gofo_msg_header header; -- u8 graph_id; -- u8 profile_idx; -- u8 node_ctx_id; -- u8 frame_id; -- u8 frag_id; -- u8 req_done_msg; -- u8 req_done_irq; -- u8 reserved[1]; -- ipu7_msg_teb_t payload_reuse_bm; -- ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; --}; -- --struct ipu7_msg_task_done { -- struct ia_gofo_msg_header_ack header; -- u8 graph_id; -- u8 frame_id; -- u8 node_ctx_id; -- u8 profile_idx; -- u8 frag_id; -- u8 reserved[3]; --}; -- --enum ipu7_msg_err_task { -- IPU_MSG_ERR_TASK_OK = IA_GOFO_MSG_ERR_OK, -- IPU_MSG_ERR_TASK_GRAPH_ID = 1, -- IPU_MSG_ERR_TASK_NODE_CTX_ID = 2, -- IPU_MSG_ERR_TASK_PROFILE_IDX = 3, -- IPU_MSG_ERR_TASK_CTX_MEMORY_TASK = 4, -- IPU_MSG_ERR_TASK_TERM_PAYLOAD_PTR = 5, -- IPU_MSG_ERR_TASK_FRAME_ID = 6, -- IPU_MSG_ERR_TASK_FRAG_ID = 7, -- IPU_MSG_ERR_TASK_EXEC_EXT = 8, -- IPU_MSG_ERR_TASK_EXEC_SBX = 9, -- IPU_MSG_ERR_TASK_EXEC_INT = 10, -- IPU_MSG_ERR_TASK_EXEC_UNKNOWN = 11, -- IPU_MSG_ERR_TASK_PRE_EXEC = 12, -- IPU_MSG_ERR_TASK_N --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --enum ipu7_msg_term_type { -- IPU_MSG_TERM_TYPE_PAD = 0, -- IPU_MSG_TERM_TYPE_BASE, -- IPU_MSG_TERM_TYPE_N, --}; -- --#define IPU_MSG_TERM_EVENT_TYPE_NONE 0U --#define IPU_MSG_TERM_EVENT_TYPE_PROGRESS 1U --#define IPU_MSG_TERM_EVENT_TYPE_N (IPU_MSG_TERM_EVENT_TYPE_PROGRESS + 1U) -- --struct ipu7_msg_term { -- struct ia_gofo_tlv_header tlv_header; -- u8 term_id; -- u8 event_req_bm; -- u8 reserved[2]; -- u32 payload_size; -- struct ia_gofo_tlv_list term_options; --}; -- --enum ipu7_msg_term_option_types { -- IPU_MSG_TERM_OPTION_TYPES_PADDING = 0, -- IPU_MSG_TERM_OPTION_TYPES_N --}; -- --struct ipu7_msg_term_event { -- struct ia_gofo_msg_header header; -- u8 graph_id; -- u8 frame_id; -- u8 node_ctx_id; -- u8 profile_idx; -- u8 frag_id; -- u8 term_id; -- u8 event_type; -- u8 reserved[1]; -- u64 event_ts; --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --#define IPU_MSG_DEVICE_SEND_MSG_ENABLED 1U --#define IPU_MSG_DEVICE_SEND_MSG_DISABLED 0U -- --#define IPU_MSG_DEVICE_OPEN_SEND_RESP BIT(0) --#define IPU_MSG_DEVICE_OPEN_SEND_IRQ BIT(1) -- --#define IPU_MSG_DEVICE_CLOSE_SEND_RESP BIT(0) --#define IPU_MSG_DEVICE_CLOSE_SEND_IRQ BIT(1) -- --struct ipu7_msg_dev_open { -- struct ia_gofo_msg_header header; -- u32 max_graphs; -- u8 dev_msg_map; -- u8 enable_power_gating; -- u8 reserved[2]; --}; -- --struct ipu7_msg_dev_open_ack { -- struct ia_gofo_msg_header_ack header; --}; -- --struct ipu7_msg_dev_close { -- struct ia_gofo_msg_header header; -- u8 dev_msg_map; -- u8 reserved[7]; --}; -- --struct ipu7_msg_dev_close_ack { -- struct ia_gofo_msg_header_ack header; --}; -- --enum ipu7_msg_err_device { -- IPU_MSG_ERR_DEVICE_OK = IA_GOFO_MSG_ERR_OK, -- IPU_MSG_ERR_DEVICE_MAX_GRAPHS = 1, -- IPU_MSG_ERR_DEVICE_MSG_MAP = 2, -- IPU_MSG_ERR_DEVICE_N --}; -- --#pragma pack(pop) -- --#pragma pack(push, 1) --#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) --#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U --#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U -- --#define IPU_MSG_GRAPH_OPEN_SEND_RESP BIT(0) --#define IPU_MSG_GRAPH_OPEN_SEND_IRQ BIT(1) -- --#define IPU_MSG_GRAPH_CLOSE_SEND_RESP BIT(0) --#define IPU_MSG_GRAPH_CLOSE_SEND_IRQ BIT(1) -- --struct ipu7_msg_graph_open { -- struct ia_gofo_msg_header header; -- struct ia_gofo_tlv_list nodes; -- struct ia_gofo_tlv_list links; -- u8 graph_id; -- u8 graph_msg_map; -- u8 reserved[6]; --}; -- --enum ipu7_msg_graph_ack_option_types { -- IPU_MSG_GRAPH_ACK_OPTION_TYPES_PADDING = 0, -- IPU_MSG_GRAPH_ACK_TASK_Q_INFO, -- IPU_MSG_GRAPH_ACK_OPTION_TYPES_N --}; -- --struct ipu7_msg_graph_open_ack_task_q_info { -- struct ia_gofo_tlv_header header; -- u8 node_ctx_id; -- u8 q_id; -- u8 reserved[2]; --}; -- --struct ipu7_msg_graph_open_ack { -- struct ia_gofo_msg_header_ack header; -- u8 graph_id; -- u8 reserved[7]; --}; -- --struct ipu7_msg_graph_close { -- struct ia_gofo_msg_header header; -- u8 graph_id; -- u8 graph_msg_map; -- u8 reserved[6]; --}; -- --struct ipu7_msg_graph_close_ack { -- struct ia_gofo_msg_header_ack header; -- u8 graph_id; -- u8 reserved[7]; --}; -- --enum ipu7_msg_err_graph { -- IPU_MSG_ERR_GRAPH_OK = IA_GOFO_MSG_ERR_OK, -- IPU_MSG_ERR_GRAPH_GRAPH_STATE = 1, -- IPU_MSG_ERR_GRAPH_MAX_GRAPHS = 2, -- IPU_MSG_ERR_GRAPH_GRAPH_ID = 3, -- IPU_MSG_ERR_GRAPH_NODE_CTX_ID = 4, -- IPU_MSG_ERR_GRAPH_NODE_RSRC_ID = 5, -- IPU_MSG_ERR_GRAPH_PROFILE_IDX = 6, -- IPU_MSG_ERR_GRAPH_TERM_ID = 7, -- IPU_MSG_ERR_GRAPH_TERM_PAYLOAD_SIZE = 8, -- IPU_MSG_ERR_GRAPH_LINK_NODE_CTX_ID = 9, -- IPU_MSG_ERR_GRAPH_LINK_TERM_ID = 10, -- IPU_MSG_ERR_GRAPH_PROFILE_TYPE = 11, -- IPU_MSG_ERR_GRAPH_NUM_FRAGS = 12, -- IPU_MSG_ERR_GRAPH_QUEUE_ID_USAGE = 13, -- IPU_MSG_ERR_GRAPH_QUEUE_OPEN = 14, -- IPU_MSG_ERR_GRAPH_QUEUE_CLOSE = 15, -- IPU_MSG_ERR_GRAPH_QUEUE_ID_TASK_REQ_MISMATCH = 16, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_FGRAPH = 17, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE = 18, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE_PROFILE = 19, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_TERM = 20, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_LINK = 21, -- IPU_MSG_ERR_GRAPH_CTX_MSG_MAP = 22, -- IPU_MSG_ERR_GRAPH_CTX_FOREIGN_KEY = 23, -- IPU_MSG_ERR_GRAPH_CTX_STREAMING_MODE = 24, -- IPU_MSG_ERR_GRAPH_CTX_PBK_RSRC = 25, -- IPU_MSG_ERR_GRAPH_UNSUPPORTED_EVENT_TYPE = 26, -- IPU_MSG_ERR_GRAPH_TOO_MANY_EVENTS = 27, -- IPU_MSG_ERR_GRAPH_CTX_MEMORY_CMPRS = 28, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_ALIGN_INTERVAL = 29, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_PLANE_ID = 30, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_UNSUPPORTED_MODE = 31, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_BIT_DEPTH = 32, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_STRIDE_ALIGNMENT = 33, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_SUB_BUFFER_ALIGNMENT = 34, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_ORDER = 35, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_OVERLAP = 36, -- IPU_MSG_ERR_GRAPH_CTX_CMPRS_BUFFER_TOO_SMALL = 37, -- IPU_MSG_ERR_GRAPH_CTX_DELAYED_LINK = 38, -- IPU_MSG_ERR_GRAPH_N --}; -- --#pragma pack(pop) -- --#define FWPS_MSG_ABI_MAX_INPUT_QUEUES (60U) --#define FWPS_MSG_ABI_MAX_OUTPUT_QUEUES (2U) --#define FWPS_MSG_ABI_MAX_QUEUES \ -- (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES + FWPS_MSG_ABI_MAX_INPUT_QUEUES) -- --#define FWPS_MSG_ABI_OUT_ACK_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID) --#define FWPS_MSG_ABI_OUT_LOG_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID) --#if (FWPS_MSG_ABI_OUT_LOG_QUEUE_ID >= FWPS_MSG_ABI_MAX_OUTPUT_QUEUES) --#error "Maximum output queues configuration is too small to fit ACK and LOG \ --queues" --#endif --#define FWPS_MSG_ABI_IN_DEV_QUEUE_ID (IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID) --#define FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID (3U) --#define FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID \ -- (FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID + 1U) -- --#if (FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID >= FWPS_MSG_ABI_MAX_INPUT_QUEUES) --#error "Maximum queues configuration is too small to fit minimum number of \ --useful queues" --#endif -- --#define FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID (FWPS_MSG_ABI_MAX_QUEUES - 1U) --#define FWPS_MSG_ABI_IN_MAX_TASK_QUEUES \ -- (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID - \ -- FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID + 1U) --#define FWPS_MSG_ABI_OUT_FIRST_QUEUE_ID (FWPS_MSG_ABI_OUT_ACK_QUEUE_ID) --#define FWPS_MSG_ABI_OUT_LAST_QUEUE_ID (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES - 1U) --#define FWPS_MSG_ABI_IN_FIRST_QUEUE_ID (FWPS_MSG_ABI_IN_DEV_QUEUE_ID) --#define FWPS_MSG_ABI_IN_LAST_QUEUE_ID (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID) -- --#define FWPS_MSG_HOST2FW_MAX_SIZE (2U * 1024U) --#define FWPS_MSG_FW2HOST_MAX_SIZE (256U) -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -deleted file mode 100644 -index 0af04c8c6a88..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -+++ /dev/null -@@ -1,24 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ --#define IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ -- --#include -- --#include "ipu7_fw_boot_abi.h" --#include "ipu7_fw_config_abi.h" -- --struct ipu7_psys_config { -- u32 use_debug_manifest; -- u32 timeout_val_ms; -- u32 compression_support_enabled; -- struct ia_gofo_logger_config logger_config; -- struct ipu7_wdt_abi wdt_config; -- u8 ipu_psys_debug_bitmask; -- u8 padding[3]; --}; -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -deleted file mode 100644 -index bfa5258d5b97..000000000000 ---- a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -+++ /dev/null -@@ -1,49 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_SYSCOM_ABI_H --#define IPU7_FW_SYSCOM_ABI_H -- --#include -- --#include "ipu7_fw_common_abi.h" -- --#pragma pack(push, 1) --#define SYSCOM_QUEUE_MIN_CAPACITY 2U -- --struct syscom_queue_params_config { -- ia_gofo_addr_t token_array_mem; -- u16 token_size_in_bytes; -- u16 max_capacity; --}; -- --struct syscom_config_s { -- u16 max_output_queues; -- u16 max_input_queues; --}; -- --#pragma pack(pop) -- --static inline struct syscom_queue_params_config * --syscom_config_get_queue_configs(struct syscom_config_s *config) --{ -- return (struct syscom_queue_params_config *)(&config[1]); --} -- --static inline const struct syscom_queue_params_config * --syscom_config_get_queue_configs_const(const struct syscom_config_s *config) --{ -- return (const struct syscom_queue_params_config *)(&config[1]); --} -- --#pragma pack(push, 1) --struct syscom_queue_indices_s { -- u32 read_index; -- u32 write_index; --}; -- --#pragma pack(pop) -- --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.c b/drivers/media/pci/intel/ipu7/ipu7-boot.c -deleted file mode 100644 -index a88182d70173..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-boot.c -+++ /dev/null -@@ -1,431 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2022 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_boot_abi.h" -- --#include "ipu7.h" --#include "ipu7-boot.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-dma.h" --#include "ipu7-platform-regs.h" --#include "ipu7-syscom.h" -- --#define IPU_FW_START_STOP_TIMEOUT 2000 --#define IPU_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC) --#define BOOT_STATE_IS_CRITICAL(s) IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s) --#define BOOT_STATE_IS_READY(s) ((s) == IA_GOFO_FW_BOOT_STATE_READY) --#define BOOT_STATE_IS_INACTIVE(s) ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE) -- --struct ipu7_boot_context { -- u32 base; -- u32 dmem_address; -- u32 status_ctrl_reg; -- u32 fw_start_address_reg; -- u32 fw_code_base_reg; --}; -- --static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = { -- { -- /* ISYS */ -- .dmem_address = IPU_ISYS_DMEM_OFFSET, -- .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS, -- .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR, -- .fw_code_base_reg = IS_UC_CTRL_BASE -- }, -- { -- /* PSYS */ -- .dmem_address = IPU_PSYS_DMEM_OFFSET, -- .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS, -- .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR, -- .fw_code_base_reg = PS_UC_CTRL_BASE -- } --}; -- --static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev, -- enum ia_gofo_buttress_reg_id reg) --{ -- u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX; -- -- return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg); --} -- --static void write_fw_boot_param(const struct ipu7_bus_device *adev, -- enum ia_gofo_buttress_reg_id reg, -- u32 val) --{ -- void __iomem *base = adev->isp->base; -- -- dev_dbg(&adev->auxdev.dev, -- "write boot param reg: %d addr: %x val: 0x%x\n", -- reg, get_fw_boot_reg_addr(adev, reg), val); -- writel(val, base + get_fw_boot_reg_addr(adev, reg)); --} -- --static u32 read_fw_boot_param(const struct ipu7_bus_device *adev, -- enum ia_gofo_buttress_reg_id reg) --{ -- void __iomem *base = adev->isp->base; -- -- return readl(base + get_fw_boot_reg_addr(adev, reg)); --} -- --static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- const struct device *dev = &adev->auxdev.dev; -- u32 ucx_ctrl_status = ctx->status_ctrl_reg; -- u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT; -- void __iomem *base = adev->isp->base; -- u32 val, val2; -- int ret; -- -- dev_dbg(dev, "cell enter reset...\n"); -- val = readl(base + ucx_ctrl_status); -- dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n", -- ucx_ctrl_status, val); -- -- dev_dbg(dev, "force cell reset...\n"); -- val |= UCX_CTL_RESET; -- val &= ~UCX_CTL_RUN; -- -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ucx_ctrl_status, val); -- writel(val, base + ucx_ctrl_status); -- -- ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -- (val2 & 0x3U) == (val & 0x3U), 100, timeout); -- if (ret) { -- dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2); -- return -ETIMEDOUT; -- } -- -- dev_dbg(dev, "cell exit reset...\n"); -- val = readl(base + ucx_ctrl_status); -- WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN), -- "cell status 0x%x", val); -- -- val &= ~(UCX_CTL_RESET | UCX_CTL_RUN); -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ucx_ctrl_status, val); -- writel(val, base + ucx_ctrl_status); -- -- ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -- (val2 & 0x3U) == (val & 0x3U), 100, timeout); -- if (ret) { -- dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2); -- return -ETIMEDOUT; -- } -- -- return 0; --} -- --static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- void __iomem *base = adev->isp->base; -- const struct device *dev = &adev->auxdev.dev; -- u32 val; -- -- dev_dbg(dev, "starting cell...\n"); -- val = readl(base + ctx->status_ctrl_reg); -- WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN)); -- -- val &= ~UCX_CTL_RESET; -- val |= UCX_CTL_RUN; -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ctx->status_ctrl_reg, val); -- writel(val, base + ctx->status_ctrl_reg); --} -- --static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- void __iomem *base = adev->isp->base; -- const struct device *dev = &adev->auxdev.dev; -- u32 val; -- -- dev_dbg(dev, "stopping cell...\n"); -- -- val = readl(base + ctx->status_ctrl_reg); -- val &= ~UCX_CTL_RUN; -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ctx->status_ctrl_reg, val); -- writel(val, base + ctx->status_ctrl_reg); -- -- /* Wait for uC transactions complete */ -- usleep_range(10, 20); -- -- val = readl(base + ctx->status_ctrl_reg); -- val |= UCX_CTL_RESET; -- dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -- ctx->status_ctrl_reg, val); -- writel(val, base + ctx->status_ctrl_reg); --} -- --static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev) --{ -- const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -- void __iomem *base = adev->isp->base; -- -- dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n", -- ctx->fw_start_address_reg, adev->fw_entry); -- writel(adev->fw_entry, base + ctx->fw_start_address_reg); -- -- return ipu7_boot_cell_reset(adev); --} -- --static void init_boot_config(struct ia_gofo_boot_config *boot_config, -- u32 length, u8 major) --{ -- /* syscom version, new syscom2 version */ -- boot_config->length = length; -- boot_config->config_version.major = 1U; -- boot_config->config_version.minor = 0U; -- boot_config->config_version.subminor = 0U; -- boot_config->config_version.patch = 0U; -- -- /* msg version for task interface */ -- boot_config->client_version_support.num_versions = 1U; -- boot_config->client_version_support.versions[0].major = major; -- boot_config->client_version_support.versions[0].minor = 0U; -- boot_config->client_version_support.versions[0].subminor = 0U; -- boot_config->client_version_support.versions[0].patch = 0U; --} -- --int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -- struct syscom_queue_config *qconfigs, -- int num_queues, u32 uc_freq, -- dma_addr_t subsys_config, u8 major) --{ -- u32 total_queue_size_aligned = 0; -- struct ipu7_syscom_context *syscom = adev->syscom; -- struct ia_gofo_boot_config *boot_config; -- struct syscom_queue_params_config *cfgs; -- struct device *dev = &adev->auxdev.dev; -- struct syscom_config_s *syscfg; -- dma_addr_t queue_mem_dma_ptr; -- void *queue_mem_ptr; -- unsigned int i; -- -- dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n", -- num_queues, uc_freq, &subsys_config); -- /* Allocate boot config. */ -- adev->boot_config_size = -- sizeof(*cfgs) * num_queues + sizeof(*boot_config); -- adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size, -- &adev->boot_config_dma_addr, -- GFP_KERNEL, 0); -- if (!adev->boot_config) { -- dev_err(dev, "Failed to allocate boot config.\n"); -- return -ENOMEM; -- } -- -- boot_config = adev->boot_config; -- memset(boot_config, 0, sizeof(struct ia_gofo_boot_config)); -- init_boot_config(boot_config, adev->boot_config_size, major); -- boot_config->subsys_config = subsys_config; -- -- boot_config->uc_tile_frequency = uc_freq; -- boot_config->uc_tile_frequency_units = -- IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ; -- boot_config->syscom_context_config.max_output_queues = -- syscom->num_output_queues; -- boot_config->syscom_context_config.max_input_queues = -- syscom->num_input_queues; -- -- ipu7_dma_sync_single(adev, adev->boot_config_dma_addr, -- adev->boot_config_size); -- -- for (i = 0; i < num_queues; i++) { -- u32 queue_size = qconfigs[i].max_capacity * -- qconfigs[i].token_size_in_bytes; -- -- queue_size = ALIGN(queue_size, 64U); -- total_queue_size_aligned += queue_size; -- qconfigs[i].queue_size = queue_size; -- } -- -- /* Allocate queue memory */ -- syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned, -- &syscom->queue_mem_dma_addr, -- GFP_KERNEL, 0); -- if (!syscom->queue_mem) { -- dev_err(dev, "Failed to allocate queue memory.\n"); -- return -ENOMEM; -- } -- syscom->queue_mem_size = total_queue_size_aligned; -- -- syscfg = &boot_config->syscom_context_config; -- cfgs = ipu7_syscom_get_queue_config(syscfg); -- queue_mem_ptr = syscom->queue_mem; -- queue_mem_dma_ptr = syscom->queue_mem_dma_addr; -- for (i = 0; i < num_queues; i++) { -- cfgs[i].token_array_mem = queue_mem_dma_ptr; -- cfgs[i].max_capacity = qconfigs[i].max_capacity; -- cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes; -- qconfigs[i].token_array_mem = queue_mem_ptr; -- queue_mem_dma_ptr += qconfigs[i].queue_size; -- queue_mem_ptr += qconfigs[i].queue_size; -- } -- -- ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr, -- total_queue_size_aligned); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7"); -- --void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev) --{ -- struct ipu7_syscom_context *syscom = adev->syscom; -- -- if (syscom->queue_mem) { -- ipu7_dma_free(adev, syscom->queue_mem_size, -- syscom->queue_mem, -- syscom->queue_mem_dma_addr, 0); -- syscom->queue_mem = NULL; -- syscom->queue_mem_dma_addr = 0; -- } -- -- if (adev->boot_config) { -- ipu7_dma_free(adev, adev->boot_config_size, -- adev->boot_config, -- adev->boot_config_dma_addr, 0); -- adev->boot_config = NULL; -- adev->boot_config_dma_addr = 0; -- } --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7"); -- --int ipu7_boot_start_fw(const struct ipu7_bus_device *adev) --{ -- const struct device *dev = &adev->auxdev.dev; -- u32 timeout = IPU_FW_START_STOP_TIMEOUT; -- void __iomem *base = adev->isp->base; -- u32 boot_state, last_boot_state; -- u32 indices_addr, msg_ver, id; -- int ret; -- -- ret = ipu7_boot_cell_init(adev); -- if (ret) -- return ret; -- -- dev_dbg(dev, "start booting fw...\n"); -- /* store "uninit" state to syscom/boot state reg */ -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -- IA_GOFO_FW_BOOT_STATE_UNINIT); -- /* -- * Set registers to zero -- * (not strictly required, but recommended for diagnostics) -- */ -- write_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0); -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0); -- /* store firmware configuration address */ -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID, -- adev->boot_config_dma_addr); -- -- /* Kick uC, then wait for boot complete */ -- ipu7_boot_cell_start(adev); -- -- last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT; -- while (timeout--) { -- boot_state = read_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_STATE_ID); -- if (boot_state != last_boot_state) { -- dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n", -- last_boot_state, boot_state); -- last_boot_state = boot_state; -- } -- if (BOOT_STATE_IS_CRITICAL(boot_state) || -- BOOT_STATE_IS_READY(boot_state)) -- break; -- usleep_range(1000, 1200); -- } -- -- if (BOOT_STATE_IS_CRITICAL(boot_state)) { -- ipu7_dump_fw_error_log(adev); -- dev_err(dev, "critical boot state error 0x%x\n", boot_state); -- return -EINVAL; -- } else if (!BOOT_STATE_IS_READY(boot_state)) { -- dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state); -- return -ETIMEDOUT; -- } -- dev_dbg(dev, "fw boot done.\n"); -- -- /* Get FW syscom queue indices addr */ -- id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID; -- indices_addr = read_fw_boot_param(adev, id); -- adev->syscom->queue_indices = base + indices_addr; -- dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr); -- -- /* Get message version. */ -- msg_ver = read_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID); -- dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7"); -- --int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev) --{ -- const struct device *dev = &adev->auxdev.dev; -- u32 timeout = IPU_FW_START_STOP_TIMEOUT; -- u32 boot_state; -- -- boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); -- if (BOOT_STATE_IS_CRITICAL(boot_state) || -- !BOOT_STATE_IS_READY(boot_state)) { -- dev_err(dev, "fw not ready for shutdown, state 0x%x\n", -- boot_state); -- return -EBUSY; -- } -- -- /* Issue shutdown to start shutdown process */ -- dev_dbg(dev, "stopping fw...\n"); -- write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -- IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD); -- while (timeout--) { -- boot_state = read_fw_boot_param(adev, -- IA_GOFO_FW_BOOT_STATE_ID); -- if (BOOT_STATE_IS_CRITICAL(boot_state) || -- BOOT_STATE_IS_INACTIVE(boot_state)) -- break; -- usleep_range(1000, 1200); -- } -- -- if (BOOT_STATE_IS_CRITICAL(boot_state)) { -- ipu7_dump_fw_error_log(adev); -- dev_err(dev, "critical boot state error 0x%x\n", boot_state); -- return -EINVAL; -- } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) { -- dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state); -- return -ETIMEDOUT; -- } -- -- ipu7_boot_cell_stop(adev); -- dev_dbg(dev, "stop fw done.\n"); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7"); -- --u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev) --{ -- return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); --} --EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.h b/drivers/media/pci/intel/ipu7/ipu7-boot.h -deleted file mode 100644 -index 5600be849931..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-boot.h -+++ /dev/null -@@ -1,25 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2022 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BOOT_H --#define IPU7_BOOT_H -- --#include -- --struct ipu7_bus_device; --struct syscom_queue_config; -- --#define FW_QUEUE_CONFIG_SIZE(num_queues) \ -- (sizeof(struct syscom_queue_config) * (num_queues)) -- --int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -- struct syscom_queue_config *qconfigs, -- int num_queues, u32 uc_freq, -- dma_addr_t subsys_config, u8 major); --void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev); --int ipu7_boot_start_fw(const struct ipu7_bus_device *adev); --int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev); --u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.c b/drivers/media/pci/intel/ipu7/ipu7-bus.c -deleted file mode 100644 -index 7da44fde002a..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-bus.c -+++ /dev/null -@@ -1,158 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-boot.h" --#include "ipu7-dma.h" -- --static int bus_pm_runtime_suspend(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- int ret; -- -- ret = pm_generic_runtime_suspend(dev); -- if (ret) -- return ret; -- -- ret = ipu_buttress_powerdown(dev, adev->ctrl); -- if (!ret) -- return 0; -- -- dev_err(dev, "power down failed!\n"); -- -- /* Powering down failed, attempt to resume device now */ -- ret = pm_generic_runtime_resume(dev); -- if (!ret) -- return -EBUSY; -- -- return -EIO; --} -- --static int bus_pm_runtime_resume(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- int ret; -- -- ret = ipu_buttress_powerup(dev, adev->ctrl); -- if (ret) -- return ret; -- -- ret = pm_generic_runtime_resume(dev); -- if (ret) -- goto out_err; -- -- return 0; -- --out_err: -- ipu_buttress_powerdown(dev, adev->ctrl); -- -- return -EBUSY; --} -- --static struct dev_pm_domain ipu7_bus_pm_domain = { -- .ops = { -- .runtime_suspend = bus_pm_runtime_suspend, -- .runtime_resume = bus_pm_runtime_resume, -- }, --}; -- --static DEFINE_MUTEX(ipu7_bus_mutex); --static void ipu7_bus_release(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- -- kfree(adev->pdata); -- kfree(adev); --} -- --struct ipu7_bus_device * --ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -- void *pdata, const struct ipu_buttress_ctrl *ctrl, -- const char *name) --{ -- struct auxiliary_device *auxdev; -- struct ipu7_bus_device *adev; -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- int ret; -- -- adev = kzalloc(sizeof(*adev), GFP_KERNEL); -- if (!adev) -- return ERR_PTR(-ENOMEM); -- -- adev->isp = isp; -- adev->ctrl = ctrl; -- adev->pdata = pdata; -- auxdev = &adev->auxdev; -- auxdev->name = name; -- auxdev->id = (pci_domain_nr(pdev->bus) << 16) | -- PCI_DEVID(pdev->bus->number, pdev->devfn); -- -- auxdev->dev.parent = parent; -- auxdev->dev.release = ipu7_bus_release; -- -- ret = auxiliary_device_init(auxdev); -- if (ret < 0) { -- dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", -- ret); -- kfree(adev); -- return ERR_PTR(ret); -- } -- -- dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain); -- -- pm_runtime_forbid(&adev->auxdev.dev); -- pm_runtime_enable(&adev->auxdev.dev); -- -- return adev; --} -- --int ipu7_bus_add_device(struct ipu7_bus_device *adev) --{ -- struct auxiliary_device *auxdev = &adev->auxdev; -- int ret; -- -- ret = auxiliary_device_add(auxdev); -- if (ret) { -- auxiliary_device_uninit(auxdev); -- return ret; -- } -- -- mutex_lock(&ipu7_bus_mutex); -- list_add(&adev->list, &adev->isp->devices); -- mutex_unlock(&ipu7_bus_mutex); -- -- pm_runtime_allow(&auxdev->dev); -- -- return 0; --} -- --void ipu7_bus_del_devices(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu7_bus_device *adev, *save; -- -- mutex_lock(&ipu7_bus_mutex); -- -- list_for_each_entry_safe(adev, save, &isp->devices, list) { -- pm_runtime_disable(&adev->auxdev.dev); -- list_del(&adev->list); -- auxiliary_device_delete(&adev->auxdev); -- auxiliary_device_uninit(&adev->auxdev); -- } -- -- mutex_unlock(&ipu7_bus_mutex); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.h b/drivers/media/pci/intel/ipu7/ipu7-bus.h -deleted file mode 100644 -index 45157df16e90..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-bus.h -+++ /dev/null -@@ -1,69 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BUS_H --#define IPU7_BUS_H -- --#include --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_boot_abi.h" -- --#include "ipu7-syscom.h" -- --struct pci_dev; --struct ipu_buttress_ctrl; --struct ipu7_mmu; --struct ipu7_device; -- --enum ipu7_subsys { -- IPU_IS = 0, -- IPU_PS = 1, -- IPU_SUBSYS_NUM = 2, --}; -- --struct ipu7_bus_device { -- struct auxiliary_device auxdev; -- const struct auxiliary_driver *auxdrv; -- const struct ipu7_auxdrv_data *auxdrv_data; -- struct list_head list; -- enum ipu7_subsys subsys; -- void *pdata; -- struct ipu7_mmu *mmu; -- struct ipu7_device *isp; -- const struct ipu_buttress_ctrl *ctrl; -- u64 dma_mask; -- struct sg_table fw_sgt; -- u32 fw_entry; -- struct ipu7_syscom_context *syscom; -- struct ia_gofo_boot_config *boot_config; -- dma_addr_t boot_config_dma_addr; -- u32 boot_config_size; --}; -- --struct ipu7_auxdrv_data { -- irqreturn_t (*isr)(struct ipu7_bus_device *adev); -- irqreturn_t (*isr_threaded)(struct ipu7_bus_device *adev); -- bool wake_isr_thread; --}; -- --#define to_ipu7_bus_device(_dev) \ -- container_of(to_auxiliary_dev(_dev), struct ipu7_bus_device, auxdev) --#define auxdev_to_adev(_auxdev) \ -- container_of(_auxdev, struct ipu7_bus_device, auxdev) --#define ipu7_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) -- --struct ipu7_bus_device * --ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -- void *pdata, const struct ipu_buttress_ctrl *ctrl, -- const char *name); --int ipu7_bus_add_device(struct ipu7_bus_device *adev); --void ipu7_bus_del_devices(struct pci_dev *pdev); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h b/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -deleted file mode 100644 -index 3eafd6a3813d..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -+++ /dev/null -@@ -1,461 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BUTTRESS_REGS_H --#define IPU7_BUTTRESS_REGS_H -- --#define BUTTRESS_REG_IRQ_STATUS 0x2000 --#define BUTTRESS_REG_IRQ_STATUS_UNMASKED 0x2004 --#define BUTTRESS_REG_IRQ_ENABLE 0x2008 --#define BUTTRESS_REG_IRQ_CLEAR 0x200c --#define BUTTRESS_REG_IRQ_MASK 0x2010 --#define BUTTRESS_REG_TSC_CMD 0x2014 --#define BUTTRESS_REG_TSC_CTL 0x2018 --#define BUTTRESS_REG_TSC_LO 0x201c --#define BUTTRESS_REG_TSC_HI 0x2020 -- --/* valid for PTL */ --#define BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030 --#define BUTTRESS_REG_PB_TIMESTAMP_HI 0x2034 --#define BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038 -- --#define BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100 --#define BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104 --#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ 0x2108 --#define BUTTRESS_REG_PS_DOMAINS_STATUS 0x2110 --#define BUTTRESS_REG_PWR_STATUS 0x2114 --#define BUTTRESS_REG_PS_WORKPOINT_REQ_SHADOW 0x2120 --#define BUTTRESS_REG_IS_WORKPOINT_REQ_SHADOW 0x2124 --#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ_SHADOW 0x2128 --#define BUTTRESS_REG_ISPS_WORKPOINT_DOWNLOAD 0x212c --#define BUTTRESS_REG_PG_FLOW_OVERRIDE 0x2180 --#define BUTTRESS_REG_GLOBAL_OVERRIDE_UNGATE_CTL 0x2184 --#define BUTTRESS_REG_PWR_FSM_CTL 0x2188 --#define BUTTRESS_REG_IDLE_WDT 0x218c --#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_EN 0x2190 --#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_ADDR 0x2194 --#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_DATA 0x2198 --#define BUTTRESS_REG_POWER_EN_DELAY 0x219c --#define IPU7_BUTTRESS_REG_LTR_CONTROL 0x21a0 --#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4 --#define IPU7_BUTTRESS_REG_INT_FRM_PUNIT 0x21a8 --#define IPU8_BUTTRESS_REG_LTR_CONTROL 0x21a4 --#define IPU8_BUTTRESS_REG_NDE_CONTROL 0x21a8 --#define IPU8_BUTTRESS_REG_INT_FRM_PUNIT 0x21ac --#define BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0 --#define BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4 --#define BUTTRESS_REG_DVFS_FSM_STATUS 0x21b8 --#define BUTTRESS_REG_PS_PLL_ENABLE 0x21bc --#define BUTTRESS_REG_D2D_CTL 0x21d4 --#define BUTTRESS_REG_IB_CLK_CTL 0x21d8 --#define BUTTRESS_REG_IB_CRO_CLK_CTL 0x21dc --#define BUTTRESS_REG_FUNC_FUSES 0x21e0 --#define BUTTRESS_REG_ISOCH_CTL 0x21e4 --#define BUTTRESS_REG_WORKPOINT_CTL 0x21f0 --#define BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS 0x2200 --#define BUTTRESS_REG_DRV_IS_UCX_START_ADDR 0x2204 --#define BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS 0x2208 --#define BUTTRESS_REG_DRV_PS_UCX_START_ADDR 0x220c --#define BUTTRESS_REG_DRV_UCX_RESET_CFG 0x2210 -- --/* configured by CSE */ --#define BUTTRESS_REG_CSE_IS_UCX_CONTROL_STATUS 0x2300 --#define BUTTRESS_REG_CSE_IS_UCX_START_ADDR 0x2304 --#define BUTTRESS_REG_CSE_PS_UCX_CONTROL_STATUS 0x2308 --#define BUTTRESS_REG_CSE_PS_UCX_START_ADDR 0x230c -- --#define BUTTRESS_REG_CAMERA_MASK 0x2310 --#define BUTTRESS_REG_FW_CTL 0x2314 --#define BUTTRESS_REG_SECURITY_CTL 0x2318 --#define BUTTRESS_REG_FUNCTIONAL_FW_SETUP 0x231c --#define BUTTRESS_REG_FW_BASE 0x2320 --#define BUTTRESS_REG_FW_BASE_LIMIT 0x2324 --#define BUTTRESS_REG_FW_SCRATCH_BASE 0x2328 --#define BUTTRESS_REG_FW_SCRATCH_LIMIT 0x232c --#define BUTTRESS_REG_CSE_ACTION 0x2330 -- --/* configured by SW */ --#define BUTTRESS_REG_FW_RESET_CTL 0x2334 --#define BUTTRESS_REG_FW_SOURCE_SIZE 0x2338 --#define BUTTRESS_REG_FW_SOURCE_BASE 0x233c -- --#define BUTTRESS_REG_IPU_SEC_CP_LSB 0x2400 --#define BUTTRESS_REG_IPU_SEC_CP_MSB 0x2404 --#define BUTTRESS_REG_IPU_SEC_WAC_LSB 0x2408 --#define BUTTRESS_REG_IPU_SEC_WAC_MSB 0x240c --#define BUTTRESS_REG_IPU_SEC_RAC_LSB 0x2410 --#define BUTTRESS_REG_IPU_SEC_RAC_MSB 0x2414 --#define BUTTRESS_REG_IPU_DRV_CP_LSB 0x2418 --#define BUTTRESS_REG_IPU_DRV_CP_MSB 0x241c --#define BUTTRESS_REG_IPU_DRV_WAC_LSB 0x2420 --#define BUTTRESS_REG_IPU_DRV_WAC_MSB 0x2424 --#define BUTTRESS_REG_IPU_DRV_RAC_LSB 0x2428 --#define BUTTRESS_REG_IPU_DRV_RAC_MSB 0x242c --#define BUTTRESS_REG_IPU_FW_CP_LSB 0x2430 --#define BUTTRESS_REG_IPU_FW_CP_MSB 0x2434 --#define BUTTRESS_REG_IPU_FW_WAC_LSB 0x2438 --#define BUTTRESS_REG_IPU_FW_WAC_MSB 0x243c --#define BUTTRESS_REG_IPU_FW_RAC_LSB 0x2440 --#define BUTTRESS_REG_IPU_FW_RAC_MSB 0x2444 --#define BUTTRESS_REG_IPU_BIOS_SEC_CP_LSB 0x2448 --#define BUTTRESS_REG_IPU_BIOS_SEC_CP_MSB 0x244c --#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_LSB 0x2450 --#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_MSB 0x2454 --#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_LSB 0x2458 --#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_MSB 0x245c --#define BUTTRESS_REG_IPU_DFD_CP_LSB 0x2460 --#define BUTTRESS_REG_IPU_DFD_CP_MSB 0x2464 --#define BUTTRESS_REG_IPU_DFD_WAC_LSB 0x2468 --#define BUTTRESS_REG_IPU_DFD_WAC_MSB 0x246c --#define BUTTRESS_REG_IPU_DFD_RAC_LSB 0x2470 --#define BUTTRESS_REG_IPU_DFD_RAC_MSB 0x2474 --#define BUTTRESS_REG_CSE2IUDB0 0x2500 --#define BUTTRESS_REG_CSE2IUDATA0 0x2504 --#define BUTTRESS_REG_CSE2IUCSR 0x2508 --#define BUTTRESS_REG_IU2CSEDB0 0x250c --#define BUTTRESS_REG_IU2CSEDATA0 0x2510 --#define BUTTRESS_REG_IU2CSECSR 0x2514 --#define BUTTRESS_REG_CSE2IUDB0_CR_SHADOW 0x2520 --#define BUTTRESS_REG_CSE2IUDATA0_CR_SHADOW 0x2524 --#define BUTTRESS_REG_CSE2IUCSR_CR_SHADOW 0x2528 --#define BUTTRESS_REG_IU2CSEDB0_CR_SHADOW 0x252c --#define BUTTRESS_REG_DVFS_FSM_SURVIVABILITY 0x2900 --#define BUTTRESS_REG_FLOWS_FSM_SURVIVABILITY 0x2904 --#define BUTTRESS_REG_FABRICS_FSM_SURVIVABILITY 0x2908 --#define BUTTRESS_REG_PS_SUB1_PM_FSM_SURVIVABILITY 0x290c --#define BUTTRESS_REG_PS_SUB0_PM_FSM_SURVIVABILITY 0x2910 --#define BUTTRESS_REG_PS_PM_FSM_SURVIVABILITY 0x2914 --#define BUTTRESS_REG_IS_PM_FSM_SURVIVABILITY 0x2918 --#define BUTTRESS_REG_FLR_RST_FSM_SURVIVABILITY 0x291c --#define BUTTRESS_REG_FW_RST_FSM_SURVIVABILITY 0x2920 --#define BUTTRESS_REG_RESETPREP_FSM_SURVIVABILITY 0x2924 --#define BUTTRESS_REG_POWER_FSM_DOMAIN_STATUS 0x3000 --#define BUTTRESS_REG_IDLEREQ_STATUS1 0x3004 --#define BUTTRESS_REG_POWER_FSM_STATUS_IS_PS 0x3008 --#define BUTTRESS_REG_POWER_ACK_B_STATUS 0x300c --#define BUTTRESS_REG_DOMAIN_RETENTION_CTL 0x3010 --#define BUTTRESS_REG_CG_CTRL_BITS 0x3014 --#define BUTTRESS_REG_IS_IFC_STATUS0 0x3018 --#define BUTTRESS_REG_IS_IFC_STATUS1 0x301c --#define BUTTRESS_REG_PS_IFC_STATUS0 0x3020 --#define BUTTRESS_REG_PS_IFC_STATUS1 0x3024 --#define BUTTRESS_REG_BTRS_IFC_STATUS0 0x3028 --#define BUTTRESS_REG_BTRS_IFC_STATUS1 0x302c --#define BUTTRESS_REG_IPU_SKU 0x3030 --#define BUTTRESS_REG_PS_IDLEACK 0x3034 --#define BUTTRESS_REG_IS_IDLEACK 0x3038 --#define BUTTRESS_REG_SPARE_REGS_0 0x303c --#define BUTTRESS_REG_SPARE_REGS_1 0x3040 --#define BUTTRESS_REG_SPARE_REGS_2 0x3044 --#define BUTTRESS_REG_SPARE_REGS_3 0x3048 --#define BUTTRESS_REG_IUNIT_ACV 0x304c --#define BUTTRESS_REG_CHICKEN_BITS 0x3050 --#define BUTTRESS_REG_SBENDPOINT_CFG 0x3054 --#define BUTTRESS_REG_ECC_ERR_LOG 0x3058 --#define BUTTRESS_REG_POWER_FSM_STATUS 0x3070 --#define BUTTRESS_REG_RESET_FSM_STATUS 0x3074 --#define BUTTRESS_REG_IDLE_STATUS 0x3078 --#define BUTTRESS_REG_IDLEACK_STATUS 0x307c --#define BUTTRESS_REG_IPU_DEBUG 0x3080 -- --#define BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 --#define BUTTRESS_REG_FW_BOOT_PARAMS1 0x4004 --#define BUTTRESS_REG_FW_BOOT_PARAMS2 0x4008 --#define BUTTRESS_REG_FW_BOOT_PARAMS3 0x400c --#define BUTTRESS_REG_FW_BOOT_PARAMS4 0x4010 --#define BUTTRESS_REG_FW_BOOT_PARAMS5 0x4014 --#define BUTTRESS_REG_FW_BOOT_PARAMS6 0x4018 --#define BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c --#define BUTTRESS_REG_FW_BOOT_PARAMS8 0x4020 --#define BUTTRESS_REG_FW_BOOT_PARAMS9 0x4024 --#define BUTTRESS_REG_FW_BOOT_PARAMS10 0x4028 --#define BUTTRESS_REG_FW_BOOT_PARAMS11 0x402c --#define BUTTRESS_REG_FW_BOOT_PARAMS12 0x4030 --#define BUTTRESS_REG_FW_BOOT_PARAMS13 0x4034 --#define BUTTRESS_REG_FW_BOOT_PARAMS14 0x4038 --#define BUTTRESS_REG_FW_BOOT_PARAMS15 0x403c -- --#define BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \ -- (BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U)) --#define BUTTRESS_REG_FW_GP(i) (0x4040 + 0x4 * (i)) --#define BUTTRESS_REG_FPGA_SUPPORT(i) (0x40c0 + 0x4 * (i)) -- --#define BUTTRESS_REG_FW_GP8 0x4060 --#define BUTTRESS_REG_FW_GP24 0x40a0 -- --#define BUTTRESS_REG_GPIO_0_PADCFG_ADDR_CR 0x4100 --#define BUTTRESS_REG_GPIO_1_PADCFG_ADDR_CR 0x4104 --#define BUTTRESS_REG_GPIO_2_PADCFG_ADDR_CR 0x4108 --#define BUTTRESS_REG_GPIO_3_PADCFG_ADDR_CR 0x410c --#define BUTTRESS_REG_GPIO_4_PADCFG_ADDR_CR 0x4110 --#define BUTTRESS_REG_GPIO_5_PADCFG_ADDR_CR 0x4114 --#define BUTTRESS_REG_GPIO_6_PADCFG_ADDR_CR 0x4118 --#define BUTTRESS_REG_GPIO_7_PADCFG_ADDR_CR 0x411c --#define BUTTRESS_REG_GPIO_ENABLE 0x4140 --#define BUTTRESS_REG_GPIO_VALUE_CR 0x4144 -- --#define BUTTRESS_REG_IS_MEM_CORRECTABLE_ERROR_STATUS 0x5000 --#define BUTTRESS_REG_IS_MEM_FATAL_ERROR_STATUS 0x5004 --#define BUTTRESS_REG_IS_MEM_NON_FATAL_ERROR_STATUS 0x5008 --#define BUTTRESS_REG_IS_MEM_CHECK_PASSED 0x500c --#define BUTTRESS_REG_IS_MEM_ERROR_INJECT 0x5010 --#define BUTTRESS_REG_IS_MEM_ERROR_CLEAR 0x5014 --#define BUTTRESS_REG_PS_MEM_CORRECTABLE_ERROR_STATUS 0x5040 --#define BUTTRESS_REG_PS_MEM_FATAL_ERROR_STATUS 0x5044 --#define BUTTRESS_REG_PS_MEM_NON_FATAL_ERROR_STATUS 0x5048 --#define BUTTRESS_REG_PS_MEM_CHECK_PASSED 0x504c --#define BUTTRESS_REG_PS_MEM_ERROR_INJECT 0x5050 --#define BUTTRESS_REG_PS_MEM_ERROR_CLEAR 0x5054 -- --#define BUTTRESS_REG_IS_AB_REGION_MIN_ADDRESS(i) (0x6000 + 0x8 * (i)) --#define BUTTRESS_REG_IS_AB_REGION_MAX_ADDRESS(i) (0x6004 + 0x8 * (i)) --#define BUTTRESS_REG_IS_AB_VIOLATION_LOG0 0x6080 --#define BUTTRESS_REG_IS_AB_VIOLATION_LOG1 0x6084 --#define BUTTRESS_REG_PS_AB_REGION_MIN_ADDRESS(i) (0x6100 + 0x8 * (i)) --#define BUTTRESS_REG_PS_AB_REGION_MAX_ADDRESS0 (0x6104 + 0x8 * (i)) --#define BUTTRESS_REG_PS_AB_VIOLATION_LOG0 0x6180 --#define BUTTRESS_REG_PS_AB_VIOLATION_LOG1 0x6184 --#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG0 0x6200 --#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG1 0x6204 --#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG0 0x6208 --#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG1 0x620c --#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG0 0x6210 --#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG1 0x6214 --#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG0 0x6218 --#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG1 0x621c --#define BUTTRESS_REG_AB_ENABLE 0x6220 --#define BUTTRESS_REG_AB_DEFAULT_ACCESS 0x6230 -- --/* Indicates CSE has received an IPU driver IPC transaction */ --#define BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0) --/* Indicates an IPC transaction from CSE has arrived */ --#define BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1) --/* Indicates a CSR update from CSE has arrived */ --#define BUTTRESS_IRQ_CSE_CSR_SET BIT(2) --/* Indicates an interrupt set by Punit (not in use at this time) */ --#define BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ BIT(3) --/* Indicates an SAI violation was detected on access to IB registers */ --#define BUTTRESS_IRQ_SAI_VIOLATION BIT(4) --/* Indicates a transaction to IS was not able to pass the access blocker */ --#define BUTTRESS_IRQ_IS_AB_VIOLATION BIT(5) --/* Indicates a transaction to PS was not able to pass the access blocker */ --#define BUTTRESS_IRQ_PS_AB_VIOLATION BIT(6) --/* Indicates an error response was detected by the IB config NoC */ --#define BUTTRESS_IRQ_IB_CFG_NOC_ERR_IRQ BIT(7) --/* Indicates an error response was detected by the IB data NoC */ --#define BUTTRESS_IRQ_IB_DATA_NOC_ERR_IRQ BIT(8) --/* Transaction to DVP regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_IB_DVP_AB_VIOLATION BIT(9) --/* Transaction to ATB2DTF regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_ATB2DTF_AB_VIOLATION BIT(10) --/* Transaction to IS debug regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_IS_DEBUG_AB_VIOLATION BIT(11) --/* Transaction to PS debug regs was not able to pass the access blocker */ --#define BUTTRESS_IRQ_PS_DEBUG_AB_VIOLATION BIT(12) --/* Indicates timeout occurred waiting for a response from a target */ --#define BUTTRESS_IRQ_IB_CFG_NOC_TIMEOUT_IRQ BIT(13) --/* Set when any correctable ECC error input wire to buttress is set */ --#define BUTTRESS_IRQ_ECC_CORRECTABLE BIT(14) --/* Any noncorrectable-nonfatal ECC error input wire to buttress is set */ --#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_NONFATAL BIT(15) --/* Set when any noncorrectable-fatal ECC error input wire to buttress is set */ --#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_FATAL BIT(16) --/* Set when timeout occurred waiting for a response from a target */ --#define BUTTRESS_IRQ_IS_CFG_NOC_TIMEOUT_IRQ BIT(17) --#define BUTTRESS_IRQ_PS_CFG_NOC_TIMEOUT_IRQ BIT(18) --#define BUTTRESS_IRQ_LB_CFG_NOC_TIMEOUT_IRQ BIT(19) --/* IS FW double exception event */ --#define BUTTRESS_IRQ_IS_UC_PFATAL_ERROR BIT(26) --/* PS FW double exception event */ --#define BUTTRESS_IRQ_PS_UC_PFATAL_ERROR BIT(27) --/* IS FW watchdog event */ --#define BUTTRESS_IRQ_IS_WATCHDOG BIT(28) --/* PS FW watchdog event */ --#define BUTTRESS_IRQ_PS_WATCHDOG BIT(29) --/* IS IRC irq out */ --#define BUTTRESS_IRQ_IS_IRQ BIT(30) --/* PS IRC irq out */ --#define BUTTRESS_IRQ_PS_IRQ BIT(31) -- --/* buttress irq */ --#define BUTTRESS_PWR_STATUS_HH_STATE_IDLE 0U --#define BUTTRESS_PWR_STATUS_HH_STATE_IN_PRGS 1U --#define BUTTRESS_PWR_STATUS_HH_STATE_DONE 2U --#define BUTTRESS_PWR_STATUS_HH_STATE_ERR 3U -- --#define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) --#define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 --#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) --#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) --/* new for PTL */ --#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) --#define BUTTRESS_IRQS (BUTTRESS_IRQ_IS_IRQ | \ -- BUTTRESS_IRQ_PS_IRQ | \ -- BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \ -- BUTTRESS_IRQ_CSE_CSR_SET | \ -- BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE | \ -- BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -- --/* Iunit to CSE regs */ --#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) --#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27 --#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10 --#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 -- --#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 --#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 --#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 --#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 -- --#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) --#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) --#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) --#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) -- --#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) --#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) --#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) --#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) --#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) --#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) -- --/* 0x20 == NACK, 0xf == unknown command */ --#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 --#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff -- --/* IS/PS freq control */ --#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU --#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU -- --#define IPU7_IS_FREQ_MAX 450 --#define IPU7_IS_FREQ_MIN 50 --#define IPU7_PS_FREQ_MAX 750 --#define BUTTRESS_PS_FREQ_RATIO_STEP 25U --/* valid for IPU8 */ --#define BUTTRESS_IS_FREQ_RATIO_STEP 25U -- --/* IS: 400mhz, PS: 500mhz */ --#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b --#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14 --/* IS: 400mhz, PS: 400mhz */ --#define IPU8_IS_FREQ_CTL_DEFAULT_RATIO 0x10 --#define IPU8_PS_FREQ_CTL_DEFAULT_RATIO 0x10 -- --#define IPU_FREQ_CTL_CDYN 0x80 --#define IPU_FREQ_CTL_RATIO_SHIFT 0x0 --#define IPU_FREQ_CTL_CDYN_SHIFT 0x8 -- --/* buttree power status */ --#define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 --#define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ -- (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) -- --#define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 --#define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ -- (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) -- --#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 --#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 --#define IPU_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 --#define IPU_BUTTRESS_PWR_STATE_UP_DONE 0x3 -- --#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 --#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3 << 3) -- --#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 --#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3 << 6) -- --#define PS_FSM_CG BIT(3) -- --#define BUTTRESS_OVERRIDE_IS_CLK BIT(1) --#define BUTTRESS_OVERRIDE_PS_CLK BIT(2) --/* ps_pll only valid for ipu8 */ --#define BUTTRESS_OWN_ACK_PS_PLL BIT(8) --#define BUTTRESS_OWN_ACK_IS_CLK BIT(9) --#define BUTTRESS_OWN_ACK_PS_CLK BIT(10) -- --/* FW reset ctrl */ --#define BUTTRESS_FW_RESET_CTL_START BIT(0) --#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) -- --/* security */ --#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) --#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) -- --#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) --#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) --#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) -- --/* D2D */ --#define BUTTRESS_D2D_PWR_EN BIT(0) --#define BUTTRESS_D2D_PWR_ACK BIT(4) -- --/* NDE */ --#define NDE_VAL_MASK GENMASK(9, 0) --#define NDE_SCALE_MASK GENMASK(12, 10) --#define NDE_VALID_MASK BIT(13) --#define NDE_RESVEC_MASK GENMASK(19, 16) --#define NDE_IN_VBLANK_DIS_MASK BIT(31) -- --#define BUTTRESS_NDE_VAL_ACTIVE 48 --#define BUTTRESS_NDE_SCALE_ACTIVE 2 --#define BUTTRESS_NDE_VALID_ACTIVE 1 -- --#define BUTTRESS_NDE_VAL_DEFAULT 1023 --#define BUTTRESS_NDE_SCALE_DEFAULT 2 --#define BUTTRESS_NDE_VALID_DEFAULT 0 -- --/* IS and PS UCX control */ --#define UCX_CTL_RESET BIT(0) --#define UCX_CTL_RUN BIT(1) --#define UCX_CTL_WAKEUP BIT(2) --#define UCX_CTL_SPARE GENMASK(7, 3) --#define UCX_STS_PWR GENMASK(17, 16) --#define UCX_STS_SLEEPING BIT(18) -- --/* offset from PHY base */ --#define PHY_CSI_CFG 0xc0 --#define PHY_CSI_RCOMP_CONTROL 0xc8 --#define PHY_CSI_BSCAN_EXCLUDE 0xd8 -- --#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) --#define PHY_DPHY_DLL_OVRD(x) (0x14c + 0x100 * (x)) --#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) --#define PHY_CPHY_RX_CONTROL2(x) (0x114 + 0x100 * (x)) --#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) --#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) -- --/* PB registers */ --#define INTERRUPT_STATUS 0x0 --#define BTRS_LOCAL_INTERRUPT_MASK 0x4 --#define GLOBAL_INTERRUPT_MASK 0x8 --#define HM_ATS 0xc --#define ATS_ERROR_LOG1 0x10 --#define ATS_ERROR_LOG2 0x14 --#define ATS_ERROR_CLEAR 0x18 --#define CFI_0_ERROR_LOG 0x1c --#define CFI_0_ERROR_CLEAR 0x20 --#define HASH_CONFIG 0x2c --#define TLBID_HASH_ENABLE_31_0 0x30 --#define TLBID_HASH_ENABLE_63_32 0x34 --#define TLBID_HASH_ENABLE_95_64 0x38 --#define TLBID_HASH_ENABLE_127_96 0x3c --#define CFI_1_ERROR_LOGGING 0x40 --#define CFI_1_ERROR_CLEAR 0x44 --#define IMR_ERROR_LOGGING_LOW 0x48 --#define IMR_ERROR_LOGGING_HIGH 0x4c --#define IMR_ERROR_CLEAR 0x50 --#define PORT_ARBITRATION_WEIGHTS 0x54 --#define IMR_ERROR_LOGGING_CFI_1_LOW 0x58 --#define IMR_ERROR_LOGGING_CFI_1_HIGH 0x5c --#define IMR_ERROR_CLEAR_CFI_1 0x60 --#define BAR2_MISC_CONFIG 0x64 --#define RSP_ID_CONFIG_AXI2CFI_0 0x68 --#define RSP_ID_CONFIG_AXI2CFI_1 0x6c --#define PB_DRIVER_PCODE_MAILBOX_STATUS 0x70 --#define PB_DRIVER_PCODE_MAILBOX_INTERFACE 0x74 --#define PORT_ARBITRATION_WEIGHTS_ATS 0x78 -- --#endif /* IPU7_BUTTRESS_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.c b/drivers/media/pci/intel/ipu7/ipu7-buttress.c -deleted file mode 100644 -index b350ca5678d0..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-buttress.c -+++ /dev/null -@@ -1,1193 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-buttress-regs.h" -- --#define BOOTLOADER_STATUS_OFFSET BUTTRESS_REG_FW_BOOT_PARAMS7 -- --#define BOOTLOADER_MAGIC_KEY 0xb00710adU -- --#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 --#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 --#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE -- --#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10U -- --#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) -- --#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) --#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) --#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) -- --#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC --#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC --#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) --#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) -- --#define BUTTRESS_IPC_RESET_RETRY 2000U --#define BUTTRESS_CSE_IPC_RESET_RETRY 4U --#define BUTTRESS_IPC_CMD_SEND_RETRY 1U -- --struct ipu7_ipc_buttress_msg { -- u32 cmd; -- u32 expected_resp; -- bool require_resp; -- u8 cmd_size; --}; -- --static const u32 ipu7_adev_irq_mask[2] = { -- BUTTRESS_IRQ_IS_IRQ, -- BUTTRESS_IRQ_PS_IRQ --}; -- --int ipu_buttress_ipc_reset(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc) --{ -- unsigned int retries = BUTTRESS_IPC_RESET_RETRY; -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- u32 val = 0, csr_in_clr; -- -- if (!isp->secure_mode) { -- dev_dbg(dev, "Skip IPC reset for non-secure mode\n"); -- return 0; -- } -- -- mutex_lock(&b->ipc_mutex); -- -- /* Clear-by-1 CSR (all bits), corresponding internal states. */ -- val = readl(isp->base + ipc->csr_in); -- writel(val, isp->base + ipc->csr_in); -- -- /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ -- writel(ENTRY, isp->base + ipc->csr_out); -- /* -- * Clear-by-1 all CSR bits EXCEPT following -- * bits: -- * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -- * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- * C. Possibly custom bits, depending on -- * their role. -- */ -- csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | -- BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | -- BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; -- -- do { -- usleep_range(400, 500); -- val = readl(isp->base + ipc->csr_in); -- switch (val) { -- case ENTRY | EXIT: -- case ENTRY | EXIT | QUERY: -- /* -- * 1) Clear-by-1 CSR bits -- * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -- * IPC_PEER_COMP_ACTIONS_RST_PHASE2). -- * 2) Set peer CSR bit -- * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -- */ -- writel(ENTRY | EXIT, isp->base + ipc->csr_in); -- writel(QUERY, isp->base + ipc->csr_out); -- break; -- case ENTRY: -- case ENTRY | QUERY: -- /* -- * 1) Clear-by-1 CSR bits -- * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -- * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). -- * 2) Set peer CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE1. -- */ -- writel(ENTRY | QUERY, isp->base + ipc->csr_in); -- writel(ENTRY, isp->base + ipc->csr_out); -- break; -- case EXIT: -- case EXIT | QUERY: -- /* -- * Clear-by-1 CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- * 1) Clear incoming doorbell. -- * 2) Clear-by-1 all CSR bits EXCEPT following -- * bits: -- * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -- * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- * C. Possibly custom bits, depending on -- * their role. -- * 3) Set peer CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -- */ -- writel(EXIT, isp->base + ipc->csr_in); -- writel(0, isp->base + ipc->db0_in); -- writel(csr_in_clr, isp->base + ipc->csr_in); -- writel(EXIT, isp->base + ipc->csr_out); -- -- /* -- * Read csr_in again to make sure if RST_PHASE2 is done. -- * If csr_in is QUERY, it should be handled again. -- */ -- usleep_range(200, 300); -- val = readl(isp->base + ipc->csr_in); -- if (val & QUERY) { -- dev_dbg(dev, -- "RST_PHASE2 retry csr_in = %x\n", val); -- break; -- } -- mutex_unlock(&b->ipc_mutex); -- return 0; -- case QUERY: -- /* -- * 1) Clear-by-1 CSR bit -- * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -- * 2) Set peer CSR bit -- * IPC_PEER_COMP_ACTIONS_RST_PHASE1 -- */ -- writel(QUERY, isp->base + ipc->csr_in); -- writel(ENTRY, isp->base + ipc->csr_out); -- break; -- default: -- dev_dbg_ratelimited(dev, "Unexpected CSR 0x%x\n", val); -- break; -- } -- } while (retries--); -- -- mutex_unlock(&b->ipc_mutex); -- dev_err(dev, "Timed out while waiting for CSE\n"); -- -- return -ETIMEDOUT; --} -- --static void ipu_buttress_ipc_validity_close(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc) --{ -- writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, -- isp->base + ipc->csr_out); --} -- --static int --ipu_buttress_ipc_validity_open(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc) --{ -- unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; -- void __iomem *addr; -- int ret; -- u32 val; -- -- writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, -- isp->base + ipc->csr_out); -- -- addr = isp->base + ipc->csr_in; -- ret = readl_poll_timeout(addr, val, val & mask, 200, -- BUTTRESS_IPC_VALIDITY_TIMEOUT_US); -- if (ret) { -- dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); -- ipu_buttress_ipc_validity_close(isp, ipc); -- } -- -- return ret; --} -- --static void ipu_buttress_ipc_recv(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc, u32 *ipc_msg) --{ -- if (ipc_msg) -- *ipc_msg = readl(isp->base + ipc->data0_in); -- writel(0, isp->base + ipc->db0_in); --} -- --static int ipu_buttress_ipc_send_msg(struct ipu7_device *isp, -- struct ipu7_ipc_buttress_msg *msg) --{ -- unsigned long tx_timeout_jiffies, rx_timeout_jiffies; -- unsigned int retry = BUTTRESS_IPC_CMD_SEND_RETRY; -- struct ipu_buttress *b = &isp->buttress; -- struct ipu_buttress_ipc *ipc = &b->cse; -- struct device *dev = &isp->pdev->dev; -- int tout; -- u32 val; -- int ret; -- -- mutex_lock(&b->ipc_mutex); -- -- ret = ipu_buttress_ipc_validity_open(isp, ipc); -- if (ret) { -- dev_err(dev, "IPC validity open failed\n"); -- goto out; -- } -- -- tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); -- rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); -- --try: -- reinit_completion(&ipc->send_complete); -- if (msg->require_resp) -- reinit_completion(&ipc->recv_complete); -- -- dev_dbg(dev, "IPC command: 0x%x\n", msg->cmd); -- writel(msg->cmd, isp->base + ipc->data0_out); -- val = BUTTRESS_IU2CSEDB0_BUSY | msg->cmd_size; -- writel(val, isp->base + ipc->db0_out); -- -- tout = wait_for_completion_timeout(&ipc->send_complete, -- tx_timeout_jiffies); -- if (!tout) { -- dev_err(dev, "send IPC response timeout\n"); -- if (!retry--) { -- ret = -ETIMEDOUT; -- goto out; -- } -- -- /* Try again if CSE is not responding on first try */ -- writel(0, isp->base + ipc->db0_out); -- goto try; -- } -- -- if (!msg->require_resp) { -- ret = -EIO; -- goto out; -- } -- -- tout = wait_for_completion_timeout(&ipc->recv_complete, -- rx_timeout_jiffies); -- if (!tout) { -- dev_err(dev, "recv IPC response timeout\n"); -- ret = -ETIMEDOUT; -- goto out; -- } -- -- if (ipc->nack_mask && -- (ipc->recv_data & ipc->nack_mask) == ipc->nack) { -- dev_err(dev, "IPC NACK for cmd 0x%x\n", msg->cmd); -- ret = -EIO; -- goto out; -- } -- -- if (ipc->recv_data != msg->expected_resp) { -- dev_err(dev, -- "expected resp: 0x%x, IPC response: 0x%x\n", -- msg->expected_resp, ipc->recv_data); -- ret = -EIO; -- goto out; -- } -- -- dev_dbg(dev, "IPC commands done\n"); -- --out: -- ipu_buttress_ipc_validity_close(isp, ipc); -- mutex_unlock(&b->ipc_mutex); -- -- return ret; --} -- --static int ipu_buttress_ipc_send(struct ipu7_device *isp, -- u32 ipc_msg, u32 size, bool require_resp, -- u32 expected_resp) --{ -- struct ipu7_ipc_buttress_msg msg = { -- .cmd = ipc_msg, -- .cmd_size = size, -- .require_resp = require_resp, -- .expected_resp = expected_resp, -- }; -- -- return ipu_buttress_ipc_send_msg(isp, &msg); --} -- --static irqreturn_t ipu_buttress_call_isr(struct ipu7_bus_device *adev) --{ -- irqreturn_t ret = IRQ_WAKE_THREAD; -- -- if (!adev || !adev->auxdrv || !adev->auxdrv_data) -- return IRQ_NONE; -- -- if (adev->auxdrv_data->isr) -- ret = adev->auxdrv_data->isr(adev); -- -- if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) -- ret = IRQ_NONE; -- -- return ret; --} -- --irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) --{ -- struct ipu7_device *isp = isp_ptr; -- struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- irqreturn_t ret = IRQ_NONE; -- u32 pb_irq, pb_local_irq; -- u32 disable_irqs = 0; -- u32 irq_status; -- unsigned int i; -- -- pm_runtime_get_noresume(dev); -- -- pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); -- writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); -- -- /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ -- pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); -- if (pb_local_irq & ~BIT(0)) { -- dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, -- pb_local_irq); -- dev_warn(dev, "Details: %x %x %x %x %x %x %x %x\n", -- readl(isp->pb_base + ATS_ERROR_LOG1), -- readl(isp->pb_base + ATS_ERROR_LOG2), -- readl(isp->pb_base + CFI_0_ERROR_LOG), -- readl(isp->pb_base + CFI_1_ERROR_LOGGING), -- readl(isp->pb_base + IMR_ERROR_LOGGING_LOW), -- readl(isp->pb_base + IMR_ERROR_LOGGING_HIGH), -- readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_LOW), -- readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_HIGH)); -- } -- -- irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -- if (!irq_status) { -- pm_runtime_put_noidle(dev); -- return IRQ_NONE; -- } -- -- do { -- writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR); -- -- for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask); i++) { -- irqreturn_t r = ipu_buttress_call_isr(adev[i]); -- -- if (!(irq_status & ipu7_adev_irq_mask[i])) -- continue; -- -- if (r == IRQ_WAKE_THREAD) { -- ret = IRQ_WAKE_THREAD; -- disable_irqs |= ipu7_adev_irq_mask[i]; -- } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { -- ret = IRQ_HANDLED; -- } -- } -- -- if (irq_status & (BUTTRESS_IRQS | BUTTRESS_IRQ_SAI_VIOLATION) && -- ret == IRQ_NONE) -- ret = IRQ_HANDLED; -- -- if (irq_status & BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING) { -- dev_dbg(dev, "BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING\n"); -- ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); -- complete(&b->cse.recv_complete); -- } -- -- if (irq_status & BUTTRESS_IRQ_CSE_CSR_SET) -- dev_dbg(dev, "BUTTRESS_IRQ_CSE_CSR_SET\n"); -- -- if (irq_status & BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) { -- dev_dbg(dev, "BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE\n"); -- complete(&b->cse.send_complete); -- } -- -- if (irq_status & BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -- dev_dbg(dev, "BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ\n"); -- -- if (irq_status & BUTTRESS_IRQ_SAI_VIOLATION && -- ipu_buttress_get_secure_mode(isp)) -- dev_err(dev, "BUTTRESS_IRQ_SAI_VIOLATION\n"); -- -- irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -- } while (irq_status); -- -- if (disable_irqs) -- writel(BUTTRESS_IRQS & ~disable_irqs, -- isp->base + BUTTRESS_REG_IRQ_ENABLE); -- -- pm_runtime_put(dev); -- -- return ret; --} -- --irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) --{ -- struct ipu7_device *isp = isp_ptr; -- struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -- const struct ipu7_auxdrv_data *drv_data = NULL; -- irqreturn_t ret = IRQ_NONE; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask) && adev[i]; i++) { -- drv_data = adev[i]->auxdrv_data; -- if (!drv_data) -- continue; -- -- if (drv_data->wake_isr_thread && -- drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) -- ret = IRQ_HANDLED; -- } -- -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -- -- return ret; --} -- --static int isys_d2d_power(struct device *dev, bool on) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- int ret = 0; -- u32 target = on ? BUTTRESS_D2D_PWR_ACK : 0U; -- u32 val; -- -- dev_dbg(dev, "power %s isys d2d.\n", on ? "UP" : "DOWN"); -- val = readl(isp->base + BUTTRESS_REG_D2D_CTL); -- if ((val & BUTTRESS_D2D_PWR_ACK) == target) { -- dev_info(dev, "d2d already in %s state.\n", -- on ? "UP" : "DOWN"); -- return 0; -- } -- -- val = on ? val | BUTTRESS_D2D_PWR_EN : val & (~BUTTRESS_D2D_PWR_EN); -- writel(val, isp->base + BUTTRESS_REG_D2D_CTL); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_D2D_CTL, -- val, (val & BUTTRESS_D2D_PWR_ACK) == target, -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) -- dev_err(dev, "power %s d2d timeout. status: 0x%x\n", -- on ? "UP" : "DOWN", val); -- -- return ret; --} -- --static void isys_nde_control(struct device *dev, bool on) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, value, scale, valid, resvec; -- u32 nde_reg; -- -- if (on) { -- value = BUTTRESS_NDE_VAL_ACTIVE; -- scale = BUTTRESS_NDE_SCALE_ACTIVE; -- valid = BUTTRESS_NDE_VALID_ACTIVE; -- } else { -- value = BUTTRESS_NDE_VAL_DEFAULT; -- scale = BUTTRESS_NDE_SCALE_DEFAULT; -- valid = BUTTRESS_NDE_VALID_DEFAULT; -- } -- -- /* only set the fabrics resource ownership for ipu8 */ -- nde_reg = is_ipu8(isp->hw_ver) ? IPU8_BUTTRESS_REG_NDE_CONTROL : -- IPU7_BUTTRESS_REG_NDE_CONTROL; -- resvec = is_ipu8(isp->hw_ver) ? 0x2 : 0xe; -- val = FIELD_PREP(NDE_VAL_MASK, value) | -- FIELD_PREP(NDE_SCALE_MASK, scale) | -- FIELD_PREP(NDE_VALID_MASK, valid) | -- FIELD_PREP(NDE_RESVEC_MASK, resvec); -- -- writel(val, isp->base + nde_reg); --} -- --static int ipu7_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- -- exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -- if (ctrl->subsys_id == IPU_IS) { -- ret = isys_d2d_power(dev, true); -- if (ret) -- goto out_power; -- isys_nde_control(dev, true); -- } -- -- /* request clock resource ownership */ -- val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- val |= ctrl->ovrd_clk; -- writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS, -- val, (val & ctrl->own_clk_ack), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) -- dev_warn(dev, "request clk ownership timeout. status 0x%x\n", -- val); -- -- val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -- -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power up timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- -- /* release clock resource ownership */ -- val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- val &= ~ctrl->ovrd_clk; -- writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -- --out_power: -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --static int ipu7_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- -- exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -- val = 0x8U << ctrl->ratio_shift; -- -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power down timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); --out_power: -- if (ctrl->subsys_id == IPU_IS && !ret) { -- isys_d2d_power(dev, false); -- isys_nde_control(dev, false); -- } -- -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --static int ipu8_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 sleep_level_reg = BUTTRESS_REG_SLEEP_LEVEL_STS; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -- if (ctrl->subsys_id == IPU_IS) { -- ret = isys_d2d_power(dev, true); -- if (ret) -- goto out_power; -- isys_nde_control(dev, true); -- } -- -- /* request ps_pll when psys freq > 400Mhz */ -- if (ctrl->subsys_id == IPU_PS && ctrl->ratio > 0x10) { -- writel(1, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -- ret = readl_poll_timeout(isp->base + sleep_level_reg, -- val, (val & ctrl->own_clk_ack), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) -- dev_warn(dev, "ps_pll req ack timeout. status 0x%x\n", -- val); -- } -- -- val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power up timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); --out_power: -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --static int ipu8_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- u32 val, exp_sts; -- int ret = 0; -- -- if (!ctrl) -- return 0; -- -- mutex_lock(&isp->buttress.power_mutex); -- exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -- -- if (ctrl->subsys_id == IPU_PS) -- val = 0x10U << ctrl->ratio_shift; -- else -- val = 0x8U << ctrl->ratio_shift; -- -- dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -- ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -- writel(val, isp->base + ctrl->freq_ctl); -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -- val, ((val & ctrl->pwr_sts_mask) == exp_sts), -- 100, BUTTRESS_POWER_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "%s power down timeout with status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -- goto out_power; -- } -- -- dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -- ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); --out_power: -- if (ctrl->subsys_id == IPU_IS && !ret) { -- isys_d2d_power(dev, false); -- isys_nde_control(dev, false); -- } -- -- if (ctrl->subsys_id == IPU_PS) { -- val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS); -- if (val & ctrl->own_clk_ack) -- writel(0, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -- } -- mutex_unlock(&isp->buttress.power_mutex); -- -- return ret; --} -- --int ipu_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- -- if (is_ipu8(isp->hw_ver)) -- return ipu8_buttress_powerup(dev, ctrl); -- -- return ipu7_buttress_powerup(dev, ctrl); --} -- --int ipu_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl) --{ -- struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -- -- if (is_ipu8(isp->hw_ver)) -- return ipu8_buttress_powerdown(dev, ctrl); -- -- return ipu7_buttress_powerdown(dev, ctrl); --} -- --bool ipu_buttress_get_secure_mode(struct ipu7_device *isp) --{ -- u32 val; -- -- val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -- -- return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; --} -- --bool ipu_buttress_auth_done(struct ipu7_device *isp) --{ -- u32 val; -- -- if (!isp->secure_mode) -- return true; -- -- val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -- val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); -- -- return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_auth_done, "INTEL_IPU7"); -- --int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq) --{ -- u32 reg_val; -- int ret; -- -- ret = pm_runtime_get_sync(&isp->isys->auxdev.dev); -- if (ret < 0) { -- pm_runtime_put(&isp->isys->auxdev.dev); -- dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -- return ret; -- } -- -- reg_val = readl(isp->base + BUTTRESS_REG_IS_WORKPOINT_REQ); -- -- pm_runtime_put(&isp->isys->auxdev.dev); -- -- if (is_ipu8(isp->hw_ver)) -- *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 25; -- else -- *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 50 / 3; -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_isys_freq, "INTEL_IPU7"); -- --int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq) --{ -- u32 reg_val; -- int ret; -- -- ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -- if (ret < 0) { -- pm_runtime_put(&isp->psys->auxdev.dev); -- dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -- return ret; -- } -- -- reg_val = readl(isp->base + BUTTRESS_REG_PS_WORKPOINT_REQ); -- -- pm_runtime_put(&isp->psys->auxdev.dev); -- -- reg_val &= BUTTRESS_PS_FREQ_CTL_RATIO_MASK; -- *freq = BUTTRESS_PS_FREQ_RATIO_STEP * reg_val; -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_psys_freq, "INTEL_IPU7"); -- --int ipu_buttress_reset_authentication(struct ipu7_device *isp) --{ -- struct device *dev = &isp->pdev->dev; -- int ret; -- u32 val; -- -- if (!isp->secure_mode) { -- dev_dbg(dev, "Skip auth for non-secure mode\n"); -- return 0; -- } -- -- writel(BUTTRESS_FW_RESET_CTL_START, isp->base + -- BUTTRESS_REG_FW_RESET_CTL); -- -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, -- val & BUTTRESS_FW_RESET_CTL_DONE, 500, -- BUTTRESS_CSE_FWRESET_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "Time out while resetting authentication state\n"); -- return ret; -- } -- -- dev_dbg(dev, "FW reset for authentication done\n"); -- writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); -- /* leave some time for HW restore */ -- usleep_range(800, 1000); -- -- return 0; --} -- --int ipu_buttress_authenticate(struct ipu7_device *isp) --{ -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- u32 data, mask, done, fail; -- int ret; -- -- if (!isp->secure_mode) { -- dev_dbg(dev, "Skip auth for non-secure mode\n"); -- return 0; -- } -- -- mutex_lock(&b->auth_mutex); -- -- if (ipu_buttress_auth_done(isp)) { -- ret = 0; -- goto out_unlock; -- } -- -- /* -- * BUTTRESS_REG_FW_SOURCE_BASE needs to be set with FW CPD -- * package address for secure mode. -- */ -- -- writel(isp->cpd_fw->size, isp->base + BUTTRESS_REG_FW_SOURCE_SIZE); -- writel(sg_dma_address(isp->psys->fw_sgt.sgl), -- isp->base + BUTTRESS_REG_FW_SOURCE_BASE); -- -- /* -- * Write boot_load into IU2CSEDATA0 -- * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -- * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -- */ -- dev_info(dev, "Sending BOOT_LOAD to CSE\n"); -- ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, -- 1, true, -- BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); -- if (ret) { -- dev_err(dev, "CSE boot_load failed\n"); -- goto out_unlock; -- } -- -- mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; -- done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; -- fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -- ((data & mask) == done || -- (data & mask) == fail), 500, -- BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "CSE boot_load timeout\n"); -- goto out_unlock; -- } -- -- if ((data & mask) == fail) { -- dev_err(dev, "CSE auth failed\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- ret = readl_poll_timeout(isp->base + BOOTLOADER_STATUS_OFFSET, -- data, data == BOOTLOADER_MAGIC_KEY, 500, -- BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "Unexpected magic number 0x%x\n", data); -- goto out_unlock; -- } -- -- /* -- * Write authenticate_run into IU2CSEDATA0 -- * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -- * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -- */ -- dev_info(dev, "Sending AUTHENTICATE_RUN to CSE\n"); -- ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, -- 1, true, -- BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); -- if (ret) { -- dev_err(dev, "CSE authenticate_run failed\n"); -- goto out_unlock; -- } -- -- done = BUTTRESS_SECURITY_CTL_AUTH_DONE; -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -- ((data & mask) == done || -- (data & mask) == fail), 500, -- BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); -- if (ret) { -- dev_err(dev, "CSE authenticate timeout\n"); -- goto out_unlock; -- } -- -- if ((data & mask) == fail) { -- dev_err(dev, "CSE boot_load failed\n"); -- ret = -EINVAL; -- goto out_unlock; -- } -- -- dev_info(dev, "CSE authenticate_run done\n"); -- --out_unlock: -- mutex_unlock(&b->auth_mutex); -- -- return ret; --} -- --static int ipu_buttress_send_tsc_request(struct ipu7_device *isp) --{ -- u32 val, mask, done; -- int ret; -- -- mask = BUTTRESS_PWR_STATUS_HH_STATUS_MASK; -- -- writel(BUTTRESS_TSC_CMD_START_TSC_SYNC, -- isp->base + BUTTRESS_REG_TSC_CMD); -- -- val = readl(isp->base + BUTTRESS_REG_PWR_STATUS); -- val = FIELD_GET(mask, val); -- if (val == BUTTRESS_PWR_STATUS_HH_STATE_ERR) { -- dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); -- return -EINVAL; -- } -- -- done = BUTTRESS_PWR_STATUS_HH_STATE_DONE; -- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, val, -- FIELD_GET(mask, val) == done, 500, -- BUTTRESS_TSC_SYNC_TIMEOUT_US); -- if (ret) -- dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); -- -- return ret; --} -- --int ipu_buttress_start_tsc_sync(struct ipu7_device *isp) --{ -- void __iomem *base = isp->base; -- unsigned int i; -- u32 val; -- -- if (is_ipu8(isp->hw_ver)) { -- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -- val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -- if (val == 1) -- return 0; -- usleep_range(40, 50); -- } -- -- dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -- return -ETIMEDOUT; -- } -- -- if (is_ipu7p5(isp->hw_ver)) { -- val = readl(base + BUTTRESS_REG_TSC_CTL); -- val |= BUTTRESS_SEL_PB_TIMESTAMP; -- writel(val, base + BUTTRESS_REG_TSC_CTL); -- -- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -- val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -- if (val == 1) -- return 0; -- usleep_range(40, 50); -- } -- -- dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -- -- return -ETIMEDOUT; -- } -- -- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -- int ret; -- -- ret = ipu_buttress_send_tsc_request(isp); -- if (ret != -ETIMEDOUT) -- return ret; -- -- val = readl(base + BUTTRESS_REG_TSC_CTL); -- val = val | BUTTRESS_TSW_WA_SOFT_RESET; -- writel(val, base + BUTTRESS_REG_TSC_CTL); -- val = val & (~BUTTRESS_TSW_WA_SOFT_RESET); -- writel(val, base + BUTTRESS_REG_TSC_CTL); -- } -- -- dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); -- -- return -ETIMEDOUT; --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_start_tsc_sync, "INTEL_IPU7"); -- --void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val) --{ -- unsigned long flags; -- u32 tsc_hi, tsc_lo; -- -- local_irq_save(flags); -- if (is_ipu7(isp->hw_ver)) { -- tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); -- tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI); -- } else { -- tsc_lo = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_LO); -- tsc_hi = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_HI); -- } -- *val = (u64)tsc_hi << 32 | tsc_lo; -- local_irq_restore(flags); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_read, "INTEL_IPU7"); -- --u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp) --{ -- u64 ns = ticks * 10000; -- -- /* -- * converting TSC tick count to ns is calculated by: -- * Example (TSC clock frequency is 19.2MHz): -- * ns = ticks * 1000 000 000 / 19.2Mhz -- * = ticks * 1000 000 000 / 19200000Hz -- * = ticks * 10000 / 192 ns -- */ -- return div_u64(ns, isp->buttress.ref_clk); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_ticks_to_ns, "INTEL_IPU7"); -- --/* trigger uc control to wakeup fw */ --void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp) --{ -- u32 val; -- -- val = readl(isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); -- val |= UCX_CTL_WAKEUP; -- writel(val, isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_is_uc, "INTEL_IPU7"); -- --void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp) --{ -- u32 val; -- -- val = readl(isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); -- val |= UCX_CTL_WAKEUP; -- writel(val, isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); --} --EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_ps_uc, "INTEL_IPU7"); -- --static const struct x86_cpu_id ipu_misc_cfg_exclusion[] = { -- X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0), -- {}, --}; -- --static void ipu_buttress_setup(struct ipu7_device *isp) --{ -- struct device *dev = &isp->pdev->dev; -- u32 val; -- -- /* program PB BAR */ --#define WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19) -- writel(0, isp->pb_base + GLOBAL_INTERRUPT_MASK); -- val = readl(isp->pb_base + BAR2_MISC_CONFIG); -- if (is_ipu7(isp->hw_ver) || x86_match_cpu(ipu_misc_cfg_exclusion)) -- val |= 0x100U; -- else -- val |= FIELD_PREP(WRXREQOP_OVRD_VAL_MASK, 0xf) | -- BIT(18) | 0x100U; -- -- writel(val, isp->pb_base + BAR2_MISC_CONFIG); -- val = readl(isp->pb_base + BAR2_MISC_CONFIG); -- -- if (is_ipu8(isp->hw_ver)) { -- writel(BIT(13), isp->pb_base + TLBID_HASH_ENABLE_63_32); -- writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -- dev_dbg(dev, "IPU8 TLBID_HASH %x %x\n", -- readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -- readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -- } else if (is_ipu7p5(isp->hw_ver)) { -- writel(BIT(14), isp->pb_base + TLBID_HASH_ENABLE_63_32); -- writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -- dev_dbg(dev, "IPU7P5 TLBID_HASH %x %x\n", -- readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -- readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -- } else { -- writel(BIT(22), isp->pb_base + TLBID_HASH_ENABLE_63_32); -- writel(BIT(1), isp->pb_base + TLBID_HASH_ENABLE_127_96); -- dev_dbg(dev, "TLBID_HASH %x %x\n", -- readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -- readl(isp->pb_base + TLBID_HASH_ENABLE_127_96)); -- } -- -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_CLEAR); -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_MASK); -- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -- /* LNL SW workaround for PS PD hang when PS sub-domain during PD */ -- writel(PS_FSM_CG, isp->base + BUTTRESS_REG_CG_CTRL_BITS); --} -- --void ipu_buttress_restore(struct ipu7_device *isp) --{ -- struct ipu_buttress *b = &isp->buttress; -- -- ipu_buttress_setup(isp); -- -- writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_IDLE_WDT); --} -- --int ipu_buttress_init(struct ipu7_device *isp) --{ -- int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; -- struct ipu_buttress *b = &isp->buttress; -- struct device *dev = &isp->pdev->dev; -- u32 val; -- -- mutex_init(&b->power_mutex); -- mutex_init(&b->auth_mutex); -- mutex_init(&b->cons_mutex); -- mutex_init(&b->ipc_mutex); -- init_completion(&b->cse.send_complete); -- init_completion(&b->cse.recv_complete); -- -- b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; -- b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; -- b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; -- b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; -- b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; -- b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; -- b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; -- b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; -- -- isp->secure_mode = ipu_buttress_get_secure_mode(isp); -- val = readl(isp->base + BUTTRESS_REG_IPU_SKU); -- dev_info(dev, "IPU%u SKU %u in %s mode mask 0x%x\n", val & 0xfU, -- (val >> 4) & 0x7U, isp->secure_mode ? "secure" : "non-secure", -- readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); -- b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_IDLE_WDT); -- b->ref_clk = 384; -- -- ipu_buttress_setup(isp); -- -- /* Retry couple of times in case of CSE initialization is delayed */ -- do { -- ret = ipu_buttress_ipc_reset(isp, &b->cse); -- if (ret) { -- dev_warn(dev, "IPC reset protocol failed, retrying\n"); -- } else { -- dev_dbg(dev, "IPC reset done\n"); -- return 0; -- } -- } while (ipc_reset_retry--); -- -- dev_err(dev, "IPC reset protocol failed\n"); -- -- mutex_destroy(&b->power_mutex); -- mutex_destroy(&b->auth_mutex); -- mutex_destroy(&b->cons_mutex); -- mutex_destroy(&b->ipc_mutex); -- -- return ret; --} -- --void ipu_buttress_exit(struct ipu7_device *isp) --{ -- struct ipu_buttress *b = &isp->buttress; -- -- writel(0, isp->base + BUTTRESS_REG_IRQ_ENABLE); -- mutex_destroy(&b->power_mutex); -- mutex_destroy(&b->auth_mutex); -- mutex_destroy(&b->cons_mutex); -- mutex_destroy(&b->ipc_mutex); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.h b/drivers/media/pci/intel/ipu7/ipu7-buttress.h -deleted file mode 100644 -index 8da7dd612575..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-buttress.h -+++ /dev/null -@@ -1,77 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_BUTTRESS_H --#define IPU7_BUTTRESS_H -- --#include --#include --#include --#include -- --struct device; --struct ipu7_device; -- --struct ipu_buttress_ctrl { -- u32 subsys_id; -- u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; -- u32 ratio; -- u32 ratio_shift; -- u32 cdyn; -- u32 cdyn_shift; -- u32 ovrd_clk; -- u32 own_clk_ack; --}; -- --struct ipu_buttress_ipc { -- struct completion send_complete; -- struct completion recv_complete; -- u32 nack; -- u32 nack_mask; -- u32 recv_data; -- u32 csr_out; -- u32 csr_in; -- u32 db0_in; -- u32 db0_out; -- u32 data0_out; -- u32 data0_in; --}; -- --struct ipu_buttress { -- struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; -- struct ipu_buttress_ipc cse; -- u32 psys_min_freq; -- u32 wdt_cached_value; -- u8 psys_force_ratio; -- bool force_suspend; -- u32 ref_clk; --}; -- --int ipu_buttress_ipc_reset(struct ipu7_device *isp, -- struct ipu_buttress_ipc *ipc); --int ipu_buttress_powerup(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl); --int ipu_buttress_powerdown(struct device *dev, -- const struct ipu_buttress_ctrl *ctrl); --bool ipu_buttress_get_secure_mode(struct ipu7_device *isp); --int ipu_buttress_authenticate(struct ipu7_device *isp); --int ipu_buttress_reset_authentication(struct ipu7_device *isp); --bool ipu_buttress_auth_done(struct ipu7_device *isp); --int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq); --int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq); --int ipu_buttress_start_tsc_sync(struct ipu7_device *isp); --void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val); --u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp); -- --irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr); --irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr); --int ipu_buttress_init(struct ipu7_device *isp); --void ipu_buttress_exit(struct ipu7_device *isp); --void ipu_buttress_csi_port_config(struct ipu7_device *isp, -- u32 legacy, u32 combo); --void ipu_buttress_restore(struct ipu7_device *isp); --void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp); --void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp); --#endif /* IPU7_BUTTRESS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.c b/drivers/media/pci/intel/ipu7/ipu7-cpd.c -deleted file mode 100644 -index 25dd71e1809e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-cpd.c -+++ /dev/null -@@ -1,277 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2015 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-cpd.h" -- --/* $CPD */ --#define CPD_HDR_MARK 0x44504324 -- --/* Maximum size is 4K DWORDs or 16KB */ --#define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) -- --#define CPD_MANIFEST_IDX 0 --#define CPD_BINARY_START_IDX 1U --#define CPD_METADATA_START_IDX 2U --#define CPD_BINARY_NUM 2U /* ISYS + PSYS */ --/* -- * Entries include: -- * 1 manifest entry. -- * 1 metadata entry for each sub system(ISYS and PSYS). -- * 1 binary entry for each sub system(ISYS and PSYS). -- */ --#define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U) -- --#define CPD_METADATA_ATTR 0xa --#define CPD_METADATA_IPL 0x1c --#define ONLINE_METADATA_SIZE 128U --#define ONLINE_METADATA_LINES 6U -- --struct ipu7_cpd_hdr { -- u32 hdr_mark; -- u32 ent_cnt; -- u8 hdr_ver; -- u8 ent_ver; -- u8 hdr_len; -- u8 rsvd; -- u8 partition_name[4]; -- u32 crc32; --} __packed; -- --struct ipu7_cpd_ent { -- u8 name[12]; -- u32 offset; -- u32 len; -- u8 rsvd[4]; --} __packed; -- --struct ipu7_cpd_metadata_hdr { -- u32 type; -- u32 len; --} __packed; -- --struct ipu7_cpd_metadata_attr { -- struct ipu7_cpd_metadata_hdr hdr; -- u8 compression_type; -- u8 encryption_type; -- u8 rsvd[2]; -- u32 uncompressed_size; -- u32 compressed_size; -- u32 module_id; -- u8 hash[48]; --} __packed; -- --struct ipu7_cpd_metadata_ipl { -- struct ipu7_cpd_metadata_hdr hdr; -- u32 param[4]; -- u8 rsvd[8]; --} __packed; -- --struct ipu7_cpd_metadata { -- struct ipu7_cpd_metadata_attr attr; -- struct ipu7_cpd_metadata_ipl ipl; --} __packed; -- --static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx) --{ -- const struct ipu7_cpd_hdr *cpd_hdr = cpd; -- -- return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx; --} -- --#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0) -- --static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) --{ -- struct ipu7_cpd_ent *cpd_ent = -- ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -- -- return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); --} -- --static int ipu7_cpd_validate_cpd(struct ipu7_device *isp, -- const void *cpd, unsigned long data_size) --{ -- const struct ipu7_cpd_hdr *cpd_hdr = cpd; -- struct device *dev = &isp->pdev->dev; -- struct ipu7_cpd_ent *ent; -- unsigned int i; -- u8 len; -- -- len = cpd_hdr->hdr_len; -- -- /* Ensure cpd hdr is within moduledata */ -- if (data_size < len) { -- dev_err(dev, "Invalid CPD moduledata size\n"); -- return -EINVAL; -- } -- -- /* Check for CPD file marker */ -- if (cpd_hdr->hdr_mark != CPD_HDR_MARK) { -- dev_err(dev, "Invalid CPD header marker\n"); -- return -EINVAL; -- } -- -- /* Sanity check for CPD entry header */ -- if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) { -- dev_err(dev, "Invalid CPD entry number %d\n", -- cpd_hdr->ent_cnt); -- return -EINVAL; -- } -- if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { -- dev_err(dev, "Invalid CPD entry headers\n"); -- return -EINVAL; -- } -- -- /* Ensure that all entries are within moduledata */ -- ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len); -- for (i = 0; i < cpd_hdr->ent_cnt; i++) { -- if (data_size < ent->offset || -- data_size - ent->offset < ent->len) { -- dev_err(dev, "Invalid CPD entry %d\n", i); -- return -EINVAL; -- } -- ent++; -- } -- -- return 0; --} -- --static int ipu7_cpd_validate_metadata(struct ipu7_device *isp, -- const void *cpd, int idx) --{ -- const struct ipu7_cpd_ent *cpd_ent = -- ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -- const struct ipu7_cpd_metadata *metadata = -- ipu7_cpd_get_metadata(cpd, idx); -- struct device *dev = &isp->pdev->dev; -- -- /* Sanity check for metadata size */ -- if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { -- dev_err(dev, "Invalid metadata size\n"); -- return -EINVAL; -- } -- -- /* Validate type and length of metadata sections */ -- if (metadata->attr.hdr.type != CPD_METADATA_ATTR) { -- dev_err(dev, "Invalid metadata attr type (%d)\n", -- metadata->attr.hdr.type); -- return -EINVAL; -- } -- if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { -- dev_err(dev, "Invalid metadata attr size (%d)\n", -- metadata->attr.hdr.len); -- return -EINVAL; -- } -- if (metadata->ipl.hdr.type != CPD_METADATA_IPL) { -- dev_err(dev, "Invalid metadata ipl type (%d)\n", -- metadata->ipl.hdr.type); -- return -EINVAL; -- } -- if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { -- dev_err(dev, "Invalid metadata ipl size (%d)\n", -- metadata->ipl.hdr.len); -- return -EINVAL; -- } -- -- return 0; --} -- --int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file, -- unsigned long cpd_file_size) --{ -- struct device *dev = &isp->pdev->dev; -- struct ipu7_cpd_ent *ent; -- unsigned int i; -- int ret; -- char *buf; -- -- ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size); -- if (ret) { -- dev_err(dev, "Invalid CPD in file\n"); -- return -EINVAL; -- } -- -- /* Sanity check for manifest size */ -- ent = ipu7_cpd_get_manifest(cpd_file); -- if (ent->len > MAX_MANIFEST_SIZE) { -- dev_err(dev, "Invalid manifest size\n"); -- return -EINVAL; -- } -- -- /* Validate metadata */ -- for (i = 0; i < CPD_BINARY_NUM; i++) { -- ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); -- if (ret) { -- dev_err(dev, "Invalid metadata%d\n", i); -- return ret; -- } -- } -- -- /* Get fw binary version. */ -- buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL); -- if (!buf) -- return -ENOMEM; -- for (i = 0; i < CPD_BINARY_NUM; i++) { -- char *lines[ONLINE_METADATA_LINES]; -- char *info = buf; -- unsigned int l; -- -- ent = ipu7_cpd_get_entry(cpd_file, -- CPD_BINARY_START_IDX + i * 2U); -- memcpy(info, (u8 *)cpd_file + ent->offset + ent->len - -- ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE); -- for (l = 0; l < ONLINE_METADATA_LINES; l++) { -- lines[l] = strsep((char **)&info, "\n"); -- if (!lines[l]) -- break; -- } -- if (l < ONLINE_METADATA_LINES) { -- dev_err(dev, "Failed to parse fw binary%d info.\n", i); -- continue; -- } -- dev_info(dev, "FW binary%d info:\n", i); -- dev_info(dev, "Name: %s\n", lines[1]); -- dev_info(dev, "Version: %s\n", lines[2]); -- dev_info(dev, "Timestamp: %s\n", lines[3]); -- dev_info(dev, "Commit: %s\n", lines[4]); -- } -- kfree(buf); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7"); -- --int ipu7_cpd_copy_binary(const void *cpd, const char *name, -- void *code_region, u32 *entry) --{ -- unsigned int i; -- -- for (i = 0; i < CPD_BINARY_NUM; i++) { -- const struct ipu7_cpd_ent *binary = -- ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U); -- const struct ipu7_cpd_metadata *metadata = -- ipu7_cpd_get_metadata(cpd, i); -- -- if (!strncmp(binary->name, name, sizeof(binary->name))) { -- memcpy(code_region + metadata->ipl.param[0], -- cpd + binary->offset, binary->len); -- *entry = metadata->ipl.param[2]; -- return 0; -- } -- } -- -- return -ENOENT; --} --EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.h b/drivers/media/pci/intel/ipu7/ipu7-cpd.h -deleted file mode 100644 -index b4178848c6b9..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-cpd.h -+++ /dev/null -@@ -1,16 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2015 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_CPD_H --#define IPU7_CPD_H -- --struct ipu7_device; -- --int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, -- const void *cpd_file, -- unsigned long cpd_file_size); --int ipu7_cpd_copy_binary(const void *cpd, const char *name, -- void *code_region, u32 *entry); --#endif /* IPU7_CPD_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.c b/drivers/media/pci/intel/ipu7/ipu7-dma.c -deleted file mode 100644 -index d974e13e7933..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-dma.c -+++ /dev/null -@@ -1,478 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-mmu.h" -- --struct vm_info { -- struct list_head list; -- struct page **pages; -- dma_addr_t ipu7_iova; -- void *vaddr; -- unsigned long size; --}; -- --static struct vm_info *get_vm_info(struct ipu7_mmu *mmu, dma_addr_t iova) --{ -- struct vm_info *info, *save; -- -- list_for_each_entry_safe(info, save, &mmu->vma_list, list) { -- if (iova >= info->ipu7_iova && -- iova < (info->ipu7_iova + info->size)) -- return info; -- } -- -- return NULL; --} -- --static void __clear_buffer(struct page *page, size_t size, unsigned long attrs) --{ -- void *ptr; -- -- if (!page) -- return; -- /* -- * Ensure that the allocated pages are zeroed, and that any data -- * lurking in the kernel direct-mapped region is invalidated. -- */ -- ptr = page_address(page); -- memset(ptr, 0, size); -- if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -- clflush_cache_range(ptr, size); --} -- --static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs) --{ -- unsigned int count = PHYS_PFN(size); -- unsigned int array_size = count * sizeof(struct page *); -- struct page **pages; -- int i = 0; -- -- pages = kvzalloc(array_size, GFP_KERNEL); -- if (!pages) -- return NULL; -- -- gfp |= __GFP_NOWARN; -- -- while (count) { -- int j, order = __fls(count); -- -- pages[i] = alloc_pages(gfp, order); -- while (!pages[i] && order) -- pages[i] = alloc_pages(gfp, --order); -- if (!pages[i]) -- goto error; -- -- if (order) { -- split_page(pages[i], order); -- j = 1U << order; -- while (j--) -- pages[i + j] = pages[i] + j; -- } -- -- __clear_buffer(pages[i], PAGE_SIZE << order, attrs); -- i += 1U << order; -- count -= 1U << order; -- } -- -- return pages; --error: -- while (i--) -- if (pages[i]) -- __free_pages(pages[i], 0); -- kvfree(pages); -- return NULL; --} -- --static void __free_buffer(struct page **pages, size_t size, unsigned long attrs) --{ -- unsigned int count = PHYS_PFN(size); -- unsigned int i; -- -- for (i = 0; i < count && pages[i]; i++) { -- __clear_buffer(pages[i], PAGE_SIZE, attrs); -- __free_pages(pages[i], 0); -- } -- -- kvfree(pages); --} -- --void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -- size_t size) --{ -- void *vaddr; -- u32 offset; -- struct vm_info *info; -- struct ipu7_mmu *mmu = sys->mmu; -- -- info = get_vm_info(mmu, dma_handle); -- if (WARN_ON(!info)) -- return; -- -- offset = dma_handle - info->ipu7_iova; -- if (WARN_ON(size > (info->size - offset))) -- return; -- -- vaddr = info->vaddr + offset; -- clflush_cache_range(vaddr, size); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_single, "INTEL_IPU7"); -- --void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- unsigned int nents) --{ -- struct scatterlist *sg; -- int i; -- -- for_each_sg(sglist, sg, nents, i) -- clflush_cache_range(sg_virt(sg), sg->length); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sg, "INTEL_IPU7"); -- --void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt) --{ -- ipu7_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sgtable, "INTEL_IPU7"); -- --void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -- dma_addr_t *dma_handle, gfp_t gfp, -- unsigned long attrs) --{ -- struct device *dev = &sys->auxdev.dev; -- struct pci_dev *pdev = sys->isp->pdev; -- dma_addr_t pci_dma_addr, ipu7_iova; -- struct ipu7_mmu *mmu = sys->mmu; -- struct vm_info *info; -- unsigned long count; -- struct page **pages; -- struct iova *iova; -- unsigned int i; -- int ret; -- -- info = kzalloc(sizeof(*info), GFP_KERNEL); -- if (!info) -- return NULL; -- -- size = PAGE_ALIGN(size); -- count = PHYS_PFN(size); -- -- iova = alloc_iova(&mmu->dmap->iovad, count, -- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -- if (!iova) -- goto out_kfree; -- -- pages = __alloc_buffer(size, gfp, attrs); -- if (!pages) -- goto out_free_iova; -- -- dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", -- size, iova->pfn_lo, iova->pfn_hi); -- for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { -- pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, -- PAGE_SIZE, DMA_BIDIRECTIONAL, -- attrs); -- dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", -- &pci_dma_addr); -- if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { -- dev_err(dev, "pci_dma_mapping for page[%d] failed", i); -- goto out_unmap; -- } -- -- ret = ipu7_mmu_map(mmu->dmap->mmu_info, -- PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, -- PAGE_SIZE); -- if (ret) { -- dev_err(dev, "ipu7_mmu_map for pci_dma[%d] %pad failed", -- i, &pci_dma_addr); -- dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, -- PAGE_SIZE, DMA_BIDIRECTIONAL, -- attrs); -- goto out_unmap; -- } -- } -- -- info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); -- if (!info->vaddr) -- goto out_unmap; -- -- *dma_handle = PFN_PHYS(iova->pfn_lo); -- -- info->pages = pages; -- info->ipu7_iova = *dma_handle; -- info->size = size; -- list_add(&info->list, &mmu->vma_list); -- -- return info->vaddr; -- --out_unmap: -- while (i--) { -- ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -- pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -- ipu7_iova); -- dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -- DMA_BIDIRECTIONAL, attrs); -- -- ipu7_mmu_unmap(mmu->dmap->mmu_info, ipu7_iova, PAGE_SIZE); -- } -- -- __free_buffer(pages, size, attrs); -- --out_free_iova: -- __free_iova(&mmu->dmap->iovad, iova); --out_kfree: -- kfree(info); -- -- return NULL; --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_alloc, "INTEL_IPU7"); -- --void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -- dma_addr_t dma_handle, unsigned long attrs) --{ -- struct ipu7_mmu *mmu = sys->mmu; -- struct pci_dev *pdev = sys->isp->pdev; -- struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); -- dma_addr_t pci_dma_addr, ipu7_iova; -- struct vm_info *info; -- struct page **pages; -- unsigned int i; -- -- if (WARN_ON(!iova)) -- return; -- -- info = get_vm_info(mmu, dma_handle); -- if (WARN_ON(!info)) -- return; -- -- if (WARN_ON(!info->vaddr)) -- return; -- -- if (WARN_ON(!info->pages)) -- return; -- -- list_del(&info->list); -- -- size = PAGE_ALIGN(size); -- -- pages = info->pages; -- -- vunmap(vaddr); -- -- for (i = 0; i < PHYS_PFN(size); i++) { -- ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -- pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -- ipu7_iova); -- dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -- DMA_BIDIRECTIONAL, attrs); -- } -- -- ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- -- __free_buffer(pages, size, attrs); -- -- mmu->tlb_invalidate(mmu); -- -- __free_iova(&mmu->dmap->iovad, iova); -- -- kfree(info); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_free, "INTEL_IPU7"); -- --int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -- void *addr, dma_addr_t iova, size_t size, -- unsigned long attrs) --{ -- struct ipu7_mmu *mmu = sys->mmu; -- size_t count = PFN_UP(size); -- struct vm_info *info; -- size_t i; -- int ret; -- -- info = get_vm_info(mmu, iova); -- if (!info) -- return -EFAULT; -- -- if (!info->vaddr) -- return -EFAULT; -- -- if (vma->vm_start & ~PAGE_MASK) -- return -EINVAL; -- -- if (size > info->size) -- return -EFAULT; -- -- for (i = 0; i < count; i++) { -- ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), -- info->pages[i]); -- if (ret < 0) -- return ret; -- } -- -- return 0; --} -- --void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_mmu *mmu = sys->mmu; -- struct iova *iova = find_iova(&mmu->dmap->iovad, -- PHYS_PFN(sg_dma_address(sglist))); -- struct scatterlist *sg; -- dma_addr_t pci_dma_addr; -- unsigned int i; -- -- if (!nents) -- return; -- -- if (WARN_ON(!iova)) -- return; -- -- /* -- * Before IPU7 mmu unmap, return the pci dma address back to sg -- * assume the nents is less than orig_nents as the least granule -- * is 1 SZ_4K page -- */ -- dev_dbg(dev, "trying to unmap concatenated %u ents\n", nents); -- for_each_sg(sglist, sg, nents, i) { -- dev_dbg(dev, "unmap sg[%d] %pad size %u\n", i, -- &sg_dma_address(sg), sg_dma_len(sg)); -- pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -- sg_dma_address(sg)); -- dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", -- &pci_dma_addr, i); -- sg_dma_address(sg) = pci_dma_addr; -- } -- -- dev_dbg(dev, "ipu7_mmu_unmap low pfn %lu high pfn %lu\n", -- iova->pfn_lo, iova->pfn_hi); -- ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- -- mmu->tlb_invalidate(mmu); -- __free_iova(&mmu->dmap->iovad, iova); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7"); -- --int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_mmu *mmu = sys->mmu; -- struct scatterlist *sg; -- struct iova *iova; -- size_t npages = 0; -- unsigned long iova_addr; -- int i; -- -- for_each_sg(sglist, sg, nents, i) { -- if (sg->offset) { -- dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", -- i, sg->offset); -- return -EFAULT; -- } -- } -- -- for_each_sg(sglist, sg, nents, i) -- npages += PFN_UP(sg_dma_len(sg)); -- -- dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n", -- nents, npages); -- -- if (attrs & DMA_ATTR_RESERVE_REGION) { -- /* -- * Reserve iova with size aligned to IPU_FW_CODE_REGION_SIZE. -- * Only apply for non-secure mode. -- */ -- unsigned long lo, hi; -- -- lo = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_START); -- hi = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_END) - 1U; -- iova = reserve_iova(&mmu->dmap->iovad, lo, hi); -- if (!iova) { -- dev_err(dev, "Reserve iova[%lx:%lx] failed.\n", lo, hi); -- return -ENOMEM; -- } -- dev_dbg(dev, "iova[%lx:%lx] reserved for FW code.\n", lo, hi); -- } else { -- iova = alloc_iova(&mmu->dmap->iovad, npages, -- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), -- 0); -- if (!iova) -- return 0; -- } -- -- dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, -- iova->pfn_hi); -- -- iova_addr = iova->pfn_lo; -- for_each_sg(sglist, sg, nents, i) { -- phys_addr_t iova_pa; -- int ret; -- -- iova_pa = PFN_PHYS(iova_addr); -- dev_dbg(dev, "mapping entry %d: iova %pap phy %pap size %d\n", -- i, &iova_pa, &sg_dma_address(sg), sg_dma_len(sg)); -- -- ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -- sg_dma_address(sg), -- PAGE_ALIGN(sg_dma_len(sg))); -- if (ret) -- goto out_fail; -- -- sg_dma_address(sg) = PFN_PHYS(iova_addr); -- -- iova_addr += PFN_UP(sg_dma_len(sg)); -- } -- -- dev_dbg(dev, "dmamap %d ents %zu pages mapped\n", nents, npages); -- -- return nents; -- --out_fail: -- ipu7_dma_unmap_sg(sys, sglist, i, dir, attrs); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sg, "INTEL_IPU7"); -- --int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs) --{ -- int nents; -- -- nents = ipu7_dma_map_sg(sys, sgt->sgl, sgt->nents, dir, attrs); -- if (nents < 0) -- return nents; -- -- sgt->nents = nents; -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sgtable, "INTEL_IPU7"); -- --void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs) --{ -- ipu7_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sgtable, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.h b/drivers/media/pci/intel/ipu7/ipu7-dma.h -deleted file mode 100644 -index fe789af5e664..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-dma.h -+++ /dev/null -@@ -1,46 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* Copyright (C) 2013--2025 Intel Corporation */ -- --#ifndef IPU7_DMA_H --#define IPU7_DMA_H -- --#include --#include --#include --#include --#include -- --#include "ipu7-bus.h" -- --#define DMA_ATTR_RESERVE_REGION BIT(31) --struct ipu7_mmu_info; -- --struct ipu7_dma_mapping { -- struct ipu7_mmu_info *mmu_info; -- struct iova_domain iovad; --}; -- --void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -- size_t size); --void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- unsigned int nents); --void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt); --void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -- dma_addr_t *dma_handle, gfp_t gfp, -- unsigned long attrs); --void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -- dma_addr_t dma_handle, unsigned long attrs); --int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -- void *addr, dma_addr_t iova, size_t size, -- unsigned long attrs); --int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs); --void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -- int nents, enum dma_data_direction dir, -- unsigned long attrs); --int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs); --void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -- enum dma_data_direction dir, unsigned long attrs); --#endif /* IPU7_DMA_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -deleted file mode 100644 -index 2958e39a359e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -+++ /dev/null -@@ -1,389 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_insys_config_abi.h" --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7.h" --#include "ipu7-boot.h" --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-fw-isys.h" --#include "ipu7-isys.h" --#include "ipu7-platform-regs.h" --#include "ipu7-syscom.h" -- --static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = { -- "STREAM_OPEN", -- "STREAM_START_AND_CAPTURE", -- "STREAM_CAPTURE", -- "STREAM_ABORT", -- "STREAM_FLUSH", -- "STREAM_CLOSE" --}; -- --int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, -- void *cpu_mapped_buf, -- dma_addr_t dma_mapped_buf, -- size_t size, u16 send_type) --{ -- struct ipu7_syscom_context *ctx = isys->adev->syscom; -- struct device *dev = &isys->adev->auxdev.dev; -- struct ipu7_insys_send_queue_token *token; -- -- if (send_type >= N_IPU_INSYS_SEND_TYPE) -- return -EINVAL; -- -- dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); -- -- /* -- * Time to flush cache in case we have some payload. Not all messages -- * have that -- */ -- if (cpu_mapped_buf) -- clflush_cache_range(cpu_mapped_buf, size); -- -- token = ipu7_syscom_get_token(ctx, stream_handle + -- IPU_INSYS_INPUT_MSG_QUEUE); -- if (!token) -- return -EBUSY; -- -- token->addr = dma_mapped_buf; -- token->buf_handle = (unsigned long)cpu_mapped_buf; -- token->send_type = send_type; -- token->stream_id = stream_handle; -- token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE; -- -- ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE); -- /* now wakeup FW */ -- ipu_buttress_wakeup_is_uc(isys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, u16 send_type) --{ -- return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, -- send_type); --} -- --int ipu7_fw_isys_init(struct ipu7_isys *isys) --{ -- struct syscom_queue_config *queue_configs; -- struct ipu7_bus_device *adev = isys->adev; -- struct device *dev = &adev->auxdev.dev; -- struct ipu7_insys_config *isys_config; -- struct ipu7_syscom_context *syscom; -- dma_addr_t isys_config_dma_addr; -- unsigned int i, num_queues; -- u32 freq; -- u8 major; -- int ret; -- -- /* Allocate and init syscom context. */ -- syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -- GFP_KERNEL); -- if (!syscom) -- return -ENOMEM; -- -- adev->syscom = syscom; -- syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; -- syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; -- num_queues = syscom->num_input_queues + syscom->num_output_queues; -- queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -- GFP_KERNEL); -- if (!queue_configs) { -- ipu7_fw_isys_release(isys); -- return -ENOMEM; -- } -- syscom->queue_configs = queue_configs; -- queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity = -- IPU_ISYS_SIZE_RECV_QUEUE; -- queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = -- sizeof(struct ipu7_insys_resp); -- queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity = -- IPU_ISYS_SIZE_LOG_QUEUE; -- queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = -- sizeof(struct ipu7_insys_resp); -- queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; -- queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; -- -- queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity = -- IPU_ISYS_MAX_STREAMS; -- queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes = -- sizeof(struct ipu7_insys_send_queue_token); -- -- for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) { -- queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE; -- queue_configs[i].token_size_in_bytes = -- sizeof(struct ipu7_insys_send_queue_token); -- } -- -- /* Allocate ISYS subsys config. */ -- isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config), -- &isys_config_dma_addr, GFP_KERNEL, 0); -- if (!isys_config) { -- dev_err(dev, "Failed to allocate isys subsys config.\n"); -- ipu7_fw_isys_release(isys); -- return -ENOMEM; -- } -- isys->subsys_config = isys_config; -- isys->subsys_config_dma_addr = isys_config_dma_addr; -- memset(isys_config, 0, sizeof(struct ipu7_insys_config)); -- isys_config->logger_config.use_source_severity = 0; -- isys_config->logger_config.use_channels_enable_bitmask = 1; -- isys_config->logger_config.channels_enable_bitmask = -- LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK; -- isys_config->logger_config.hw_printf_buffer_base_addr = 0U; -- isys_config->logger_config.hw_printf_buffer_size_bytes = 0U; -- isys_config->wdt_config.wdt_timer1_us = 0; -- isys_config->wdt_config.wdt_timer2_us = 0; -- ret = ipu_buttress_get_isys_freq(adev->isp, &freq); -- if (ret) { -- dev_err(dev, "Failed to get ISYS frequency.\n"); -- ipu7_fw_isys_release(isys); -- return ret; -- } -- -- ipu7_dma_sync_single(adev, isys_config_dma_addr, -- sizeof(struct ipu7_insys_config)); -- -- major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U; -- ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -- freq, isys_config_dma_addr, major); -- if (ret) -- ipu7_fw_isys_release(isys); -- -- return ret; --} -- --void ipu7_fw_isys_release(struct ipu7_isys *isys) --{ -- struct ipu7_bus_device *adev = isys->adev; -- -- ipu7_boot_release_boot_config(adev); -- if (isys->subsys_config) { -- ipu7_dma_free(adev, -- sizeof(struct ipu7_insys_config), -- isys->subsys_config, -- isys->subsys_config_dma_addr, 0); -- isys->subsys_config = NULL; -- isys->subsys_config_dma_addr = 0; -- } --} -- --int ipu7_fw_isys_open(struct ipu7_isys *isys) --{ -- return ipu7_boot_start_fw(isys->adev); --} -- --int ipu7_fw_isys_close(struct ipu7_isys *isys) --{ -- return ipu7_boot_stop_fw(isys->adev); --} -- --struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys) --{ -- return (struct ipu7_insys_resp *) -- ipu7_syscom_get_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_MSG_QUEUE); --} -- --void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) --{ -- ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); --} -- --#ifdef ENABLE_FW_OFFLINE_LOGGER --int ipu7_fw_isys_get_log(struct ipu7_isys *isys) --{ -- u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -- struct device *dev = &isys->adev->auxdev.dev; -- struct isys_fw_log *fw_log = isys->fw_log; -- struct ia_gofo_msg_log *log_msg; -- u8 msg_type, msg_len; -- u32 count, fmt_id; -- void *token; -- -- token = ipu7_syscom_get_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- if (!token) -- return -ENODATA; -- -- while (token) { -- log_msg = (struct ia_gofo_msg_log *)token; -- -- msg_type = log_msg->header.tlv_header.tlv_type; -- msg_len = log_msg->header.tlv_header.tlv_len32; -- if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -- dev_warn(dev, "Invalid msg data from Log queue!\n"); -- -- count = log_msg->log_info_ts.log_info.log_counter; -- fmt_id = log_msg->log_info_ts.log_info.fmt_id; -- if (count > fw_log->count + 1) -- dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -- count, fw_log->count); -- -- if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -- dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -- ipu7_syscom_put_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- return -EIO; -- } -- -- if (log_size + fw_log->head - fw_log->addr > -- FW_LOG_BUF_SIZE) -- fw_log->head = fw_log->addr; -- -- memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -- sizeof(struct ia_gofo_msg_log_info_ts)); -- -- fw_log->count = count; -- fw_log->head += log_size; -- fw_log->size += log_size; -- -- ipu7_syscom_put_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- -- token = ipu7_syscom_get_token(isys->adev->syscom, -- IPU_INSYS_OUTPUT_LOG_QUEUE); -- }; -- -- return 0; --} -- --#endif --void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -- struct ipu7_insys_stream_cfg *cfg) --{ -- unsigned int i; -- -- dev_dbg(dev, "---------------------------\n"); -- dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); -- -- dev_dbg(dev, ".port id %d\n", cfg->port_id); -- dev_dbg(dev, ".vc %d\n", cfg->vc); -- dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins); -- dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins); -- dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map); -- -- for (i = 0; i < cfg->nof_input_pins; i++) { -- dev_dbg(dev, ".input_pin[%d]:\n", i); -- dev_dbg(dev, "\t.dt = 0x%0x\n", -- cfg->input_pins[i].dt); -- dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n", -- cfg->input_pins[i].disable_mipi_unpacking); -- dev_dbg(dev, "\t.dt_rename_mode = %d\n", -- cfg->input_pins[i].dt_rename_mode); -- dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", -- cfg->input_pins[i].mapped_dt); -- dev_dbg(dev, "\t.input_res = %d x %d\n", -- cfg->input_pins[i].input_res.width, -- cfg->input_pins[i].input_res.height); -- dev_dbg(dev, "\t.sync_msg_map = 0x%x\n", -- cfg->input_pins[i].sync_msg_map); -- } -- -- for (i = 0; i < cfg->nof_output_pins; i++) { -- dev_dbg(dev, ".output_pin[%d]:\n", i); -- dev_dbg(dev, "\t.input_pin_id = %d\n", -- cfg->output_pins[i].input_pin_id); -- dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); -- dev_dbg(dev, "\t.send_irq = %d\n", -- cfg->output_pins[i].send_irq); -- dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); -- -- dev_dbg(dev, "\t.link.buffer_lines = %d\n", -- cfg->output_pins[i].link.buffer_lines); -- dev_dbg(dev, "\t.link.foreign_key = %d\n", -- cfg->output_pins[i].link.foreign_key); -- dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n", -- cfg->output_pins[i].link.granularity_pointer_update); -- dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n", -- cfg->output_pins[i].link.msg_link_streaming_mode); -- dev_dbg(dev, "\t.link.pbk_id = %d\n", -- cfg->output_pins[i].link.pbk_id); -- dev_dbg(dev, "\t.link.pbk_slot_id = %d\n", -- cfg->output_pins[i].link.pbk_slot_id); -- dev_dbg(dev, "\t.link.dest = %d\n", -- cfg->output_pins[i].link.dest); -- dev_dbg(dev, "\t.link.use_sw_managed = %d\n", -- cfg->output_pins[i].link.use_sw_managed); -- dev_dbg(dev, "\t.link.is_snoop = %d\n", -- cfg->output_pins[i].link.is_snoop); -- -- dev_dbg(dev, "\t.crop.line_top = %d\n", -- cfg->output_pins[i].crop.line_top); -- dev_dbg(dev, "\t.crop.line_bottom = %d\n", -- cfg->output_pins[i].crop.line_bottom); --#ifdef IPU8_INSYS_NEW_ABI -- dev_dbg(dev, "\t.crop.column_left = %d\n", -- cfg->output_pins[i].crop.column_left); -- dev_dbg(dev, "\t.crop.colunm_right = %d\n", -- cfg->output_pins[i].crop.column_right); --#endif -- -- dev_dbg(dev, "\t.dpcm_enable = %d\n", -- cfg->output_pins[i].dpcm.enable); -- dev_dbg(dev, "\t.dpcm.type = %d\n", -- cfg->output_pins[i].dpcm.type); -- dev_dbg(dev, "\t.dpcm.predictor = %d\n", -- cfg->output_pins[i].dpcm.predictor); --#ifdef IPU8_INSYS_NEW_ABI -- dev_dbg(dev, "\t.upipe_enable = %d\n", -- cfg->output_pins[i].upipe_enable); -- dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); -- dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); -- dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); -- dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); -- dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", -- cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); --#endif -- } -- dev_dbg(dev, "---------------------------\n"); --} -- --void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -- struct ipu7_insys_buffset *buf, -- unsigned int outputs) --{ -- unsigned int i; -- -- dev_dbg(dev, "--------------------------\n"); -- dev_dbg(dev, "IPU_ISYS_BUFF_SET\n"); -- dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map); -- dev_dbg(dev, ".frame_id = %d\n", buf->frame_id); -- dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame); -- -- for (i = 0; i < outputs; i++) { -- dev_dbg(dev, ".output_pin[%d]:\n", i); --#ifndef IPU8_INSYS_NEW_ABI -- dev_dbg(dev, "\t.user_token = %llx\n", -- buf->output_pins[i].user_token); -- dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); --#else -- dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", -- buf->output_pins[i].pin_payload.user_token); -- dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", -- buf->output_pins[i].pin_payload.addr); -- dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", -- buf->output_pins[i].upipe_capture_cfg); --#endif -- } -- dev_dbg(dev, "---------------------------\n"); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -deleted file mode 100644 -index 1235adc9694e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -+++ /dev/null -@@ -1,42 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_FW_ISYS_H --#define IPU7_FW_ISYS_H -- --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --struct device; --struct ipu7_insys_buffset; --struct ipu7_insys_stream_cfg; --struct ipu7_isys; -- --/* From here on type defines not coming from the ISYSAPI interface */ -- --int ipu7_fw_isys_init(struct ipu7_isys *isys); --void ipu7_fw_isys_release(struct ipu7_isys *isys); --int ipu7_fw_isys_open(struct ipu7_isys *isys); --int ipu7_fw_isys_close(struct ipu7_isys *isys); -- --void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -- struct ipu7_insys_stream_cfg *cfg); --void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -- struct ipu7_insys_buffset *buf, -- unsigned int outputs); --int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, u16 send_type); --int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -- const unsigned int stream_handle, -- void *cpu_mapped_buf, -- dma_addr_t dma_mapped_buf, -- size_t size, u16 send_type); --struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); --void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); --#ifdef ENABLE_FW_OFFLINE_LOGGER --int ipu7_fw_isys_get_log(struct ipu7_isys *isys); --#endif --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -deleted file mode 100644 -index b8c5db7ae300..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -+++ /dev/null -@@ -1,1034 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include -- --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-platform-regs.h" --#include "ipu7-isys-csi-phy.h" -- --#define PORT_A 0U --#define PORT_B 1U --#define PORT_C 2U --#define PORT_D 3U -- --#define N_DATA_IDS 8U --static DECLARE_BITMAP(data_ids, N_DATA_IDS); -- --struct ddlcal_counter_ref_s { -- u16 min_mbps; -- u16 max_mbps; -- -- u16 ddlcal_counter_ref; --}; -- --struct ddlcal_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 oa_lanex_hsrx_cdphy_sel_fast; -- u16 ddlcal_max_phase; -- u16 phase_bound; -- u16 ddlcal_dll_fbk; -- u16 ddlcal_ddl_coarse_bank; -- u16 fjump_deskew; -- u16 min_eye_opening_deskew; --}; -- --struct i_thssettle_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 i_thssettle; --}; -- -- /* lane2 for 4l3t, lane1 for 2l2t */ --struct oa_lane_clk_div_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 oa_lane_hsrx_hs_clk_div; --}; -- --struct cdr_fbk_cap_prog_params { -- u16 min_mbps; -- u16 max_mbps; -- u16 val; --}; -- --static const struct ddlcal_counter_ref_s table0[] = { -- { 1500, 1999, 118 }, -- { 2000, 2499, 157 }, -- { 2500, 3499, 196 }, -- { 3500, 4499, 274 }, -- { 4500, 4500, 352 }, -- { } --}; -- --static const struct ddlcal_params table1[] = { -- { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 }, -- { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 }, -- { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 }, -- { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 }, -- { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 }, -- { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 }, -- { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 }, -- { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 }, -- { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 }, -- { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 }, -- { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 }, -- { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 }, -- { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 }, -- { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 }, -- { } --}; -- --static const struct i_thssettle_params table2[] = { -- { 80, 124, 24 }, -- { 125, 249, 20 }, -- { 250, 499, 16 }, -- { 500, 749, 14 }, -- { 750, 1499, 13 }, -- { 1500, 4500, 12 }, -- { } --}; -- --static const struct oa_lane_clk_div_params table6[] = { -- { 80, 159, 0x1 }, -- { 160, 319, 0x2 }, -- { 320, 639, 0x3 }, -- { 640, 1279, 0x4 }, -- { 1280, 2560, 0x5 }, -- { 2561, 4500, 0x6 }, -- { } --}; -- --static const struct cdr_fbk_cap_prog_params table7[] = { -- { 80, 919, 0 }, -- { 920, 1029, 1 }, -- { 1030, 1169, 2 }, -- { 1170, 1349, 3 }, -- { 1350, 1589, 4 }, -- { 1590, 1949, 5 }, -- { 1950, 2499, 6 }, -- { } --}; -- --static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -- -- dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x", -- base + addr - isys_base, data); -- writew(data, base + addr); --} -- --static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -- u16 data; -- -- data = readw(base + addr); -- dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x", -- base + addr - isys_base, data); -- -- return data; --} -- --static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -- struct device *dev = &isys->adev->auxdev.dev; -- -- dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, data); -- writel(data, base + addr); -- dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, readl(base + addr)); --} -- --static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) --{ -- void __iomem *isys_base = isys->pdata->base; -- u32 gpreg = isys->pdata->ipdata->csi2.gpreg; -- void __iomem *base = isys_base + gpreg + 0x1000 * id; -- struct device *dev = &isys->adev->auxdev.dev; -- -- dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, data); -- writel(data, base + addr); -- dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x", -- base + addr - isys_base, readl(base + addr)); --} -- --static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr) --{ -- void __iomem *isys_base = isys->pdata->base; -- void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -- u32 data; -- -- data = readl(base + addr); -- dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x", -- base + addr - isys_base, data); -- -- return data; --} -- --static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -- u16 val, u8 lo, u8 hi) --{ -- u32 temp, mask; -- -- WARN_ON(lo > hi); -- WARN_ON(hi > 15); -- -- mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -- temp = dwc_phy_read(isys, id, addr); -- temp &= ~mask; -- temp |= (val << lo) & mask; -- dwc_phy_write(isys, id, addr, temp); --} -- --static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -- u32 val, u8 hi, u8 lo) --{ -- u32 temp, mask; -- -- WARN_ON(lo > hi); -- -- mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -- temp = dwc_csi_read(isys, id, addr); -- temp &= ~mask; -- temp |= (val << lo) & mask; -- dwc_csi_write(isys, id, addr, temp); --} -- --static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- u32 id, lanes, phy_mode; -- u32 val; -- -- id = csi2->port; -- lanes = csi2->nlanes; -- phy_mode = csi2->phy_mode; -- dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u", -- id, lanes, phy_mode); -- -- val = dwc_csi_read(isys, id, VERSION); -- dev_dbg(dev, "csi-%d controller version = 0x%x", id, val); -- -- /* num of active data lanes */ -- dwc_csi_write(isys, id, N_LANES, lanes - 1); -- dwc_csi_write(isys, id, CDPHY_MODE, phy_mode); -- dwc_csi_write(isys, id, VC_EXTENSION, 0); -- -- /* only mask PHY_FATAL and PKT_FATAL interrupts */ -- dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff); -- dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3); -- dwc_csi_write(isys, id, INT_MSK_PHY, 0x0); -- dwc_csi_write(isys, id, INT_MSK_LINE, 0x0); -- dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0); -- dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0); -- dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0); --} -- --static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id) --{ -- dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0); -- dwc_csi_write(isys, id, DPHY_RSTZ, 0); -- dwc_csi_write(isys, id, CSI2_RESETN, 0); -- gpreg_write(isys, id, PHY_RESET, 0); -- gpreg_write(isys, id, PHY_SHUTDOWN, 0); --} -- --/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */ --static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) --{ -- struct ipu7_isys *isys = csi2->isys; -- u32 reg, n; -- u8 lo, hi; -- int ret; -- -- dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", -- id, vc, dt); -- -- dwc_csi_write(isys, id, VC_EXTENSION, 0x0); -- n = find_first_zero_bit(data_ids, N_DATA_IDS); -- if (n == N_DATA_IDS) -- return -ENOSPC; -- -- ret = test_and_set_bit(n, data_ids); -- if (ret) -- return -EBUSY; -- -- reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2; -- lo = (n % 4) * 8; -- hi = lo + 4; -- dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo); -- -- reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2; -- lo = (n % 4) * 8; -- hi = lo + 5; -- dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo); -- -- return 0; --} -- --static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) --{ -- struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- struct v4l2_mbus_frame_desc desc; -- struct v4l2_subdev *ext_sd; -- struct media_pad *pad; -- unsigned int i; -- int ret; -- -- pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -- if (IS_ERR(pad)) { -- dev_warn(dev, "can't get remote source pad of %s (%ld)\n", -- csi2->asd.sd.name, PTR_ERR(pad)); -- return PTR_ERR(pad); -- } -- -- ext_sd = media_entity_to_v4l2_subdev(pad->entity); -- if (WARN(!ext_sd, "Failed to get subdev for entity %s\n", -- pad->entity->name)) -- return -ENODEV; -- -- ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc); -- if (ret) -- return ret; -- -- if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -- dev_warn(dev, "Unsupported frame descriptor type\n"); -- return -EINVAL; -- } -- -- for (i = 0; i < desc.num_entries; i++) { -- desc_entry = &desc.entry[i]; -- if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { -- ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, -- desc_entry->bus.csi2.dt); -- if (ret) -- return ret; -- } -- } -- -- return 0; --} -- --#define CDPHY_TIMEOUT 5000000U --static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id) --{ -- void __iomem *isys_base = isys->pdata->base; -- u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; -- void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; -- struct device *dev = &isys->adev->auxdev.dev; -- unsigned int i; -- u32 phy_ready; -- u32 reg, rext; -- int ret; -- -- dev_dbg(dev, "waiting phy ready...\n"); -- ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready, -- phy_ready & BIT(0) && phy_ready != ~0U, -- 100, CDPHY_TIMEOUT); -- dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY)); -- dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id, -- dwc_csi_read(isys, id, PHY_RX)); -- dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id, -- dwc_csi_read(isys, id, PHY_STOPSTATE)); -- dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id, -- dwc_csi_read(isys, id, PHY_CAL)); -- for (i = 0; i < 4U; i++) { -- reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U); -- dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n", -- id, i, dwc_phy_read(isys, id, reg)); -- } -- dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5)); -- dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0)); -- dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB)); -- dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB)); -- dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id, -- dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT)); -- -- if (!ret) { -- if (id) { -- dev_dbg(dev, "ignore phy %u rext\n", id); -- return 0; -- } -- -- rext = dwc_phy_read(isys, id, -- CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU; -- dev_dbg(dev, "phy %u rext value = %u\n", id, rext); -- isys->phy_rext_cal = (rext ? rext : 5); -- -- return 0; -- } -- -- dev_err(dev, "wait phy ready timeout!\n"); -- -- return ret; --} -- --static int lookup_table1(u64 mbps) --{ -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(table1); i++) { -- if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps) -- return i; -- } -- -- return -ENXIO; --} -- --static const u16 deskew_fine_mem[] = { -- 0x0404, 0x040c, 0x0414, 0x041c, -- 0x0423, 0x0429, 0x0430, 0x043a, -- 0x0445, 0x044a, 0x0450, 0x045a, -- 0x0465, 0x0469, 0x0472, 0x047a, -- 0x0485, 0x0489, 0x0490, 0x049a, -- 0x04a4, 0x04ac, 0x04b4, 0x04bc, -- 0x04c4, 0x04cc, 0x04d4, 0x04dc, -- 0x04e4, 0x04ec, 0x04f4, 0x04fc, -- 0x0504, 0x050c, 0x0514, 0x051c, -- 0x0523, 0x0529, 0x0530, 0x053a, -- 0x0545, 0x054a, 0x0550, 0x055a, -- 0x0565, 0x0569, 0x0572, 0x057a, -- 0x0585, 0x0589, 0x0590, 0x059a, -- 0x05a4, 0x05ac, 0x05b4, 0x05bc, -- 0x05c4, 0x05cc, 0x05d4, 0x05dc, -- 0x05e4, 0x05ec, 0x05f4, 0x05fc, -- 0x0604, 0x060c, 0x0614, 0x061c, -- 0x0623, 0x0629, 0x0632, 0x063a, -- 0x0645, 0x064a, 0x0650, 0x065a, -- 0x0665, 0x0669, 0x0672, 0x067a, -- 0x0685, 0x0689, 0x0690, 0x069a, -- 0x06a4, 0x06ac, 0x06b4, 0x06bc, -- 0x06c4, 0x06cc, 0x06d4, 0x06dc, -- 0x06e4, 0x06ec, 0x06f4, 0x06fc, -- 0x0704, 0x070c, 0x0714, 0x071c, -- 0x0723, 0x072a, 0x0730, 0x073a, -- 0x0745, 0x074a, 0x0750, 0x075a, -- 0x0765, 0x0769, 0x0772, 0x077a, -- 0x0785, 0x0789, 0x0790, 0x079a, -- 0x07a4, 0x07ac, 0x07b4, 0x07bc, -- 0x07c4, 0x07cc, 0x07d4, 0x07dc, -- 0x07e4, 0x07ec, 0x07f4, 0x07fc, --}; -- --static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -- bool aggregation, u64 mbps) --{ -- u16 hsrxval0 = 0; -- u16 hsrxval1 = 0; -- u16 hsrxval2 = 0; -- int index; -- u16 reg; -- u16 val; -- u32 i; -- -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9); -- if (mbps > 1500) -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -- 40, 0, 7); -- else -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -- 104, 0, 7); -- -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6); -- -- for (i = 0; i < ARRAY_SIZE(table0); i++) { -- if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) { -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3, -- table0[i].ddlcal_counter_ref, -- 0, 9); -- break; -- } -- } -- -- index = lookup_table1(mbps); -- if (index >= 0) { -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, -- table1[index].phase_bound, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -- table1[index].ddlcal_dll_fbk, 4, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -- table1[index].ddlcal_ddl_coarse_bank, 0, 3); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8; -- val = table1[index].oa_lanex_hsrx_cdphy_sel_fast; -- for (i = 0; i < lanes + 1; i++) -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -- 12, 12); -- } -- -- reg = CORE_DIG_DLANE_0_RW_LP_0; -- for (i = 0; i < lanes; i++) -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, -- 0, 0, 0); -- if (!is_ipu7(isys->adev->isp->hw_ver) || -- id == PORT_B || id == PORT_C) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -- 1, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -- 0, 0, 0); -- } else { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -- 0, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -- 1, 0, 0); -- } -- -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -- 0, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -- 0, 0, 0); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12; -- val = (mbps > 1500) ? 0 : 1; -- for (i = 0; i < lanes + 1; i++) { -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3); -- } -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13; -- val = (mbps > 1500) ? 0 : 1; -- for (i = 0; i < lanes + 1; i++) { -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3); -- } -- -- if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C) -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9; -- else -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9; -- -- for (i = 0; i < ARRAY_SIZE(table6); i++) { -- if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) { -- dwc_phy_write_mask(isys, id, reg, -- table6[i].oa_lane_hsrx_hs_clk_div, -- 5, 7); -- break; -- } -- } -- -- if (aggregation) { -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, -- 1, 1); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15; -- dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -- -- val = (id == PORT_A) ? 3 : 0; -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15; -- dwc_phy_write_mask(isys, id, reg, val, 3, 4); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15; -- dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7); -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_0; -- for (i = 0; i < ARRAY_SIZE(table2); i++) { -- if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) { -- u8 j; -- -- for (j = 0; j < lanes; j++) -- dwc_phy_write_mask(isys, id, reg + (j * 0x400), -- table2[i].i_thssettle, -- 8, 15); -- break; -- } -- } -- -- /* deskew */ -- for (i = 0; i < lanes; i++) { -- reg = CORE_DIG_DLANE_0_RW_CFG_1; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), -- ((mbps > 1500) ? 0x1 : 0x2), 2, 3); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_2; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), -- ((mbps > 2500) ? 0 : 1), 15, 15); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12); -- -- reg = CORE_DIG_DLANE_0_RW_LP_0; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15); -- -- reg = CORE_DIG_DLANE_0_RW_LP_2; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_1; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_3; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2); -- index = lookup_table1(mbps); -- if (index >= 0) { -- val = table1[index].fjump_deskew; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -- 3, 8); -- } -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_4; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_5; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_6; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7); -- index = lookup_table1(mbps); -- if (index >= 0) { -- val = table1[index].min_eye_opening_deskew; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -- 8, 15); -- } -- reg = CORE_DIG_DLANE_0_RW_HS_RX_7; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15); -- -- reg = CORE_DIG_DLANE_0_RW_HS_RX_9; -- index = lookup_table1(mbps); -- if (index >= 0) { -- val = table1[index].ddlcal_max_phase; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), -- val, 0, 7); -- } -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15); -- dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0); -- -- for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++) -- dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM, -- deskew_fine_mem[i], 0, 15); -- -- if (mbps > 1500) { -- hsrxval0 = 4; -- hsrxval2 = 3; -- } -- -- if (mbps > 2500) -- hsrxval1 = 2; -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -- hsrxval0, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -- hsrxval0, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -- hsrxval0, 0, 2); -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -- hsrxval0, 0, 2); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -- hsrxval0, 0, 2); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -- hsrxval1, 3, 4); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -- hsrxval1, 3, 4); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -- hsrxval1, 3, 4); -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -- hsrxval1, 3, 4); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -- hsrxval1, 3, 4); -- } -- -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15, -- hsrxval2, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15, -- hsrxval2, 0, 2); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15, -- hsrxval2, 0, 2); -- if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15, -- hsrxval2, 0, 2); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15, -- hsrxval2, 0, 2); -- } -- -- /* force and override rext */ -- if (isys->phy_rext_cal && id) { -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, -- isys->phy_rext_cal, 0, 3); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -- 1, 11, 11); -- } --} -- --static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -- bool aggregation, u64 mbps) --{ -- u8 trios = 2; -- u16 coarse_target; -- u16 deass_thresh; -- u16 delay_thresh; -- u16 reset_thresh; -- u16 cap_prog = 6U; -- u16 reg; -- u16 val; -- u32 i; -- u64 r64; -- u32 r; -- -- if (is_ipu7p5(isys->adev->isp->hw_ver)) -- val = 0x15; -- else -- val = 0x155; -- -- if (is_ipu7(isys->adev->isp->hw_ver)) -- trios = 3; -- -- dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); -- -- reg = CORE_DIG_CLANE_0_RW_LP_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -- -- val = (mbps > 900U) ? 1U : 0U; -- for (i = 0; i < trios; i++) { -- reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_1; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_5; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_6; -- dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15); -- } -- -- /* -- * Below 900Msps, always use the same value. -- * The formula is suitable for data rate 80-3500Msps. -- * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5 -- */ -- if (mbps >= 80U) -- coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1; -- else -- coarse_target = 56; -- -- for (i = 0; i < trios; i++) { -- reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; -- dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); -- } -- -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); -- -- if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -- 1, 0, 0); -- dwc_phy_write_mask(isys, id, -- CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -- 0, 0, 0); -- } -- -- for (i = 0; i < trios; i++) { -- reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; -- dwc_phy_write_mask(isys, id, reg, 1, 6, 8); -- dwc_phy_write_mask(isys, id, reg, 1, 3, 5); -- dwc_phy_write_mask(isys, id, reg, 2, 0, 2); -- } -- -- deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1; -- if (r64 != 0) -- deass_thresh++; -- -- reg = CORE_DIG_RW_TRIO0_2; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, -- deass_thresh, 0, 7); -- -- delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u; -- -- if (delay_thresh < 1) -- delay_thresh = 1; -- -- reg = CORE_DIG_RW_TRIO0_1; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, -- delay_thresh, 0, 15); -- -- reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r); -- if (!r) -- reset_thresh--; -- -- if (reset_thresh < 1) -- reset_thresh = 1; -- -- reg = CORE_DIG_RW_TRIO0_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, -- reset_thresh, 9, 11); -- -- reg = CORE_DIG_CLANE_0_RW_LP_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); -- -- reg = CORE_DIG_CLANE_0_RW_LP_2; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); -- -- reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -- for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); -- -- for (i = 0; i < ARRAY_SIZE(table7); i++) { -- if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) { -- cap_prog = table7[i].val; -- break; -- } -- } -- -- for (i = 0; i < (lanes + 1); i++) { -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; -- dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); -- dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); -- -- reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; -- dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); -- } --} -- --static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -- bool aggregation) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- u32 phy_mode; -- s64 link_freq; -- u64 mbps; -- -- if (aggregation) -- link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]); -- else -- link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]); -- -- if (link_freq < 0) { -- dev_err(dev, "get link freq failed (%lld)\n", link_freq); -- return link_freq; -- } -- -- mbps = div_u64(link_freq, 500000); -- dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n", -- id, lanes, aggregation, mbps); -- -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7); -- dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2, -- 1, 12, 13); -- dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0, -- 63, 2, 7); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1, -- 563, 0, 11); -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7); -- /* bypass the RCAL state (bit6) */ -- if (aggregation && id != PORT_A) -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45, -- 0, 7); -- -- dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7); -- dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8); -- dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6); -- dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1); -- dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1); -- dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -- 0, 10, 10); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -- 1, 10, 10); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -- 0, 15, 15); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3, -- 3, 8, 9); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -- 0, 15, 15); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6, -- 7, 12, 14); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -- 0, 8, 10); -- dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, -- 0, 8, 8); -- -- if (aggregation) -- phy_mode = isys->csi2[0].phy_mode; -- else -- phy_mode = isys->csi2[id].phy_mode; -- -- if (phy_mode == PHY_MODE_DPHY) { -- ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps); -- } else if (phy_mode == PHY_MODE_CPHY) { -- ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps); -- } else { -- dev_err(dev, "unsupported phy mode %d!\n", -- isys->csi2[id].phy_mode); -- } -- -- return 0; --} -- --int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- u32 lanes = csi2->nlanes; -- bool aggregation = false; -- u32 id = csi2->port; -- int ret; -- -- /* lanes remapping for aggregation (port AB) mode */ -- if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { -- aggregation = true; -- lanes = 2; -- } -- -- ipu7_isys_csi_phy_reset(isys, id); -- gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); -- gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -- gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); -- gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); -- gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); -- -- /* config PORT_B if aggregation mode */ -- if (aggregation) { -- ipu7_isys_csi_phy_reset(isys, PORT_B); -- gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0); -- gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3); -- gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -- gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf); -- gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode); -- } -- -- ipu7_isys_csi_ctrl_cfg(csi2); -- ipu7_isys_csi_ctrl_dids_config(csi2, id); -- -- ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, id, PHY_RESET, 1); -- gpreg_write(isys, id, PHY_SHUTDOWN, 1); -- dwc_csi_write(isys, id, DPHY_RSTZ, 1); -- dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1); -- dwc_csi_write(isys, id, CSI2_RESETN, 1); -- -- ret = ipu7_isys_phy_ready(isys, id); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0); -- gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0); -- -- /* config PORT_B if aggregation mode */ -- if (aggregation) { -- ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, PORT_B, PHY_RESET, 1); -- gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1); -- dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1); -- dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1); -- dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1); -- ret = ipu7_isys_phy_ready(isys, PORT_B); -- if (ret < 0) -- return ret; -- -- gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0); -- gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0); -- } -- -- return 0; --} -- --void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- -- ipu7_isys_csi_phy_reset(isys, csi2->port); -- if (!is_ipu7(isys->adev->isp->hw_ver) && -- csi2->nlanes > 2U && csi2->port == PORT_A) -- ipu7_isys_csi_phy_reset(isys, PORT_B); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -deleted file mode 100644 -index dfdcb61540c4..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -+++ /dev/null -@@ -1,16 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_CSI_PHY_H --#define IPU7_ISYS_CSI_PHY_H -- --struct ipu7_isys; -- --#define PHY_MODE_DPHY 0U --#define PHY_MODE_CPHY 1U -- --int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2); --void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -deleted file mode 100644 -index aad52c44a005..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -+++ /dev/null -@@ -1,1197 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2020 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_CSI2_REG_H --#define IPU7_ISYS_CSI2_REG_H -- --/* IS main regs base */ --#define IS_MAIN_BASE 0x240000 --#define IS_MAIN_S2B_BASE (IS_MAIN_BASE + 0x22000) --#define IS_MAIN_B2O_BASE (IS_MAIN_BASE + 0x26000) --#define IS_MAIN_ISD_M0_BASE (IS_MAIN_BASE + 0x2b000) --#define IS_MAIN_ISD_M1_BASE (IS_MAIN_BASE + 0x2b100) --#define IS_MAIN_ISD_INT_BASE (IS_MAIN_BASE + 0x2b200) --#define IS_MAIN_GDA_BASE (IS_MAIN_BASE + 0x32000) --#define IS_MAIN_GPREGS_MAIN_BASE (IS_MAIN_BASE + 0x32500) --#define IS_MAIN_IRQ_CTRL_BASE (IS_MAIN_BASE + 0x32700) --#define IS_MAIN_PWM_CTRL_BASE (IS_MAIN_BASE + 0x32b00) -- --#define S2B_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_S2B_BASE + 0x1c) --#define S2B_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_S2B_BASE + 0x20) --#define S2B_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_S2B_BASE + 0x24) --#define S2B_IID_IRQ_CTL_STATUS(iid) (IS_MAIN_S2B_BASE + 0x94 + \ -- 0x100 * (iid)) -- --#define B2O_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_B2O_BASE + 0x30) --#define B2O_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_B2O_BASE + 0x34) --#define B2O_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_B2O_BASE + 0x38) --#define B2O_IID_IRQ_CTL_STATUS(oid) (IS_MAIN_B2O_BASE + 0x3dc + \ -- 0x200 * (oid)) -- --#define ISD_M0_IRQ_CTL_STATUS (IS_MAIN_ISD_M0_BASE + 0x1c) --#define ISD_M0_IRQ_CTL_CLEAR (IS_MAIN_ISD_M0_BASE + 0x20) --#define ISD_M0_IRQ_CTL_ENABLE (IS_MAIN_ISD_M0_BASE + 0x24) -- --#define ISD_M1_IRQ_CTL_STATUS (IS_MAIN_ISD_M1_BASE + 0x1c) --#define ISD_M1_IRQ_CTL_CLEAR (IS_MAIN_ISD_M1_BASE + 0x20) --#define ISD_M1_IRQ_CTL_ENABLE (IS_MAIN_ISD_M1_BASE + 0x24) -- --#define ISD_INT_IRQ_CTL_STATUS (IS_MAIN_ISD_INT_BASE + 0x1c) --#define ISD_INT_IRQ_CTL_CLEAR (IS_MAIN_ISD_INT_BASE + 0x20) --#define ISD_INT_IRQ_CTL_ENABLE (IS_MAIN_ISD_INT_BASE + 0x24) -- --#define GDA_IRQ_CTL_STATUS (IS_MAIN_GDA_BASE + 0x1c) --#define GDA_IRQ_CTL_CLEAR (IS_MAIN_GDA_BASE + 0x20) --#define GDA_IRQ_CTL_ENABLE (IS_MAIN_GDA_BASE + 0x24) -- --#define IS_MAIN_IRQ_CTL_EDGE IS_MAIN_IRQ_CTRL_BASE --#define IS_MAIN_IRQ_CTL_MASK (IS_MAIN_IRQ_CTRL_BASE + 0x4) --#define IS_MAIN_IRQ_CTL_STATUS (IS_MAIN_IRQ_CTRL_BASE + 0x8) --#define IS_MAIN_IRQ_CTL_CLEAR (IS_MAIN_IRQ_CTRL_BASE + 0xc) --#define IS_MAIN_IRQ_CTL_ENABLE (IS_MAIN_IRQ_CTRL_BASE + 0x10) --#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IS_MAIN_IRQ_CTRL_BASE + 0x14) -- --/* IS IO regs base */ --#define IS_PHY_NUM 4U --#define IS_IO_BASE 0x280000 -- --/* dwc csi cdphy registers */ --#define IS_IO_CDPHY_BASE(i) (IS_IO_BASE + 0x10000 * (i)) --#define PPI_STARTUP_RW_COMMON_DPHY_0 0x1800 --#define PPI_STARTUP_RW_COMMON_DPHY_1 0x1802 --#define PPI_STARTUP_RW_COMMON_DPHY_2 0x1804 --#define PPI_STARTUP_RW_COMMON_DPHY_3 0x1806 --#define PPI_STARTUP_RW_COMMON_DPHY_4 0x1808 --#define PPI_STARTUP_RW_COMMON_DPHY_5 0x180a --#define PPI_STARTUP_RW_COMMON_DPHY_6 0x180c --#define PPI_STARTUP_RW_COMMON_DPHY_7 0x180e --#define PPI_STARTUP_RW_COMMON_DPHY_8 0x1810 --#define PPI_STARTUP_RW_COMMON_DPHY_9 0x1812 --#define PPI_STARTUP_RW_COMMON_DPHY_A 0x1814 --#define PPI_STARTUP_RW_COMMON_DPHY_10 0x1820 --#define PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822 --#define PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824 --#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848 --#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a --#define PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c --#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e --#define PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850 --#define PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852 --#define PPI_CALIBCTRL_R_ADC_DEBUG 0x1854 --#define PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00 --#define PPI_RW_LPDCOCAL_TIMEBASE 0x1c02 --#define PPI_RW_LPDCOCAL_NREF 0x1c04 --#define PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06 --#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08 --#define PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a --#define PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c --#define PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e --#define PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16 --#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18 --#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a --#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c --#define PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e --#define PPI_RW_LB_TIMEBASE_CONFIG 0x1c20 --#define PPI_RW_LB_STARTCMU_CONFIG 0x1c22 --#define PPI_R_LBPULSE_COUNTER_RB 0x1c24 --#define PPI_R_LB_START_CMU_RB 0x1c26 --#define PPI_RW_LB_DPHY_BURST_START 0x1c28 --#define PPI_RW_LB_CPHY_BURST_START 0x1c2a --#define PPI_RW_DDLCAL_CFG_0 0x1c40 --#define PPI_RW_DDLCAL_CFG_1 0x1c42 --#define PPI_RW_DDLCAL_CFG_2 0x1c44 --#define PPI_RW_DDLCAL_CFG_3 0x1c46 --#define PPI_RW_DDLCAL_CFG_4 0x1c48 --#define PPI_RW_DDLCAL_CFG_5 0x1c4a --#define PPI_RW_DDLCAL_CFG_6 0x1c4c --#define PPI_RW_DDLCAL_CFG_7 0x1c4e --#define PPI_R_DDLCAL_DEBUG_0 0x1c50 --#define PPI_R_DDLCAL_DEBUG_1 0x1c52 --#define PPI_RW_PARITY_TEST 0x1c60 --#define PPI_RW_STARTUP_OVR_0 0x1c62 --#define PPI_RW_STARTUP_STATE_OVR_1 0x1c64 --#define PPI_RW_DTB_SELECTOR 0x1c66 --#define PPI_RW_DPHY_CLK_SPARE 0x1c6a --#define PPI_RW_COMMON_CFG 0x1c6c --#define PPI_RW_TERMCAL_CFG_0 0x1c80 --#define PPI_R_TERMCAL_DEBUG_0 0x1c82 --#define PPI_RW_TERMCAL_CTRL_0 0x1c84 --#define PPI_RW_OFFSETCAL_CFG_0 0x1ca0 --#define PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2 --#define PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4 --#define PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6 --#define PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8 --#define PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa --#define PPI_RW_HSDCOCAL_CFG_O 0x1d00 --#define PPI_RW_HSDCOCAL_CFG_1 0x1d02 --#define PPI_RW_HSDCOCAL_CFG_2 0x1d04 --#define PPI_RW_HSDCOCAL_CFG_3 0x1d06 --#define PPI_RW_HSDCOCAL_CFG_4 0x1d08 --#define PPI_RW_HSDCOCAL_CFG_5 0x1d0a --#define PPI_RW_HSDCOCAL_CFG_6 0x1d0c --#define PPI_RW_HSDCOCAL_CFG_7 0x1d0e --#define PPI_RW_HSDCOCAL_CFG_8 0x1d10 --#define PPI_R_HSDCOCAL_DEBUG_RB 0x1d12 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068 --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c --#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086 --#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8 --#define CORE_DIG_RW_TRIO0_0 0x2100 --#define CORE_DIG_RW_TRIO0_1 0x2102 --#define CORE_DIG_RW_TRIO0_2 0x2104 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468 --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c --#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486 --#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8 --#define CORE_DIG_RW_TRIO1_0 0x2500 --#define CORE_DIG_RW_TRIO1_1 0x2502 --#define CORE_DIG_RW_TRIO1_2 0x2504 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868 --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c --#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886 --#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4 --#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6 --#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8 --#define CORE_DIG_RW_TRIO2_0 0x2900 --#define CORE_DIG_RW_TRIO2_1 0x2902 --#define CORE_DIG_RW_TRIO2_2 0x2904 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c --#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68 --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c --#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86 --#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068 --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c --#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086 --#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400 --#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402 --#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812 --#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826 --#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854 --#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856 --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858 --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e --#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860 --#define CORE_DIG_RW_COMMON_0 0x3880 --#define CORE_DIG_RW_COMMON_1 0x3882 --#define CORE_DIG_RW_COMMON_2 0x3884 --#define CORE_DIG_RW_COMMON_3 0x3886 --#define CORE_DIG_RW_COMMON_4 0x3888 --#define CORE_DIG_RW_COMMON_5 0x388a --#define CORE_DIG_RW_COMMON_6 0x388c --#define CORE_DIG_RW_COMMON_7 0x388e --#define CORE_DIG_RW_COMMON_8 0x3890 --#define CORE_DIG_RW_COMMON_9 0x3892 --#define CORE_DIG_RW_COMMON_10 0x3894 --#define CORE_DIG_RW_COMMON_11 0x3896 --#define CORE_DIG_RW_COMMON_12 0x3898 --#define CORE_DIG_RW_COMMON_13 0x389a --#define CORE_DIG_RW_COMMON_14 0x389c --#define CORE_DIG_RW_COMMON_15 0x389e --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0 --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2 --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4 --#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6 --#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0 --#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2 --#define PPI_RW_DPHY_LANE0_LBERT_0 0x4000 --#define PPI_RW_DPHY_LANE0_LBERT_1 0x4002 --#define PPI_R_DPHY_LANE0_LBERT_0 0x4004 --#define PPI_R_DPHY_LANE0_LBERT_1 0x4006 --#define PPI_RW_DPHY_LANE0_SPARE 0x4008 --#define PPI_RW_DPHY_LANE1_LBERT_0 0x4400 --#define PPI_RW_DPHY_LANE1_LBERT_1 0x4402 --#define PPI_R_DPHY_LANE1_LBERT_0 0x4404 --#define PPI_R_DPHY_LANE1_LBERT_1 0x4406 --#define PPI_RW_DPHY_LANE1_SPARE 0x4408 --#define PPI_RW_DPHY_LANE2_LBERT_0 0x4800 --#define PPI_RW_DPHY_LANE2_LBERT_1 0x4802 --#define PPI_R_DPHY_LANE2_LBERT_0 0x4804 --#define PPI_R_DPHY_LANE2_LBERT_1 0x4806 --#define PPI_RW_DPHY_LANE2_SPARE 0x4808 --#define PPI_RW_DPHY_LANE3_LBERT_0 0x4c00 --#define PPI_RW_DPHY_LANE3_LBERT_1 0x4c02 --#define PPI_R_DPHY_LANE3_LBERT_0 0x4c04 --#define PPI_R_DPHY_LANE3_LBERT_1 0x4c06 --#define PPI_RW_DPHY_LANE3_SPARE 0x4c08 --#define CORE_DIG_DLANE_0_RW_CFG_0 0x6000 --#define CORE_DIG_DLANE_0_RW_CFG_1 0x6002 --#define CORE_DIG_DLANE_0_RW_CFG_2 0x6004 --#define CORE_DIG_DLANE_0_RW_LP_0 0x6080 --#define CORE_DIG_DLANE_0_RW_LP_1 0x6082 --#define CORE_DIG_DLANE_0_RW_LP_2 0x6084 --#define CORE_DIG_DLANE_0_R_LP_0 0x60a0 --#define CORE_DIG_DLANE_0_R_LP_1 0x60a2 --#define CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0 --#define CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100 --#define CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102 --#define CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104 --#define CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106 --#define CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108 --#define CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a --#define CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c --#define CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e --#define CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110 --#define CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112 --#define CORE_DIG_DLANE_0_R_HS_RX_0 0x6120 --#define CORE_DIG_DLANE_0_R_HS_RX_1 0x6122 --#define CORE_DIG_DLANE_0_R_HS_RX_2 0x6124 --#define CORE_DIG_DLANE_0_R_HS_RX_3 0x6126 --#define CORE_DIG_DLANE_0_R_HS_RX_4 0x6128 --#define CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200 --#define CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202 --#define CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204 --#define CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206 --#define CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208 --#define CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a --#define CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c --#define CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e --#define CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210 --#define CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212 --#define CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214 --#define CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216 --#define CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218 --#define CORE_DIG_DLANE_1_RW_CFG_0 0x6400 --#define CORE_DIG_DLANE_1_RW_CFG_1 0x6402 --#define CORE_DIG_DLANE_1_RW_CFG_2 0x6404 --#define CORE_DIG_DLANE_1_RW_LP_0 0x6480 --#define CORE_DIG_DLANE_1_RW_LP_1 0x6482 --#define CORE_DIG_DLANE_1_RW_LP_2 0x6484 --#define CORE_DIG_DLANE_1_R_LP_0 0x64a0 --#define CORE_DIG_DLANE_1_R_LP_1 0x64a2 --#define CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0 --#define CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500 --#define CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502 --#define CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504 --#define CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506 --#define CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508 --#define CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a --#define CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c --#define CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e --#define CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510 --#define CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512 --#define CORE_DIG_DLANE_1_R_HS_RX_0 0x6520 --#define CORE_DIG_DLANE_1_R_HS_RX_1 0x6522 --#define CORE_DIG_DLANE_1_R_HS_RX_2 0x6524 --#define CORE_DIG_DLANE_1_R_HS_RX_3 0x6526 --#define CORE_DIG_DLANE_1_R_HS_RX_4 0x6528 --#define CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600 --#define CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602 --#define CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604 --#define CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606 --#define CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608 --#define CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a --#define CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c --#define CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e --#define CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610 --#define CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612 --#define CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614 --#define CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616 --#define CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618 --#define CORE_DIG_DLANE_2_RW_CFG_0 0x6800 --#define CORE_DIG_DLANE_2_RW_CFG_1 0x6802 --#define CORE_DIG_DLANE_2_RW_CFG_2 0x6804 --#define CORE_DIG_DLANE_2_RW_LP_0 0x6880 --#define CORE_DIG_DLANE_2_RW_LP_1 0x6882 --#define CORE_DIG_DLANE_2_RW_LP_2 0x6884 --#define CORE_DIG_DLANE_2_R_LP_0 0x68a0 --#define CORE_DIG_DLANE_2_R_LP_1 0x68a2 --#define CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0 --#define CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900 --#define CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902 --#define CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904 --#define CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906 --#define CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908 --#define CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a --#define CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c --#define CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e --#define CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910 --#define CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912 --#define CORE_DIG_DLANE_2_R_HS_RX_0 0x6920 --#define CORE_DIG_DLANE_2_R_HS_RX_1 0x6922 --#define CORE_DIG_DLANE_2_R_HS_RX_2 0x6924 --#define CORE_DIG_DLANE_2_R_HS_RX_3 0x6926 --#define CORE_DIG_DLANE_2_R_HS_RX_4 0x6928 --#define CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00 --#define CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02 --#define CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04 --#define CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06 --#define CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08 --#define CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a --#define CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c --#define CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e --#define CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10 --#define CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12 --#define CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14 --#define CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16 --#define CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18 --#define CORE_DIG_DLANE_3_RW_CFG_0 0x6c00 --#define CORE_DIG_DLANE_3_RW_CFG_1 0x6c02 --#define CORE_DIG_DLANE_3_RW_CFG_2 0x6c04 --#define CORE_DIG_DLANE_3_RW_LP_0 0x6c80 --#define CORE_DIG_DLANE_3_RW_LP_1 0x6c82 --#define CORE_DIG_DLANE_3_RW_LP_2 0x6c84 --#define CORE_DIG_DLANE_3_R_LP_0 0x6ca0 --#define CORE_DIG_DLANE_3_R_LP_1 0x6ca2 --#define CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0 --#define CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00 --#define CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02 --#define CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04 --#define CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06 --#define CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08 --#define CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a --#define CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c --#define CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e --#define CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10 --#define CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12 --#define CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20 --#define CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22 --#define CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24 --#define CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26 --#define CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28 --#define CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00 --#define CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02 --#define CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04 --#define CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06 --#define CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08 --#define CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a --#define CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c --#define CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e --#define CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10 --#define CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12 --#define CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14 --#define CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16 --#define CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18 --#define CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000 --#define CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002 --#define CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004 --#define CORE_DIG_DLANE_CLK_RW_LP_0 0x7080 --#define CORE_DIG_DLANE_CLK_RW_LP_1 0x7082 --#define CORE_DIG_DLANE_CLK_RW_LP_2 0x7084 --#define CORE_DIG_DLANE_CLK_R_LP_0 0x70a0 --#define CORE_DIG_DLANE_CLK_R_LP_1 0x70a2 --#define CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a --#define CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c --#define CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e --#define CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110 --#define CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112 --#define CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120 --#define CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122 --#define CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124 --#define CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126 --#define CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a --#define CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c --#define CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e --#define CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216 --#define CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218 --#define PPI_RW_CPHY_TRIO0_LBERT_0 0x8000 --#define PPI_RW_CPHY_TRIO0_LBERT_1 0x8002 --#define PPI_R_CPHY_TRIO0_LBERT_0 0x8004 --#define PPI_R_CPHY_TRIO0_LBERT_1 0x8006 --#define PPI_RW_CPHY_TRIO0_SPARE 0x8008 --#define PPI_RW_CPHY_TRIO1_LBERT_0 0x8400 --#define PPI_RW_CPHY_TRIO1_LBERT_1 0x8402 --#define PPI_R_CPHY_TRIO1_LBERT_0 0x8404 --#define PPI_R_CPHY_TRIO1_LBERT_1 0x8406 --#define PPI_RW_CPHY_TRIO1_SPARE 0x8408 --#define PPI_RW_CPHY_TRIO2_LBERT_0 0x8800 --#define PPI_RW_CPHY_TRIO2_LBERT_1 0x8802 --#define PPI_R_CPHY_TRIO2_LBERT_0 0x8804 --#define PPI_R_CPHY_TRIO2_LBERT_1 0x8806 --#define PPI_RW_CPHY_TRIO2_SPARE 0x8808 --#define CORE_DIG_CLANE_0_RW_CFG_0 0xa000 --#define CORE_DIG_CLANE_0_RW_CFG_2 0xa004 --#define CORE_DIG_CLANE_0_RW_LP_0 0xa080 --#define CORE_DIG_CLANE_0_RW_LP_1 0xa082 --#define CORE_DIG_CLANE_0_RW_LP_2 0xa084 --#define CORE_DIG_CLANE_0_R_LP_0 0xa0a0 --#define CORE_DIG_CLANE_0_R_LP_1 0xa0a2 --#define CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100 --#define CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102 --#define CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104 --#define CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106 --#define CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108 --#define CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a --#define CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c --#define CORE_DIG_CLANE_0_R_RX_0 0xa120 --#define CORE_DIG_CLANE_0_R_RX_1 0xa122 --#define CORE_DIG_CLANE_0_R_TX_0 0xa124 --#define CORE_DIG_CLANE_0_R_RX_2 0xa126 --#define CORE_DIG_CLANE_0_R_RX_3 0xa128 --#define CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200 --#define CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202 --#define CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204 --#define CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206 --#define CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208 --#define CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a --#define CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c --#define CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e --#define CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210 --#define CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212 --#define CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214 --#define CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216 --#define CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218 --#define CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a --#define CORE_DIG_CLANE_1_RW_CFG_0 0xa400 --#define CORE_DIG_CLANE_1_RW_CFG_2 0xa404 --#define CORE_DIG_CLANE_1_RW_LP_0 0xa480 --#define CORE_DIG_CLANE_1_RW_LP_1 0xa482 --#define CORE_DIG_CLANE_1_RW_LP_2 0xa484 --#define CORE_DIG_CLANE_1_R_LP_0 0xa4a0 --#define CORE_DIG_CLANE_1_R_LP_1 0xa4a2 --#define CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500 --#define CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502 --#define CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504 --#define CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506 --#define CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508 --#define CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a --#define CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c --#define CORE_DIG_CLANE_1_R_RX_0 0xa520 --#define CORE_DIG_CLANE_1_R_RX_1 0xa522 --#define CORE_DIG_CLANE_1_R_TX_0 0xa524 --#define CORE_DIG_CLANE_1_R_RX_2 0xa526 --#define CORE_DIG_CLANE_1_R_RX_3 0xa528 --#define CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600 --#define CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602 --#define CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604 --#define CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606 --#define CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608 --#define CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a --#define CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c --#define CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e --#define CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610 --#define CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612 --#define CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614 --#define CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616 --#define CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618 --#define CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a --#define CORE_DIG_CLANE_2_RW_CFG_0 0xa800 --#define CORE_DIG_CLANE_2_RW_CFG_2 0xa804 --#define CORE_DIG_CLANE_2_RW_LP_0 0xa880 --#define CORE_DIG_CLANE_2_RW_LP_1 0xa882 --#define CORE_DIG_CLANE_2_RW_LP_2 0xa884 --#define CORE_DIG_CLANE_2_R_LP_0 0xa8a0 --#define CORE_DIG_CLANE_2_R_LP_1 0xa8a2 --#define CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900 --#define CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902 --#define CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904 --#define CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906 --#define CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908 --#define CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a --#define CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c --#define CORE_DIG_CLANE_2_R_RX_0 0xa920 --#define CORE_DIG_CLANE_2_R_RX_1 0xa922 --#define CORE_DIG_CLANE_2_R_TX_0 0xa924 --#define CORE_DIG_CLANE_2_R_RX_2 0xa926 --#define CORE_DIG_CLANE_2_R_RX_3 0xa928 --#define CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00 --#define CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02 --#define CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04 --#define CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06 --#define CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08 --#define CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a --#define CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c --#define CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e --#define CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10 --#define CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12 --#define CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14 --#define CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16 --#define CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18 --#define CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a -- --/* dwc csi host controller registers */ --#define IS_IO_CSI2_HOST_BASE(i) (IS_IO_BASE + 0x40000 + \ -- 0x2000 * (i)) --#define VERSION 0x0 --#define N_LANES 0x4 --#define CSI2_RESETN 0x8 --#define INT_ST_MAIN 0xc --#define DATA_IDS_1 0x10 --#define DATA_IDS_2 0x14 --#define CDPHY_MODE 0x1c --#define DATA_IDS_VC_1 0x30 --#define DATA_IDS_VC_2 0x34 --#define PHY_SHUTDOWNZ 0x40 --#define DPHY_RSTZ 0x44 --#define PHY_RX 0x48 --#define PHY_STOPSTATE 0x4c --#define PHY_TEST_CTRL0 0x50 --#define PHY_TEST_CTRL1 0x54 --#define PPI_PG_PATTERN_VRES 0x60 --#define PPI_PG_PATTERN_HRES 0x64 --#define PPI_PG_CONFIG 0x68 --#define PPI_PG_ENABLE 0x6c --#define PPI_PG_STATUS 0x70 --#define VC_EXTENSION 0xc8 --#define PHY_CAL 0xcc --#define INT_ST_PHY_FATAL 0xe0 --#define INT_MSK_PHY_FATAL 0xe4 --#define INT_FORCE_PHY_FATAL 0xe8 --#define INT_ST_PKT_FATAL 0xf0 --#define INT_MSK_PKT_FATAL 0xf4 --#define INT_FORCE_PKT_FATAL 0xf8 --#define INT_ST_PHY 0x110 --#define INT_MSK_PHY 0x114 --#define INT_FORCE_PHY 0x118 --#define INT_ST_LINE 0x130 --#define INT_MSK_LINE 0x134 --#define INT_FORCE_LINE 0x138 --#define INT_ST_BNDRY_FRAME_FATAL 0x280 --#define INT_MSK_BNDRY_FRAME_FATAL 0x284 --#define INT_FORCE_BNDRY_FRAME_FATAL 0x288 --#define INT_ST_SEQ_FRAME_FATAL 0x290 --#define INT_MSK_SEQ_FRAME_FATAL 0x294 --#define INT_FORCE_SEQ_FRAME_FATAL 0x298 --#define INT_ST_CRC_FRAME_FATAL 0x2a0 --#define INT_MSK_CRC_FRAME_FATAL 0x2a4 --#define INT_FORCE_CRC_FRAME_FATAL 0x2a8 --#define INT_ST_PLD_CRC_FATAL 0x2b0 --#define INT_MSK_PLD_CRC_FATAL 0x2b4 --#define INT_FORCE_PLD_CRC_FATAL 0x2b8 --#define INT_ST_DATA_ID 0x2c0 --#define INT_MSK_DATA_ID 0x2c4 --#define INT_FORCE_DATA_ID 0x2c8 --#define INT_ST_ECC_CORRECTED 0x2d0 --#define INT_MSK_ECC_CORRECTED 0x2d4 --#define INT_FORCE_ECC_CORRECTED 0x2d8 --#define SCRAMBLING 0x300 --#define SCRAMBLING_SEED1 0x304 --#define SCRAMBLING_SEED2 0x308 --#define SCRAMBLING_SEED3 0x30c --#define SCRAMBLING_SEED4 0x310 --#define SCRAMBLING 0x300 -- --#define IS_IO_CSI2_ADPL_PORT_BASE(i) (IS_IO_BASE + 0x40800 + \ -- 0x2000 * (i)) --#define CSI2_ADPL_INPUT_MODE 0x0 --#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4 --#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8 --#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc --#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4 --#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8 --#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac --#define CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc --#define CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0 --#define CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4 --#define CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8 --#define CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc --#define CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0 -- --/* software control the legacy csi irq */ --#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40c00 + \ -- 0x2000 * (i)) --#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \ -- 0x2000 * (i)) --#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IS_IO_BASE + 0x49000) --#define IS_IO_CSI2_IRQ_CTRL_BASE (IS_IO_BASE + 0x4e100) -- --#define IRQ_CTL_EDGE 0x0 --#define IRQ_CTL_MASK 0x4 --#define IRQ_CTL_STATUS 0x8 --#define IRQ_CTL_CLEAR 0xc --#define IRQ_CTL_ENABLE 0x10 --/* FE irq for PTL */ --#define IRQ1_CTL_MASK 0x14 --#define IRQ1_CTL_STATUS 0x18 --#define IRQ1_CTL_CLEAR 0x1c --#define IRQ1_CTL_ENABLE 0x20 -- --/* software to set the clock gate to use the port or mgc */ --#define IS_IO_GPREGS_BASE (IS_IO_BASE + 0x49200) --#define SRST_PORT_ARB 0x0 --#define SRST_MGC 0x4 --#define SRST_WIDTH_CONV 0x8 --#define SRST_CSI_IRQ 0xc --#define SRST_CSI_LEGACY_IRQ 0x10 --#define CLK_EN_TXCLKESC 0x14 --#define CLK_DIV_FACTOR_IS_CLK 0x18 --#define CLK_DIV_FACTOR_APB_CLK 0x1c --#define CSI_PORT_CLK_GATE 0x20 --#define CSI_PORTAB_AGGREGATION 0x24 --#define MGC_CLK_GATE 0x2c --#define CG_CTRL_BITS 0x3c --#define SPARE_RW 0xf8 --#define SPARE_RO 0xfc -- --#define IS_IO_CSI2_MPF_PORT_BASE(i) (IS_IO_BASE + 0x53000 + \ -- 0x1000 * (i)) --#define MPF_16_IRQ_CNTRL_STATUS 0x238 --#define MPF_16_IRQ_CNTRL_CLEAR 0x23c --#define MPF_16_IRQ_CNTRL_ENABLE 0x240 -- --/* software config the phy */ --#define IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x53400) --#define IPU8_IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x40e00) --#define CSI_ADAPT_LAYER_SRST 0x0 --#define MPF_SRST_RST 0x4 --#define CSI_ERR_IRQ_CTRL_SRST 0x8 --#define CSI_SYNC_RC_SRST 0xc --#define CSI_CG_CTRL_BITS 0x10 --#define SOC_CSI2HOST_SELECT 0x14 --#define PHY_RESET 0x18 --#define PHY_SHUTDOWN 0x1c --#define PHY_MODE 0x20 --#define PHY_READY 0x24 --#define PHY_CLK_LANE_FORCE_CONTROL 0x28 --#define PHY_CLK_LANE_CONTROL 0x2c --#define PHY_CLK_LANE_STATUS 0x30 --#define PHY_LANE_RX_ESC_REQ 0x34 --#define PHY_LANE_RX_ESC_DATA 0x38 --#define PHY_LANE_TURNDISABLE 0x3c --#define PHY_LANE_DIRECTION 0x40 --#define PHY_LANE_FORCE_CONTROL 0x44 --#define PHY_LANE_CONTROL_EN 0x48 --#define PHY_LANE_CONTROL_DATAWIDTH 0x4c --#define PHY_LANE_STATUS 0x50 --#define PHY_LANE_ERR 0x54 --#define PHY_LANE_RXALP 0x58 --#define PHY_LANE_RXALP_NIBBLE 0x5c --#define PHY_PARITY_ERROR 0x60 --#define PHY_DEBUG_REGS_CLK_GATE_EN 0x64 --#define SPARE_RW 0xf8 --#define SPARE_RO 0xfc -- --/* software not touch */ --#define PORT_ARB_BASE (IS_IO_BASE + 0x4e000) --#define PORT_ARB_IRQ_CTL_STATUS 0x4 --#define PORT_ARB_IRQ_CTL_CLEAR 0x8 --#define PORT_ARB_IRQ_CTL_ENABLE 0xc -- --#define MGC_PPC 4U --#define MGC_DTYPE_RAW(i) (((i) - 8) / 2) --#define IS_IO_MGC_BASE (IS_IO_BASE + 0x48000) --#define MGC_KICK 0x0 --#define MGC_ASYNC_STOP 0x4 --#define MGC_PORT_OFFSET 0x100 --#define MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4) --#define MGC_MG_PORT(i) (IS_IO_MGC_BASE + \ -- (i) * MGC_PORT_OFFSET) --/* per mgc instance */ --#define MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28 --#define MGC_MG_MODE 0x2c --#define MGC_MG_INIT_COUNTER 0x30 --#define MGC_MG_MIPI_VC 0x34 --#define MGC_MG_MIPI_DTYPES 0x38 --#define MGC_MG_MULTI_DTYPES_MODE 0x3c --#define MGC_MG_NOF_FRAMES 0x40 --#define MGC_MG_FRAME_DIM 0x44 --#define MGC_MG_HBLANK 0x48 --#define MGC_MG_VBLANK 0x4c --#define MGC_MG_TPG_MODE 0x50 --#define MGC_MG_TPG_R0 0x54 --#define MGC_MG_TPG_G0 0x58 --#define MGC_MG_TPG_B0 0x5c --#define MGC_MG_TPG_R1 0x60 --#define MGC_MG_TPG_G1 0x64 --#define MGC_MG_TPG_B1 0x68 --#define MGC_MG_TPG_FACTORS 0x6c --#define MGC_MG_TPG_MASKS 0x70 --#define MGC_MG_TPG_XY_MASK 0x74 --#define MGC_MG_TPG_TILE_DIM 0x78 --#define MGC_MG_PRBS_LFSR_INIT_0 0x7c --#define MGC_MG_PRBS_LFSR_INIT_1 0x80 --#define MGC_MG_SYNC_STOP_POINT 0x84 --#define MGC_MG_SYNC_STOP_POINT_LOC 0x88 --#define MGC_MG_ERR_INJECT 0x8c --#define MGC_MG_ERR_LOCATION 0x90 --#define MGC_MG_DTO_SPEED_CTRL_EN 0x94 --#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98 --#define MGC_MG_HOR_LOC_STTS 0x9c --#define MGC_MG_VER_LOC_STTS 0xa0 --#define MGC_MG_FRAME_NUM_STTS 0xa4 --#define MGC_MG_BUSY_STTS 0xa8 --#define MGC_MG_STOPPED_STTS 0xac --/* tile width and height in pixels for Chess board and Color palette */ --#define MGC_TPG_TILE_WIDTH 64U --#define MGC_TPG_TILE_HEIGHT 64U -- --#define IPU_CSI_PORT_A_ADDR_OFFSET 0x0 --#define IPU_CSI_PORT_B_ADDR_OFFSET 0x0 --#define IPU_CSI_PORT_C_ADDR_OFFSET 0x0 --#define IPU_CSI_PORT_D_ADDR_OFFSET 0x0 -- --/* -- * 0 - CSI RX Port 0 interrupt; -- * 1 - MPF Port 0 interrupt; -- * 2 - CSI RX Port 1 interrupt; -- * 3 - MPF Port 1 interrupt; -- * 4 - CSI RX Port 2 interrupt; -- * 5 - MPF Port 2 interrupt; -- * 6 - CSI RX Port 3 interrupt; -- * 7 - MPF Port 3 interrupt; -- * 8 - Port ARB FIFO 0 overflow; -- * 9 - Port ARB FIFO 1 overflow; -- * 10 - Port ARB FIFO 2 overflow; -- * 11 - Port ARB FIFO 3 overflow; -- * 12 - isys_cfgnoc_err_probe_intl; -- * 13-15 - reserved -- */ --#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff -- --/* Adapter layer irq */ --#define IPU7_CSI_ADPL_IRQ_MASK 0xffff -- --/* sw irq from legacy irq control -- * legacy irq status -- * IPU7 -- * 0 - CSI Port 0 error interrupt -- * 1 - CSI Port 0 sync interrupt -- * 2 - CSI Port 1 error interrupt -- * 3 - CSI Port 1 sync interrupt -- * 4 - CSI Port 2 error interrupt -- * 5 - CSI Port 2 sync interrupt -- * 6 - CSI Port 3 error interrupt -- * 7 - CSI Port 3 sync interrupt -- * IPU7P5 -- * 0 - CSI Port 0 error interrupt -- * 1 - CSI Port 0 fs interrupt -- * 2 - CSI Port 0 fe interrupt -- * 3 - CSI Port 1 error interrupt -- * 4 - CSI Port 1 fs interrupt -- * 5 - CSI Port 1 fe interrupt -- * 6 - CSI Port 2 error interrupt -- * 7 - CSI Port 2 fs interrupt -- * 8 - CSI Port 2 fe interrupt -- */ --#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff -- --/* legacy error status per port -- * 0 - Error handler FIFO full; -- * 1 - Reserved Short Packet encoding detected; -- * 2 - Reserved Long Packet encoding detected; -- * 3 - Received packet is too short (fewer data words than specified in header); -- * 4 - Received packet is too long (more data words than specified in header); -- * 5 - Short packet discarded due to errors; -- * 6 - Long packet discarded due to errors; -- * 7 - CSI Combo Rx interrupt; -- * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0; -- */ --#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff -- --/* -- * 0 - VC0 frame start received -- * 1 - VC0 frame end received -- * 2 - VC1 frame start received -- * 3 - VC1 frame end received -- * 4 - VC2 frame start received -- * 5 - VC2 frame end received -- * 6 - VC3 frame start received -- * 7 - VC3 frame end received -- * 8 - VC4 frame start received -- * 9 - VC4 frame end received -- * 10 - VC5 frame start received -- * 11 - VC5 frame end received -- * 12 - VC6 frame start received -- * 13 - VC6 frame end received -- * 14 - VC7 frame start received -- * 15 - VC7 frame end received -- * 16 - VC8 frame start received -- * 17 - VC8 frame end received -- * 18 - VC9 frame start received -- * 19 - VC9 frame end received -- * 20 - VC10 frame start received -- * 21 - VC10 frame end received -- * 22 - VC11 frame start received -- * 23 - VC11 frame end received -- * 24 - VC12 frame start received -- * 25 - VC12 frame end received -- * 26 - VC13 frame start received -- * 27 - VC13 frame end received -- * 28 - VC14 frame start received -- * 29 - VC14 frame end received -- * 30 - VC15 frame start received -- * 31 - VC15 frame end received -- */ -- --#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0 --#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0 -- --#define CSI_RX_NUM_ERRORS_IN_IRQ 12U --#define CSI_RX_NUM_SYNC_IN_IRQ 32U -- --enum CSI_FE_MODE_TYPE { -- CSI_FE_DPHY_MODE = 0, -- CSI_FE_CPHY_MODE = 1, --}; -- --enum CSI_FE_INPUT_MODE { -- CSI_SENSOR_INPUT = 0, -- CSI_MIPIGEN_INPUT = 1, --}; -- --enum MGC_CSI_ADPL_TYPE { -- MGC_MAPPED_2_LANES = 0, -- MGC_MAPPED_4_LANES = 1, --}; -- --enum CSI2HOST_SELECTION { -- CSI2HOST_SEL_SOC = 0, -- CSI2HOST_SEL_CSI2HOST = 1, --}; -- --#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port)) --#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port)) -- --/* ---------------------------------------------------------------- */ --#define CSI_REG_BASE 0x220000 --#define CSI_REG_BASE_PORT(id) ((id) * 0x1000) -- --/* CSI Port General Purpose Registers */ --#define CSI_REG_PORT_GPREG_SRST 0x0 --#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 --#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 -- --#define CSI_RX_NUM_IRQ 32U -- --#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555 --#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa --#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff --#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff -- --#endif /* IPU7_ISYS_CSI2_REG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -deleted file mode 100644 -index 0fe4299222e8..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -+++ /dev/null -@@ -1,544 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-isys-csi-phy.h" -- --static const u32 csi2_supported_codes[] = { -- MEDIA_BUS_FMT_Y10_1X10, -- MEDIA_BUS_FMT_RGB565_1X16, -- MEDIA_BUS_FMT_RGB888_1X24, -- MEDIA_BUS_FMT_UYVY8_1X16, -- MEDIA_BUS_FMT_YUYV8_1X16, -- MEDIA_BUS_FMT_YUYV10_1X20, -- MEDIA_BUS_FMT_SBGGR10_1X10, -- MEDIA_BUS_FMT_SGBRG10_1X10, -- MEDIA_BUS_FMT_SGRBG10_1X10, -- MEDIA_BUS_FMT_SRGGB10_1X10, -- MEDIA_BUS_FMT_SBGGR12_1X12, -- MEDIA_BUS_FMT_SGBRG12_1X12, -- MEDIA_BUS_FMT_SGRBG12_1X12, -- MEDIA_BUS_FMT_SRGGB12_1X12, -- MEDIA_BUS_FMT_SBGGR8_1X8, -- MEDIA_BUS_FMT_SGBRG8_1X8, -- MEDIA_BUS_FMT_SGRBG8_1X8, -- MEDIA_BUS_FMT_SRGGB8_1X8, -- 0, --}; -- --s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) --{ -- struct media_pad *src_pad; -- -- src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -- if (IS_ERR(src_pad)) { -- dev_err(&csi2->isys->adev->auxdev.dev, -- "can't get source pad of %s (%ld)\n", -- csi2->asd.sd.name, PTR_ERR(src_pad)); -- return PTR_ERR(src_pad); -- } -- -- return v4l2_get_link_freq(src_pad, 0, 0); --} -- --static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -- struct v4l2_event_subscription *sub) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- -- dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", -- sub->type, sub->id); -- -- switch (sub->type) { -- case V4L2_EVENT_FRAME_SYNC: -- return v4l2_event_subscribe(fh, sub, 10, NULL); -- case V4L2_EVENT_CTRL: -- return v4l2_ctrl_subscribe_event(fh, sub); -- default: -- return -EINVAL; -- } --} -- --static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { -- .subscribe_event = csi2_subscribe_event, -- .unsubscribe_event = v4l2_event_subdev_unsubscribe, --}; -- --static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_device *isp = csi2->isys->adev->isp; -- unsigned int offset, mask; -- -- /* enable CSI2 legacy error irq */ -- offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(mask, csi2->base + offset + IRQ_CTL_MASK); -- writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -- -- /* enable CSI2 legacy sync irq */ -- offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(mask, csi2->base + offset + IRQ_CTL_MASK); -- writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -- -- mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK; -- if (!is_ipu7(isp->hw_ver)) { -- writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -- writel(mask, csi2->base + offset + IRQ1_CTL_MASK); -- writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE); -- } --} -- --static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_device *isp = csi2->isys->adev->isp; -- unsigned int offset, mask; -- -- /* disable CSI2 legacy error irq */ -- offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(0, csi2->base + offset + IRQ_CTL_MASK); -- writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -- -- /* disable CSI2 legacy sync irq */ -- offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -- mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -- writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -- writel(0, csi2->base + offset + IRQ_CTL_MASK); -- writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -- -- if (!is_ipu7(isp->hw_ver)) { -- writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -- writel(0, csi2->base + offset + IRQ1_CTL_MASK); -- writel(0, csi2->base + offset + IRQ1_CTL_ENABLE); -- } --} -- --static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- void __iomem *isys_base = isys->pdata->base; -- -- ipu7_isys_csi_phy_powerdown(csi2); -- -- writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); -- csi2_irq_disable(csi2); --} -- --static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_isys *isys = csi2->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- void __iomem *isys_base = isys->pdata->base; -- unsigned int port, nlanes, offset; -- int ret; -- -- port = csi2->port; -- nlanes = csi2->nlanes; -- -- offset = IS_IO_GPREGS_BASE; -- writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK); -- dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", -- port, readl(isys_base + offset + CSI_PORT_CLK_GATE), -- readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); -- if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { -- dev_dbg(dev, "CSI port %u in aggregation mode\n", port); -- writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); -- } -- -- /* input is coming from CSI receiver (sensor) */ -- offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -- writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE); -- writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); -- -- ret = ipu7_isys_csi_phy_powerup(csi2); -- if (ret) { -- dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret); -- return ret; -- } -- -- csi2_irq_enable(csi2); -- -- return 0; --} -- --static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_selection *sel) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct device *dev = &asd->isys->adev->auxdev.dev; -- struct v4l2_mbus_framefmt *sink_ffmt; -- struct v4l2_mbus_framefmt *src_ffmt; -- struct v4l2_rect *crop; -- -- if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) -- return -EINVAL; -- -- sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -- sel->pad, -- sel->stream); -- if (!sink_ffmt) -- return -EINVAL; -- -- src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); -- if (!src_ffmt) -- return -EINVAL; -- -- crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -- if (!crop) -- return -EINVAL; -- -- /* Only vertical cropping is supported */ -- sel->r.left = 0; -- sel->r.width = sink_ffmt->width; -- /* Non-bayer formats can't be single line cropped */ -- if (!ipu7_isys_is_bayer_format(sink_ffmt->code)) -- sel->r.top &= ~1U; -- sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT, -- sink_ffmt->height - sel->r.top); -- *crop = sel->r; -- -- /* update source pad format */ -- src_ffmt->width = sel->r.width; -- src_ffmt->height = sel->r.height; -- if (ipu7_isys_is_bayer_format(sink_ffmt->code)) -- src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code, -- sel->r.left, -- sel->r.top); -- dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", -- sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, -- src_ffmt->code); -- -- return 0; --} -- --static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_selection *sel) --{ -- struct v4l2_mbus_framefmt *sink_ffmt; -- struct v4l2_rect *crop; -- int ret = 0; -- -- if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) -- return -EINVAL; -- -- sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -- sel->pad, -- sel->stream); -- if (!sink_ffmt) -- return -EINVAL; -- -- crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -- if (!crop) -- return -EINVAL; -- -- switch (sel->target) { -- case V4L2_SEL_TGT_CROP_DEFAULT: -- case V4L2_SEL_TGT_CROP_BOUNDS: -- sel->r.left = 0; -- sel->r.top = 0; -- sel->r.width = sink_ffmt->width; -- sel->r.height = sink_ffmt->height; -- break; -- case V4L2_SEL_TGT_CROP: -- sel->r = *crop; -- break; -- default: -- ret = -EINVAL; -- } -- -- return ret; --} -- --/* -- * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set -- * of streams. -- */ --#define CSI2_SUBDEV_MAX_STREAM_ID 63 -- --static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- u32 pad, u64 streams_mask) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -- struct v4l2_subdev *r_sd; -- struct media_pad *rp; -- u32 sink_pad, sink_stream; -- int ret, i; -- -- if (!csi2->stream_count) { -- dev_dbg(&csi2->isys->adev->auxdev.dev, -- "stream on CSI2-%u with %u lanes\n", csi2->port, -- csi2->nlanes); -- ret = ipu7_isys_csi2_enable_stream(csi2); -- if (ret) -- return ret; -- } -- -- for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -- if (streams_mask & BIT_ULL(i)) -- break; -- } -- -- ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -- &sink_pad, &sink_stream); -- if (ret) -- return ret; -- -- rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -- r_sd = media_entity_to_v4l2_subdev(rp->entity); -- -- ret = v4l2_subdev_enable_streams(r_sd, rp->index, -- BIT_ULL(sink_stream)); -- if (!ret) { -- csi2->stream_count++; -- return 0; -- } -- -- if (!csi2->stream_count) -- ipu7_isys_csi2_disable_stream(csi2); -- -- return ret; --} -- --static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- u32 pad, u64 streams_mask) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -- struct v4l2_subdev *r_sd; -- struct media_pad *rp; -- u32 sink_pad, sink_stream; -- int ret, i; -- -- for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -- if (streams_mask & BIT_ULL(i)) -- break; -- } -- -- ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -- &sink_pad, &sink_stream); -- if (ret) -- return ret; -- -- rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -- r_sd = media_entity_to_v4l2_subdev(rp->entity); -- -- v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream)); -- -- if (--csi2->stream_count) -- return 0; -- -- dev_dbg(&csi2->isys->adev->auxdev.dev, -- "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); -- -- ipu7_isys_csi2_disable_stream(csi2); -- -- return 0; --} -- --static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { -- .get_fmt = v4l2_subdev_get_fmt, -- .set_fmt = ipu7_isys_subdev_set_fmt, -- .get_selection = ipu7_isys_csi2_get_sel, -- .set_selection = ipu7_isys_csi2_set_sel, -- .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -- .enable_streams = ipu7_isys_csi2_enable_streams, -- .disable_streams = ipu7_isys_csi2_disable_streams, -- .set_routing = ipu7_isys_subdev_set_routing, --}; -- --static const struct v4l2_subdev_ops csi2_sd_ops = { -- .core = &csi2_sd_core_ops, -- .pad = &csi2_sd_pad_ops, --}; -- --static const struct media_entity_operations csi2_entity_ops = { -- .link_validate = v4l2_subdev_link_validate, -- .has_pad_interdep = v4l2_subdev_has_pad_interdep, --}; -- --void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2) --{ -- if (!csi2->isys) -- return; -- -- v4l2_device_unregister_subdev(&csi2->asd.sd); -- v4l2_subdev_cleanup(&csi2->asd.sd); -- ipu7_isys_subdev_cleanup(&csi2->asd); -- csi2->isys = NULL; --} -- --int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, -- struct ipu7_isys *isys, -- void __iomem *base, unsigned int index) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- int ret; -- -- csi2->isys = isys; -- csi2->base = base; -- csi2->port = index; -- -- if (!is_ipu7(isys->adev->isp->hw_ver)) -- csi2->legacy_irq_mask = 0x7U << (index * 3U); -- else -- csi2->legacy_irq_mask = 0x3U << (index * 2U); -- -- dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index, -- csi2->legacy_irq_mask); -- -- csi2->asd.sd.entity.ops = &csi2_entity_ops; -- csi2->asd.isys = isys; -- -- ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, -- IPU7_NR_OF_CSI2_SINK_PADS, -- IPU7_NR_OF_CSI2_SRC_PADS); -- if (ret) -- return ret; -- -- csi2->asd.source = (int)index; -- csi2->asd.supported_codes = csi2_supported_codes; -- snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), -- IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index); -- v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); -- -- ret = v4l2_subdev_init_finalize(&csi2->asd.sd); -- if (ret) { -- dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret); -- goto isys_subdev_cleanup; -- } -- -- ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); -- if (ret) { -- dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret); -- goto v4l2_subdev_cleanup; -- } -- -- return 0; -- --v4l2_subdev_cleanup: -- v4l2_subdev_cleanup(&csi2->asd.sd); --isys_subdev_cleanup: -- ipu7_isys_subdev_cleanup(&csi2->asd); -- -- return ret; --} -- --void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct video_device *vdev = csi2->asd.sd.devnode; -- struct v4l2_event ev = { -- .type = V4L2_EVENT_FRAME_SYNC, -- }; -- -- ev.id = stream->vc; -- ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -- v4l2_event_queue(vdev, &ev); -- -- dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", -- csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); --} -- --void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- struct device *dev = &stream->isys->adev->auxdev.dev; -- u32 frame_sequence = atomic_read(&stream->sequence); -- -- dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", -- csi2->port, frame_sequence); --} -- --int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -- struct ipu7_isys_csi2 *csi2, -- struct media_entity *source_entity, -- struct v4l2_mbus_frame_desc_entry *entry, -- int *nr_queues) --{ -- struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- struct v4l2_mbus_frame_desc desc; -- struct v4l2_subdev *source; -- struct media_pad *pad; -- unsigned int i; -- int ret; -- -- source = media_entity_to_v4l2_subdev(source_entity); -- if (!source) -- return -EPIPE; -- -- pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); -- if (!pad) -- return -EPIPE; -- -- ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); -- if (ret) -- return ret; -- -- if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -- dev_err(dev, "Unsupported frame descriptor type\n"); -- return -EINVAL; -- } -- -- for (i = 0; i < desc.num_entries; i++) { -- if (source_stream == desc.entry[i].stream) { -- desc_entry = &desc.entry[i]; -- break; -- } -- } -- -- if (!desc_entry) { -- dev_err(dev, "Failed to find stream %u from remote subdev\n", -- source_stream); -- return -EINVAL; -- } -- -- if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) { -- dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); -- return -EINVAL; -- } -- -- *entry = *desc_entry; -- -- for (i = 0; i < desc.num_entries; i++) { -- if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) -- (*nr_queues)++; -- } -- -- return 0; --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -deleted file mode 100644 -index 6c23b80f92a2..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -+++ /dev/null -@@ -1,64 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_CSI2_H --#define IPU7_ISYS_CSI2_H -- --#include --#include -- --#include "ipu7-isys-subdev.h" --#include "ipu7-isys-video.h" -- --struct ipu7_isys; --struct ipu7_isys_csi2_pdata; --struct ipu7_isys_stream; -- --#define IPU7_NR_OF_CSI2_VC 16U --#define INVALID_VC_ID -1 --#define IPU7_NR_OF_CSI2_SINK_PADS 1U --#define IPU7_CSI2_PAD_SINK 0U --#define IPU7_NR_OF_CSI2_SRC_PADS 8U --#define IPU7_CSI2_PAD_SRC 1U --#define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \ -- IPU7_NR_OF_CSI2_SRC_PADS) -- --/* -- * struct ipu7_isys_csi2 -- * -- * @nlanes: number of lanes in the receiver -- */ --struct ipu7_isys_csi2 { -- struct ipu7_isys_subdev asd; -- struct ipu7_isys_csi2_pdata *pdata; -- struct ipu7_isys *isys; -- struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS]; -- -- void __iomem *base; -- u32 receiver_errors; -- u32 legacy_irq_mask; -- unsigned int nlanes; -- unsigned int port; -- unsigned int phy_mode; -- unsigned int stream_count; --}; -- --#define ipu7_isys_subdev_to_csi2(__sd) \ -- container_of(__sd, struct ipu7_isys_csi2, asd) -- --#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd) -- --s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2); --int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys, -- void __iomem *base, unsigned int index); --void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2); --void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream); --void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream); --int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -- struct ipu7_isys_csi2 *csi2, -- struct media_entity *source_entity, -- struct v4l2_mbus_frame_desc_entry *entry, -- int *nr_queues); --#endif /* IPU7_ISYS_CSI2_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -deleted file mode 100644 -index 43955c6fa8cf..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -+++ /dev/null -@@ -1,1198 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#include --#endif -- --#include --#include --#include --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-fw-isys.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-isys-video.h" --#include "ipu7-platform-regs.h" --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#include "ipu7-cpd.h" --#endif -- --#define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) -- --static int ipu7_isys_buf_init(struct vb2_buffer *vb) --{ -- struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -- struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- int ret; -- -- ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); -- if (ret) -- return ret; -- -- ivb->dma_addr = sg_dma_address(sg->sgl); -- -- return 0; --} -- --static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb) --{ -- struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -- struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- -- ivb->dma_addr = 0; -- ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); --} -- --static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, -- unsigned int *num_planes, unsigned int sizes[], -- struct device *alloc_devs[]) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- u32 size = av->pix_fmt.sizeimage; -- -- /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ -- if (!*num_planes) { -- sizes[0] = size; -- } else if (sizes[0] < size) { -- dev_dbg(dev, "%s: queue setup: size %u < %u\n", -- av->vdev.name, sizes[0], size); -- return -EINVAL; -- } -- -- *num_planes = 1; -- -- return 0; --} -- --static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -- struct device *dev = &av->isys->adev->auxdev.dev; -- u32 bytesperline = av->pix_fmt.bytesperline; -- u32 height = av->pix_fmt.height; -- -- dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", -- av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0)); -- -- if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0)) -- return -EINVAL; -- -- dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n", -- av->vdev.name, bytesperline, height); -- vb2_set_plane_payload(vb, 0, bytesperline * height); -- -- /* assume IPU is not DMA coherent */ -- ipu7_dma_sync_sgtable(isys->adev, sg); -- -- return 0; --} -- --/* -- * Queue a buffer list back to incoming or active queues. The buffers -- * are removed from the buffer list. -- */ --void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -- unsigned long op_flags, -- enum vb2_buffer_state state) --{ -- struct ipu7_isys_buffer *ib, *ib_safe; -- unsigned long flags; -- bool first = true; -- -- if (!bl) -- return; -- -- WARN_ON_ONCE(!bl->nbufs); -- WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE && -- op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING); -- -- list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { -- struct ipu7_isys_video *av; -- -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- struct ipu7_isys_queue *aq = -- vb2_queue_to_isys_queue(vb->vb2_queue); -- -- av = ipu7_isys_queue_to_video(aq); -- spin_lock_irqsave(&aq->lock, flags); -- list_del(&ib->head); -- if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) -- list_add(&ib->head, &aq->active); -- else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) -- list_add_tail(&ib->head, &aq->incoming); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE) -- vb2_buffer_done(vb, state); -- -- if (first) { -- dev_dbg(&av->isys->adev->auxdev.dev, -- "queue buf list %p flags %lx, s %d, %d bufs\n", -- bl, op_flags, state, bl->nbufs); -- first = false; -- } -- -- bl->nbufs--; -- } -- -- WARN_ON(bl->nbufs); --} -- --/* -- * flush_firmware_streamon_fail() - Flush in cases where requests may -- * have been queued to firmware and the *firmware streamon fails for a -- * reason or another. -- */ --static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_queue *aq; -- unsigned long flags; -- -- lockdep_assert_held(&stream->mutex); -- -- list_for_each_entry(aq, &stream->queues, node) { -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_buffer *ib, *ib_safe; -- -- spin_lock_irqsave(&aq->lock, flags); -- list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { -- struct vb2_buffer *vb = -- ipu7_isys_buffer_to_vb2_buffer(ib); -- -- list_del(&ib->head); -- if (av->streaming) { -- dev_dbg(dev, -- "%s: queue buffer %u back to incoming\n", -- av->vdev.name, vb->index); -- /* Queue already streaming, return to driver. */ -- list_add(&ib->head, &aq->incoming); -- continue; -- } -- /* Queue not yet streaming, return to user. */ -- dev_dbg(dev, "%s: return %u back to videobuf2\n", -- av->vdev.name, vb->index); -- vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib), -- VB2_BUF_STATE_QUEUED); -- } -- spin_unlock_irqrestore(&aq->lock, flags); -- } --} -- --/* -- * Attempt obtaining a buffer list from the incoming queues, a list of buffers -- * that contains one entry from each video buffer queue. If a buffer can't be -- * obtained from every queue, the buffers are returned back to the queue. -- */ --static int buffer_list_get(struct ipu7_isys_stream *stream, -- struct ipu7_isys_buffer_list *bl) --{ -- unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING; -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct ipu7_isys_queue *aq; -- unsigned long flags; -- -- bl->nbufs = 0; -- INIT_LIST_HEAD(&bl->head); -- -- list_for_each_entry(aq, &stream->queues, node) { -- struct ipu7_isys_buffer *ib; -- -- spin_lock_irqsave(&aq->lock, flags); -- if (list_empty(&aq->incoming)) { -- spin_unlock_irqrestore(&aq->lock, flags); -- if (!list_empty(&bl->head)) -- ipu7_isys_buffer_list_queue(bl, buf_flag, 0); -- return -ENODATA; -- } -- -- ib = list_last_entry(&aq->incoming, -- struct ipu7_isys_buffer, head); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- -- if (av->skipframe) { -- atomic_set(&ib->skipframe_flag, 1); -- av->skipframe--; -- } else { -- atomic_set(&ib->skipframe_flag, 0); -- } --#endif -- dev_dbg(dev, "buffer: %s: buffer %u\n", -- ipu7_isys_queue_to_video(aq)->vdev.name, -- ipu7_isys_buffer_to_vb2_buffer(ib)->index); -- list_del(&ib->head); -- list_add(&ib->head, &bl->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- bl->nbufs++; -- } -- -- dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); -- -- return 0; --} -- --static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, -- struct ipu7_insys_buffset *set) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- --#ifndef IPU8_INSYS_NEW_ABI -- set->output_pins[aq->fw_output].addr = ivb->dma_addr; -- set->output_pins[aq->fw_output].user_token = (uintptr_t)set; --#else -- set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; -- set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; -- set->output_pins[aq->fw_output].upipe_capture_cfg = 0; --#endif --} -- --/* -- * Convert a buffer list to a isys fw ABI framebuffer set. The -- * buffer list is not modified. -- */ --#define IPU_ISYS_FRAME_NUM_THRESHOLD (30) --void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -- struct ipu7_isys_stream *stream, -- struct ipu7_isys_buffer_list *bl) --{ -- struct ipu7_isys_buffer *ib; -- u32 buf_id; -- -- WARN_ON(!bl->nbufs); -- -- set->skip_frame = 0; -- set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP | -- IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ; -- -- buf_id = atomic_fetch_inc(&stream->buf_id); -- set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER; -- -- list_for_each_entry(ib, &bl->head, head) { -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- ipu7_isys_buf_to_fw_frame_buf_pin(vb, set); -- } --} -- --/* Start streaming for real. The buffer list must be available. */ --static int ipu7_isys_stream_start(struct ipu7_isys_video *av, -- struct ipu7_isys_buffer_list *bl, bool error) --{ -- struct ipu7_isys_stream *stream = av->stream; -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct ipu7_isys_buffer_list __bl; -- int ret; -- -- mutex_lock(&stream->isys->stream_mutex); -- -- ret = ipu7_isys_video_set_streaming(av, 1, bl); -- mutex_unlock(&stream->isys->stream_mutex); -- if (ret) -- goto out_requeue; -- -- stream->streaming = 1; -- -- bl = &__bl; -- -- do { -- struct ipu7_insys_buffset *buf = NULL; -- struct isys_fw_msgs *msg; -- enum ipu7_insys_send_type send_type = -- IPU_INSYS_SEND_TYPE_STREAM_CAPTURE; -- -- ret = buffer_list_get(stream, bl); -- if (ret < 0) -- break; -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) -- return -ENOMEM; -- -- buf = &msg->fw_msg.frame; -- -- ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -- -- ipu7_fw_isys_dump_frame_buff_set(dev, buf, -- stream->nr_output_pins); -- -- ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, -- 0); -- -- ret = ipu7_fw_isys_complex_cmd(stream->isys, -- stream->stream_handle, buf, -- msg->dma_addr, sizeof(*buf), -- send_type); -- } while (!WARN_ON(ret)); -- -- return 0; -- --out_requeue: -- if (bl && bl->nbufs) -- ipu7_isys_buffer_list_queue(bl, -- IPU_ISYS_BUFFER_LIST_FL_INCOMING | -- (error ? -- IPU_ISYS_BUFFER_LIST_FL_SET_STATE : -- 0), error ? VB2_BUF_STATE_ERROR : -- VB2_BUF_STATE_QUEUED); -- flush_firmware_streamon_fail(stream); -- -- return ret; --} -- --static void buf_queue(struct vb2_buffer *vb) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_video_buffer *ivb = -- vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- struct media_pipeline *media_pipe = -- media_entity_pipeline(&av->vdev.entity); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_isys_buffer *ib = &ivb->ib; -- struct ipu7_insys_buffset *buf = NULL; -- struct ipu7_isys_buffer_list bl; -- struct isys_fw_msgs *msg; -- unsigned long flags; -- dma_addr_t dma; -- int ret; -- -- dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); -- -- dma = ivb->dma_addr; -- dev_dbg(dev, "iova: iova %pad\n", &dma); -- -- spin_lock_irqsave(&aq->lock, flags); -- list_add(&ib->head, &aq->incoming); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- if (!media_pipe || !vb->vb2_queue->start_streaming_called) { -- dev_dbg(dev, "media pipeline is not ready for %s\n", -- av->vdev.name); -- return; -- } -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_lock(&av->isys->reset_mutex); -- if (av->isys->state & RESET_STATE_IN_RESET) { -- dev_dbg(dev, "in reset, adding to incoming\n"); -- mutex_unlock(&av->isys->reset_mutex); -- return; -- } -- mutex_unlock(&av->isys->reset_mutex); -- -- /* ip may be cleared in ipu reset */ -- stream = av->stream; --#endif -- mutex_lock(&stream->mutex); -- -- if (stream->nr_streaming != stream->nr_queues) { -- dev_dbg(dev, "not streaming yet, adding to incoming\n"); -- goto out; -- } -- -- /* -- * We just put one buffer to the incoming list of this queue -- * (above). Let's see whether all queues in the pipeline would -- * have a buffer. -- */ -- ret = buffer_list_get(stream, &bl); -- if (ret < 0) { -- dev_dbg(dev, "No buffers available\n"); -- goto out; -- } -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) { -- ret = -ENOMEM; -- goto out; -- } -- -- buf = &msg->fw_msg.frame; -- -- ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl); -- -- ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); -- -- if (!stream->streaming) { -- ret = ipu7_isys_stream_start(av, &bl, true); -- if (ret) -- dev_err(dev, "stream start failed.\n"); -- goto out; -- } -- -- /* -- * We must queue the buffers in the buffer list to the -- * appropriate video buffer queues BEFORE passing them to the -- * firmware since we could get a buffer event back before we -- * have queued them ourselves to the active queue. -- */ -- ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -- -- ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle, -- buf, msg->dma_addr, sizeof(*buf), -- IPU_INSYS_SEND_TYPE_STREAM_CAPTURE); -- if (ret < 0) -- dev_err(dev, "send stream capture failed\n"); -- --out: -- mutex_unlock(&stream->mutex); --} -- --static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) --{ -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct media_pad *remote_pad = -- media_pad_remote_pad_first(av->vdev.entity.pads); -- struct v4l2_mbus_framefmt format; -- struct v4l2_subdev *sd; -- u32 r_stream, code; -- int ret; -- -- if (!remote_pad) -- return -ENOTCONN; -- -- sd = media_entity_to_v4l2_subdev(remote_pad->entity); -- r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index); -- -- ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, -- &format); -- if (ret) { -- dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", -- sd->entity.name, remote_pad->index, r_stream); -- return ret; -- } -- -- if (format.width != av->pix_fmt.width || -- format.height != av->pix_fmt.height) { -- dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", -- av->pix_fmt.width, av->pix_fmt.height, format.width, -- format.height); -- return -EINVAL; -- } -- -- code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -- if (format.code != code) { -- dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", -- code, format.code); -- return -EINVAL; -- } -- -- return 0; --} -- --static void return_buffers(struct ipu7_isys_queue *aq, -- enum vb2_buffer_state state) --{ --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- bool need_reset = false; -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); --#endif -- struct ipu7_isys_buffer *ib; -- struct vb2_buffer *vb; -- unsigned long flags; -- -- spin_lock_irqsave(&aq->lock, flags); -- /* -- * Something went wrong (FW crash / HW hang / not all buffers -- * returned from isys) if there are still buffers queued in active -- * queue. We have to clean up places a bit. -- */ -- while (!list_empty(&aq->active)) { -- ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -- head); -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- vb2_buffer_done(vb, state); -- -- spin_lock_irqsave(&aq->lock, flags); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- need_reset = true; --#endif -- } -- -- while (!list_empty(&aq->incoming)) { -- ib = list_last_entry(&aq->incoming, struct ipu7_isys_buffer, -- head); -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- vb2_buffer_done(vb, state); -- -- spin_lock_irqsave(&aq->lock, flags); -- } -- -- spin_unlock_irqrestore(&aq->lock, flags); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- -- if (need_reset) { -- mutex_lock(&av->isys->reset_mutex); -- av->isys->need_reset = true; -- mutex_unlock(&av->isys->reset_mutex); -- } --#endif --} -- --static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) --{ -- video_device_pipeline_stop(&av->vdev); -- ipu7_isys_put_stream(av->stream); -- av->stream = NULL; --} -- --static int start_streaming(struct vb2_queue *q, unsigned int count) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- struct ipu7_isys_buffer_list __bl, *bl = NULL; -- struct ipu7_isys_stream *stream; -- struct media_entity *source_entity = NULL; -- int nr_queues, ret; -- -- dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", -- av->vdev.name, av->pix_fmt.width, av->pix_fmt.height, -- pfmt->css_pixelformat); -- -- ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -- if (ret < 0) { -- dev_dbg(dev, "failed to setup video\n"); -- goto out_return_buffers; -- } -- -- ret = ipu7_isys_link_fmt_validate(aq); -- if (ret) { -- dev_dbg(dev, -- "%s: link format validation failed (%d)\n", -- av->vdev.name, ret); -- goto out_pipeline_stop; -- } -- -- stream = av->stream; -- mutex_lock(&stream->mutex); -- if (!stream->nr_streaming) { -- ret = ipu7_isys_video_prepare_stream(av, source_entity, -- nr_queues); -- if (ret) { -- mutex_unlock(&stream->mutex); -- goto out_pipeline_stop; -- } -- } -- -- stream->nr_streaming++; -- dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -- stream->nr_queues); -- -- list_add(&aq->node, &stream->queues); -- -- if (stream->nr_streaming != stream->nr_queues) -- goto out; -- -- bl = &__bl; -- ret = buffer_list_get(stream, bl); -- if (ret < 0) { -- dev_warn(dev, "no buffer available, DRIVER BUG?\n"); -- goto out; -- } -- -- ret = ipu7_isys_fw_open(av->isys); -- if (ret) -- goto out_stream_start; -- -- ipu7_isys_setup_hw(av->isys); -- -- ret = ipu7_isys_stream_start(av, bl, false); -- if (ret) -- goto out_isys_fw_close; -- --out: -- mutex_unlock(&stream->mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- av->start_streaming = 1; --#endif -- -- return 0; -- --out_isys_fw_close: -- ipu7_isys_fw_close(av->isys); -- --out_stream_start: -- list_del(&aq->node); -- stream->nr_streaming--; -- mutex_unlock(&stream->mutex); -- --out_pipeline_stop: -- ipu7_isys_stream_cleanup(av); -- --out_return_buffers: -- return_buffers(aq, VB2_BUF_STATE_QUEUED); -- -- return ret; --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --static void reset_stop_streaming(struct ipu7_isys_video *av) --{ -- struct ipu7_isys_queue *aq = &av->aq; -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_isys_buffer *ib; -- struct vb2_buffer *vb; -- unsigned long flags; -- -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- -- mutex_lock(&stream->mutex); -- stream->nr_streaming--; -- list_del(&aq->node); -- stream->streaming = 0; -- mutex_unlock(&stream->mutex); -- -- ipu7_isys_stream_cleanup(av); -- -- spin_lock_irqsave(&aq->lock, flags); -- while (!list_empty(&aq->active)) { -- ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -- head); -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -- -- spin_lock_irqsave(&aq->lock, flags); -- } -- spin_unlock_irqrestore(&aq->lock, flags); -- -- ipu7_isys_fw_close(av->isys); --} -- --static int reset_start_streaming(struct ipu7_isys_video *av) --{ -- struct ipu7_isys_queue *aq = &av->aq; -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_buffer_list __bl, *bl = NULL; -- struct ipu7_isys_stream *stream; -- struct media_entity *source_entity = NULL; -- int nr_queues; -- int ret; -- -- dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); -- -- av->skipframe = 1; -- -- ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -- if (ret < 0) { -- dev_dbg(dev, "failed to setup video\n"); -- goto out_return_buffers; -- } -- -- ret = ipu7_isys_link_fmt_validate(aq); -- if (ret) { -- dev_dbg(dev, -- "%s: link format validation failed (%d)\n", -- av->vdev.name, ret); -- goto out_pipeline_stop; -- } -- -- stream = av->stream; -- mutex_lock(&stream->mutex); -- if (!stream->nr_streaming) { -- ret = ipu7_isys_video_prepare_stream(av, source_entity, -- nr_queues); -- if (ret) { -- mutex_unlock(&stream->mutex); -- goto out_pipeline_stop; -- } -- } -- -- stream->nr_streaming++; -- dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -- stream->nr_queues); -- -- list_add(&aq->node, &stream->queues); -- -- if (stream->nr_streaming != stream->nr_queues) -- goto out; -- -- bl = &__bl; -- ret = buffer_list_get(stream, bl); -- /* -- * In reset start streaming and no buffer available, -- * it is considered that gstreamer has been closed, -- * and reset start is no needed, not driver bug. -- */ -- if (ret) { -- dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- -- goto out_stream_start; -- } -- -- ret = ipu7_isys_fw_open(av->isys); -- if (ret) -- goto out_stream_start; -- -- ipu7_isys_setup_hw(av->isys); -- -- ret = ipu7_isys_stream_start(av, bl, false); -- if (ret) -- goto out_isys_fw_close; -- --out: -- mutex_unlock(&stream->mutex); -- av->start_streaming = 1; -- return 0; -- --out_isys_fw_close: -- ipu7_isys_fw_close(av->isys); -- --out_stream_start: -- list_del(&aq->node); -- stream->nr_streaming--; -- mutex_unlock(&stream->mutex); -- --out_pipeline_stop: -- ipu7_isys_stream_cleanup(av); -- --out_return_buffers: -- return_buffers(aq, VB2_BUF_STATE_QUEUED); -- av->start_streaming = 0; -- dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); -- return ret; --} -- --static int ipu_isys_reset(struct ipu7_isys_video *self_av, -- struct ipu7_isys_stream *self_stream) --{ -- struct ipu7_isys *isys = self_av->isys; -- struct ipu7_bus_device *adev = isys->adev; -- struct ipu7_isys_video *av = NULL; -- struct ipu7_isys_stream *stream = NULL; -- struct device *dev = &adev->auxdev.dev; -- int i, j; -- int has_streaming = 0; -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- -- mutex_lock(&isys->reset_mutex); -- if (isys->state & RESET_STATE_IN_RESET) { -- mutex_unlock(&isys->reset_mutex); -- return 0; -- } -- isys->state |= RESET_STATE_IN_RESET; -- dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); -- -- while (isys->state & RESET_STATE_IN_STOP_STREAMING) { -- dev_dbg(dev, "isys reset: %s: wait for stop\n", -- self_av->vdev.name); -- mutex_unlock(&isys->reset_mutex); -- usleep_range(10000, 11000); -- mutex_lock(&isys->reset_mutex); -- } -- -- mutex_unlock(&isys->reset_mutex); -- -- dev_dbg(dev, "reset stop streams\n"); -- for (i = 0; i < csi2_pdata->nports; i++) { -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -- av = &isys->csi2[i].av[j]; -- if (av == self_av) -- continue; -- -- stream = av->stream; -- if (!stream || stream == self_stream) -- continue; -- -- if (!stream->streaming && !stream->nr_streaming) -- continue; -- -- av->reset = true; -- has_streaming = true; -- reset_stop_streaming(av); -- } -- } -- -- if (!has_streaming) -- goto end_of_reset; -- -- ipu7_cleanup_fw_msg_bufs(isys); -- -- dev_dbg(dev, "reset start streams\n"); -- -- for (j = 0; j < csi2_pdata->nports; j++) { -- for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { -- av = &isys->csi2[j].av[i]; -- if (!av->reset) -- continue; -- -- av->reset = false; -- reset_start_streaming(av); -- } -- } -- --end_of_reset: -- mutex_lock(&isys->reset_mutex); -- isys->state &= ~RESET_STATE_IN_RESET; -- mutex_unlock(&isys->reset_mutex); -- dev_dbg(dev, "reset done\n"); -- -- return 0; --} -- --static void stop_streaming(struct vb2_queue *q) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct ipu7_isys_stream *stream = av->stream; -- int ret = 0; -- -- struct device *dev = &av->isys->adev->auxdev.dev; -- bool need_reset; -- -- dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); -- -- mutex_lock(&av->isys->reset_mutex); -- while (av->isys->state) { -- mutex_unlock(&av->isys->reset_mutex); -- dev_dbg(dev, "stop: %s: wait for rest or stop, isys->state = %d\n", -- av->vdev.name, av->isys->state); -- usleep_range(10000, 11000); -- mutex_lock(&av->isys->reset_mutex); -- } -- -- if (!av->start_streaming) { -- mutex_unlock(&av->isys->reset_mutex); -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- return; -- } -- -- av->isys->state |= RESET_STATE_IN_STOP_STREAMING; -- mutex_unlock(&av->isys->reset_mutex); -- -- stream = av->stream; -- if (!stream) { -- dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- mutex_lock(&av->isys->reset_mutex); -- av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -- mutex_unlock(&av->isys->reset_mutex); -- return; -- } -- -- mutex_lock(&stream->mutex); -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ret = ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- if (ret) { -- dev_err(dev, "stop: video set streaming failed\n"); -- mutex_unlock(&stream->mutex); -- return; -- } -- -- stream->nr_streaming--; -- list_del(&aq->node); -- stream->streaming = 0; -- -- mutex_unlock(&stream->mutex); -- -- ipu7_isys_stream_cleanup(av); -- -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- -- ipu7_isys_fw_close(av->isys); -- -- av->start_streaming = 0; -- mutex_lock(&av->isys->reset_mutex); -- av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -- need_reset = av->isys->need_reset; -- mutex_unlock(&av->isys->reset_mutex); -- -- if (need_reset) { -- if (!stream->nr_streaming) { -- ipu_isys_reset(av, stream); -- } else { -- mutex_lock(&av->isys->reset_mutex); -- av->isys->need_reset = false; -- mutex_unlock(&av->isys->reset_mutex); -- } -- } -- -- dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); --} --#else --static void stop_streaming(struct vb2_queue *q) --{ -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct ipu7_isys_stream *stream = av->stream; -- int ret = 0; -- -- mutex_lock(&stream->mutex); -- mutex_lock(&av->isys->stream_mutex); -- if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ret = ipu7_isys_video_set_streaming(av, 0, NULL); -- mutex_unlock(&av->isys->stream_mutex); -- if (ret) { -- dev_err(&av->isys->adev->auxdev.dev, -- "stop: video set streaming failed\n"); -- mutex_unlock(&stream->mutex); -- return; -- } -- -- stream->nr_streaming--; -- list_del(&aq->node); -- stream->streaming = 0; -- -- mutex_unlock(&stream->mutex); -- -- ipu7_isys_stream_cleanup(av); -- -- return_buffers(aq, VB2_BUF_STATE_ERROR); -- -- ipu7_isys_fw_close(av->isys); --} --#endif -- --static unsigned int --get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) --{ -- struct ipu7_isys *isys = stream->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- unsigned int i; -- -- /* -- * The timestamp is invalid as no TSC in some FPGA platform, -- * so get the sequence from pipeline directly in this case. -- */ -- if (time == 0) -- return atomic_read(&stream->sequence) - 1; -- -- for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -- if (time == stream->seq[i].timestamp) { -- dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n", -- stream->seq[i].sequence, time); -- return stream->seq[i].sequence; -- } -- -- dev_dbg(dev, "SOF: looking for %llu\n", time); -- for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -- dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n", -- stream->seq[i].sequence, stream->seq[i].timestamp); -- dev_dbg(dev, "SOF sequence number not found\n"); -- -- return atomic_read(&stream->sequence) - 1; --} -- --static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time) --{ -- struct ipu7_bus_device *adev = av->isys->adev; -- struct ipu7_device *isp = adev->isp; -- u64 delta, tsc_now; -- -- ipu_buttress_tsc_read(isp, &tsc_now); -- if (!tsc_now) -- return 0; -- -- delta = tsc_now - time; -- -- return ipu_buttress_tsc_ticks_to_ns(delta, isp); --} -- --static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib, -- u64 time) --{ -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); -- struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- u64 ns; -- u32 sequence; -- -- ns = ktime_get_ns() - get_sof_ns_delta(av, time); -- sequence = get_sof_sequence_by_timestamp(stream, time); -- -- vbuf->vb2_buf.timestamp = ns; -- vbuf->sequence = sequence; -- -- dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", -- av->vdev.name, ktime_get_ns(), sequence); -- dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, -- vbuf->vb2_buf.timestamp); --} -- --static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) --{ -- struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- -- if (atomic_read(&ib->str2mmio_flag)) { -- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -- /* -- * Operation on buffer is ended with error and will be reported -- * to the userspace when it is de-queued -- */ -- atomic_set(&ib->str2mmio_flag, 0); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- } else if (atomic_read(&ib->skipframe_flag)) { -- vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -- atomic_set(&ib->skipframe_flag, 0); --#endif -- } else { -- vb2_buffer_done(vb, VB2_BUF_STATE_DONE); -- } --} -- --void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -- struct ipu7_insys_resp *info) --{ -- struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq; -- u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]); -- struct ipu7_isys *isys = stream->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- struct ipu7_isys_buffer *ib; -- struct vb2_buffer *vb; -- unsigned long flags; -- bool first = true; -- struct vb2_v4l2_buffer *buf; -- -- dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n", -- ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr, -- info->frame_id); -- -- spin_lock_irqsave(&aq->lock, flags); -- if (list_empty(&aq->active)) { -- spin_unlock_irqrestore(&aq->lock, flags); -- dev_err(dev, "active queue empty\n"); -- return; -- } -- -- list_for_each_entry_reverse(ib, &aq->active, head) { -- struct ipu7_isys_video_buffer *ivb; -- struct vb2_v4l2_buffer *vvb; -- dma_addr_t addr; -- -- vb = ipu7_isys_buffer_to_vb2_buffer(ib); -- vvb = to_vb2_v4l2_buffer(vb); -- ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb); -- addr = ivb->dma_addr; -- -- if (info->pin.addr != addr) { -- if (first) -- dev_err(dev, "Unexpected buffer address %pad\n", -- &addr); -- -- first = false; -- continue; -- } -- -- dev_dbg(dev, "buffer: found buffer %pad\n", &addr); -- -- buf = to_vb2_v4l2_buffer(vb); -- buf->field = V4L2_FIELD_NONE; -- -- list_del(&ib->head); -- spin_unlock_irqrestore(&aq->lock, flags); -- -- ipu7_isys_buf_calc_sequence_time(ib, time); -- -- ipu7_isys_queue_buf_done(ib); -- -- return; -- } -- -- dev_err(dev, "Failed to find a matching video buffer\n"); -- -- spin_unlock_irqrestore(&aq->lock, flags); --} -- --static const struct vb2_ops ipu7_isys_queue_ops = { -- .queue_setup = ipu7_isys_queue_setup, -- .buf_init = ipu7_isys_buf_init, -- .buf_prepare = ipu7_isys_buf_prepare, -- .buf_cleanup = ipu7_isys_buf_cleanup, -- .start_streaming = start_streaming, -- .stop_streaming = stop_streaming, -- .buf_queue = buf_queue, --}; -- --int ipu7_isys_queue_init(struct ipu7_isys_queue *aq) --{ -- struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys; -- struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -- struct ipu7_bus_device *adev = isys->adev; -- int ret; -- -- if (!aq->vbq.io_modes) -- aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; -- -- aq->vbq.drv_priv = isys; -- aq->vbq.ops = &ipu7_isys_queue_ops; -- aq->vbq.lock = &av->mutex; -- aq->vbq.mem_ops = &vb2_dma_sg_memops; -- aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -- aq->vbq.min_queued_buffers = 1; -- aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -- -- ret = vb2_queue_init(&aq->vbq); -- if (ret) -- return ret; -- -- aq->dev = &adev->auxdev.dev; -- aq->vbq.dev = &adev->isp->pdev->dev; -- spin_lock_init(&aq->lock); -- INIT_LIST_HEAD(&aq->active); -- INIT_LIST_HEAD(&aq->incoming); -- -- return 0; --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -deleted file mode 100644 -index 5a909c3a78d2..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -+++ /dev/null -@@ -1,75 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_QUEUE_H --#define IPU7_ISYS_QUEUE_H -- --#include --#include --#include --#include -- --#include -- --struct device; --struct ipu7_isys_stream; --struct ipu7_insys_resp; --struct ipu7_insys_buffset; -- --struct ipu7_isys_queue { -- struct vb2_queue vbq; -- struct list_head node; -- struct device *dev; -- spinlock_t lock; -- struct list_head active; -- struct list_head incoming; -- unsigned int fw_output; --}; -- --struct ipu7_isys_buffer { -- struct list_head head; -- atomic_t str2mmio_flag; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- atomic_t skipframe_flag; --#endif --}; -- --struct ipu7_isys_video_buffer { -- struct vb2_v4l2_buffer vb_v4l2; -- struct ipu7_isys_buffer ib; -- dma_addr_t dma_addr; --}; -- --#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) --#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) --#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) -- --struct ipu7_isys_buffer_list { -- struct list_head head; -- unsigned int nbufs; --}; -- --#define vb2_queue_to_isys_queue(__vb2) \ -- container_of(__vb2, struct ipu7_isys_queue, vbq) -- --#define ipu7_isys_to_isys_video_buffer(__ib) \ -- container_of(__ib, struct ipu7_isys_video_buffer, ib) -- --#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb) \ -- container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2) -- --#define ipu7_isys_buffer_to_vb2_buffer(__ib) \ -- (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) -- --void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -- unsigned long op_flags, -- enum vb2_buffer_state state); --void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -- struct ipu7_isys_stream *stream, -- struct ipu7_isys_buffer_list *bl); --void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -- struct ipu7_insys_resp *info); --int ipu7_isys_queue_init(struct ipu7_isys_queue *aq); --#endif /* IPU7_ISYS_QUEUE_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -deleted file mode 100644 -index 98b6ef6a2f21..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -+++ /dev/null -@@ -1,348 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include -- --#include --#include --#include --#include -- --#include -- --#include "ipu7-bus.h" --#include "ipu7-isys.h" --#include "ipu7-isys-subdev.h" -- --unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) --{ -- switch (code) { -- case MEDIA_BUS_FMT_RGB565_1X16: -- return MIPI_CSI2_DT_RGB565; -- case MEDIA_BUS_FMT_RGB888_1X24: -- return MIPI_CSI2_DT_RGB888; -- case MEDIA_BUS_FMT_YUYV10_1X20: -- return MIPI_CSI2_DT_YUV422_10B; -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- return MIPI_CSI2_DT_YUV422_8B; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- return MIPI_CSI2_DT_RAW12; -- case MEDIA_BUS_FMT_Y10_1X10: -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- return MIPI_CSI2_DT_RAW10; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- return MIPI_CSI2_DT_RAW8; -- default: -- WARN_ON(1); -- return 0xff; -- } --} -- --bool ipu7_isys_is_bayer_format(u32 code) --{ -- switch (ipu7_isys_mbus_code_to_mipi(code)) { -- case MIPI_CSI2_DT_RAW8: -- case MIPI_CSI2_DT_RAW10: -- case MIPI_CSI2_DT_RAW12: -- case MIPI_CSI2_DT_RAW14: -- case MIPI_CSI2_DT_RAW16: -- case MIPI_CSI2_DT_RAW20: -- case MIPI_CSI2_DT_RAW24: -- case MIPI_CSI2_DT_RAW28: -- return true; -- default: -- return false; -- } --} -- --u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y) --{ -- static const u32 code_map[] = { -- MEDIA_BUS_FMT_SRGGB8_1X8, -- MEDIA_BUS_FMT_SGRBG8_1X8, -- MEDIA_BUS_FMT_SGBRG8_1X8, -- MEDIA_BUS_FMT_SBGGR8_1X8, -- MEDIA_BUS_FMT_SRGGB10_1X10, -- MEDIA_BUS_FMT_SGRBG10_1X10, -- MEDIA_BUS_FMT_SGBRG10_1X10, -- MEDIA_BUS_FMT_SBGGR10_1X10, -- MEDIA_BUS_FMT_SRGGB12_1X12, -- MEDIA_BUS_FMT_SGRBG12_1X12, -- MEDIA_BUS_FMT_SGBRG12_1X12, -- MEDIA_BUS_FMT_SBGGR12_1X12, -- }; -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(code_map); i++) -- if (code_map[i] == code) -- break; -- -- if (WARN_ON(i == ARRAY_SIZE(code_map))) -- return code; -- -- return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))]; --} -- --int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_format *format) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- u32 code = asd->supported_codes[0]; -- struct v4l2_mbus_framefmt *fmt; -- u32 other_pad, other_stream; -- struct v4l2_rect *crop; -- unsigned int i; -- int ret; -- -- /* No transcoding, source and sink formats must match. */ -- if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && -- sd->entity.num_pads > 1) -- return v4l2_subdev_get_fmt(sd, state, format); -- -- format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH, -- IPU_ISYS_MAX_WIDTH); -- format->format.height = clamp(format->format.height, -- IPU_ISYS_MIN_HEIGHT, -- IPU_ISYS_MAX_HEIGHT); -- -- for (i = 0; asd->supported_codes[i]; i++) { -- if (asd->supported_codes[i] == format->format.code) { -- code = asd->supported_codes[i]; -- break; -- } -- } -- format->format.code = code; -- format->format.field = V4L2_FIELD_NONE; -- -- /* Store the format and propagate it to the source pad. */ -- fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); -- if (!fmt) -- return -EINVAL; -- -- *fmt = format->format; -- -- if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) -- return 0; -- -- /* propagate format to following source pad */ -- fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, -- format->stream); -- if (!fmt) -- return -EINVAL; -- -- *fmt = format->format; -- -- ret = v4l2_subdev_routing_find_opposite_end(&state->routing, -- format->pad, -- format->stream, -- &other_pad, -- &other_stream); -- if (ret) -- return -EINVAL; -- -- crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); -- /* reset crop */ -- crop->left = 0; -- crop->top = 0; -- crop->width = fmt->width; -- crop->height = fmt->height; -- -- return 0; --} -- --int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_mbus_code_enum *code) --{ -- struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -- const u32 *supported_codes = asd->supported_codes; -- u32 index; -- -- for (index = 0; supported_codes[index]; index++) { -- if (index == code->index) { -- code->code = supported_codes[index]; -- return 0; -- } -- } -- -- return -EINVAL; --} -- --static int subdev_set_routing(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_krouting *routing) --{ -- static const struct v4l2_mbus_framefmt fmt = { -- .width = 4096, -- .height = 3072, -- .code = MEDIA_BUS_FMT_SGRBG10_1X10, -- .field = V4L2_FIELD_NONE, -- }; -- int ret; -- -- ret = v4l2_subdev_routing_validate(sd, routing, -- V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); -- if (ret) -- return ret; -- -- return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); --} -- --int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -- struct v4l2_mbus_framefmt *format) --{ -- struct v4l2_subdev_state *state; -- struct v4l2_mbus_framefmt *fmt; -- -- if (!sd || !format) -- return -EINVAL; -- -- state = v4l2_subdev_lock_and_get_active_state(sd); -- fmt = v4l2_subdev_state_get_format(state, pad, stream); -- if (fmt) -- *format = *fmt; -- v4l2_subdev_unlock_state(state); -- -- return fmt ? 0 : -EINVAL; --} -- --u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) --{ -- struct v4l2_subdev_state *state; -- struct v4l2_subdev_route *routes; -- u32 source_stream = 0; -- unsigned int i; -- -- state = v4l2_subdev_lock_and_get_active_state(sd); -- if (!state) -- return 0; -- -- routes = state->routing.routes; -- for (i = 0; i < state->routing.num_routes; i++) { -- if (routes[i].source_pad == pad) { -- source_stream = routes[i].source_stream; -- break; -- } -- } -- -- v4l2_subdev_unlock_state(state); -- -- return source_stream; --} -- --static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state) --{ -- struct v4l2_subdev_route route = { -- .sink_pad = 0, -- .sink_stream = 0, -- .source_pad = 1, -- .source_stream = 0, -- .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -- }; -- struct v4l2_subdev_krouting routing = { -- .num_routes = 1, -- .routes = &route, -- }; -- -- return subdev_set_routing(sd, state, &routing); --} -- --int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- enum v4l2_subdev_format_whence which, -- struct v4l2_subdev_krouting *routing) --{ -- return subdev_set_routing(sd, state, routing); --} -- --static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { -- .init_state = ipu7_isys_subdev_init_state, --}; -- --int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -- const struct v4l2_subdev_ops *ops, -- unsigned int nr_ctrls, -- unsigned int num_sink_pads, -- unsigned int num_source_pads) --{ -- unsigned int num_pads = num_sink_pads + num_source_pads; -- unsigned int i; -- int ret; -- -- v4l2_subdev_init(&asd->sd, ops); -- -- asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -- V4L2_SUBDEV_FL_HAS_EVENTS | -- V4L2_SUBDEV_FL_STREAMS; -- asd->sd.owner = THIS_MODULE; -- asd->sd.dev = &asd->isys->adev->auxdev.dev; -- asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -- asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; -- -- asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, -- sizeof(*asd->pad), GFP_KERNEL); -- if (!asd->pad) -- return -ENOMEM; -- -- for (i = 0; i < num_sink_pads; i++) -- asd->pad[i].flags = MEDIA_PAD_FL_SINK | -- MEDIA_PAD_FL_MUST_CONNECT; -- -- for (i = num_sink_pads; i < num_pads; i++) -- asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; -- -- ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); -- if (ret) { -- pr_err("isys subdev init failed %d.\n", ret); -- return ret; -- } -- -- if (asd->ctrl_init) { -- ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); -- if (ret) -- goto out_media_entity_cleanup; -- -- asd->ctrl_init(&asd->sd); -- if (asd->ctrl_handler.error) { -- ret = asd->ctrl_handler.error; -- goto out_v4l2_ctrl_handler_free; -- } -- -- asd->sd.ctrl_handler = &asd->ctrl_handler; -- } -- -- asd->source = -1; -- -- return 0; -- --out_v4l2_ctrl_handler_free: -- v4l2_ctrl_handler_free(&asd->ctrl_handler); -- --out_media_entity_cleanup: -- media_entity_cleanup(&asd->sd.entity); -- -- return ret; --} -- --void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) --{ -- media_entity_cleanup(&asd->sd.entity); -- v4l2_ctrl_handler_free(&asd->ctrl_handler); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -deleted file mode 100644 -index aeefbb515807..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -+++ /dev/null -@@ -1,56 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_SUBDEV_H --#define IPU7_ISYS_SUBDEV_H -- --#include -- --#include --#include --#include -- --struct ipu7_isys; -- --struct ipu7_isys_subdev { -- struct v4l2_subdev sd; -- struct ipu7_isys *isys; -- u32 const *supported_codes; -- struct media_pad *pad; -- struct v4l2_ctrl_handler ctrl_handler; -- void (*ctrl_init)(struct v4l2_subdev *sd); -- int source; /* SSI stream source; -1 if unset */ --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- bool is_tpg; --#endif --}; -- --#define to_ipu7_isys_subdev(__sd) \ -- container_of(__sd, struct ipu7_isys_subdev, sd) --unsigned int ipu7_isys_mbus_code_to_mipi(u32 code); --bool ipu7_isys_is_bayer_format(u32 code); --u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y); -- --int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_format *format); --int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- struct v4l2_subdev_mbus_code_enum -- *code); --u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); --int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -- struct v4l2_mbus_framefmt *format); --int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state, -- enum v4l2_subdev_format_whence which, -- struct v4l2_subdev_krouting *routing); --int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -- const struct v4l2_subdev_ops *ops, -- unsigned int nr_ctrls, -- unsigned int num_sink_pads, -- unsigned int num_source_pads); --void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd); --#endif /* IPU7_ISYS_SUBDEV_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -deleted file mode 100644 -index 35b6298e4f8c..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -+++ /dev/null -@@ -1,693 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include -- --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-isys.h" --#include "ipu7-isys-subdev.h" --#include "ipu7-isys-tpg.h" --#include "ipu7-isys-video.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-platform-regs.h" -- --static const u32 tpg_supported_codes[] = { -- MEDIA_BUS_FMT_SBGGR8_1X8, -- MEDIA_BUS_FMT_SGBRG8_1X8, -- MEDIA_BUS_FMT_SGRBG8_1X8, -- MEDIA_BUS_FMT_SRGGB8_1X8, -- MEDIA_BUS_FMT_SBGGR10_1X10, -- MEDIA_BUS_FMT_SGBRG10_1X10, -- MEDIA_BUS_FMT_SGRBG10_1X10, -- MEDIA_BUS_FMT_SRGGB10_1X10, -- MEDIA_BUS_FMT_SBGGR12_1X12, -- MEDIA_BUS_FMT_SGBRG12_1X12, -- MEDIA_BUS_FMT_SGRBG12_1X12, -- MEDIA_BUS_FMT_SRGGB12_1X12, -- 0, --}; -- --#define IPU_ISYS_FREQ 533000000UL -- --static u32 isys_mbus_code_to_bpp(u32 code) --{ -- switch (code) { -- case MEDIA_BUS_FMT_RGB888_1X24: -- return 24; -- case MEDIA_BUS_FMT_YUYV10_1X20: -- return 20; -- case MEDIA_BUS_FMT_Y10_1X10: -- case MEDIA_BUS_FMT_RGB565_1X16: -- case MEDIA_BUS_FMT_UYVY8_1X16: -- case MEDIA_BUS_FMT_YUYV8_1X16: -- return 16; -- case MEDIA_BUS_FMT_SBGGR12_1X12: -- case MEDIA_BUS_FMT_SGBRG12_1X12: -- case MEDIA_BUS_FMT_SGRBG12_1X12: -- case MEDIA_BUS_FMT_SRGGB12_1X12: -- return 12; -- case MEDIA_BUS_FMT_SBGGR10_1X10: -- case MEDIA_BUS_FMT_SGBRG10_1X10: -- case MEDIA_BUS_FMT_SGRBG10_1X10: -- case MEDIA_BUS_FMT_SRGGB10_1X10: -- return 10; -- case MEDIA_BUS_FMT_SBGGR8_1X8: -- case MEDIA_BUS_FMT_SGBRG8_1X8: -- case MEDIA_BUS_FMT_SGRBG8_1X8: -- case MEDIA_BUS_FMT_SRGGB8_1X8: -- return 8; -- default: -- WARN_ON(1); -- return 0; -- } --} -- --static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { -- .s_stream = tpg_set_stream, --}; -- --static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) --{ -- struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, -- struct -- ipu7_isys_subdev, -- ctrl_handler), -- struct ipu7_isys_tpg, asd); -- switch (ctrl->id) { -- case V4L2_CID_HBLANK: -- writel(ctrl->val, tpg->base + MGC_MG_HBLANK); -- break; -- case V4L2_CID_VBLANK: -- writel(ctrl->val, tpg->base + MGC_MG_VBLANK); -- break; -- case V4L2_CID_TEST_PATTERN: -- writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); -- break; -- } -- -- return 0; --} -- --static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { -- .s_ctrl = ipu7_isys_tpg_s_ctrl, --}; -- --static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) --{ -- return MGC_PPC * IPU_ISYS_FREQ / bpp; --} -- --static const char *const tpg_mode_items[] = { -- "Ramp", -- "Checkerboard", -- "Monochrome per frame", -- "Color palette", --}; -- --static struct v4l2_ctrl_config tpg_mode = { -- .ops = &ipu7_isys_tpg_ctrl_ops, -- .id = V4L2_CID_TEST_PATTERN, -- .name = "Test Pattern", -- .type = V4L2_CTRL_TYPE_MENU, -- .min = TPG_MODE_RAMP, -- .max = ARRAY_SIZE(tpg_mode_items) - 1, -- .def = TPG_MODE_COLOR_PALETTE, -- .menu_skip_mask = 0x2, -- .qmenu = tpg_mode_items, --}; -- --static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) --{ -- struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -- int hblank; -- u64 default_pixel_rate; -- -- hblank = 1024; -- -- tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -- &ipu7_isys_tpg_ctrl_ops, -- V4L2_CID_HBLANK, 8, 65535, 1, hblank); -- -- tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -- &ipu7_isys_tpg_ctrl_ops, -- V4L2_CID_VBLANK, 8, 65535, 1, 1024); -- -- default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); -- tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -- &ipu7_isys_tpg_ctrl_ops, -- V4L2_CID_PIXEL_RATE, -- default_pixel_rate, -- default_pixel_rate, -- 1, default_pixel_rate); -- if (tpg->pixel_rate) { -- tpg->pixel_rate->cur.val = default_pixel_rate; -- tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; -- } -- -- v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); --} -- --static int tpg_sd_init_cfg(struct v4l2_subdev *sd, -- struct v4l2_subdev_state *state) --{ -- struct v4l2_subdev_route routes[] = { -- { -- .source_pad = 0, -- .source_stream = 0, -- .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -- } -- }; -- -- struct v4l2_subdev_krouting routing = { -- .num_routes = 1, -- .routes = routes, -- }; -- -- static const struct v4l2_mbus_framefmt format = { -- .width = 1920, -- .height = 1080, -- .code = MEDIA_BUS_FMT_SBGGR10_1X10, -- .field = V4L2_FIELD_NONE, -- }; -- -- return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); --} -- --static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { -- .init_state = tpg_sd_init_cfg, --}; -- --static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { -- .get_fmt = v4l2_subdev_get_fmt, -- .set_fmt = ipu7_isys_subdev_set_fmt, -- .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, --}; -- --static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -- struct v4l2_event_subscription *sub) --{ -- switch (sub->type) { -- case V4L2_EVENT_FRAME_SYNC: -- return v4l2_event_subscribe(fh, sub, 10, NULL); -- case V4L2_EVENT_CTRL: -- return v4l2_ctrl_subscribe_event(fh, sub); -- default: -- return -EINVAL; -- } --}; -- --/* V4L2 subdev core operations */ --static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { -- .subscribe_event = subscribe_event, -- .unsubscribe_event = v4l2_event_subdev_unsubscribe, --}; -- --static const struct v4l2_subdev_ops tpg_sd_ops = { -- .core = &tpg_sd_core_ops, -- .video = &tpg_sd_video_ops, -- .pad = &tpg_sd_pad_ops, --}; -- --static struct media_entity_operations tpg_entity_ops = { -- .link_validate = v4l2_subdev_link_validate, --}; -- --void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -- struct video_device *vdev = tpg->asd.sd.devnode; -- struct v4l2_event ev = { -- .type = V4L2_EVENT_FRAME_SYNC, -- }; -- -- ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -- -- v4l2_event_queue(vdev, &ev); -- -- dev_dbg(&stream->isys->adev->auxdev.dev, -- "sof_event::tpg-%i sequence: %i\n", tpg->index, -- ev.u.frame_sync.frame_sequence); --} -- --void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) --{ -- struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -- u32 frame_sequence = atomic_read(&stream->sequence); -- -- dev_dbg(&stream->isys->adev->auxdev.dev, -- "eof_event::tpg-%i sequence: %i\n", -- tpg->index, frame_sequence); --} -- --#define DEFAULT_VC_ID 0 --static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) --{ -- return false; --} -- --static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, -- void __iomem *mg_base) --{ -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- -- dev_dbg(dev, "---------MGC REG DUMP START----------"); -- -- dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", -- MGC_MG_CSI_ADAPT_LAYER_TYPE, -- readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); -- dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", -- MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); -- dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", -- MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); -- dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", -- MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); -- dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", -- MGC_MG_MULTI_DTYPES_MODE, -- readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); -- dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", -- MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); -- dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", -- MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); -- dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", -- MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); -- dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", -- MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); -- dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", -- MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); -- dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", -- readl(mg_base + MGC_MG_TPG_R0), -- readl(mg_base + MGC_MG_TPG_G0), -- readl(mg_base + MGC_MG_TPG_B0)); -- dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", -- readl(mg_base + MGC_MG_TPG_R1), -- readl(mg_base + MGC_MG_TPG_G1), -- readl(mg_base + MGC_MG_TPG_B1)); -- dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", -- MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); -- dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", -- MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); -- dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", -- MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); -- dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", -- MGC_MG_DTO_SPEED_CTRL_EN, -- readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); -- dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", -- MGC_MG_DTO_SPEED_CTRL_INCR_VAL, -- readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); -- dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", -- MGC_MG_FRAME_NUM_STTS, -- readl(mg_base + MGC_MG_FRAME_NUM_STTS)); -- -- dev_dbg(dev, "---------MGC REG DUMP END----------"); --} -- --#define TPG_STOP_TIMEOUT 500000 --static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) --{ -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- int ret; -- unsigned int port; -- u32 status; -- void __iomem *reg; -- void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -- void __iomem *mg_base = tpg->base; -- -- port = 1 << tpg->index; -- -- dev_dbg(dev, "MG%d generated %u frames", tpg->index, -- readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); -- writel(port, mgc_base + MGC_ASYNC_STOP); -- -- dev_dbg(dev, "wait for MG%d stop", tpg->index); -- -- reg = mg_base + MGC_MG_STOPPED_STTS; -- ret = readl_poll_timeout(reg, status, status & 0x1, 200, -- TPG_STOP_TIMEOUT); -- if (ret < 0) { -- dev_err(dev, "mgc stop timeout"); -- return ret; -- } -- -- dev_dbg(dev, "MG%d STOPPED", tpg->index); -- -- return 0; --} -- --#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) --#define TPG_FRAME_RATE 30 --#define TPG_BLANK_RATIO (4 / 3) --static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, -- u32 *hblank_cycles, u32 *vblank_cycles) --{ -- struct v4l2_mbus_framefmt format; -- u32 width, height; -- u32 code; -- u32 bpp; -- u32 bits_per_line; -- u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; -- u64 vblank_us, hblank_us; -- u32 ref_clk; -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- u32 dto_incr_val = 0x100; -- int ret; -- -- ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -- 0, 0, &format); -- if (ret) -- return; -- -- width = format.width; -- height = format.height; -- code = format.code; -- -- bpp = isys_mbus_code_to_bpp(code); -- if (!bpp) -- return; -- -- dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); -- bits_per_line = width * bpp * TPG_BLANK_RATIO; -- -- cycles = div_u64(bits_per_line, 64); -- dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, -- bits_per_line, cycles); -- -- do { -- dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, -- dto_incr_val); -- rate = div_u64(1 << 16, dto_incr_val); -- ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); -- dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, -- ns_per_cycle); -- -- line_time_ns = cycles * ns_per_cycle; -- frame_time_us = line_time_ns * height / 1000; -- dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", -- tpg->index, line_time_ns, frame_time_us); -- -- if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) -- break; -- -- /* dto incr val step 0x100 */ -- dto_incr_val += 0x100; -- } while (dto_incr_val < (1 << 16)); -- -- if (dto_incr_val >= (1 << 16)) { -- dev_warn(dev, "No DTO_INCR_VAL found\n"); -- hblank_us = 10; /* 10us */ -- vblank_us = 10000; /* 10ms */ -- dto_incr_val = 0x1000; -- } else { -- hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; -- vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; -- } -- -- dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", -- hblank_us, vblank_us, dto_incr_val); -- -- ref_clk = tpg->isys->adev->isp->buttress.ref_clk; -- -- *dto = dto_incr_val; -- *hblank_cycles = hblank_us * ref_clk / 10; -- *vblank_cycles = vblank_us * ref_clk / 10; -- dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", -- *hblank_cycles, *vblank_cycles); --} -- --static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) --{ -- struct v4l2_mbus_framefmt format; -- u32 port_map; -- u32 csi_port; -- u32 code, bpp; -- u32 width, height; -- u32 dto, hblank, vblank; -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -- void __iomem *mg_base = tpg->base; -- int ret; -- -- ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -- 0, 0, &format); -- if (ret) -- return ret; -- -- width = format.width; -- height = format.height; -- code = format.code; -- dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", -- tpg->index, code, width, height); -- bpp = isys_mbus_code_to_bpp(code); -- if (!bpp) -- return -EINVAL; -- -- csi_port = tpg->index; -- if (csi_port >= 4) -- dev_err(dev, "invalid tpg index %u\n", tpg->index); -- -- dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", -- DEFAULT_VC_ID, csi_port); -- -- /* config port map -- * TODO: add VC support and TPG with multiple -- * source pads. Currently, for simplicity, only map 1 mg to 1 csi port -- */ -- port_map = 1 << tpg->index; -- writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); -- -- /* configure adapt layer type */ -- writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); -- -- /* configure MGC mode -- * 0 - Disable MGC -- * 1 - Enable PRBS -- * 2 - Enable TPG -- * 3 - Reserved [Write phase: SW/FW debug] -- */ -- writel(2, mg_base + MGC_MG_MODE); -- -- /* config mg init counter */ -- writel(0, mg_base + MGC_MG_INIT_COUNTER); -- -- /* -- * configure virtual channel -- * TODO: VC support if need -- * currently each MGC just uses 1 virtual channel -- */ -- writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); -- -- /* -- * configure data type and multi dtypes mode -- * TODO: it needs to add the metedata flow. -- */ -- if (is_metadata_enabled(tpg)) { -- writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -- mg_base + MGC_MG_MIPI_DTYPES); -- writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); -- } else { -- writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -- mg_base + MGC_MG_MIPI_DTYPES); -- writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); -- } -- -- /* -- * configure frame information -- */ -- writel(0, mg_base + MGC_MG_NOF_FRAMES); -- writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); -- -- tpg_get_timing(tpg, &dto, &hblank, &vblank); -- writel(hblank, mg_base + MGC_MG_HBLANK); -- writel(vblank, mg_base + MGC_MG_VBLANK); -- -- /* -- * configure tpg mode, colors, mask, tile dimension -- * Mode was set by user configuration -- * 0 - Ramp mode -- * 1 - Checkerboard -- * 2 - Monochrome per frame -- * 3 - Color palette -- */ -- writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); -- -- /* red and green for checkerboard, n/a for other modes */ -- writel(58, mg_base + MGC_MG_TPG_R0); -- writel(122, mg_base + MGC_MG_TPG_G0); -- writel(46, mg_base + MGC_MG_TPG_B0); -- writel(123, mg_base + MGC_MG_TPG_R1); -- writel(85, mg_base + MGC_MG_TPG_G1); -- writel(67, mg_base + MGC_MG_TPG_B1); -- -- writel(0x0, mg_base + MGC_MG_TPG_FACTORS); -- -- /* hor_mask [15:0] ver_mask [31:16] */ -- writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); -- /* xy_mask [11:0] */ -- writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); -- writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), -- mg_base + MGC_MG_TPG_TILE_DIM); -- -- writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); -- writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); -- -- /* disable err_injection */ -- writel(0, mg_base + MGC_MG_ERR_INJECT); -- writel(0, mg_base + MGC_MG_ERR_LOCATION); -- -- ipu7_mipigen_regdump(tpg, mg_base); -- -- dev_dbg(dev, "starting MG%d streaming...\n", csi_port); -- -- /* kick and start */ -- writel(port_map, mgc_base + MGC_KICK); -- -- return 0; --} -- --static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) --{ -- struct ipu7_isys_csi2 *csi2; -- u32 offset; -- struct ipu7_isys *isys = tpg->isys; -- -- csi2 = &isys->csi2[tpg->index]; -- offset = IS_IO_GPREGS_BASE; -- -- /* MGC is in use by SW or not */ -- if (enable) -- writel(1, csi2->base + offset + MGC_CLK_GATE); -- else -- writel(0, csi2->base + offset + MGC_CLK_GATE); --} -- --static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) --{ -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- struct ipu7_isys *isys = tpg->isys; -- struct ipu7_isys_csi2 *csi2; -- u32 port, offset, val; -- void __iomem *isys_base = isys->pdata->base; -- -- port = tpg->index; -- csi2 = &isys->csi2[port]; -- -- offset = IS_IO_GPREGS_BASE; -- val = readl(isys_base + offset + CSI_PORT_CLK_GATE); -- dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); -- -- if (!enable) { -- writel(~(1 << port) & val, -- isys_base + offset + CSI_PORT_CLK_GATE); -- return; -- } -- -- /* set csi port is using by SW */ -- writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); -- /* input is coming from MGC */ -- offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -- writel(CSI_MIPIGEN_INPUT, -- csi2->base + offset + CSI2_ADPL_INPUT_MODE); --} -- --/* TODO: add the processing of vc */ --int tpg_set_stream(struct v4l2_subdev *sd, int enable) --{ -- struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -- struct ipu7_isys_stream *stream = tpg->av->stream; -- struct device *dev = &tpg->isys->adev->auxdev.dev; -- int ret; -- -- if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { -- dev_err(dev, "invalid MGC index %d\n", tpg->index); -- return -EINVAL; -- } -- -- if (!enable) { -- /* Stop MGC */ -- stream->asd->is_tpg = false; -- stream->asd = NULL; -- ipu7_isys_mgc_csi2_s_stream(tpg, enable); -- ret = tpg_stop_stream(tpg); -- ipu7_isys_ungate_mgc(tpg, enable); -- -- return ret; -- } -- -- stream->asd = &tpg->asd; -- /* ungate the MGC clock to program */ -- ipu7_isys_ungate_mgc(tpg, enable); -- /* Start MGC */ -- ret = tpg_start_stream(tpg); -- v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); -- ipu7_isys_mgc_csi2_s_stream(tpg, enable); -- -- return ret; --} -- --void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) --{ -- v4l2_device_unregister_subdev(&tpg->asd.sd); -- ipu7_isys_subdev_cleanup(&tpg->asd); --} -- --int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, -- void __iomem *base, void __iomem *sel, -- unsigned int index) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- int ret; -- -- tpg->isys = isys; -- tpg->base = base; -- tpg->sel = sel; -- tpg->index = index; -- -- tpg->asd.sd.entity.ops = &tpg_entity_ops; -- tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; -- tpg->asd.isys = isys; -- -- ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, -- NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); -- if (ret) -- return ret; -- -- tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; -- tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -- tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -- -- tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; -- tpg->asd.supported_codes = tpg_supported_codes; -- tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; -- -- snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), -- IPU_ISYS_ENTITY_PREFIX " TPG %u", index); -- v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); -- -- ret = v4l2_subdev_init_finalize(&tpg->asd.sd); -- if (ret) { -- dev_err(dev, "failed to finalize subdev (%d)\n", ret); -- goto fail; -- } -- -- ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); -- if (ret) { -- dev_info(dev, "can't register v4l2 subdev\n"); -- goto fail; -- } -- -- return 0; -- --fail: -- ipu7_isys_tpg_cleanup(tpg); -- -- return ret; --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -deleted file mode 100644 -index e2542a6472e3..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -+++ /dev/null -@@ -1,70 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_TPG_H --#define IPU7_ISYS_TPG_H -- --#include --#include --#include -- --#include "ipu7-isys-subdev.h" --#include "ipu7-isys-video.h" --#include "ipu7-isys-queue.h" -- --struct ipu7_isys_tpg_pdata; --struct ipu7_isys; -- --#define TPG_PAD_SOURCE 0 --#define NR_OF_TPG_PADS 1 --#define NR_OF_TPG_SOURCE_PADS 1 --#define NR_OF_TPG_SINK_PADS 0 --#define NR_OF_TPG_STREAMS 1 -- --enum isys_tpg_mode { -- TPG_MODE_RAMP = 0, -- TPG_MODE_CHECKERBOARD = 1, -- TPG_MODE_MONO = 2, -- TPG_MODE_COLOR_PALETTE = 3, --}; -- --/* -- * struct ipu7_isys_tpg -- * -- * @nlanes: number of lanes in the receiver -- */ --struct ipu7_isys_tpg { -- struct ipu7_isys_subdev asd; -- struct ipu7_isys_tpg_pdata *pdata; -- struct ipu7_isys *isys; -- struct ipu7_isys_video *av; -- -- /* MG base not MGC */ -- void __iomem *base; -- void __iomem *sel; -- unsigned int index; -- -- struct v4l2_ctrl *hblank; -- struct v4l2_ctrl *vblank; -- struct v4l2_ctrl *pixel_rate; --}; -- --#define ipu7_isys_subdev_to_tpg(__sd) \ -- container_of(__sd, struct ipu7_isys_tpg, asd) -- --#define to_ipu7_isys_tpg(sd) \ -- container_of(to_ipu7_isys_subdev(sd), \ -- struct ipu7_isys_tpg, asd) -- --void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); --void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); --int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, -- struct ipu7_isys *isys, -- void __iomem *base, void __iomem *sel, -- unsigned int index); --void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); --int tpg_set_stream(struct v4l2_subdev *sd, int enable); -- --#endif /* IPU7_ISYS_TPG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -deleted file mode 100644 -index 12fdf556c656..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -+++ /dev/null -@@ -1,1311 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#include --#endif -- --#include --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-fw-isys.h" --#include "ipu7-isys.h" --#include "ipu7-isys-video.h" --#include "ipu7-platform-regs.h" -- --const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { -- {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW16}, -- {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, -- IPU_INSYS_FRAME_FORMAT_RAW8}, -- {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -- IPU_INSYS_FRAME_FORMAT_RAW12}, -- {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -- IPU_INSYS_FRAME_FORMAT_RAW10}, -- {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -- IPU_INSYS_FRAME_FORMAT_UYVY}, -- {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -- IPU_INSYS_FRAME_FORMAT_YUYV}, -- {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -- IPU_INSYS_FRAME_FORMAT_RGB565}, -- {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -- IPU_INSYS_FRAME_FORMAT_RGBA888}, --}; -- --static int video_open(struct file *file) --{ --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- struct ipu7_isys_video *av = video_drvdata(file); -- struct ipu7_isys *isys = av->isys; -- struct ipu7_bus_device *adev = isys->adev; -- -- mutex_lock(&isys->reset_mutex); -- if (isys->need_reset) { -- mutex_unlock(&isys->reset_mutex); -- dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -- return -EIO; -- } -- mutex_unlock(&isys->reset_mutex); -- --#endif -- return v4l2_fh_open(file); --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --static int video_release(struct file *file) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- dev_dbg(&av->isys->adev->auxdev.dev, -- "release: %s: enter\n", av->vdev.name); -- mutex_lock(&av->isys->reset_mutex); -- while (av->isys->state & RESET_STATE_IN_RESET) { -- mutex_unlock(&av->isys->reset_mutex); -- dev_dbg(&av->isys->adev->auxdev.dev, -- "release: %s: wait for reset\n", av->vdev.name); -- usleep_range(10000, 11000); -- mutex_lock(&av->isys->reset_mutex); -- } -- mutex_unlock(&av->isys->reset_mutex); -- return vb2_fop_release(file); --} -- --#endif --const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) --{ -- unsigned int i; -- -- for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -- const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i]; -- -- if (pfmt->pixelformat == pixelformat) -- return pfmt; -- } -- -- return &ipu7_isys_pfmts[0]; --} -- --static int ipu7_isys_vidioc_querycap(struct file *file, void *fh, -- struct v4l2_capability *cap) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); -- strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); -- -- return 0; --} -- --static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, -- struct v4l2_fmtdesc *f) --{ -- unsigned int i, num_found; -- -- for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -- continue; -- -- if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code) -- continue; -- -- if (num_found < f->index) { -- num_found++; -- continue; -- } -- -- f->flags = 0; -- f->pixelformat = ipu7_isys_pfmts[i].pixelformat; -- -- return 0; -- } -- -- return -EINVAL; --} -- --static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, -- struct v4l2_frmsizeenum *fsize) --{ -- unsigned int i; -- -- if (fsize->index > 0) -- return -EINVAL; -- -- for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -- if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) -- continue; -- -- fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; -- fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH; -- fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH; -- fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT; -- fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT; -- fsize->stepwise.step_width = 2; -- fsize->stepwise.step_height = 2; -- -- return 0; -- } -- -- return -EINVAL; --} -- --static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, -- struct v4l2_format *f) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- f->fmt.pix = av->pix_fmt; -- -- return 0; --} -- --static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type, -- u32 *format, u32 *width, u32 *height, -- u32 *bytesperline, u32 *sizeimage) --{ -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(*format); -- -- *format = pfmt->pixelformat; -- *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH); -- *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); -- -- if (pfmt->bpp != pfmt->bpp_packed) -- *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); -- else -- *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); -- -- *bytesperline = ALIGN(*bytesperline, 64U); -- -- /* -- * (height + 1) * bytesperline due to a hardware issue: the DMA unit -- * is a power of two, and a line should be transferred as few units -- * as possible. The result is that up to line length more data than -- * the image size may be transferred to memory after the image. -- * Another limitation is the GDA allocation unit size. For low -- * resolution it gives a bigger number. Use larger one to avoid -- * memory corruption. -- */ -- *sizeimage = *bytesperline * *height + -- max(*bytesperline, -- av->isys->pdata->ipdata->isys_dma_overshoot); --} -- --static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, -- struct v4l2_format *f) --{ -- ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, -- &f->fmt.pix.width, &f->fmt.pix.height, -- &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); -- -- f->fmt.pix.field = V4L2_FIELD_NONE; -- f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; -- f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -- f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; -- f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; --} -- --static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, -- struct v4l2_format *f) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- if (vb2_is_busy(&av->aq.vbq)) -- return -EBUSY; -- -- __ipu_isys_vidioc_try_fmt_vid_cap(av, f); -- -- return 0; --} -- --static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, -- struct v4l2_format *f) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- -- ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); -- av->pix_fmt = f->fmt.pix; -- -- return 0; --} -- --static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv, -- struct v4l2_requestbuffers *p) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- int ret; -- -- av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); -- av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); -- -- ret = vb2_queue_change_type(&av->aq.vbq, p->type); -- if (ret) -- return ret; -- -- return vb2_ioctl_reqbufs(file, priv, p); --} -- --static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv, -- struct v4l2_create_buffers *p) --{ -- struct ipu7_isys_video *av = video_drvdata(file); -- int ret; -- -- av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); -- av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); -- -- ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); -- if (ret) -- return ret; -- -- return vb2_ioctl_create_bufs(file, priv, p); --} -- --static int link_validate(struct media_link *link) --{ -- struct ipu7_isys_video *av = -- container_of(link->sink, struct ipu7_isys_video, pad); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct v4l2_subdev_state *s_state; -- struct v4l2_mbus_framefmt *s_fmt; -- struct v4l2_subdev *s_sd; -- struct media_pad *s_pad; -- u32 s_stream, code; -- int ret = -EPIPE; -- -- if (!link->source->entity) -- return ret; -- -- s_sd = media_entity_to_v4l2_subdev(link->source->entity); -- s_state = v4l2_subdev_get_unlocked_active_state(s_sd); -- if (!s_state) -- return ret; -- -- dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", -- link->source->entity->name, link->source->index, -- link->sink->entity->name); -- -- s_pad = media_pad_remote_pad_first(&av->pad); -- s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); -- -- v4l2_subdev_lock_state(s_state); -- -- s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); -- if (!s_fmt) { -- dev_err(dev, "failed to get source pad format\n"); -- goto unlock; -- } -- -- code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -- -- if (s_fmt->width != av->pix_fmt.width || -- s_fmt->height != av->pix_fmt.height || s_fmt->code != code) { -- dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", -- s_fmt->width, s_fmt->height, s_fmt->code, -- av->pix_fmt.width, av->pix_fmt.height, code); -- goto unlock; -- } -- -- v4l2_subdev_unlock_state(s_state); -- -- return 0; --unlock: -- v4l2_subdev_unlock_state(s_state); -- -- return ret; --} -- --static void get_stream_opened(struct ipu7_isys_video *av) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&av->isys->streams_lock, flags); -- av->isys->stream_opened++; -- spin_unlock_irqrestore(&av->isys->streams_lock, flags); --} -- --static void put_stream_opened(struct ipu7_isys_video *av) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&av->isys->streams_lock, flags); -- av->isys->stream_opened--; -- spin_unlock_irqrestore(&av->isys->streams_lock, flags); --} -- --static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, -- struct ipu7_insys_stream_cfg *cfg) --{ -- struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); -- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); -- struct ipu7_isys_stream *stream = av->stream; -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- struct ipu7_insys_output_pin *output_pin; -- struct ipu7_insys_input_pin *input_pin; -- int input_pins = cfg->nof_input_pins++; -- struct ipu7_isys_queue *aq = &av->aq; -- struct ipu7_isys *isys = av->isys; -- struct device *dev = &isys->adev->auxdev.dev; -- struct v4l2_mbus_framefmt fmt; -- int output_pins; -- u32 src_stream; -- int ret; -- -- src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index); -- ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, -- &fmt); -- if (ret < 0) { -- dev_err(dev, "can't get stream format (%d)\n", ret); -- return ret; -- } -- -- input_pin = &cfg->input_pins[input_pins]; -- input_pin->input_res.width = fmt.width; -- input_pin->input_res.height = fmt.height; -- input_pin->dt = av->dt; -- input_pin->disable_mipi_unpacking = 0; -- pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) -- input_pin->disable_mipi_unpacking = 1; -- input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE; -- input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME; -- /* if enable polling isys interrupt, the follow values maybe set */ -- input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | -- IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | -- IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; -- -- output_pins = cfg->nof_output_pins++; -- aq->fw_output = output_pins; -- stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready; -- stream->output_pins[output_pins].aq = aq; -- -- output_pin = &cfg->output_pins[output_pins]; -- /* output pin msg link */ -- output_pin->link.buffer_lines = 0; -- output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE; -- output_pin->link.granularity_pointer_update = 0; -- output_pin->link.msg_link_streaming_mode = -- IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF; -- -- output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE; -- output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE; -- output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM; -- output_pin->link.use_sw_managed = 1; -- /* TODO: set the snoop bit for metadata capture */ -- output_pin->link.is_snoop = 0; -- -- /* output pin crop */ -- output_pin->crop.line_top = 0; -- output_pin->crop.line_bottom = 0; --#ifdef IPU8_INSYS_NEW_ABI -- output_pin->crop.column_left = 0; -- output_pin->crop.column_right = 0; --#endif -- -- /* output de-compression */ -- output_pin->dpcm.enable = 0; -- --#ifdef IPU8_INSYS_NEW_ABI -- /* upipe_cfg */ -- output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; -- output_pin->upipe_pin_cfg.plane_offset_1 = 0; -- output_pin->upipe_pin_cfg.plane_offset_2 = 0; -- output_pin->upipe_pin_cfg.single_uob_fifo = 0; -- output_pin->upipe_pin_cfg.shared_uob_fifo = 0; -- output_pin->upipe_enable = 0; -- output_pin->binning_factor = 0; -- /* stupid setting, even unused, SW still need to set a valid value */ -- output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; --#endif -- -- /* frame format type */ -- pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- output_pin->ft = (u16)pfmt->css_pixelformat; -- -- /* stride in bytes */ -- output_pin->stride = av->pix_fmt.bytesperline; -- output_pin->send_irq = 1; -- output_pin->early_ack_en = 0; -- -- /* input pin id */ -- output_pin->input_pin_id = input_pins; -- -- return 0; --} -- --/* Create stream and start it using the CSS FW ABI. */ --static int start_stream_firmware(struct ipu7_isys_video *av, -- struct ipu7_isys_buffer_list *bl) --{ -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_insys_stream_cfg *stream_cfg; -- struct ipu7_insys_buffset *buf = NULL; -- struct isys_fw_msgs *msg = NULL; -- struct ipu7_isys_queue *aq; -- int ret, retout, tout; -- u16 send_type; -- -- if (WARN_ON(!bl)) -- return -EIO; -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) -- return -ENOMEM; -- -- stream_cfg = &msg->fw_msg.stream; -- stream_cfg->port_id = stream->stream_source; -- stream_cfg->vc = stream->vc; -- stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP | -- IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; -- -- list_for_each_entry(aq, &stream->queues, node) { -- struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq); -- -- ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg); -- if (ret < 0) { -- ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -- return ret; -- } -- } -- -- ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg); -- -- stream->nr_output_pins = stream_cfg->nof_output_pins; -- -- reinit_completion(&stream->stream_open_completion); -- -- ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, -- stream_cfg, msg->dma_addr, -- sizeof(*stream_cfg), -- IPU_INSYS_SEND_TYPE_STREAM_OPEN); -- if (ret < 0) { -- dev_err(dev, "can't open stream (%d)\n", ret); -- ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -- return ret; -- } -- -- get_stream_opened(av); -- -- tout = wait_for_completion_timeout(&stream->stream_open_completion, -- FW_CALL_TIMEOUT_JIFFIES); -- -- ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -- -- if (!tout) { -- dev_err(dev, "stream open time out\n"); -- ret = -ETIMEDOUT; -- goto out_put_stream_opened; -- } -- if (stream->error) { -- dev_err(dev, "stream open error: %d\n", stream->error); -- ret = -EIO; -- goto out_put_stream_opened; -- } -- dev_dbg(dev, "start stream: open complete\n"); -- -- msg = ipu7_get_fw_msg_buf(stream); -- if (!msg) { -- ret = -ENOMEM; -- goto out_put_stream_opened; -- } -- buf = &msg->fw_msg.frame; -- -- ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -- ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -- -- reinit_completion(&stream->stream_start_completion); -- -- send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE; -- ipu7_fw_isys_dump_frame_buff_set(dev, buf, -- stream_cfg->nof_output_pins); -- ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf, -- msg->dma_addr, sizeof(*buf), -- send_type); -- if (ret < 0) { -- dev_err(dev, "can't start streaming (%d)\n", ret); -- goto out_stream_close; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_start_completion, -- FW_CALL_TIMEOUT_JIFFIES); -- if (!tout) { -- dev_err(dev, "stream start time out\n"); -- ret = -ETIMEDOUT; -- goto out_stream_close; -- } -- if (stream->error) { -- dev_err(dev, "stream start error: %d\n", stream->error); -- ret = -EIO; -- goto out_stream_close; -- } -- dev_dbg(dev, "start stream: complete\n"); -- -- return 0; -- --out_stream_close: -- reinit_completion(&stream->stream_close_completion); -- -- retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -- IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -- if (retout < 0) { -- dev_dbg(dev, "can't close stream (%d)\n", retout); -- goto out_put_stream_opened; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_close_completion, -- FW_CALL_TIMEOUT_JIFFIES); -- if (!tout) -- dev_err(dev, "stream close time out with error %d\n", -- stream->error); -- else -- dev_dbg(dev, "stream close complete\n"); -- --out_put_stream_opened: -- put_stream_opened(av); -- -- return ret; --} -- --static void stop_streaming_firmware(struct ipu7_isys_video *av) --{ -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- int ret, tout; -- -- reinit_completion(&stream->stream_stop_completion); -- -- ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -- IPU_INSYS_SEND_TYPE_STREAM_FLUSH); -- if (ret < 0) { -- dev_err(dev, "can't stop stream (%d)\n", ret); -- return; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_stop_completion, --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- FW_CALL_TIMEOUT_JIFFIES_RESET); --#else -- FW_CALL_TIMEOUT_JIFFIES); --#endif -- if (!tout) -- dev_warn(dev, "stream stop time out\n"); -- else if (stream->error) -- dev_warn(dev, "stream stop error: %d\n", stream->error); -- else -- dev_dbg(dev, "stop stream: complete\n"); --} -- --static void close_streaming_firmware(struct ipu7_isys_video *av) --{ -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct ipu7_isys_stream *stream = av->stream; -- int ret, tout; -- -- reinit_completion(&stream->stream_close_completion); -- -- ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -- IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -- if (ret < 0) { -- dev_err(dev, "can't close stream (%d)\n", ret); -- return; -- } -- -- tout = wait_for_completion_timeout(&stream->stream_close_completion, --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- FW_CALL_TIMEOUT_JIFFIES_RESET); --#else -- FW_CALL_TIMEOUT_JIFFIES); --#endif -- if (!tout) -- dev_warn(dev, "stream close time out\n"); -- else if (stream->error) -- dev_warn(dev, "stream close error: %d\n", stream->error); -- else -- dev_dbg(dev, "close stream: complete\n"); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- stream->last_sequence = atomic_read(&stream->sequence); -- dev_dbg(dev, "ip->last_sequence = %d\n", -- stream->last_sequence); -- --#endif -- put_stream_opened(av); --} -- --int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -- struct media_entity *source_entity, -- int nr_queues) --{ -- struct ipu7_isys_stream *stream = av->stream; -- struct ipu7_isys_csi2 *csi2; -- -- if (WARN_ON(stream->nr_streaming)) -- return -EINVAL; -- -- stream->nr_queues = nr_queues; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- if (av->isys->state & RESET_STATE_IN_RESET) { -- atomic_set(&stream->sequence, stream->last_sequence); -- dev_dbg(&av->isys->adev->auxdev.dev, -- "atomic_set : stream->last_sequence = %d\n", -- stream->last_sequence); -- } else { -- atomic_set(&stream->sequence, 0); -- } --#else -- atomic_set(&stream->sequence, 0); --#endif -- atomic_set(&stream->buf_id, 0); -- -- stream->seq_index = 0; -- memset(stream->seq, 0, sizeof(stream->seq)); -- -- if (WARN_ON(!list_empty(&stream->queues))) -- return -EINVAL; -- -- stream->stream_source = stream->asd->source; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (!stream->asd->is_tpg) { -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- csi2->receiver_errors = 0; -- } --#else -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- csi2->receiver_errors = 0; --#endif -- stream->source_entity = source_entity; -- -- dev_dbg(&av->isys->adev->auxdev.dev, -- "prepare stream: external entity %s\n", -- stream->source_entity->name); -- -- return 0; --} -- --void ipu7_isys_put_stream(struct ipu7_isys_stream *stream) --{ -- unsigned long flags; -- struct device *dev; -- unsigned int i; -- -- if (!stream) { -- pr_err("ipu7-isys: no available stream\n"); -- return; -- } -- -- dev = &stream->isys->adev->auxdev.dev; -- -- spin_lock_irqsave(&stream->isys->streams_lock, flags); -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (&stream->isys->streams[i] == stream) { -- if (stream->isys->streams_ref_count[i] > 0) -- stream->isys->streams_ref_count[i]--; -- else -- dev_warn(dev, "invalid stream %d\n", i); -- -- break; -- } -- } -- spin_unlock_irqrestore(&stream->isys->streams_lock, flags); --} -- --static struct ipu7_isys_stream * --ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd) --{ -- struct ipu7_isys_stream *stream = NULL; -- struct ipu7_isys *isys = av->isys; -- unsigned long flags; -- unsigned int i; -- u8 vc = av->vc; -- -- if (!isys) -- return NULL; -- -- spin_lock_irqsave(&isys->streams_lock, flags); -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && -- isys->streams[i].asd == asd) { -- isys->streams_ref_count[i]++; -- stream = &isys->streams[i]; -- break; -- } -- } -- -- if (!stream) { -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (!isys->streams_ref_count[i]) { -- isys->streams_ref_count[i]++; -- stream = &isys->streams[i]; -- stream->vc = vc; -- stream->asd = asd; -- break; -- } -- } -- } -- spin_unlock_irqrestore(&isys->streams_lock, flags); -- -- return stream; --} -- --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle) --{ -- unsigned long flags; -- struct ipu7_isys_stream *stream = NULL; -- -- if (!isys) -- return NULL; -- -- if (stream_handle >= IPU_ISYS_MAX_STREAMS) { -- dev_err(&isys->adev->auxdev.dev, -- "stream_handle %d is invalid\n", stream_handle); -- return NULL; -- } -- -- spin_lock_irqsave(&isys->streams_lock, flags); -- if (isys->streams_ref_count[stream_handle] > 0) { -- isys->streams_ref_count[stream_handle]++; -- stream = &isys->streams[stream_handle]; -- } -- spin_unlock_irqrestore(&isys->streams_lock, flags); -- -- return stream; --} -- --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc) --{ -- struct ipu7_isys_stream *stream = NULL; -- unsigned long flags; -- unsigned int i; -- -- if (!isys) -- return NULL; -- -- if (source < 0) { -- dev_err(&isys->adev->auxdev.dev, -- "query stream with invalid port number\n"); -- return NULL; -- } -- -- spin_lock_irqsave(&isys->streams_lock, flags); -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- if (!isys->streams_ref_count[i]) -- continue; -- -- if (isys->streams[i].stream_source == source && -- isys->streams[i].vc == vc) { -- stream = &isys->streams[i]; -- isys->streams_ref_count[i]++; -- break; -- } -- } -- spin_unlock_irqrestore(&isys->streams_lock, flags); -- -- return stream; --} -- --static u32 get_remote_pad_stream(struct media_pad *r_pad) --{ -- struct v4l2_subdev_state *state; -- struct v4l2_subdev *sd; -- u32 stream_id = 0; -- unsigned int i; -- -- sd = media_entity_to_v4l2_subdev(r_pad->entity); -- state = v4l2_subdev_lock_and_get_active_state(sd); -- if (!state) -- return 0; -- -- for (i = 0; i < state->stream_configs.num_configs; i++) { -- struct v4l2_subdev_stream_config *cfg = -- &state->stream_configs.configs[i]; -- if (cfg->pad == r_pad->index) { -- stream_id = cfg->stream; -- break; -- } -- } -- -- v4l2_subdev_unlock_state(state); -- -- return stream_id; --} -- --int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -- struct ipu7_isys_buffer_list *bl) --{ -- struct ipu7_isys_stream *stream = av->stream; -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct media_pad *r_pad; -- struct v4l2_subdev *sd; -- u32 r_stream; -- int ret = 0; -- -- dev_dbg(dev, "set stream: %d\n", state); -- -- if (WARN(!stream->source_entity, "No source entity for stream\n")) -- return -ENODEV; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (stream->asd->is_tpg) { -- sd = &stream->asd->sd; -- r_pad = media_pad_remote_pad_first(&av->pad); -- r_stream = -- ipu7_isys_get_src_stream_by_src_pad(sd, r_pad->index); -- -- if (!state) { -- stop_streaming_firmware(av); -- dev_dbg(dev, "disable streams 0x%lx of %s\n", -- BIT(r_stream), sd->name); -- ret = v4l2_subdev_disable_streams(sd, r_pad->index, -- BIT(r_stream)); -- if (ret) -- dev_err(dev, "disable streams of %s failed\n", -- sd->name); -- -- close_streaming_firmware(av); -- } else { -- ret = start_stream_firmware(av, bl); -- if (ret) { -- dev_err(dev, "start FW stream failed\n"); -- return ret; -- } -- -- dev_dbg(dev, "set stream: source %d, handle %d\n", -- stream->stream_source, stream->stream_handle); -- -- dev_dbg(dev, "enable streams 0x%lx of %s\n", -- BIT(r_stream), sd->name); -- /* start sub-device which connects with video */ -- ret = v4l2_subdev_enable_streams(sd, r_pad->index, -- BIT(r_stream)); -- if (ret) { -- dev_err(dev, "enable streams of %s failed\n", -- sd->name); -- goto out_media_entity_stop_streaming_firmware; -- } -- } -- av->streaming = state; -- -- return 0; -- } -- --#endif -- sd = &stream->asd->sd; -- r_pad = media_pad_remote_pad_first(&av->pad); -- r_stream = get_remote_pad_stream(r_pad); -- if (!state) { -- stop_streaming_firmware(av); -- -- /* stop sub-device which connects with video */ -- dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n", -- sd->name, r_pad->index, BIT_ULL(r_stream)); -- ret = v4l2_subdev_disable_streams(sd, r_pad->index, -- BIT_ULL(r_stream)); -- if (ret) { -- dev_err(dev, "disable streams %s failed with %d\n", -- sd->name, ret); -- return ret; -- } -- -- close_streaming_firmware(av); -- } else { -- ret = start_stream_firmware(av, bl); -- if (ret) { -- dev_err(dev, "start stream of firmware failed\n"); -- return ret; -- } -- -- /* start sub-device which connects with video */ -- dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n", -- sd->name, r_pad->index, BIT_ULL(r_stream)); -- ret = v4l2_subdev_enable_streams(sd, r_pad->index, -- BIT_ULL(r_stream)); -- if (ret) { -- dev_err(dev, "enable streams %s failed with %d\n", -- sd->name, ret); -- goto out_media_entity_stop_streaming_firmware; -- } -- } -- -- av->streaming = state; -- -- return 0; -- --out_media_entity_stop_streaming_firmware: -- stop_streaming_firmware(av); -- -- return ret; --} -- --static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { -- .vidioc_querycap = ipu7_isys_vidioc_querycap, -- .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, -- .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes, -- .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap, -- .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap, -- .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap, -- .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs, -- .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs, -- .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -- .vidioc_querybuf = vb2_ioctl_querybuf, -- .vidioc_qbuf = vb2_ioctl_qbuf, -- .vidioc_dqbuf = vb2_ioctl_dqbuf, -- .vidioc_streamon = vb2_ioctl_streamon, -- .vidioc_streamoff = vb2_ioctl_streamoff, -- .vidioc_expbuf = vb2_ioctl_expbuf, --}; -- --static const struct media_entity_operations entity_ops = { -- .link_validate = link_validate, --}; -- --static const struct v4l2_file_operations isys_fops = { -- .owner = THIS_MODULE, -- .poll = vb2_fop_poll, -- .unlocked_ioctl = video_ioctl2, -- .mmap = vb2_fop_mmap, -- .open = video_open, --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- .release = video_release, --#else -- .release = vb2_fop_release, --#endif --}; -- --int ipu7_isys_fw_open(struct ipu7_isys *isys) --{ -- struct ipu7_bus_device *adev = isys->adev; -- int ret; -- -- ret = pm_runtime_resume_and_get(&adev->auxdev.dev); -- if (ret < 0) -- return ret; -- -- mutex_lock(&isys->mutex); -- -- if (isys->ref_count++) -- goto unlock; -- -- /* -- * Buffers could have been left to wrong queue at last closure. -- * Move them now back to empty buffer queue. -- */ -- ipu7_cleanup_fw_msg_bufs(isys); -- -- ret = ipu7_fw_isys_open(isys); -- if (ret < 0) -- goto out; -- --unlock: -- mutex_unlock(&isys->mutex); -- -- return 0; --out: -- isys->ref_count--; -- mutex_unlock(&isys->mutex); -- pm_runtime_put(&adev->auxdev.dev); -- -- return ret; --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --void ipu7_isys_fw_close(struct ipu7_isys *isys) --{ -- int ret = 0; -- -- mutex_lock(&isys->mutex); -- isys->ref_count--; -- if (!isys->ref_count) { -- /* need reset when fw close is abnormal */ -- ret = ipu7_fw_isys_close(isys); -- if (ret) { -- mutex_lock(&isys->reset_mutex); -- isys->need_reset = true; -- mutex_unlock(&isys->reset_mutex); -- } -- } -- -- mutex_unlock(&isys->mutex); -- -- mutex_lock(&isys->reset_mutex); -- if (isys->need_reset) { -- mutex_unlock(&isys->reset_mutex); -- pm_runtime_put_sync(&isys->adev->auxdev.dev); -- } else { -- mutex_unlock(&isys->reset_mutex); -- pm_runtime_put(&isys->adev->auxdev.dev); -- } --} --#else --void ipu7_isys_fw_close(struct ipu7_isys *isys) --{ -- mutex_lock(&isys->mutex); -- -- isys->ref_count--; -- -- if (!isys->ref_count) -- ipu7_fw_isys_close(isys); -- -- mutex_unlock(&isys->mutex); -- pm_runtime_put(&isys->adev->auxdev.dev); --} --#endif -- --int ipu7_isys_setup_video(struct ipu7_isys_video *av, -- struct media_entity **source_entity, int *nr_queues) --{ -- const struct ipu7_isys_pixelformat *pfmt = -- ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -- struct device *dev = &av->isys->adev->auxdev.dev; -- struct media_pad *source_pad, *remote_pad; -- struct v4l2_mbus_frame_desc_entry entry; -- struct v4l2_subdev_route *route = NULL; -- struct v4l2_subdev_route *r; -- struct v4l2_subdev_state *state; -- struct ipu7_isys_subdev *asd; -- struct v4l2_subdev *remote_sd; -- struct media_pipeline *pipeline; -- int ret = -EINVAL; -- -- *nr_queues = 0; -- -- remote_pad = media_pad_remote_pad_unique(&av->pad); -- if (IS_ERR(remote_pad)) { -- dev_dbg(dev, "failed to get remote pad\n"); -- return PTR_ERR(remote_pad); -- } -- -- remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -- asd = to_ipu7_isys_subdev(remote_sd); --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- -- if (strncmp(remote_pad->entity->name, "Intel IPU7 TPG", -- strlen("Intel IPU7 TPG")) == 0) { -- dev_dbg(dev, "Find TPG:%s stream\n", remote_sd->name); -- -- av->vc = 0; -- av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -- ret = video_device_pipeline_alloc_start(&av->vdev); -- if (ret < 0) { -- dev_dbg(dev, "media pipeline start failed\n"); -- return ret; -- } -- -- *source_entity = remote_pad->entity; -- av->stream = ipu7_isys_get_stream(av, asd); -- if (!av->stream) { -- video_device_pipeline_stop(&av->vdev); -- dev_err(dev, "no available stream for firmware\n"); -- return -EINVAL; -- } -- -- av->stream->asd->is_tpg = true; -- *nr_queues = 1; -- -- return 0; -- } --#endif -- -- source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); -- if (!source_pad) { -- dev_dbg(dev, "No external source entity\n"); -- return -ENODEV; -- } -- -- *source_entity = source_pad->entity; -- -- state = v4l2_subdev_lock_and_get_active_state(remote_sd); -- for_each_active_route(&state->routing, r) { -- if (r->source_pad == remote_pad->index) -- route = r; -- } -- -- if (!route) { -- v4l2_subdev_unlock_state(state); -- dev_dbg(dev, "Failed to find route\n"); -- return -ENODEV; -- } -- -- v4l2_subdev_unlock_state(state); -- -- ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, -- to_ipu7_isys_csi2(asd), -- *source_entity, &entry, -- nr_queues); -- if (ret == -ENOIOCTLCMD) { -- av->vc = 0; -- av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -- if (av->dt == 0xff) -- return -EINVAL; -- *nr_queues = 1; -- } else if (*nr_queues && !ret) { -- dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", -- entry.stream, entry.length, entry.bus.csi2.vc, -- entry.bus.csi2.dt); -- -- av->vc = entry.bus.csi2.vc; -- av->dt = entry.bus.csi2.dt; -- } else { -- dev_err(dev, "failed to get remote frame desc\n"); -- return ret; -- } -- -- pipeline = media_entity_pipeline(&av->vdev.entity); -- if (!pipeline) -- ret = video_device_pipeline_alloc_start(&av->vdev); -- else -- ret = video_device_pipeline_start(&av->vdev, pipeline); -- if (ret < 0) { -- dev_dbg(dev, "media pipeline start failed\n"); -- return ret; -- } -- -- av->stream = ipu7_isys_get_stream(av, asd); -- if (!av->stream) { -- video_device_pipeline_stop(&av->vdev); -- dev_err(dev, "no available stream for firmware\n"); -- return -EINVAL; -- } -- -- return 0; --} -- --/* -- * Do everything that's needed to initialise things related to video -- * buffer queue, video node, and the related media entity. The caller -- * is expected to assign isys field and set the name of the video -- * device. -- */ --int ipu7_isys_video_init(struct ipu7_isys_video *av) --{ -- struct v4l2_format format = { -- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, -- .fmt.pix = { -- .width = 1920, -- .height = 1080, -- }, -- }; -- int ret; -- -- mutex_init(&av->mutex); -- av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | -- V4L2_CAP_VIDEO_CAPTURE; -- av->vdev.vfl_dir = VFL_DIR_RX; -- -- ret = ipu7_isys_queue_init(&av->aq); -- if (ret) -- goto out_mutex_destroy; -- -- av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; -- ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); -- if (ret) -- goto out_vb2_queue_cleanup; -- -- av->vdev.entity.ops = &entity_ops; -- av->vdev.release = video_device_release_empty; -- av->vdev.fops = &isys_fops; -- av->vdev.v4l2_dev = &av->isys->v4l2_dev; -- av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev; -- av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops; -- av->vdev.queue = &av->aq.vbq; -- av->vdev.lock = &av->mutex; -- -- __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); -- av->pix_fmt = format.fmt.pix; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- av->reset = false; -- av->skipframe = 0; -- av->start_streaming = 0; --#endif -- -- set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); -- video_set_drvdata(&av->vdev, av); -- -- ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); -- if (ret) -- goto out_media_entity_cleanup; -- -- return ret; -- --out_media_entity_cleanup: -- vb2_video_unregister_device(&av->vdev); -- media_entity_cleanup(&av->vdev.entity); -- --out_vb2_queue_cleanup: -- vb2_queue_release(&av->aq.vbq); -- --out_mutex_destroy: -- mutex_destroy(&av->mutex); -- -- return ret; --} -- --void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) --{ -- vb2_video_unregister_device(&av->vdev); -- media_entity_cleanup(&av->vdev.entity); -- mutex_destroy(&av->mutex); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.h b/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -deleted file mode 100644 -index e6d1da2b7b47..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -+++ /dev/null -@@ -1,125 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_VIDEO_H --#define IPU7_ISYS_VIDEO_H -- --#include --#include --#include --#include --#include -- --#include --#include -- --#include "ipu7-isys-queue.h" -- --#define IPU_INSYS_OUTPUT_PINS 11U --#define IPU_ISYS_MAX_PARALLEL_SOF 2U -- --struct file; --struct ipu7_isys; --struct ipu7_isys_csi2; --struct ipu7_insys_stream_cfg; --struct ipu7_isys_subdev; -- --struct ipu7_isys_pixelformat { -- u32 pixelformat; -- u32 bpp; -- u32 bpp_packed; -- u32 code; -- u32 css_pixelformat; --}; -- --struct sequence_info { -- unsigned int sequence; -- u64 timestamp; --}; -- --struct output_pin_data { -- void (*pin_ready)(struct ipu7_isys_stream *stream, -- struct ipu7_insys_resp *info); -- struct ipu7_isys_queue *aq; --}; -- --/* -- * Align with firmware stream. Each stream represents a CSI virtual channel. -- * May map to multiple video devices -- */ --struct ipu7_isys_stream { -- struct mutex mutex; -- struct media_entity *source_entity; -- atomic_t sequence; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- int last_sequence; --#endif -- atomic_t buf_id; -- unsigned int seq_index; -- struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; -- int stream_source; -- int stream_handle; -- unsigned int nr_output_pins; -- struct ipu7_isys_subdev *asd; -- -- int nr_queues; /* Number of capture queues */ -- int nr_streaming; -- int streaming; -- struct list_head queues; -- struct completion stream_open_completion; -- struct completion stream_close_completion; -- struct completion stream_start_completion; -- struct completion stream_stop_completion; -- struct ipu7_isys *isys; -- -- struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS]; -- int error; -- u8 vc; --}; -- --struct ipu7_isys_video { -- struct ipu7_isys_queue aq; -- /* Serialise access to other fields in the struct. */ -- struct mutex mutex; -- struct media_pad pad; -- struct video_device vdev; -- struct v4l2_pix_format pix_fmt; -- struct ipu7_isys *isys; -- struct ipu7_isys_csi2 *csi2; -- struct ipu7_isys_stream *stream; -- unsigned int streaming; -- u8 vc; -- u8 dt; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- unsigned int reset; -- unsigned int skipframe; -- unsigned int start_streaming; --#endif --}; -- --#define ipu7_isys_queue_to_video(__aq) \ -- container_of(__aq, struct ipu7_isys_video, aq) -- --extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[]; -- --const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat); --int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -- struct media_entity *source_entity, -- int nr_queues); --int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -- struct ipu7_isys_buffer_list *bl); --int ipu7_isys_fw_open(struct ipu7_isys *isys); --void ipu7_isys_fw_close(struct ipu7_isys *isys); --int ipu7_isys_setup_video(struct ipu7_isys_video *av, -- struct media_entity **source_entity, int *nr_queues); --int ipu7_isys_video_init(struct ipu7_isys_video *av); --void ipu7_isys_video_cleanup(struct ipu7_isys_video *av); --void ipu7_isys_put_stream(struct ipu7_isys_stream *stream); --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, -- u8 stream_handle); --struct ipu7_isys_stream * --ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc); --#endif /* IPU7_ISYS_VIDEO_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c -deleted file mode 100644 -index 44e860d3db24..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys.c -+++ /dev/null -@@ -1,1625 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#include --#include --#ifdef CONFIG_DEBUG_FS --#include --#endif --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include --#include --#include --#include --#include --#include -- --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-cpd.h" --#include "ipu7-dma.h" --#include "ipu7-fw-isys.h" --#include "ipu7-mmu.h" --#include "ipu7-isys.h" --#include "ipu7-isys-csi2.h" --#include "ipu7-isys-csi-phy.h" --#include "ipu7-isys-csi2-regs.h" --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --#include "ipu7-isys-tpg.h" --#endif --#include "ipu7-isys-video.h" --#include "ipu7-platform-regs.h" -- --#define ISYS_PM_QOS_VALUE 300 -- --static int --isys_complete_ext_device_registration(struct ipu7_isys *isys, -- struct v4l2_subdev *sd, -- struct ipu7_isys_csi2_config *csi2) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- unsigned int i; -- int ret; -- -- v4l2_set_subdev_hostdata(sd, csi2); -- -- for (i = 0; i < sd->entity.num_pads; i++) { -- if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -- break; -- } -- -- if (i == sd->entity.num_pads) { -- dev_warn(dev, "no source pad in external entity\n"); -- ret = -ENOENT; -- goto skip_unregister_subdev; -- } -- -- ret = media_create_pad_link(&sd->entity, i, -- &isys->csi2[csi2->port].asd.sd.entity, -- 0, MEDIA_LNK_FL_ENABLED | -- MEDIA_LNK_FL_IMMUTABLE); -- if (ret) { -- dev_warn(dev, "can't create link\n"); -- goto skip_unregister_subdev; -- } -- -- isys->csi2[csi2->port].nlanes = csi2->nlanes; -- if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) -- isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; -- else -- isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; -- -- return 0; -- --skip_unregister_subdev: -- v4l2_device_unregister_subdev(sd); -- return ret; --} -- --struct isys_i2c_test { -- u8 bus_nr; -- u16 addr; -- struct i2c_client *client; --}; -- --static int isys_i2c_test(struct device *dev, void *priv) --{ -- struct i2c_client *client = i2c_verify_client(dev); -- struct isys_i2c_test *test = priv; -- -- if (!client) -- return 0; -- -- if (i2c_adapter_id(client->adapter) != test->bus_nr || -- client->addr != test->addr) -- return 0; -- -- test->client = client; -- -- return 0; --} -- --static --struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -- struct ipu7_isys_subdev_info *sd_info) --{ -- struct i2c_board_info *info = &sd_info->i2c.board_info; -- struct isys_i2c_test test = { -- .bus_nr = i2c_adapter_id(adapter), -- .addr = info->addr, -- }; -- int ret; -- -- ret = i2c_for_each_dev(&test, isys_i2c_test); -- if (ret || !test.client) -- return NULL; -- return test.client; --} -- --static int isys_register_ext_subdev(struct ipu7_isys *isys, -- struct ipu7_isys_subdev_info *sd_info) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct i2c_adapter *adapter; -- struct v4l2_subdev *sd; -- struct i2c_client *client; -- int ret; -- int bus; -- -- bus = sd_info->i2c.i2c_adapter_id; -- adapter = i2c_get_adapter(bus); -- if (!adapter) { -- dev_warn(dev, "can't find adapter\n"); -- return -ENOENT; -- } -- -- dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -- sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -- bus); -- -- if (sd_info->csi2) { -- dev_info(dev, "sensor device on CSI port: %d\n", -- sd_info->csi2->port); -- if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -- !isys->csi2[sd_info->csi2->port].isys) { -- dev_warn(dev, "invalid csi2 port %u\n", -- sd_info->csi2->port); -- ret = -EINVAL; -- goto skip_put_adapter; -- } -- } else { -- dev_info(dev, "No camera subdevice\n"); -- } -- -- client = isys_find_i2c_subdev(adapter, sd_info); -- if (client) { -- dev_warn(dev, "Device exists\n"); --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -- /* TODO: remove i2c_unregister_device() */ -- i2c_unregister_device(client); --#else -- ret = 0; -- goto skip_put_adapter; --#endif -- } -- -- sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -- &sd_info->i2c.board_info, NULL); -- if (!sd) { -- dev_warn(dev, "can't create new i2c subdev\n"); -- ret = -EINVAL; -- goto skip_put_adapter; -- } -- -- if (!sd_info->csi2) -- return 0; -- -- return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -- --skip_put_adapter: -- i2c_put_adapter(adapter); -- -- return ret; --} -- --static void isys_register_ext_subdevs(struct ipu7_isys *isys) --{ -- struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -- struct ipu7_isys_subdev_info **sd_info; -- -- if (!spdata) { -- dev_info(&isys->adev->auxdev.dev, -- "no subdevice info provided\n"); -- return; -- } -- for (sd_info = spdata->subdevs; *sd_info; sd_info++) -- isys_register_ext_subdev(isys, *sd_info); --} -- --static void isys_stream_init(struct ipu7_isys *isys) --{ -- unsigned int i; -- -- for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -- mutex_init(&isys->streams[i].mutex); -- init_completion(&isys->streams[i].stream_open_completion); -- init_completion(&isys->streams[i].stream_close_completion); -- init_completion(&isys->streams[i].stream_start_completion); -- init_completion(&isys->streams[i].stream_stop_completion); -- INIT_LIST_HEAD(&isys->streams[i].queues); -- isys->streams[i].isys = isys; -- isys->streams[i].stream_handle = i; -- isys->streams[i].vc = INVALID_VC_ID; -- } --} -- --static int isys_fw_log_init(struct ipu7_isys *isys) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct isys_fw_log *fw_log; -- void *log_buf; -- -- if (isys->fw_log) -- return 0; -- -- fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -- if (!fw_log) -- return -ENOMEM; -- -- mutex_init(&fw_log->mutex); -- -- log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!log_buf) -- return -ENOMEM; -- -- fw_log->head = log_buf; -- fw_log->addr = log_buf; -- fw_log->count = 0; -- fw_log->size = 0; -- -- isys->fw_log = fw_log; -- -- return 0; --} -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --/* The .bound() notifier callback when a match is found */ --static int isys_notifier_bound(struct v4l2_async_notifier *notifier, -- struct v4l2_subdev *sd, -- struct v4l2_async_connection *asc) --{ -- struct ipu7_isys *isys = container_of(notifier, -- struct ipu7_isys, notifier); -- struct sensor_async_sd *s_asd = -- container_of(asc, struct sensor_async_sd, asc); -- struct device *dev = &isys->adev->auxdev.dev; -- int ret; -- -- ret = ipu_bridge_instantiate_vcm(sd->dev); -- if (ret) { -- dev_err(dev, "instantiate vcm failed\n"); -- return ret; -- } -- -- dev_info(dev, "bind %s nlanes is %d port is %d\n", -- sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); -- isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); -- -- return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); --} -- --static int isys_notifier_complete(struct v4l2_async_notifier *notifier) --{ -- struct ipu7_isys *isys = container_of(notifier, -- struct ipu7_isys, notifier); -- -- dev_info(&isys->adev->auxdev.dev, -- "All sensor registration completed.\n"); -- -- return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); --} -- --static const struct v4l2_async_notifier_operations isys_async_ops = { -- .bound = isys_notifier_bound, -- .complete = isys_notifier_complete, --}; -- --static int isys_notifier_init(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2 = -- &isys->pdata->ipdata->csi2; -- struct ipu7_device *isp = isys->adev->isp; -- struct device *dev = &isp->pdev->dev; -- unsigned int i; -- int ret; -- -- v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); -- -- for (i = 0; i < csi2->nports; i++) { -- struct v4l2_fwnode_endpoint vep = { -- .bus_type = V4L2_MBUS_UNKNOWN -- }; -- struct sensor_async_sd *s_asd; -- struct fwnode_handle *ep; -- -- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, -- FWNODE_GRAPH_ENDPOINT_NEXT); -- if (!ep) -- continue; -- -- ret = v4l2_fwnode_endpoint_parse(ep, &vep); -- if (ret) -- goto err_parse; -- -- if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && -- vep.bus_type != V4L2_MBUS_CSI2_CPHY) { -- ret = -EINVAL; -- dev_err(dev, "unsupported bus type %d!\n", -- vep.bus_type); -- goto err_parse; -- } -- -- s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, -- struct -- sensor_async_sd); -- if (IS_ERR(s_asd)) { -- ret = PTR_ERR(s_asd); -- goto err_parse; -- } -- -- s_asd->csi2.port = vep.base.port; -- s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; -- s_asd->csi2.bus_type = vep.bus_type; -- -- fwnode_handle_put(ep); -- -- continue; -- --err_parse: -- fwnode_handle_put(ep); -- return ret; -- } -- -- if (list_empty(&isys->notifier.waiting_list)) { -- /* isys probe could continue with async subdevs missing */ -- dev_warn(dev, "no subdev found in graph\n"); -- return 0; -- } -- -- isys->notifier.ops = &isys_async_ops; -- ret = v4l2_async_nf_register(&isys->notifier); -- if (ret) { -- dev_err(dev, "failed to register async notifier(%d)\n", ret); -- v4l2_async_nf_cleanup(&isys->notifier); -- } -- -- return ret; --} -- --static void isys_notifier_cleanup(struct ipu7_isys *isys) --{ -- v4l2_async_nf_unregister(&isys->notifier); -- v4l2_async_nf_cleanup(&isys->notifier); --} --#endif -- --static void isys_unregister_video_devices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- unsigned int i, j; -- -- for (i = 0; i < csi2_pdata->nports; i++) -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) -- ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); --} -- --static int isys_register_video_devices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- unsigned int i, j; -- int ret; -- -- for (i = 0; i < csi2_pdata->nports; i++) { -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -- struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -- -- snprintf(av->vdev.name, sizeof(av->vdev.name), -- IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u", -- i * IPU7_NR_OF_CSI2_SRC_PADS + j); -- av->isys = isys; -- av->aq.vbq.buf_struct_size = -- sizeof(struct ipu7_isys_video_buffer); -- -- ret = ipu7_isys_video_init(av); -- if (ret) -- goto fail; -- } -- } -- -- return 0; -- --fail: -- i = i + 1U; -- while (i--) { -- while (j--) -- ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); -- j = IPU7_NR_OF_CSI2_SRC_PADS; -- } -- -- return ret; --} -- --static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2 = -- &isys->pdata->ipdata->csi2; -- unsigned int i; -- -- for (i = 0; i < csi2->nports; i++) -- ipu7_isys_csi2_cleanup(&isys->csi2[i]); --} -- --static int isys_csi2_register_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- unsigned int i; -- int ret; -- -- for (i = 0; i < csi2_pdata->nports; i++) { -- ret = ipu7_isys_csi2_init(&isys->csi2[i], isys, -- isys->pdata->base + -- csi2_pdata->offsets[i], i); -- if (ret) -- goto fail; -- } -- -- isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK; -- -- return 0; -- --fail: -- while (i--) -- ipu7_isys_csi2_cleanup(&isys->csi2[i]); -- -- return ret; --} -- --static int isys_csi2_create_media_links(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -- &isys->pdata->ipdata->csi2; -- struct device *dev = &isys->adev->auxdev.dev; -- struct media_entity *sd; -- unsigned int i, j; -- int ret; -- -- for (i = 0; i < csi2_pdata->nports; i++) { -- sd = &isys->csi2[i].asd.sd.entity; -- -- for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -- struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -- -- ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j, -- &av->vdev.entity, 0, 0); -- if (ret) { -- dev_err(dev, "CSI2 can't create link\n"); -- return ret; -- } -- -- av->csi2 = &isys->csi2[i]; -- } -- } -- -- return 0; --} -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --static void isys_tpg_unregister_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -- &isys->pdata->ipdata->tpg; -- unsigned int i; -- -- if (!isys->tpg) -- return; -- -- for (i = 0; i < tpg_pdata->ntpgs; i++) -- ipu7_isys_tpg_cleanup(&isys->tpg[i]); -- -- kfree(isys->tpg); -- isys->tpg = NULL; --} -- --/* --static int isys_tpg_register_subdevices(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -- &isys->pdata->ipdata->tpg; -- unsigned int i; -- int ret; -- -- isys->tpg = kcalloc(tpg_pdata->ntpgs, sizeof(*isys->tpg), GFP_KERNEL); -- if (!isys->tpg) -- return -ENOMEM; -- -- for (i = 0; i < tpg_pdata->ntpgs; i++) { -- ret = ipu7_isys_tpg_init(&isys->tpg[i], isys, -- isys->pdata->base + -- tpg_pdata->offsets[i], -- tpg_pdata->sels ? -- (isys->pdata->base + -- tpg_pdata->sels[i]) : NULL, i); -- if (ret) -- goto fail; -- } -- -- return 0; -- --fail: -- while (i--) -- ipu7_isys_tpg_cleanup(&isys->tpg[i]); -- -- kfree(isys->tpg); -- isys->tpg = NULL; -- -- return ret; --} -- --static int isys_tpg_create_media_links(struct ipu7_isys *isys) --{ -- const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -- &isys->pdata->ipdata->tpg; -- struct device *dev = &isys->adev->auxdev.dev; -- struct ipu7_isys_tpg *tpg; -- struct media_entity *sd; -- unsigned int i; -- int ret; -- -- for (i = 0; i < tpg_pdata->ntpgs; i++) { -- tpg = &isys->tpg[i]; -- sd = &tpg->asd.sd.entity; -- tpg->av = &isys->csi2[tpg->index].av[0]; -- -- ret = media_create_pad_link(sd, TPG_PAD_SOURCE, -- &tpg->av->vdev.entity, -- TPG_PAD_SOURCE, 0); -- if (ret) { -- dev_err(dev, "TPG can't create link\n"); -- return ret; -- } -- } -- -- return 0; --} --*/ --#endif -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --static int isys_register_devices(struct ipu7_isys *isys) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct pci_dev *pdev = isys->adev->isp->pdev; -- int ret; -- -- media_device_pci_init(&isys->media_dev, -- pdev, IPU_MEDIA_DEV_MODEL_NAME); -- -- strscpy(isys->v4l2_dev.name, isys->media_dev.model, -- sizeof(isys->v4l2_dev.name)); -- -- ret = media_device_register(&isys->media_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- isys->v4l2_dev.mdev = &isys->media_dev; -- isys->v4l2_dev.ctrl_handler = NULL; -- -- ret = v4l2_device_register(dev, &isys->v4l2_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- ret = isys_register_video_devices(isys); -- if (ret) -- goto out_v4l2_device_unregister; -- -- ret = isys_csi2_register_subdevices(isys); -- if (ret) -- goto out_video_unregister_device; -- -- ret = isys_csi2_create_media_links(isys); -- if (ret) -- goto out_csi2_unregister_subdevices; -- -- if (!isys->pdata->spdata) { -- ret = isys_notifier_init(isys); -- if (ret) -- goto out_csi2_unregister_subdevices; -- } else { -- isys_register_ext_subdevs(isys); -- ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -- if (ret) -- goto out_csi2_unregister_subdevices; -- } -- -- return 0; -- --out_csi2_unregister_subdevices: -- isys_csi2_unregister_subdevices(isys); -- --out_video_unregister_device: -- isys_unregister_video_devices(isys); -- --out_v4l2_device_unregister: -- v4l2_device_unregister(&isys->v4l2_dev); -- --out_media_device_unregister: -- media_device_unregister(&isys->media_dev); -- media_device_cleanup(&isys->media_dev); -- -- dev_err(dev, "failed to register isys devices\n"); -- -- return ret; --} --#else --static int isys_register_devices(struct ipu7_isys *isys) --{ -- struct device *dev = &isys->adev->auxdev.dev; -- struct pci_dev *pdev = isys->adev->isp->pdev; -- int ret; -- -- media_device_pci_init(&isys->media_dev, -- pdev, IPU_MEDIA_DEV_MODEL_NAME); -- -- strscpy(isys->v4l2_dev.name, isys->media_dev.model, -- sizeof(isys->v4l2_dev.name)); -- -- ret = media_device_register(&isys->media_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- isys->v4l2_dev.mdev = &isys->media_dev; -- isys->v4l2_dev.ctrl_handler = NULL; -- -- ret = v4l2_device_register(dev, &isys->v4l2_dev); -- if (ret < 0) -- goto out_media_device_unregister; -- -- ret = isys_register_video_devices(isys); -- if (ret) -- goto out_v4l2_device_unregister; -- -- ret = isys_csi2_register_subdevices(isys); -- if (ret) -- goto out_video_unregister_device; -- -- ret = isys_csi2_create_media_links(isys); -- if (ret) -- goto out_csi2_unregister_subdevices; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- ret = isys_tpg_register_subdevices(isys); -- if (!ret) -- ret = isys_tpg_create_media_links(isys); -- -- if (ret) -- goto out_tpg_unregister_subdevices; -- --#endif -- -- isys_register_ext_subdevs(isys); -- ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -- if (ret) --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- goto out_tpg_unregister_subdevices; --#else -- goto out_csi2_unregister_subdevices; --#endif -- -- return 0; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --out_tpg_unregister_subdevices: -- isys_tpg_unregister_subdevices(isys); --#endif --out_csi2_unregister_subdevices: -- isys_csi2_unregister_subdevices(isys); -- --out_video_unregister_device: -- isys_unregister_video_devices(isys); -- --out_v4l2_device_unregister: -- v4l2_device_unregister(&isys->v4l2_dev); -- --out_media_device_unregister: -- media_device_unregister(&isys->media_dev); -- media_device_cleanup(&isys->media_dev); -- -- dev_err(dev, "failed to register isys devices\n"); -- -- return ret; --} --#endif -- --static void isys_unregister_devices(struct ipu7_isys *isys) --{ -- isys_unregister_video_devices(isys); -- isys_csi2_unregister_subdevices(isys); --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- isys_tpg_unregister_subdevices(isys); --#endif -- v4l2_device_unregister(&isys->v4l2_dev); -- media_device_unregister(&isys->media_dev); -- media_device_cleanup(&isys->media_dev); --} -- --static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable) --{ -- u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -- void __iomem *base = isys->pdata->base; -- u32 mask = isys->isr_csi2_mask; -- -- if (!enable) { -- writel(mask, base + offset + IRQ_CTL_CLEAR); -- writel(0, base + offset + IRQ_CTL_ENABLE); -- return; -- } -- -- writel(mask, base + offset + IRQ_CTL_EDGE); -- writel(mask, base + offset + IRQ_CTL_CLEAR); -- writel(mask, base + offset + IRQ_CTL_MASK); -- writel(mask, base + offset + IRQ_CTL_ENABLE); --} -- --static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable) --{ -- void __iomem *base = isys->pdata->base; -- u32 mask = IS_UC_TO_SW_IRQ_MASK; -- u32 offset = IS_UC_CTRL_BASE; -- -- if (!enable) { -- writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE); -- return; -- } -- -- writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR); -- writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N); -- writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE); --} -- --void ipu7_isys_setup_hw(struct ipu7_isys *isys) --{ -- u32 offset; -- void __iomem *base = isys->pdata->base; -- -- /* soft reset */ -- offset = IS_IO_GPREGS_BASE; -- -- writel(0x0, base + offset + CLK_EN_TXCLKESC); -- /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */ -- writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK); -- /* correct the initial printf configuration */ -- writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -- -- enable_to_sw_irq(isys, 1); -- enable_csi2_legacy_irq(isys, 1); --} -- --static void isys_cleanup_hw(struct ipu7_isys *isys) --{ -- enable_csi2_legacy_irq(isys, 0); -- enable_to_sw_irq(isys, 0); --} -- --static int isys_runtime_pm_resume(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- struct ipu7_device *isp = adev->isp; -- unsigned long flags; -- int ret; -- -- if (!isys) -- return 0; -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- return ret; -- -- cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); -- -- ret = ipu_buttress_start_tsc_sync(isp); -- if (ret) -- return ret; -- -- spin_lock_irqsave(&isys->power_lock, flags); -- isys->power = 1; -- spin_unlock_irqrestore(&isys->power_lock, flags); -- -- return 0; --} -- --static int isys_runtime_pm_suspend(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- unsigned long flags; -- -- if (!isys) -- return 0; -- -- isys_cleanup_hw(isys); -- -- spin_lock_irqsave(&isys->power_lock, flags); -- isys->power = 0; -- spin_unlock_irqrestore(&isys->power_lock, flags); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_lock(&isys->reset_mutex); -- isys->need_reset = false; -- mutex_unlock(&isys->reset_mutex); -- --#endif -- cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- -- return 0; --} -- --static int isys_suspend(struct device *dev) --{ -- struct ipu7_isys *isys = dev_get_drvdata(dev); -- -- /* If stream is open, refuse to suspend */ -- if (isys->stream_opened) -- return -EBUSY; -- -- return 0; --} -- --static int isys_resume(struct device *dev) --{ -- return 0; --} -- --static const struct dev_pm_ops isys_pm_ops = { -- .runtime_suspend = isys_runtime_pm_suspend, -- .runtime_resume = isys_runtime_pm_resume, -- .suspend = isys_suspend, -- .resume = isys_resume, --}; -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --static void isys_remove(struct auxiliary_device *auxdev) --{ -- struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -- struct isys_fw_msgs *fwmsg, *safe; -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- --#ifdef CONFIG_DEBUG_FS -- if (adev->isp->ipu7_dir) -- debugfs_remove_recursive(isys->debugfsdir); --#endif -- for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -- mutex_destroy(&isys->streams[i].mutex); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- if (!isys->pdata->spdata) -- isys_notifier_cleanup(isys); -- -- isys_unregister_devices(isys); -- -- cpu_latency_qos_remove_request(&isys->pm_qos); -- -- mutex_destroy(&isys->stream_mutex); -- mutex_destroy(&isys->mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_destroy(&isys->reset_mutex); --#endif --} --#else --static void isys_remove(struct auxiliary_device *auxdev) --{ -- struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -- struct isys_fw_msgs *fwmsg, *safe; -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- --#ifdef CONFIG_DEBUG_FS -- if (adev->isp->ipu7_dir) -- debugfs_remove_recursive(isys->debugfsdir); --#endif -- for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -- mutex_destroy(&isys->streams[i].mutex); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- fwmsg, fwmsg->dma_addr, 0); -- -- isys_unregister_devices(isys); -- -- cpu_latency_qos_remove_request(&isys->pm_qos); -- -- mutex_destroy(&isys->stream_mutex); -- mutex_destroy(&isys->mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_destroy(&isys->reset_mutex); --#endif --} --#endif -- --#ifdef CONFIG_DEBUG_FS --static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -- loff_t *pos) --{ -- struct ipu7_isys *isys = file->private_data; -- struct isys_fw_log *fw_log = isys->fw_log; -- struct device *dev = &isys->adev->auxdev.dev; -- u32 log_size; -- int ret = 0; -- void *buf; -- -- if (!fw_log) -- return 0; -- -- buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!buf) -- return -ENOMEM; -- -- mutex_lock(&fw_log->mutex); -- if (!fw_log->size) { -- dev_warn(dev, "no available fw log\n"); -- mutex_unlock(&fw_log->mutex); -- goto free_and_return; -- } -- -- if (fw_log->size > FW_LOG_BUF_SIZE) -- log_size = FW_LOG_BUF_SIZE; -- else -- log_size = fw_log->size; -- -- memcpy(buf, fw_log->addr, log_size); -- dev_info(dev, "copy %d bytes fw log to user...\n", log_size); -- mutex_unlock(&fw_log->mutex); -- -- ret = simple_read_from_buffer(userbuf, size, pos, buf, -- log_size); --free_and_return: -- kvfree(buf); -- -- return ret; --} -- --static const struct file_operations isys_fw_log_fops = { -- .open = simple_open, -- .owner = THIS_MODULE, -- .read = fwlog_read, -- .llseek = default_llseek, --}; -- --static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) --{ -- struct dentry *file; -- struct dentry *dir; -- -- dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); -- if (IS_ERR(dir)) -- return -ENOMEM; -- -- file = debugfs_create_file("fwlog", 0400, -- dir, isys, &isys_fw_log_fops); -- if (IS_ERR(file)) -- goto err; -- -- isys->debugfsdir = dir; -- -- return 0; --err: -- debugfs_remove_recursive(dir); -- return -ENOMEM; --} --#endif -- --static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) --{ -- struct ipu7_bus_device *adev = isys->adev; -- struct isys_fw_msgs *addr; -- dma_addr_t dma_addr; -- unsigned long flags; -- unsigned int i; -- -- for (i = 0; i < amount; i++) { -- addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs), -- &dma_addr, GFP_KERNEL, 0); -- if (!addr) -- break; -- addr->dma_addr = dma_addr; -- -- spin_lock_irqsave(&isys->listlock, flags); -- list_add(&addr->head, &isys->framebuflist); -- spin_unlock_irqrestore(&isys->listlock, flags); -- } -- -- if (i == amount) -- return 0; -- -- spin_lock_irqsave(&isys->listlock, flags); -- while (!list_empty(&isys->framebuflist)) { -- addr = list_first_entry(&isys->framebuflist, -- struct isys_fw_msgs, head); -- list_del(&addr->head); -- spin_unlock_irqrestore(&isys->listlock, flags); -- ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -- addr, addr->dma_addr, 0); -- spin_lock_irqsave(&isys->listlock, flags); -- } -- spin_unlock_irqrestore(&isys->listlock, flags); -- -- return -ENOMEM; --} -- --struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) --{ -- struct device *dev = &stream->isys->adev->auxdev.dev; -- struct ipu7_isys *isys = stream->isys; -- struct isys_fw_msgs *msg; -- unsigned long flags; -- int ret; -- -- spin_lock_irqsave(&isys->listlock, flags); -- if (list_empty(&isys->framebuflist)) { -- spin_unlock_irqrestore(&isys->listlock, flags); -- dev_dbg(dev, "Frame buffer list empty\n"); -- -- ret = alloc_fw_msg_bufs(isys, 5); -- if (ret < 0) -- return NULL; -- -- spin_lock_irqsave(&isys->listlock, flags); -- if (list_empty(&isys->framebuflist)) { -- spin_unlock_irqrestore(&isys->listlock, flags); -- dev_err(dev, "Frame list empty\n"); -- return NULL; -- } -- } -- msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); -- list_move(&msg->head, &isys->framebuflist_fw); -- spin_unlock_irqrestore(&isys->listlock, flags); -- memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); -- -- return msg; --} -- --void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys) --{ -- struct isys_fw_msgs *fwmsg, *fwmsg0; -- unsigned long flags; -- -- spin_lock_irqsave(&isys->listlock, flags); -- list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) -- list_move(&fwmsg->head, &isys->framebuflist); -- spin_unlock_irqrestore(&isys->listlock, flags); --} -- --void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data) --{ -- struct isys_fw_msgs *msg; -- void *ptr = (void *)data; -- unsigned long flags; -- -- if (WARN_ON_ONCE(!ptr)) -- return; -- -- spin_lock_irqsave(&isys->listlock, flags); -- msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); -- list_move(&msg->head, &isys->framebuflist); -- spin_unlock_irqrestore(&isys->listlock, flags); --} -- --static int isys_probe(struct auxiliary_device *auxdev, -- const struct auxiliary_device_id *auxdev_id) --{ -- const struct ipu7_isys_internal_csi2_pdata *csi2_pdata; -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- struct ipu7_device *isp = adev->isp; -- struct ipu7_isys *isys; -- int ret = 0; -- -- if (!isp->ipu7_bus_ready_to_probe) -- return -EPROBE_DEFER; -- -- isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); -- if (!isys) -- return -ENOMEM; -- -- ret = pm_runtime_resume_and_get(&auxdev->dev); -- if (ret < 0) -- return ret; -- -- adev->auxdrv_data = -- (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -- adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); -- isys->adev = adev; -- isys->pdata = adev->pdata; -- -- INIT_LIST_HEAD(&isys->requests); -- csi2_pdata = &isys->pdata->ipdata->csi2; -- -- isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports, -- sizeof(*isys->csi2), GFP_KERNEL); -- if (!isys->csi2) { -- ret = -ENOMEM; -- goto out_runtime_put; -- } -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- goto out_runtime_put; -- -- spin_lock_init(&isys->streams_lock); -- spin_lock_init(&isys->power_lock); -- isys->power = 0; -- -- mutex_init(&isys->mutex); -- mutex_init(&isys->stream_mutex); --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_init(&isys->reset_mutex); -- isys->state = 0; --#endif -- -- spin_lock_init(&isys->listlock); -- INIT_LIST_HEAD(&isys->framebuflist); -- INIT_LIST_HEAD(&isys->framebuflist_fw); -- -- dev_set_drvdata(&auxdev->dev, isys); -- -- isys->icache_prefetch = 0; -- isys->phy_rext_cal = 0; -- -- isys_stream_init(isys); -- --#ifdef CONFIG_DEBUG_FS -- /* Debug fs failure is not fatal. */ -- ipu7_isys_init_debugfs(isys); --#endif -- -- cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -- ret = alloc_fw_msg_bufs(isys, 40); -- if (ret < 0) -- goto out_cleanup_isys; -- -- ret = ipu7_fw_isys_init(isys); -- if (ret) -- goto out_cleanup_isys; -- -- ret = isys_register_devices(isys); -- if (ret) -- goto out_cleanup_fw; -- -- ret = isys_fw_log_init(isys); -- if (ret) -- goto out_cleanup; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- mutex_destroy(&isys->reset_mutex); --#endif -- ipu7_mmu_hw_cleanup(adev->mmu); -- pm_runtime_put(&auxdev->dev); -- -- return 0; -- --out_cleanup: -- isys_unregister_devices(isys); --out_cleanup_fw: -- ipu7_fw_isys_release(isys); --out_cleanup_isys: -- cpu_latency_qos_remove_request(&isys->pm_qos); -- -- for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -- mutex_destroy(&isys->streams[i].mutex); -- -- mutex_destroy(&isys->mutex); -- mutex_destroy(&isys->stream_mutex); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- --out_runtime_put: -- pm_runtime_put(&auxdev->dev); -- -- return ret; --} -- --struct ipu7_csi2_error { -- const char *error_string; -- bool is_info_only; --}; -- --/* -- * Strings corresponding to CSI-2 receiver errors are here. -- * Corresponding macros are defined in the header file. -- */ --static const struct ipu7_csi2_error dphy_rx_errors[] = { -- { "Error handler FIFO full", false }, -- { "Reserved Short Packet encoding detected", true }, -- { "Reserved Long Packet encoding detected", true }, -- { "Received packet is too short", false}, -- { "Received packet is too long", false}, -- { "Short packet discarded due to errors", false }, -- { "Long packet discarded due to errors", false }, -- { "CSI Combo Rx interrupt", false }, -- { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false }, -- { "Received NULL packet", true }, -- { "Received blanking packet", true }, -- { "Tie to 0", true }, -- { } --}; -- --static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2) --{ -- u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -- u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS); -- u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -- -- if (!status) -- return; -- -- dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n", -- csi2->port, status); -- -- writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR); -- csi2->receiver_errors |= status & mask; --} -- --static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2) --{ -- struct ipu7_csi2_error const *errors; -- unsigned int i; -- u32 status; -- -- /* Register errors once more in case of error interrupts are disabled */ -- ipu7_isys_register_errors(csi2); -- status = csi2->receiver_errors; -- csi2->receiver_errors = 0; -- errors = dphy_rx_errors; -- -- for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { -- if (status & BIT(i)) -- dev_err_ratelimited(&csi2->isys->adev->auxdev.dev, -- "csi2-%i error: %s\n", -- csi2->port, -- errors[i].error_string); -- } --} -- --struct resp_to_msg { -- enum ipu7_insys_resp_type type; -- const char *msg; --}; -- --static const struct resp_to_msg is_fw_msg[] = { -- {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE, -- "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"}, -- {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"}, -- {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, -- "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"}, -- {IPU_INSYS_RESP_TYPE_PIN_DATA_READY, -- "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"}, -- {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"}, -- {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"}, -- {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, -- "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"}, -- {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, -- "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"}, -- {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"}, --}; -- --int isys_isr_one(struct ipu7_bus_device *adev) --{ -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- struct ipu7_isys_stream *stream = NULL; -- struct device *dev = &adev->auxdev.dev; -- struct ipu7_isys_csi2 *csi2 = NULL; -- struct ia_gofo_msg_err err_info; -- struct ipu7_insys_resp *resp; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- struct ipu7_isys_tpg *tpg = NULL; --#endif -- u64 ts; -- -- if (!isys->adev->syscom) -- return 1; -- --#ifdef ENABLE_FW_OFFLINE_LOGGER -- ipu7_fw_isys_get_log(isys); --#endif -- -- resp = ipu7_fw_isys_get_resp(isys); -- if (!resp) -- return 1; -- if (resp->type >= N_IPU_INSYS_RESP_TYPE) { -- dev_err(dev, "Unknown response type %u stream %u\n", -- resp->type, resp->stream_id); -- ipu7_fw_isys_put_resp(isys); -- return 1; -- } -- -- err_info = resp->error_info; -- ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0]; -- if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE && -- err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) { -- /* receive a sp w/o command, firmware drop it */ -- dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n", -- resp->type, is_fw_msg[resp->type].msg, -- resp->stream_id); -- dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n", -- resp->pin_id, resp->buf_id, resp->frame_id); -- dev_dbg(dev, "\terror group %u code %u details [%u %u]\n", -- err_info.err_group, err_info.err_code, -- err_info.err_detail[0], err_info.err_detail[1]); -- } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) { -- dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -- resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -- resp->pin_id, resp->buf_id, resp->frame_id); -- dev_err(dev, "\terror group %u code %u details [%u %u]\n", -- err_info.err_group, err_info.err_code, -- err_info.err_detail[0], err_info.err_detail[1]); -- } else { -- dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -- resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -- resp->pin_id, resp->buf_id, resp->frame_id); -- dev_dbg(dev, "\tts %llu\n", ts); -- } -- -- if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) { -- dev_err(dev, "bad stream handle %u\n", -- resp->stream_id); -- goto leave; -- } -- -- stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id); -- if (!stream) { -- dev_err(dev, "stream of stream_handle %u is unused\n", -- resp->stream_id); -- goto leave; -- } -- -- stream->error = err_info.err_code; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (stream->asd) { -- if (stream->asd->is_tpg) -- tpg = ipu7_isys_subdev_to_tpg(stream->asd); -- else -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -- } --#else -- if (stream->asd) -- csi2 = ipu7_isys_subdev_to_csi2(stream->asd); --#endif -- -- switch (resp->type) { -- case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE: -- complete(&stream->stream_open_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK: -- complete(&stream->stream_close_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: -- complete(&stream->stream_start_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK: -- complete(&stream->stream_stop_completion); -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK: -- complete(&stream->stream_stop_completion); -- break; -- case IPU_INSYS_RESP_TYPE_PIN_DATA_READY: -- /* -- * firmware only release the capture msg until software -- * get pin_data_ready event -- */ -- ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id); -- if (resp->pin_id < IPU_INSYS_OUTPUT_PINS && -- stream->output_pins[resp->pin_id].pin_ready) -- stream->output_pins[resp->pin_id].pin_ready(stream, -- resp); -- else -- dev_err(dev, "No handler for pin %u ready\n", -- resp->pin_id); -- if (csi2) -- ipu7_isys_csi2_error(csi2); -- -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK: -- break; -- case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: -- case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE: -- break; -- case IPU_INSYS_RESP_TYPE_FRAME_SOF: -- if (csi2) -- ipu7_isys_csi2_sof_event_by_stream(stream); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (tpg) -- ipu7_isys_tpg_sof_event_by_stream(stream); -- --#endif -- stream->seq[stream->seq_index].sequence = -- atomic_read(&stream->sequence) - 1U; -- stream->seq[stream->seq_index].timestamp = ts; -- dev_dbg(dev, -- "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n", -- resp->stream_id, resp->frame_id, -- stream->seq[stream->seq_index].sequence, ts); -- stream->seq_index = (stream->seq_index + 1U) -- % IPU_ISYS_MAX_PARALLEL_SOF; -- break; -- case IPU_INSYS_RESP_TYPE_FRAME_EOF: -- if (csi2) -- ipu7_isys_csi2_eof_event_by_stream(stream); -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- if (tpg) -- ipu7_isys_tpg_eof_event_by_stream(stream); -- --#endif -- dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n", -- resp->stream_id, -- stream->seq[stream->seq_index].sequence, ts); -- break; -- default: -- dev_err(dev, "Unknown response type %u stream %u\n", -- resp->type, resp->stream_id); -- break; -- } -- -- ipu7_isys_put_stream(stream); --leave: -- ipu7_fw_isys_put_resp(isys); -- -- return 0; --} -- --static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2) --{ -- struct device *dev = &csi2->isys->adev->auxdev.dev; -- struct ipu7_device *isp = csi2->isys->adev->isp; -- struct ipu7_isys_stream *s; -- u32 sync, offset; -- u32 fe = 0; -- u8 vc; -- -- ipu7_isys_register_errors(csi2); -- -- offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -- sync = readl(csi2->base + offset + IRQ_CTL_STATUS); -- writel(sync, csi2->base + offset + IRQ_CTL_CLEAR); -- dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync); -- -- if (!is_ipu7(isp->hw_ver)) { -- fe = readl(csi2->base + offset + IRQ1_CTL_STATUS); -- writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR); -- dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe); -- } -- -- for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) { -- s = ipu7_isys_query_stream_by_source(csi2->isys, -- csi2->asd.source, vc); -- if (!s) -- continue; -- -- if (!is_ipu7(isp->hw_ver)) { -- if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc)) -- ipu7_isys_csi2_sof_event_by_stream(s); -- -- if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc)) -- ipu7_isys_csi2_eof_event_by_stream(s); -- } else { -- if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2))) -- ipu7_isys_csi2_sof_event_by_stream(s); -- -- if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2))) -- ipu7_isys_csi2_eof_event_by_stream(s); -- } -- } --} -- --static irqreturn_t isys_isr(struct ipu7_bus_device *adev) --{ -- struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -- u32 status_csi, status_sw, csi_offset, sw_offset; -- struct device *dev = &isys->adev->auxdev.dev; -- void __iomem *base = isys->pdata->base; -- -- spin_lock(&isys->power_lock); -- if (!isys->power) { -- spin_unlock(&isys->power_lock); -- return IRQ_NONE; -- } -- -- csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -- sw_offset = IS_BASE; -- -- status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -- status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -- if (!status_csi && !status_sw) { -- spin_unlock(&isys->power_lock); -- return IRQ_NONE; -- } -- -- if (status_csi) -- dev_dbg(dev, "status csi 0x%08x\n", status_csi); -- if (status_sw) -- dev_dbg(dev, "status to_sw 0x%08x\n", status_sw); -- -- do { -- writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR); -- writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR); -- -- if (isys->isr_csi2_mask & status_csi) { -- unsigned int i; -- -- for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { -- /* irq from not enabled port */ -- if (!isys->csi2[i].base) -- continue; -- if (status_csi & isys->csi2[i].legacy_irq_mask) -- ipu7_isys_csi2_isr(&isys->csi2[i]); -- } -- } -- -- if (!isys_isr_one(adev)) -- status_sw = TO_SW_IRQ_FW; -- else -- status_sw = 0; -- -- status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -- status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -- } while ((status_csi & isys->isr_csi2_mask) || -- (status_sw & TO_SW_IRQ_FW)); -- -- writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N); -- -- spin_unlock(&isys->power_lock); -- -- return IRQ_HANDLED; --} -- --static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = { -- .isr = isys_isr, -- .isr_threaded = NULL, -- .wake_isr_thread = false, --}; -- --static const struct auxiliary_device_id ipu7_isys_id_table[] = { -- { -- .name = "intel_ipu7.isys", -- .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data, -- }, -- { } --}; --MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table); -- --static struct auxiliary_driver isys_driver = { -- .name = IPU_ISYS_NAME, -- .probe = isys_probe, -- .remove = isys_remove, -- .id_table = ipu7_isys_id_table, -- .driver = { -- .pm = &isys_pm_ops, -- }, --}; -- --module_auxiliary_driver(isys_driver); -- --MODULE_AUTHOR("Bingbu Cao "); --MODULE_AUTHOR("Tianshu Qiu "); --MODULE_AUTHOR("Qingwu Zhang "); --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION("Intel ipu7 input system driver"); --MODULE_IMPORT_NS("INTEL_IPU7"); --MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.h b/drivers/media/pci/intel/ipu7/ipu7-isys.h -deleted file mode 100644 -index c9ca2fbb4d1e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys.h -+++ /dev/null -@@ -1,187 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_ISYS_H --#define IPU7_ISYS_H -- --#include --#include --#include --#include --#include --#include -- --#include --#include --#include --#include -- --#include "abi/ipu7_fw_msg_abi.h" --#include "abi/ipu7_fw_isys_abi.h" -- --#include "ipu7.h" --#include "ipu7-isys-csi2.h" --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --#include "ipu7-isys-tpg.h" --#endif --#include "ipu7-isys-video.h" -- --#ifdef CONFIG_DEBUG_FS --struct dentry; -- --#endif --#define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" -- --/* FW support max 16 streams */ --#define IPU_ISYS_MAX_STREAMS 16U -- --/* -- * Current message queue configuration. These must be big enough -- * so that they never gets full. Queues are located in system memory -- */ --#define IPU_ISYS_SIZE_RECV_QUEUE 40U --#define IPU_ISYS_SIZE_LOG_QUEUE 256U --#define IPU_ISYS_SIZE_SEND_QUEUE 40U --#define IPU_ISYS_NUM_RECV_QUEUE 1U -- --#define IPU_ISYS_MIN_WIDTH 2U --#define IPU_ISYS_MIN_HEIGHT 2U --#define IPU_ISYS_MAX_WIDTH 8160U --#define IPU_ISYS_MAX_HEIGHT 8190U -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#define RESET_STATE_IN_RESET 1U --#define RESET_STATE_IN_STOP_STREAMING 2U -- --#endif --#define FW_CALL_TIMEOUT_JIFFIES \ -- msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET --#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) --#endif -- --struct isys_fw_log { -- struct mutex mutex; /* protect whole struct */ -- void *head; -- void *addr; -- u32 count; /* running counter of log */ -- u32 size; /* actual size of log content, in bits */ --}; -- --/* -- * struct ipu7_isys -- * -- * @media_dev: Media device -- * @v4l2_dev: V4L2 device -- * @adev: ISYS bus device -- * @power: Is ISYS powered on or not? -- * @isr_bits: Which bits does the ISR handle? -- * @power_lock: Serialise access to power (power state in general) -- * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers -- * @streams_lock: serialise access to streams -- * @streams: streams per firmware stream ID -- * @syscom: fw communication layer context -- #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- * @need_reset: Isys requires d0i0->i3 transition -- #endif -- * @ref_count: total number of callers fw open -- * @mutex: serialise access isys video open/release related operations -- * @stream_mutex: serialise stream start and stop, queueing requests -- * @pdata: platform data pointer -- * @csi2: CSI-2 receivers -- #ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- * @tpg: test pattern generators -- #endif -- */ --struct ipu7_isys { -- struct media_device media_dev; -- struct v4l2_device v4l2_dev; -- struct ipu7_bus_device *adev; -- -- int power; -- spinlock_t power_lock; /* Serialise access to power */ -- u32 isr_csi2_mask; -- u32 csi2_rx_ctrl_cached; -- spinlock_t streams_lock; -- struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS]; -- int streams_ref_count[IPU_ISYS_MAX_STREAMS]; -- u32 phy_rext_cal; -- bool icache_prefetch; -- bool csi2_cse_ipc_not_supported; -- unsigned int ref_count; -- unsigned int stream_opened; -- --#ifdef CONFIG_DEBUG_FS -- struct dentry *debugfsdir; --#endif -- struct mutex mutex; /* Serialise isys video open/release related */ -- struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ -- -- struct ipu7_isys_pdata *pdata; -- -- struct ipu7_isys_csi2 *csi2; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- struct ipu7_isys_tpg *tpg; --#endif -- struct isys_fw_log *fw_log; -- -- struct list_head requests; -- struct pm_qos_request pm_qos; -- spinlock_t listlock; /* Protect framebuflist */ -- struct list_head framebuflist; -- struct list_head framebuflist_fw; -- struct v4l2_async_notifier notifier; -- -- struct ipu7_insys_config *subsys_config; -- dma_addr_t subsys_config_dma_addr; --#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -- struct mutex reset_mutex; -- bool need_reset; -- int state; --#endif --}; -- --struct isys_fw_msgs { -- union { -- u64 dummy; -- struct ipu7_insys_buffset frame; -- struct ipu7_insys_stream_cfg stream; -- } fw_msg; -- struct list_head head; -- dma_addr_t dma_addr; --}; -- --struct ipu7_isys_csi2_config { -- unsigned int nlanes; -- unsigned int port; -- enum v4l2_mbus_type bus_type; --}; -- --struct ipu7_isys_subdev_i2c_info { -- struct i2c_board_info board_info; -- int i2c_adapter_id; -- char i2c_adapter_bdf[32]; --}; -- --struct ipu7_isys_subdev_info { -- struct ipu7_isys_csi2_config *csi2; -- struct ipu7_isys_subdev_i2c_info i2c; --}; -- --struct ipu7_isys_subdev_pdata { -- struct ipu7_isys_subdev_info **subdevs; --}; -- --struct sensor_async_sd { -- struct v4l2_async_connection asc; -- struct ipu7_isys_csi2_config csi2; --}; -- --struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream); --void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data); --void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys); --int isys_isr_one(struct ipu7_bus_device *adev); --void ipu7_isys_setup_hw(struct ipu7_isys *isys); --#endif /* IPU7_ISYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.c b/drivers/media/pci/intel/ipu7/ipu7-mmu.c -deleted file mode 100644 -index e6989e3e59a1..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-mmu.c -+++ /dev/null -@@ -1,854 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-dma.h" --#include "ipu7-mmu.h" --#include "ipu7-platform-regs.h" -- --#define ISP_PAGE_SHIFT 12 --#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) --#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1U)) -- --#define ISP_L1PT_SHIFT 22 --#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) -- --#define ISP_L2PT_SHIFT 12 --#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) -- --#define ISP_L1PT_PTES 1024U --#define ISP_L2PT_PTES 1024U -- --#define ISP_PADDR_SHIFT 12 -- --#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ --#define REG_INFO 0x0008 -- --#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) -- --#define MMU_TLB_INVALIDATE_TIMEOUT 2000 -- --static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) --{ -- unsigned int i; -- u32 irq_cause; -- -- for (i = 0; i < mmu->nr_mmus; i++) { -- irq_cause = readl(mmu->mmu_hw[i].base + MMU_REG_IRQ_CAUSE); -- pr_info("mmu %s irq_cause = 0x%x", mmu->mmu_hw[i].name, -- irq_cause); -- writel(0x1ffff, mmu->mmu_hw[i].base + MMU_REG_IRQ_CLEAR); -- } --} -- --static void tlb_invalidate(struct ipu7_mmu *mmu) --{ -- unsigned long flags; -- unsigned int i; -- int ret; -- u32 val; -- -- spin_lock_irqsave(&mmu->ready_lock, flags); -- if (!mmu->ready) { -- spin_unlock_irqrestore(&mmu->ready_lock, flags); -- return; -- } -- -- for (i = 0; i < mmu->nr_mmus; i++) { -- writel(0xffffffffU, mmu->mmu_hw[i].base + -- MMU_REG_INVALIDATE_0); -- -- /* Need check with HW, use l1streams or l2streams */ -- if (mmu->mmu_hw[i].nr_l2streams > 32) -- writel(0xffffffffU, mmu->mmu_hw[i].base + -- MMU_REG_INVALIDATE_1); -- -- /* -- * The TLB invalidation is a "single cycle" (IOMMU clock cycles) -- * When the actual MMIO write reaches the IPU TLB Invalidate -- * register, wmb() will force the TLB invalidate out if the CPU -- * attempts to update the IOMMU page table (or sooner). -- */ -- wmb(); -- -- /* wait invalidation done */ -- ret = readl_poll_timeout_atomic(mmu->mmu_hw[i].base + -- MMU_REG_INVALIDATION_STATUS, -- val, !(val & 0x1U), 500, -- MMU_TLB_INVALIDATE_TIMEOUT); -- if (ret) -- dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i); -- } -- -- spin_unlock_irqrestore(&mmu->ready_lock, flags); --} -- --static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr) --{ -- dma_addr_t dma; -- -- dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); -- if (dma_mapping_error(mmu_info->dev, dma)) -- return 0; -- -- return dma; --} -- --static int get_dummy_page(struct ipu7_mmu_info *mmu_info) --{ -- void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- dma_addr_t dma; -- -- if (!pt) -- return -ENOMEM; -- -- dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); -- -- dma = map_single(mmu_info, pt); -- if (!dma) { -- dev_err(mmu_info->dev, "Failed to map dummy page\n"); -- goto err_free_page; -- } -- -- mmu_info->dummy_page = pt; -- mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; -- -- return 0; -- --err_free_page: -- free_page((unsigned long)pt); -- return -ENOMEM; --} -- --static void free_dummy_page(struct ipu7_mmu_info *mmu_info) --{ -- dma_unmap_single(mmu_info->dev, -- TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->dummy_page); --} -- --static int alloc_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) --{ -- u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- dma_addr_t dma; -- unsigned int i; -- -- if (!pt) -- return -ENOMEM; -- -- dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); -- -- dma = map_single(mmu_info, pt); -- if (!dma) { -- dev_err(mmu_info->dev, "Failed to map l2pt page\n"); -- goto err_free_page; -- } -- -- for (i = 0; i < ISP_L2PT_PTES; i++) -- pt[i] = mmu_info->dummy_page_pteval; -- -- mmu_info->dummy_l2_pt = pt; -- mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; -- -- return 0; -- --err_free_page: -- free_page((unsigned long)pt); -- return -ENOMEM; --} -- --static void free_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) --{ -- dma_unmap_single(mmu_info->dev, -- TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->dummy_l2_pt); --} -- --static u32 *alloc_l1_pt(struct ipu7_mmu_info *mmu_info) --{ -- u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- dma_addr_t dma; -- unsigned int i; -- -- if (!pt) -- return NULL; -- -- dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); -- -- for (i = 0; i < ISP_L1PT_PTES; i++) -- pt[i] = mmu_info->dummy_l2_pteval; -- -- dma = map_single(mmu_info, pt); -- if (!dma) { -- dev_err(mmu_info->dev, "Failed to map l1pt page\n"); -- goto err_free_page; -- } -- -- mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; -- dev_dbg(mmu_info->dev, "l1 pt %p mapped at %pad\n", pt, &dma); -- -- return pt; -- --err_free_page: -- free_page((unsigned long)pt); -- return NULL; --} -- --static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info) --{ -- u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -- unsigned int i; -- -- if (!pt) -- return NULL; -- -- dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); -- -- for (i = 0; i < ISP_L1PT_PTES; i++) -- pt[i] = mmu_info->dummy_page_pteval; -- -- return pt; --} -- --static void l2_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t dummy, size_t size) --{ -- unsigned int l2_entries; -- unsigned int l2_idx; -- unsigned long flags; -- u32 l1_idx; -- u32 *l2_pt; -- -- spin_lock_irqsave(&mmu_info->lock, flags); -- for (l1_idx = iova >> ISP_L1PT_SHIFT; -- size > 0U && l1_idx < ISP_L1PT_PTES; l1_idx++) { -- dev_dbg(mmu_info->dev, -- "unmapping l2 pgtable (l1 index %u (iova 0x%8.8lx))\n", -- l1_idx, iova); -- -- if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { -- dev_err(mmu_info->dev, -- "unmap not mapped iova 0x%8.8lx l1 index %u\n", -- iova, l1_idx); -- continue; -- } -- l2_pt = mmu_info->l2_pts[l1_idx]; -- -- l2_entries = 0; -- for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -- size > 0U && l2_idx < ISP_L2PT_PTES; l2_idx++) { -- phys_addr_t pteval = TBL_PHYS_ADDR(l2_pt[l2_idx]); -- -- dev_dbg(mmu_info->dev, -- "unmap l2 index %u with pteval 0x%p\n", -- l2_idx, &pteval); -- l2_pt[l2_idx] = mmu_info->dummy_page_pteval; -- -- iova += ISP_PAGE_SIZE; -- size -= ISP_PAGE_SIZE; -- -- l2_entries++; -- } -- -- WARN_ON_ONCE(!l2_entries); -- clflush_cache_range(&l2_pt[l2_idx - l2_entries], -- sizeof(l2_pt[0]) * l2_entries); -- } -- -- WARN_ON_ONCE(size); -- spin_unlock_irqrestore(&mmu_info->lock, flags); --} -- --static int l2_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size) --{ -- struct device *dev = mmu_info->dev; -- unsigned int l2_entries; -- u32 *l2_pt, *l2_virt; -- unsigned int l2_idx; -- unsigned long flags; -- size_t mapped = 0; -- dma_addr_t dma; -- u32 l1_entry; -- u32 l1_idx; -- int err = 0; -- -- spin_lock_irqsave(&mmu_info->lock, flags); -- -- paddr = ALIGN(paddr, ISP_PAGE_SIZE); -- for (l1_idx = iova >> ISP_L1PT_SHIFT; -- size && l1_idx < ISP_L1PT_PTES; l1_idx++) { -- dev_dbg(dev, -- "mapping l2 page table for l1 index %u (iova %8.8x)\n", -- l1_idx, (u32)iova); -- -- l1_entry = mmu_info->l1_pt[l1_idx]; -- if (l1_entry == mmu_info->dummy_l2_pteval) { -- l2_virt = mmu_info->l2_pts[l1_idx]; -- if (likely(!l2_virt)) { -- l2_virt = alloc_l2_pt(mmu_info); -- if (!l2_virt) { -- err = -ENOMEM; -- goto error; -- } -- } -- -- dma = map_single(mmu_info, l2_virt); -- if (!dma) { -- dev_err(dev, "Failed to map l2pt page\n"); -- free_page((unsigned long)l2_virt); -- err = -EINVAL; -- goto error; -- } -- -- l1_entry = dma >> ISP_PADDR_SHIFT; -- -- dev_dbg(dev, "page for l1_idx %u %p allocated\n", -- l1_idx, l2_virt); -- mmu_info->l1_pt[l1_idx] = l1_entry; -- mmu_info->l2_pts[l1_idx] = l2_virt; -- -- clflush_cache_range(&mmu_info->l1_pt[l1_idx], -- sizeof(mmu_info->l1_pt[l1_idx])); -- } -- -- l2_pt = mmu_info->l2_pts[l1_idx]; -- l2_entries = 0; -- -- for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -- size && l2_idx < ISP_L2PT_PTES; l2_idx++) { -- l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; -- -- dev_dbg(dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, -- l2_pt[l2_idx]); -- -- iova += ISP_PAGE_SIZE; -- paddr += ISP_PAGE_SIZE; -- mapped += ISP_PAGE_SIZE; -- size -= ISP_PAGE_SIZE; -- -- l2_entries++; -- } -- -- WARN_ON_ONCE(!l2_entries); -- clflush_cache_range(&l2_pt[l2_idx - l2_entries], -- sizeof(l2_pt[0]) * l2_entries); -- } -- -- spin_unlock_irqrestore(&mmu_info->lock, flags); -- -- return 0; -- --error: -- spin_unlock_irqrestore(&mmu_info->lock, flags); -- /* unroll mapping in case something went wrong */ -- if (mapped) -- l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped); -- -- return err; --} -- --static int __ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size) --{ -- u32 iova_start = round_down(iova, ISP_PAGE_SIZE); -- u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); -- -- dev_dbg(mmu_info->dev, -- "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr %pap\n", -- iova_start, iova_end, size, &paddr); -- -- return l2_map(mmu_info, iova_start, paddr, size); --} -- --static void __ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, -- unsigned long iova, size_t size) --{ -- l2_unmap(mmu_info, iova, 0, size); --} -- --static int allocate_trash_buffer(struct ipu7_mmu *mmu) --{ -- unsigned int n_pages = PFN_UP(IPU_MMUV2_TRASH_RANGE); -- unsigned long iova_addr; -- struct iova *iova; -- unsigned int i; -- dma_addr_t dma; -- int ret; -- -- /* Allocate 8MB in iova range */ -- iova = alloc_iova(&mmu->dmap->iovad, n_pages, -- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -- if (!iova) { -- dev_err(mmu->dev, "cannot allocate iova range for trash\n"); -- return -ENOMEM; -- } -- -- dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { -- dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); -- ret = -ENOMEM; -- goto out_free_iova; -- } -- -- mmu->pci_trash_page = dma; -- -- /* -- * Map the 8MB iova address range to the same physical trash page -- * mmu->trash_page which is already reserved at the probe -- */ -- iova_addr = iova->pfn_lo; -- for (i = 0; i < n_pages; i++) { -- ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -- mmu->pci_trash_page, PAGE_SIZE); -- if (ret) { -- dev_err(mmu->dev, -- "mapping trash buffer range failed\n"); -- goto out_unmap; -- } -- -- iova_addr++; -- } -- -- mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); -- dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", -- mmu->mmid, (unsigned int)mmu->iova_trash_page); -- return 0; -- --out_unmap: -- ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, -- PAGE_SIZE, DMA_BIDIRECTIONAL); --out_free_iova: -- __free_iova(&mmu->dmap->iovad, iova); -- return ret; --} -- --static void __mmu_at_init(struct ipu7_mmu *mmu) --{ -- struct ipu7_mmu_info *mmu_info; -- unsigned int i; -- -- mmu_info = mmu->dmap->mmu_info; -- for (i = 0; i < mmu->nr_mmus; i++) { -- struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -- unsigned int j; -- -- /* Write page table address per MMU */ -- writel((phys_addr_t)mmu_info->l1_pt_dma, -- mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR); -- dev_dbg(mmu->dev, "mmu %s base was set as %x\n", mmu_hw->name, -- readl(mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR)); -- -- /* Set info bits and axi_refill per MMU */ -- writel(mmu_hw->info_bits, -- mmu_hw->base + MMU_REG_USER_INFO_BITS); -- writel(mmu_hw->refill, mmu_hw->base + MMU_REG_AXI_REFILL_IF_ID); -- writel(mmu_hw->collapse_en_bitmap, -- mmu_hw->base + MMU_REG_COLLAPSE_ENABLE_BITMAP); -- -- dev_dbg(mmu->dev, "mmu %s info_bits was set as %x\n", -- mmu_hw->name, -- readl(mmu_hw->base + MMU_REG_USER_INFO_BITS)); -- -- if (mmu_hw->at_sp_arb_cfg) -- writel(mmu_hw->at_sp_arb_cfg, -- mmu_hw->base + MMU_REG_AT_SP_ARB_CFG); -- -- /* default irq configuration */ -- writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_MASK); -- writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_ENABLE); -- -- /* Configure MMU TLB stream configuration for L1/L2 */ -- for (j = 0; j < mmu_hw->nr_l1streams; j++) { -- writel(mmu_hw->l1_block_sz[j], mmu_hw->base + -- mmu_hw->l1_block + 4U * j); -- } -- -- for (j = 0; j < mmu_hw->nr_l2streams; j++) { -- writel(mmu_hw->l2_block_sz[j], mmu_hw->base + -- mmu_hw->l2_block + 4U * j); -- } -- -- for (j = 0; j < mmu_hw->uao_p_num; j++) { -- if (!mmu_hw->uao_p2tlb[j]) -- continue; -- writel(mmu_hw->uao_p2tlb[j], mmu_hw->uao_base + 4U * j); -- } -- } --} -- --static void __mmu_zlx_init(struct ipu7_mmu *mmu) --{ -- unsigned int i; -- -- dev_dbg(mmu->dev, "mmu zlx init\n"); -- -- for (i = 0; i < mmu->nr_mmus; i++) { -- struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -- unsigned int j; -- -- dev_dbg(mmu->dev, "mmu %s zlx init\n", mmu_hw->name); -- for (j = 0; j < IPU_ZLX_POOL_NUM; j++) { -- if (!mmu_hw->zlx_axi_pool[j]) -- continue; -- writel(mmu_hw->zlx_axi_pool[j], -- mmu_hw->zlx_base + ZLX_REG_AXI_POOL + j * 0x4U); -- } -- -- for (j = 0; j < mmu_hw->zlx_nr; j++) { -- if (!mmu_hw->zlx_conf[j]) -- continue; -- -- writel(mmu_hw->zlx_conf[j], -- mmu_hw->zlx_base + ZLX_REG_CONF + j * 0x8U); -- } -- -- for (j = 0; j < mmu_hw->zlx_nr; j++) { -- if (!mmu_hw->zlx_en[j]) -- continue; -- -- writel(mmu_hw->zlx_en[j], -- mmu_hw->zlx_base + ZLX_REG_EN + j * 0x8U); -- } -- } --} -- --int ipu7_mmu_hw_init(struct ipu7_mmu *mmu) --{ -- unsigned long flags; -- -- dev_dbg(mmu->dev, "IPU mmu hardware init\n"); -- -- /* Initialise the each MMU and ZLX */ -- __mmu_at_init(mmu); -- __mmu_zlx_init(mmu); -- -- if (!mmu->trash_page) { -- int ret; -- -- mmu->trash_page = alloc_page(GFP_KERNEL); -- if (!mmu->trash_page) { -- dev_err(mmu->dev, "insufficient memory for trash buffer\n"); -- return -ENOMEM; -- } -- -- ret = allocate_trash_buffer(mmu); -- if (ret) { -- __free_page(mmu->trash_page); -- mmu->trash_page = NULL; -- dev_err(mmu->dev, "trash buffer allocation failed\n"); -- return ret; -- } -- } -- -- spin_lock_irqsave(&mmu->ready_lock, flags); -- mmu->ready = true; -- spin_unlock_irqrestore(&mmu->ready_lock, flags); -- -- return 0; --} --EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_init, "INTEL_IPU7"); -- --static struct ipu7_mmu_info *ipu7_mmu_alloc(struct ipu7_device *isp) --{ -- struct ipu7_mmu_info *mmu_info; -- int ret; -- -- mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); -- if (!mmu_info) -- return NULL; -- -- if (isp->secure_mode) { -- mmu_info->aperture_start = IPU_FW_CODE_REGION_END; -- mmu_info->aperture_end = -- (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS); -- } else { -- mmu_info->aperture_start = IPU_FW_CODE_REGION_START; -- mmu_info->aperture_end = -- (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS_NON_SECURE); -- } -- -- mmu_info->pgsize_bitmap = SZ_4K; -- mmu_info->dev = &isp->pdev->dev; -- -- ret = get_dummy_page(mmu_info); -- if (ret) -- goto err_free_info; -- -- ret = alloc_dummy_l2_pt(mmu_info); -- if (ret) -- goto err_free_dummy_page; -- -- mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); -- if (!mmu_info->l2_pts) -- goto err_free_dummy_l2_pt; -- -- /* -- * We always map the L1 page table (a single page as well as -- * the L2 page tables). -- */ -- mmu_info->l1_pt = alloc_l1_pt(mmu_info); -- if (!mmu_info->l1_pt) -- goto err_free_l2_pts; -- -- spin_lock_init(&mmu_info->lock); -- -- dev_dbg(mmu_info->dev, "domain initialised\n"); -- -- return mmu_info; -- --err_free_l2_pts: -- vfree(mmu_info->l2_pts); --err_free_dummy_l2_pt: -- free_dummy_l2_pt(mmu_info); --err_free_dummy_page: -- free_dummy_page(mmu_info); --err_free_info: -- kfree(mmu_info); -- -- return NULL; --} -- --void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu) --{ -- unsigned long flags; -- -- spin_lock_irqsave(&mmu->ready_lock, flags); -- mmu->ready = false; -- spin_unlock_irqrestore(&mmu->ready_lock, flags); --} --EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_cleanup, "INTEL_IPU7"); -- --static struct ipu7_dma_mapping *alloc_dma_mapping(struct ipu7_device *isp) --{ -- struct ipu7_dma_mapping *dmap; -- unsigned long base_pfn; -- -- dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); -- if (!dmap) -- return NULL; -- -- dmap->mmu_info = ipu7_mmu_alloc(isp); -- if (!dmap->mmu_info) { -- kfree(dmap); -- return NULL; -- } -- -- /* 0~64M is forbidden for uctile controller */ -- base_pfn = max_t(unsigned long, 1, -- PFN_DOWN(dmap->mmu_info->aperture_start)); -- init_iova_domain(&dmap->iovad, SZ_4K, base_pfn); -- dmap->mmu_info->dmap = dmap; -- -- dev_dbg(&isp->pdev->dev, "alloc mapping\n"); -- -- iova_cache_get(); -- -- return dmap; --} -- --phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -- dma_addr_t iova) --{ -- phys_addr_t phy_addr; -- unsigned long flags; -- u32 *l2_pt; -- -- spin_lock_irqsave(&mmu_info->lock, flags); -- l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; -- phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; -- phy_addr <<= ISP_PAGE_SHIFT; -- spin_unlock_irqrestore(&mmu_info->lock, flags); -- -- return phy_addr; --} -- --void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- size_t size) --{ -- unsigned int min_pagesz; -- -- dev_dbg(mmu_info->dev, "unmapping iova 0x%lx size 0x%zx\n", iova, size); -- -- /* find out the minimum page size supported */ -- min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -- -- /* -- * The virtual address and the size of the mapping must be -- * aligned (at least) to the size of the smallest page supported -- * by the hardware -- */ -- if (!IS_ALIGNED(iova | size, min_pagesz)) { -- dev_err(mmu_info->dev, -- "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", -- iova, size, min_pagesz); -- return; -- } -- -- __ipu7_mmu_unmap(mmu_info, iova, size); --} -- --int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size) --{ -- unsigned int min_pagesz; -- -- if (mmu_info->pgsize_bitmap == 0UL) -- return -ENODEV; -- -- /* find out the minimum page size supported */ -- min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -- -- /* -- * both the virtual address and the physical one, as well as -- * the size of the mapping, must be aligned (at least) to the -- * size of the smallest page supported by the hardware -- */ -- if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { -- dev_err(mmu_info->dev, -- "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", -- iova, &paddr, size, min_pagesz); -- return -EINVAL; -- } -- -- dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", -- iova, &paddr, size); -- -- return __ipu7_mmu_map(mmu_info, iova, paddr, size); --} -- --static void ipu7_mmu_destroy(struct ipu7_mmu *mmu) --{ -- struct ipu7_dma_mapping *dmap = mmu->dmap; -- struct ipu7_mmu_info *mmu_info = dmap->mmu_info; -- struct iova *iova; -- u32 l1_idx; -- -- if (mmu->iova_trash_page) { -- iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); -- if (iova) { -- /* unmap and free the trash buffer iova */ -- ipu7_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), -- PFN_PHYS(iova_size(iova))); -- __free_iova(&dmap->iovad, iova); -- } else { -- dev_err(mmu->dev, "trash buffer iova not found.\n"); -- } -- -- mmu->iova_trash_page = 0; -- dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- mmu->pci_trash_page = 0; -- __free_page(mmu->trash_page); -- } -- -- for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { -- if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { -- dma_unmap_single(mmu_info->dev, -- TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->l2_pts[l1_idx]); -- } -- } -- -- vfree(mmu_info->l2_pts); -- free_dummy_page(mmu_info); -- dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), -- PAGE_SIZE, DMA_BIDIRECTIONAL); -- free_page((unsigned long)mmu_info->dummy_l2_pt); -- free_page((unsigned long)mmu_info->l1_pt); -- kfree(mmu_info); --} -- --struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -- void __iomem *base, int mmid, -- const struct ipu7_hw_variants *hw) --{ -- struct ipu7_device *isp = pci_get_drvdata(to_pci_dev(dev)); -- struct ipu7_mmu_pdata *pdata; -- struct ipu7_mmu *mmu; -- unsigned int i; -- -- if (hw->nr_mmus > IPU_MMU_MAX_NUM) -- return ERR_PTR(-EINVAL); -- -- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -- if (!pdata) -- return ERR_PTR(-ENOMEM); -- -- for (i = 0; i < hw->nr_mmus; i++) { -- struct ipu7_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; -- const struct ipu7_mmu_hw *src_mmu = &hw->mmu_hw[i]; -- -- if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS || -- src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS) -- return ERR_PTR(-EINVAL); -- -- *pdata_mmu = *src_mmu; -- pdata_mmu->base = base + src_mmu->offset; -- pdata_mmu->zlx_base = base + src_mmu->zlx_offset; -- pdata_mmu->uao_base = base + src_mmu->uao_offset; -- } -- -- mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); -- if (!mmu) -- return ERR_PTR(-ENOMEM); -- -- mmu->mmid = mmid; -- mmu->mmu_hw = pdata->mmu_hw; -- mmu->nr_mmus = hw->nr_mmus; -- mmu->tlb_invalidate = tlb_invalidate; -- mmu->ready = false; -- INIT_LIST_HEAD(&mmu->vma_list); -- spin_lock_init(&mmu->ready_lock); -- -- mmu->dmap = alloc_dma_mapping(isp); -- if (!mmu->dmap) { -- dev_err(dev, "can't alloc dma mapping\n"); -- return ERR_PTR(-ENOMEM); -- } -- -- return mmu; --} -- --void ipu7_mmu_cleanup(struct ipu7_mmu *mmu) --{ -- struct ipu7_dma_mapping *dmap = mmu->dmap; -- -- ipu7_mmu_destroy(mmu); -- mmu->dmap = NULL; -- iova_cache_put(); -- put_iova_domain(&dmap->iovad); -- kfree(dmap); --} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.h b/drivers/media/pci/intel/ipu7/ipu7-mmu.h -deleted file mode 100644 -index d85bb8ffc711..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-mmu.h -+++ /dev/null -@@ -1,414 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_MMU_H --#define IPU7_MMU_H -- --#include --#include --#include --#include -- --struct device; --struct page; --struct ipu7_hw_variants; --struct ipu7_mmu; --struct ipu7_mmu_info; -- --#define ISYS_MMID 0x1 --#define PSYS_MMID 0x0 -- --/* IPU7 for LNL */ --/* IS MMU Cmd RD */ --#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 --#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3 --#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Cmd WR */ --#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000 --#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3 --#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Data WR Snoop */ --#define IPU7_IS_MMU_M0_OFFSET 0x276000 --#define IPU7_IS_MMU_M0_STREAM_NUM 8 --#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74 -- --/* IS MMU Data WR ISOC */ --#define IPU7_IS_MMU_M1_OFFSET 0x277000 --#define IPU7_IS_MMU_M1_STREAM_NUM 16 --#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54 --#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -- --/* PS MMU FW RD */ --#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000 --#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20 --#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4 -- --/* PS MMU FW WR */ --#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000 --#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10 --#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -- --/* PS MMU FW Data RD VC0 */ --#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000 --#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40 --#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4 -- --/* PS MMU FW Data WR VC0 */ --#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000 --#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40 --#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 --#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4 -- --/* IS UAO UC RD */ --#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000 --#define IPU7_IS_UAO_UC_RD_PLANENUM 4 -- --/* IS UAO UC WR */ --#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000 --#define IPU7_IS_UAO_UC_WR_PLANENUM 4 -- --/* IS UAO M0 WR */ --#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000 --#define IPU7_IS_UAO_M0_WR_PLANENUM 8 -- --/* IS UAO M1 WR */ --#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000 --#define IPU7_IS_UAO_M1_WR_PLANENUM 16 -- --/* PS UAO FW RD */ --#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000 --#define IPU7_PS_UAO_FW_RD_PLANENUM 20 -- --/* PS UAO FW WR */ --#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000 --#define IPU7_PS_UAO_FW_WR_PLANENUM 16 -- --/* PS UAO SRT RD */ --#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000 --#define IPU7_PS_UAO_SRT_RD_PLANENUM 40 -- --/* PS UAO SRT WR */ --#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000 --#define IPU7_PS_UAO_SRT_WR_PLANENUM 40 -- --#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000 --#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000 --#define IPU7_IS_ZLX_M0_OFFSET 0x27a000 --#define IPU7_IS_ZLX_M1_OFFSET 0x27b000 --#define IPU7_IS_ZLX_UC_RD_NUM 4 --#define IPU7_IS_ZLX_UC_WR_NUM 4 --#define IPU7_IS_ZLX_M0_NUM 8 --#define IPU7_IS_ZLX_M1_NUM 16 -- --#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000 --#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000 --#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000 --#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000 --#define IPU7_PS_ZLX_DATA_RD_NUM 32 --#define IPU7_PS_ZLX_DATA_WR_NUM 32 --#define IPU7_PS_ZLX_FW_RD_NUM 16 --#define IPU7_PS_ZLX_FW_WR_NUM 10 -- --/* IPU7P5 for PTL */ --/* IS MMU Cmd RD */ --#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000 --#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3 --#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Cmd WR */ --#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000 --#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3 --#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Data WR Snoop */ --#define IPU7P5_IS_MMU_M0_OFFSET 0x276000 --#define IPU7P5_IS_MMU_M0_STREAM_NUM 16 --#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -- --/* IS MMU Data WR ISOC */ --#define IPU7P5_IS_MMU_M1_OFFSET 0x277000 --#define IPU7P5_IS_MMU_M1_STREAM_NUM 16 --#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54 --#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -- --/* PS MMU FW RD */ --#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000 --#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16 --#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94 -- --/* PS MMU FW WR */ --#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000 --#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10 --#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -- --/* PS MMU FW Data RD VC0 */ --#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000 --#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22 --#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac -- --/* PS MMU FW Data WR VC0 */ --#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000 --#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32 --#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 --#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4 -- --/* IS UAO UC RD */ --#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000 --#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4 -- --/* IS UAO UC WR */ --#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000 --#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4 -- --/* IS UAO M0 WR */ --#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000 --#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16 -- --/* IS UAO M1 WR */ --#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000 --#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16 -- --/* PS UAO FW RD */ --#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000 --#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16 -- --/* PS UAO FW WR */ --#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000 --#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10 -- --/* PS UAO SRT RD */ --#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000 --#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22 -- --/* PS UAO SRT WR */ --#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000 --#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32 -- --#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000 --#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000 --#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000 --#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000 --#define IPU7P5_IS_ZLX_UC_RD_NUM 4 --#define IPU7P5_IS_ZLX_UC_WR_NUM 4 --#define IPU7P5_IS_ZLX_M0_NUM 16 --#define IPU7P5_IS_ZLX_M1_NUM 16 -- --#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000 --#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000 --#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000 --#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000 --#define IPU7P5_PS_ZLX_DATA_RD_NUM 22 --#define IPU7P5_PS_ZLX_DATA_WR_NUM 32 --#define IPU7P5_PS_ZLX_FW_RD_NUM 16 --#define IPU7P5_PS_ZLX_FW_WR_NUM 10 -- --/* IS MMU Cmd RD */ --#define IPU8_IS_MMU_FW_RD_OFFSET 0x270000 --#define IPU8_IS_MMU_FW_RD_STREAM_NUM 3 --#define IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Cmd WR */ --#define IPU8_IS_MMU_FW_WR_OFFSET 0x271000 --#define IPU8_IS_MMU_FW_WR_STREAM_NUM 3 --#define IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -- --/* IS MMU Data WR Snoop */ --#define IPU8_IS_MMU_M0_OFFSET 0x272000 --#define IPU8_IS_MMU_M0_STREAM_NUM 16 --#define IPU8_IS_MMU_M0_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -- --/* IS MMU Data WR ISOC */ --#define IPU8_IS_MMU_M1_OFFSET 0x273000 --#define IPU8_IS_MMU_M1_STREAM_NUM 16 --#define IPU8_IS_MMU_M1_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -- --/* IS MMU UPIPE ISOC */ --#define IPU8_IS_MMU_UPIPE_OFFSET 0x274000 --#define IPU8_IS_MMU_UPIPE_STREAM_NUM 6 --#define IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG 0x54 --#define IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG 0x6c -- --/* PS MMU FW RD */ --#define IPU8_PS_MMU_FW_RD_OFFSET 0x148000 --#define IPU8_PS_MMU_FW_RD_STREAM_NUM 12 --#define IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x84 -- --/* PS MMU FW WR */ --#define IPU8_PS_MMU_FW_WR_OFFSET 0x149000 --#define IPU8_PS_MMU_FW_WR_STREAM_NUM 8 --#define IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x74 -- --/* PS MMU FW Data RD VC0 */ --#define IPU8_PS_MMU_SRT_RD_OFFSET 0x14a000 --#define IPU8_PS_MMU_SRT_RD_STREAM_NUM 26 --#define IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xbc -- --/* PS MMU FW Data WR VC0 */ --#define IPU8_PS_MMU_SRT_WR_OFFSET 0x14b000 --#define IPU8_PS_MMU_SRT_WR_STREAM_NUM 26 --#define IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 --#define IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xbc -- --/* IS UAO UC RD */ --#define IPU8_IS_UAO_UC_RD_OFFSET 0x27a000 --#define IPU8_IS_UAO_UC_RD_PLANENUM 4 -- --/* IS UAO UC WR */ --#define IPU8_IS_UAO_UC_WR_OFFSET 0x27b000 --#define IPU8_IS_UAO_UC_WR_PLANENUM 4 -- --/* IS UAO M0 WR */ --#define IPU8_IS_UAO_M0_WR_OFFSET 0x27c000 --#define IPU8_IS_UAO_M0_WR_PLANENUM 16 -- --/* IS UAO M1 WR */ --#define IPU8_IS_UAO_M1_WR_OFFSET 0x27d000 --#define IPU8_IS_UAO_M1_WR_PLANENUM 16 -- --/* IS UAO UPIPE */ --#define IPU8_IS_UAO_UPIPE_OFFSET 0x27e000 --#define IPU8_IS_UAO_UPIPE_PLANENUM 6 -- --/* PS UAO FW RD */ --#define IPU8_PS_UAO_FW_RD_OFFSET 0x156000 --#define IPU8_PS_UAO_FW_RD_PLANENUM 12 -- --/* PS UAO FW WR */ --#define IPU8_PS_UAO_FW_WR_OFFSET 0x157000 --#define IPU8_PS_UAO_FW_WR_PLANENUM 8 -- --/* PS UAO SRT RD */ --#define IPU8_PS_UAO_SRT_RD_OFFSET 0x154000 --#define IPU8_PS_UAO_SRT_RD_PLANENUM 26 -- --/* PS UAO SRT WR */ --#define IPU8_PS_UAO_SRT_WR_OFFSET 0x155000 --#define IPU8_PS_UAO_SRT_WR_PLANENUM 26 -- --#define IPU8_IS_ZLX_UC_RD_OFFSET 0x275000 --#define IPU8_IS_ZLX_UC_WR_OFFSET 0x276000 --#define IPU8_IS_ZLX_M0_OFFSET 0x277000 --#define IPU8_IS_ZLX_M1_OFFSET 0x278000 --#define IPU8_IS_ZLX_UPIPE_OFFSET 0x279000 --#define IPU8_IS_ZLX_UC_RD_NUM 4 --#define IPU8_IS_ZLX_UC_WR_NUM 4 --#define IPU8_IS_ZLX_M0_NUM 16 --#define IPU8_IS_ZLX_M1_NUM 16 --#define IPU8_IS_ZLX_UPIPE_NUM 6 -- --#define IPU8_PS_ZLX_DATA_RD_OFFSET 0x14e000 --#define IPU8_PS_ZLX_DATA_WR_OFFSET 0x14f000 --#define IPU8_PS_ZLX_FW_RD_OFFSET 0x150000 --#define IPU8_PS_ZLX_FW_WR_OFFSET 0x151000 --#define IPU8_PS_ZLX_DATA_RD_NUM 26 --#define IPU8_PS_ZLX_DATA_WR_NUM 26 --#define IPU8_PS_ZLX_FW_RD_NUM 12 --#define IPU8_PS_ZLX_FW_WR_NUM 8 -- --#define MMU_REG_INVALIDATE_0 0x00 --#define MMU_REG_INVALIDATE_1 0x04 --#define MMU_REG_PAGE_TABLE_BASE_ADDR 0x08 --#define MMU_REG_USER_INFO_BITS 0x0c --#define MMU_REG_AXI_REFILL_IF_ID 0x10 --#define MMU_REG_PW_EN_BITMAP 0x14 --#define MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18 --#define MMU_REG_GENERAL_REG 0x1c --#define MMU_REG_AT_SP_ARB_CFG 0x20 --#define MMU_REG_INVALIDATION_STATUS 0x24 --#define MMU_REG_IRQ_LEVEL_NO_PULSE 0x28 --#define MMU_REG_IRQ_MASK 0x2c --#define MMU_REG_IRQ_ENABLE 0x30 --#define MMU_REG_IRQ_EDGE 0x34 --#define MMU_REG_IRQ_CLEAR 0x38 --#define MMU_REG_IRQ_CAUSE 0x3c --#define MMU_REG_CG_CTRL_BITS 0x40 --#define MMU_REG_RD_FIFOS_STATUS 0x44 --#define MMU_REG_WR_FIFOS_STATUS 0x48 --#define MMU_REG_COMMON_FIFOS_STATUS 0x4c --#define MMU_REG_FSM_STATUS 0x50 -- --#define ZLX_REG_AXI_POOL 0x0 --#define ZLX_REG_EN 0x20 --#define ZLX_REG_CONF 0x24 --#define ZLX_REG_CG_CTRL 0x900 --#define ZLX_REG_FORCE_BYPASS 0x904 -- --struct ipu7_mmu_info { -- struct device *dev; -- -- u32 *l1_pt; -- u32 l1_pt_dma; -- u32 **l2_pts; -- -- u32 *dummy_l2_pt; -- u32 dummy_l2_pteval; -- void *dummy_page; -- u32 dummy_page_pteval; -- -- dma_addr_t aperture_start; -- dma_addr_t aperture_end; -- unsigned long pgsize_bitmap; -- -- spinlock_t lock; /* Serialize access to users */ -- struct ipu7_dma_mapping *dmap; --}; -- --struct ipu7_mmu { -- struct list_head node; -- -- struct ipu7_mmu_hw *mmu_hw; -- unsigned int nr_mmus; -- unsigned int mmid; -- -- phys_addr_t pgtbl; -- struct device *dev; -- -- struct ipu7_dma_mapping *dmap; -- struct list_head vma_list; -- -- struct page *trash_page; -- dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ -- dma_addr_t iova_trash_page; /* IOVA for IPU child nodes to use */ -- -- bool ready; -- spinlock_t ready_lock; /* Serialize access to bool ready */ -- -- void (*tlb_invalidate)(struct ipu7_mmu *mmu); --}; -- --struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -- void __iomem *base, int mmid, -- const struct ipu7_hw_variants *hw); --void ipu7_mmu_cleanup(struct ipu7_mmu *mmu); --int ipu7_mmu_hw_init(struct ipu7_mmu *mmu); --void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu); --int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- phys_addr_t paddr, size_t size); --void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -- size_t size); --phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -- dma_addr_t iova); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h b/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -deleted file mode 100644 -index eeadc886a8cf..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -+++ /dev/null -@@ -1,82 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2018 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_PLATFORM_REGS_H --#define IPU7_PLATFORM_REGS_H -- --#define IS_BASE 0x230000 --#define IS_UC_CTRL_BASE (IS_BASE + 0x0) -- --#define PS_BASE 0x130000 --#define PS_UC_CTRL_BASE (PS_BASE + 0x0) -- --/* -- * bit 0: IRQ from FW, -- * bit 1, 2 and 3: IRQ from HW -- */ --#define TO_SW_IRQ_MASK 0xf --#define TO_SW_IRQ_FW BIT(0) -- --#define FW_CODE_BASE 0x0 --#define FW_DATA_BASE 0x4 --#define PRINTF_EN_THROUGH_TRACE 0x3004 --#define PRINTF_EN_DIRECTLY_TO_DDR 0x3008 --#define PRINTF_DDR_BASE_ADDR 0x300c --#define PRINTF_DDR_SIZE 0x3010 --#define PRINTF_DDR_NEXT_ADDR 0x3014 --#define PRINTF_STATUS 0x3018 --#define PRINTF_AXI_CNTL 0x301c --#define PRINTF_MSG_LENGTH 0x3020 --#define TO_SW_IRQ_CNTL_EDGE 0x4000 --#define TO_SW_IRQ_CNTL_MASK_N 0x4004 --#define TO_SW_IRQ_CNTL_STATUS 0x4008 --#define TO_SW_IRQ_CNTL_CLEAR 0x400c --#define TO_SW_IRQ_CNTL_ENABLE 0x4010 --#define TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x4014 --#define ERR_IRQ_CNTL_EDGE 0x4018 --#define ERR_IRQ_CNTL_MASK_N 0x401c --#define ERR_IRQ_CNTL_STATUS 0x4020 --#define ERR_IRQ_CNTL_CLEAR 0x4024 --#define ERR_IRQ_CNTL_ENABLE 0x4028 --#define ERR_IRQ_CNTL_LEVEL_NOT_PULSE 0x402c --#define LOCAL_DMEM_BASE_ADDR 0x1300000 -- --/* -- * IS_UC_TO_SW irqs -- * bit 0: IRQ from local FW -- * bit 1~3: IRQ from HW -- */ --#define IS_UC_TO_SW_IRQ_MASK 0xf -- --#define IPU_ISYS_SPC_OFFSET 0x210000 --#define IPU7_PSYS_SPC_OFFSET 0x118000 --#define IPU_ISYS_DMEM_OFFSET 0x200000 --#define IPU_PSYS_DMEM_OFFSET 0x100000 -- --#define IPU7_ISYS_CSI_PORT_NUM 4 -- --/* IRQ-related registers in PSYS */ --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_EDGE 0x134000 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK 0x134004 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS 0x134008 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR 0x13400c --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE 0x134010 --#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x134014 --#define IRQ_FROM_LOCAL_FW BIT(0) -- --/* -- * psys subdomains power request regs -- */ --enum ipu7_device_buttress_psys_domain_pos { -- IPU_PSYS_SUBDOMAIN_LB = 0, -- IPU_PSYS_SUBDOMAIN_BB = 1, --}; -- --#define IPU7_PSYS_DOMAIN_POWER_MASK (BIT(IPU_PSYS_SUBDOMAIN_LB) | \ -- BIT(IPU_PSYS_SUBDOMAIN_BB)) --#define IPU8_PSYS_DOMAIN_POWER_MASK BIT(IPU_PSYS_SUBDOMAIN_LB) --#define IPU_PSYS_DOMAIN_POWER_IN_PROGRESS BIT(31) -- --#endif /* IPU7_PLATFORM_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.c b/drivers/media/pci/intel/ipu7/ipu7-syscom.c -deleted file mode 100644 -index 1a1c8cb02d83..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-syscom.c -+++ /dev/null -@@ -1,79 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include -- --#include "abi/ipu7_fw_syscom_abi.h" -- --#include "ipu7.h" --#include "ipu7-syscom.h" -- --static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx, -- u32 q) --{ -- return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s)); --} -- --void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q) --{ -- struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -- void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -- u32 write_index = readl(queue_indices + -- offsetof(struct syscom_queue_indices_s, -- write_index)); -- u32 read_index = readl(queue_indices + -- offsetof(struct syscom_queue_indices_s, -- read_index)); -- void *token = NULL; -- -- if (q < ctx->num_output_queues) { -- /* Output queue */ -- bool empty = (write_index == read_index); -- -- if (!empty) -- token = queue_params->token_array_mem + -- read_index * -- queue_params->token_size_in_bytes; -- } else { -- /* Input queue */ -- bool full = (read_index == ((write_index + 1U) % -- (u32)queue_params->max_capacity)); -- -- if (!full) -- token = queue_params->token_array_mem + -- write_index * queue_params->token_size_in_bytes; -- } -- return token; --} --EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7"); -- --void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q) --{ -- struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -- void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -- u32 offset, index; -- -- if (q < ctx->num_output_queues) -- /* Output queue */ -- offset = offsetof(struct syscom_queue_indices_s, read_index); -- -- else -- /* Input queue */ -- offset = offsetof(struct syscom_queue_indices_s, write_index); -- -- index = readl(queue_indices + offset); -- writel((index + 1U) % queue_params->max_capacity, -- queue_indices + offset); --} --EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7"); -- --struct syscom_queue_params_config * --ipu7_syscom_get_queue_config(struct syscom_config_s *config) --{ -- return (struct syscom_queue_params_config *)(&config[1]); --} --EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.h b/drivers/media/pci/intel/ipu7/ipu7-syscom.h -deleted file mode 100644 -index e1fbe3b7914e..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7-syscom.h -+++ /dev/null -@@ -1,35 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_SYSCOM_H --#define IPU7_SYSCOM_H -- --#include -- --struct syscom_config_s; --struct syscom_queue_params_config; -- --struct syscom_queue_config { -- void *token_array_mem; -- u32 queue_size; -- u16 token_size_in_bytes; -- u16 max_capacity; --}; -- --struct ipu7_syscom_context { -- u16 num_input_queues; -- u16 num_output_queues; -- struct syscom_queue_config *queue_configs; -- void __iomem *queue_indices; -- dma_addr_t queue_mem_dma_addr; -- void *queue_mem; -- u32 queue_mem_size; --}; -- --void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q); --void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q); --struct syscom_queue_params_config * --ipu7_syscom_get_queue_config(struct syscom_config_s *config); --#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7.c b/drivers/media/pci/intel/ipu7/ipu7.c -deleted file mode 100644 -index a76863eea356..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7.c -+++ /dev/null -@@ -1,2864 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#include --#include --#include --#include --#ifdef CONFIG_DEBUG_FS --#include --#endif --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include -- --#include "abi/ipu7_fw_common_abi.h" -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-cpd.h" --#include "ipu7-dma.h" --#include "ipu7-isys-csi2-regs.h" --#include "ipu7-mmu.h" --#include "ipu7-platform-regs.h" -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) --#include -- --#endif --#define IPU_PCI_BAR 0 --#define IPU_PCI_PBBAR 4 -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --static const unsigned int ipu7_tpg_offsets[] = { -- MGC_MG_PORT(0), -- MGC_MG_PORT(1), -- MGC_MG_PORT(2), --}; --#endif -- --static const unsigned int ipu7_csi_offsets[] = { -- IPU_CSI_PORT_A_ADDR_OFFSET, -- IPU_CSI_PORT_B_ADDR_OFFSET, -- IPU_CSI_PORT_C_ADDR_OFFSET, -- IPU_CSI_PORT_D_ADDR_OFFSET, --}; -- --static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = { -- .csi2 = { -- .gpreg = IS_IO_CSI2_GPREGS_BASE, -- }, -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7P5_IS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "IS_FW_RD", -- .offset = IPU7P5_IS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET, -- .info_bits = 0x20005101, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0 -- }, -- .zlx_conf = { -- 0x0, -- }, -- .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004c, -- 0x0000004d, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_FW_WR", -- .offset = IPU7P5_IS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET, -- .info_bits = 0x20005001, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x00010101, -- 0x00010101, -- 0x0, -- }, -- .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004a, -- 0x0000004b, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_DATA_WR_ISOC", -- .offset = IPU7P5_IS_MMU_M0_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET, -- .info_bits = 0x20004e01, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_M0_NUM, -- .zlx_axi_pool = { -- 0x00000f10, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- }, -- .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x00000041, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- }, -- }, -- { -- .name = "IS_DATA_WR_SNOOP", -- .offset = IPU7P5_IS_MMU_M1_OFFSET, -- .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET, -- .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET, -- .info_bits = 0x20004f01, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -- .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU7P5_IS_ZLX_M1_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- }, -- .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- }, -- }, -- }, -- .cdc_fifos = 3, -- .cdc_fifo_threshold = {6, 8, 2}, -- .dmem_offset = IPU_ISYS_DMEM_OFFSET, -- .spc_offset = IPU_ISYS_SPC_OFFSET, -- }, -- .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, --}; -- --static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = { -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7P5_PS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "PS_FW_RD", -- .offset = IPU7P5_PS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET, -- .info_bits = 0x20004001, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000d, -- 0x0000000f, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001a, -- 0x0000001a, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0, 1, 1, 0, 0, -- 0, 1, 1, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00000000, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00010101, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00010101, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM, -- .uao_p2tlb = { -- 0x0000002e, -- 0x00000035, -- 0x00000036, -- 0x00000031, -- 0x00000037, -- 0x00000038, -- 0x00000039, -- 0x00000032, -- 0x00000033, -- 0x0000003a, -- 0x0000003b, -- 0x0000003c, -- 0x00000034, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_FW_WR", -- .offset = IPU7P5_PS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET, -- .info_bits = 0x20003e01, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000010, -- 0x00000010, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00000000, -- 0x00010101, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000002e, -- 0x0000002f, -- 0x00000030, -- 0x00000031, -- 0x00000032, -- 0x00000033, -- 0x00000034, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_DATA_RD", -- .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET, -- .info_bits = 0x20003f01, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000b, -- 0x0000000d, -- 0x0000000f, -- 0x00000013, -- 0x00000017, -- 0x00000019, -- 0x0000001b, -- 0x0000001d, -- 0x0000001f, -- 0x0000002b, -- 0x00000033, -- 0x0000003f, -- 0x00000047, -- 0x00000049, -- 0x0000004b, -- 0x0000004c, -- 0x0000004d, -- 0x0000004e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00030303, -- 0x00010101, -- 0x00010101, -- 0x00030202, -- 0x00010101, -- 0x00010101, -- 0x00030303, -- 0x00030303, -- 0x00010101, -- 0x00030800, -- 0x00030500, -- 0x00020101, -- 0x00042000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00020400, -- 0x00010101, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM, -- .uao_p2tlb = { -- 0x0000001c, -- 0x0000001d, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- 0x00000022, -- 0x00000023, -- 0x00000024, -- 0x00000025, -- 0x00000026, -- 0x00000027, -- 0x00000028, -- 0x00000029, -- 0x0000002a, -- 0x0000002b, -- 0x0000002c, -- 0x0000002d, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- }, -- { -- .name = "PS_DATA_WR", -- .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET, -- .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET, -- .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET, -- .info_bits = 0x20003d01, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -- .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000006, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000028, -- 0x0000002a, -- 0x00000036, -- 0x0000003e, -- 0x00000040, -- 0x00000042, -- 0x0000004e, -- 0x00000056, -- 0x0000005c, -- 0x00000068, -- 0x00000070, -- 0x00000076, -- 0x00000077, -- 0x00000078, -- 0x00000079, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000006, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000028, -- 0x0000002a, -- 0x00000036, -- 0x0000003e, -- 0x00000040, -- 0x00000042, -- 0x0000004e, -- 0x00000056, -- 0x0000005c, -- 0x00000068, -- 0x00000070, -- 0x00000076, -- 0x00000077, -- 0x00000078, -- 0x00000079, -- }, -- .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f50, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 0, 0, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00010102, -- 0x00030103, -- 0x00030103, -- 0x00010101, -- 0x00010101, -- 0x00030101, -- 0x00010101, -- 0x38010101, -- 0x00000000, -- 0x00000000, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x00030303, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00010101, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000000, -- 0x00000001, -- 0x00000002, -- 0x00000003, -- 0x00000004, -- 0x00000005, -- 0x00000006, -- 0x00000007, -- 0x00000008, -- 0x00000009, -- 0x0000000a, -- 0x0000000b, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000015, -- 0x00000016, -- 0x00000017, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001b, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- }, -- }, -- .dmem_offset = IPU_PSYS_DMEM_OFFSET, -- }, --}; -- --static struct ipu_isys_internal_pdata ipu7_isys_ipdata = { -- .csi2 = { -- .gpreg = IS_IO_CSI2_GPREGS_BASE, -- }, -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7_IS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "IS_FW_RD", -- .offset = IPU7_IS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET, -- .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET, -- .info_bits = 0x20006701, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 0, 0, 0 -- }, -- .zlx_conf = { -- 0x0, 0x0, 0x0, 0x0, -- }, -- .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000061, -- 0x00000064, -- 0x00000065, -- }, -- }, -- { -- .name = "IS_FW_WR", -- .offset = IPU7_IS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET, -- .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET, -- .info_bits = 0x20006801, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000061, -- 0x00000062, -- 0x00000063, -- }, -- }, -- { -- .name = "IS_DATA_WR_ISOC", -- .offset = IPU7_IS_MMU_M0_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_M0_OFFSET, -- .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET, -- .info_bits = 0x20006601, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, -- }, -- .zlx_nr = IPU7_IS_ZLX_M0_NUM, -- .zlx_axi_pool = { -- 0x00000f10, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004a, -- 0x0000004b, -- 0x0000004c, -- 0x0000004d, -- 0x0000004e, -- 0x0000004f, -- 0x00000050, -- }, -- }, -- { -- .name = "IS_DATA_WR_SNOOP", -- .offset = IPU7_IS_MMU_M1_OFFSET, -- .zlx_offset = IPU7_IS_ZLX_M1_OFFSET, -- .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET, -- .info_bits = 0x20006901, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG, -- .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM, -- .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x3, 0x6, 0x9, 0xc, -- 0xe, 0x10, 0x12, 0x14, 0x16, -- 0x18, 0x1a, 0x1c, 0x1e, 0x20, -- 0x22, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, -- }, -- .zlx_nr = IPU7_IS_ZLX_M1_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010103, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000051, -- 0x00000052, -- 0x00000053, -- 0x00000054, -- 0x00000055, -- 0x00000056, -- 0x00000057, -- 0x00000058, -- 0x00000059, -- 0x0000005a, -- 0x0000005b, -- 0x0000005c, -- 0x0000005d, -- 0x0000005e, -- 0x0000005f, -- 0x00000060, -- }, -- }, -- }, -- .cdc_fifos = 3, -- .cdc_fifo_threshold = {6, 8, 2}, -- .dmem_offset = IPU_ISYS_DMEM_OFFSET, -- .spc_offset = IPU_ISYS_SPC_OFFSET, -- }, -- .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, --}; -- --static struct ipu_psys_internal_pdata ipu7_psys_ipdata = { -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU7_PS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "PS_FW_RD", -- .offset = IPU7_PS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET, -- .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET, -- .info_bits = 0x20004801, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0, 0x8, 0xa, 0xc, 0xd, -- 0xf, 0x11, 0x12, 0x13, 0x14, -- 0x16, 0x18, 0x19, 0x1a, 0x1a, -- 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, 0x20, 0x22, 0x24, 0x26, -- }, -- .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x0, -- }, -- .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000036, -- 0x0000003d, -- 0x0000003e, -- 0x00000039, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x0000003a, -- 0x0000003b, -- 0x00000042, -- 0x00000043, -- 0x00000044, -- 0x0000003c, -- }, -- }, -- { -- .name = "PS_FW_WR", -- .offset = IPU7_PS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET, -- .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET, -- .info_bits = 0x20004601, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0, 0x8, 0xa, 0xc, 0xd, -- 0xe, 0xf, 0x10, 0x10, 0x10, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- }, -- .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, 0, 0, 0, 0, -- 0, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x00010101, -- 0x00010101, -- }, -- .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000036, -- 0x00000037, -- 0x00000038, -- 0x00000039, -- 0x0000003a, -- 0x0000003b, -- 0x0000003c, -- }, -- }, -- { -- .name = "PS_DATA_RD", -- .offset = IPU7_PS_MMU_SRT_RD_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET, -- .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET, -- .info_bits = 0x20004701, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x4, 0x6, 0x8, 0xb, -- 0xd, 0xf, 0x11, 0x13, 0x15, -- 0x17, 0x23, 0x2b, 0x37, 0x3f, -- 0x41, 0x43, 0x44, 0x45, 0x46, -- 0x47, 0x48, 0x49, 0x4a, 0x4b, -- 0x4c, 0x4d, 0x4e, 0x4f, 0x50, -- 0x51, 0x52, 0x53, 0x55, 0x57, -- 0x59, 0x5b, 0x5d, 0x5f, 0x61, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, 0x20, 0x22, 0x24, 0x26, -- 0x28, 0x2a, 0x2c, 0x2e, 0x30, -- 0x32, 0x34, 0x36, 0x38, 0x3a, -- 0x3c, 0x3e, 0x40, 0x42, 0x44, -- 0x46, 0x48, 0x4a, 0x4c, 0x4e, -- }, -- .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 0, 0, 0, 0, 0, 0, 0, 0, -- 0, 0, 0, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x00030303, -- 0x00010101, -- 0x00010101, -- 0x00030202, -- 0x00010101, -- 0x00010101, -- 0x00010101, -- 0x00030800, -- 0x00030500, -- 0x00020101, -- 0x00042000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00020400, -- 0x00010101, -- }, -- .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000022, -- 0x00000023, -- 0x00000024, -- 0x00000025, -- 0x00000026, -- 0x00000027, -- 0x00000028, -- 0x00000029, -- 0x0000002a, -- 0x0000002b, -- 0x0000002c, -- 0x0000002d, -- 0x0000002e, -- 0x0000002f, -- 0x00000030, -- 0x00000031, -- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- 0x00000032, -- 0x00000033, -- 0x00000034, -- 0x00000035, -- }, -- }, -- { -- .name = "PS_DATA_WR", -- .offset = IPU7_PS_MMU_SRT_WR_OFFSET, -- .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET, -- .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET, -- .info_bits = 0x20004501, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x0, -- .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -- .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -- .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x2, 0x6, 0xa, 0xc, -- 0xe, 0x10, 0x12, 0x14, 0x16, -- 0x18, 0x1a, 0x1c, 0x1e, 0x20, -- 0x22, 0x24, 0x26, 0x32, 0x3a, -- 0x3c, 0x3e, 0x4a, 0x52, 0x58, -- 0x64, 0x6c, 0x72, 0x7e, 0x86, -- 0x8c, 0x8d, 0x8e, 0x8f, 0x90, -- 0x91, 0x92, 0x94, 0x96, 0x98, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, 0x6, 0x8, -- 0xa, 0xc, 0xe, 0x10, 0x12, -- 0x14, 0x16, 0x18, 0x1a, 0x1c, -- 0x1e, 0x20, 0x22, 0x24, 0x26, -- 0x28, 0x2a, 0x2c, 0x2e, 0x30, -- 0x32, 0x34, 0x36, 0x38, 0x3a, -- 0x3c, 0x3e, 0x40, 0x42, 0x44, -- 0x46, 0x48, 0x4a, 0x4c, 0x4e, -- }, -- .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f50, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 0, 0, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 0, 0, -- }, -- .zlx_conf = { -- 0x00010102, -- 0x00030103, -- 0x00030103, -- 0x00010101, -- 0x00010101, -- 0x00030101, -- 0x00010101, -- 0x38010101, -- 0x0, -- 0x0, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x38010101, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00010101, -- 0x00010101, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x00042000, -- 0x00031000, -- 0x00031000, -- 0x0, -- 0x0, -- }, -- .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000000, -- 0x00000001, -- 0x00000002, -- 0x00000003, -- 0x00000004, -- 0x00000005, -- 0x00000006, -- 0x00000007, -- 0x00000008, -- 0x00000009, -- 0x0000000a, -- 0x0000000b, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000015, -- 0x00000016, -- 0x00000017, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001b, -- 0x0000001c, -- 0x0000001d, -- 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- }, -- }, -- }, -- .dmem_offset = IPU_PSYS_DMEM_OFFSET, -- }, --}; -- --static struct ipu_isys_internal_pdata ipu8_isys_ipdata = { -- .csi2 = { -- .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE, -- }, -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU8_IS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "IS_FW_RD", -- .offset = IPU8_IS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_UC_RD_OFFSET, -- .uao_offset = IPU8_IS_UAO_UC_RD_OFFSET, -- .info_bits = 0x20005101, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU8_IS_ZLX_UC_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0 -- }, -- .zlx_conf = { -- 0, 2, 0, 0 -- }, -- .uao_p_num = IPU8_IS_UAO_UC_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004c, -- 0x0000004d, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_FW_WR", -- .offset = IPU8_IS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_UC_WR_OFFSET, -- .uao_offset = IPU8_IS_UAO_UC_WR_OFFSET, -- .info_bits = 0x20005001, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x0, 0x8, 0xa, -- }, -- .l2_block_sz = { -- 0x0, 0x2, 0x4, -- }, -- .zlx_nr = IPU8_IS_ZLX_UC_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x2, -- 0x2, -- 0x0, -- }, -- .uao_p_num = IPU8_IS_UAO_UC_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000049, -- 0x0000004a, -- 0x0000004b, -- 0x00000000, -- }, -- }, -- { -- .name = "IS_DATA_WR_ISOC", -- .offset = IPU8_IS_MMU_M0_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_M0_OFFSET, -- .uao_offset = IPU8_IS_UAO_M0_WR_OFFSET, -- .info_bits = 0x20004e01, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_M0_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_M0_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_M0_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_M0_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU8_IS_ZLX_M0_NUM, -- .zlx_axi_pool = { -- 0x00000f10, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- }, -- .uao_p_num = IPU8_IS_UAO_M0_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- 0x0000003b, -- 0x0000003c, -- 0x0000003d, -- 0x0000003e, -- }, -- }, -- { -- .name = "IS_DATA_WR_SNOOP", -- .offset = IPU8_IS_MMU_M1_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_M1_OFFSET, -- .uao_offset = IPU8_IS_UAO_M1_WR_OFFSET, -- .info_bits = 0x20004f01, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_M1_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_M1_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_M1_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_M1_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- }, -- .zlx_nr = IPU8_IS_ZLX_M1_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- }, -- .uao_p_num = IPU8_IS_UAO_M1_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- 0x0000003f, -- 0x00000040, -- 0x00000041, -- 0x00000042, -- }, -- }, -- { -- .name = "IS_UPIPE", -- .offset = IPU8_IS_MMU_UPIPE_OFFSET, -- .zlx_offset = IPU8_IS_ZLX_UPIPE_OFFSET, -- .uao_offset = IPU8_IS_UAO_UPIPE_OFFSET, -- .info_bits = 0x20005201, -- .refill = 0x00002928, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG, -- .l2_block = IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -- .nr_l2streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- }, -- .zlx_nr = IPU8_IS_ZLX_UPIPE_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, -- }, -- .zlx_conf = { -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- 0x3, -- }, -- .uao_p_num = IPU8_IS_UAO_UPIPE_PLANENUM, -- .uao_p2tlb = { -- 0x00000043, -- 0x00000044, -- 0x00000045, -- 0x00000046, -- 0x00000047, -- 0x00000048, -- }, -- }, -- }, -- .cdc_fifos = 3, -- .cdc_fifo_threshold = {6, 8, 2}, -- .dmem_offset = IPU_ISYS_DMEM_OFFSET, -- .spc_offset = IPU_ISYS_SPC_OFFSET, -- }, -- .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, --}; -- --static struct ipu_psys_internal_pdata ipu8_psys_ipdata = { -- .hw_variant = { -- .offset = IPU_UNIFIED_OFFSET, -- .nr_mmus = IPU8_PS_MMU_NUM, -- .mmu_hw = { -- { -- .name = "PS_FW_RD", -- .offset = IPU8_PS_MMU_FW_RD_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_FW_RD_OFFSET, -- .uao_offset = IPU8_PS_UAO_FW_RD_OFFSET, -- .info_bits = 0x20003a01, -- .refill = 0x00002726, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x00000018, -- 0x00000018, -- 0x00000018, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- }, -- .zlx_nr = IPU8_PS_ZLX_FW_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 0, 1, 0, 0, 1, 1, 0, 0, -- 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x0, -- 0x2, -- 0x0, -- 0x0, -- 0x2, -- 0x2, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_FW_RD_PLANENUM, -- .uao_p2tlb = { -- 0x0000002d, -- 0x00000032, -- 0x00000033, -- 0x00000030, -- 0x00000034, -- 0x00000035, -- 0x00000036, -- 0x00000031, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_FW_WR", -- .offset = IPU8_PS_MMU_FW_WR_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_FW_WR_OFFSET, -- .uao_offset = IPU8_PS_UAO_FW_WR_OFFSET, -- .info_bits = 0x20003901, -- .refill = 0x00002524, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000010, -- 0x00000010, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- }, -- .zlx_nr = IPU8_PS_ZLX_FW_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f20, -- }, -- .zlx_en = { -- 0, 1, 1, 0, 0, 0, 0, 0, -- }, -- .zlx_conf = { -- 0x0, 0x2, 0x2, 0x0, -- 0x0, 0x0, 0x0, 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_FW_WR_PLANENUM, -- .uao_p2tlb = { -- 0x0000002d, -- 0x0000002e, -- 0x0000002f, -- 0x00000030, -- 0x00000031, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_DATA_RD", -- .offset = IPU8_PS_MMU_SRT_RD_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_DATA_RD_OFFSET, -- .uao_offset = IPU8_PS_UAO_SRT_RD_OFFSET, -- .info_bits = 0x20003801, -- .refill = 0x00002322, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000014, -- 0x00000018, -- 0x0000001c, -- 0x0000001e, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- 0x0000002c, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- 0x00000036, -- 0x0000003a, -- 0x0000003c, -- 0x0000003c, -- 0x0000003c, -- 0x0000003c, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- 0x0000002c, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- }, -- .zlx_nr = IPU8_PS_ZLX_DATA_RD_NUM, -- .zlx_axi_pool = { -- 0x00000f30, -- }, -- .zlx_en = { -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 0, 0, -- 0, 0, -- }, -- .zlx_conf = { -- 0x6, 0x3, 0x3, 0x6, -- 0x2, 0x2, 0x6, 0x6, -- 0x6, 0x3, 0x6, 0x3, -- 0x3, 0x2, 0x2, 0x2, -- 0x2, 0x2, 0x2, 0x6, -- 0x6, 0x3, 0x0, 0x0, -- 0x0, 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_SRT_RD_PLANENUM, -- .uao_p2tlb = { -- 0x00000017, -- 0x00000018, -- 0x00000019, -- 0x0000001a, -- 0x0000001b, -- 0x0000001c, -- 0x0000001d, -- 0x0000001e, -- 0x0000001f, -- 0x00000020, -- 0x00000021, -- 0x00000022, -- 0x00000023, -- 0x00000024, -- 0x00000025, -- 0x00000026, -- 0x00000027, -- 0x00000028, -- 0x00000029, -- 0x0000002a, -- 0x0000002b, -- 0x0000002c, -- 0x0, -- 0x0, -- 0x0, -- 0x0, -- }, -- }, -- { -- .name = "PS_DATA_WR", -- .offset = IPU8_PS_MMU_SRT_WR_OFFSET, -- .zlx_offset = IPU8_PS_ZLX_DATA_WR_OFFSET, -- .uao_offset = IPU8_PS_UAO_SRT_WR_OFFSET, -- .info_bits = 0x20003701, -- .refill = 0x00002120, -- .collapse_en_bitmap = 0x1, -- .at_sp_arb_cfg = 0x1, -- .l1_block = IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -- .l2_block = IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -- .nr_l1streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -- .nr_l2streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -- .l1_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001c, -- 0x0000001e, -- 0x00000022, -- 0x00000024, -- 0x00000028, -- 0x0000002a, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- 0x00000036, -- 0x00000038, -- 0x0000003a, -- 0x0000003a, -- 0x0000003a, -- }, -- .l2_block_sz = { -- 0x00000000, -- 0x00000002, -- 0x00000004, -- 0x00000006, -- 0x00000008, -- 0x0000000a, -- 0x0000000c, -- 0x0000000e, -- 0x00000010, -- 0x00000012, -- 0x00000014, -- 0x00000016, -- 0x00000018, -- 0x0000001a, -- 0x0000001c, -- 0x0000001e, -- 0x00000020, -- 0x00000022, -- 0x00000024, -- 0x00000026, -- 0x00000028, -- 0x0000002a, -- 0x0000002c, -- 0x0000002e, -- 0x00000030, -- 0x00000032, -- }, -- .zlx_nr = IPU8_PS_ZLX_DATA_WR_NUM, -- .zlx_axi_pool = { -- 0x00000f50, -- }, -- .zlx_en = { -- 1, 1, 1, 0, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 1, -- 1, 1, 1, 1, 1, 1, 1, 0, -- 0, 0, -- }, -- .zlx_conf = { -- 0x3, -- 0x6, -- 0x38000002, -- 0x38000000, -- 0x3, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x38000002, -- 0x6, -- 0x3, -- 0x6, -- 0x3, -- 0x6, -- 0x3, -- 0x6, -- 0x3, -- 0x3, -- 0x6, -- 0x3, -- 0x3, -- 0x0, -- 0x0, -- 0x0, -- }, -- .uao_p_num = IPU8_PS_UAO_SRT_WR_PLANENUM, -- .uao_p2tlb = { -- 0x00000000, -- 0x00000001, -- 0x00000002, -- 0x00000003, -- 0x00000004, -- 0x00000005, -- 0x00000006, -- 0x00000007, -- 0x00000008, -- 0x00000009, -- 0x0000000a, -- 0x0000000b, -- 0x0000000c, -- 0x0000000d, -- 0x0000000e, -- 0x0000000f, -- 0x00000010, -- 0x00000011, -- 0x00000012, -- 0x00000013, -- 0x00000014, -- 0x00000015, -- 0x00000016, -- 0x00000000, -- 0x00000000, -- 0x00000000, -- }, -- }, -- }, -- .dmem_offset = IPU_PSYS_DMEM_OFFSET, -- }, --}; -- --static const struct ipu_buttress_ctrl ipu7_isys_buttress_ctrl = { -- .subsys_id = IPU_IS, -- .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -- .ovrd_clk = BUTTRESS_OVERRIDE_IS_CLK, -- .own_clk_ack = BUTTRESS_OWN_ACK_IS_CLK, --}; -- --static const struct ipu_buttress_ctrl ipu7_psys_buttress_ctrl = { -- .subsys_id = IPU_PS, -- .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -- .ovrd_clk = BUTTRESS_OVERRIDE_PS_CLK, -- .own_clk_ack = BUTTRESS_OWN_ACK_PS_CLK, --}; -- --static const struct ipu_buttress_ctrl ipu8_isys_buttress_ctrl = { -- .subsys_id = IPU_IS, -- .ratio = IPU8_IS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, --}; -- --static const struct ipu_buttress_ctrl ipu8_psys_buttress_ctrl = { -- .subsys_id = IPU_PS, -- .ratio = IPU8_PS_FREQ_CTL_DEFAULT_RATIO, -- .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -- .cdyn = IPU_FREQ_CTL_CDYN, -- .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -- .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -- .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -- .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -- .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -- .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -- .own_clk_ack = BUTTRESS_OWN_ACK_PS_PLL, --}; -- --void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -- struct ipu_psys_internal_pdata *psys_ipdata) --{ -- isys_ipdata->csi2.nports = ARRAY_SIZE(ipu7_csi_offsets); -- isys_ipdata->csi2.offsets = ipu7_csi_offsets; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- isys_ipdata->tpg.ntpgs = ARRAY_SIZE(ipu7_tpg_offsets); -- isys_ipdata->tpg.offsets = ipu7_tpg_offsets; -- isys_ipdata->tpg.sels = NULL; --#endif -- isys_ipdata->num_parallel_streams = IPU7_ISYS_NUM_STREAMS; -- psys_ipdata->hw_variant.spc_offset = IPU7_PSYS_SPC_OFFSET; --} -- --static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) --{ -- struct fwnode_handle *endpoint; -- -- if (IS_ERR_OR_NULL(fwnode)) -- return -EINVAL; -- -- endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); -- if (endpoint) { -- fwnode_handle_put(endpoint); -- return 0; -- } -- -- return ipu7_isys_check_fwnode_graph(fwnode->secondary); --} -- --static struct ipu7_bus_device * --ipu7_isys_init(struct pci_dev *pdev, struct device *parent, -- const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -- struct ipu7_isys_subdev_pdata *spdata, -- const struct ipu_isys_internal_pdata *ipdata, -- unsigned int nr) --{ -- struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -- struct ipu7_bus_device *isys_adev; -- struct device *dev = &pdev->dev; -- struct ipu7_isys_pdata *pdata; -- int ret; -- -- ret = ipu7_isys_check_fwnode_graph(fwnode); -- if (ret) { -- if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { -- dev_err(dev, -- "fwnode graph has no endpoints connection\n"); -- return ERR_PTR(-EINVAL); -- } -- -- ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); -- if (ret) { -- dev_err_probe(dev, ret, "IPU bridge init failed\n"); -- return ERR_PTR(ret); -- } -- } -- -- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -- if (!pdata) -- return ERR_PTR(-ENOMEM); -- -- pdata->base = base; -- pdata->ipdata = ipdata; -- pdata->spdata = spdata; -- -- isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -- IPU_ISYS_NAME); -- if (IS_ERR(isys_adev)) { -- dev_err_probe(dev, PTR_ERR(isys_adev), -- "ipu7_bus_initialize_device isys failed\n"); -- kfree(pdata); -- return ERR_CAST(isys_adev); -- } -- -- isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, -- &ipdata->hw_variant); -- if (IS_ERR(isys_adev->mmu)) { -- dev_err_probe(dev, PTR_ERR(isys_adev->mmu), -- "ipu7_mmu_init(isys_adev->mmu) failed\n"); -- put_device(&isys_adev->auxdev.dev); -- kfree(pdata); -- return ERR_CAST(isys_adev->mmu); -- } -- -- isys_adev->mmu->dev = &isys_adev->auxdev.dev; -- isys_adev->subsys = IPU_IS; -- -- ret = ipu7_bus_add_device(isys_adev); -- if (ret) { -- kfree(pdata); -- return ERR_PTR(ret); -- } -- -- return isys_adev; --} -- --static struct ipu7_bus_device * --ipu7_psys_init(struct pci_dev *pdev, struct device *parent, -- const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -- const struct ipu_psys_internal_pdata *ipdata, unsigned int nr) --{ -- struct ipu7_bus_device *psys_adev; -- struct ipu7_psys_pdata *pdata; -- int ret; -- -- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -- if (!pdata) -- return ERR_PTR(-ENOMEM); -- -- pdata->base = base; -- pdata->ipdata = ipdata; -- -- psys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -- IPU_PSYS_NAME); -- if (IS_ERR(psys_adev)) { -- dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), -- "ipu7_bus_initialize_device psys failed\n"); -- kfree(pdata); -- return ERR_CAST(psys_adev); -- } -- -- psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, -- &ipdata->hw_variant); -- if (IS_ERR(psys_adev->mmu)) { -- dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), -- "ipu7_mmu_init(psys_adev->mmu) failed\n"); -- put_device(&psys_adev->auxdev.dev); -- kfree(pdata); -- return ERR_CAST(psys_adev->mmu); -- } -- -- psys_adev->mmu->dev = &psys_adev->auxdev.dev; -- psys_adev->subsys = IPU_PS; -- -- ret = ipu7_bus_add_device(psys_adev); -- if (ret) { -- kfree(pdata); -- return ERR_PTR(ret); -- } -- -- return psys_adev; --} -- --static struct ia_gofo_msg_log_info_ts fw_error_log[IPU_SUBSYS_NUM]; --void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) --{ -- void __iomem *reg = adev->isp->base + ((adev->subsys == IPU_IS) ? -- BUTTRESS_REG_FW_GP24 : -- BUTTRESS_REG_FW_GP8); -- -- memcpy_fromio(&fw_error_log[adev->subsys], reg, -- sizeof(fw_error_log[adev->subsys])); --} --EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); -- --#ifdef CONFIG_DEBUG_FS --static struct debugfs_blob_wrapper isys_fw_error; --static struct debugfs_blob_wrapper psys_fw_error; -- --static int ipu7_init_debugfs(struct ipu7_device *isp) --{ -- struct dentry *file; -- struct dentry *dir; -- -- dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -- if (!dir) -- return -ENOMEM; -- -- isys_fw_error.data = &fw_error_log[IPU_IS]; -- isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); -- file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); -- if (!file) -- goto err; -- psys_fw_error.data = &fw_error_log[IPU_PS]; -- psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); -- file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); -- if (!file) -- goto err; -- -- isp->ipu7_dir = dir; -- -- return 0; --err: -- debugfs_remove_recursive(dir); -- return -ENOMEM; --} -- --static void ipu7_remove_debugfs(struct ipu7_device *isp) --{ -- /* -- * Since isys and psys debugfs dir will be created under ipu root dir, -- * mark its dentry to NULL to avoid duplicate removal. -- */ -- debugfs_remove_recursive(isp->ipu7_dir); -- isp->ipu7_dir = NULL; --} --#endif /* CONFIG_DEBUG_FS */ -- --static void ipu7_pci_config_setup(struct pci_dev *dev) --{ -- u16 pci_command; -- -- pci_read_config_word(dev, PCI_COMMAND, &pci_command); -- pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; -- pci_write_config_word(dev, PCI_COMMAND, pci_command); --} -- --static int ipu7_map_fw_code_region(struct ipu7_bus_device *sys, -- void *data, size_t size) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct sg_table *sgt = &sys->fw_sgt; -- struct ipu7_device *isp = adev->isp; -- struct pci_dev *pdev = isp->pdev; -- unsigned long n_pages, i; -- unsigned long attr = 0; -- struct page **pages; -- int ret; -- -- n_pages = PFN_UP(size); -- -- pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); -- if (!pages) -- return -ENOMEM; -- -- for (i = 0; i < n_pages; i++) { -- struct page *p = vmalloc_to_page(data); -- -- if (!p) { -- ret = -ENODEV; -- goto out; -- } -- -- pages[i] = p; -- data += PAGE_SIZE; -- } -- -- ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size, -- GFP_KERNEL); -- if (ret) { -- ret = -ENOMEM; -- goto out; -- } -- -- if (!isp->secure_mode) -- attr |= DMA_ATTR_RESERVE_REGION; -- -- ret = dma_map_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -- if (ret < 0) { -- dev_err(dev, "map fw code[%lu pages %u nents] failed\n", -- n_pages, sgt->nents); -- ret = -ENOMEM; -- sg_free_table(sgt); -- goto out; -- } -- -- ret = ipu7_dma_map_sgtable(sys, sgt, DMA_BIDIRECTIONAL, attr); -- if (ret) { -- dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -- sg_free_table(sgt); -- goto out; -- } -- -- ipu7_dma_sync_sgtable(sys, sgt); -- -- dev_dbg(dev, "fw code region mapped at 0x%pad entries %d\n", -- &sgt->sgl->dma_address, sgt->nents); -- --out: -- kfree(pages); -- -- return ret; --} -- --static void ipu7_unmap_fw_code_region(struct ipu7_bus_device *sys) --{ -- struct pci_dev *pdev = sys->isp->pdev; -- struct sg_table *sgt = &sys->fw_sgt; -- -- ipu7_dma_unmap_sgtable(sys, sgt, DMA_BIDIRECTIONAL, 0); -- dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -- sg_free_table(sgt); --} -- --static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, -- const char *sys_name) --{ -- struct device *dev = &sys->auxdev.dev; -- struct ipu7_device *isp = sys->isp; -- int ret; -- -- /* Copy FW binaries to specific location. */ -- ret = ipu7_cpd_copy_binary(isp->cpd_fw->data, sys_name, -- isp->fw_code_region, &sys->fw_entry); -- if (ret) { -- dev_err(dev, "%s binary not found.\n", sys_name); -- return ret; -- } -- -- ret = pm_runtime_get_sync(dev); -- if (ret < 0) { -- dev_err(dev, "Failed to get runtime PM\n"); -- return ret; -- } -- -- ret = ipu7_mmu_hw_init(sys->mmu); -- if (ret) { -- dev_err(dev, "Failed to set mmu hw\n"); -- pm_runtime_put(dev); -- return ret; -- } -- -- /* Map code region. */ -- ret = ipu7_map_fw_code_region(sys, isp->fw_code_region, -- IPU_FW_CODE_REGION_SIZE); -- if (ret) -- dev_err(dev, "Failed to map fw code region for %s.\n", -- sys_name); -- -- ipu7_mmu_hw_cleanup(sys->mmu); -- pm_runtime_put(dev); -- -- return ret; --} -- --static int ipu7_init_fw_code_region(struct ipu7_device *isp) --{ -- int ret; -- -- /* -- * Allocate and map memory for FW execution. -- * Not required in secure mode, in which FW runs in IMR. -- */ -- isp->fw_code_region = vmalloc(IPU_FW_CODE_REGION_SIZE); -- if (!isp->fw_code_region) -- return -ENOMEM; -- -- ret = ipu7_init_fw_code_region_by_sys(isp->isys, "isys"); -- if (ret) -- goto fail_init; -- -- ret = ipu7_init_fw_code_region_by_sys(isp->psys, "psys"); -- if (ret) -- goto fail_init; -- -- return 0; -- --fail_init: -- vfree(isp->fw_code_region); -- -- return ret; --} -- --static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) --{ -- struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; -- struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -- const struct ipu_buttress_ctrl *isys_buttress_ctrl; -- const struct ipu_buttress_ctrl *psys_buttress_ctrl; -- struct ipu_isys_internal_pdata *isys_ipdata; -- struct ipu_psys_internal_pdata *psys_ipdata; -- unsigned int dma_mask = IPU_DMA_MASK; -- struct device *dev = &pdev->dev; -- void __iomem *isys_base = NULL; -- void __iomem *psys_base = NULL; -- phys_addr_t phys, pb_phys; -- struct ipu7_device *isp; -- u32 is_es; -- int ret; -- -- if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es)) -- is_es = 0; -- -- isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); -- if (!isp) -- return -ENOMEM; -- -- isp->pdev = pdev; -- INIT_LIST_HEAD(&isp->devices); -- -- ret = pcim_enable_device(pdev); -- if (ret) -- return dev_err_probe(dev, ret, "Enable PCI device failed\n"); -- -- dev_info(dev, "Device 0x%x (rev: 0x%x)\n", -- pdev->device, pdev->revision); -- -- phys = pci_resource_start(pdev, IPU_PCI_BAR); -- pb_phys = pci_resource_start(pdev, IPU_PCI_PBBAR); -- dev_info(dev, "IPU7 PCI BAR0 base %pap BAR2 base %pap\n", -- &phys, &pb_phys); -- -- isp->base = pcim_iomap_region(pdev, IPU_PCI_BAR, IPU_NAME); -- if (IS_ERR(isp->base)) -- return dev_err_probe(dev, PTR_ERR(isp->base), -- "Failed to I/O memory remapping bar %u\n", -- IPU_PCI_BAR); -- -- isp->pb_base = pcim_iomap_region(pdev, IPU_PCI_PBBAR, IPU_NAME); -- if (IS_ERR(isp->pb_base)) -- return dev_err_probe(dev, PTR_ERR(isp->pb_base), -- "Failed to I/O memory remapping bar %u\n", -- IPU_PCI_PBBAR); -- -- dev_info(dev, "IPU7 PCI BAR0 mapped at %p\n BAR2 mapped at %p\n", -- isp->base, isp->pb_base); -- -- pci_set_drvdata(pdev, isp); -- pci_set_master(pdev); -- -- switch (id->device) { -- case IPU7_PCI_ID: -- isp->hw_ver = IPU_VER_7; -- isp->cpd_fw_name = IPU7_FIRMWARE_NAME; -- isys_ipdata = &ipu7_isys_ipdata; -- psys_ipdata = &ipu7_psys_ipdata; -- isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -- psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -- break; -- case IPU7P5_PCI_ID: -- isp->hw_ver = IPU_VER_7P5; -- isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME; -- isys_ipdata = &ipu7p5_isys_ipdata; -- psys_ipdata = &ipu7p5_psys_ipdata; -- isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -- psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -- break; -- case IPU8_PCI_ID: -- isp->hw_ver = IPU_VER_8; -- isp->cpd_fw_name = IPU8_FIRMWARE_NAME; -- isys_ipdata = &ipu8_isys_ipdata; -- psys_ipdata = &ipu8_psys_ipdata; -- isys_buttress_ctrl = &ipu8_isys_buttress_ctrl; -- psys_buttress_ctrl = &ipu8_psys_buttress_ctrl; -- break; -- default: -- WARN(1, "Unsupported IPU device"); -- return -ENODEV; -- } -- -- ipu_internal_pdata_init(isys_ipdata, psys_ipdata); -- -- isys_base = isp->base + isys_ipdata->hw_variant.offset; -- psys_base = isp->base + psys_ipdata->hw_variant.offset; -- -- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_mask)); -- if (ret) -- return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); -- -- dma_set_max_seg_size(dev, UINT_MAX); -- -- ipu7_pci_config_setup(pdev); -- -- ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); -- if (ret < 0) -- return dev_err_probe(dev, ret, "Failed to alloc irq vector\n"); -- -- ret = ipu_buttress_init(isp); -- if (ret) -- goto pci_irq_free; -- -- dev_info(dev, "firmware cpd file: %s\n", isp->cpd_fw_name); -- -- ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev); -- if (ret) { -- dev_err_probe(dev, ret, -- "Requesting signed firmware %s failed\n", -- isp->cpd_fw_name); -- goto buttress_exit; -- } -- -- ret = ipu7_cpd_validate_cpd_file(isp, isp->cpd_fw->data, -- isp->cpd_fw->size); -- if (ret) { -- dev_err_probe(dev, ret, "Failed to validate cpd\n"); -- goto out_ipu_bus_del_devices; -- } -- -- isys_ctrl = devm_kmemdup(dev, isys_buttress_ctrl, -- sizeof(*isys_buttress_ctrl), GFP_KERNEL); -- if (!isys_ctrl) { -- ret = -ENOMEM; -- goto out_ipu_bus_del_devices; -- } -- --#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -- ipu_get_acpi_devices(&dev->platform_data); --#endif -- isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -- dev->platform_data, -- isys_ipdata, 0); -- if (IS_ERR(isp->isys)) { -- ret = PTR_ERR(isp->isys); -- goto out_ipu_bus_del_devices; -- } -- -- psys_ctrl = devm_kmemdup(dev, psys_buttress_ctrl, -- sizeof(*psys_buttress_ctrl), GFP_KERNEL); -- if (!psys_ctrl) { -- ret = -ENOMEM; -- goto out_ipu_bus_del_devices; -- } -- -- isp->psys = ipu7_psys_init(pdev, &isp->isys->auxdev.dev, -- psys_ctrl, psys_base, -- psys_ipdata, 0); -- if (IS_ERR(isp->psys)) { -- ret = PTR_ERR(isp->psys); -- goto out_ipu_bus_del_devices; -- } -- -- ret = devm_request_threaded_irq(dev, pdev->irq, -- ipu_buttress_isr, -- ipu_buttress_isr_threaded, -- IRQF_SHARED, IPU_NAME, isp); -- if (ret) -- goto out_ipu_bus_del_devices; -- -- if (!isp->secure_mode) { -- ret = ipu7_init_fw_code_region(isp); -- if (ret) -- goto out_ipu_bus_del_devices; -- } else { -- ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -- if (ret < 0) { -- dev_err(&isp->psys->auxdev.dev, -- "Failed to get runtime PM\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ret = ipu7_mmu_hw_init(isp->psys->mmu); -- if (ret) { -- dev_err_probe(&isp->pdev->dev, ret, -- "Failed to init MMU hardware\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ret = ipu7_map_fw_code_region(isp->psys, -- (void *)isp->cpd_fw->data, -- isp->cpd_fw->size); -- if (ret) { -- dev_err_probe(&isp->pdev->dev, ret, -- "failed to map fw image\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ret = ipu_buttress_authenticate(isp); -- if (ret) { -- dev_err_probe(&isp->pdev->dev, ret, -- "FW authentication failed\n"); -- goto out_ipu_bus_del_devices; -- } -- -- ipu7_mmu_hw_cleanup(isp->psys->mmu); -- pm_runtime_put(&isp->psys->auxdev.dev); -- } -- --#ifdef CONFIG_DEBUG_FS -- ret = ipu7_init_debugfs(isp); -- if (ret) { -- dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); -- goto out_ipu_bus_del_devices; -- } --#endif -- pm_runtime_put_noidle(dev); -- pm_runtime_allow(dev); -- -- isp->ipu7_bus_ready_to_probe = true; -- -- return 0; -- --out_ipu_bus_del_devices: -- if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->isys); -- if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->psys); --#ifdef CONFIG_DEBUG_FS -- if (!IS_ERR_OR_NULL(isp->fw_code_region)) -- vfree(isp->fw_code_region); --#endif -- if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) -- ipu7_mmu_cleanup(isp->psys->mmu); -- if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -- ipu7_mmu_cleanup(isp->isys->mmu); -- if (!IS_ERR_OR_NULL(isp->psys)) -- pm_runtime_put(&isp->psys->auxdev.dev); -- ipu7_bus_del_devices(pdev); -- release_firmware(isp->cpd_fw); --buttress_exit: -- ipu_buttress_exit(isp); --pci_irq_free: -- pci_free_irq_vectors(pdev); -- -- return ret; --} -- --static void ipu7_pci_remove(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- --#ifdef CONFIG_DEBUG_FS -- ipu7_remove_debugfs(isp); --#endif -- if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->isys); -- if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -- ipu7_unmap_fw_code_region(isp->psys); -- -- if (!IS_ERR_OR_NULL(isp->fw_code_region)) -- vfree(isp->fw_code_region); -- -- ipu7_mmu_cleanup(isp->isys->mmu); -- ipu7_mmu_cleanup(isp->psys->mmu); -- -- ipu7_bus_del_devices(pdev); -- -- pm_runtime_forbid(&pdev->dev); -- pm_runtime_get_noresume(&pdev->dev); -- -- ipu_buttress_exit(isp); -- -- release_firmware(isp->cpd_fw); -- --} -- --static void ipu7_pci_reset_prepare(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- -- dev_warn(&pdev->dev, "FLR prepare\n"); -- pm_runtime_forbid(&isp->pdev->dev); --} -- --static void ipu7_pci_reset_done(struct pci_dev *pdev) --{ -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- -- ipu_buttress_restore(isp); -- if (isp->secure_mode) -- ipu_buttress_reset_authentication(isp); -- -- isp->ipc_reinit = true; -- pm_runtime_allow(&isp->pdev->dev); -- -- dev_warn(&pdev->dev, "FLR completed\n"); --} -- --/* -- * PCI base driver code requires driver to provide these to enable -- * PCI device level PM state transitions (D0<->D3) -- */ --static int ipu7_suspend(struct device *dev) --{ -- return 0; --} -- --static int ipu7_resume(struct device *dev) --{ -- struct pci_dev *pdev = to_pci_dev(dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu_buttress *b = &isp->buttress; -- int ret; -- -- isp->secure_mode = ipu_buttress_get_secure_mode(isp); -- dev_info(dev, "IPU7 in %s mode\n", -- isp->secure_mode ? "secure" : "non-secure"); -- -- ipu_buttress_restore(isp); -- -- ret = ipu_buttress_ipc_reset(isp, &b->cse); -- if (ret) -- dev_err(dev, "IPC reset protocol failed!\n"); -- -- ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -- if (ret < 0) { -- dev_err(dev, "Failed to get runtime PM\n"); -- return 0; -- } -- -- ret = ipu_buttress_authenticate(isp); -- if (ret) -- dev_err(dev, "FW authentication failed(%d)\n", ret); -- -- pm_runtime_put(&isp->psys->auxdev.dev); -- -- return 0; --} -- --static int ipu7_runtime_resume(struct device *dev) --{ -- struct pci_dev *pdev = to_pci_dev(dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- int ret; -- -- ipu_buttress_restore(isp); -- -- if (isp->ipc_reinit) { -- struct ipu_buttress *b = &isp->buttress; -- -- isp->ipc_reinit = false; -- ret = ipu_buttress_ipc_reset(isp, &b->cse); -- if (ret) -- dev_err(dev, "IPC reset protocol failed!\n"); -- } -- -- return 0; --} -- --static const struct dev_pm_ops ipu7_pm_ops = { -- SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) -- RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL) --}; -- --static const struct pci_device_id ipu7_pci_tbl[] = { -- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, -- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, -- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, -- {0,} --}; --MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); -- --static const struct pci_error_handlers pci_err_handlers = { -- .reset_prepare = ipu7_pci_reset_prepare, -- .reset_done = ipu7_pci_reset_done, --}; -- --static struct pci_driver ipu7_pci_driver = { -- .name = IPU_NAME, -- .id_table = ipu7_pci_tbl, -- .probe = ipu7_pci_probe, -- .remove = ipu7_pci_remove, -- .driver = { -- .pm = &ipu7_pm_ops, -- }, -- .err_handler = &pci_err_handlers, --}; -- --module_pci_driver(ipu7_pci_driver); -- --MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); --MODULE_AUTHOR("Bingbu Cao "); --MODULE_AUTHOR("Tianshu Qiu "); --MODULE_AUTHOR("Qingwu Zhang "); --MODULE_AUTHOR("Intel"); --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION("Intel ipu7 pci driver"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7.h b/drivers/media/pci/intel/ipu7/ipu7.h -deleted file mode 100644 -index 454d702ef3d4..000000000000 ---- a/drivers/media/pci/intel/ipu7/ipu7.h -+++ /dev/null -@@ -1,259 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2013 - 2025 Intel Corporation -- */ -- --#ifndef IPU7_H --#define IPU7_H -- --#include --#include --#include -- --#include "ipu7-buttress.h" -- --struct ipu7_bus_device; --struct pci_dev; --struct firmware; -- --#define IPU_NAME "intel-ipu7" --#define IPU_MEDIA_DEV_MODEL_NAME "ipu7" -- --#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin" --#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin" --#define IPU8_FIRMWARE_NAME "intel/ipu/ipu8_fw.bin" -- --#define IPU7_ISYS_NUM_STREAMS 12 -- --#define IPU7_PCI_ID 0x645d --#define IPU7P5_PCI_ID 0xb05d --#define IPU8_PCI_ID 0xd719 -- --#define FW_LOG_BUF_SIZE (2 * 1024 * 1024) -- --enum ipu_version { -- IPU_VER_INVALID = 0, -- IPU_VER_7 = 1, -- IPU_VER_7P5 = 2, -- IPU_VER_8 = 3, --}; -- --static inline bool is_ipu7p5(u8 hw_ver) --{ -- return hw_ver == IPU_VER_7P5; --} -- --static inline bool is_ipu7(u8 hw_ver) --{ -- return hw_ver == IPU_VER_7; --} -- --static inline bool is_ipu8(u8 hw_ver) --{ -- return hw_ver == IPU_VER_8; --} -- --#define IPU_UNIFIED_OFFSET 0 -- --/* -- * ISYS DMA can overshoot. For higher resolutions over allocation is one line -- * but it must be at minimum 1024 bytes. Value could be different in -- * different versions / generations thus provide it via platform data. -- */ --#define IPU_ISYS_OVERALLOC_MIN 1024 -- --#define IPU_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */ --#define IPU_FW_CODE_REGION_START 0x4000000 /* 64MB */ --#define IPU_FW_CODE_REGION_END (IPU_FW_CODE_REGION_START + \ -- IPU_FW_CODE_REGION_SIZE) /* 80MB */ -- --struct ipu7_device { -- struct pci_dev *pdev; -- struct list_head devices; -- struct ipu7_bus_device *isys; -- struct ipu7_bus_device *psys; -- struct ipu_buttress buttress; -- -- const struct firmware *cpd_fw; -- const char *cpd_fw_name; -- /* Only for non-secure mode. */ -- void *fw_code_region; -- -- void __iomem *base; -- void __iomem *pb_base; --#ifdef CONFIG_DEBUG_FS -- struct dentry *ipu7_dir; --#endif -- u8 hw_ver; -- bool ipc_reinit; -- bool secure_mode; -- bool ipu7_bus_ready_to_probe; --}; -- --#define IPU_DMA_MASK 39 --#define IPU_LIB_CALL_TIMEOUT_MS 2000 --#define IPU_PSYS_CMD_TIMEOUT_MS 2000 --#define IPU_PSYS_OPEN_CLOSE_TIMEOUT_US 50 --#define IPU_PSYS_OPEN_CLOSE_RETRY (10000 / IPU_PSYS_OPEN_CLOSE_TIMEOUT_US) -- --#define IPU_ISYS_NAME "isys" --#define IPU_PSYS_NAME "psys" -- --#define IPU_MMU_ADDR_BITS 32 --/* FW is accessible within the first 2 GiB only in non-secure mode. */ --#define IPU_MMU_ADDR_BITS_NON_SECURE 31 -- --#define IPU7_IS_MMU_NUM 4U --#define IPU7_PS_MMU_NUM 4U --#define IPU7P5_IS_MMU_NUM 4U --#define IPU7P5_PS_MMU_NUM 4U --#define IPU8_IS_MMU_NUM 5U --#define IPU8_PS_MMU_NUM 4U --#define IPU_MMU_MAX_NUM 5U /* max(IS, PS) */ --#define IPU_MMU_MAX_TLB_L1_STREAMS 40U --#define IPU_MMU_MAX_TLB_L2_STREAMS 40U --#define IPU_ZLX_MAX_NUM 32U --#define IPU_ZLX_POOL_NUM 8U --#define IPU_UAO_PLANE_MAX_NUM 64U -- --/* -- * To maximize the IOSF utlization, IPU need to send requests in bursts. -- * At the DMA interface with the buttress, there are CDC FIFOs with burst -- * collection capability. CDC FIFO burst collectors have a configurable -- * threshold and is configured based on the outcome of performance measurements. -- * -- * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 -- * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 -- * -- * Threshold values are pre-defined and are arrived at after performance -- * evaluations on a type of IPU -- */ --#define IPU_MAX_VC_IOSF_PORTS 4 -- --/* -- * IPU must configure correct arbitration mechanism related to the IOSF VC -- * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on -- * stall and 1 means stall until the request is completed. -- */ --#define IPU_BTRS_ARB_MODE_TYPE_REARB 0 --#define IPU_BTRS_ARB_MODE_TYPE_STALL 1 -- --/* Currently chosen arbitration mechanism for VC0 */ --#define IPU_BTRS_ARB_STALL_MODE_VC0 IPU_BTRS_ARB_MODE_TYPE_REARB -- --/* Currently chosen arbitration mechanism for VC1 */ --#define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB -- --struct ipu7_isys_subdev_pdata; -- --/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ --#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) --/* Max L2 blocks per stream */ --#define IPU_MMUV2_MAX_L2_BLOCKS 2 --/* Max L1 blocks per stream */ --#define IPU_MMUV2_MAX_L1_BLOCKS 16 --#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \ -- IPU_MMUV2_MAX_L2_BLOCKS) --/* Entries per L1 block */ --#define MMUV2_ENTRIES_PER_L1_BLOCK 16 --#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) --#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE -- --struct ipu7_mmu_hw { -- char name[32]; -- -- void __iomem *base; -- void __iomem *zlx_base; -- void __iomem *uao_base; -- -- u32 offset; -- u32 zlx_offset; -- u32 uao_offset; -- -- u32 info_bits; -- u32 refill; -- u32 collapse_en_bitmap; -- u32 at_sp_arb_cfg; -- -- u32 l1_block; -- u32 l2_block; -- -- u8 nr_l1streams; -- u8 nr_l2streams; -- u32 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS]; -- u32 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS]; -- -- u8 zlx_nr; -- u32 zlx_axi_pool[IPU_ZLX_POOL_NUM]; -- u32 zlx_en[IPU_ZLX_MAX_NUM]; -- u32 zlx_conf[IPU_ZLX_MAX_NUM]; -- -- u32 uao_p_num; -- u32 uao_p2tlb[IPU_UAO_PLANE_MAX_NUM]; --}; -- --struct ipu7_mmu_pdata { -- u32 nr_mmus; -- struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -- int mmid; --}; -- --struct ipu7_isys_csi2_pdata { -- void __iomem *base; --}; -- --struct ipu7_isys_internal_csi2_pdata { -- u32 nports; -- u32 const *offsets; -- u32 gpreg; --}; -- --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC --struct ipu7_isys_internal_tpg_pdata { -- u32 ntpgs; -- u32 const *offsets; -- u32 *sels; --}; --#endif -- --struct ipu7_hw_variants { -- unsigned long offset; -- u32 nr_mmus; -- struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -- u8 cdc_fifos; -- u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS]; -- u32 dmem_offset; -- u32 spc_offset; /* SPC offset from psys base */ --}; -- --struct ipu_isys_internal_pdata { -- struct ipu7_isys_internal_csi2_pdata csi2; --#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -- struct ipu7_isys_internal_tpg_pdata tpg; --#endif -- struct ipu7_hw_variants hw_variant; -- u32 num_parallel_streams; -- u32 isys_dma_overshoot; --}; -- --struct ipu7_isys_pdata { -- void __iomem *base; -- const struct ipu_isys_internal_pdata *ipdata; -- struct ipu7_isys_subdev_pdata *spdata; --}; -- --struct ipu_psys_internal_pdata { -- struct ipu7_hw_variants hw_variant; --}; -- --struct ipu7_psys_pdata { -- void __iomem *base; -- const struct ipu_psys_internal_pdata *ipdata; --}; -- --int request_cpd_fw(const struct firmware **firmware_p, const char *name, -- struct device *device); --void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -- struct ipu_psys_internal_pdata *psys_ipdata); --void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev); --#endif /* IPU7_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/Makefile b/drivers/media/pci/intel/ipu7/psys/Makefile -deleted file mode 100644 -index 33eb383a14bc..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/Makefile -+++ /dev/null -@@ -1,18 +0,0 @@ --# SPDX-License-Identifier: GPL-2.0 --# Copyright (c) 2017 - 2024 Intel Corporation. -- --is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) --ifeq ($(is_kernel_lt_6_10), 1) --ifneq ($(EXTERNAL_BUILD), 1) --src := $(srctree)/$(src) --endif --endif -- --intel-ipu7-psys-objs += ipu-psys.o \ -- ipu7-psys.o \ -- ipu7-fw-psys.o -- --obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o -- --ccflags-y += -I$(src)/ --ccflags-y += -I$(src)/../ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -deleted file mode 100644 -index 582e59c89d7b..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -+++ /dev/null -@@ -1,1545 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --// Copyright (C) 2013 - 2024 Intel Corporation -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include -- --#include "ipu7.h" --#include "ipu7-mmu.h" --#include "ipu7-bus.h" --#include "ipu7-buttress.h" --#include "ipu7-cpd.h" --#include "ipu7-dma.h" --#include "ipu7-fw-psys.h" --#include "ipu7-psys.h" --#include "ipu7-platform-regs.h" --#include "ipu7-syscom.h" --#include "ipu7-boot.h" -- --#define IPU_PSYS_NUM_DEVICES 4U -- --static int psys_runtime_pm_resume(struct device *dev); --static int psys_runtime_pm_suspend(struct device *dev); -- --#define IPU_FW_CALL_TIMEOUT_JIFFIES \ -- msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) -- --static dev_t ipu7_psys_dev_t; --static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); --static DEFINE_MUTEX(ipu7_psys_mutex); -- --static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) --{ -- struct vm_area_struct *vma; -- unsigned long start, end; -- int npages, array_size; -- struct page **pages; -- struct sg_table *sgt; -- int ret = -ENOMEM; -- int nr = 0; -- u32 flags; -- -- start = (unsigned long)attach->userptr; -- end = PAGE_ALIGN(start + attach->len); -- npages = PHYS_PFN(end - (start & PAGE_MASK)); -- array_size = npages * sizeof(struct page *); -- -- sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -- if (!sgt) -- return -ENOMEM; -- -- WARN_ON_ONCE(attach->npages); -- -- pages = kvzalloc(array_size, GFP_KERNEL); -- if (!pages) -- goto free_sgt; -- -- mmap_read_lock(current->mm); -- vma = vma_lookup(current->mm, start); -- if (unlikely(!vma)) { -- ret = -EFAULT; -- goto error_up_read; -- } -- mmap_read_unlock(current->mm); -- -- flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; -- nr = pin_user_pages_fast(start & PAGE_MASK, npages, -- flags, pages); -- if (nr < npages) -- goto error; -- -- attach->pages = pages; -- attach->npages = npages; -- -- ret = sg_alloc_table_from_pages(sgt, pages, npages, -- start & ~PAGE_MASK, attach->len, -- GFP_KERNEL); -- if (ret < 0) -- goto error; -- -- attach->sgt = sgt; -- -- return 0; -- --error_up_read: -- mmap_read_unlock(current->mm); --error: -- if (nr) -- unpin_user_pages(pages, nr); -- -- kvfree(pages); --free_sgt: -- kfree(sgt); -- -- pr_err("failed to get userpages:%d\n", ret); -- -- return ret; --} -- --static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) --{ -- if (!attach || !attach->userptr || !attach->sgt) -- return; -- -- unpin_user_pages(attach->pages, attach->npages); -- -- kvfree(attach->pages); -- -- sg_free_table(attach->sgt); -- kfree(attach->sgt); -- attach->sgt = NULL; --} -- --static int ipu7_dma_buf_attach(struct dma_buf *dbuf, -- struct dma_buf_attachment *attach) --{ -- struct ipu7_psys_kbuffer *kbuf = dbuf->priv; -- struct ipu7_dma_buf_attach *ipu7_attach; -- int ret; -- -- ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); -- if (!ipu7_attach) -- return -ENOMEM; -- -- ipu7_attach->len = kbuf->len; -- ipu7_attach->userptr = kbuf->userptr; -- -- attach->priv = ipu7_attach; -- -- ret = ipu7_psys_get_userpages(ipu7_attach); -- if (ret) { -- kfree(ipu7_attach); -- return ret; -- } -- -- return 0; --} -- --static void ipu7_dma_buf_detach(struct dma_buf *dbuf, -- struct dma_buf_attachment *attach) --{ -- struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -- -- ipu7_psys_put_userpages(ipu7_attach); -- kfree(ipu7_attach); -- attach->priv = NULL; --} -- --static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, -- enum dma_data_direction dir) --{ -- struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -- struct pci_dev *pdev = to_pci_dev(attach->dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu7_bus_device *adev = isp->psys; -- unsigned long attrs; -- int ret; -- -- attrs = DMA_ATTR_SKIP_CPU_SYNC; -- ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, -- attrs); -- if (ret) { -- dev_err(attach->dev, "pci buf map failed\n"); -- return ERR_PTR(-EIO); -- } -- -- dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, -- DMA_BIDIRECTIONAL); -- -- ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); -- if (ret) { -- dev_err(attach->dev, "ipu7 buf map failed\n"); -- return ERR_PTR(-EIO); -- } -- -- ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); -- -- return ipu7_attach->sgt; --} -- --static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, -- struct sg_table *sgt, -- enum dma_data_direction dir) --{ -- struct pci_dev *pdev = to_pci_dev(attach->dev); -- struct ipu7_device *isp = pci_get_drvdata(pdev); -- struct ipu7_bus_device *adev = isp->psys; -- -- ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); -- dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); --} -- --static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) --{ -- return -ENOTTY; --} -- --static void ipu7_dma_buf_release(struct dma_buf *buf) --{ -- struct ipu7_psys_kbuffer *kbuf = buf->priv; -- -- if (!kbuf) -- return; -- -- if (kbuf->db_attach) -- ipu7_psys_put_userpages(kbuf->db_attach->priv); -- -- kfree(kbuf); --} -- --static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, -- enum dma_data_direction dir) --{ -- return -ENOTTY; --} -- --static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) --{ -- struct dma_buf_attachment *attach; -- struct ipu7_dma_buf_attach *ipu7_attach; -- -- if (list_empty(&dmabuf->attachments)) -- return -EINVAL; -- -- attach = list_last_entry(&dmabuf->attachments, -- struct dma_buf_attachment, node); -- ipu7_attach = attach->priv; -- -- if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) -- return -EINVAL; -- -- map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); -- map->is_iomem = false; -- if (!map->vaddr) -- return -EINVAL; -- -- return 0; --} -- --static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) --{ -- struct dma_buf_attachment *attach; -- struct ipu7_dma_buf_attach *ipu7_attach; -- -- if (WARN_ON(list_empty(&dmabuf->attachments))) -- return; -- -- attach = list_last_entry(&dmabuf->attachments, -- struct dma_buf_attachment, node); -- ipu7_attach = attach->priv; -- -- if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || -- !ipu7_attach->npages)) -- return; -- -- vm_unmap_ram(map->vaddr, ipu7_attach->npages); --} -- --static struct dma_buf_ops ipu7_dma_buf_ops = { -- .attach = ipu7_dma_buf_attach, -- .detach = ipu7_dma_buf_detach, -- .map_dma_buf = ipu7_dma_buf_map, -- .unmap_dma_buf = ipu7_dma_buf_unmap, -- .release = ipu7_dma_buf_release, -- .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, -- .mmap = ipu7_dma_buf_mmap, -- .vmap = ipu7_dma_buf_vmap, -- .vunmap = ipu7_dma_buf_vunmap, --}; -- --static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) --{ -- u8 graph_id = 0; -- -- for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { -- if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) -- break; -- } -- -- if (graph_id == IPU_PSYS_NUM_STREAMS) -- return -EBUSY; -- -- fh->psys->graph_id[graph_id] = graph_id; -- return graph_id; --} -- --static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) --{ -- fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; --} -- --static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, -- struct ipu7_bus_device *adev) --{ -- if (tq->msg_task) -- ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), -- tq->msg_task, tq->task_dma_addr, 0); -- -- list_del(&tq->list); -- kfree(tq); --} -- --static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, -- struct ipu7_bus_device *adev) --{ -- struct ipu_psys_task_ack *ack; -- struct ipu_psys_task_ack *event; -- struct ipu_psys_task_ack *tmp; -- -- struct ipu_psys_task_queue *tq; -- struct ipu_psys_task_queue *tq_tmp; -- -- mutex_destroy(&ip->event_mutex); -- mutex_destroy(&ip->task_mutex); -- -- list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -- ipu7_psys_free_msg_task(tq, adev); -- } -- -- list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { -- ipu7_psys_free_msg_task(tq, adev); -- } -- -- list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -- list_del(&event->list); -- kfree(event); -- } -- -- list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { -- list_del(&ack->list); -- kfree(ack); -- } --} -- --static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, -- struct ipu7_bus_device *adev) --{ -- struct device *dev = &adev->auxdev.dev; -- struct ipu_psys_task_ack *event; -- struct ipu_psys_task_ack *tmp; -- struct ipu_psys_task_queue *tq; -- struct ipu_psys_task_queue *tq_tmp; -- u8 i; -- -- INIT_LIST_HEAD(&ip->event_list); -- INIT_LIST_HEAD(&ip->ack_list); -- -- INIT_LIST_HEAD(&ip->tq_running_list); -- INIT_LIST_HEAD(&ip->tq_list); -- -- for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { -- event = kzalloc(sizeof(*event), GFP_KERNEL); -- if (!event) -- goto event_cleanup; -- -- list_add(&event->list, &ip->event_list); -- } -- -- for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { -- tq = kzalloc(sizeof(*tq), GFP_KERNEL); -- if (!tq) -- goto tq_cleanup; -- -- list_add(&tq->list, &ip->tq_list); -- -- tq->msg_task = -- ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), -- &tq->task_dma_addr, GFP_KERNEL, 0); -- -- if (!tq->msg_task) { -- dev_err(dev, "Failed to allocate msg task.\n"); -- goto tq_cleanup; -- } -- } -- -- init_completion(&ip->graph_open); -- init_completion(&ip->graph_close); -- -- return 0; -- --tq_cleanup: -- list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -- ipu7_psys_free_msg_task(tq, adev); -- } -- --event_cleanup: -- list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -- list_del(&event->list); -- kfree(event); -- } -- -- return -ENOMEM; --} -- --static int ipu7_psys_open(struct inode *inode, struct file *file) --{ -- struct ipu7_psys *psys = inode_to_ipu_psys(inode); -- struct device *dev = &psys->adev->auxdev.dev; -- struct ipu7_psys_fh *fh; -- struct ipu7_psys_stream *ip; -- int ret; -- -- fh = kzalloc(sizeof(*fh), GFP_KERNEL); -- if (!fh) -- return -ENOMEM; -- -- ip = kzalloc(sizeof(*ip), GFP_KERNEL); -- if (!ip) { -- ret = -ENOMEM; -- goto alloc_failed; -- } -- -- ret = ipu7_psys_stream_init(ip, psys->adev); -- if (ret) -- goto stream_init_failed; -- -- fh->ip = ip; -- ip->fh = fh; -- -- fh->psys = psys; -- -- file->private_data = fh; -- -- mutex_init(&fh->mutex); -- INIT_LIST_HEAD(&fh->bufmap); -- init_waitqueue_head(&fh->wait); -- -- mutex_init(&ip->task_mutex); -- mutex_init(&ip->event_mutex); -- -- mutex_lock(&psys->mutex); -- -- ret = ipu7_psys_get_graph_id(fh); -- if (ret < 0) -- goto open_failed; -- -- fh->ip->graph_id = ret; -- -- ret = pm_runtime_get_sync(dev); -- if (ret < 0) { -- dev_err(dev, "Runtime PM failed (%d)\n", ret); -- goto rpm_put; -- } -- -- list_add_tail(&fh->list, &psys->fhs); -- -- mutex_unlock(&psys->mutex); -- -- return 0; -- --rpm_put: -- pm_runtime_put(dev); -- ipu7_psys_put_graph_id(fh); -- --open_failed: -- ipu7_psys_stream_deinit(ip, psys->adev); -- -- mutex_destroy(&fh->mutex); -- -- mutex_unlock(&psys->mutex); -- --stream_init_failed: -- kfree(ip); -- --alloc_failed: -- kfree(fh); -- -- return ret; --} -- --static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, -- struct ipu7_psys_kbuffer *kbuf) --{ -- if (!kbuf) -- return; -- -- kbuf->valid = false; -- if (kbuf->kaddr) { -- struct iosys_map dmap; -- -- iosys_map_set_vaddr(&dmap, kbuf->kaddr); -- dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); -- } -- -- if (!kbuf->userptr) -- ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, -- DMA_BIDIRECTIONAL, 0); -- -- if (kbuf->sgt) -- dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -- kbuf->sgt, -- DMA_BIDIRECTIONAL); -- if (kbuf->db_attach) -- dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -- dma_buf_put(kbuf->dbuf); -- -- kbuf->db_attach = NULL; -- kbuf->dbuf = NULL; -- kbuf->sgt = NULL; --} -- --static int ipu7_psys_release(struct inode *inode, struct file *file) --{ -- struct ipu7_psys *psys = inode_to_ipu_psys(inode); -- struct ipu7_psys_fh *fh = file->private_data; -- struct ipu7_psys_kbuffer *kbuf, *kbuf0; -- struct dma_buf_attachment *dba; -- -- mutex_lock(&fh->mutex); -- /* clean up buffers */ -- if (!list_empty(&fh->bufmap)) { -- list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { -- list_del(&kbuf->list); -- dba = kbuf->db_attach; -- -- /* Unmap and release buffers */ -- if (kbuf->dbuf && dba) { -- ipu7_psys_kbuf_unmap(fh, kbuf); -- } else { -- if (dba) -- ipu7_psys_put_userpages(dba->priv); -- kfree(kbuf); -- } -- } -- } -- mutex_unlock(&fh->mutex); -- -- ipu7_psys_stream_deinit(fh->ip, psys->adev); -- -- mutex_lock(&psys->mutex); -- list_del(&fh->list); -- -- ipu7_psys_put_graph_id(fh); -- kfree(fh->ip); -- -- mutex_unlock(&psys->mutex); -- mutex_destroy(&fh->mutex); -- kfree(fh); -- -- pm_runtime_put_sync(&psys->adev->auxdev.dev); -- -- return 0; --} -- --static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, -- struct ipu7_psys_fh *fh) --{ -- struct device *dev = &fh->psys->adev->auxdev.dev; -- struct ipu7_psys_kbuffer *kbuf; -- -- DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -- struct dma_buf *dbuf; -- int ret; -- -- if (!buf->base.userptr) { -- dev_err(dev, "Buffer allocation not supported\n"); -- return -EINVAL; -- } -- -- if (!PAGE_ALIGNED(buf->base.userptr)) { -- dev_err(dev, "Not page-aligned userptr is not supported\n"); -- return -EINVAL; -- } -- -- kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -- if (!kbuf) -- return -ENOMEM; -- -- kbuf->len = buf->len; -- kbuf->userptr = buf->base.userptr; -- kbuf->flags = buf->flags; -- -- exp_info.ops = &ipu7_dma_buf_ops; -- exp_info.size = kbuf->len; -- exp_info.flags = O_RDWR; -- exp_info.priv = kbuf; -- -- dbuf = dma_buf_export(&exp_info); -- if (IS_ERR(dbuf)) { -- kfree(kbuf); -- return PTR_ERR(dbuf); -- } -- -- ret = dma_buf_fd(dbuf, 0); -- if (ret < 0) { -- dma_buf_put(dbuf); -- return ret; -- } -- -- kbuf->fd = ret; -- buf->base.fd = ret; -- buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; -- buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; -- kbuf->flags = buf->flags; -- -- mutex_lock(&fh->mutex); -- list_add(&kbuf->list, &fh->bufmap); -- mutex_unlock(&fh->mutex); -- -- dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", -- buf->base.userptr, buf->len, buf->base.fd); -- -- return 0; --} -- --static int --ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) --{ -- return 0; --} -- --static struct ipu7_psys_kbuffer * --ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) --{ -- struct ipu7_psys_kbuffer *kbuf; -- -- list_for_each_entry(kbuf, &fh->bufmap, list) { -- if (kbuf->fd == fd) -- return kbuf; -- } -- -- return NULL; --} -- --static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, -- struct ipu7_psys_kbuffer *kbuf) --{ -- struct device *dev = &fh->psys->adev->auxdev.dev; -- -- if (!kbuf || fd != kbuf->fd) { -- dev_err(dev, "invalid kbuffer\n"); -- return -EINVAL; -- } -- -- /* From now on it is not safe to use this kbuffer */ -- ipu7_psys_kbuf_unmap(fh, kbuf); -- -- list_del(&kbuf->list); -- -- if (!kbuf->userptr) -- kfree(kbuf); -- -- dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); -- -- return 0; --} -- --static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, -- struct ipu7_psys_kbuffer *kbuf) --{ -- struct ipu7_psys *psys = fh->psys; -- struct device *dev = &psys->adev->isp->pdev->dev; -- struct dma_buf *dbuf; -- struct iosys_map dmap; -- int ret; -- -- dbuf = dma_buf_get(fd); -- if (IS_ERR(dbuf)) -- return -EINVAL; -- -- if (!kbuf) { -- /* This fd isn't generated by ipu7_psys_getbuf, it -- * is a new fd. Create a new kbuf item for this fd, and -- * add this kbuf to bufmap list. -- */ -- kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -- if (!kbuf) { -- ret = -ENOMEM; -- goto mapbuf_fail; -- } -- -- list_add(&kbuf->list, &fh->bufmap); -- } -- -- /* fd valid and found, need remap */ -- if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { -- dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", -- fd, kbuf); -- ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -- if (ret) -- goto mapbuf_fail; -- -- kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -- /* changed external dmabuf */ -- if (!kbuf) { -- kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -- if (!kbuf) { -- ret = -ENOMEM; -- goto mapbuf_fail; -- } -- list_add(&kbuf->list, &fh->bufmap); -- } -- } -- -- if (kbuf->sgt) { -- dev_dbg(dev, "fd %d has been mapped!\n", fd); -- dma_buf_put(dbuf); -- goto mapbuf_end; -- } -- -- kbuf->dbuf = dbuf; -- -- if (kbuf->len == 0) -- kbuf->len = kbuf->dbuf->size; -- -- kbuf->fd = fd; -- -- kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); -- if (IS_ERR(kbuf->db_attach)) { -- ret = PTR_ERR(kbuf->db_attach); -- dev_err(dev, "dma buf attach failed\n"); -- goto attach_fail; -- } -- -- kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, -- DMA_BIDIRECTIONAL); -- if (IS_ERR_OR_NULL(kbuf->sgt)) { -- ret = -EINVAL; -- kbuf->sgt = NULL; -- dev_err(dev, "dma buf map attachment failed\n"); -- goto kbuf_map_fail; -- } -- -- if (!kbuf->userptr) { -- ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, -- DMA_BIDIRECTIONAL, 0); -- if (ret) { -- dev_dbg(dev, "ipu7 buf map failed\n"); -- goto kbuf_map_fail; -- } -- } -- -- kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); -- -- /* no need vmap for imported dmabufs */ -- if (!kbuf->userptr) -- goto mapbuf_end; -- -- dmap.is_iomem = false; -- ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); -- if (ret) { -- dev_err(dev, "dma buf vmap failed\n"); -- goto kbuf_map_fail; -- } -- kbuf->kaddr = dmap.vaddr; -- --mapbuf_end: -- dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", -- __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, -- kbuf->len); -- kbuf->valid = true; -- -- return 0; -- --kbuf_map_fail: -- if (!IS_ERR_OR_NULL(kbuf->sgt)) { -- if (!kbuf->userptr) -- ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, -- DMA_BIDIRECTIONAL, 0); -- dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -- kbuf->sgt, -- DMA_BIDIRECTIONAL); -- } -- dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -- --attach_fail: -- list_del(&kbuf->list); -- if (!kbuf->userptr) -- kfree(kbuf); -- --mapbuf_fail: -- dma_buf_put(dbuf); -- -- dev_err(dev, "%s failed for fd %d\n", __func__, fd); -- return ret; --} -- --static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) --{ -- long ret; -- struct ipu7_psys_kbuffer *kbuf; -- -- dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); -- -- mutex_lock(&fh->mutex); -- kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -- ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); -- mutex_unlock(&fh->mutex); -- -- return ret; --} -- --static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) --{ -- struct device *dev = &fh->psys->adev->auxdev.dev; -- struct ipu7_psys_kbuffer *kbuf; -- long ret; -- -- dev_dbg(dev, "IOC_UNMAPBUF\n"); -- -- mutex_lock(&fh->mutex); -- kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -- if (!kbuf) { -- dev_err(dev, -- "buffer with fd %d not found\n", fd); -- mutex_unlock(&fh->mutex); -- return -EINVAL; -- } -- ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -- mutex_unlock(&fh->mutex); -- -- return ret; --} -- --static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, -- struct ipu7_psys_fh *fh) --{ -- struct ipu7_psys *psys = fh->psys; -- int ret = 0; -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -- dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -- fh->ip->graph_state, fh->ip->graph_id); -- return -EINVAL; -- } -- -- if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { -- dev_err(&psys->dev, "nodes is wrong\n"); -- return -EINVAL; -- } -- -- if (copy_from_user(fh->ip->nodes, graph->nodes, -- graph->num_nodes * sizeof(*graph->nodes))) { -- dev_err(&psys->dev, "Failed to copy nodes\n"); -- return -EINVAL; -- } -- -- reinit_completion(&fh->ip->graph_open); -- -- ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); -- if (ret) { -- dev_err(&psys->dev, "Failed to open graph %d\n", -- fh->ip->graph_id); -- return ret; -- } -- -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; -- -- ret = wait_for_completion_timeout(&fh->ip->graph_open, -- IPU_FW_CALL_TIMEOUT_JIFFIES); -- if (!ret) { -- dev_err(&psys->dev, "Open graph %d timeout\n", -- fh->ip->graph_id); -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- return -ETIMEDOUT; -- } -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -- dev_err(&psys->dev, "Failed to set graph\n"); -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- return -EINVAL; -- } -- -- graph->graph_id = fh->ip->graph_id; -- -- return 0; --} -- --static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) --{ -- struct ipu7_psys *psys = fh->psys; -- int ret = 0; -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -- dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -- fh->ip->graph_state, fh->ip->graph_id); -- return -EINVAL; -- } -- -- reinit_completion(&fh->ip->graph_close); -- -- ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); -- if (ret) { -- dev_err(&psys->dev, "Failed to close graph %d\n", -- fh->ip->graph_id); -- return ret; -- } -- -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; -- -- ret = wait_for_completion_timeout(&fh->ip->graph_close, -- IPU_FW_CALL_TIMEOUT_JIFFIES); -- if (!ret) { -- dev_err(&psys->dev, "Close graph %d timeout\n", -- fh->ip->graph_id); -- return -ETIMEDOUT; -- } -- -- if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -- dev_err(&psys->dev, "Failed to close graph\n"); -- fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- return -EINVAL; -- } -- -- return 0; --} -- --static struct ipu_psys_task_queue * --ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, -- struct ipu_psys_task_request *task) --{ -- struct device *dev = &ip->fh->psys->dev; -- struct ipu7_psys_kbuffer *kbuf = NULL; -- struct ipu_psys_task_queue *tq; -- int fd, prevfd = -1; -- u32 i; -- -- if (task->term_buf_count > MAX_GRAPH_TERMINALS) { -- dev_err(dev, "num_teminal_buffer is too large\n"); -- return NULL; -- } -- -- mutex_lock(&ip->task_mutex); -- if (list_empty(&ip->tq_list)) { -- dev_err(dev, "No available take queue for stream %p\n", ip); -- goto unlock; -- } -- -- tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, -- list); -- -- if (copy_from_user(tq->task_buffers, -- task->task_buffers, -- task->term_buf_count * -- sizeof(*task->task_buffers))) { -- dev_err(dev, "failed to copy task buffers\n"); -- goto unlock; -- } -- -- for (i = 0; i < task->term_buf_count; i++) { -- fd = tq->task_buffers[i].term_buf.base.fd; -- kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); -- if (!kbuf) { -- dev_err(dev, "fd %d not found\n", fd); -- goto unlock; -- } -- tq->ipu7_addr[i] = kbuf->dma_addr -- + tq->task_buffers[i].term_buf.data_offset; -- -- if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & -- IPU_BUFFER_FLAG_NO_FLUSH)) -- continue; -- -- prevfd = fd; -- -- if (kbuf->kaddr) -- clflush_cache_range(kbuf->kaddr, kbuf->len); -- } -- -- dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); -- -- list_move_tail(&tq->list, &ip->tq_running_list); -- -- mutex_unlock(&ip->task_mutex); -- return tq; -- --unlock: -- mutex_unlock(&ip->task_mutex); -- return NULL; --} -- --static long ipu_psys_task_request(struct ipu_psys_task_request *task, -- struct ipu7_psys_fh *fh) --{ -- struct ipu7_psys *psys = fh->psys; -- struct ipu_psys_task_queue *tq; -- int ret = 0; -- -- if (task->term_buf_count == 0 || !task->task_buffers) { -- dev_err(&psys->dev, "task_buffer is NULL\n"); -- return -EINVAL; -- } -- -- tq = ipu7_psys_get_task_queue(fh->ip, task); -- if (!tq) { -- dev_err(&psys->dev, "Failed to get task queue\n"); -- return -EINVAL; -- } -- -- ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); -- if (ret) { -- dev_err(&psys->dev, "Failed to request task %d\n", -- fh->ip->graph_id); -- mutex_lock(&fh->ip->task_mutex); -- list_move_tail(&tq->list, &fh->ip->tq_list); -- mutex_unlock(&fh->ip->task_mutex); -- return ret; -- } -- -- tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; -- -- return 0; --} -- --static unsigned int ipu7_psys_poll(struct file *file, -- struct poll_table_struct *wait) --{ -- struct ipu7_psys_fh *fh = file->private_data; -- struct device *dev = &fh->psys->adev->auxdev.dev; -- struct ipu7_psys_stream *ip = fh->ip; -- unsigned int res = 0; -- -- dev_dbg(dev, "ipu psys poll\n"); -- -- poll_wait(file, &fh->wait, wait); -- -- mutex_lock(&ip->event_mutex); -- if (!list_empty(&ip->ack_list)) -- res = POLLIN; -- mutex_unlock(&ip->event_mutex); -- -- dev_dbg(dev, "ipu psys poll res %u\n", res); -- -- return res; --} -- --static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, -- unsigned long arg) --{ -- union { -- struct ipu_psys_graph_info graph; -- struct ipu_psys_task_request task; -- struct ipu_psys_buffer buf; -- struct ipu_psys_event ev; -- } karg; -- struct ipu7_psys_fh *fh = file->private_data; -- long err = 0; -- void __user *up = (void __user *)arg; -- bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && -- cmd != IPU_IOC_GRAPH_CLOSE); -- -- if (copy) { -- if (_IOC_SIZE(cmd) > sizeof(karg)) -- return -ENOTTY; -- -- if (_IOC_DIR(cmd) & _IOC_WRITE) { -- err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); -- if (err) -- return -EFAULT; -- } -- } -- -- switch (cmd) { -- case IPU_IOC_MAPBUF: -- err = ipu7_psys_mapbuf(arg, fh); -- break; -- case IPU_IOC_UNMAPBUF: -- err = ipu7_psys_unmapbuf(arg, fh); -- break; -- case IPU_IOC_GETBUF: -- err = ipu7_psys_getbuf(&karg.buf, fh); -- break; -- case IPU_IOC_PUTBUF: -- err = ipu7_psys_putbuf(&karg.buf, fh); -- break; -- case IPU_IOC_GRAPH_OPEN: -- err = ipu_psys_graph_open(&karg.graph, fh); -- break; -- case IPU_IOC_GRAPH_CLOSE: -- err = ipu_psys_graph_close(arg, fh); -- break; -- case IPU_IOC_TASK_REQUEST: -- err = ipu_psys_task_request(&karg.task, fh); -- break; -- case IPU_IOC_DQEVENT: -- err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); -- break; -- default: -- err = -ENOTTY; -- break; -- } -- -- if (err) -- return err; -- -- if (copy && _IOC_DIR(cmd) & _IOC_READ) -- if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) -- return -EFAULT; -- -- return 0; --} -- --static const struct file_operations ipu7_psys_fops = { -- .open = ipu7_psys_open, -- .release = ipu7_psys_release, -- .unlocked_ioctl = ipu7_psys_ioctl, -- .poll = ipu7_psys_poll, -- .owner = THIS_MODULE, --}; -- --static void ipu7_psys_dev_release(struct device *dev) --{ --} -- --static int psys_runtime_pm_resume(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -- unsigned long flags; -- int ret; -- -- if (!psys) -- return 0; -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- if (psys->ready) { -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- return 0; -- } -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- return ret; -- -- if (!ipu_buttress_auth_done(adev->isp)) { -- dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); -- return 0; -- } -- -- ipu7_psys_setup_hw(psys); -- -- ipu7_psys_subdomains_power(psys, 1); -- -- ret = ipu7_boot_start_fw(psys->adev); -- if (ret) { -- dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); -- return ret; -- } -- -- ret = ipu7_fw_psys_open(psys); -- if (ret) { -- dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); -- return ret; -- } -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- psys->ready = 1; -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- return 0; --} -- --static int psys_runtime_pm_suspend(struct device *dev) --{ -- struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -- struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -- unsigned long flags; -- -- if (!psys) -- return 0; -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- if (!psys->ready) { -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- return 0; -- } -- psys->ready = 0; -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- ipu7_fw_psys_close(psys); -- -- ipu7_boot_stop_fw(psys->adev); -- -- ipu7_psys_subdomains_power(psys, 0); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- -- return 0; --} -- --/* The following PM callbacks are needed to enable runtime PM in IPU PCI -- * device resume, otherwise, runtime PM can't work in PCI resume from -- * S3 state. -- */ --static int psys_resume(struct device *dev) --{ -- return 0; --} -- --static int psys_suspend(struct device *dev) --{ -- struct ipu7_psys *psys = dev_get_drvdata(dev); -- unsigned long flags; -- int ret = 0; -- -- spin_lock_irqsave(&psys->ready_lock, flags); -- if (psys->ready) -- ret = -EBUSY; -- spin_unlock_irqrestore(&psys->ready_lock, flags); -- -- return ret; --} -- --static const struct dev_pm_ops psys_pm_ops = { -- .runtime_suspend = psys_runtime_pm_suspend, -- .runtime_resume = psys_runtime_pm_resume, -- .suspend = psys_suspend, -- .resume = psys_resume, --}; -- --#ifdef CONFIG_DEBUG_FS --static int psys_fw_log_init(struct ipu7_psys *psys) --{ -- struct device *dev = &psys->adev->auxdev.dev; -- struct psys_fw_log *fw_log; -- void *log_buf; -- -- if (psys->fw_log) -- return 0; -- -- fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -- if (!fw_log) -- return -ENOMEM; -- -- mutex_init(&fw_log->mutex); -- -- log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!log_buf) -- return -ENOMEM; -- -- fw_log->head = log_buf; -- fw_log->addr = log_buf; -- fw_log->count = 0; -- fw_log->size = 0; -- -- psys->fw_log = fw_log; -- -- return 0; --} -- --static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -- loff_t *pos) --{ -- struct ipu7_psys *psys = file->private_data; -- struct psys_fw_log *fw_log = psys->fw_log; -- struct device *dev = &psys->adev->auxdev.dev; -- u32 log_size; -- void *buf; -- int ret = 0; -- -- if (!fw_log) -- return 0; -- -- buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -- if (!buf) -- return -ENOMEM; -- -- mutex_lock(&fw_log->mutex); -- if (!fw_log->size) { -- dev_warn(dev, "no available fw log\n"); -- mutex_unlock(&fw_log->mutex); -- goto free_and_return; -- } -- -- if (fw_log->size > FW_LOG_BUF_SIZE) -- log_size = FW_LOG_BUF_SIZE; -- else -- log_size = fw_log->size; -- -- memcpy(buf, fw_log->addr, log_size); -- dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); -- mutex_unlock(&fw_log->mutex); -- -- ret = simple_read_from_buffer(userbuf, size, pos, buf, -- log_size); --free_and_return: -- kvfree(buf); -- -- return ret; --} -- --static const struct file_operations psys_fw_log_fops = { -- .open = simple_open, -- .owner = THIS_MODULE, -- .read = fwlog_read, -- .llseek = default_llseek, --}; -- --static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) --{ -- struct dentry *file; -- struct dentry *dir; -- -- dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -- if (IS_ERR(dir)) -- return -ENOMEM; -- -- file = debugfs_create_file("fwlog", 0400, -- dir, psys, &psys_fw_log_fops); -- if (IS_ERR(file)) -- goto err; -- -- psys->debugfsdir = dir; -- -- return 0; --err: -- debugfs_remove_recursive(dir); -- return -ENOMEM; --} --#endif -- --static const struct bus_type ipu7_psys_bus = { -- .name = "intel-ipu7-psys", --}; -- --static int ipu7_psys_probe(struct auxiliary_device *auxdev, -- const struct auxiliary_device_id *auxdev_id) --{ -- struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -- struct device *dev = &auxdev->dev; -- struct ipu7_psys *psys; -- unsigned int minor; -- unsigned int i; -- int ret; -- -- if (!adev->isp->ipu7_bus_ready_to_probe) -- return -EPROBE_DEFER; -- -- ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, -- IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); -- if (ret) { -- dev_err(dev, "can't alloc psys chrdev region (%d)\n", -- ret); -- return ret; -- } -- -- ret = pm_runtime_resume_and_get(&auxdev->dev); -- if (ret < 0) -- return ret; -- -- ret = ipu7_mmu_hw_init(adev->mmu); -- if (ret) -- goto out_unregister_chr_region; -- -- mutex_lock(&ipu7_psys_mutex); -- -- minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); -- if (minor == IPU_PSYS_NUM_DEVICES) { -- dev_err(dev, "too many devices\n"); -- goto out_unlock; -- } -- -- psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); -- if (!psys) { -- ret = -ENOMEM; -- goto out_unlock; -- } -- -- for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) -- psys->graph_id[i] = INVALID_STREAM_ID; -- -- adev->auxdrv_data = -- (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -- adev->auxdrv = to_auxiliary_drv(dev->driver); -- -- psys->adev = adev; -- psys->pdata = adev->pdata; -- -- cdev_init(&psys->cdev, &ipu7_psys_fops); -- psys->cdev.owner = ipu7_psys_fops.owner; -- -- ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); -- if (ret) { -- dev_err(dev, "cdev_add failed (%d)\n", ret); -- goto out_unlock; -- } -- -- set_bit(minor, ipu7_psys_devices); -- -- spin_lock_init(&psys->ready_lock); -- -- psys->ready = 0; -- psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; -- -- mutex_init(&psys->mutex); -- INIT_LIST_HEAD(&psys->fhs); -- -- ret = ipu7_fw_psys_init(psys); -- if (ret) { -- dev_err(dev, "FW init failed(%d)\n", ret); -- goto out_mutex_destroy; -- } -- -- psys->dev.bus = &ipu7_psys_bus; -- psys->dev.parent = dev; -- psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); -- psys->dev.release = ipu7_psys_dev_release; -- dev_set_name(&psys->dev, "ipu7-psys%d", minor); -- ret = device_register(&psys->dev); -- if (ret < 0) { -- dev_err(&psys->dev, "psys device_register failed\n"); -- goto out_fw_release; -- } -- -- dev_set_drvdata(dev, psys); --#ifdef CONFIG_DEBUG_FS -- psys_fw_log_init(psys); -- ipu7_psys_init_debugfs(psys); --#endif -- dev_info(dev, "IPU psys probe done.\n"); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- pm_runtime_put(&auxdev->dev); -- -- return 0; -- --out_fw_release: -- ipu7_fw_psys_release(psys); --out_mutex_destroy: -- mutex_destroy(&psys->mutex); -- cdev_del(&psys->cdev); --out_unlock: -- /* Safe to call even if the init is not called */ -- mutex_unlock(&ipu7_psys_mutex); -- -- ipu7_mmu_hw_cleanup(adev->mmu); -- --out_unregister_chr_region: -- unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -- pm_runtime_put(&auxdev->dev); -- -- return ret; --} -- --static void ipu7_psys_remove(struct auxiliary_device *auxdev) --{ -- struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); -- struct device *dev = &auxdev->dev; --#ifdef CONFIG_DEBUG_FS -- struct ipu7_device *isp = psys->adev->isp; -- -- if (isp->ipu7_dir) -- debugfs_remove_recursive(psys->debugfsdir); --#endif -- -- mutex_lock(&ipu7_psys_mutex); -- ipu7_fw_psys_release(psys); -- device_unregister(&psys->dev); -- clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); -- cdev_del(&psys->cdev); -- mutex_unlock(&ipu7_psys_mutex); -- -- mutex_destroy(&psys->mutex); -- -- unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -- -- dev_info(dev, "removed\n"); --} -- --static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) --{ -- struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -- struct device *dev = &psys->adev->auxdev.dev; -- void __iomem *base = psys->pdata->base; -- u32 status, state; -- int r; -- -- mutex_lock(&psys->mutex); -- r = pm_runtime_get_if_in_use(dev); -- if (!r || WARN_ON_ONCE(r < 0)) { -- mutex_unlock(&psys->mutex); -- return IRQ_NONE; -- } -- -- state = ipu7_boot_get_boot_state(adev); -- if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { -- dev_warn(&psys->dev, "error state %u\n", state); -- } else { -- status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); -- writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -- -- if (status & IRQ_FROM_LOCAL_FW) -- ipu7_psys_handle_events(psys); -- } -- -- pm_runtime_put(dev); -- mutex_unlock(&psys->mutex); -- -- return IRQ_HANDLED; --} -- --static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { -- .isr_threaded = psys_isr_threaded, -- .wake_isr_thread = true, --}; -- --static const struct auxiliary_device_id ipu7_psys_id_table[] = { -- { -- .name = "intel_ipu7.psys", -- .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, -- }, -- { } --}; -- --MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); -- --static struct auxiliary_driver ipu7_psys_driver = { -- .name = IPU_PSYS_NAME, -- .probe = ipu7_psys_probe, -- .remove = ipu7_psys_remove, -- .id_table = ipu7_psys_id_table, -- .driver = { -- .pm = &psys_pm_ops, -- }, --}; -- --module_auxiliary_driver(ipu7_psys_driver); -- --MODULE_AUTHOR("Bingbu Cao "); --MODULE_AUTHOR("Qingwu Zhang "); --MODULE_AUTHOR("Tianshu Qiu "); -- --MODULE_LICENSE("GPL"); --MODULE_DESCRIPTION("Intel ipu7 processing system driver"); --MODULE_IMPORT_NS("INTEL_IPU7"); --MODULE_IMPORT_NS("DMA_BUF"); -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -deleted file mode 100644 -index 15ba548ecd83..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -+++ /dev/null -@@ -1,603 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --// Copyright (C) 2016 - 2024 Intel Corporation -- --#include --#include -- --#include -- --#include "abi/ipu7_fw_common_abi.h" --#include "abi/ipu7_fw_msg_abi.h" --#include "abi/ipu7_fw_psys_config_abi.h" -- --#include "ipu7-boot.h" --#include "ipu7-bus.h" --#include "ipu7-dma.h" --#include "ipu7-fw-psys.h" --#include "ipu7-syscom.h" --#include "ipu7-psys.h" -- --#define TLV_TYPE(type) ((u32)(type) & 0x3FU) --#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) -- --/* -- * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. -- */ --#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) -- --/* -- * Special node resource ID to identify a generic external node. -- * Required when there is a link to/from IPU and that node. -- */ --#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) -- --int ipu7_fw_psys_init(struct ipu7_psys *psys) --{ -- struct ipu7_bus_device *adev = psys->adev; -- struct device *dev = &adev->auxdev.dev; -- struct ipu7_syscom_context *syscom; -- struct ipu7_psys_config *psys_config; -- struct syscom_queue_config *queue_configs; -- dma_addr_t psys_config_dma_addr; -- u32 freq; -- int i, num_queues, ret; -- -- /* Allocate and init syscom context. */ -- syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -- GFP_KERNEL); -- if (!syscom) -- return -ENOMEM; -- -- adev->syscom = syscom; -- syscom->num_input_queues = FWPS_MSG_ABI_MAX_INPUT_QUEUES; -- syscom->num_output_queues = FWPS_MSG_ABI_MAX_OUTPUT_QUEUES; -- num_queues = syscom->num_input_queues + syscom->num_output_queues; -- queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -- GFP_KERNEL); -- if (!queue_configs) { -- ipu7_fw_psys_release(psys); -- return -ENOMEM; -- } -- syscom->queue_configs = queue_configs; -- queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = -- IPU_PSYS_ACK_QUEUE_SIZE; -- queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = -- IPU_PSYS_OUT_MSG_SIZE; -- queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = -- IPU_PSYS_LOG_QUEUE_SIZE; -- queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = -- IPU_PSYS_OUT_MSG_SIZE; -- queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = -- IPU_PSYS_CMD_QUEUE_SIZE; -- queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = -- FWPS_MSG_HOST2FW_MAX_SIZE; -- queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; -- queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = -- 0; -- -- for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { -- queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; -- queue_configs[i].token_size_in_bytes = -- sizeof(struct ia_gofo_msg_indirect); -- } -- -- /* Allocate PSYS subsys config. */ -- psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), -- &psys_config_dma_addr, GFP_KERNEL, 0); -- if (!psys_config) { -- dev_err(dev, "Failed to allocate psys subsys config.\n"); -- ipu7_fw_psys_release(psys); -- return -ENOMEM; -- } -- psys->subsys_config = psys_config; -- psys->subsys_config_dma_addr = psys_config_dma_addr; -- memset(psys_config, 0, sizeof(struct ipu7_psys_config)); -- ret = ipu_buttress_get_psys_freq(adev->isp, &freq); -- if (ret) { -- dev_err(dev, "Failed to get PSYS frequency.\n"); -- ipu7_fw_psys_release(psys); -- return ret; -- } -- -- ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -- freq, psys_config_dma_addr, 1U); -- if (ret) -- ipu7_fw_psys_release(psys); -- return ret; --} -- --void ipu7_fw_psys_release(struct ipu7_psys *psys) --{ -- struct ipu7_bus_device *adev = psys->adev; -- -- ipu7_boot_release_boot_config(adev); -- if (psys->subsys_config) { -- ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), -- psys->subsys_config, -- psys->subsys_config_dma_addr, 0); -- psys->subsys_config = NULL; -- psys->subsys_config_dma_addr = 0; -- } --} -- --static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) --{ -- const struct ia_gofo_msg_header_ack *ack_header; -- u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -- int ret; -- -- ret = ipu7_fw_psys_event_handle(psys, buffer); -- if (ret) -- return ret; -- -- ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -- -- if (ack_header->header.tlv_header.tlv_type == type) -- return 0; -- -- return -EAGAIN; --} -- --static int ipu7_fw_dev_open(struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_dev_open *token; -- -- dev_dbg(&psys->dev, "send_token: fw psys open\n"); -- -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); -- token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -- token->header.user_token = 0; -- -- token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; -- token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | -- IPU_MSG_DEVICE_OPEN_SEND_IRQ); -- token->enable_power_gating = 0; -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_open(struct ipu7_psys *psys) --{ -- u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -- int ret; -- -- ret = ipu7_fw_dev_open(psys); -- if (ret) { -- dev_err(&psys->dev, "failed to open PSYS dev.\n"); -- return ret; -- } -- psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; -- -- do { -- usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -- IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -- ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); -- if (!ret) { -- dev_dbg(&psys->dev, "dev open done.\n"); -- psys->dev_state = IPU_MSG_DEV_STATE_OPEN; -- return 0; -- } -- } while (retry--); -- -- if (!retry) -- dev_err(&psys->dev, "wait dev open timeout!\n"); -- -- return ret; --} -- --static int ipu7_fw_dev_close(struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_dev_close *token; -- -- dev_dbg(&psys->dev, "send_token: fw psys close\n"); -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); -- token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -- token->header.user_token = 0; -- -- token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | -- IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --void ipu7_fw_psys_close(struct ipu7_psys *psys) --{ -- u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -- int ret; -- -- ret = ipu7_fw_dev_close(psys); -- if (ret) { -- dev_err(&psys->dev, "failed to close PSYS dev.\n"); -- return; -- } -- -- psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; -- -- do { -- usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -- IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -- ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); -- if (!ret) { -- dev_dbg(&psys->dev, "dev close done.\n"); -- psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; -- return; -- } -- } while (retry--); -- -- if (!retry) -- dev_err(&psys->dev, "wait dev close timeout!\n"); --} -- --static void --ipu7_fw_psys_build_node_profile(const struct node_profile *profile, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_cb_profile *cb_profile = -- (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; -- u16 buf_size = sizeof(*cb_profile); -- -- memcpy(cb_profile->profile_base.teb, profile->teb, -- sizeof(cb_profile->profile_base.teb)); -- -- memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); -- memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); -- memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); -- -- cb_profile->profile_base.tlv_header.tlv_type = -- TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); -- cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- -- *buf_ptr_ptr += buf_size; --} -- --/* skip term, return false */ --static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; -- u16 buf_size = sizeof(*msg_term); -- -- if (!term->term_id && !term->buf_size) -- return false; -- -- memset(msg_term, 0, sizeof(*msg_term)); -- msg_term->term_id = term->term_id; -- /* Disable progress message on connect terminals */ -- msg_term->event_req_bm = 0U; -- msg_term->payload_size = term->buf_size; -- -- msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); -- msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- -- *buf_ptr_ptr += buf_size; -- return true; --} -- --/* When skip processing node, just return false */ --static bool ipu7_fw_psys_build_node(const struct graph_node *node, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; -- u16 buf_size = sizeof(*msg_node); -- bool ret = false; -- u8 i = 0; -- u8 max_terms = 0; -- -- memset(msg_node, 0, sizeof(*msg_node)); -- /** -- * Pass node info to FW, do not check for external IP and ISYS -- * As FW expects a external node -- */ -- if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && -- node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -- if (node->profiles[0].teb[0] == 0U) -- return false; -- } -- -- /** -- * Sanity check for dummy node, TEB should set to required one -- */ -- if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || -- node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -- if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || -- node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) -- return false; -- } -- -- msg_node->node_rsrc_id = node->node_rsrc_id; -- msg_node->node_ctx_id = node->node_ctx_id; -- msg_node->num_frags = 1; -- -- *buf_ptr_ptr += buf_size; -- -- msg_node->profiles_list.head_offset = -- (u16)((uintptr_t)*buf_ptr_ptr -- - (uintptr_t)&msg_node->profiles_list); -- for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { -- ipu7_fw_psys_build_node_profile(&node->profiles[i], -- buf_ptr_ptr); -- msg_node->profiles_list.num_elems++; -- } -- -- msg_node->terms_list.head_offset = -- (u16)((uintptr_t)*buf_ptr_ptr - -- (uintptr_t)&msg_node->terms_list); -- max_terms = ARRAY_SIZE(node->terminals); -- for (i = 0; i < max_terms && i < node->num_terms; i++) { -- ret = ipu7_fw_psys_build_node_term(&node->terminals[i], -- buf_ptr_ptr); -- if (ret) -- msg_node->terms_list.num_elems++; -- } -- -- buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; -- msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); -- msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- -- return true; --} -- --static bool ipu7_fw_psys_build_link(const struct graph_link *link, -- void **buf_ptr_ptr) --{ -- struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; -- -- if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && -- !link->ep_src.term_id && !link->ep_dst.term_id) -- return false; -- -- msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; -- msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; -- -- msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; -- msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; -- -- msg_link->foreign_key = link->foreign_key; -- msg_link->streaming_mode = link->streaming_mode; -- msg_link->pbk_id = link->pbk_id; -- msg_link->pbk_slot_id = link->pbk_slot_id; -- msg_link->delayed_link = link->delayed_link; -- -- *buf_ptr_ptr += sizeof(*msg_link); -- -- msg_link->link_options.num_elems = 0; -- msg_link->link_options.head_offset = -- (u16)((uintptr_t)*buf_ptr_ptr - -- (uintptr_t)&msg_link->link_options); -- msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); -- msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); -- -- return true; --} -- --int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -- struct ipu7_psys *psys, -- struct ipu7_psys_stream *ip) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- void *buf_ptr; -- struct ipu7_msg_graph_open *graph_open; -- u32 buf_size = 0; -- bool ret = false; -- u8 i = 0; -- -- dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); -- buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!buf_ptr) -- return -ENODATA; -- -- graph_open = (struct ipu7_msg_graph_open *)buf_ptr; -- -- memset(graph_open, 0, sizeof(*graph_open)); -- graph_open->graph_id = ip->graph_id; -- graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP -- | IPU_MSG_GRAPH_OPEN_SEND_IRQ); -- -- buf_ptr += sizeof(*graph_open); -- graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr -- - (uintptr_t)&graph_open->nodes); -- for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { -- ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); -- if (ret) -- graph_open->nodes.num_elems++; -- } -- -- graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr -- - (uintptr_t)&graph_open->links); -- for (i = 0; i < ARRAY_SIZE(graph->links); i++) { -- ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); -- if (ret) -- graph_open->links.num_elems++; -- } -- -- buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); -- graph_open->header.tlv_header.tlv_type = -- TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); -- graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -- graph_open->header.user_token = 0; -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_graph_close *token; -- -- dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); -- token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -- token->header.user_token = 0; -- -- token->graph_id = graph_id; -- token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP -- | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -- struct ipu7_psys_stream *ip, -- struct ipu_psys_task_queue *tq, -- struct ipu7_psys *psys) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- struct ipu7_msg_task *msg = tq->msg_task; -- struct ia_gofo_msg_indirect *ind; -- u32 node_q_id = ip->q_id[task->node_ctx_id]; -- u32 teb_hi, teb_lo; -- u64 teb; -- u8 i, term_id; -- u8 num_terms; -- -- ind = ipu7_syscom_get_token(ctx, node_q_id); -- if (!ind) -- return -ENODATA; -- -- memset(msg, 0, sizeof(*msg)); -- msg->graph_id = task->graph_id; -- msg->node_ctx_id = task->node_ctx_id; -- msg->profile_idx = 0U; /* Only one profile on HKR */ -- msg->frame_id = task->frame_id; -- msg->frag_id = 0U; /* No frag, set to 0 */ -- /* -- * Each task has a flag indicating if ack needed, it may be used to -- * reduce interrupts if multiple CBs supported. -- */ -- msg->req_done_msg = 1; -- msg->req_done_irq = 1; -- -- memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, -- sizeof(task->payload_reuse_bm)); -- -- teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; -- teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; -- teb = (teb_lo | (((u64)teb_hi) << 32)); -- -- num_terms = ip->nodes[msg->node_ctx_id].num_terms; -- for (i = 0U; i < num_terms; i++) { -- term_id = tq->task_buffers[i].term_id; -- if ((1U << term_id) & teb) -- msg->term_buffers[term_id] = tq->ipu7_addr[i]; -- } -- -- msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); -- msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); -- msg->header.user_token = (uintptr_t)tq; -- -- ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); -- ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); -- ind->header.msg_options.num_elems = 0; -- ind->header.msg_options.head_offset = 0; -- ind->ref_header = msg->header.tlv_header; -- ind->ref_msg_ptr = tq->task_dma_addr; -- -- ipu7_syscom_put_token(ctx, node_q_id); -- -- ipu_buttress_wakeup_ps_uc(psys->adev->isp); -- -- return 0; --} -- --int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) --{ -- struct ipu7_syscom_context *ctx = psys->adev->syscom; -- void *token; -- -- token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); -- -- ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -- return 0; --} -- --int ipu7_fw_psys_get_log(struct ipu7_psys *psys) --{ -- void *token; -- struct ia_gofo_msg_log *log_msg; -- u8 msg_type, msg_len; -- u32 count, fmt_id; -- struct device *dev = &psys->adev->auxdev.dev; -- struct psys_fw_log *fw_log = psys->fw_log; -- u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -- -- token = ipu7_syscom_get_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- if (!token) -- return -ENODATA; -- -- while (token) { -- log_msg = (struct ia_gofo_msg_log *)token; -- -- msg_type = log_msg->header.tlv_header.tlv_type; -- msg_len = log_msg->header.tlv_header.tlv_len32; -- if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -- dev_warn(dev, "Invalid msg data from Log queue!\n"); -- -- count = log_msg->log_info_ts.log_info.log_counter; -- fmt_id = log_msg->log_info_ts.log_info.fmt_id; -- if (count > fw_log->count + 1) -- dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -- count, fw_log->count); -- -- if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -- dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -- ipu7_syscom_put_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- return -EIO; -- } -- -- if (log_size + fw_log->head - fw_log->addr > -- FW_LOG_BUF_SIZE) -- fw_log->head = fw_log->addr; -- -- memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -- sizeof(struct ia_gofo_msg_log_info_ts)); -- -- fw_log->count = count; -- fw_log->head += log_size; -- fw_log->size += log_size; -- -- ipu7_syscom_put_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- -- token = ipu7_syscom_get_token(psys->adev->syscom, -- FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -- }; -- -- return 0; --} -- -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -deleted file mode 100644 -index c43f235386d1..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -+++ /dev/null -@@ -1,42 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0-only */ --/* -- * Copyright (C) 2016 - 2024 Intel Corporation -- */ -- --#ifndef IPU7_FW_PSYS_H --#define IPU7_FW_PSYS_H -- --#include "abi/ipu7_fw_common_abi.h" --#include "abi/ipu7_fw_msg_abi.h" -- --#include "ipu7-syscom.h" -- --#include -- --#define IPU_PSYS_MAX_GRAPH_NUMS (8U) --#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS -- --struct ipu7_msg_to_str { -- const enum ipu7_msg_type type; -- const char *msg; --}; -- --struct ipu7_psys; --struct ipu7_psys_stream; --struct ipu_psys_task_queue; -- --int ipu7_fw_psys_init(struct ipu7_psys *psys); --void ipu7_fw_psys_release(struct ipu7_psys *psys); --int ipu7_fw_psys_open(struct ipu7_psys *psys); --void ipu7_fw_psys_close(struct ipu7_psys *psys); --int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -- struct ipu7_psys *psys, -- struct ipu7_psys_stream *ip); --int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); --int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -- struct ipu7_psys_stream *ip, -- struct ipu_psys_task_queue *tq, -- struct ipu7_psys *psys); --int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); --int ipu7_fw_psys_get_log(struct ipu7_psys *psys); --#endif /* IPU7_FW_PSYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -deleted file mode 100644 -index 1ce52ae75a4c..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -+++ /dev/null -@@ -1,398 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0 --// Copyright (C) 2020 - 2024 Intel Corporation -- --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-bus.h" --#include "ipu7-buttress-regs.h" --#include "ipu7-psys.h" --#include "ipu7-platform-regs.h" --#include "ipu7-fw-psys.h" -- --#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) -- --static const struct ipu7_msg_to_str ps_fw_msg[] = { -- {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, -- {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, -- {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, -- {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, -- {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, -- {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, -- {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, -- {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, -- {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, -- {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, -- {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, -- {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, -- {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, -- {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, -- {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, --}; -- --void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) --{ -- struct device *dev = &psys->adev->auxdev.dev; -- struct ipu7_device *isp = psys->adev->isp; -- void __iomem *base = isp->base; -- u32 mask; -- u32 val; -- u32 req; -- int ret; -- -- /* power domain req */ -- mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : -- IPU7_PSYS_DOMAIN_POWER_MASK; -- req = on ? mask : 0; -- val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); -- -- dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", -- on ? "UP" : "DOWN", val); -- if ((val & mask) == req) { -- dev_warn(dev, -- "psys sub-domains power already in request state.\n"); -- return; -- } -- writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); -- ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, -- val, -- !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && -- ((val & mask) == req), -- 100, DOMAIN_POWE_TIMEOUT_US); -- if (ret) -- dev_err(dev, -- "Psys sub-domains power %s timeout! status: 0x%x\n", -- on ? "UP" : "DOWN", val); --} -- --void ipu7_psys_setup_hw(struct ipu7_psys *psys) --{ -- void __iomem *base = psys->pdata->base; -- u32 val = IRQ_FROM_LOCAL_FW; -- -- /* Enable TO_SW IRQ from FW */ -- writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -- writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); -- writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -- -- /* correct the initial printf configuration */ -- writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); --} -- --static struct ipu7_psys_stream* --ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) --{ -- struct ipu7_psys_fh *fh; -- -- list_for_each_entry(fh, &psys->fhs, list) { -- if (fh->ip->graph_id == graph_id) -- return fh->ip; -- } -- -- return NULL; --} -- --static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, -- const void *buffer) --{ -- const struct ipu7_msg_graph_open_ack *ack_msg = -- (const struct ipu7_msg_graph_open_ack *)buffer; -- const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -- struct ipu7_psys_stream *ip; -- struct device *dev = &psys->dev; -- const struct ia_gofo_tlv_header *msg_tlv_item; -- u16 num_items; -- u16 head_offset; -- u32 i; -- -- dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); -- -- if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -- if (!ip) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { -- dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -- goto open_graph_exit; -- } -- -- num_items = ack_header->header.msg_options.num_elems; -- if (!num_items) { -- dev_err(dev, "%s, num_items is 0\n", __func__); -- goto open_graph_exit; -- } -- -- head_offset = ack_header->header.msg_options.head_offset; -- msg_tlv_item = (const struct ia_gofo_tlv_header *) -- ((u8 *)&ack_header->header.msg_options + head_offset); -- -- if (!msg_tlv_item) { -- dev_err(dev, "%s: failed to get tlv item\n", __func__); -- goto open_graph_exit; -- } -- -- for (i = 0U; i < num_items; i++) { -- u32 option_type = msg_tlv_item->tlv_type; -- u32 option_bytes = msg_tlv_item->tlv_len32 * -- TLV_ITEM_ALIGNMENT; -- struct ipu7_msg_graph_open_ack_task_q_info *msg_option = -- (void *)msg_tlv_item; -- -- switch (option_type) { -- case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: -- /* -- * Should do check that: -- * - Each managed node has a queue ID -- * - Queue ID's are sane -- */ -- dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", -- msg_option->node_ctx_id, msg_option->q_id); -- ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; -- break; -- default: -- /* -- * Only one option supported -- */ -- dev_err(dev, "not supported %u\n", option_type); -- break; -- } -- -- msg_tlv_item = (struct ia_gofo_tlv_header *) -- (((u8 *)msg_tlv_item) + option_bytes); -- } -- -- ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; -- --open_graph_exit: -- complete(&ip->graph_open); --} -- --static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, -- void *buffer, u32 error) --{ -- const struct ipu7_msg_task_done *ack_msg = -- (const struct ipu7_msg_task_done *)buffer; -- const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -- const struct ia_gofo_msg_header *msg_header = &ack_header->header; -- struct ipu_psys_task_queue *tq; -- struct device *dev = &psys->dev; -- struct ipu7_psys_stream *ip; -- struct ipu_psys_task_ack *event; -- -- if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return -EINVAL; -- } -- -- ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -- if (!ip) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return -EINVAL; -- } -- -- tq = (void *)(uintptr_t)msg_header->user_token; -- if (!tq) { -- dev_err(dev, "%s: task_token is NULL\n", __func__); -- return -EINVAL; -- } -- -- mutex_lock(&ip->task_mutex); -- if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { -- dev_err(dev, "%s: graph %d node %d error %d\n", __func__, -- ack_msg->graph_id, ack_msg->node_ctx_id, -- tq->task_state); -- -- list_move_tail(&tq->list, &ip->tq_list); -- mutex_unlock(&ip->task_mutex); -- return -ENOENT; -- } -- -- tq->task_state = IPU_MSG_TASK_STATE_DONE; -- dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); -- -- list_move_tail(&tq->list, &ip->tq_list); -- mutex_unlock(&ip->task_mutex); -- -- mutex_lock(&ip->event_mutex); -- if (!list_empty(&ip->event_list)) { -- event = list_first_entry(&ip->event_list, -- struct ipu_psys_task_ack, list); -- event->graph_id = ack_msg->graph_id; -- event->node_ctx_id = ack_msg->node_ctx_id; -- event->frame_id = ack_msg->frame_id; -- event->err_code = error; -- -- list_move_tail(&event->list, &ip->ack_list); -- } else { -- dev_dbg(dev, "event queue is full, add new one\n"); -- event = kzalloc(sizeof(*event), GFP_KERNEL); -- -- if (event) { -- event->graph_id = ack_msg->graph_id; -- event->node_ctx_id = ack_msg->node_ctx_id; -- event->frame_id = ack_msg->frame_id; -- event->err_code = error; -- -- list_add_tail(&event->list, &ip->ack_list); -- } else { -- dev_err(dev, "failed to alloc event buf\n"); -- } -- } -- mutex_unlock(&ip->event_mutex); -- -- wake_up_interruptible(&ip->fh->wait); -- -- return 0; --} -- --static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, -- void *buffer) --{ -- struct ipu7_msg_graph_close_ack *ack_msg = -- (struct ipu7_msg_graph_close_ack *)buffer; -- struct device *dev = &psys->dev; -- struct ipu7_psys_stream *ip; -- -- dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); -- -- if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -- if (!ip) { -- dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -- return; -- } -- -- if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { -- dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -- goto graph_close_exit; -- } -- -- ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -- --graph_close_exit: -- complete(&ip->graph_close); --} -- --void ipu7_psys_handle_events(struct ipu7_psys *psys) --{ -- const struct ia_gofo_msg_header_ack *ack_header; -- u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -- struct device *dev = &psys->dev; -- u32 error = 0; -- int ret = 0; -- -- do { --#ifdef ENABLE_FW_OFFLINE_LOGGER -- ipu7_fw_psys_get_log(psys); --#endif -- ret = ipu7_fw_psys_event_handle(psys, buffer); -- if (ret) -- break; -- -- ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -- -- dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, -- ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); -- -- if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { -- dev_err(dev, "group %d, code %d, detail: %d, %d\n", -- ack_header->err.err_group, -- ack_header->err.err_code, -- ack_header->err.err_detail[0], -- ack_header->err.err_detail[1]); -- error = (ack_header->err.err_group == -- IPU_MSG_ERR_GROUP_TASK) ? -- IPU_PSYS_EVT_ERROR_FRAME : -- IPU_PSYS_EVT_ERROR_INTERNAL; -- } -- -- switch (ack_header->header.tlv_header.tlv_type) { -- case IPU_MSG_TYPE_GRAPH_OPEN_ACK: -- ipu7_psys_handle_graph_open_ack(psys, buffer); -- break; -- case IPU_MSG_TYPE_TASK_DONE: -- ipu7_psys_handle_task_done(psys, buffer, error); -- break; -- case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: -- ipu7_psys_handle_graph_close_ack(psys, buffer); -- break; -- case IPU_MSG_TYPE_GENERAL_ERR: -- /* already printed the log above for general error */ -- break; -- default: -- dev_err(&psys->dev, "Unknown type %d\n", -- ack_header->header.tlv_header.tlv_type); -- } -- } while (1); --} -- --static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, -- struct ipu_psys_event *event) --{ -- struct ipu_psys_task_ack *ack; -- -- mutex_lock(&ip->event_mutex); -- /* Check if there is already an event in the list */ -- if (list_empty(&ip->ack_list)) { -- mutex_unlock(&ip->event_mutex); -- return -EAGAIN; -- } -- -- ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); -- -- event->graph_id = ack->graph_id; -- event->node_ctx_id = ack->node_ctx_id; -- event->frame_id = ack->frame_id; -- event->error = ack->err_code; -- -- list_move_tail(&ack->list, &ip->event_list); -- mutex_unlock(&ip->event_mutex); -- -- dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", -- event->graph_id, event->node_ctx_id, event->frame_id); -- -- return 0; --} -- --long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -- struct ipu7_psys_fh *fh, unsigned int f_flags) --{ -- struct ipu7_psys *psys = fh->psys; -- int ret = 0; -- -- dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); -- -- if (!(f_flags & O_NONBLOCK)) { -- ret = wait_event_interruptible(fh->wait, -- !ipu7_psys_get_event(fh->ip, -- event)); -- if (ret == -ERESTARTSYS) -- return ret; -- } else { -- ret = ipu7_psys_get_event(fh->ip, event); -- } -- -- return ret; --} -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -deleted file mode 100644 -index a72ce4a7c5b9..000000000000 ---- a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -+++ /dev/null -@@ -1,184 +0,0 @@ --/* SPDX-License-Identifier: GPL-2.0 */ --/* Copyright (C) 2013 - 2024 Intel Corporation */ -- --#ifndef IPU7_PSYS_H --#define IPU7_PSYS_H -- --#include --#include --#include -- --#include "ipu7.h" --#include "ipu7-fw-psys.h" -- --#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq -- --#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 --#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 --#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 --#define IPU_PSYS_LOG_QUEUE_SIZE 256 --#define IPU_PSYS_OUT_MSG_SIZE 256 -- --/** -- * Each event from FW will be first queued into a -- * event queue, define the queue depth here -- */ --#define TASK_EVENT_QUEUE_SIZE 3U --/** -- * Each task queue from user will be first queued into -- * a task queue, define the queue depth here -- */ --#define TASK_REQUEST_QUEUE_SIZE 8U -- --#define INVALID_STREAM_ID 0xFF --/** -- * Task request queues per stream -- * -- * Each task will first assigned a task queue buffer here, -- * all the nodes will share the same task queue, maximum -- * queue will be full there. -- */ --struct ipu_psys_task_queue { -- struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; -- dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; -- -- struct ipu7_msg_task *msg_task; -- dma_addr_t task_dma_addr; -- -- struct list_head list; -- -- /* task state of each task input, represent ipu7_msg_task_state */ -- enum ipu7_msg_task_state task_state; --}; -- --struct psys_fw_log { -- struct mutex mutex; /* protect whole struct */ -- void *head; -- void *addr; -- u32 count; /* running counter of log */ -- u32 size; /* actual size of log content, in bits */ --}; -- --/** -- * Task quest event context -- * -- * Each task request should get its event ack from FW and save -- * to this structure and for user dequeue purpose. -- */ --struct ipu_psys_task_ack { -- u8 graph_id; /* graph id of the task request */ -- u8 node_ctx_id; /* logical node id */ -- u8 frame_id; /* frame id of the original task request */ -- -- struct list_head list; -- -- u32 err_code; /* error indication to user */ --}; -- --/** -- * stream here is equal to pipe, each stream has -- * its dedicated graph_id, and task request queue. -- * -- * For multiple stream supported design. -- */ --struct ipu7_psys_stream { -- struct ipu7_psys_fh *fh; -- -- u8 graph_id; /* graph_id on this stream */ -- -- /* Handle events from FW */ -- struct mutex event_mutex; -- struct list_head event_list; /* Reserved event list */ -- struct list_head ack_list; /* Received ack from FW */ -- -- /* Serialize task queue */ -- struct mutex task_mutex; -- struct list_head tq_list; /* Reserved task queue list */ -- struct list_head tq_running_list; /* Running task sent to FW */ -- -- u8 num_nodes; /* Number of enabled nodes */ -- struct graph_node nodes[MAX_GRAPH_NODES]; -- u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ -- -- struct completion graph_open; -- struct completion graph_close; -- -- /* Graph state, represent enum ipu7_msg_graph_state */ -- enum ipu7_msg_graph_state graph_state; --}; -- --struct task_struct; --struct ipu7_psys { -- struct cdev cdev; -- struct device dev; -- -- struct mutex mutex; /* Psys various */ -- int ready; /* psys fw status */ -- spinlock_t ready_lock; /* protect psys firmware state */ -- -- struct list_head fhs; -- -- struct ipu7_psys_pdata *pdata; -- struct ipu7_bus_device *adev; --#ifdef CONFIG_DEBUG_FS -- struct dentry *debugfsdir; --#endif -- -- unsigned long timeout; -- -- struct psys_fw_log *fw_log; -- -- /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ -- u8 graph_id[IPU_PSYS_NUM_STREAMS]; -- -- /* Device state, represent enum ipu7_msg_dev_state */ -- enum ipu7_msg_dev_state dev_state; -- -- struct ipu7_psys_config *subsys_config; -- dma_addr_t subsys_config_dma_addr; --}; -- --struct ipu7_psys_fh { -- struct ipu7_psys *psys; -- struct mutex mutex; /* Protects bufmap & kcmds fields */ -- struct list_head list; -- struct list_head bufmap; -- wait_queue_head_t wait; -- -- struct ipu7_psys_stream *ip; --}; -- --struct ipu7_dma_buf_attach { -- struct device *dev; -- u64 len; -- uintptr_t *userptr; -- struct sg_table *sgt; -- struct page **pages; -- size_t npages; --}; -- --struct ipu7_psys_kbuffer { -- u64 len; -- uintptr_t *userptr; -- u32 flags; -- int fd; -- void *kaddr; -- struct list_head list; -- dma_addr_t dma_addr; -- struct sg_table *sgt; -- struct dma_buf_attachment *db_attach; -- struct dma_buf *dbuf; -- bool valid; /* True when buffer is usable */ --}; -- --#define inode_to_ipu_psys(inode) \ -- container_of((inode)->i_cdev, struct ipu7_psys, cdev) -- --void ipu7_psys_setup_hw(struct ipu7_psys *psys); --void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); --void ipu7_psys_handle_events(struct ipu7_psys *psys); -- --long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -- struct ipu7_psys_fh *fh, unsigned int f_flags); -- --#endif /* IPU7_PSYS_H */ --- -2.43.0 - diff --git a/SPECS/kernel/0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu b/SPECS/kernel/0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu deleted file mode 100644 index 57f1c1782..000000000 --- a/SPECS/kernel/0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu +++ /dev/null @@ -1,173 +0,0 @@ -From 2e94f5756fed15080864a4cf953b6ef279e4fa38 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 4 Nov 2025 14:28:23 +0800 -Subject: [PATCH 1/2] Revert "media: i2c: max9x: uniform serdes driver - compilation" - -This reverts commit 9e46b764cbbd8def4373a51b40bf7abd62c736e3. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/isx031.c | 4 ++-- - drivers/media/i2c/max9x/max9295.c | 9 +++++---- - drivers/media/i2c/max9x/max9296.c | 2 +- - drivers/media/i2c/max9x/serdes.c | 22 ++++++++++++++++------ - 4 files changed, 24 insertions(+), 13 deletions(-) - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index 2d669e964b08..e613cdb06b83 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -370,7 +370,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); -+ ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); - return ret; - } - -@@ -411,7 +411,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return ret; - } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -- (u32)mode); -+ mode); - if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", - cur_mode, mode); -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index 8fbd4215e110..a5bacc3684a9 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -177,7 +177,7 @@ static int max9295_setup_gpio(struct max9x_common *common) - { - struct device *dev = common->dev; - int ret; -- struct max9x_gpio_pdata *gpio_pdata = NULL; -+ struct max9x_gpio_pdata *gpio_pdata; - - if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -@@ -697,14 +697,15 @@ static int max9295_remap_reset(struct max9x_common *common) - struct device *dev = common->dev; - struct max9x_pdata *pdata = dev->platform_data; - u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -- common->client->addr; -+ common->client->addr; - u32 virt_addr = common->client->addr; - - dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, - phys_addr); - -- TRY(ret, regmap_update_bits(common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -- FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ TRY(ret, regmap_update_bits( -+ common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); - - return 0; - } -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index a28aa190364d..41074d60cc01 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -730,7 +730,7 @@ static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned i - link_cfg = MAX9296_LINK_B; - else { - dev_err(dev, "No link was detected"); -- return -EINVAL; -+ return -1; - } - - dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index 8c1db87ed8c2..fce930a1fdea 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -133,7 +133,7 @@ static const struct of_device_id max9x_of_match[] = { - MODULE_DEVICE_TABLE(of, max9x_of_match); - - static const struct i2c_device_id max9x_id[] = { -- { "max9x", 0 }, -+ { "max9x", MAX9296 }, - { "max9296", MAX9296 }, - { "max96724", MAX96724 }, - { "max9295", MAX9295 }, -@@ -841,7 +841,6 @@ void max9x_destroy(struct max9x_common *common) - /* unregister devices? */ - - v4l2_async_unregister_subdev(&common->v4l.sd); -- v4l2_subdev_cleanup(&common->v4l.sd); - media_entity_cleanup(&common->v4l.sd.entity); - - i2c_mux_del_adapters(common->muxc); -@@ -1484,7 +1483,10 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); - -- return &common->v4l.ffmts[fmt->pad]; -+ if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -+ return &common->v4l.ffmts[fmt->pad]; -+ -+ return ERR_PTR(-EINVAL); - } - - static int max9x_get_fmt(struct v4l2_subdev *sd, -@@ -1560,7 +1562,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - for_each_active_route(&state->routing, route) { - if (route->source_pad != pad) - continue; -- if (unlikely(route->sink_pad >= common->v4l.num_pads)) { -+ if (route->sink_pad >= common->v4l.num_pads) { - ret = -EINVAL; - dev_err(common->dev, "Found invalid route sink_pad!"); - goto out_unlock; -@@ -2090,7 +2092,7 @@ int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) - struct device *dev = common->dev; - int ret; - -- if (unlikely(link_id >= common->num_serial_links)) -+ if (link_id >= common->num_serial_links) - return 0; - - serial_link = &common->serial_link[link_id]; -@@ -2411,9 +2413,13 @@ static int max9x_parse_subdev_pdata(struct max9x_common *common, - int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(10000); - -+ dev_dbg(common->dev, "try to select %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2427,7 +2433,7 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - usleep_range(1000, 1050); - - if (time_is_before_jiffies(timeout)) { -- dev_warn(common->dev, "select %d TIMEOUT", chan_id); -+ dev_dbg(common->dev, "select %d TIMEOUT", chan_id); - return -ETIMEDOUT; - } - } while (1); -@@ -2445,8 +2451,12 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - -+ dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - --- -2.43.0 - diff --git a/SPECS/kernel/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt b/SPECS/kernel/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt index ff861dba9..fbbb6d5a7 100644 --- a/SPECS/kernel/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt +++ b/SPECS/kernel/0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt @@ -1,4 +1,4 @@ -From f0341e34032802a88510f2d9a1cab712bd56058e Mon Sep 17 00:00:00 2001 +From 4c6e62ab34fa8228916e9a9c3bd9a6cee5d3d84e Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Sat, 27 Feb 2016 08:09:11 +0100 Subject: [PATCH 1/9] drm/i915: Use preempt_disable/enable_rt() where @@ -33,10 +33,10 @@ Signed-off-by: Sebastian Andrzej Siewior 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c -index 70ba7aa26bf4..b82e9fc51af0 100644 +index c15234c1d96e7..d0d9e2c7d5476 100644 --- a/drivers/gpu/drm/i915/display/intel_vblank.c +++ b/drivers/gpu/drm/i915/display/intel_vblank.c -@@ -312,6 +312,20 @@ static void intel_vblank_section_exit(struct intel_display *display) +@@ -315,6 +315,20 @@ static void intel_vblank_section_exit(struct intel_display *display) struct drm_i915_private *i915 = to_i915(display->drm); spin_unlock(&i915->uncore.lock); } @@ -57,7 +57,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 #else static void intel_vblank_section_enter(struct intel_display *display) { -@@ -320,6 +334,17 @@ static void intel_vblank_section_enter(struct intel_display *display) +@@ -323,6 +337,17 @@ static void intel_vblank_section_enter(struct intel_display *display) static void intel_vblank_section_exit(struct intel_display *display) { } @@ -75,7 +75,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 #endif static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, -@@ -356,10 +381,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, +@@ -359,10 +384,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, * timing critical raw register reads, potentially with * preemption disabled, so the following code must not block. */ @@ -89,7 +89,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 /* Get optional system timestamp before query. */ if (stime) -@@ -423,10 +448,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, +@@ -426,10 +451,10 @@ static bool i915_get_crtc_scanoutpos(struct drm_crtc *_crtc, if (etime) *etime = ktime_get(); @@ -103,7 +103,7 @@ index 70ba7aa26bf4..b82e9fc51af0 100644 /* * While in vblank, position will be negative -@@ -464,13 +489,11 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc) +@@ -467,13 +492,11 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc) unsigned long irqflags; int position; diff --git a/SPECS/kernel/0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov b/SPECS/kernel/0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov deleted file mode 100644 index 0db2923be..000000000 --- a/SPECS/kernel/0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov +++ /dev/null @@ -1,35 +0,0 @@ -From b003ca4cc1194119da149d36f7fc54b4737edae6 Mon Sep 17 00:00:00 2001 -From: Dongwon Kim -Date: Wed, 10 Sep 2025 20:48:45 -0700 -Subject: [PATCH] drm/virtio: Wait until the control and cursor queues are - fully emptied - -It is needed to wait until the queues are fully emptied before -doing freeze/suspend. - -Signed-off-by: Dongwon Kim ---- - drivers/gpu/drm/virtio/virtgpu_drv.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c -index 8f333ed92836..b28ecc19581d 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.c -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c -@@ -181,6 +181,13 @@ static int virtgpu_freeze(struct virtio_device *vdev) - flush_work(&vgdev->ctrlq.dequeue_work); - flush_work(&vgdev->cursorq.dequeue_work); - flush_work(&vgdev->config_changed_work); -+ -+ wait_event(vgdev->ctrlq.ack_queue, -+ vgdev->ctrlq.vq->num_free == vgdev->ctrlq.vq->num_max); -+ -+ wait_event(vgdev->cursorq.ack_queue, -+ vgdev->cursorq.vq->num_free == vgdev->cursorq.vq->num_max); -+ - vdev->config->del_vqs(vdev); - - return 0; --- -2.43.0 - diff --git a/SPECS/kernel/0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm b/SPECS/kernel/0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm deleted file mode 100644 index 28caeb708..000000000 --- a/SPECS/kernel/0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm +++ /dev/null @@ -1,155 +0,0 @@ -From ba4ba68864fde80935d47423761930085e275d14 Mon Sep 17 00:00:00 2001 -From: Dongwon Kim -Date: Tue, 28 Jun 2022 11:23:10 -0700 -Subject: [PATCH 1/2] drm/virtio: freeze and restore hooks to support suspend - and resume - -virtio device needs to delete before VM suspend happens -then reinitialize all virtqueues again upon resume - -Signed-off-by: Dongwon Kim ---- - drivers/gpu/drm/virtio/virtgpu_drv.c | 53 +++++++++++++++++++++++++++- - drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + - drivers/gpu/drm/virtio/virtgpu_kms.c | 25 +++++++++---- - 3 files changed, 72 insertions(+), 7 deletions(-) - -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c -index 71c6ccad4b99..905246316dc9 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.c -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c -@@ -163,6 +163,53 @@ static unsigned int features[] = { - VIRTIO_GPU_F_RESOURCE_BLOB, - VIRTIO_GPU_F_CONTEXT_INIT, - }; -+ -+#ifdef CONFIG_PM_SLEEP -+static int virtgpu_freeze(struct virtio_device *vdev) -+{ -+ struct drm_device *dev = vdev->priv; -+ struct virtio_gpu_device *vgdev = dev->dev_private; -+ int error; -+ -+ error = drm_mode_config_helper_suspend(dev); -+ if (error) { -+ DRM_ERROR("suspend error %d\n", error); -+ return error; -+ } -+ -+ flush_work(&vgdev->obj_free_work); -+ flush_work(&vgdev->ctrlq.dequeue_work); -+ flush_work(&vgdev->cursorq.dequeue_work); -+ flush_work(&vgdev->config_changed_work); -+ vdev->config->del_vqs(vdev); -+ -+ return 0; -+} -+ -+static int virtgpu_restore(struct virtio_device *vdev) -+{ -+ struct drm_device *dev = vdev->priv; -+ struct virtio_gpu_device *vgdev = dev->dev_private; -+ int error; -+ -+ error = virtio_gpu_find_vqs(vgdev); -+ if (error) { -+ DRM_ERROR("failed to find virt queues\n"); -+ return error; -+ } -+ -+ virtio_device_ready(vdev); -+ -+ error = drm_mode_config_helper_resume(dev); -+ if (error) { -+ DRM_ERROR("resume error %d\n", error); -+ return error; -+ } -+ -+ return 0; -+} -+#endif -+ - static struct virtio_driver virtio_gpu_driver = { - .feature_table = features, - .feature_table_size = ARRAY_SIZE(features), -@@ -171,7 +218,11 @@ static struct virtio_driver virtio_gpu_driver = { - .probe = virtio_gpu_probe, - .remove = virtio_gpu_remove, - .shutdown = virtio_gpu_shutdown, -- .config_changed = virtio_gpu_config_changed -+ .config_changed = virtio_gpu_config_changed, -+#ifdef CONFIG_PM_SLEEP -+ .freeze = virtgpu_freeze, -+ .restore = virtgpu_restore, -+#endif - }; - - static int __init virtio_gpu_driver_init(void) -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h -index f17660a71a3e..1279f998c8e0 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.h -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h -@@ -300,6 +300,7 @@ void virtio_gpu_deinit(struct drm_device *dev); - void virtio_gpu_release(struct drm_device *dev); - int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); - void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); -+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev); - - /* virtgpu_gem.c */ - int virtio_gpu_gem_object_open(struct drm_gem_object *obj, -diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c -index 7dfb2006c561..46963f050b88 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_kms.c -+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c -@@ -114,15 +114,29 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, - vgdev->num_capsets = num_capsets; - } - --int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) -+int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev) - { - struct virtqueue_info vqs_info[] = { - { "control", virtio_gpu_ctrl_ack }, - { "cursor", virtio_gpu_cursor_ack }, - }; -- struct virtio_gpu_device *vgdev; -- /* this will expand later */ -+ - struct virtqueue *vqs[2]; -+ int ret; -+ -+ ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); -+ if (ret) -+ return ret; -+ -+ vgdev->ctrlq.vq = vqs[0]; -+ vgdev->cursorq.vq = vqs[1]; -+ -+ return 0; -+} -+ -+int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) -+{ -+ struct virtio_gpu_device *vgdev; - u32 num_scanouts, num_capsets; - int ret = 0; - -@@ -206,13 +220,12 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) - DRM_INFO("features: %ccontext_init\n", - vgdev->has_context_init ? '+' : '-'); - -- ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); -+ ret = virtio_gpu_find_vqs(vgdev); - if (ret) { - DRM_ERROR("failed to find virt queues\n"); - goto err_vqs; - } -- vgdev->ctrlq.vq = vqs[0]; -- vgdev->cursorq.vq = vqs[1]; -+ - ret = virtio_gpu_alloc_vbufs(vgdev); - if (ret) { - DRM_ERROR("failed to alloc vbufs\n"); --- -2.43.0 - diff --git a/SPECS/kernel/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm b/SPECS/kernel/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm index 0de8c730e..7bd54b129 100644 --- a/SPECS/kernel/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm +++ b/SPECS/kernel/0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm @@ -1,6 +1,6 @@ -From 31de56f202bc6575b2519e08e705213a0f292a76 Mon Sep 17 00:00:00 2001 +From 8154dcb40dcdfcae5595c1b8217e5fbee14e43ba Mon Sep 17 00:00:00 2001 From: "Wong, Chee Yin" -Date: Tue, 6 Jan 2026 09:23:07 +0800 +Date: Tue, 6 Jan 2026 09:05:12 +0800 Subject: [PATCH] drm/xe: Upgrade PTL and BMG GuC to 70.55.3, MTL,LNL,DG2 GuC to 70.53.0 @@ -12,7 +12,7 @@ Signed-off-by: Wong, Chee Yin 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c -index 1e90098b6655..1021b932850f 100644 +index 622b76078567d..3bff84ab4b5d8 100644 --- a/drivers/gpu/drm/xe/xe_uc_fw.c +++ b/drivers/gpu/drm/xe/xe_uc_fw.c @@ -115,11 +115,11 @@ struct fw_blobs_by_type { @@ -21,7 +21,7 @@ index 1e90098b6655..1021b932850f 100644 #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ - fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 49, 4)) \ - fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 49, 4)) \ -- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 49, 4)) \ +- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \ - fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \ - fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \ + fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 55, 3)) \ diff --git a/SPECS/kernel/0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm b/SPECS/kernel/0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm deleted file mode 100644 index fc378ac2c..000000000 --- a/SPECS/kernel/0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm +++ /dev/null @@ -1,30 +0,0 @@ -From 34426e2fd079c65d4765b02fb4cacb912d0e833d Mon Sep 17 00:00:00 2001 -From: "Mazlan, Hazwan Arif" -Date: Mon, 3 Nov 2025 15:01:54 +0800 -Subject: [PATCH] drm/xe: Upgrade XE GuC to the latest upstream - -Signed-off-by: Mazlan, Hazwan Arif ---- - drivers/gpu/drm/xe/xe_uc_fw.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c -index 9bbdde604923..1e90098b6655 100644 ---- a/drivers/gpu/drm/xe/xe_uc_fw.c -+++ b/drivers/gpu/drm/xe/xe_uc_fw.c -@@ -115,9 +115,9 @@ struct fw_blobs_by_type { - #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED - - #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ -- fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 47, 0)) \ -- fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 45, 2)) \ -- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \ -+ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 49, 4)) \ -+ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 49, 4)) \ -+ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 49, 4)) \ - fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \ - fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \ - fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ --- -2.43.0 - diff --git a/SPECS/kernel/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov b/SPECS/kernel/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov deleted file mode 100644 index e77447566..000000000 --- a/SPECS/kernel/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov +++ /dev/null @@ -1,35 +0,0 @@ -From 7bfac7617b764daf36532e6a4575b4341507ef00 Mon Sep 17 00:00:00 2001 -From: "Kooran Paul, Princy" -Date: Fri, 8 Aug 2025 16:04:16 +0800 -Subject: [PATCH] drm/xe/xe_vm: bypass vm_bind failure as wa to enable hwspice - -Xe driver fails during vm_bind as the HW spice encode pipeline is using -an imported dmabuf. That's why same failure is not seen in other h264 -encoding tests on BMG/PTL. - -While validating bo during vm_bind, update the return status to 0 -instead of error as WA to allow the HW spice encode pipeline continue -work. - -Signed-off-by: Kasireddy, Vivek -Signed-off-by: Goh, Wei Khang1 ---- - drivers/gpu/drm/xe/xe_vm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c -index ec04bef8ae40..3e3b9a5c3056 100644 ---- a/drivers/gpu/drm/xe/xe_vm.c -+++ b/drivers/gpu/drm/xe/xe_vm.c -@@ -3501,7 +3501,7 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, - * how it was mapped on the CPU. Just assume is it - * potentially cached on CPU side. - */ -- return -EINVAL; -+ return 0; - } - - /* If a BO is protected it can only be mapped if the key is still valid */ --- -2.43.0 - diff --git a/SPECS/kernel/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm b/SPECS/kernel/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm new file mode 100644 index 000000000..7c388f538 --- /dev/null +++ b/SPECS/kernel/0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm @@ -0,0 +1,36 @@ +From 10f078fcb45e21c033d328b07df63b351f8ed529 Mon Sep 17 00:00:00 2001 +From: "Kooran Paul, Princy" +Date: Wed, 7 May 2025 15:21:15 +0800 +Subject: [PATCH 1/3] drm/xe/xe_vm: bypass vm_bind failure as wa to enable + hwspice + +Xe driver fails during vm_bind as the HW spice encode pipeline is using +an imported dmabuf. That's why same failure is not seen in other h264 +encoding tests on BMG/PTL. + +While validating bo during vm_bind, update the return status to 0 +instead of error as WA to allow the HW spice encode pipeline continue +work. + +Signed-off-by: Kasireddy, Vivek +Signed-off-by: Goh, Wei Khang1 +--- + drivers/gpu/drm/xe/xe_vm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c +index cdd1dc540a59e..25a2c95e9480f 100644 +--- a/drivers/gpu/drm/xe/xe_vm.c ++++ b/drivers/gpu/drm/xe/xe_vm.c +@@ -3479,7 +3479,7 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo, + * how it was mapped on the CPU. Just assume is it + * potentially cached on CPU side. + */ +- return -EINVAL; ++ return 0; + } + + /* If a BO is protected it can only be mapped if the key is still valid */ +-- +2.43.0 + diff --git a/SPECS/kernel/0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu b/SPECS/kernel/0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu deleted file mode 100644 index 4bc30d3ad..000000000 --- a/SPECS/kernel/0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu +++ /dev/null @@ -1,62 +0,0 @@ -From 34c4e824b277073582084f12dcb4f85f955932d2 Mon Sep 17 00:00:00 2001 -From: Khai Wen Ng -Date: Thu, 11 Sep 2025 07:49:24 +0800 -Subject: [PATCH 1/6] i2c: add identifier for ATR and MUX adapters - -In ACPI, ATR and MUX adapter are created dynamically -by drivers, there are no pre-allocated resource in ACPI namespace. -Adding identifier so that acpi driver can handle devices that -resides on ATR and MUX adapter. - -Signed-off-by: Khai Wen Ng -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/i2c-atr.c | 2 ++ - drivers/i2c/i2c-mux.c | 2 ++ - include/linux/i2c.h | 4 ++++ - 3 files changed, 8 insertions(+) - -diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c -index dd194476b118..f998e5f43fc0 100644 ---- a/drivers/i2c/i2c-atr.c -+++ b/drivers/i2c/i2c-atr.c -@@ -825,6 +825,8 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) - chan->adap.timeout = parent->timeout; - chan->adap.quirks = parent->quirks; - chan->adap.lock_ops = &i2c_atr_lock_ops; -+ chan->adap.is_atr = true; -+ chan->adap.chan_id = chan_id; - - if (bus_handle) { - device_set_node(&chan->adap.dev, fwnode_handle_get(bus_handle)); -diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c -index 4d8690981a55..ace64e0b0491 100644 ---- a/drivers/i2c/i2c-mux.c -+++ b/drivers/i2c/i2c-mux.c -@@ -321,6 +321,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc, - priv->adap.retries = parent->retries; - priv->adap.timeout = parent->timeout; - priv->adap.quirks = parent->quirks; -+ priv->adap.is_mux = true; -+ priv->adap.chan_id = chan_id; - if (muxc->mux_locked) - priv->adap.lock_ops = &i2c_mux_lock_ops; - else -diff --git a/include/linux/i2c.h b/include/linux/i2c.h -index 20fd41b51d5c..83288fe9bcb2 100644 ---- a/include/linux/i2c.h -+++ b/include/linux/i2c.h -@@ -765,6 +765,10 @@ struct i2c_adapter { - - /* 7bit address space */ - DECLARE_BITMAP(addrs_in_instantiation, 1 << 7); -+ -+ bool is_atr; -+ bool is_mux; -+ int chan_id; - }; - #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) - --- -2.43.0 - diff --git a/SPECS/kernel/0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm b/SPECS/kernel/0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm new file mode 100644 index 000000000..1d5076a11 --- /dev/null +++ b/SPECS/kernel/0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm @@ -0,0 +1,48 @@ +From 16b46595039e12fe48cef4b96a78e3e8061508e5 Mon Sep 17 00:00:00 2001 +From: "Saravanan, Thineshkumar" +Date: Mon, 9 Mar 2026 13:04:27 +0800 +Subject: [PATCH] i915 and xe/gt: Update GuC versions accordingly + +Signed-off-by: Saravanan, Thineshkumar +--- + drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 2 +- + drivers/gpu/drm/xe/xe_uc_fw.c | 8 ++++---- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +index 20af322d35d7..1636023f2fa6 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c ++++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +@@ -96,7 +96,7 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, + fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 49, 4)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \ +- fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \ ++ fw_def(DG1, 0, guc_maj(dg1, 70, 49, 4)) \ + fw_def(ROCKETLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(TIGERLAKE, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(JASPERLAKE, 0, guc_mmp(ehl, 70, 1, 1)) \ +diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c +index 049eda2f085b..6aa25c614a55 100644 +--- a/drivers/gpu/drm/xe/xe_uc_fw.c ++++ b/drivers/gpu/drm/xe/xe_uc_fw.c +@@ -115,12 +115,12 @@ struct fw_blobs_by_type { + #define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED + + #define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \ +- fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 55, 3)) \ +- fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 55, 3)) \ +- fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 53, 0)) \ ++ fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 58, 0)) \ ++ fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 58, 0)) \ ++ fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 58, 0)) \ + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ +- fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ ++ fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 49, 4)) \ + fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ + fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 49, 4)) \ + fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ +-- +2.34.1 + diff --git a/SPECS/kernel/0001-i915-gt-GuC-for-legacy-platform.drm b/SPECS/kernel/0001-i915-gt-GuC-for-legacy-platform.drm new file mode 100644 index 000000000..2f104bc44 --- /dev/null +++ b/SPECS/kernel/0001-i915-gt-GuC-for-legacy-platform.drm @@ -0,0 +1,51 @@ +From 09505a121ef45e85d00049090bffbc621729d091 Mon Sep 17 00:00:00 2001 +From: "Mazlan, Hazwan Arif" +Date: Wed, 4 Feb 2026 10:09:52 +0800 +Subject: [PATCH] i915/gt: GuC for legacy platform + +Reference ID: +https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tag/?h=20260110 + +Signed-off-by: Mazlan, Hazwan Arif +--- + drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 4 ++-- + drivers/gpu/drm/xe/xe_uc_fw.c | 6 +++--- + 2 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +index 9852ea55e772e..20af322d35d76 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c ++++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +@@ -90,10 +90,10 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, + #define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \ + fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 53, 0)) \ + fw_def(DG2, 0, guc_maj(dg2, 70, 53, 0)) \ +- fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 49, 4)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 70, 1, 1)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 69, 0, 3)) \ +- fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 49, 4)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \ + fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \ +diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c +index 1b87684f45256..049eda2f085b7 100644 +--- a/drivers/gpu/drm/xe/xe_uc_fw.c ++++ b/drivers/gpu/drm/xe/xe_uc_fw.c +@@ -121,9 +121,9 @@ struct fw_blobs_by_type { + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ + fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ +- fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ +- fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 53, 0)) \ +- fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ ++ fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 49, 4)) \ ++ fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 49, 4)) \ + fw_def(ROCKETLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ + fw_def(TIGERLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) + +-- +2.43.0 + diff --git a/SPECS/kernel/0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm b/SPECS/kernel/0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm new file mode 100644 index 000000000..2fc93404f --- /dev/null +++ b/SPECS/kernel/0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm @@ -0,0 +1,55 @@ +From ce87e78b6e40fcbb601636cc4a9c42622d6ae4d4 Mon Sep 17 00:00:00 2001 +From: "Mazlan, Hazwan Arif" +Date: Tue, 27 Jan 2026 11:14:26 +0800 +Subject: [PATCH] i915/gt: Upgrade GuCs accordingly to 20260110 baseline + +Following kernel.org binaries latest changeset for i915/xe +https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tag/?h=20260110 + +Signed-off-by: Mazlan, Hazwan Arif +--- + drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 8 ++++---- + drivers/gpu/drm/xe/xe_uc_fw.c | 6 +++--- + 2 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +index e848a04a80dc2..9852ea55e772e 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c ++++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +@@ -88,12 +88,12 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, + * security fixes, etc. to be enabled. + */ + #define INTEL_GUC_FIRMWARE_DEFS(fw_def, guc_maj, guc_mmp) \ +- fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 12, 1)) \ +- fw_def(DG2, 0, guc_maj(dg2, 70, 12, 1)) \ +- fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 12, 1)) \ ++ fw_def(METEORLAKE, 0, guc_maj(mtl, 70, 53, 0)) \ ++ fw_def(DG2, 0, guc_maj(dg2, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_P, 0, guc_maj(adlp, 70, 53, 0)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 70, 1, 1)) \ + fw_def(ALDERLAKE_P, 0, guc_mmp(adlp, 69, 0, 3)) \ +- fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 12, 1)) \ ++ fw_def(ALDERLAKE_S, 0, guc_maj(tgl, 70, 53, 0)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 70, 1, 1)) \ + fw_def(ALDERLAKE_S, 0, guc_mmp(tgl, 69, 0, 3)) \ + fw_def(DG1, 0, guc_maj(dg1, 70, 5, 1)) \ +diff --git a/drivers/gpu/drm/xe/xe_uc_fw.c b/drivers/gpu/drm/xe/xe_uc_fw.c +index 3bff84ab4b5d8..1b87684f45256 100644 +--- a/drivers/gpu/drm/xe/xe_uc_fw.c ++++ b/drivers/gpu/drm/xe/xe_uc_fw.c +@@ -121,9 +121,9 @@ struct fw_blobs_by_type { + fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 53, 0)) \ + fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 53, 0)) \ + fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \ +- fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ +- fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 44, 1)) \ +- fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ ++ fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 53, 0)) \ ++ fw_def(ALDERLAKE_S, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 53, 0)) \ + fw_def(ROCKETLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \ + fw_def(TIGERLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) + +-- +2.43.0 + diff --git a/SPECS/kernel/0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet b/SPECS/kernel/0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet new file mode 100644 index 000000000..039d48009 --- /dev/null +++ b/SPECS/kernel/0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet @@ -0,0 +1,249 @@ +From 120d58bdd8591832aeb89e0f542b6b80f35e6122 Mon Sep 17 00:00:00 2001 +From: Vinicius Costa Gomes +Date: Tue, 4 May 2021 13:47:03 -0700 +Subject: [PATCH 01/14] igc: Add support for DMA timestamp for non-PTP packets + +For PTP traffic, timestamp is retrieved from TXSTMP register. +For all other packets, DMA timestamp field of the Transmit +Descriptor Write-back is used. + +This is required to work around the HW time stamp misses in +TXSTAMP register due to the HW limitation, when heavy traffic +is initiated. + +Signed-off-by: Vinicius Costa Gomes +Signed-off-by: Aravindhan Gunasekaran +Signed-off-by: Muhammad Husaini Zulkifli +--- + drivers/net/ethernet/intel/igc/igc.h | 3 + + drivers/net/ethernet/intel/igc/igc_base.h | 2 +- + drivers/net/ethernet/intel/igc/igc_defines.h | 2 + + drivers/net/ethernet/intel/igc/igc_main.c | 23 ++++++- + drivers/net/ethernet/intel/igc/igc_ptp.c | 72 ++++++++++++++++++++ + 5 files changed, 99 insertions(+), 3 deletions(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index a427f05814c1a..b9f846389e75e 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -559,6 +559,7 @@ enum igc_tx_flags { + IGC_TX_FLAGS_TSTAMP_3 = 0x400, + + IGC_TX_FLAGS_TSTAMP_TIMER_1 = 0x800, ++ IGC_TX_FLAGS_DMA_TSTAMP = 0x200, + }; + + enum igc_boards { +@@ -780,6 +781,8 @@ int igc_ptp_hwtstamp_get(struct net_device *netdev, + int igc_ptp_hwtstamp_set(struct net_device *netdev, + struct kernel_hwtstamp_config *config, + struct netlink_ext_ack *extack); ++void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, ++ struct sk_buff *skb, u64 tstamp); + void igc_ptp_tx_hang(struct igc_adapter *adapter); + void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); + void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); +diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h +index eaf17cd031c3a..b05cdfaa5b945 100644 +--- a/drivers/net/ethernet/intel/igc/igc_base.h ++++ b/drivers/net/ethernet/intel/igc/igc_base.h +@@ -18,7 +18,7 @@ union igc_adv_tx_desc { + __le32 olinfo_status; + } read; + struct { +- __le64 rsvd; /* Reserved */ ++ __le64 dma_tstamp; + __le32 nxtseq_seed; + __le32 status; + } wb; +diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h +index 9482ab11f050f..56833df911f08 100644 +--- a/drivers/net/ethernet/intel/igc/igc_defines.h ++++ b/drivers/net/ethernet/intel/igc/igc_defines.h +@@ -315,6 +315,7 @@ + #define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ + #define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ + #define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */ ++#define IGC_TXD_STAT_TS_STAT 0x00000002 /* DMA Timestamp in packet */ + #define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */ + #define IGC_TXD_CMD_IP 0x02000000 /* IP packet */ + #define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ +@@ -586,6 +587,7 @@ + /* Transmit Scheduling */ + #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001 + #define IGC_TQAVCTRL_PREEMPT_ENA 0x00000002 ++#define IGC_TQAVCTRL_1588_STAT_EN 0x00000004 + #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008 + #define IGC_TQAVCTRL_FUTSCDDIS 0x00000080 + #define IGC_TQAVCTRL_MIN_FRAG_MASK 0x0000C000 +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 728d7ca5338bf..9b52d60ca315c 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -1556,6 +1556,14 @@ static bool igc_request_tx_tstamp(struct igc_adapter *adapter, struct sk_buff *s + return false; + } + ++static bool igc_is_ptp_packet(struct sk_buff *skb) ++{ ++ __be16 protocol = vlan_get_protocol(skb); ++ ++ /* FIXME: also handle UDP packets */ ++ return protocol == htons(ETH_P_1588); ++} ++ + static int igc_insert_empty_frame(struct igc_ring *tx_ring) + { + struct igc_tx_buffer *empty_info; +@@ -1659,16 +1667,21 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, + + if (unlikely(test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags) && + skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { ++ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); ++ bool is_ptp = igc_is_ptp_packet(skb); + unsigned long flags; + u32 tstamp_flags; + + spin_lock_irqsave(&adapter->ptp_tx_lock, flags); +- if (igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { ++ if (is_ptp && igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + tx_flags |= IGC_TX_FLAGS_TSTAMP | tstamp_flags; + if (skb->sk && +- READ_ONCE(skb->sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC) ++ READ_ONCE(skb->sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC) + tx_flags |= IGC_TX_FLAGS_TSTAMP_TIMER_1; ++ } else if (!is_ptp) { ++ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; ++ tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; + } else { + adapter->tx_hwtstamp_skipped++; + } +@@ -3170,6 +3183,12 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + if (tx_buffer->type == IGC_TX_BUFFER_TYPE_XSK && + tx_buffer->xsk_pending_ts) + break; ++ if (eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_TS_STAT) && ++ tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { ++ u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); ++ ++ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); ++ } + + /* clear next_to_watch to prevent false hangs */ + tx_buffer->next_to_watch = NULL; +diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c +index 7aae83c108fd7..c1af5edae7aae 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ptp.c ++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c +@@ -446,6 +446,31 @@ static int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, + return 0; + } + ++static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, ++ struct skb_shared_hwtstamps *hwtstamps, ++ u64 systim) ++{ ++ struct igc_hw *hw = &adapter->hw; ++ u32 sec, nsec; ++ ++ /* FIXME: use a workqueue to read these values to avoid ++ * reading these registers in the hot path. ++ */ ++ nsec = rd32(IGC_SYSTIML); ++ sec = rd32(IGC_SYSTIMH); ++ ++ switch (adapter->hw.mac.type) { ++ case igc_i225: ++ memset(hwtstamps, 0, sizeof(*hwtstamps)); ++ ++ /* HACK */ ++ hwtstamps->hwtstamp = ktime_set(sec, systim & 0xFFFFFFFF); ++ break; ++ default: ++ break; ++ } ++} ++ + /** + * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer + * @adapter: Pointer to adapter the packet buffer belongs to +@@ -579,6 +604,7 @@ static void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter) + static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) + { + struct igc_hw *hw = &adapter->hw; ++ u32 tqavctrl; + int i; + + /* Clear the flags first to avoid new packets to be enqueued +@@ -593,14 +619,26 @@ static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) + /* Now we can clean the pending TX timestamp requests. */ + igc_ptp_clear_tx_tstamp(adapter); + ++ tqavctrl = rd32(IGC_TQAVCTRL); ++ tqavctrl &= ~IGC_TQAVCTRL_1588_STAT_EN; ++ ++ wr32(IGC_TQAVCTRL, tqavctrl); ++ + wr32(IGC_TSYNCTXCTL, 0); + } + + static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) + { + struct igc_hw *hw = &adapter->hw; ++ u32 tqavctrl; + int i; + ++ /* Enable DMA Fetch timestamping */ ++ tqavctrl = rd32(IGC_TQAVCTRL); ++ tqavctrl |= IGC_TQAVCTRL_1588_STAT_EN; ++ ++ wr32(IGC_TQAVCTRL, tqavctrl); ++ + wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); + + /* Read TXSTMP registers to discard any timestamp previously stored. */ +@@ -841,6 +879,40 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) + } + } + ++void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, ++ struct sk_buff *skb, u64 tstamp) ++{ ++ struct skb_shared_hwtstamps shhwtstamps; ++ int adjust = 0; ++ ++ if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) ++ return; ++ ++ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); ++ ++ /* FIXME: Use different latencies for DMA timestamps? */ ++ switch (adapter->link_speed) { ++ case SPEED_10: ++ adjust = IGC_I225_TX_LATENCY_10; ++ break; ++ case SPEED_100: ++ adjust = IGC_I225_TX_LATENCY_100; ++ break; ++ case SPEED_1000: ++ adjust = IGC_I225_TX_LATENCY_1000; ++ break; ++ case SPEED_2500: ++ adjust = IGC_I225_TX_LATENCY_2500; ++ break; ++ } ++ ++ shhwtstamps.hwtstamp = ++ ktime_add_ns(shhwtstamps.hwtstamp, adjust); ++ ++ /* Notify the stack and free the skb after we've unlocked */ ++ skb_tstamp_tx(skb, &shhwtstamps); ++} ++ + /** + * igc_ptp_tx_tstamp_event + * @adapter: board private structure +-- +2.43.0 + diff --git a/SPECS/kernel/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet b/SPECS/kernel/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet index c752057a9..af69304f9 100644 --- a/SPECS/kernel/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet +++ b/SPECS/kernel/0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet @@ -1,7 +1,7 @@ -From eec452978215d4a46a735237cf74a8bfd7acdca6 Mon Sep 17 00:00:00 2001 +From def1018b29cd29ff0d8fc01068a119777bec7012 Mon Sep 17 00:00:00 2001 From: Malli C Date: Mon, 25 Jan 2021 17:43:51 -0800 -Subject: [PATCH 01/19] igc: Only dump registers if configured to dump HW +Subject: [PATCH 1/4] igc: Only dump registers if configured to dump HW information To avoid polluting the users logs with register dumps, only dump the @@ -20,7 +20,7 @@ Signed-off-by: Aravindhan Gunasekaran 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_dump.c b/drivers/net/ethernet/intel/igc/igc_dump.c -index c09c95cc5f70..4abbeb763374 100644 +index c09c95cc5f70e..4abbeb763374e 100644 --- a/drivers/net/ethernet/intel/igc/igc_dump.c +++ b/drivers/net/ethernet/intel/igc/igc_dump.c @@ -308,6 +308,9 @@ void igc_regs_dump(struct igc_adapter *adapter) diff --git a/SPECS/kernel/0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet b/SPECS/kernel/0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet deleted file mode 100644 index 6b8d933c9..000000000 --- a/SPECS/kernel/0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet +++ /dev/null @@ -1,47 +0,0 @@ -From 67aa7e943ecb59b1fd526792049cd98eaaa95693 Mon Sep 17 00:00:00 2001 -From: Chwee-Lin Choong -Date: Thu, 4 Dec 2025 20:21:50 +0800 -Subject: [PATCH] igc: Reduce TSN TX packet buffer from 7KB to 5KB per queue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The previous 7 KB per queue caused TX unit hangs under heavy -timestamping load. Reducing to 5 KB avoids these hangs and matches -the TSN recommendation in I225/I226 SW User Manual Section 7.5.4. - -The 8 KB “freed” by this change is currently unused. This reduction -is not expected to impact throughput, as the i226 is PCIe-limited -for small TSN packets rather than TX-buffer-limited. - -Fixes: 0d58cdc902da ("igc: optimize TX packet buffer utilization for TSN mode") -Reported-by: Zdenek Bouska -Closes: https://lore.kernel.org/netdev/AS1PR10MB5675DBFE7CE5F2A9336ABFA4EBEAA@AS1PR10MB5675.EURPRD10.PROD.OUTLOOK.COM/ -Reviewed-by: Paul Menzel -Reviewed-by: Simon Horman -Reviewed-by: Aleksandr Loktionov -Signed-off-by: Chwee-Lin Choong ---- - drivers/net/ethernet/intel/igc/igc_defines.h | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h -index ea673df406a9..56833df911f0 100644 ---- a/drivers/net/ethernet/intel/igc/igc_defines.h -+++ b/drivers/net/ethernet/intel/igc/igc_defines.h -@@ -444,9 +444,10 @@ - #define IGC_TXPBSIZE_DEFAULT ( \ - IGC_TXPB0SIZE(20) | IGC_TXPB1SIZE(0) | IGC_TXPB2SIZE(0) | \ - IGC_TXPB3SIZE(0) | IGC_OS2BMCPBSIZE(4)) -+/* TSN value following I225/I226 SW User Manual Section 7.5.4 */ - #define IGC_TXPBSIZE_TSN ( \ -- IGC_TXPB0SIZE(7) | IGC_TXPB1SIZE(7) | IGC_TXPB2SIZE(7) | \ -- IGC_TXPB3SIZE(7) | IGC_OS2BMCPBSIZE(4)) -+ IGC_TXPB0SIZE(5) | IGC_TXPB1SIZE(5) | IGC_TXPB2SIZE(5) | \ -+ IGC_TXPB3SIZE(5) | IGC_OS2BMCPBSIZE(4)) - - #define IGC_DTXMXPKTSZ_TSN 0x19 /* 1600 bytes of max TX DMA packet size */ - #define IGC_DTXMXPKTSZ_DEFAULT 0x98 /* 9728-byte Jumbo frames */ --- -2.43.0 - diff --git a/SPECS/kernel/0001-igc-Remove-XDP-metadata-invalidation.ethernet b/SPECS/kernel/0001-igc-Remove-XDP-metadata-invalidation.ethernet deleted file mode 100644 index 9d9e34a32..000000000 --- a/SPECS/kernel/0001-igc-Remove-XDP-metadata-invalidation.ethernet +++ /dev/null @@ -1,32 +0,0 @@ -From 4485c4753d8ab46da9c1cee8e4f946b5d8c48a98 Mon Sep 17 00:00:00 2001 -From: KhaiWenTan -Date: Wed, 24 Dec 2025 15:13:12 +0800 -Subject: [PATCH] igc: Remove XDP metadata invalidation - -Delete the else branch that invalidates XDP metadata when RX -timestamping is off. Only set metadata when hardware RX timestamping -is enabled, simplifying the receive path logic. - -Fixes: c2697c3fa88d ("igc: Enable HW RX Timestamp for AF_XDP ZC") -Signed-off-by: KhaiWenTan -Signed-off-by: Song Yoong Siang ---- - drivers/net/ethernet/intel/igc/igc_main.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 68fd90e2839e..2a3f4cf2c4ca 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -2868,8 +2868,6 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) - md = bi->xdp->data - sizeof(*md); - md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); - bi->xdp->data_meta = md; -- } else { -- xdp_set_data_meta_invalid(bi->xdp); - } - - res = __igc_xdp_run_prog(adapter, prog, bi->xdp); --- -2.43.0 - diff --git a/SPECS/kernel/0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet b/SPECS/kernel/0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet deleted file mode 100644 index ddfe2ded8..000000000 --- a/SPECS/kernel/0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet +++ /dev/null @@ -1,121 +0,0 @@ -From 68f67a96c9ee707aa42bb6c5b4da02a8e3e4a935 Mon Sep 17 00:00:00 2001 -From: Chwee-Lin Choong -Date: Fri, 28 Nov 2025 18:53:04 +0800 -Subject: [PATCH] igc: fix race condition in TX timestamp read for register 0 -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The current HW bug workaround checks the TXTT_0 ready bit first, -then reads TXSTMPL_0 twice (before and after reading TXSTMPH_0) -to detect whether a new timestamp was captured by timestamp -register 0 during the workaround. - -This sequence has a race: if a new timestamp is captured after -checking the TXTT_0 bit but before the first TXSTMPL_0 read, the -detection fails because both the “old” and “new” values come from -the same timestamp. - -Fix by reading TXSTMPL_0 first to establish a baseline, then -checking the TXTT_0 bit. This ensures any timestamp captured -during the race window will be detected. - -Old sequence: - 1. Check TXTT_0 ready bit - 2. Read TXSTMPL_0 (baseline) - 3. Read TXSTMPH_0 (interrupt workaround) - 4. Read TXSTMPL_0 (detect changes vs baseline) - -New sequence: - 1. Read TXSTMPL_0 (baseline) - 2. Check TXTT_0 ready bit - 3. Read TXSTMPH_0 (interrupt workaround) - 4. Read TXSTMPL_0 (detect changes vs baseline) - -Fixes: c789ad7cbebc ("igc: Work around HW bug causing missing timestamps") -Suggested-by: Avi Shalev -Reviewed-by: Aleksandr Loktionov -Co-developed-by: Song Yoong Siang -Signed-off-by: Song Yoong Siang -Signed-off-by: Chwee-Lin Choong ---- - drivers/net/ethernet/intel/igc/igc_ptp.c | 43 ++++++++++++++---------- - 1 file changed, 25 insertions(+), 18 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index 4fef3f1abe29..23a55ca30677 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -815,36 +815,43 @@ static void igc_ptp_tx_reg_to_stamp(struct igc_adapter *adapter, - static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) - { - struct igc_hw *hw = &adapter->hw; -+ u32 txstmpl_old; - u64 regval; - u32 mask; - int i; - -+ /* Establish baseline of TXSTMPL_0 before checking TXTT_0. -+ * This baseline is used to detect if a new timestamp arrives in -+ * register 0 during the hardware bug workaround below. -+ */ -+ txstmpl_old = rd32(IGC_TXSTMPL); -+ - mask = rd32(IGC_TSYNCTXCTL) & IGC_TSYNCTXCTL_TXTT_ANY; - if (mask & IGC_TSYNCTXCTL_TXTT_0) { - regval = rd32(IGC_TXSTMPL); - regval |= (u64)rd32(IGC_TXSTMPH) << 32; - } else { -- /* There's a bug in the hardware that could cause -- * missing interrupts for TX timestamping. The issue -- * is that for new interrupts to be triggered, the -- * IGC_TXSTMPH_0 register must be read. -+ /* TXTT_0 not set - register 0 has no new timestamp initially. -+ * -+ * Hardware bug: Future timestamp interrupts won't fire unless -+ * TXSTMPH_0 is read, even if the timestamp was captured in -+ * registers 1-3. - * -- * To avoid discarding a valid timestamp that just -- * happened at the "wrong" time, we need to confirm -- * that there was no timestamp captured, we do that by -- * assuming that no two timestamps in sequence have -- * the same nanosecond value. -+ * Workaround: Read TXSTMPH_0 here to enable future interrupts. -+ * However, this read clears TXTT_0. If a timestamp arrives in -+ * register 0 after checking TXTT_0 but before this read, it -+ * would be lost. - * -- * So, we read the "low" register, read the "high" -- * register (to latch a new timestamp) and read the -- * "low" register again, if "old" and "new" versions -- * of the "low" register are different, a valid -- * timestamp was captured, we can read the "high" -- * register again. -+ * To detect this race: We saved a baseline read of TXSTMPL_0 -+ * before TXTT_0 check. After performing the workaround read of -+ * TXSTMPH_0, we read TXSTMPL_0 again. Since consecutive -+ * timestamps never share the same nanosecond value, a change -+ * between the baseline and new TXSTMPL_0 indicates a timestamp -+ * arrived during the race window. If so, read the complete -+ * timestamp. - */ -- u32 txstmpl_old, txstmpl_new; -+ u32 txstmpl_new; - -- txstmpl_old = rd32(IGC_TXSTMPL); - rd32(IGC_TXSTMPH); - txstmpl_new = rd32(IGC_TXSTMPL); - -@@ -859,7 +866,7 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) - - done: - /* Now that the problematic first register was handled, we can -- * use retrieve the timestamps from the other registers -+ * retrieve the timestamps from the other registers - * (starting from '1') with less complications. - */ - for (i = 1; i < IGC_MAX_TX_TSTAMP_REGS; i++) { --- -2.43.0 - diff --git a/SPECS/kernel/0001-issei-initial-driver-skeleton.security b/SPECS/kernel/0001-issei-initial-driver-skeleton.security index 5af327e63..33d70d129 100644 --- a/SPECS/kernel/0001-issei-initial-driver-skeleton.security +++ b/SPECS/kernel/0001-issei-initial-driver-skeleton.security @@ -1,7 +1,7 @@ -From 28c125e31e9c3aac24f3d4229282ee03ed5f05a9 Mon Sep 17 00:00:00 2001 +From b50d2a7550d5cf9d63171d44eda4bdc124c0dd1e Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 17 Apr 2025 12:38:06 +0300 -Subject: [PATCH 1/5] issei: initial driver skeleton +Subject: [PATCH 1/7] issei: initial driver skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -21,19 +21,19 @@ Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- - Documentation/driver-api/issei/issei.rst | 138 ++++++++++++++++++ + Documentation/driver-api/issei/issei.rst | 138 ++++++++++++++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/issei/Kconfig | 13 ++ drivers/misc/issei/Makefile | 7 + - drivers/misc/issei/cdev.c | 160 +++++++++++++++++++++ - drivers/misc/issei/cdev.h | 12 ++ - drivers/misc/issei/dma.c | 172 ++++++++++++++++++++++ - drivers/misc/issei/dma.h | 58 ++++++++ - drivers/misc/issei/hw_msg.h | 176 +++++++++++++++++++++++ - drivers/misc/issei/issei_dev.h | 153 ++++++++++++++++++++ - include/uapi/linux/issei.h | 78 ++++++++++ - 12 files changed, 969 insertions(+) + drivers/misc/issei/cdev.c | 225 +++++++++++++++++++++++ + drivers/misc/issei/cdev.h | 18 ++ + drivers/misc/issei/dma.c | 172 +++++++++++++++++ + drivers/misc/issei/dma.h | 58 ++++++ + drivers/misc/issei/hw_msg.h | 176 ++++++++++++++++++ + drivers/misc/issei/issei_dev.h | 155 ++++++++++++++++ + include/uapi/linux/issei.h | 67 +++++++ + 12 files changed, 1031 insertions(+) create mode 100644 Documentation/driver-api/issei/issei.rst create mode 100644 drivers/misc/issei/Kconfig create mode 100644 drivers/misc/issei/Makefile @@ -47,7 +47,7 @@ Signed-off-by: Alexander Usyskin diff --git a/Documentation/driver-api/issei/issei.rst b/Documentation/driver-api/issei/issei.rst new file mode 100644 -index 000000000000..8bfbe1e0c6b6 +index 0000000000000..07b0412d1b3d9 --- /dev/null +++ b/Documentation/driver-api/issei/issei.rst @@ -0,0 +1,138 @@ @@ -74,7 +74,7 @@ index 000000000000..8bfbe1e0c6b6 +upon connection. + +Intel ISSEI Driver -+================ ++================== + +The driver exposes a character device with device nodes /dev/isseiX. + @@ -139,12 +139,12 @@ index 000000000000..8bfbe1e0c6b6 +User space API + +ioctl: -+======= ++====== + +The Intel ISSEI Driver supports the following ioctl commands: + +IOCTL_ISSEI_CONNECT_CLIENT -+------------------------- ++-------------------------- +Connect to firmware Feature/Client. + +.. code-block:: none @@ -176,7 +176,7 @@ index 000000000000..8bfbe1e0c6b6 + requests up to bytes 2k and received responses up to 2k bytes). + +IOCTL_ISSEI_DISCONNECT_CLIENT -+------------------------- ++----------------------------- +Disconnect from firmware Feature/Client. + +.. code-block:: none @@ -190,7 +190,7 @@ index 000000000000..8bfbe1e0c6b6 + ENODEV Device or Connection is not initialized or ready. + ENOTCONN Feature/Client is not connected. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig -index b9c11f67315f..43ab202e0b22 100644 +index b9c11f67315f0..43ab202e0b224 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -661,4 +661,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig" @@ -200,17 +200,17 @@ index b9c11f67315f..43ab202e0b22 100644 +source "drivers/misc/issei/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile -index e2e66f5f4fb8..60d004bcde29 100644 +index b32a2597d2467..01072ebecf829 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile -@@ -76,3 +76,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o +@@ -75,3 +75,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o obj-y += keba/ obj-y += amd-sbi/ obj-$(CONFIG_MISC_RP1) += rp1/ +obj-$(CONFIG_INTEL_SSEI) += issei/ diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig new file mode 100644 -index 000000000000..ffd027e826df +index 0000000000000..ffd027e826dfd --- /dev/null +++ b/drivers/misc/issei/Kconfig @@ -0,0 +1,13 @@ @@ -229,7 +229,7 @@ index 000000000000..ffd027e826df + If in doubt, select N. diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile new file mode 100644 -index 000000000000..9e3ef22305ac +index 0000000000000..9e3ef22305ac9 --- /dev/null +++ b/drivers/misc/issei/Makefile @@ -0,0 +1,7 @@ @@ -242,18 +242,18 @@ index 000000000000..9e3ef22305ac +issei-objs += dma.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c new file mode 100644 -index 000000000000..26ac95e5f818 +index 0000000000000..c265012062c71 --- /dev/null +++ b/drivers/misc/issei/cdev.c -@@ -0,0 +1,160 @@ +@@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include +#include ++#include ++#include +#include +#include -+#include -+#include +#include + +#include "issei_dev.h" @@ -297,64 +297,129 @@ index 000000000000..26ac95e5f818 + if (ret >= 0) + idev->minor = ret; + else if (ret == -ENOSPC) -+ dev_err(idev->dev, "too many issei devices\n"); ++ dev_err(&idev->dev, "too many issei devices\n"); + + return ret; +} + -+static void issei_minor_free(struct issei_device *idev) ++static void issei_minor_free(int minor) +{ + guard(mutex)(&issei_minor_lock); + -+ idr_remove(&issei_idr, idev->minor); ++ idr_remove(&issei_idr, minor); ++} ++ ++static void issei_device_release(struct device *dev) ++{ ++ kfree(dev_get_drvdata(dev)); ++} ++ ++static void issei_device_init(struct issei_device *idev, struct device *parent, ++ const struct issei_dma_length *dma_length, ++ const struct issei_hw_ops *ops) ++{ ++ idev->parent = parent; ++ idev->power_down = false; ++ init_waitqueue_head(&idev->wait_rst_irq); ++ atomic_set(&idev->rst_irq, 0); ++ init_waitqueue_head(&idev->wait_rst_state); ++ idev->rst_state = ISSEI_RST_STATE_INIT; ++ ++ mutex_init(&idev->host_client_lock); ++ INIT_LIST_HEAD(&idev->host_client_list); ++ idev->host_client_last_id = 0; ++ idev->host_client_count = 0; ++ ++ mutex_init(&idev->fw_client_lock); ++ INIT_LIST_HEAD(&idev->fw_client_list); ++ ++ idev->dma.length = *dma_length; ++ ++ INIT_LIST_HEAD(&idev->write_queue); ++ ++ idev->ops = ops; +} + +/** + * issei_register: register issei character device -+ * @idev: the device structure ++ * @hw_size: size of the hardware structure to allocate + * @parent: parent device ++ * @dma_length: structure with DMA sizes ++ * @ops: hardware-related operations + * -+ * Return: 0 on sucess, <0 on failure ++ * Return: pointer allocated to issei_device structure, error on failure + */ -+int issei_register(struct issei_device *idev, struct device *parent) ++struct issei_device *issei_register(size_t hw_size, struct device *parent, ++ const struct issei_dma_length *dma_length, ++ const struct issei_hw_ops *ops) +{ -+ struct device *clsdev; ++ struct issei_device *idev; ++ int minor; + int ret, devno; + ++ idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); ++ if (!idev) ++ return ERR_PTR(-ENOMEM); ++ ++ issei_device_init(idev, parent, dma_length, ops); ++ + ret = issei_minor_get(idev); -+ if (ret < 0) -+ return ret; ++ if (ret < 0) { ++ kfree(idev); ++ return ERR_PTR(ret); ++ } ++ minor = idev->minor; + + devno = MKDEV(MAJOR(issei_devt), idev->minor); -+ cdev_init(&idev->cdev, &issei_fops); ++ ++ device_initialize(&idev->dev); ++ idev->dev.devt = devno; ++ idev->dev.class = issei_class; ++ idev->dev.parent = parent; ++ idev->dev.groups = issei_groups; ++ idev->dev.release = issei_device_release; ++ dev_set_drvdata(&idev->dev, idev); ++ ++ idev->cdev = cdev_alloc(); ++ if (!idev->cdev) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ idev->cdev->ops = &issei_fops; + if (parent->driver) -+ idev->cdev.owner = parent->driver->owner; ++ idev->cdev->owner = parent->driver->owner; ++ cdev_set_parent(idev->cdev, &idev->dev.kobj); + -+ ret = cdev_add(&idev->cdev, devno, 1); ++ ret = cdev_add(idev->cdev, devno, 1); + if (ret) { + dev_err(parent, "unable to add device %d:%d\n", + MAJOR(issei_devt), idev->minor); -+ goto err_dev_add; ++ goto err_del_cdev; + } + -+ clsdev = device_create_with_groups(issei_class, parent, devno, -+ idev, issei_groups, -+ "issei%d", idev->minor); -+ if (IS_ERR(clsdev)) { -+ dev_err(parent, "unable to create device %d:%d\n", -+ MAJOR(issei_devt), idev->minor); -+ ret = PTR_ERR(clsdev); -+ goto err_dev_create; ++ ret = dev_set_name(&idev->dev, "issei%d", idev->minor); ++ if (ret) { ++ dev_err(parent, "unable to set name to device %d:%d ret = %d\n", ++ MAJOR(issei_devt), idev->minor, ret); ++ goto err_del_cdev; + } + -+ return 0; ++ ret = device_add(&idev->dev); ++ if (ret) { ++ dev_err(parent, "unable to add device %d:%d ret = %d\n", ++ MAJOR(issei_devt), idev->minor, ret); ++ goto err_del_cdev; ++ } + -+err_dev_create: -+ cdev_del(&idev->cdev); -+err_dev_add: -+ issei_minor_free(idev); ++ return idev; + -+ return ret; ++err_del_cdev: ++ cdev_del(idev->cdev); ++err: ++ put_device(&idev->dev); ++ issei_minor_free(minor); ++ ++ return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(issei_register); + @@ -364,14 +429,14 @@ index 000000000000..26ac95e5f818 + */ +void issei_deregister(struct issei_device *idev) +{ -+ int devno; ++ int minor = idev->minor; ++ int devno = idev->cdev->dev; + -+ devno = idev->cdev.dev; -+ cdev_del(&idev->cdev); ++ cdev_del(idev->cdev); + + device_destroy(issei_class, devno); + -+ issei_minor_free(idev); ++ issei_minor_free(minor); +} +EXPORT_SYMBOL_GPL(issei_deregister); + @@ -408,25 +473,31 @@ index 000000000000..26ac95e5f818 +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h new file mode 100644 -index 000000000000..dbdb0e868482 +index 0000000000000..d63541227e677 --- /dev/null +++ b/drivers/misc/issei/cdev.h -@@ -0,0 +1,12 @@ +@@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2025 Intel Corporation */ +#ifndef _ISSEI_CDEV_H_ +#define _ISSEI_CDEV_H_ + ++#include ++ +struct device; +struct issei_device; ++struct issei_dma_length; ++struct issei_hw_ops; + -+int issei_register(struct issei_device *idev, struct device *parent); ++struct issei_device *issei_register(size_t hw_size, struct device *parent, ++ const struct issei_dma_length *dma_length, ++ const struct issei_hw_ops *ops); +void issei_deregister(struct issei_device *idev); + +#endif /* _ISSEI_CDEV_H_ */ diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c new file mode 100644 -index 000000000000..8ad54f4636dc +index 0000000000000..2dd081d05a6bd --- /dev/null +++ b/drivers/misc/issei/dma.c @@ -0,0 +1,172 @@ @@ -476,7 +547,7 @@ index 000000000000..8ad54f4636dc + return 0; + } + -+ dma->vaddr = dmam_alloc_coherent(idev->dev, size, &dma->daddr, GFP_KERNEL | __GFP_ZERO); ++ dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr, GFP_KERNEL | __GFP_ZERO); + return dma->vaddr ? 0 : -ENOMEM; +} + @@ -503,7 +574,7 @@ index 000000000000..8ad54f4636dc +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + -+ dev_dbg(idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); ++ dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->f2h_counter_rd++; +} @@ -512,7 +583,7 @@ index 000000000000..8ad54f4636dc +{ + struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma); + -+ dev_dbg(idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); ++ dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr); + /* No need to check overflow - the firmware counters overflow the same way */ + ctl->h2f_counter_wr++; +} @@ -530,16 +601,16 @@ index 000000000000..8ad54f4636dc + struct ham_message_header *hdr = (struct ham_message_header *)write_buf; + + if (data->length > idev->dma.length.h2f - sizeof(*hdr)) { -+ dev_err(idev->dev, "Message too big\n"); ++ dev_err(&idev->dev, "Message too big\n"); + return -EMSGSIZE; + } + + if (__issei_dma_is_write_busy(&idev->dma)) { + if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) { -+ dev_err(idev->dev, "Write stuck in queue\n"); ++ dev_err(&idev->dev, "Write stuck in queue\n"); + return -EIO; + } -+ dev_info(idev->dev, "Write is busy\n"); ++ dev_info(&idev->dev, "Write is busy\n"); + return -EBUSY; + } + @@ -586,14 +657,14 @@ index 000000000000..8ad54f4636dc +int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data) +{ + if (!__issei_dma_is_read_busy(&idev->dma)) { -+ dev_dbg(idev->dev, "Nothing to read\n"); ++ dev_dbg(&idev->dev, "Nothing to read\n"); + return -ENODATA; + } + -+ dev_dbg(idev->dev, "Reading header\n"); ++ dev_dbg(&idev->dev, "Reading header\n"); + __issei_dma_read_hdr(idev, data); + -+ dev_dbg(idev->dev, "Reading data (size %u)\n", data->length); ++ dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length); + data->buf = kmalloc(data->length, GFP_KERNEL); + if (!data->buf) + return -ENOMEM; @@ -604,7 +675,7 @@ index 000000000000..8ad54f4636dc +} diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h new file mode 100644 -index 000000000000..8d92285f5da7 +index 0000000000000..8d92285f5da76 --- /dev/null +++ b/drivers/misc/issei/dma.h @@ -0,0 +1,58 @@ @@ -668,7 +739,7 @@ index 000000000000..8d92285f5da7 +#endif /*_ISSEI_DMA_H_*/ diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h new file mode 100644 -index 000000000000..75346d3ff986 +index 0000000000000..75346d3ff986b --- /dev/null +++ b/drivers/misc/issei/hw_msg.h @@ -0,0 +1,176 @@ @@ -850,10 +921,10 @@ index 000000000000..75346d3ff986 +#endif /* _ISSEI_HW_MSG_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h new file mode 100644 -index 000000000000..4e507ce4d10e +index 0000000000000..cfac43c14ba24 --- /dev/null +++ b/drivers/misc/issei/issei_dev.h -@@ -0,0 +1,153 @@ +@@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2025 Intel Corporation */ +#ifndef _ISSEI_DEV_H_ @@ -951,6 +1022,7 @@ index 000000000000..4e507ce4d10e + +/** + * struct issei_device - issei device ++ * @parent: parent device object + * @dev: associated device object + * @cdev: character device + * @minor: allocated minor number @@ -977,8 +1049,9 @@ index 000000000000..4e507ce4d10e + * @hw: hw-specific data + */ +struct issei_device { -+ struct device *dev; -+ struct cdev cdev; ++ struct device *parent; ++ struct device dev; ++ struct cdev *cdev; + int minor; + bool power_down; + wait_queue_head_t wait_rst_irq; @@ -1009,10 +1082,10 @@ index 000000000000..4e507ce4d10e +#endif /* _ISSEI_DEV_H_ */ diff --git a/include/uapi/linux/issei.h b/include/uapi/linux/issei.h new file mode 100644 -index 000000000000..234fbc0aeaaa +index 0000000000000..78a231775b521 --- /dev/null +++ b/include/uapi/linux/issei.h -@@ -0,0 +1,78 @@ +@@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2023-2025 Intel Corporation @@ -1079,17 +1152,6 @@ index 000000000000..234fbc0aeaaa +#define IOCTL_ISSEI_DISCONNECT_CLIENT \ + _IO('H', 0x02) + -+/* -+ * This IOCTL is used to obtain driver status -+ * -+ * The 32bit output parameter: -+ * Bit[0] - driver reset status (1 - in reset, 0 - out of reset) -+ * Bit[1-7] - Reserved -+ * Bit[8-15] - Number of resets performed after driver load -+ */ -+#define IOCTL_ISSEI_DRIVER_STATUS \ -+ _IOR('H', 0x03, __u32) -+ +#endif /* _LINUX_ISSEI_H */ -- 2.43.0 diff --git a/SPECS/kernel/0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu b/SPECS/kernel/0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu deleted file mode 100644 index afd8a13e4..000000000 --- a/SPECS/kernel/0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu +++ /dev/null @@ -1,878 +0,0 @@ -From 34b0b6fcd8a20612950a7a8a7d1b252c43313119 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 21 Oct 2025 10:35:24 +0800 -Subject: [PATCH 1/2] media: i2c: max9x: fix S3/S4 error for max9x - -Tracked-On: #JILCNT-697 - Tracked-On: #H15018371204 - -Signed-off-by: hepengpx -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/isx031.c | 70 +++++++----- - drivers/media/i2c/max9x/Makefile | 2 +- - drivers/media/i2c/max9x/max9295.c | 39 ++++--- - drivers/media/i2c/max9x/max9296.h | 2 +- - drivers/media/i2c/max9x/max96724.c | 7 +- - drivers/media/i2c/max9x/regmap-retry.c | 52 +++++++++ - drivers/media/i2c/max9x/regmap-retry.h | 18 +++ - drivers/media/i2c/max9x/serdes.c | 148 ++++++++----------------- - 8 files changed, 188 insertions(+), 150 deletions(-) - create mode 100644 drivers/media/i2c/max9x/regmap-retry.c - create mode 100644 drivers/media/i2c/max9x/regmap-retry.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index ddc3e512efa4..e613cdb06b83 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -33,6 +33,8 @@ - #define ISX031_MODE_4LANES_30FPS 0x17 - #define ISX031_MODE_2LANES_30FPS 0x18 - -+static DEFINE_MUTEX(isx031_mutex); -+ - struct isx031_reg { - enum { - ISX031_REG_LEN_DELAY = 0, -@@ -95,7 +97,7 @@ struct isx031 { - u8 lanes; - - /* To serialize asynchronus callbacks */ -- struct mutex mutex; -+ struct mutex *mutex; - - /* i2c client */ - struct i2c_client *client; -@@ -306,6 +308,21 @@ static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) - return 0; - } - -+static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ int ret; -+ int retry = 100; -+ -+ while (retry--) { -+ ret = isx031_write_reg(isx031, reg, len, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ - static int isx031_write_reg_list(struct isx031 *isx031, - const struct isx031_reg_list *r_list) - { -@@ -318,7 +335,7 @@ static int isx031_write_reg_list(struct isx031 *isx031, - msleep(r_list->regs[i].val); - continue; - } -- ret = isx031_write_reg(isx031, r_list->regs[i].address, -+ ret = isx031_write_reg_retry(isx031, r_list->regs[i].address, - ISX031_REG_LEN_08BIT, - r_list->regs[i].val); - if (ret) { -@@ -353,7 +370,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); - return ret; - } - -@@ -506,13 +523,12 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - if (isx031->streaming == enable) - return 0; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - - ret = isx031_start_streaming(isx031); -@@ -528,7 +544,8 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - - isx031->streaming = enable; - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(isx031->mutex); - - return ret; - } -@@ -553,11 +570,11 @@ static int __maybe_unused isx031_suspend(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - if (isx031->streaming) - isx031_stop_streaming(isx031); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -568,7 +585,9 @@ static int __maybe_unused isx031_resume(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - const struct isx031_reg_list *reg_list; -- int ret; -+ int ret = 0; -+ -+ mutex_lock(isx031->mutex); - - if (isx031->reset_gpio != NULL) - isx031_reset(isx031->reset_gpio); -@@ -579,27 +598,26 @@ static int __maybe_unused isx031_resume(struct device *dev) - ret = isx031_write_reg_list(isx031, reg_list); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); -- return ret; -+ goto err_unlock; - } - } else { - dev_err(&client->dev, "isx031 resume failed"); -- return ret; -+ goto err_unlock; - } - -- mutex_lock(&isx031->mutex); - if (isx031->streaming) { - ret = isx031_start_streaming(isx031); - if (ret) { - isx031->streaming = false; - isx031_stop_streaming(isx031); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - } - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(isx031->mutex); - -- return 0; -+ return ret; - } - - static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -@@ -638,7 +656,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - if (i >= ARRAY_SIZE(supported_modes)) - mode = &supported_modes[0]; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -@@ -647,7 +665,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - isx031->cur_mode = mode; - } - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -658,14 +676,14 @@ static int isx031_get_format(struct v4l2_subdev *sd, - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - isx031_update_pad_format(isx031->cur_mode, &fmt->format); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -674,10 +692,10 @@ static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(isx031->mutex); - isx031_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(isx031->mutex); - - return 0; - } -@@ -710,12 +728,10 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { - static void isx031_remove(struct i2c_client *client) - { - struct v4l2_subdev *sd = i2c_get_clientdata(client); -- struct isx031 *isx031 = to_isx031(sd); - - v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); - - } - -@@ -776,8 +792,7 @@ static int isx031_probe(struct i2c_client *client) - if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - -- mutex_init(&isx031->mutex); -- -+ isx031->mutex = &isx031_mutex; - /* 1920x1536 default */ - isx031->cur_mode = NULL; - isx031->pre_mode = &supported_modes[0]; -@@ -808,7 +823,6 @@ static int isx031_probe(struct i2c_client *client) - probe_error_media_entity_cleanup: - media_entity_cleanup(&isx031->sd.entity); - pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); - - return ret; - } -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -index ab533b790f72..ec275fe74e94 100644 ---- a/drivers/media/i2c/max9x/Makefile -+++ b/drivers/media/i2c/max9x/Makefile -@@ -7,4 +7,4 @@ ccflags-y += -I$(src)/../../pci/intel/ipu6/ - endif - - obj-m += max9x.o --max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -+max9x-y += regmap-retry.o serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index cc1ee2f5ff92..a5bacc3684a9 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -30,6 +30,7 @@ - #include - - #include "max9295.h" -+#include "regmap-retry.h" - - static const char *const max9295_gpio_chip_names[] = { - "MFP1", -@@ -261,14 +262,14 @@ static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, - struct device *dev = common->dev; - struct regmap *map = common->map; - int data_type_slot, dt; -- int ret; -+ int ret = 0; - - for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { - dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; - dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", - pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); - -- TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), - MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -@@ -510,35 +511,35 @@ static int max9295_enable_rclk(struct max9x_common *common) - dev_info(dev, "Enable RCLK: 27MHz"); - - // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_PREDEF_FREQ_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) - ); - - // Enable reference generation -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) - ); - - // Configure reference generation output on MFP4 -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_GPIO_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) - ); - - // Enable output -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) - ); - -- TRY(ret, regmap_update_bits(map, MAX9295_REG3, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REG3, - MAX9295_RCLK_SEL_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) - ); - -- TRY(ret, regmap_update_bits(map, MAX9295_REG6, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REG6, - MAX9295_RCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) - ); -@@ -553,7 +554,7 @@ static int max9295_set_local_control_channel_enabled(struct max9x_common *common - - dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ return regmap_update_bits_retry(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, - MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); - } - -@@ -575,8 +576,8 @@ static int max9295_verify_devid(struct max9x_common *common) - int ret; - - // Fetch and output chip name + revision -- TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -- TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_REV, &dev_rev)); - - switch (dev_id) { - case MAX9295A: -@@ -617,10 +618,10 @@ static int max9295_enable(struct max9x_common *common) - TRY(ret, max9295_set_local_control_channel_enabled(common, false)); - - /* Clear the pipe maps */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_9, 0)); - - /* Clear the csi port selections */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); - - return 0; - } -@@ -685,6 +686,8 @@ static int max9295_remap_addr(struct max9x_common *common) - TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, - MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); - -+ msleep(20); -+ - return 0; - } - -@@ -717,18 +720,18 @@ static int max9295_add_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr || src == 0) { - dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", - MAX9295_I2C_SRC(i2c_id, alias), virt_addr, - MAX9295_I2C_DST(i2c_id, alias), phys_addr); -- TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) - ); - -- TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_SRC(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) - ); - } -@@ -746,10 +749,10 @@ static int max9295_remove_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr) { -- return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ return regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); - } - } -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -index 38c344669df4..acc6bb03405b 100644 ---- a/drivers/media/i2c/max9x/max9296.h -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -49,7 +49,7 @@ enum max9296_link_mode { - #define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ - /* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ - --#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 350 - - #define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 - -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index b214e1176963..37a29d2ec046 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -30,6 +30,7 @@ - #include - - #include "max96724.h" -+#include "regmap-retry.h" - - // Params - int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -@@ -337,7 +338,7 @@ static int max96724_set_all_reset(struct max9x_common *common, bool enable) - - dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX96724_RESET_ALL, -+ return regmap_update_bits_retry(map, MAX96724_RESET_ALL, - MAX96724_RESET_ALL_FIELD, - MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); - } -@@ -354,8 +355,8 @@ static int max96724_soft_reset(struct max9x_common *common) - return ret; - - /* Wait for hardware available after soft reset */ -- /* TODO: Optimize sleep time 45 ms */ -- msleep(45); -+ /* TODO: Optimize sleep time 20 ms */ -+ msleep(20); - - ret = max96724_set_all_reset(common, 0); - if (ret) -diff --git a/drivers/media/i2c/max9x/regmap-retry.c b/drivers/media/i2c/max9x/regmap-retry.c -new file mode 100644 -index 000000000000..48e5ec4bcb4f ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.c -@@ -0,0 +1,52 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013--2025 Intel Corporation -+ */ -+ -+#include "regmap-retry.h" -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_write(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_update_bits(map, reg, mask, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -diff --git a/drivers/media/i2c/max9x/regmap-retry.h b/drivers/media/i2c/max9x/regmap-retry.h -new file mode 100644 -index 000000000000..9e9ac63878a0 ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.h -@@ -0,0 +1,18 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013--2025 Intel Corporation -+ */ -+ -+#ifndef _REGMAP_RETRY_H -+#define _REGMAP_RETRY_H -+ -+#include -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val); -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val); -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val); -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index 633c55504273..fce930a1fdea 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -31,6 +31,7 @@ - #include - - #include "serdes.h" -+#include "regmap-retry.h" - - static const s64 max9x_op_sys_clock[] = { - MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -@@ -397,22 +398,6 @@ static void *parse_serdes_pdata(struct device *dev) - return des_pdata; - } - --static int regmap_read_retry(struct regmap *map, unsigned int reg, -- unsigned int *val) --{ -- int ret = 0; -- int i = 0; -- -- for (i = 0; i < 50; i++) { -- ret = regmap_read(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- - static int max9x_enable_resume(struct max9x_common *common) - { - struct device *dev = common->dev; -@@ -457,6 +442,7 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - unsigned int phys_addr, virt_addr; - struct i2c_client *phys_client; - struct regmap *phys_map, *virt_map; -+ struct device *dev = &serial_link->remote.client->dev; - unsigned int val; - const struct regmap_config regmap_config = { - .reg_bits = 16, -@@ -466,27 +452,23 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - if (!serial_link->remote.pdata) - return 0; - -- ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; -- - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; - if (phys_addr == virt_addr) - return 0; - -- dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ dev_info(dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); - - phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); - if (IS_ERR_OR_NULL(phys_client)) { -- dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_client); - return ret; - } - - phys_map = regmap_init_i2c(phys_client, ®map_config); - if (IS_ERR_OR_NULL(phys_map)) { -- dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_map); - goto err_client; - } -@@ -494,36 +476,36 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); - - virt_map = ser_common->map; -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { -- dev_info(common->dev, "Remap not necessary"); -+ dev_info(dev, "Remap not necessary"); - ret = 0; - goto err_regmap; - } - - ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ dev_err(dev, "Device not present at 0x%02x", phys_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ dev_info(dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { -- dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ dev_err(dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); - goto err_regmap; - } - - msleep(100); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ dev_err(dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ dev_info(dev, "DEV_ID after: 0x%02x", val); - } - - err_regmap: -@@ -531,8 +513,6 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - err_client: - i2c_unregister_device(phys_client); - -- max9x_des_deisolate_serial_link(common, link_id); -- - return ret; - } - -@@ -582,42 +562,31 @@ int max9x_common_resume(struct max9x_common *common) - struct max9x_common *des_common = NULL; - struct device *dev = common->dev; - u32 des_link; -- u32 phys_addr, virt_addr; - int ret = 0; -- int retry = 50; - -- if (dev->platform_data) { -+ if (dev->platform_data && common->type == MAX9X_SERIALIZER) { - struct max9x_pdata *pdata = dev->platform_data; -- -- virt_addr = common->client->addr; -- phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -- -- if (common->type == MAX9X_SERIALIZER) { -- WARN_ON(pdata->num_serial_links < 1); -- -- des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -- if (des_common) { -- des_link = pdata->serial_links[0].des_link_id; -- ret = max9x_remap_serializers_resume(des_common, des_link); -- if (ret) -- return ret; -- ret = max9x_des_isolate_serial_link(des_common, des_link); -- if (ret) -- goto err_reset_serializer; -- } -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common( -+ pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ } else { -+ return ret; - } - } - -- while (retry--) { -- ret = max9x_verify_devid(common); -- if (ret) -- msleep(100); -- else -- break; -- } -+ ret = max9x_verify_devid(common); - if (ret) { - dev_err(dev, "can't get devid after resume"); -- goto err_deisolate; -+ goto err_reset_serializer; - } - - ret = max9x_enable_resume(common); -@@ -641,11 +610,6 @@ int max9x_common_resume(struct max9x_common *common) - err_disable: - max9x_disable(common); - --err_deisolate: -- if (common->type == MAX9X_SERIALIZER && des_common) { -- max9x_des_deisolate_serial_link(des_common, des_link); -- } -- - err_reset_serializer: - if (common->type == MAX9X_SERIALIZER) { - if (common->common_ops && common->common_ops->remap_reset) { -@@ -655,6 +619,11 @@ int max9x_common_resume(struct max9x_common *common) - } - } - -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ - return ret; - } - -@@ -664,26 +633,12 @@ int max9x_common_suspend(struct max9x_common *common) - - dev_dbg(common->dev, "try to suspend"); - -- max9x_disable_translations(common); - - for (link_id = 0; link_id < common->num_serial_links; link_id++) - max9x_disable_serial_link(common, link_id); - - max9x_disable(common); - -- if (common->type == MAX9X_SERIALIZER) { -- struct device *dev = common->dev; -- int ret; -- -- if (dev->platform_data) { -- if (common->common_ops && common->common_ops->remap_reset) { -- ret = common->common_ops->remap_reset(common); -- if (ret) -- return ret; -- } -- } -- } -- - return 0; - } - -@@ -1099,7 +1054,7 @@ int max9x_verify_devid(struct max9x_common *common) - * Fetch and output chip name + revision - * try both virtual address and physical address - */ -- ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); -+ ret = regmap_read_retry(map, MAX9X_DEV_ID, &dev_id); - if (ret) { - dev_warn(dev, "Failed to read chip ID from virtual address"); - if (phys_map) { -@@ -1119,7 +1074,7 @@ int max9x_verify_devid(struct max9x_common *common) - } - common->des = &max9x_chips[chip_type]; - common->type = common->des->serdes_type; -- TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, common->des->rev_reg, &dev_rev)); - dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); - - dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -@@ -1176,14 +1131,14 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - if (IS_ERR_OR_NULL(virt_map)) - goto err_virt_client; - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { - dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_virt_regmap; - } - -- ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_virt_regmap; -@@ -1191,7 +1146,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { - dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); -@@ -1200,7 +1155,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - usleep_range(1000, 1050); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_virt_regmap; -@@ -1218,10 +1173,6 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - err_client: - i2c_unregister_device(phys_client); - -- max9x_deselect_i2c_chan(common->muxc, link_id); -- -- max9x_des_deisolate_serial_link(common, link_id); -- - return ret; - } - -@@ -1786,6 +1737,7 @@ static int max9x_registered(struct v4l2_subdev *sd) - if (subdev_pdata) { - struct max9x_pdata *ser_pdata = - subdev_pdata->board_info.platform_data; -+ struct v4l2_subdev *subdev = NULL; - - WARN_ON(ser_pdata->num_serial_links < 1); - -@@ -1797,13 +1749,13 @@ static int max9x_registered(struct v4l2_subdev *sd) - * physical i2c at the same time - */ - ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; - -- struct v4l2_subdev *subdev = -- v4l2_i2c_new_subdev_board(sd->v4l2_dev, -- common->muxc->adapter[link_id], -- &subdev_pdata->board_info, NULL); -+ if (!ret) -+ subdev = v4l2_i2c_new_subdev_board( -+ sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, -+ NULL); - - ret = max9x_des_deisolate_serial_link(common, link_id); - if (ret) -@@ -2633,8 +2585,6 @@ int max9x_setup_translations(struct max9x_common *common) - break; - } - -- msleep(10); -- - return err; - } - --- -2.43.0 - diff --git a/SPECS/kernel/0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu b/SPECS/kernel/0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu deleted file mode 100644 index f0134876e..000000000 --- a/SPECS/kernel/0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu +++ /dev/null @@ -1,32439 +0,0 @@ -From f4927c135b40fcf235caf994b82a1444ca1fcca2 Mon Sep 17 00:00:00 2001 -From: Hao Yao -Date: Tue, 30 Sep 2025 15:52:51 +0800 -Subject: [PATCH 01/11] media: ipu7: IPU7 driver release for PTL Beta v6.17 iot - -Signed-off-by: Hao Yao ---- - drivers/media/i2c/isx031.c | 840 +++++ - drivers/media/i2c/lt6911gxd.c | 644 ++++ - drivers/media/i2c/max9x/Makefile | 10 + - drivers/media/i2c/max9x/max9295.c | 790 +++++ - drivers/media/i2c/max9x/max9295.h | 143 + - drivers/media/i2c/max9x/max9296.c | 881 +++++ - drivers/media/i2c/max9x/max9296.h | 144 + - drivers/media/i2c/max9x/max96717.c | 589 ++++ - drivers/media/i2c/max9x/max96717.h | 90 + - drivers/media/i2c/max9x/max96724.c | 1169 +++++++ - drivers/media/i2c/max9x/max96724.h | 241 ++ - drivers/media/i2c/max9x/max9x_pdata.h | 103 + - drivers/media/i2c/max9x/serdes.c | 2750 ++++++++++++++++ - drivers/media/i2c/max9x/serdes.h | 460 +++ - drivers/media/pci/intel/ipu7/Kconfig | 45 + - drivers/media/pci/intel/ipu7/Makefile | 37 + - .../pci/intel/ipu7/abi/ipu7_fw_boot_abi.h | 163 + - .../pci/intel/ipu7/abi/ipu7_fw_common_abi.h | 175 + - .../pci/intel/ipu7/abi/ipu7_fw_config_abi.h | 19 + - .../intel/ipu7/abi/ipu7_fw_insys_config_abi.h | 19 + - .../pci/intel/ipu7/abi/ipu7_fw_isys_abi.h | 459 +++ - .../pci/intel/ipu7/abi/ipu7_fw_msg_abi.h | 465 +++ - .../intel/ipu7/abi/ipu7_fw_psys_config_abi.h | 24 + - .../pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h | 49 + - drivers/media/pci/intel/ipu7/ipu7-boot.c | 431 +++ - drivers/media/pci/intel/ipu7/ipu7-boot.h | 25 + - drivers/media/pci/intel/ipu7/ipu7-bus.c | 158 + - drivers/media/pci/intel/ipu7/ipu7-bus.h | 69 + - .../media/pci/intel/ipu7/ipu7-buttress-regs.h | 461 +++ - drivers/media/pci/intel/ipu7/ipu7-buttress.c | 1193 +++++++ - drivers/media/pci/intel/ipu7/ipu7-buttress.h | 77 + - drivers/media/pci/intel/ipu7/ipu7-cpd.c | 277 ++ - drivers/media/pci/intel/ipu7/ipu7-cpd.h | 16 + - drivers/media/pci/intel/ipu7/ipu7-dma.c | 478 +++ - drivers/media/pci/intel/ipu7/ipu7-dma.h | 46 + - drivers/media/pci/intel/ipu7/ipu7-fw-isys.c | 389 +++ - drivers/media/pci/intel/ipu7/ipu7-fw-isys.h | 42 + - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.c | 1034 ++++++ - .../media/pci/intel/ipu7/ipu7-isys-csi-phy.h | 16 + - .../pci/intel/ipu7/ipu7-isys-csi2-regs.h | 1197 +++++++ - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c | 544 ++++ - drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h | 64 + - .../media/pci/intel/ipu7/ipu7-isys-queue.c | 1193 +++++++ - .../media/pci/intel/ipu7/ipu7-isys-queue.h | 75 + - .../media/pci/intel/ipu7/ipu7-isys-subdev.c | 348 ++ - .../media/pci/intel/ipu7/ipu7-isys-subdev.h | 56 + - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c | 693 ++++ - drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h | 70 + - .../media/pci/intel/ipu7/ipu7-isys-video.c | 1311 ++++++++ - .../media/pci/intel/ipu7/ipu7-isys-video.h | 125 + - drivers/media/pci/intel/ipu7/ipu7-isys.c | 1624 ++++++++++ - drivers/media/pci/intel/ipu7/ipu7-isys.h | 187 ++ - drivers/media/pci/intel/ipu7/ipu7-mmu.c | 854 +++++ - drivers/media/pci/intel/ipu7/ipu7-mmu.h | 414 +++ - .../media/pci/intel/ipu7/ipu7-platform-regs.h | 82 + - drivers/media/pci/intel/ipu7/ipu7-syscom.c | 79 + - drivers/media/pci/intel/ipu7/ipu7-syscom.h | 35 + - drivers/media/pci/intel/ipu7/ipu7.c | 2864 +++++++++++++++++ - drivers/media/pci/intel/ipu7/ipu7.h | 259 ++ - drivers/media/pci/intel/ipu7/psys/Makefile | 18 + - drivers/media/pci/intel/ipu7/psys/ipu-psys.c | 1545 +++++++++ - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.c | 603 ++++ - .../media/pci/intel/ipu7/psys/ipu7-fw-psys.h | 42 + - drivers/media/pci/intel/ipu7/psys/ipu7-psys.c | 398 +++ - drivers/media/pci/intel/ipu7/psys/ipu7-psys.h | 184 ++ - drivers/media/platform/intel/Kconfig | 27 +- - drivers/media/platform/intel/Makefile | 21 +- - .../media/platform/intel/ipu-acpi-common.c | 441 +++ - drivers/media/platform/intel/ipu-acpi-pdata.c | 594 ++++ - drivers/media/platform/intel/ipu-acpi.c | 224 ++ - .../media/platform/intel/ipu7-fpga-pdata.c | 60 + - include/media/i2c/isx031.h | 24 + - include/media/ipu-acpi-pdata.h | 120 + - include/media/ipu-acpi.h | 186 ++ - include/uapi/linux/ipu7-psys.h | 246 ++ - 75 files changed, 31815 insertions(+), 13 deletions(-) - create mode 100644 drivers/media/i2c/isx031.c - create mode 100644 drivers/media/i2c/lt6911gxd.c - create mode 100644 drivers/media/i2c/max9x/Makefile - create mode 100644 drivers/media/i2c/max9x/max9295.c - create mode 100644 drivers/media/i2c/max9x/max9295.h - create mode 100644 drivers/media/i2c/max9x/max9296.c - create mode 100644 drivers/media/i2c/max9x/max9296.h - create mode 100644 drivers/media/i2c/max9x/max96717.c - create mode 100644 drivers/media/i2c/max9x/max96717.h - create mode 100644 drivers/media/i2c/max9x/max96724.c - create mode 100644 drivers/media/i2c/max9x/max96724.h - create mode 100644 drivers/media/i2c/max9x/max9x_pdata.h - create mode 100644 drivers/media/i2c/max9x/serdes.c - create mode 100644 drivers/media/i2c/max9x/serdes.h - create mode 100644 drivers/media/pci/intel/ipu7/Kconfig - create mode 100644 drivers/media/pci/intel/ipu7/Makefile - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-boot.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-bus.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-buttress.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-cpd.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-dma.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-fw-isys.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-queue.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys-video.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-isys.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-mmu.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-platform-regs.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7-syscom.h - create mode 100644 drivers/media/pci/intel/ipu7/ipu7.c - create mode 100644 drivers/media/pci/intel/ipu7/ipu7.h - create mode 100644 drivers/media/pci/intel/ipu7/psys/Makefile - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu-psys.c - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.c - create mode 100644 drivers/media/pci/intel/ipu7/psys/ipu7-psys.h - create mode 100644 drivers/media/platform/intel/ipu-acpi-common.c - create mode 100644 drivers/media/platform/intel/ipu-acpi-pdata.c - create mode 100644 drivers/media/platform/intel/ipu-acpi.c - create mode 100644 drivers/media/platform/intel/ipu7-fpga-pdata.c - create mode 100644 include/media/i2c/isx031.h - create mode 100644 include/media/ipu-acpi-pdata.h - create mode 100644 include/media/ipu-acpi.h - create mode 100644 include/uapi/linux/ipu7-psys.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -new file mode 100644 -index 000000000000..e7d9e417745a ---- /dev/null -+++ b/drivers/media/i2c/isx031.c -@@ -0,0 +1,840 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2022-2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#define to_isx031(_sd) container_of(_sd, struct isx031, sd) -+ -+#define ISX031_REG_MODE_SET_F 0x8A01 -+#define ISX031_MODE_STANDBY 0x00 -+#define ISX031_MODE_STREAMING 0x80 -+ -+#define ISX031_REG_SENSOR_STATE 0x6005 -+#define ISX031_STATE_STREAMING 0x05 -+#define ISX031_STATE_STARTUP 0x02 -+ -+#define ISX031_REG_MODE_SET_F_LOCK 0xBEF0 -+#define ISX031_MODE_UNLOCK 0x53 -+ -+#define ISX031_REG_MODE_SELECT 0x8A00 -+#define ISX031_MODE_4LANES_60FPS 0x01 -+#define ISX031_MODE_4LANES_30FPS 0x17 -+#define ISX031_MODE_2LANES_30FPS 0x18 -+ -+struct isx031_reg { -+ enum { -+ ISX031_REG_LEN_DELAY = 0, -+ ISX031_REG_LEN_08BIT = 1, -+ ISX031_REG_LEN_16BIT = 2, -+ } mode; -+ u16 address; -+ u16 val; -+}; -+ -+struct isx031_reg_list { -+ u32 num_of_regs; -+ const struct isx031_reg *regs; -+}; -+ -+struct isx031_link_freq_config { -+ const struct isx031_reg_list reg_list; -+}; -+ -+struct isx031_driver_mode { -+ int lanes; -+ int fps; -+ int mode; -+}; -+ -+static const struct isx031_driver_mode isx031_driver_modes[] = { -+ { 4, 60, ISX031_MODE_4LANES_60FPS }, -+ { 4, 30, ISX031_MODE_4LANES_30FPS }, -+ { 2, 30, ISX031_MODE_2LANES_30FPS }, -+}; -+ -+struct isx031_mode { -+ /* Frame width in pixels */ -+ u32 width; -+ -+ /* Frame height in pixels */ -+ u32 height; -+ -+ /* MEDIA_BUS_FMT */ -+ u32 code; -+ -+ /* CSI-2 data type ID */ -+ u8 datatype; -+ -+ /* MODE_FPS*/ -+ u32 fps; -+ -+ /* Sensor register settings for this resolution */ -+ const struct isx031_reg_list reg_list; -+}; -+ -+struct isx031 { -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ -+ /* Current mode */ -+ const struct isx031_mode *cur_mode; -+ /* Previous mode */ -+ const struct isx031_mode *pre_mode; -+ u8 lanes; -+ -+ /* To serialize asynchronus callbacks */ -+ struct mutex mutex; -+ -+ /* i2c client */ -+ struct i2c_client *client; -+ -+ struct isx031_platform_data *platform_data; -+ struct gpio_desc *reset_gpio; -+ -+ /* Streaming on/off */ -+ bool streaming; -+}; -+ -+static const struct isx031_reg isx031_init_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0xFFFF, 0x00}, // select mode -+ {ISX031_REG_LEN_08BIT, 0x0171, 0x00}, // close F_EBD -+ {ISX031_REG_LEN_08BIT, 0x0172, 0x00}, // close R_EBD -+}; -+ -+static const struct isx031_reg isx031_framesync_reg[] = { -+ /* External sync */ -+ {ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */ -+ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ -+ {ISX031_REG_LEN_08BIT, 0x0153, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */ -+ {ISX031_REG_LEN_08BIT, 0x0144, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AF1, 0x00}, -+}; -+ -+static const struct isx031_reg isx031_1920_1536_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, // H size = 1920 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, // H croped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0x00}, // V size 1536 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x06}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0x00}, // V cropped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x80}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x06}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, -+}; -+ -+static const struct isx031_reg isx031_1920_1080_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x80}, // H size = 1920 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x00}, // H croped 0 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0x38}, // V size 1080 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x04}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0xE4}, // V cropped 228*2 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x80}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x07}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0x38}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x04}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0xE4}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x00}, -+ -+}; -+ -+static const struct isx031_reg isx031_1280_720_30fps_reg[] = { -+ {ISX031_REG_LEN_08BIT, 0x8AA8, 0x01}, // crop enable -+ {ISX031_REG_LEN_08BIT, 0x8AAA, 0x00}, // H size = 1280 -+ {ISX031_REG_LEN_08BIT, 0x8AAB, 0x05}, -+ {ISX031_REG_LEN_08BIT, 0x8AAC, 0x40}, // H croped 320*2 -+ {ISX031_REG_LEN_08BIT, 0x8AAD, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0x8AAE, 0xD0}, // V size 720 -+ {ISX031_REG_LEN_08BIT, 0x8AAF, 0x02}, -+ {ISX031_REG_LEN_08BIT, 0x8AB0, 0x98}, // V cropped 408*2 -+ {ISX031_REG_LEN_08BIT, 0x8AB1, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0x8ADA, 0x03}, // DCROP_DATA_SEL -+ {ISX031_REG_LEN_08BIT, 0xBF04, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF06, 0x00}, -+ {ISX031_REG_LEN_08BIT, 0xBF07, 0x05}, -+ {ISX031_REG_LEN_08BIT, 0xBF08, 0x40}, -+ {ISX031_REG_LEN_08BIT, 0xBF09, 0x01}, -+ {ISX031_REG_LEN_08BIT, 0xBF0A, 0xD0}, -+ {ISX031_REG_LEN_08BIT, 0xBF0B, 0x02}, -+ {ISX031_REG_LEN_08BIT, 0xBF0C, 0x98}, -+ {ISX031_REG_LEN_08BIT, 0xBF0D, 0x01}, -+}; -+ -+static const struct isx031_reg_list isx031_init_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_init_reg), -+ .regs = isx031_init_reg, -+}; -+ -+static const struct isx031_reg_list isx031_framesync_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_framesync_reg), -+ .regs = isx031_framesync_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1920_1536_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1920_1536_30fps_reg), -+ .regs = isx031_1920_1536_30fps_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1920_1080_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1920_1080_30fps_reg), -+ .regs = isx031_1920_1080_30fps_reg, -+}; -+ -+static const struct isx031_reg_list isx031_1280_720_30fps_reg_list = { -+ .num_of_regs = ARRAY_SIZE(isx031_1280_720_30fps_reg), -+ .regs = isx031_1280_720_30fps_reg, -+}; -+ -+static const struct isx031_mode supported_modes[] = { -+ { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .datatype = MIPI_CSI2_DT_YUV422_8B, -+ .fps = 30, -+ .reg_list = isx031_1920_1080_30fps_reg_list, -+ }, -+ { -+ .width = 1280, -+ .height = 720, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .datatype = MIPI_CSI2_DT_YUV422_8B, -+ .fps = 30, -+ .reg_list = isx031_1280_720_30fps_reg_list, -+ }, -+ { -+ .width = 1920, -+ .height = 1536, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .datatype = MIPI_CSI2_DT_YUV422_8B, -+ .fps = 30, -+ .reg_list = isx031_1920_1536_30fps_reg_list, -+ }, -+}; -+ -+static int isx031_reset(struct gpio_desc *reset_gpio) -+{ -+ if (!IS_ERR_OR_NULL(reset_gpio)) { -+ gpiod_set_value_cansleep(reset_gpio, 0); -+ usleep_range(500, 1000); -+ gpiod_set_value_cansleep(reset_gpio, 1); -+ /*Needs to sleep for quite a while before register writes*/ -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int isx031_read_reg(struct isx031 *isx031, u16 reg, u16 len, u32 *val) -+{ -+ struct i2c_client *client = isx031->client; -+ struct i2c_msg msgs[2]; -+ u8 addr_buf[2]; -+ u8 data_buf[4] = {0}; -+ int ret; -+ -+ if (len > 4) -+ return -EINVAL; -+ -+ put_unaligned_be16(reg, addr_buf); -+ msgs[0].addr = client->addr; -+ msgs[0].flags = 0; -+ msgs[0].len = sizeof(addr_buf); -+ msgs[0].buf = addr_buf; -+ msgs[1].addr = client->addr; -+ msgs[1].flags = I2C_M_RD; -+ msgs[1].len = len; -+ msgs[1].buf = &data_buf[4 - len]; -+ -+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); -+ if (ret != ARRAY_SIZE(msgs)) -+ return -EIO; -+ -+ *val = get_unaligned_be32(data_buf); -+ -+ return 0; -+} -+ -+static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ struct i2c_client *client = isx031->client; -+ u8 buf[6]; -+ -+ if (len > 4) -+ return -EINVAL; -+ -+ dev_dbg(&client->dev, "%s, reg %x len %x, val %x\n", __func__, reg, len, val); -+ put_unaligned_be16(reg, buf); -+ put_unaligned_be32(val << 8 * (4 - len), buf + 2); -+ if (i2c_master_send(client, buf, len + 2) != len + 2) { -+ dev_err(&client->dev, "%s:failed: reg=%2x\n", __func__, reg); -+ return -EIO; -+ } -+ -+ return 0; -+} -+ -+static int isx031_write_reg_list(struct isx031 *isx031, -+ const struct isx031_reg_list *r_list) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(&isx031->sd); -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < r_list->num_of_regs; i++) { -+ if (r_list->regs[i].mode == ISX031_REG_LEN_DELAY) { -+ msleep(r_list->regs[i].val); -+ continue; -+ } -+ ret = isx031_write_reg(isx031, r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val); -+ if (ret) { -+ dev_err_ratelimited(&client->dev, -+ "failed to write reg 0x%4.4x. error = %d", -+ r_list->regs[i].address, ret); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static int isx031_find_driver_mode(int lanes, int fps) -+{ -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(isx031_driver_modes); i++) { -+ if (isx031_driver_modes[i].lanes == lanes && isx031_driver_modes[i].fps == fps) -+ return isx031_driver_modes[i].mode; -+ } -+ -+ return -EINVAL; -+} -+ -+static int isx031_set_driver_mode(struct isx031 *isx031) -+{ -+ int ret; -+ int mode; -+ -+ mode = isx031_find_driver_mode(isx031->lanes, isx031->cur_mode->fps); -+ if (mode < 0) -+ return mode; -+ -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ return ret; -+} -+ -+static int isx031_mode_transit(struct isx031 *isx031, int state) -+{ -+ struct i2c_client *client = isx031->client; -+ int ret; -+ int cur_mode, mode; -+ u32 val; -+ int retry = 50; -+ -+ if (state == ISX031_STATE_STARTUP) -+ mode = ISX031_MODE_STANDBY; -+ else if (state == ISX031_STATE_STREAMING) -+ mode = ISX031_MODE_STREAMING; -+ -+ retry = 50; -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0) -+ break; -+ usleep_range(10000, 10500); -+ } -+ cur_mode = val; -+ -+ //TODO: only set if isx031->lanes != 0, means get lanes from pdata -+ ret = isx031_set_driver_mode(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to set driver mode"); -+ return ret; -+ } -+ -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F_LOCK, 1, -+ ISX031_MODE_UNLOCK); -+ if (ret) { -+ dev_err(&client->dev, "failed to unlock mode"); -+ return ret; -+ } -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -+ mode); -+ if (ret) { -+ dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", -+ cur_mode, mode); -+ return ret; -+ } -+ -+ /*streaming transit to standby need 1 frame+5ms*/ -+ retry = 50; -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0 && val == state) -+ break; -+ usleep_range(10000, 10500); -+ } -+ -+ return 0; -+} -+ -+static int isx031_identify_module(struct isx031 *isx031) -+{ -+ struct i2c_client *client = isx031->client; -+ int ret; -+ int retry = 50; -+ u32 val; -+ -+ while (retry--) { -+ ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ISX031_REG_LEN_08BIT, &val); -+ if (ret == 0) -+ break; -+ usleep_range(100000, 100500); -+ } -+ -+ if (ret) -+ return ret; -+ -+ dev_dbg(&client->dev, "sensor in mode 0x%x", val); -+ -+ /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly*/ -+ if (val == ISX031_STATE_STREAMING) { -+ if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) -+ return ret; -+ } -+ -+ ret = isx031_write_reg_list(isx031, &isx031_init_reg_list); -+ if (ret) -+ return ret; -+ if (isx031->platform_data != NULL && !isx031->platform_data->irq_pin_flags) { -+ ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed in set framesync."); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static void isx031_update_pad_format(const struct isx031_mode *mode, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ fmt->width = mode->width; -+ fmt->height = mode->height; -+ fmt->code = mode->code; -+ fmt->field = V4L2_FIELD_NONE; -+} -+ -+static int isx031_start_streaming(struct isx031 *isx031) -+{ -+ int ret; -+ struct i2c_client *client = isx031->client; -+ const struct isx031_reg_list *reg_list; -+ -+ if (isx031->cur_mode != isx031->pre_mode) { -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed to set stream mode"); -+ return ret; -+ } -+ isx031->pre_mode = isx031->cur_mode; -+ } else { -+ dev_dbg(&client->dev, "same mode, skip write reg list"); -+ } -+ -+ ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); -+ if (ret) { -+ dev_err(&client->dev, "failed to start streaming"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void isx031_stop_streaming(struct isx031 *isx031) -+{ -+ struct i2c_client *client = isx031->client; -+ if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) -+ dev_err(&client->dev, "failed to stop streaming"); -+} -+ -+static int isx031_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ struct i2c_client *client = isx031->client; -+ int ret = 0; -+ -+ if (isx031->streaming == enable) -+ return 0; -+ -+ mutex_lock(&isx031->mutex); -+ if (enable) { -+ ret = pm_runtime_get_sync(&client->dev); -+ if (ret < 0) { -+ pm_runtime_put_noidle(&client->dev); -+ mutex_unlock(&isx031->mutex); -+ return ret; -+ } -+ -+ ret = isx031_start_streaming(isx031); -+ if (ret) { -+ enable = 0; -+ isx031_stop_streaming(isx031); -+ pm_runtime_put(&client->dev); -+ } -+ } else { -+ isx031_stop_streaming(isx031); -+ pm_runtime_put(&client->dev); -+ } -+ -+ isx031->streaming = enable; -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return ret; -+} -+ -+static int isx031_enable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return isx031_set_stream(subdev, true); -+} -+ -+static int isx031_disable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return isx031_set_stream(subdev, false); -+} -+ -+static int __maybe_unused isx031_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ if (isx031->streaming) -+ isx031_stop_streaming(isx031); -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int __maybe_unused isx031_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ const struct isx031_reg_list *reg_list; -+ int ret; -+ -+ if (isx031->reset_gpio != NULL) -+ isx031_reset(isx031->reset_gpio); -+ -+ ret = isx031_identify_module(isx031); -+ if (ret == 0) { -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "resume: failed to apply cur mode"); -+ return ret; -+ } -+ } else { -+ dev_err(&client->dev, "isx031 resume failed"); -+ return ret; -+ } -+ -+ mutex_lock(&isx031->mutex); -+ if (isx031->streaming) { -+ ret = isx031_start_streaming(isx031); -+ if (ret) { -+ isx031->streaming = false; -+ isx031_stop_streaming(isx031); -+ mutex_unlock(&isx031->mutex); -+ return ret; -+ } -+ } -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -+ struct v4l2_mbus_frame_desc *desc) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -+ desc->num_entries = 0; -+ desc->entry[desc->num_entries].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; -+ desc->entry[desc->num_entries].stream = 0; -+ desc->entry[desc->num_entries].pixelcode = isx031->cur_mode->code; -+ desc->entry[desc->num_entries].length = 0; -+ desc->entry[desc->num_entries].bus.csi2.vc = 0; -+ desc->entry[desc->num_entries].bus.csi2.dt = isx031->cur_mode->datatype; -+ desc->num_entries++; -+ return 0; -+} -+ -+static int isx031_set_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ const struct isx031_mode *mode; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) -+ if (supported_modes[i].code == fmt->format.code && -+ supported_modes[i].width == fmt->format.width && -+ supported_modes[i].height == fmt->format.height) { -+ mode = &supported_modes[i]; -+ break; -+ } -+ -+ if (i >= ARRAY_SIZE(supported_modes)) -+ mode = &supported_modes[0]; -+ -+ mutex_lock(&isx031->mutex); -+ -+ isx031_update_pad_format(mode, &fmt->format); -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; -+ } else { -+ isx031->cur_mode = mode; -+ } -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_get_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ fmt->format = *v4l2_subdev_state_get_format(sd_state, -+ fmt->pad); -+ else -+ isx031_update_pad_format(isx031->cur_mode, &fmt->format); -+ -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -+{ -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ mutex_lock(&isx031->mutex); -+ isx031_update_pad_format(&supported_modes[0], -+ v4l2_subdev_state_get_format(fh->state, 0)); -+ mutex_unlock(&isx031->mutex); -+ -+ return 0; -+} -+ -+static const struct v4l2_subdev_video_ops isx031_video_ops = { -+ .s_stream = isx031_set_stream, -+}; -+ -+static const struct v4l2_subdev_pad_ops isx031_pad_ops = { -+ .set_fmt = isx031_set_format, -+ .get_fmt = isx031_get_format, -+ .get_frame_desc = isx031_get_frame_desc, -+ .enable_streams = isx031_enable_streams, -+ .disable_streams = isx031_disable_streams, -+}; -+ -+static const struct v4l2_subdev_ops isx031_subdev_ops = { -+ .video = &isx031_video_ops, -+ .pad = &isx031_pad_ops, -+}; -+ -+static const struct media_entity_operations isx031_subdev_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+static const struct v4l2_subdev_internal_ops isx031_internal_ops = { -+ .open = isx031_open, -+}; -+ -+static void isx031_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); -+ -+ v4l2_async_unregister_subdev(sd); -+ media_entity_cleanup(&sd->entity); -+ pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); -+ -+} -+ -+static int isx031_probe(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd; -+ struct isx031 *isx031; -+ const struct isx031_reg_list *reg_list; -+ int ret; -+ -+ isx031 = devm_kzalloc(&client->dev, sizeof(*isx031), GFP_KERNEL); -+ if (!isx031) -+ return -ENOMEM; -+ -+ isx031->client = client; -+ isx031->platform_data = client->dev.platform_data; -+ if (isx031->platform_data == NULL) -+ dev_warn(&client->dev, "no platform data provided\n"); -+ -+ isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", -+ GPIOD_OUT_HIGH); -+ if (IS_ERR(isx031->reset_gpio)) -+ return -EPROBE_DEFER; -+ else if (isx031->reset_gpio == NULL) -+ dev_warn(&client->dev, "Reset GPIO not found"); -+ else { -+ dev_dbg(&client->dev, "Found reset GPIO"); -+ isx031_reset(isx031->reset_gpio); -+ } -+ -+ /* initialize subdevice */ -+ sd = &isx031->sd; -+ v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops); -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; -+ sd->internal_ops = &isx031_internal_ops; -+ sd->entity.ops = &isx031_subdev_entity_ops; -+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ -+ /* initialize subdev media pad */ -+ isx031->pad.flags = MEDIA_PAD_FL_SOURCE; -+ ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad); -+ if (ret < 0) { -+ dev_err(&client->dev, -+ "%s : media entity init Failed %d\n", __func__, ret); -+ return ret; -+ } -+ -+ ret = isx031_identify_module(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to find sensor: %d", ret); -+ return ret; -+ } -+ -+ if (isx031->platform_data && isx031->platform_data->suffix) -+ snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %s", -+ isx031->platform_data->suffix); -+ -+ if (isx031->platform_data && isx031->platform_data->lanes) -+ isx031->lanes = isx031->platform_data->lanes; -+ -+ mutex_init(&isx031->mutex); -+ -+ /* 1920x1536 default */ -+ isx031->cur_mode = NULL; -+ isx031->pre_mode = &supported_modes[0]; -+ reg_list = &isx031->pre_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list); -+ if (ret) { -+ dev_err(&client->dev, "failed to apply preset mode"); -+ goto probe_error_media_entity_cleanup; -+ } -+ isx031->cur_mode = isx031->pre_mode; -+ ret = v4l2_async_register_subdev_sensor(&isx031->sd); -+ if (ret < 0) { -+ dev_err(&client->dev, "failed to register V4L2 subdev: %d", -+ ret); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ /* -+ * Device is already turned on by i2c-core with ACPI domain PM. -+ * Enable runtime PM and turn off the device. -+ */ -+ pm_runtime_set_active(&client->dev); -+ pm_runtime_enable(&client->dev); -+ pm_runtime_idle(&client->dev); -+ -+ return 0; -+ -+probe_error_media_entity_cleanup: -+ media_entity_cleanup(&isx031->sd.entity); -+ pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops isx031_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(isx031_suspend, isx031_resume) -+}; -+ -+static const struct i2c_device_id isx031_id_table[] = { -+ { "isx031", 0 }, -+ { /* sentinel */ }, -+}; -+MODULE_DEVICE_TABLE(i2c, isx031_id_table); -+ -+static struct i2c_driver isx031_i2c_driver = { -+ .driver = { -+ .name = "isx031", -+ .pm = &isx031_pm_ops, -+ }, -+ .probe = isx031_probe, -+ .remove = isx031_remove, -+ .id_table = isx031_id_table, -+}; -+ -+module_i2c_driver(isx031_i2c_driver); -+ -+MODULE_AUTHOR("Hao Yao "); -+MODULE_DESCRIPTION("isx031 sensor driver"); -+MODULE_LICENSE("GPL v2"); -diff --git a/drivers/media/i2c/lt6911gxd.c b/drivers/media/i2c/lt6911gxd.c -new file mode 100644 -index 000000000000..d791560ce1a3 ---- /dev/null -+++ b/drivers/media/i2c/lt6911gxd.c -@@ -0,0 +1,644 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2023 - 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define LT6911GXD_CHIP_ID 0x2204 -+#define REG_CHIP_ID CCI_REG16(0xe100) -+ -+#define REG_ENABLE_I2C CCI_REG8(0xe0ee) -+ -+#define REG_PIX_CLK CCI_REG24(0xe085) -+#define REG_BYTE_CLK CCI_REG24(0xe092) -+#define REG_H_TOTAL CCI_REG16(0xe088) -+#define REG_V_TOTAL CCI_REG16(0xe08a) -+#define REG_HALF_H_ACTIVE CCI_REG16(0xe08c) -+#define REG_V_ACTIVE CCI_REG16(0xe08e) -+#define REG_MIPI_FORMAT CCI_REG8(0xf988) -+#define REG_MIPI_TX_CTRL CCI_REG8(0xe0b0) -+ -+/* Interrupts */ -+#define REG_INT_HDMI CCI_REG8(0xe084) -+#define INT_VIDEO_DISAPPEAR 0x0 -+#define INT_VIDEO_READY 0x1 -+ -+#define LT6911GXD_DEFAULT_WIDTH 3840 -+#define LT6911GXD_DEFAULT_HEIGHT 2160 -+#define LT6911GXD_DEFAULT_LANES 2 -+#define LT6911GXD_DEFAULT_FPS 30 -+#define LT6911GXD_MAX_LINK_FREQ 297000000 -+#define LT6911GXD_PAGE_CONTROL 0xff -+#define BPP_MODE_BIT 4 -+#define YUV16_BIT 2 -+ -+static const struct regmap_range_cfg lt6911gxd_ranges[] = { -+ { -+ .name = "register_range", -+ .range_min = 0, -+ .range_max = 0xffff, -+ .selector_reg = LT6911GXD_PAGE_CONTROL, -+ .selector_mask = 0xff, -+ .selector_shift = 0, -+ .window_start = 0, -+ .window_len = 0x100, -+ }, -+}; -+ -+static const struct regmap_config lt6911gxd_regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = 0xffff, -+ .ranges = lt6911gxd_ranges, -+ .num_ranges = ARRAY_SIZE(lt6911gxd_ranges), -+}; -+ -+struct lt6911gxd_mode { -+ u32 width; -+ u32 height; -+ u32 code; -+ u32 fps; -+ u32 lanes; -+ s64 link_freq; -+}; -+ -+struct lt6911gxd { -+ struct v4l2_subdev sd; -+ struct media_pad pad; -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct v4l2_ctrl *pixel_rate; -+ struct v4l2_ctrl *link_freq; -+ -+ struct lt6911gxd_mode cur_mode; -+ struct regmap *regmap; -+ struct gpio_desc *reset_gpio; -+ struct gpio_desc *irq_gpio; -+}; -+ -+static const struct v4l2_event lt6911gxd_ev_source_change = { -+ .type = V4L2_EVENT_SOURCE_CHANGE, -+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, -+}; -+ -+static const struct v4l2_event lt6911gxd_ev_stream_end = { -+ .type = V4L2_EVENT_EOS, -+}; -+ -+static inline struct lt6911gxd *to_lt6911gxd(struct v4l2_subdev *sd) -+{ -+ return container_of(sd, struct lt6911gxd, sd); -+} -+ -+static const struct lt6911gxd_mode default_mode = { -+ .width = LT6911GXD_DEFAULT_WIDTH, -+ .height = LT6911GXD_DEFAULT_HEIGHT, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ .fps = LT6911GXD_DEFAULT_FPS, -+ .lanes = LT6911GXD_DEFAULT_LANES, -+ .link_freq = LT6911GXD_MAX_LINK_FREQ, -+}; -+ -+static s64 get_pixel_rate(struct lt6911gxd *lt6911gxd) -+{ -+ s64 pixel_rate; -+ -+ pixel_rate = (s64)lt6911gxd->cur_mode.width * -+ lt6911gxd->cur_mode.height * -+ lt6911gxd->cur_mode.fps * 16; -+ do_div(pixel_rate, lt6911gxd->cur_mode.lanes); -+ -+ return pixel_rate; -+} -+ -+static int lt6911gxd_status_update(struct lt6911gxd *lt6911gxd) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(<6911gxd->sd); -+ u64 int_event; -+ u64 byte_clk, pixel_clk, fps; -+ u64 htotal, vtotal, width, height; -+ int timeout_cnt = 3; -+ int ret = 0; -+ -+ /* Read interrupt event */ -+ cci_read(lt6911gxd->regmap, REG_INT_HDMI, &int_event, &ret); -+ while (ret && timeout_cnt--) { -+ ret = cci_read(lt6911gxd->regmap, REG_INT_HDMI, -+ &int_event, NULL); -+ } -+ -+ if (ret) -+ return dev_err_probe(&client->dev, ret, -+ "failed to read interrupt event\n"); -+ -+ /* TODO: add audio ready and disappear type */ -+ switch (int_event) { -+ case INT_VIDEO_READY: -+ cci_read(lt6911gxd->regmap, REG_BYTE_CLK, &byte_clk, &ret); -+ byte_clk *= 1000; -+ cci_read(lt6911gxd->regmap, REG_PIX_CLK, &pixel_clk, &ret); -+ pixel_clk *= 1000; -+ -+ if (ret || byte_clk == 0 || pixel_clk == 0) { -+ dev_err(&client->dev, -+ "invalid ByteClock or PixelClock\n"); -+ return -EINVAL; -+ } -+ -+ cci_read(lt6911gxd->regmap, REG_H_TOTAL, &htotal, &ret); -+ cci_read(lt6911gxd->regmap, REG_V_TOTAL, &vtotal, &ret); -+ if (ret || htotal == 0 || vtotal == 0) { -+ dev_err(&client->dev, "invalid htotal or vtotal\n"); -+ return -EINVAL; -+ } -+ -+ fps = div_u64(pixel_clk, htotal * vtotal); -+ if (fps > 60) { -+ dev_err(&client->dev, -+ "max fps is 60, current fps: %llu\n", fps); -+ return -EINVAL; -+ } -+ -+ cci_read(lt6911gxd->regmap, REG_HALF_H_ACTIVE, -+ &width, &ret); -+ cci_read(lt6911gxd->regmap, REG_V_ACTIVE, &height, &ret); -+ if (ret || width == 0 || width > 3840 || -+ height == 0 || height > 2160) { -+ dev_err(&client->dev, "invalid width or height\n"); -+ return -EINVAL; -+ } -+ -+ lt6911gxd->cur_mode.height = height; -+ lt6911gxd->cur_mode.width = width; -+ lt6911gxd->cur_mode.fps = fps; -+ /* MIPI Clock Rate = ByteClock × 4, defined in lt6911gxd spec */ -+ lt6911gxd->cur_mode.link_freq = byte_clk * 4; -+ v4l2_subdev_notify_event(<6911gxd->sd, -+ <6911gxd_ev_source_change); -+ break; -+ -+ case INT_VIDEO_DISAPPEAR: -+ cci_write(lt6911gxd->regmap, REG_MIPI_TX_CTRL, 0x0, NULL); -+ lt6911gxd->cur_mode.height = 0; -+ lt6911gxd->cur_mode.width = 0; -+ lt6911gxd->cur_mode.fps = 0; -+ lt6911gxd->cur_mode.link_freq = 0; -+ v4l2_subdev_notify_event(<6911gxd->sd, -+ <6911gxd_ev_stream_end); -+ break; -+ -+ default: -+ return -ENOLINK; -+ } -+ -+ return ret; -+} -+ -+/* TODO: add audio sampling rate and present control */ -+static int lt6911gxd_init_controls(struct lt6911gxd *lt6911gxd) -+{ -+ struct v4l2_ctrl_handler *ctrl_hdlr; -+ s64 pixel_rate; -+ int ret; -+ -+ ctrl_hdlr = <6911gxd->ctrl_handler; -+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); -+ if (ret) -+ return ret; -+ -+ lt6911gxd->link_freq = -+ v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL, V4L2_CID_LINK_FREQ, -+ sizeof(lt6911gxd->cur_mode.link_freq), -+ 0, <6911gxd->cur_mode.link_freq); -+ -+ pixel_rate = get_pixel_rate(lt6911gxd); -+ lt6911gxd->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL, -+ V4L2_CID_PIXEL_RATE, -+ pixel_rate, pixel_rate, 1, -+ pixel_rate); -+ -+ if (ctrl_hdlr->error) { -+ ret = ctrl_hdlr->error; -+ goto hdlr_free; -+ } -+ lt6911gxd->sd.ctrl_handler = ctrl_hdlr; -+ -+ return 0; -+ -+hdlr_free: -+ v4l2_ctrl_handler_free(ctrl_hdlr); -+ return ret; -+} -+ -+static void lt6911gxd_update_pad_format(const struct lt6911gxd_mode *mode, -+ struct v4l2_mbus_framefmt *fmt) -+{ -+ fmt->width = mode->width; -+ fmt->height = mode->height; -+ fmt->code = mode->code; -+ fmt->field = V4L2_FIELD_NONE; -+} -+ -+static int lt6911gxd_enable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct i2c_client *client = v4l2_get_subdevdata(sd); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ int ret; -+ -+ ret = pm_runtime_resume_and_get(&client->dev); -+ if (ret < 0) -+ return ret; -+ -+ cci_write(lt6911gxd->regmap, REG_MIPI_TX_CTRL, 0x1, &ret); -+ if (ret) { -+ dev_err(&client->dev, "failed to start stream: %d\n", ret); -+ goto err_rpm_put; -+ } -+ -+ return 0; -+ -+err_rpm_put: -+ pm_runtime_put(&client->dev); -+ return ret; -+} -+ -+static int lt6911gxd_disable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ struct i2c_client *client = v4l2_get_subdevdata(<6911gxd->sd); -+ int ret; -+ -+ ret = cci_write(lt6911gxd->regmap, REG_MIPI_TX_CTRL, 0x0, NULL); -+ if (ret) -+ dev_err(&client->dev, "failed to stop stream: %d\n", ret); -+ -+ pm_runtime_put(&client->dev); -+ return 0; -+} -+ -+static int lt6911gxd_set_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ u64 pixel_rate, link_freq; -+ -+ lt6911gxd_update_pad_format(<6911gxd->cur_mode, &fmt->format); -+ *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ return 0; -+ -+ pixel_rate = get_pixel_rate(lt6911gxd); -+ __v4l2_ctrl_modify_range(lt6911gxd->pixel_rate, pixel_rate, -+ pixel_rate, 1, pixel_rate); -+ -+ link_freq = lt6911gxd->cur_mode.link_freq; -+ __v4l2_ctrl_modify_range(lt6911gxd->link_freq, link_freq, -+ link_freq, 1, link_freq); -+ -+ return 0; -+} -+ -+static int lt6911gxd_get_format(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ fmt->format = *v4l2_subdev_state_get_format(sd_state, fmt->pad); -+ else -+ lt6911gxd_update_pad_format(<6911gxd->cur_mode, &fmt->format); -+ -+ return 0; -+} -+ -+static int lt6911gxd_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (code->index) -+ return -EINVAL; -+ -+ code->code = lt6911gxd->cur_mode.code; -+ -+ return 0; -+} -+ -+static int lt6911gxd_enum_frame_size(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_frame_size_enum *fse) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (fse->index != 0) -+ return -EINVAL; -+ -+ if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16) -+ return -EINVAL; -+ -+ fse->min_width = lt6911gxd->cur_mode.width; -+ fse->max_width = fse->min_width; -+ fse->min_height = lt6911gxd->cur_mode.height; -+ fse->max_height = fse->min_height; -+ -+ return 0; -+} -+ -+static int lt6911gxd_enum_frame_interval(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state, -+ struct v4l2_subdev_frame_interval_enum *fie) -+{ -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ if (fie->index != 0) -+ return -EINVAL; -+ -+ fie->interval.numerator = 1; -+ fie->interval.denominator = lt6911gxd->cur_mode.fps; -+ -+ return 0; -+} -+ -+static int lt6911gxd_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *sd_state) -+{ -+ struct v4l2_subdev_format fmt = { -+ .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY -+ : V4L2_SUBDEV_FORMAT_ACTIVE, -+ }; -+ -+ return lt6911gxd_set_format(sd, sd_state, &fmt); -+} -+ -+static const struct v4l2_subdev_video_ops lt6911gxd_video_ops = { -+ .s_stream = v4l2_subdev_s_stream_helper, -+}; -+ -+static const struct v4l2_subdev_pad_ops lt6911gxd_pad_ops = { -+ .set_fmt = lt6911gxd_set_format, -+ .get_fmt = lt6911gxd_get_format, -+ .enable_streams = lt6911gxd_enable_streams, -+ .disable_streams = lt6911gxd_disable_streams, -+ .enum_mbus_code = lt6911gxd_enum_mbus_code, -+ .enum_frame_size = lt6911gxd_enum_frame_size, -+ .enum_frame_interval = lt6911gxd_enum_frame_interval, -+ .get_frame_interval = v4l2_subdev_get_frame_interval, -+}; -+ -+static const struct v4l2_subdev_core_ops lt6911gxd_subdev_core_ops = { -+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static const struct v4l2_subdev_ops lt6911gxd_subdev_ops = { -+ .core = <6911gxd_subdev_core_ops, -+ .video = <6911gxd_video_ops, -+ .pad = <6911gxd_pad_ops, -+}; -+ -+static const struct media_entity_operations lt6911gxd_subdev_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+static const struct v4l2_subdev_internal_ops lt6911gxd_internal_ops = { -+ .init_state = lt6911gxd_init_state, -+}; -+ -+static int lt6911gxd_fwnode_parse(struct lt6911gxd *lt6911gxd, -+ struct device *dev) -+{ -+ struct fwnode_handle *endpoint; -+ struct v4l2_fwnode_endpoint bus_cfg = { -+ .bus_type = V4L2_MBUS_CSI2_CPHY, -+ }; -+ int ret; -+ -+ endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!endpoint) -+ return dev_err_probe(dev, -EPROBE_DEFER, -+ "endpoint node not found\n"); -+ -+ ret = v4l2_fwnode_endpoint_parse(endpoint, &bus_cfg); -+ fwnode_handle_put(endpoint); -+ if (ret) { -+ dev_err(dev, "failed to parse endpoint node: %d\n", ret); -+ goto out_err; -+ } -+ -+ /* -+ * Check the number of MIPI CSI2 data lanes, -+ * lt6911gxd only support 4 lanes. -+ */ -+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != LT6911GXD_DEFAULT_LANES) { -+ dev_err(dev, "only 2 data lanes are currently supported\n"); -+ goto out_err; -+ } -+ -+ return 0; -+ -+out_err: -+ v4l2_fwnode_endpoint_free(&bus_cfg); -+ return ret; -+} -+ -+static int lt6911gxd_identify_module(struct lt6911gxd *lt6911gxd, -+ struct device *dev) -+{ -+ u64 val; -+ int ret = 0; -+ -+ /* Chip ID should be confirmed when the I2C slave is active */ -+ cci_write(lt6911gxd->regmap, REG_ENABLE_I2C, 0x1, &ret); -+ cci_read(lt6911gxd->regmap, REG_CHIP_ID, &val, &ret); -+ cci_write(lt6911gxd->regmap, REG_ENABLE_I2C, 0x0, &ret); -+ if (ret) -+ return dev_err_probe(dev, ret, "fail to read chip id\n"); -+ -+ if (val != LT6911GXD_CHIP_ID) { -+ return dev_err_probe(dev, -ENXIO, "chip id mismatch: %x!=%x\n", -+ LT6911GXD_CHIP_ID, (u16)val); -+ } -+ -+ return 0; -+} -+ -+static irqreturn_t lt6911gxd_threaded_irq_fn(int irq, void *dev_id) -+{ -+ struct v4l2_subdev *sd = dev_id; -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev_format fmt = { -+ .which = V4L2_SUBDEV_FORMAT_ACTIVE -+ }; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ lt6911gxd_status_update(lt6911gxd); -+ lt6911gxd_set_format(sd, state, &fmt); -+ v4l2_subdev_unlock_state(state); -+ -+ return IRQ_HANDLED; -+} -+ -+static void lt6911gxd_remove(struct i2c_client *client) -+{ -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ free_irq(gpiod_to_irq(lt6911gxd->irq_gpio), lt6911gxd); -+ v4l2_async_unregister_subdev(sd); -+ v4l2_subdev_cleanup(sd); -+ media_entity_cleanup(&sd->entity); -+ v4l2_ctrl_handler_free(<6911gxd->ctrl_handler); -+ pm_runtime_disable(&client->dev); -+ pm_runtime_set_suspended(&client->dev); -+} -+ -+static int lt6911gxd_probe(struct i2c_client *client) -+{ -+ struct lt6911gxd *lt6911gxd; -+ struct device *dev = &client->dev; -+ u64 irq_pin_flags; -+ int ret; -+ -+ lt6911gxd = devm_kzalloc(dev, sizeof(*lt6911gxd), GFP_KERNEL); -+ if (!lt6911gxd) -+ return -ENOMEM; -+ -+ /* define default mode: 4k@60fps, changed when interrupt occurs. */ -+ lt6911gxd->cur_mode = default_mode; -+ -+ lt6911gxd->regmap = devm_regmap_init_i2c(client, -+ <6911gxd_regmap_config); -+ if (IS_ERR(lt6911gxd->regmap)) -+ return dev_err_probe(dev, PTR_ERR(lt6911gxd->regmap), -+ "failed to init CCI\n"); -+ -+ v4l2_i2c_subdev_init(<6911gxd->sd, client, <6911gxd_subdev_ops); -+ -+ lt6911gxd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); -+ if (IS_ERR(lt6911gxd->reset_gpio)) -+ return dev_err_probe(dev, PTR_ERR(lt6911gxd->reset_gpio), -+ "failed to get reset gpio\n"); -+ -+ ret = lt6911gxd_fwnode_parse(lt6911gxd, dev); -+ if (ret) -+ return ret; -+ -+ usleep_range(10000, 10500); -+ -+ ret = lt6911gxd_identify_module(lt6911gxd, dev); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to find chip\n"); -+ -+ ret = lt6911gxd_init_controls(lt6911gxd); -+ if (ret) -+ return dev_err_probe(dev, ret, "failed to init control\n"); -+ -+ lt6911gxd->sd.dev = dev; -+ lt6911gxd->sd.internal_ops = <6911gxd_internal_ops; -+ lt6911gxd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -+ V4L2_SUBDEV_FL_HAS_EVENTS; -+ lt6911gxd->sd.entity.ops = <6911gxd_subdev_entity_ops; -+ lt6911gxd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -+ lt6911gxd->pad.flags = MEDIA_PAD_FL_SOURCE; -+ ret = media_entity_pads_init(<6911gxd->sd.entity, 1, <6911gxd->pad); -+ if (ret) { -+ dev_err(dev, "failed to init entity pads: %d\n", ret); -+ goto err_v4l2_ctrl_handler_free; -+ } -+ -+ /* -+ * Device is already turned on by i2c-core with ACPI domain PM. -+ * Enable runtime PM and turn off the device. -+ */ -+ pm_runtime_set_active(dev); -+ pm_runtime_enable(dev); -+ pm_runtime_idle(dev); -+ -+ ret = v4l2_subdev_init_finalize(<6911gxd->sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); -+ goto err_media_entity_cleanup; -+ } -+ -+ lt6911gxd->irq_gpio = devm_gpiod_get(dev, "hpd", GPIOD_IN); -+ if (IS_ERR(lt6911gxd->irq_gpio)) -+ return dev_err_probe(dev, PTR_ERR(lt6911gxd->irq_gpio), -+ "failed to get hpd gpio\n"); -+ dev_dbg(dev, "%s: use hpd irq_gpio!\n", __func__); -+ -+ /* Setting irq */ -+ irq_pin_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | -+ IRQF_ONESHOT; -+ -+ ret = request_threaded_irq(gpiod_to_irq(lt6911gxd->irq_gpio), NULL, -+ lt6911gxd_threaded_irq_fn, -+ irq_pin_flags, NULL, lt6911gxd); -+ if (ret) { -+ dev_err(dev, "failed to request IRQ: %d\n", ret); -+ goto err_subdev_cleanup; -+ } -+ -+ ret = v4l2_async_register_subdev_sensor(<6911gxd->sd); -+ if (ret) { -+ dev_err(dev, "failed to register V4L2 subdev: %d\n", ret); -+ goto err_free_irq; -+ } -+ -+ return 0; -+ -+err_free_irq: -+ free_irq(gpiod_to_irq(lt6911gxd->irq_gpio), lt6911gxd); -+ -+err_subdev_cleanup: -+ v4l2_subdev_cleanup(<6911gxd->sd); -+ -+err_media_entity_cleanup: -+ pm_runtime_disable(dev); -+ pm_runtime_set_suspended(dev); -+ media_entity_cleanup(<6911gxd->sd.entity); -+ -+err_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(lt6911gxd->sd.ctrl_handler); -+ -+ return ret; -+} -+ -+static const struct acpi_device_id lt6911gxd_acpi_ids[] = { -+ { "INTC1124" }, -+ {} -+}; -+MODULE_DEVICE_TABLE(acpi, lt6911gxd_acpi_ids); -+ -+static struct i2c_driver lt6911gxd_i2c_driver = { -+ .driver = { -+ .name = "lt6911gxd", -+ .acpi_match_table = ACPI_PTR(lt6911gxd_acpi_ids), -+ }, -+ .probe = lt6911gxd_probe, -+ .remove = lt6911gxd_remove, -+}; -+ -+module_i2c_driver(lt6911gxd_i2c_driver); -+ -+MODULE_AUTHOR("Zou, Xiaohong xiaohong.zou@intel.com"); -+MODULE_DESCRIPTION("Lontium lt6911gxd HDMI to MIPI Bridge Driver"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -new file mode 100644 -index 000000000000..ab533b790f72 ---- /dev/null -+++ b/drivers/media/i2c/max9x/Makefile -@@ -0,0 +1,10 @@ -+ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU7)),) -+ccflags-y += -I$(src)/../../pci/intel/ipu7/ -+endif -+ -+ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU6)),) -+ccflags-y += -I$(src)/../../pci/intel/ipu6/ -+endif -+ -+obj-m += max9x.o -+max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -new file mode 100644 -index 000000000000..cc1ee2f5ff92 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -0,0 +1,790 @@ -+/* -+ * max9295_main.c - Maxim MAX9295 CSI-2 to GMSL2/GMSL1 Serializer -+ * -+ * Copyright (c) 2020, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max9295.h" -+ -+static const char *const max9295_gpio_chip_names[] = { -+ "MFP1", -+ "MFP2", -+ "MFP3", -+ "MFP4", -+ "MFP5", -+ "MFP6", -+ "MFP7", -+ "MFP8", -+ "MFP9", -+ "MFP10", -+ "MFP11", -+}; -+ -+/* Declarations */ -+static int max9295_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int csi_id, bool enable); -+static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max9295_set_video_pipe_double_loading(struct max9x_common *common, unsigned int pipe_id, unsigned int bpp); -+static int max9295_set_video_pipe_pixel_padding(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int min_bpp, unsigned int max_bpp); -+static int max9295_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max9295_enable_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_disable_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_set_local_control_channel_enabled(struct max9x_common *common, bool enabled); -+static int max9295_select_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_deselect_serial_link(struct max9x_common *common, unsigned int link); -+static int max9295_verify_devid(struct max9x_common *common); -+static int max9295_enable(struct max9x_common *common); -+static int max9295_disable(struct max9x_common *common); -+static int max9295_remap_addr(struct max9x_common *common); -+static int max9295_remap_reset(struct max9x_common *common); -+static int max9295_add_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr); -+static int max9295_remove_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr); -+static int max9295_reset(struct max9x_common *common); -+ -+/* max9295 gpio */ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip); -+static int max9295_gpio_get_direction(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_direction_input(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value); -+static int max9295_gpio_get(struct gpio_chip *chip, unsigned int offset); -+static int max9295_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); -+static int max9295_setup_gpio(struct max9x_common *common); -+/* max9295 gpio */ -+ -+static struct max9x_common_ops max9295_common_ops = { -+ .max_elements = max9295_max_elements, -+ .soft_reset = max9295_reset, -+ .enable = max9295_enable, -+ .disable = max9295_disable, -+ .verify_devid = max9295_verify_devid, -+ .remap_addr = max9295_remap_addr, -+ .remap_reset = max9295_remap_reset, -+ .setup_gpio = max9295_setup_gpio, -+}; -+ -+static struct max9x_serial_link_ops max9295_serial_link_ops = { -+ .enable = max9295_enable_serial_link, -+ .disable = max9295_disable_serial_link, -+ .select = max9295_select_serial_link, -+ .deselect = max9295_deselect_serial_link, -+}; -+ -+static struct max9x_translation_ops max9295_translation_ops = { -+ .add = max9295_add_translate_addr, -+ .remove = max9295_remove_translate_addr, -+}; -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip) -+{ -+ return container_of(chip, struct max9x_common, gpio_chip); -+} -+ -+static int max9295_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ TRY(ret, regmap_read(map, MAX9295_GPIO_A(offset), &val)); -+ -+ return (FIELD_GET(MAX9295_GPIO_A_OUT_DIS_FIELD, val) == 0U ? -+ GPIOD_OUT_LOW : GPIOD_IN); -+} -+ -+static int max9295_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), -+ MAX9295_GPIO_A_OUT_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_DIS_FIELD, 1U)); -+} -+ -+static int max9295_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int mask = 0; -+ unsigned int val; -+ -+ mask = MAX9295_GPIO_A_OUT_DIS_FIELD | MAX9295_GPIO_A_OUT_FIELD | -+ MAX9295_GPIO_A_RX_EN_FIELD; -+ -+ // Enable the GPIO as an output -+ val = MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_DIS_FIELD, 0U); -+ // Write out the initial value to the GPIO -+ val |= MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U)); -+ // Disable remote control over SerDes link -+ val |= MAX9X_FIELD_PREP(MAX9295_GPIO_A_RX_EN_FIELD, 0U); -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), mask, val); -+} -+ -+static int max9295_gpio_get(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ TRY(ret, regmap_read(map, MAX9295_GPIO_A(offset), &val)); -+ -+ return FIELD_GET(MAX9295_GPIO_A_IN_FIELD, val); -+} -+ -+static int max9295_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX9295_GPIO_A(offset), -+ MAX9295_GPIO_A_OUT_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U))); -+} -+ -+static int max9295_setup_gpio(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ struct max9x_gpio_pdata *gpio_pdata; -+ -+ if (dev->platform_data) { -+ struct max9x_pdata *pdata = dev->platform_data; -+ gpio_pdata = &pdata->gpio; -+ } -+ -+ // Functions -+ if (gpio_pdata && gpio_pdata->label) -+ common->gpio_chip.label = gpio_pdata->label; -+ else -+ common->gpio_chip.label = dev_name(dev); -+ -+ dev_dbg(dev, "gpio_chip label is %s, dev_name is %s", -+ common->gpio_chip.label, dev_name(dev)); -+ -+ common->gpio_chip.parent = dev; -+ common->gpio_chip.get_direction = max9295_gpio_get_direction; -+ common->gpio_chip.direction_input = max9295_gpio_direction_input; -+ common->gpio_chip.direction_output = max9295_gpio_direction_output; -+ common->gpio_chip.get = max9295_gpio_get; -+ common->gpio_chip.set = max9295_gpio_set; -+ common->gpio_chip.ngpio = MAX9295_NUM_GPIO; -+ common->gpio_chip.can_sleep = 1; -+ common->gpio_chip.base = -1; -+ if (gpio_pdata && gpio_pdata->names) -+ common->gpio_chip.names = gpio_pdata->names; -+ else -+ common->gpio_chip.names = max9295_gpio_chip_names; -+ -+ ret = devm_gpiochip_add_data(dev, &common->gpio_chip, common); -+ if (ret < 0) { -+ dev_err(dev, "gpio_init: Failed to add max9295_gpio\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max9295_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, csi %d: %s, %d lanes", pipe_id, csi_id, -+ (enable ? "enable" : "disable"), common->csi_link[csi_id].config.num_lanes); -+ -+ // Select number of lanes for CSI port csi_id -+ TRY(ret, regmap_update_bits(map, MAX9295_MIPI_RX_1, -+ MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ common->csi_link[csi_id].config.num_lanes - 1)) -+ ); -+ -+ // Select CSI port csi_id for video pipe pipe_id -+ // Enable CSI port csi_id (9295A only has port B, 9295B has both ports) -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_0, -+ MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) | MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id), csi_id) | -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id), enable ? 1U : 0U)) -+ ); -+ -+ // Start video pipe pipe_id from CSI port csi_id -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_9, -+ MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), -+ MAX9X_FIELD_PREP(MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), enable ? 1U : 0U)) -+ ); -+ -+ // Enable video transmit for pipe -+ TRY(ret, regmap_update_bits(map, MAX9295_REG2, -+ MAX9295_REG2_VID_TX_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX9295_REG2_VID_TX_EN_FIELD(pipe_id), enable ? 1U : 0U)) -+ ); -+ -+ return 0; -+} -+ -+static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, -+ unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int data_type_slot, dt; -+ int ret; -+ -+ for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { -+ dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; -+ dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", -+ pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | -+ MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -+ ); -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9295_set_video_pipe_double_loading() - Configure Double Loading Mode on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.1 "Double Mode (Serializer)" -+ */ -+static int max9295_set_video_pipe_double_loading(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_pipe_config *config = &common->video_pipe[pipe_id].config; -+ unsigned int reg; -+ unsigned int fields; -+ int ret; -+ -+ if (pipe_id >= MAX9295_NUM_VIDEO_PIPES) -+ return -EINVAL; -+ -+ // Reset double loading registers to defaults -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_10, -+ MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id), -+ 0x0)); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_FRONTTOP_11, -+ MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id) | -+ MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id), -+ 0x0)); -+ -+ // Enable double pixel mode for pipe -+ switch (bpp) { -+ case 0: -+ //bpp not used on this pipe, but still valid input -+ break; -+ case 8: -+ reg = MAX9295_FRONTTOP_10; -+ fields = MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id); -+ break; -+ case 10: -+ reg = MAX9295_FRONTTOP_11; -+ fields = MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id); -+ break; -+ case 12: -+ reg = MAX9295_FRONTTOP_11; -+ fields = MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable pixel doubling for specified pipe -+ if (bpp != 0) { -+ dev_info(dev, "Configuring double loading mode for pipe %u (%ubpp -> %ubpp)", -+ pipe_id, bpp, (bpp * 2)); -+ TRY(ret, regmap_update_bits(map, reg, fields, 0xFF)); -+ } -+ -+ // We share MAX9295_SOFT_BPP_FIELD/MAX9295_SOFT_BPP_EN_FIELD with -+ // max9295_set_video_pipe_pixel_padding(), only write to it if zero -+ // padding is disabled and double loading mode is enabled. Zero padding -+ // takes precedence and handles the 'both are disabled' case. -+ if (config->soft_min_pixel_bpp == 0 && config->soft_max_pixel_bpp == 0) { -+ // Override output bpp -+ TRY(ret, regmap_update_bits(map, MAX9295_SOFT_BPP(pipe_id), -+ MAX9295_SOFT_BPP_EN_FIELD | MAX9295_SOFT_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_EN_FIELD, bpp == 0 ? 0U : 1U) | -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_FIELD, bpp * 2)) -+ ); -+ } -+ -+ return 0; -+} -+ -+/** -+ * max9295_set_video_pipe_pixel_padding() - Configure zero padding on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @min_bpp: Smallest BPP value of data in the pipe. Must be >= 8. -+ * @max_bpp: Largest BPP value of data in the pipe. Must be <= 16. -+ * -+ * Normally, video pipes can only transmit data with the same BPP value. Zero -+ * padding is a method to allow data with multiple BPP values to be transmitted -+ * over the same video pipe by padding the smaller BPP data to be the same BPP -+ * as the largest BPP data. The deserializer will automatically recover the -+ * data's original BPP based on datatype information transmitted alongside the -+ * padded data. -+ * -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.2 "Zero Padding" -+ */ -+static int max9295_set_video_pipe_pixel_padding(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int min_bpp, unsigned int max_bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_pipe_config *config = &common->video_pipe[pipe_id].config; -+ int ret; -+ bool enable = (min_bpp != 0) && (max_bpp != 0); -+ -+ if (enable) -+ dev_dbg(dev, "Configuring zero padding for pipe %u (%u <= bpp <= %u)", pipe_id, min_bpp, max_bpp); -+ else -+ dev_dbg(dev, "%s: pipe %u, min_bpp: %u, max_bpp: %u not enabled", __func__, pipe_id, min_bpp, max_bpp); -+ -+ if (pipe_id >= MAX9295_NUM_VIDEO_PIPES) -+ return -EINVAL; -+ -+ /* Auto bpp should be disabled to override bpp */ -+ TRY(ret, regmap_update_bits(map, MAX9295_VIDEO_TX0(pipe_id), -+ MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD, enable ? 0U : 1U)) -+ ); -+ -+ if (enable) -+ TRY(ret, regmap_update_bits(map, MAX9295_VIDEO_TX1(pipe_id), -+ MAX9295_VIDEO_TX1_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_VIDEO_TX1_BPP_FIELD, max_bpp)) -+ ); -+ -+ // We share MAX9295_SOFT_BPP_FIELD/MAX9295_SOFT_BPP_EN_FIELD with -+ // max9295_set_video_pipe_double_loading(), only write to it if zero -+ // padding is enabled (this function takes precedence) or if both zero -+ // pading is disabled and double loading is disabled. -+ if (enable || (!enable && config->dbl_pixel_bpp == 0)) { -+ TRY(ret, regmap_update_bits(map, MAX9295_SOFT_BPP(pipe_id), -+ MAX9295_SOFT_BPP_EN_FIELD | MAX9295_SOFT_BPP_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_EN_FIELD, enable ? 1U : 0U) | -+ MAX9X_FIELD_PREP(MAX9295_SOFT_BPP_FIELD, min_bpp)) -+ ); -+ } -+ -+ return 0; -+} -+ -+static int max9295_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX9295_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX9295_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX9295_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX9295_NUM_CSI_LINKS; -+ case MAX9X_DATA_TYPES: -+ return MAX9295_NUM_DATA_TYPES; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static int max9295_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_dbg(dev, "Link %d: Enable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ TRY(ret, max9295_set_pipe_data_types_enabled(common, pipe_id, true)); -+ -+ TRY(ret, max9295_set_video_pipe_double_loading(common, pipe_id, config->dbl_pixel_bpp)); -+ -+ TRY(ret, max9295_set_video_pipe_pixel_padding(common, pipe_id, -+ config->soft_min_pixel_bpp, config->soft_max_pixel_bpp)); -+ -+ TRY(ret, max9295_set_pipe_csi_enabled(common, pipe_id, config->src_csi, true)); -+ } -+ -+ return 0; -+} -+ -+static int max9295_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_dbg(dev, "Link %d: Disable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ TRY(ret, max9295_set_pipe_data_types_enabled(common, pipe_id, false)); -+ -+ TRY(ret, max9295_set_pipe_csi_enabled(common, pipe_id, config->src_csi, false)); -+ } -+ -+ return 0; -+} -+ -+/* -+ * Enable RCLK (27MHz) output on MFP4 pin. This pin is routed on some imager boards -+ * to the imager instead of an on-board oscillator. -+ */ -+static int max9295_enable_rclk(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ if (!common->external_refclk_enable) { -+ dev_dbg(dev, "Enable RCLK: external-refclk-enable not present, not enabling external refclk"); -+ return 0; -+ } -+ -+ dev_info(dev, "Enable RCLK: 27MHz"); -+ -+ // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ MAX9295_REFGEN_PREDEF_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) -+ ); -+ -+ // Enable reference generation -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, -+ MAX9295_REFGEN_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) -+ ); -+ -+ // Configure reference generation output on MFP4 -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ MAX9295_PCLK_GPIO_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) -+ ); -+ -+ // Enable output -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ MAX9295_PCLK_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_REG3, -+ MAX9295_RCLK_SEL_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9295_REG6, -+ MAX9295_RCLK_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) -+ ); -+ -+ return 0; -+} -+ -+static int max9295_set_local_control_channel_enabled(struct max9x_common *common, bool enabled) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); -+} -+ -+static int max9295_select_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max9295_set_local_control_channel_enabled(common, true); -+} -+ -+static int max9295_deselect_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max9295_set_local_control_channel_enabled(common, false); -+} -+ -+static int max9295_verify_devid(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int dev_id, dev_rev; -+ int ret; -+ -+ // Fetch and output chip name + revision -+ TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ -+ switch (dev_id) { -+ case MAX9295A: -+ dev_info(dev, "Detected MAX9295A revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ case MAX9295B: -+ dev_info(dev, "Detected MAX9295B revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ case MAX9295E: -+ dev_info(dev, "Detected MAX9295E revision %ld", -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ break; -+ default: -+ dev_err(dev, "Unknown device ID %d revision %ld", dev_id, -+ FIELD_GET(MAX9295_DEV_REV_FIELD, dev_rev)); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int max9295_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ TRY(ret, max9295_verify_devid(common)); -+ -+ // Turn on RCLK/PCLK -+ ret = max9295_enable_rclk(common); -+ if (ret) -+ dev_warn(dev, "Failed to enable RCLK output"); -+ -+ // Initialize local CC to off -+ TRY(ret, max9295_set_local_control_channel_enabled(common, false)); -+ -+ /* Clear the pipe maps */ -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ -+ /* Clear the csi port selections */ -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ -+ return 0; -+} -+ -+static int max9295_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "Disable"); -+ -+ return 0; -+} -+ -+static int max9295_remap_addr(struct max9x_common *common) -+{ -+ int ret; -+ struct device *dev = common->dev; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ -+ if (!common->phys_client) -+ return 0; -+ -+ if (!common->phys_map) -+ return 0; -+ -+ dev_info(dev, "Remap address from 0x%02x to 0x%02x", -+ common->phys_client->addr, common->client->addr); -+ -+ TRY(ret, regmap_update_bits(common->phys_map, MAX9295_REG0, -+ MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, common->client->addr)) -+ ); -+ -+ /* -+ * Use lower bits of I2C address as unique TX_SRC_ID to prevent -+ * conflicts for info frames, SPI, etc. (Leave video pipes alone) -+ */ -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGI_INFOFR_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_SPI_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ /* -+ * This exponential retry works around for MAX96724X. -+ */ -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ ret = regmap_update_bits(common->map, MAX9295_CFGC_CC_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)); -+ if (ret) { -+ dev_warn(dev, -+ "configure MAX9295_CFGC_CC_TR3 failed, trying again (waiting %d ms)", ms); -+ msleep(ms); -+ } else -+ break; -+ } -+ -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_GPIO_TR3, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_X, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, -+ MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); -+ -+ return 0; -+} -+ -+static int max9295_remap_reset(struct max9x_common *common) -+{ -+ int ret; -+ struct device *dev = common->dev; -+ struct max9x_pdata *pdata = dev->platform_data; -+ u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -+ common->client->addr; -+ u32 virt_addr = common->client->addr; -+ -+ dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, -+ phys_addr); -+ -+ TRY(ret, regmap_update_bits( -+ common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ -+ return 0; -+} -+ -+static int max9295_add_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int alias; -+ unsigned int src; -+ int ret; -+ -+ for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ -+ src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); -+ if (src == virt_addr || src == 0) { -+ dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", -+ MAX9295_I2C_SRC(i2c_id, alias), virt_addr, -+ MAX9295_I2C_DST(i2c_id, alias), phys_addr); -+ TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) -+ ); -+ } -+ } -+ -+ return 0; -+} -+ -+static int max9295_remove_translate_addr(struct max9x_common *common, -+ unsigned int i2c_id, unsigned int virt_addr, unsigned int phys_addr) -+{ -+ struct regmap *map = common->map; -+ unsigned int alias; -+ unsigned int src; -+ int ret; -+ -+ for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); -+ if (src == virt_addr) { -+ return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); -+ } -+ } -+ -+ return 0; -+} -+ -+static int max9295_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Reset"); -+ -+ /* Reset entire chip by CTRL0_RST_ALL: 0x10[7]*/ -+ TRY(ret, regmap_write(map, MAX9295_CTRL0, MAX9295_CTRL0_RST_ALL)); -+ usleep_range(45000, 45050); -+ -+ return 0; -+} -+ -+int max9295_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ (*common_ops) = &max9295_common_ops; -+ (*serial_ops) = &max9295_serial_link_ops; -+ (*csi_ops) = NULL; -+ (*lf_ops) = NULL; -+ (*trans_ops) = &max9295_translation_ops; -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Yan, Dongcheng "); -+MODULE_DESCRIPTION("Maxim MAX9295 CSI-2/parallel to GMSL2 Serializer driver"); -diff --git a/drivers/media/i2c/max9x/max9295.h b/drivers/media/i2c/max9x/max9295.h -new file mode 100644 -index 000000000000..de4505639dec ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9295.h -@@ -0,0 +1,143 @@ -+/* -+ * max9295.h - Maxim MAX9295 registers and constants. -+ * -+ * Copyright (c) 2020, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9295_H_ -+#define _MAX9295_H_ -+ -+#include -+#include "serdes.h" -+ -+enum max9295_dev_id { -+ MAX9295A = 0x91, -+ MAX9295B = 0x93, -+ MAX9295E = 0x9B -+}; -+ -+enum max9295_gpio_out_type { -+ MAX9295_GPIO_OUT_TYPE_OPEN_DRAIN = 0, -+ MAX9295_GPIO_OUT_TYPE_PUSH_PULL = 1, -+}; -+ -+enum max9295_gpio_pull_updn_sel { -+ MAX9295_GPIO_PULL_UPDN_SEL_NONE = 0, -+ MAX9295_GPIO_PULL_UPDN_SEL_UP = 1, -+ MAX9295_GPIO_PULL_UPDN_SEL_DOWN = 2, -+}; -+ -+#define MAX9295_NUM_ALIASES 2 /* 2 per i2c bus */ -+#define MAX9295_NUM_SERIAL_LINKS 1 -+#define MAX9295_NUM_VIDEO_PIPES 4 -+#define MAX9295_NUM_MIPI_MAPS 16 -+#define MAX9295_NUM_CSI_LINKS 2 // Only 1 port, but it is technically Port B -+#define MAX9295_NUM_GPIO 11 -+#define MAX9295_NUM_DATA_TYPES 4 -+ -+#define MAX9295_REG0 (0x0) -+#define MAX9295_REG0_DEV_ADDR_FIELD GENMASK(7, 1) -+ -+#define MAX9295_PHY_REM_CTRL (0x1) -+#define MAX9295_PHY_REM_CTRL_DIS_FIELD BIT(4) -+#define MAX9295_PHY_LOCAL_CTRL_DIS_FIELD BIT(5) -+ -+#define MAX9295_REG2 (0x2) -+#define MAX9295_REG2_VID_TX_EN_FIELD(pipe_id) BIT((pipe_id) + 4) -+ -+#define MAX9295_DEV_ID (0xD) -+#define MAX9295_DEV_REV (0xE) -+#define MAX9295_DEV_REV_FIELD GENMASK(3, 0) -+#define MAX9295_CTRL0 (0x10) -+#define MAX9295_CTRL0_RST_ALL BIT(7) -+ -+#define MAX9295_CFGI_INFOFR_TR3 (0x7B) -+#define MAX9295_CFGL_SPI_TR3 (0x83) -+#define MAX9295_CFGC_CC_TR3 (0x8B) -+#define MAX9295_CFGL_GPIO_TR3 (0x93) -+#define MAX9295_CFGL_IIC_X (0xA3) -+#define MAX9295_CFGL_IIC_Y (0xAB) -+#define MAX9295_TR3_TX_SRC_ID GENMASK(2, 0) -+ -+#define MAX9295_VIDEO_TX0(pipe_id) (0x100 + (pipe_id) * 8) -+#define MAX9295_VIDEO_TX0_AUTO_BPP_EN_FIELD BIT(3) -+#define MAX9295_VIDEO_TX1(pipe_id) (0x101 + (pipe_id) * 8) -+#define MAX9295_VIDEO_TX1_BPP_FIELD GENMASK(5, 0) -+ -+#define MAX9295_GPIO(gpio) (0x2BE + ((gpio) * 3)) -+#define MAX9295_GPIO_A(gpio) (MAX9295_GPIO(gpio) + 0) -+#define MAX9295_GPIO_A_OUT_DIS_FIELD BIT(0) -+#define MAX9295_GPIO_A_TX_EN_FIELD BIT(1) -+#define MAX9295_GPIO_A_RX_EN_FIELD BIT(2) -+#define MAX9295_GPIO_A_IN_FIELD BIT(3) -+#define MAX9295_GPIO_A_OUT_FIELD BIT(4) -+#define MAX9295_GPIO_A_RES_CFG_FIELD BIT(7) -+#define MAX9295_GPIO_B(gpio) (MAX9295_GPIO(gpio) + 1) -+#define MAX9295_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX9295_GPIO_B_OUT_TYPE_FIELD BIT(5) -+#define MAX9295_GPIO_B_PULL_UPDN_SEL_FIELD GENMASK(7, 6) -+#define MAX9295_GPIO_C(gpio) (MAX9295_GPIO(gpio) + 2) -+#define MAX9295_GPIO_C_RX_ID GENMASK(4, 0) -+ -+#define MAX9295_FRONTTOP_0 (0x308) -+#define MAX9295_FRONTTOP_0_LINE_INFO BIT(6) -+#define MAX9295_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_0_START_CSI_FIELD(csi_id) BIT((csi_id) + 4) -+#define MAX9295_FRONTTOP_9 (0x311) -+#define MAX9295_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id) BIT((pipe_id) + 4 * (csi_id)) -+ -+// Double loading mode -+#define MAX9295_FRONTTOP_10 (0x312) -+#define MAX9295_FRONTTOP_10_DBL8_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_11 (0x313) -+#define MAX9295_FRONTTOP_11_DBL10_FIELD(pipe_id) BIT(pipe_id) -+#define MAX9295_FRONTTOP_11_DBL12_FIELD(pipe_id) BIT((pipe_id) + 4) -+ -+#define MAX9295_MEM_DT_SEL(pipe_id, dt_slot) (0x314 + (dt_slot) / 2 * 0xC2 + 2 * (pipe_id) + (dt_slot)) -+#define MAX9295_MEM_DT_SEL_DT_FIELD GENMASK(5, 0) -+#define MAX9295_MEM_DT_SEL_EN_FIELD BIT(6) -+ -+// Software BPP override (used for double loading mode and zero padding) -+#define MAX9295_SOFT_BPP(pipe_id) (0x31C + (pipe_id)) -+#define MAX9295_SOFT_BPP_EN_FIELD BIT(5) -+#define MAX9295_SOFT_BPP_FIELD GENMASK(4, 0) -+ -+#define MAX9295_MIPI_RX (0x330) -+#define MAX9295_MIPI_RX_1 (MAX9295_MIPI_RX + 1) -+#define MAX9295_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id) (GENMASK(1, 0) << (csi_id * 4)) -+ -+// I2C SRC/DST -+#define MAX9295_I2C_SRC(i2c_id, n) ((i2c_id == 0 ? 0x42 : (0x550 + (4 * ((i2c_id) - 1)))) + (2 * (n)) + 0) -+#define MAX9295_I2C_SRC_FIELD GENMASK(7, 1) -+#define MAX9295_I2C_DST(i2c_id, n) ((i2c_id == 0 ? 0x42 : (0x550 + (4 * ((i2c_id) - 1)))) + (2 * (n)) + 1) -+#define MAX9295_I2C_DST_FIELD GENMASK(7, 1) -+ -+// RCLK registers -+#define MAX9295_REG6 (0x6) -+#define MAX9295_RCLK_EN_FIELD BIT(5) -+#define MAX9295_REG3 (0x3) -+#define MAX9295_RCLK_SEL_FIELD GENMASK(1, 0) -+#define MAX9295_REF_VTG0 (0x3F0) -+#define MAX9295_REFGEN_PREDEF_FREQ_FIELD GENMASK(5, 4) -+#define MAX9295_REFGEN_PREDEF_ALT_FIELD BIT(3) -+#define MAX9295_REFGEN_EN_FIELD BIT(0) -+#define MAX9295_REF_VTG1 (0x3F1) -+#define MAX9295_PCLK_GPIO_FIELD GENMASK(5, 1) -+#define MAX9295_PCLK_EN_FIELD BIT(0) -+ -+#endif /* _MAX9295_H_ */ -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -new file mode 100644 -index 000000000000..41074d60cc01 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -0,0 +1,881 @@ -+/* -+ * max9296.c - Maxim MAX9296 GMSL2/GMSL1 to CSI-2 Deserializer -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max9296.h" -+ -+// Params -+int max9296_serial_link_timeout_ms = MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -+module_param(max9296_serial_link_timeout_ms, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max9296_serial_link_timeout_ms, "Timeout for serial link in milliseconds"); -+ -+// Declarations -+static int max9296_set_phy_mode(struct max9x_common *common, unsigned int phy_mode); -+static int max9296_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map); -+static int max9296_set_phy_dpll_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz); -+static int max9296_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes); -+static int max9296_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, -+ bool enable, unsigned int width); -+static int max9296_configure_csi_dphy(struct max9x_common *common); -+static int max9296_enable(struct max9x_common *common); -+static int max9296_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max9296_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked); -+static int max9296_serial_link_reset(struct max9x_common *common, unsigned int link_id); -+static int max9296_set_serial_link_rate(struct max9x_common *common, unsigned int link_id); -+static int max9296_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe); -+static int max9296_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps); -+static int max9296_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int map_id, struct max9x_serdes_mipi_map *mipi_map); -+static int max9296_set_csi_double_loading_mode(struct max9x_common *common, unsigned int csi_id, unsigned int bpp); -+static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned int link_id); -+static int max9296_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9296_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9296_isolate_serial_link(struct max9x_common *common, unsigned int link); -+static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned int link); -+static int max9296_wait_link_lock(struct max9x_common *common, int link); -+static int max9296_enable_csi_link(struct max9x_common *common, unsigned int csi_link_id); -+static int max9296_disable_csi_link(struct max9x_common *common, unsigned int csi_link_id); -+ -+/* Currently unused */ -+static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_id, unsigned int *phy_lane_map); -+ -+// Functions -+static int max9296_set_phy_mode(struct max9x_common *common, unsigned int phy_mode) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI: phy_mode=%d", phy_mode); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY0, -+ MAX9296_MIPI_PHY0_MODE_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_MIPI_PHY0_MODE_FIELD, phy_mode)); -+} -+ -+static int max9296_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: %s", csi_id, (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY_ENABLE, -+ MAX9296_MIPI_PHY_ENABLE_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX9296_MIPI_PHY_ENABLE_FIELD(csi_id), enable ? 1U : 0U)); -+} -+ -+static int max9296_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: phy_lane_map=0x%04x", csi_id, phy_lane_map); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_PHY_LANE_MAP(csi_id), -+ MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi_id, 0) -+ | MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi_id, 1), -+ phy_lane_map); -+} -+ -+static int max9296_set_phy_dpll_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: DPLL %s", csi_id, (enable ? "on" : "off")); -+ -+ return regmap_update_bits(map, MAX9296_DPLL_RESET(csi_id), -+ MAX9296_DPLL_RESET_SOFT_RST_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_DPLL_RESET_SOFT_RST_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max9296_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ if (WARN_ONCE(freq_mhz > 0 && freq_mhz < MAX9296_DPLL_FREQ_MHZ_MULTIPLE, -+ "CSI frequency must be greater than %d MHz", MAX9296_DPLL_FREQ_MHZ_MULTIPLE)) -+ return -EINVAL; -+ -+ dev_dbg(dev, "CSI link %d: freq %u MHz, %u mult", csi_id, freq_mhz, freq_mhz / MAX9296_DPLL_FREQ_MHZ_MULTIPLE); -+ -+ return regmap_update_bits(map, MAX9296_DPLL_FREQ(csi_id), -+ MAX9296_DPLL_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_DPLL_FREQ_FIELD, freq_mhz / MAX9296_DPLL_FREQ_MHZ_MULTIPLE)); -+} -+ -+static int max9296_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: %d lanes", csi_id, num_lanes); -+ -+ return regmap_update_bits(map, MAX9296_MIPI_TX_LANE_CNT(csi_id), -+ MAX9296_MIPI_TX_LANE_CNT_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_LANE_CNT_FIELD, -+ (common->csi_link[csi_id].config.num_lanes - 1))); -+} -+ -+static int max9296_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, -+ bool enable, unsigned int width) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: Initial deskew %s", csi_id, enable ? "enabled" : "disabled"); -+ -+ /* clamp initial deskew width to 7 which is 8*32k UI*/ -+ if (width > 7) -+ width = 7; -+ -+ return regmap_write(map, MAX9296_MIPI_TX_DESKEW_INIT(csi_id), -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_DESKEW_INIT_AUTO_EN, enable) | -+ MAX9X_FIELD_PREP(MAX9296_MIPI_TX_DESKEW_INIT_WIDTH, width)); -+} -+ -+static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_id, -+ unsigned int *phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ int i; -+ const unsigned int phy_count = 2; -+ const unsigned int controller = csi_id / phy_count; -+ -+ if (common->csi_link[csi_id].config.num_lanes != common->csi_link[csi_id].config.num_maps) { -+ dev_err(dev, "CSI%u number of maps %u must match number of lanes %u.", csi_id, -+ common->csi_link[csi_id].config.num_maps, -+ common->csi_link[csi_id].config.num_lanes); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < common->csi_link[csi_id].config.num_maps; i++) { -+ const struct max9x_serdes_phy_map *map = &common->csi_link[csi_id].config.map[i]; -+ -+ if (map->int_csi >= 4) { -+ dev_err(dev, "CSI%u does not have %u Lanes can not map.", csi_id, -+ map->int_csi + 1); -+ return -EINVAL; -+ } -+ if (map->phy_lane >= 2) { -+ dev_err(dev, "Each PHY has 2 lanes can not map %u.", map->phy_ind); -+ return -EINVAL; -+ } -+ if (map->phy_ind < (controller * phy_count) || -+ map->phy_ind >= ((controller + 1) * phy_count)) { -+ dev_err(dev, "CSI%u does not have PHYs %u can not map.", csi_id, -+ map->phy_ind); -+ return -EINVAL; -+ } -+ phy_lane_map[map->phy_ind] |= MAX9X_FIELD_PREP( -+ MAX9296_MIPI_PHY_LANE_MAP_FIELD(map->phy_ind, map->phy_lane), map->int_csi); -+ } -+ return 0; -+} -+ -+/* -+ * MAX9296A has four PHYs, but does not support single-PHY configurations, -+ * only double-PHY configurations, even when only using two lanes. -+ * For PHY 0 + PHY 1, PHY 1 is the master PHY. -+ * For PHY 2 + PHY 3, PHY 2 is the master PHY. -+ * Clock is always on the master PHY. -+ * For first pair of PHYs, first lanes are on the master PHY. -+ * For second pair of PHYs, first lanes are on the master PHY too. -+ * -+ * PHY 0 + 1 -+ * CLK = PHY 1 -+ * PHY1 Lane 0 = D0 -+ * PHY1 Lane 1 = D1 -+ * PHY0 Lane 0 = D2 -+ * PHY0 Lane 1 = D3 -+ * -+ * PHY 2 + 3 -+ * CLK = PHY 2 -+ * PHY2 Lane 0 = D0 -+ * PHY2 Lane 1 = D1 -+ * PHY3 Lane 0 = D2 -+ * PHY3 Lane 1 = D3 -+ */ -+static int max9296_configure_csi_dphy(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phy_mode; -+ unsigned int phy_lane_map[MAX9296_NUM_CSI_LINKS] = {0}; -+ unsigned int csi_id; -+ unsigned int PHY1, PHY2; -+ int ret; -+ -+ for (csi_id = 0; csi_id < MAX9296_NUM_CSI_LINKS; csi_id++) { -+ dev_dbg(dev, "CSI link %d: enabled=%d, num_lanes=%d, freq_mhz=%d init_deskew=%d", -+ csi_id, common->csi_link[csi_id].enabled, -+ common->csi_link[csi_id].config.num_lanes, -+ common->csi_link[csi_id].config.freq_mhz, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ } -+ -+ PHY1 = common->csi_link[0].enabled ? common->csi_link[0].config.num_lanes : 0; -+ PHY2 = common->csi_link[1].enabled ? common->csi_link[1].config.num_lanes : 0; -+ /* Each csi controller has 4 lanes. each phy has 2 lanes*/ -+ if ((PHY1 + PHY2) > 4) { -+ dev_err(dev, "CSI controller 1 has more than %u lanes. PHY0: %u, PHY1: %u", 4, PHY1, -+ PHY1); -+ return -EINVAL; -+ } -+ -+ PHY1 = common->csi_link[2].enabled ? common->csi_link[2].config.num_lanes : 0; -+ PHY2 = common->csi_link[3].enabled ? common->csi_link[3].config.num_lanes : 0; -+ /* Each csi controller has 4 lanes. each phy has 2 lanes */ -+ if ((PHY1 + PHY2) > 4) { -+ dev_err(dev, "CSI controller 2 has more than %u lanes. PHY2: %u, PHY3: %u", 4, PHY1, -+ PHY1); -+ return -EINVAL; -+ } -+ -+ if (common->csi_link[1].enabled && common->csi_link[1].config.num_lanes == 4) { -+ if (common->csi_link[0].enabled) -+ dev_warn(dev, "CSI link 0 enabled, but CSI link 1 is 4 lanes."); -+ -+ /* lane 1 is master for CSI controller 1*/ -+ max9296_conf_phy_maps(common, 1, phy_lane_map); -+ if (common->csi_link[2].enabled && common->csi_link[2].config.num_lanes == 4) { -+ if (common->csi_link[3].enabled) -+ dev_warn(dev, "CSI link 3 enabled, but CSI link 2 is 4 lanes."); -+ dev_dbg(dev, "CSI phy mode is set to 2X4Lanes"); -+ /* lane 2 is master for CSI controller 2*/ -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_2X4; // 2x 4lane -+ } else { -+ dev_dbg(dev, "CSI phy mode is set to A: 4Lanes B: 2X2Lanes"); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ max9296_conf_phy_maps(common, 3, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_1X4A_22; // A: 1x 4lane B: 2x 2lane -+ } -+ } else { -+ max9296_conf_phy_maps(common, 1, phy_lane_map); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ if (common->csi_link[2].enabled && common->csi_link[2].config.num_lanes == 4) { -+ if (common->csi_link[3].enabled) -+ dev_warn(dev, "CSI link 3 enabled, but CSI link 2 is 4 lanes."); -+ dev_dbg(dev, "CSI phy mode is set to A: 2X2Lanes B: 4Lanes"); -+ /* lane 2 is master for CSI controller 2*/ -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_1X4B_22; // B: 1x 4lane A: 2x 2lane -+ } else { -+ dev_dbg(dev, "CSI phy mode is set to 4X2Lanes"); -+ max9296_conf_phy_maps(common, 2, phy_lane_map); -+ max9296_conf_phy_maps(common, 3, phy_lane_map); -+ phy_mode = MAX9296_MIPI_PHY_4X2; // 4x 2lane -+ } -+ } -+ -+ TRY(ret, max9296_set_phy_mode(common, phy_mode)); -+ -+ for (csi_id = 0; csi_id < MAX9296_NUM_CSI_LINKS; csi_id++) { -+ struct max9x_serdes_csi_config *config = -+ &common->csi_link[csi_id].config; -+ -+ TRY(ret, max9296_set_phy_enabled(common, csi_id, false)); -+ -+ TRY(ret, max9296_set_phy_dpll_enabled(common, csi_id, false)); -+ -+ TRY(ret, max9296_set_phy_lane_map(common, csi_id, -+ phy_lane_map[csi_id]) -+ ); -+ -+ TRY(ret, max9296_set_mipi_lane_cnt(common, csi_id, -+ config->num_lanes) -+ ); -+ } -+ -+ return 0; -+} -+ -+static int max9296_set_all_reset(struct max9x_common *common, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Reset ALL %s", (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_CTRL0, -+ MAX9296_CTRL0_RESET_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_CTRL0_RESET_ALL_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max9296_soft_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "Soft reset"); -+ -+ TRY(ret, max9296_set_all_reset(common, 1)); -+ /* lock time for reset_all / PWDNB in spec, I2C Wake time is 2.25ms */ -+ usleep_range(45000, 45050); -+ TRY(ret, max9296_set_all_reset(common, 0)); -+ -+ return 0; -+} -+ -+static int max9296_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX9296_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX9296_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX9296_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX9296_NUM_CSI_LINKS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static struct max9x_common_ops max9296_common_ops = { -+ .enable = max9296_enable, -+ .soft_reset = max9296_soft_reset, -+ .max_elements = max9296_max_elements, -+}; -+ -+static int max9296_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ /* -+ * link_id ignored for max9296, pipe routing done through stream-ids, -+ * which must be unique across links -+ */ -+ dev_dbg(dev, "Video-pipe %d: src_pipe=%u", pipe_id, src_pipe); -+ -+ return regmap_update_bits(map, MAX9296_VIDEO_PIPE_SEL(pipe_id), -+ MAX9296_VIDEO_PIPE_STR_SEL_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_VIDEO_PIPE_STR_SEL_FIELD, src_pipe)); -+} -+ -+static int max9296_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val = 0; -+ int ret; -+ -+ if (num_maps > 0) -+ val = GENMASK(num_maps - 1, 0); -+ -+ dev_dbg(dev, "Video-pipe %d: num_maps=%u", pipe_id, num_maps); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_EN_L(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_EN_FIELD, val)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_EN_H(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_EN_FIELD, val >> 8)) -+ ); -+ -+ return 0; -+} -+ -+static int max9296_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int map_id, struct max9x_serdes_mipi_map *mipi_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, map %d: VC%d:DT%02x->VC%d:DT%02x, dst_csi=%d ", -+ pipe_id, map_id, mipi_map->src_vc, mipi_map->src_dt, -+ mipi_map->dst_vc, mipi_map->dst_dt, mipi_map->dst_csi); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_SRC_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRC_L_VC_FIELD, mipi_map->src_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRC_L_DT_FIELD, mipi_map->src_dt)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_DST_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_DST_L_VC_FIELD, mipi_map->dst_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_DST_L_DT_FIELD, mipi_map->dst_dt)) -+ ); -+ -+ TRY(ret, regmap_write(map, MAX9296_MAP_SRCDST_H(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRCDST_H_SRC_VC_FIELD, mipi_map->src_vc) | -+ MAX9X_FIELD_PREP(MAX9296_MAP_SRCDST_H_DST_VC_FIELD, mipi_map->dst_vc)) -+ ); -+ -+ TRY(ret, regmap_update_bits(map, MAX9296_MAP_DPHY_DEST(pipe_id, map_id), -+ MAX9296_MAP_DPHY_DEST_FIELD(map_id), -+ MAX9X_FIELD_PREP(MAX9296_MAP_DPHY_DEST_FIELD(map_id), mipi_map->dst_csi)) -+ ); -+ -+ return 0; -+} -+ -+/** -+ * max9296_set_csi_double_loading_mode() - Configure Double Loading Mode on a CSI controller -+ * @common: max9x_common -+ * @csi_id: Target CSI controller's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.4.2.3 "Double Mode (Deserializer)" -+ */ -+static int max9296_set_csi_double_loading_mode(struct max9x_common *common, unsigned int csi_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int value; -+ -+ switch (bpp) { -+ case 0: -+ value = 0; -+ break; -+ case 8: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_8BPP, 1U); -+ // To fully support 8bpp, additional register writes are -+ // needed for 'bpp8dbl' and 'bpp8dbl_mode' fields on each pipe. -+ dev_err(dev, "8 BPP currently unsupported for pixel doubling"); -+ return -EINVAL; -+ case 10: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_10BPP, 1U); -+ break; -+ case 12: -+ value = FIELD_PREP(MAX9296_MIPI_TX_ALT_MEM_12BPP, 1U); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ if (bpp > 0) -+ dev_info(dev, "Configuring double loading mode on CSI %d: %u bpp -> %u bpp", -+ csi_id, bpp, (bpp * 2)); -+ -+ // Enable alt mem mapping -+ return regmap_update_bits(map, MAX9296_MIPI_TX_ALT_MEM(csi_id), -+ MAX9296_MIPI_TX_ALT_MEM_FIELD, value); -+} -+ -+static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct max9x_serdes_csi_link *csi_link; -+ int ret; -+ -+ if (csi_id > common->num_csi_links) -+ return -EINVAL; -+ -+ csi_link = &common->csi_link[csi_id]; -+ -+ if (WARN_ONCE(enable && csi_link->enabled == false, "Tried to enable a disabled CSI port???")) -+ return -EINVAL; -+ -+ if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, "Tried to enable CSI port with no lanes???")) -+ return -EINVAL; -+ -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+ dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, (enable ? "enable" : "disable"), csi_link->usecount); -+ -+ if (enable && csi_link->usecount == 1) { -+ // Enable && first user -+ ret = max9296_set_initial_deskew(common, csi_id, csi_link->config.auto_init_deskew_enabled, -+ csi_link->config.initial_deskew_width); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_freq(common, csi_id, csi_link->config.freq_mhz); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ } else if (!enable && csi_link->usecount == 0) { -+ // Disable && no more users -+ ret = max9296_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_phy_dpll_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ } -+ -+ return 0; -+} -+ -+static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Video-pipe %d: %s", pipe_id, (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX9296_VIDEO_PIPE_EN(pipe_id), -+ MAX9296_VIDEO_PIPE_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX9296_VIDEO_PIPE_EN_FIELD(pipe_id), enable ? 1U : 0U)); -+} -+ -+/***** max9296_serial_link_ops auxiliary functions *****/ -+static int max9296_set_serial_link_rate(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -+ int tx_rate, rx_rate; -+ -+ tx_rate = max9x_serdes_mhz_to_rate(max9296_tx_rates, ARRAY_SIZE(max9296_tx_rates), config->tx_freq_mhz); -+ if (tx_rate < 0) -+ return tx_rate; -+ -+ rx_rate = max9x_serdes_mhz_to_rate(max9296_rx_rates, ARRAY_SIZE(max9296_rx_rates), config->rx_freq_mhz); -+ if (rx_rate < 0) -+ return rx_rate; -+ -+ dev_dbg(dev, "Serial-link %d: TX=%d MHz RX=%d MHz", link_id, config->tx_freq_mhz, config->rx_freq_mhz); -+ -+ return regmap_update_bits(map, MAX9296_PHY_REM_CTRL, -+ MAX9296_PHY_REM_CTRL_TX_FIELD | MAX9296_PHY_REM_CTRL_RX_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_PHY_REM_CTRL_TX_FIELD, tx_rate) | -+ MAX9X_FIELD_PREP(MAX9296_PHY_REM_CTRL_RX_FIELD, rx_rate)); -+} -+ -+static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max9296_set_video_pipe_src(common, pipe_id, config->src_link, config->src_pipe); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_video_pipe_maps_enabled(common, pipe_id, config->num_maps); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max9296_set_video_pipe_map(common, pipe_id, map_id, &config->map[map_id]); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_csi_double_loading_mode(common, -+ config->map[map_id].dst_csi, config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ -+ if (common->csi_link[config->map[map_id].dst_csi].config.auto_start) { -+ ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, true); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ ret = max9296_set_video_pipe_enabled(common, pipe_id, true); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max9296_serial_link_reset(struct max9x_common *common, unsigned int link_id) -+{ -+ struct regmap *map = common->map; -+ -+ /* GMSL RX rate must be the same as the SER. This is set in REG1(0x1)[1:0] */ -+ return regmap_update_bits(map, MAX9296_CTRL0, MAX9296_CTRL0_RESET_ONESHOT_FIELD, -+ MAX9X_FIELD_PREP(MAX9296_CTRL0_RESET_ONESHOT_FIELD, 1U)); -+} -+ -+static int max9296_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked) -+{ -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ /* Only looks at link A refer to header file */ -+ ret = regmap_read(map, MAX9296_PHY_LOCKED(link_id), &val); -+ if (ret) -+ return ret; -+ -+ if (FIELD_GET(MAX9296_PHY_LOCKED_FIELD, val) != 0) -+ *locked = true; -+ else -+ *locked = false; -+ -+ return 0; -+} -+ -+static int max9296_wait_link_lock(struct max9x_common *common, int link) -+{ -+ bool locked; -+ int ret; -+ ulong timeout = jiffies + msecs_to_jiffies(max9296_serial_link_timeout_ms); -+ -+ do { -+ ret = max9296_get_serial_link_lock(common, link, &locked); -+ if (ret == 0 && locked) -+ return 0; -+ -+ usleep_range(1000, 2000); -+ } while (time_is_after_jiffies(timeout)); -+ -+ return -ETIMEDOUT; -+} -+/***** max9296_serial_link_ops auxiliary functions *****/ -+ -+/***** max9296_serial_link_ops *****/ -+static int max9296_isolate_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int link_cfg; -+ unsigned int auto_link; -+ int ret; -+ -+ dev_dbg(dev, "Isolate link %d", link); -+ -+ auto_link = 0; -+ link_cfg = (link == 0) ? MAX9296_LINK_A : MAX9296_LINK_B; -+ -+ TRY_DEV_HERE(ret, regmap_update_bits(map, MAX9296_CTRL0, -+ MAX9296_CTRL0_AUTO_CFG_FIELD | MAX9296_CTRL0_LINK_CFG_FIELD, -+ FIELD_PREP(MAX9296_CTRL0_AUTO_CFG_FIELD, auto_link) -+ | FIELD_PREP(MAX9296_CTRL0_LINK_CFG_FIELD, link_cfg)), -+ dev); -+ -+ TRY_DEV_HERE(ret, max9296_serial_link_reset(common, link), dev); -+ -+ TRY_DEV_HERE(ret, max9296_wait_link_lock(common, link), dev); -+ -+ return 0; -+} -+ -+static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int link_cfg; -+ unsigned int auto_link = 0; -+ int ret; -+ bool link_a = common->serial_link[0].detected; -+ bool link_b = common->serial_link[1].detected; -+ -+ if (link_a && link_b) -+ link_cfg = MAX9296_LINK_SPLIT; -+ else if (link_a) -+ link_cfg = MAX9296_LINK_A; -+ else if (link_b) -+ link_cfg = MAX9296_LINK_B; -+ else { -+ dev_err(dev, "No link was detected"); -+ return -1; -+ } -+ -+ dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -+ -+ TRY_DEV_HERE(ret, regmap_update_bits( -+ map, -+ MAX9296_CTRL0, -+ MAX9296_CTRL0_AUTO_CFG_FIELD -+ |MAX9296_CTRL0_LINK_CFG_FIELD, -+ FIELD_PREP(MAX9296_CTRL0_AUTO_CFG_FIELD, auto_link) -+ |FIELD_PREP(MAX9296_CTRL0_LINK_CFG_FIELD, link_cfg)), -+ dev); -+ -+ TRY_DEV_HERE(ret, max9296_serial_link_reset(common, link), dev); -+ -+ TRY_DEV_HERE(ret, max9296_wait_link_lock(common, link), dev); -+ -+ return 0; -+} -+ -+static int max9296_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ -+ if (WARN_ON_ONCE(link_id >= common->num_serial_links)) -+ return -EINVAL; -+ -+ if (WARN_ONCE(common->serial_link[link_id].config.link_type -+ != MAX9X_LINK_TYPE_GMSL2, "Only GMSL2 is supported!")) -+ return -EINVAL; -+ -+ // GMSL2 -+ ret = max9296_set_serial_link_rate(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9296_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ common->serial_link[link_id].detected = true; -+ -+ ret = max9296_set_serial_link_routing(common, link_id); -+ if (ret) -+ return ret; -+ -+ max9296_deisolate_serial_link(common, link_id); -+ -+ return 0; -+} -+ -+static int max9296_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max9296_set_video_pipe_enabled(common, pipe_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max9296_set_video_pipe_maps_enabled(common, pipe_id, 0); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+static struct max9x_serial_link_ops max9296_serial_link_ops = { -+ .enable = max9296_enable_serial_link, -+ .disable = max9296_disable_serial_link, -+ .isolate = max9296_isolate_serial_link, -+ .deisolate = max9296_deisolate_serial_link, -+}; -+/***** max9296_serial_link_ops *****/ -+ -+static int max9296_enable_csi_link(struct max9x_common *common, unsigned int csi_link_id) -+{ -+ return max9296_set_csi_link_enabled(common, csi_link_id, true); -+} -+ -+static int max9296_disable_csi_link(struct max9x_common *common, unsigned int csi_link_id) -+{ -+ return max9296_set_csi_link_enabled(common, csi_link_id, false); -+} -+ -+static struct max9x_csi_link_ops max9296_csi_link_ops = { -+ .enable = max9296_enable_csi_link, -+ .disable = max9296_disable_csi_link, -+}; -+ -+static int max9296_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int link_id; -+ int ret; -+ -+ dev_dbg(dev, "Enable"); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ ret = max9296_disable_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ } -+ -+ ret = max9296_configure_csi_dphy(common); -+ -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+int max9296_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ *common_ops = &max9296_common_ops; -+ *serial_ops = &max9296_serial_link_ops; -+ *csi_ops = &max9296_csi_link_ops; -+ *lf_ops = NULL; -+ *trans_ops = NULL; -+ -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Jacob Kiggins "); -+MODULE_AUTHOR("Yan Dongcheng "); -+MODULE_DESCRIPTION("Maxim MAX9296 Dual GMSL2/GMSL1 to CSI-2 Deserializer driver"); -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -new file mode 100644 -index 000000000000..38c344669df4 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -0,0 +1,144 @@ -+/* -+ * max9296.h - Maxim 9296 registers and constants. -+ * -+ * Copyright (c) 2023-2024 Define Design Deploy Corp. All Rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9296_H_ -+#define _MAX9296_H_ -+ -+#include -+#include "serdes.h" -+ -+enum max9296_dev_id { -+ MAX9296A = 0x94 -+}; -+ -+enum max9296_phy_mode { -+ MAX9296_MIPI_PHY_4X2 = BIT(0), // four 2-lane ports -+ MAX9296_MIPI_PHY_1X4 = BIT(1), // one 4-lane (CSI2 is master (TODO: Which port???)) -+ MAX9296_MIPI_PHY_2X4 = BIT(2), // two 4-lane ports (CSI1 is master for port A, CSI2 for port B) -+ MAX9296_MIPI_PHY_1X4A_22 = BIT(3), // one 4-lane (PHY0+PHY1, CSI1 for port A) port and two 2-lane ports -+ MAX9296_MIPI_PHY_1X4B_22 = BIT(4), // one 4-lane (PHY2+PHY3, CSI2 for port B) port and two 2-lane ports -+}; -+ -+enum max9296_link_mode { -+ MAX9296_LINK_AB, -+ MAX9296_LINK_A, -+ MAX9296_LINK_B, -+ MAX9296_LINK_SPLIT -+}; -+ -+#define MAX9296_NUM_SERIAL_LINKS 2 -+#define MAX9296_NUM_VIDEO_PIPES 4 -+#define MAX9296_NUM_MIPI_MAPS 16 -+#define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ -+/* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ -+ -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+ -+#define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 -+ -+#define MAX9296_FLD_OFS(n, bits_per_field, count) (((n) % (count)) * (bits_per_field)) -+#define MAX9296_OFFSET_GENMASK(offset, h, l) GENMASK(offset + h, offset + l) -+ -+#define MAX9296_CTRL0 0x10 -+#define MAX9296_CTRL0_AUTO_CFG_FIELD BIT(4) -+#define MAX9296_CTRL0_LINK_CFG_FIELD GENMASK(1, 0) -+#define MAX9296_CTRL0_RESET_LINK_FIELD BIT(6) // One bit to reset whole link -+#define MAX9296_CTRL0_RESET_ALL_FIELD BIT(7) -+#define MAX9296_CTRL0_RESET_ONESHOT_FIELD BIT(5) -+ -+#define MAX9296_PHY_REM_CTRL (0x1) -+#define MAX9296_PHY_REM_CTRL_TX_FIELD (GENMASK(1, 0) << 2) -+#define MAX9296_PHY_REM_CTRL_RX_FIELD GENMASK(1, 0) -+#define MAX9296_PHY_REM_CTRL_DIS_FIELD BIT(4) -+ -+/* -+ *CTRL3(0x13) LINK_MODE is set to link A. -+ */ -+#define MAX9296_PHY_LOCKED(link) (0x13) /* Based on link mode */ -+#define MAX9296_PHY_LOCKED_FIELD BIT(3) -+ -+#define MAX9296_VIDEO_PIPE_SEL(pipe) (0x50 + pipe) -+#define MAX9296_VIDEO_PIPE_STR_SEL_FIELD GENMASK(1, 0) -+ -+#define MAX9296_VIDEO_PIPE_EN(pipe) (0x2) -+#define MAX9296_VIDEO_PIPE_EN_FIELD(pipe) (BIT(pipe) << 4) -+ -+#define MAX9296_DPLL_FREQ(phy) (0x31D + ((phy) * 3)) -+#define MAX9296_DPLL_FREQ_FIELD GENMASK(4, 0) -+ -+#define MAX9296_MIPI_TX_EXT(pipe) (0x500 + ((pipe) * 0x10)) -+ -+#define MAX9296_MIPI_PHY0 0x330 -+#define MAX9296_MIPI_PHY0_MODE_FIELD GENMASK(4, 0) -+#define MAX9296_MIPI_PHY_ENABLE 0x332 -+#define MAX9296_MIPI_PHY_ENABLE_FIELD(csi) BIT((csi) + 4) -+#define MAX9296_MIPI_PHY_LANE_MAP(csi) (0x333 + (csi) / 2) -+#define MAX9296_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ -+ (GENMASK(1, 0) << (MAX9296_FLD_OFS(csi, 4, 2) + MAX9296_FLD_OFS(lane, 2, 2))) -+ -+// Note that CSIs and pipes overlap: -+#define MAX9296_MIPI_TX(pipe) (0x400 + ((pipe) * 0x40)) -+ -+/* Offsets for MAX9296_MIPI_TX under 0x0B are only for lanes 1 and 3 */ -+#define MAX9296_MIPI_TX_LANE_CNT(csi) (MAX9296_MIPI_TX(csi) + 0x0A) -+#define MAX9296_MIPI_TX_LANE_CNT_FIELD GENMASK(7, 6) -+#define MAX9296_MIPI_TX_DESKEW_INIT(csi) (MAX9296_MIPI_TX(csi) + 0x03) -+#define MAX9296_MIPI_TX_DESKEW_INIT_AUTO_EN BIT(7) -+#define MAX9296_MIPI_TX_DESKEW_INIT_WIDTH GENMASK(2, 0) -+#define MAX9296_MAP_EN_L(pipe) (MAX9296_MIPI_TX(pipe) + 0x0B) -+#define MAX9296_MAP_EN_H(pipe) (MAX9296_MIPI_TX(pipe) + 0x0C) -+#define MAX9296_MAP_EN_FIELD GENMASK(7, 0) -+#define MAX9296_MAP_SRC_L(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x0D + ((map) * 2)) -+#define MAX9296_MAP_SRC_L_VC_FIELD GENMASK(7, 6) -+#define MAX9296_MAP_SRC_L_DT_FIELD GENMASK(5, 0) -+#define MAX9296_MAP_DST_L(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x0D + ((map) * 2) + 1) -+#define MAX9296_MAP_DST_L_VC_FIELD GENMASK(7, 6) -+#define MAX9296_MAP_DST_L_DT_FIELD GENMASK(5, 0) -+#define MAX9296_MAP_SRCDST_H(pipe, map) (MAX9296_MIPI_TX_EXT(pipe) + (map)) -+#define MAX9296_MAP_SRCDST_H_SRC_VC_FIELD GENMASK(7, 5) -+#define MAX9296_MAP_SRCDST_H_DST_VC_FIELD GENMASK(4, 2) -+#define MAX9296_MAP_DPHY_DEST(pipe, map) (MAX9296_MIPI_TX(pipe) + 0x2D + ((map) / 4)) -+#define MAX9296_MAP_DPHY_DEST_FIELD(map) (GENMASK(1, 0) << MAX9296_FLD_OFS(map, 2, 4)) -+ -+#define MAX9296_MIPI_TX_ALT_MEM(csi) (MAX9296_MIPI_TX(csi) + 0x33) -+#define MAX9296_MIPI_TX_ALT_MEM_FIELD GENMASK(2, 0) -+#define MAX9296_MIPI_TX_ALT_MEM_8BPP BIT(1) -+#define MAX9296_MIPI_TX_ALT_MEM_10BPP BIT(2) -+#define MAX9296_MIPI_TX_ALT_MEM_12BPP BIT(0) -+ -+#define MAX9296_GPIO_A(gpio_num) (0x2B0 + 3 * gpio_num) -+#define MAX9296_GPIO_B(gpio_num) (MAX9296_GPIO_A(gpio_num) + 1) -+#define MAX9296_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX9296_GPIO_C(gpio_num) (MAX9296_GPIO_A(gpio_num) + 2) -+#define MAX9296_GPIO_C_RX_ID GENMASK(4, 0) -+ -+#define MAX9296_DPLL_RESET(phy) (0x1C00 + ((phy) * 0x100)) -+#define MAX9296_DPLL_RESET_SOFT_RST_FIELD BIT(0) -+ -+static struct max9x_serdes_rate_table max9296_rx_rates[] = { -+ { .val = 1, .freq_mhz = 3000}, // 3 GHz -+ { .val = 2, .freq_mhz = 6000}, // 6 GHz -+}; -+ -+static struct max9x_serdes_rate_table max9296_tx_rates[] = { -+ { .val = 0, .freq_mhz = 187}, // 187.5 MHz -+}; -+ -+#endif /* _MAX9296_H_ */ -diff --git a/drivers/media/i2c/max9x/max96717.c b/drivers/media/i2c/max9x/max96717.c -new file mode 100644 -index 000000000000..4e79ae63a6ed ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96717.c -@@ -0,0 +1,589 @@ -+/* -+ * max96717_main.c - Maxim MAX96717 CSI-2 to GMSL2/GMSL1 Serializer -+ * -+ * Copyright (c) 2022, D3 Engineering. All rights reserved. -+ * Copyright (c) 2023, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max96717.h" -+ -+static const struct regmap_config max96717_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+}; -+ -+// Declarations -+static int max96717_set_pipe_csi_enabled(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int csi_id, bool enable); -+static int max96717_video_pipe_double_pixel(struct max9x_common *common, unsigned int pipe_id, unsigned int bpp); -+static int max96717_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max96717_enable_serial_link(struct max9x_common *common, unsigned int link); -+static int max96717_disable_serial_link(struct max9x_common *common, unsigned int link); -+static int max96717_enable(struct max9x_common *common); -+static int max96717_disable(struct max9x_common *common); -+static int max96717_pixel_mode(struct max9x_common *common, bool pixel); -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip); -+static int max96717_gpio_get_direction(struct gpio_chip *chip, unsigned int offset); -+static int max96717_gpio_direction_input(struct gpio_chip *chip, unsigned int offset); -+static int max96717_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value); -+static int max96717_gpio_get(struct gpio_chip *chip, unsigned int offset); -+static int max96717_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); -+static int max96717_setup_gpio(struct max9x_common *common); -+ -+static struct max9x_common_ops max96717_common_ops = { -+ .max_elements = max96717_max_elements, -+ .enable = max96717_enable, -+ .disable = max96717_disable, -+}; -+ -+static struct max9x_serial_link_ops max96717_serial_link_ops = { -+ .enable = max96717_enable_serial_link, -+ .disable = max96717_disable_serial_link, -+}; -+ -+static struct max9x_translation_ops max96717_translation_ops; -+ -+static struct max9x_common *from_gpio_chip(struct gpio_chip *chip) -+{ -+ return container_of(chip, struct max9x_common, gpio_chip); -+} -+ -+static int max96717_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(map, MAX96717_GPIO_A(offset), &val); -+ if (ret) -+ return ret; -+ -+ return (FIELD_GET(MAX96717_GPIO_A_OUT_DIS_FIELD, val) == 0U ? -+ GPIOD_OUT_LOW : GPIOD_IN); -+} -+ -+static int max96717_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX96717_GPIO_A(offset), -+ MAX96717_GPIO_A_OUT_DIS_FIELD, -+ MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_DIS_FIELD, 1U)); -+} -+ -+static int max96717_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int mask = 0; -+ unsigned int val; -+ -+ mask = MAX96717_GPIO_A_OUT_DIS_FIELD | MAX96717_GPIO_A_OUT_FIELD | -+ MAX96717_GPIO_A_RX_EN_FIELD; -+ -+ // Enable the GPIO as an output -+ val = MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_DIS_FIELD, 0U); -+ // Write out the initial value to the GPIO -+ val |= MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U)); -+ // Disable remote control over SerDes link -+ val |= MAX9X_FIELD_PREP(MAX96717_GPIO_A_RX_EN_FIELD, 0U); -+ -+ return regmap_update_bits(map, MAX96717_GPIO_A(offset), mask, val); -+} -+ -+static int max96717_gpio_get(struct gpio_chip *chip, unsigned int offset) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(map, MAX96717_GPIO_A(offset), &val); -+ if (ret) -+ return ret; -+ -+ return FIELD_GET(MAX96717_GPIO_A_IN_FIELD, val); -+} -+ -+static int max96717_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) -+{ -+ struct max9x_common *common = from_gpio_chip(chip); -+ struct regmap *map = common->map; -+ -+ return regmap_update_bits(map, MAX96717_GPIO_A(offset), -+ MAX96717_GPIO_A_OUT_FIELD, -+ MAX9X_FIELD_PREP(MAX96717_GPIO_A_OUT_FIELD, (value == 0 ? 0U : 1U))); -+} -+ -+static int max96717_setup_gpio(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ struct max9x_gpio_pdata *gpio_pdata; -+ -+ if (common->dev->platform_data) { -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ gpio_pdata = &pdata->gpio; -+ } -+ -+ // Functions -+ if (gpio_pdata && gpio_pdata->label) -+ common->gpio_chip.label = gpio_pdata->label; -+ else -+ common->gpio_chip.label = dev_name(dev); -+ -+ dev_dbg(dev, "gpio_chip label is %s, dev_name is %s", -+ common->gpio_chip.label, dev_name(dev)); -+ -+ // Functions -+ common->gpio_chip.label = MAX96717_NAME; -+ common->gpio_chip.parent = dev; -+ common->gpio_chip.get_direction = max96717_gpio_get_direction; -+ common->gpio_chip.direction_input = max96717_gpio_direction_input; -+ common->gpio_chip.direction_output = max96717_gpio_direction_output; -+ common->gpio_chip.get = max96717_gpio_get; -+ common->gpio_chip.set = max96717_gpio_set; -+ common->gpio_chip.ngpio = MAX96717_NUM_GPIO; -+ common->gpio_chip.can_sleep = 1; -+ common->gpio_chip.base = -1; -+ if (gpio_pdata && gpio_pdata->names) -+ common->gpio_chip.names = gpio_pdata->names; -+ -+ ret = devm_gpiochip_add_data(dev, &common->gpio_chip, common); -+ if (ret < 0) { -+ dev_err(dev, "gpio_init: Failed to add max96717_gpio\n"); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_set_pipe_csi_enabled(struct max9x_common *common, -+ unsigned int pipe_id, -+ unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "Video-pipe %d, csi %d: %s, %d lanes", \ -+ pipe_id, csi_id, (enable ? "enable" : "disable"), \ -+ common->csi_link[csi_id].config.num_lanes); -+ -+ // Select number of lanes for CSI port csi_id -+ ret = regmap_update_bits(map, MAX96717_MIPI_RX_1, -+ MAX96717_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX96717_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id), -+ common->csi_link[csi_id].config.num_lanes - 1)); -+ if (ret) -+ return ret; -+ -+ // Select CSI port csi_id for video pipe pipe_id -+ ret = regmap_update_bits(map, MAX96717_FRONTTOP_0, -+ MAX96717_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) -+ | MAX96717_FRONTTOP_0_START_CSI_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX96717_FRONTTOP_0_SEL_CSI_FIELD(pipe_id), csi_id) -+ | MAX9X_FIELD_PREP(MAX96717_FRONTTOP_0_START_CSI_FIELD(csi_id), enable ? 1U : 0U)); -+ if (ret) -+ return ret; -+ -+ // Start video pipe pipe_id from CSI port csi_id -+ ret = regmap_update_bits(map, MAX96717_FRONTTOP_9, -+ MAX96717_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), -+ MAX9X_FIELD_PREP(MAX96717_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id), enable ? 1U : 0U)); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+/** -+ * max96717_video_pipe_double_pixel() - Configure Double Loading Mode on a video pipe -+ * @common: max9x_common -+ * @pipe_id: Target pipe's ID -+ * @bpp: Original BPP to double. This can be 0 (disables), 8, 10, or 12. -+ * -+ * Double loading mode squeezes two input pixels together such that they are -+ * treated as a single pixel by the video pipe. Using this method increases -+ * bandwidth efficiency. -+ * -+ * See: GMSL2 Customers User Guide Section 30.5.1.1.1.2 "Double Loading Mode" -+ * See: GMSL2 Customers User Guide Section 43.3.4.5.1 "Double Mode (Serializer)" -+ */ -+static int max96717_video_pipe_double_pixel(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ unsigned int reg; -+ unsigned int fields; -+ unsigned int vals; -+ unsigned int dbl_bpp; -+ -+ // Clear all the double pixel mode fields -+ ret = regmap_update_bits(map, -+ MAX96717_FRONTTOP_10, -+ MAX96717_FRONTTOP_10_DBL8_FIELD(pipe_id), -+ 0x0); -+ if (ret) -+ return ret; -+ -+ ret = regmap_update_bits(map, -+ MAX96717_FRONTTOP_11, -+ MAX96717_FRONTTOP_11_DBL10_FIELD(pipe_id) | -+ MAX96717_FRONTTOP_11_DBL12_FIELD(pipe_id), -+ 0x0); -+ if (ret) -+ return ret; -+ -+ // Enable/disable double pixel mode for pipe -+ switch (bpp) { -+ case 0: -+ //bpp not used on this pipe, but still valid input -+ break; -+ case 8: -+ reg = MAX96717_FRONTTOP_10; -+ fields = MAX96717_FRONTTOP_10_DBL8_FIELD(pipe_id); -+ break; -+ case 10: -+ reg = MAX96717_FRONTTOP_11; -+ fields = MAX96717_FRONTTOP_11_DBL10_FIELD(pipe_id); -+ break; -+ case 12: -+ reg = MAX96717_FRONTTOP_11; -+ fields = MAX96717_FRONTTOP_11_DBL12_FIELD(pipe_id); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable pixel doubling for specified pipe -+ if (bpp != 0) { -+ dev_info(dev, "Configure double loading mode for pipe %u (%ubpp -> %ubpp)", -+ pipe_id, bpp, (bpp * 2)); -+ ret = regmap_update_bits(map, reg, fields, 0xFF); -+ if (ret) -+ return ret; -+ } -+ -+ // Enable software BPP override and set BPP value -+ dbl_bpp = bpp * 2; -+ -+ // Override output bpp -+ reg = MAX96717_FRONTTOP_2X(pipe_id); -+ -+ fields = MAX96717_FRONTTOP_2X_BPP_EN_FIELD; -+ vals = MAX9X_FIELD_PREP(MAX96717_FRONTTOP_2X_BPP_EN_FIELD, bpp == 0 ? 0U : 1U); -+ -+ fields |= MAX96717_FRONTTOP_2X_BPP_FIELD; -+ vals |= MAX9X_FIELD_PREP(MAX96717_FRONTTOP_2X_BPP_FIELD, dbl_bpp); -+ -+ return regmap_update_bits(map, reg, fields, vals); -+} -+ -+static int max96717_max_elements(struct max9x_common *common, -+ enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX96717_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX96717_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX96717_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX96717_NUM_CSI_LINKS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static int max96717_enable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_info(dev, "Link %d: Enable", link_id); -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ ret = max96717_set_pipe_csi_enabled(common, pipe_id, -+ config->src_csi, true); -+ if (ret) -+ return ret; -+ ret = max96717_video_pipe_double_pixel(common, pipe_id, -+ config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_disable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ unsigned int pipe_id; -+ int ret; -+ -+ dev_info(dev, "Link %d: Disable", link_id); -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ -+ ret = max96717_set_pipe_csi_enabled(common, pipe_id, -+ config->src_csi, false); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "Disable"); -+ -+ return 0; -+} -+ -+static int max96717_pixel_mode(struct max9x_common *common, bool pixel) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Pixel mode: %d", pixel); -+ -+ // Register actually enables tunnel mode. Clearing the bit "enables" pixel mode. -+ return regmap_write(map, MAX96717_EXT11, -+ MAX9X_FIELD_PREP(MAX96717_TUNNEL_MODE, !pixel)); -+} -+ -+/** -+ * Enable the MAX96717 to replicate a frame sync signal from the deserializer. -+ * NOTE: Currently the MAX96717 driver supports frame sync across its -+ * GPIO8. -+ */ -+static int max96717_enable_frame_sync(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ int ret; -+ int deserializer_tx_id; -+ -+ ret = of_property_read_u32(node, "fsync-tx-id", &deserializer_tx_id); -+ // Not necessarily problematic, no frame sync tx found -+ if (ret == -ENODATA || ret == -EINVAL) { -+ dev_info(dev, "Frame sync GPIO tx id not found"); -+ return 0; -+ } -+ // Other errors are problematic -+ else if (ret < 0) { -+ dev_err(dev, "Failed to read frame sync tx id with err %d", -+ ret); -+ return ret; -+ } -+ -+ ret = regmap_write(map, MAX96717_GPIO_C(8), deserializer_tx_id); -+ if (ret) { -+ dev_err(dev, "Failed to write des frame sync id with err: %d", -+ ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96717_get_datatype(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret, datatype; -+ -+ ret = of_property_read_u32(node, "data-type", &datatype); -+ if (ret == -ENODATA || ret == -EINVAL) { -+ dev_dbg(dev, "Data-type not found not filtering"); -+ return regmap_write(map, MAX96717_FRONTTOP_16, 0); -+ } -+ // Other errors are problematic -+ else if (ret < 0) { -+ dev_err(dev, "Problem reading in data-type with err %d", ret); -+ return ret; -+ } -+ -+ dev_dbg(dev, "Setting image data type to %x", datatype); -+ /* Filter out metadata, only use the image datatype. -+ * MAX96717_FRONTTOP_16: mem_dt1_selz. -+ * Bit 6 is enable -+ */ -+ return regmap_write(map, MAX96717_FRONTTOP_16, -+ (datatype & MAX96717_FRONTTOP_16_FIELD) | MAX96717_FRONTTOP_16_ENABLE); -+} -+ -+static int max96717_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "setup gpio"); -+ ret = max96717_setup_gpio(common); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "setup datatype"); -+ ret = max96717_get_datatype(common); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "setup pixel mode"); -+ // this driver MUST be in pixel mode as that is what our driver architecture supports -+ ret = max96717_pixel_mode(common, true); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "setup frame sync"); -+ ret = max96717_enable_frame_sync(common); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+int max96717_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ *common_ops = &max96717_common_ops; -+ *serial_ops = &max96717_serial_link_ops; -+ *csi_ops = NULL; -+ *lf_ops = NULL; -+ *trans_ops = &max96717_translation_ops; -+ return 0; -+} -+ -+#if 0 -+static int max96717_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *ser = NULL; -+ int ret = 0; -+ -+ dev_info(dev, "Probing"); -+ -+ ser = devm_kzalloc(dev, sizeof(*ser), GFP_KERNEL); -+ if (!ser) { -+ dev_err(dev, "Failed to allocate memory."); -+ return -ENOMEM; -+ } -+ -+ ret = max9x_common_init_i2c_client( -+ ser, -+ client, -+ &max96717_regmap_config, -+ &max96717_common_ops, -+ &max96717_serial_link_ops, -+ NULL, /* csi_link_os */ -+ NULL); -+ if (ret) -+ return ret; -+ -+ return 0; -+ -+err: -+ max9x_destroy(ser); -+ return ret; -+} -+ -+static void max96717_remove(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *ser = NULL; -+ -+ dev_info(dev, "Removing"); -+ -+ ser = max9x_client_to_common(client); -+ -+ max9x_destroy(ser); -+ -+} -+ -+static struct i2c_device_id max96717_idtable[] = { -+ {MAX96717_NAME, 0}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(i2c, max96717_idtable); -+ -+static struct of_device_id max96717_of_match[] = { -+ { .compatible = MAX96717_NAME}, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, max96717_of_match); -+ -+static struct i2c_driver max96717_driver = { -+ .driver = { -+ .name = MAX96717_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(max96717_of_match), -+ }, -+ .probe = max96717_probe, -+ .remove = max96717_remove, -+ .id_table = max96717_idtable, -+}; -+ -+module_i2c_driver(max96717_driver); -+#endif -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Cody Burrows "); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("He Jiabin "); -+MODULE_DESCRIPTION("Maxim MAX96717 CSI-2 to GMSL2 Serializer driver"); -diff --git a/drivers/media/i2c/max9x/max96717.h b/drivers/media/i2c/max9x/max96717.h -new file mode 100644 -index 000000000000..022340a7eba9 ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96717.h -@@ -0,0 +1,90 @@ -+/* -+ * max96717.h - Maxim MAX96717 registers and constants. -+ * -+ * Copyright (c) 2022, D3 Engineering. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX96717_H_ -+#define _MAX96717_H_ -+ -+#include -+#include "serdes.h" -+ -+#define MAX96717_NAME "max96717" -+ -+enum max96717_gpio_out_type { -+ MAX96717_GPIO_OUT_TYPE_OPEN_DRAIN = 0, -+ MAX96717_GPIO_OUT_TYPE_PUSH_PULL = 1, -+}; -+ -+enum max96717_gpio_pull_updn_sel { -+ MAX96717_GPIO_PULL_UPDN_SEL_NONE = 0, -+ MAX96717_GPIO_PULL_UPDN_SEL_UP = 1, -+ MAX96717_GPIO_PULL_UPDN_SEL_DOWN = 2, -+}; -+ -+#define MAX96717_NUM_SERIAL_LINKS 1 -+#define MAX96717_NUM_VIDEO_PIPES 1 -+#define MAX96717_NUM_MIPI_MAPS 1 -+#define MAX96717_NUM_CSI_LINKS 1 -+#define MAX96717_NUM_GPIO 11 -+ -+#define MAX96717_GPIO(gpio) (0x2BE + ((gpio) * 3)) -+#define MAX96717_GPIO_A(gpio) (MAX96717_GPIO(gpio) + 0) -+#define MAX96717_GPIO_A_OUT_DIS_FIELD BIT(0) -+#define MAX96717_GPIO_A_TX_EN_FIELD BIT(1) -+#define MAX96717_GPIO_A_RX_EN_FIELD BIT(2) -+#define MAX96717_GPIO_A_IN_FIELD BIT(3) -+#define MAX96717_GPIO_A_OUT_FIELD BIT(4) -+#define MAX96717_GPIO_A_RES_CFG_FIELD BIT(7) -+#define MAX96717_GPIO_B(gpio) (MAX96717_GPIO(gpio) + 1) -+#define MAX96717_GPIO_B_TX_ID GENMASK(4, 0) -+#define MAX96717_GPIO_B_OUT_TYPE_FIELD BIT(5) -+#define MAX96717_GPIO_B_PULL_UPDN_SEL_FIELD GENMASK(7, 6) -+#define MAX96717_GPIO_C(gpio) (MAX96717_GPIO(gpio) + 2) -+#define MAX96717_GPIO_C_RX_ID GENMASK(4, 0) -+ -+/* -+ * Some macros refer to a pipe despite not using it -+ * this is to make code easier to port between drivers -+ * in spite of the max96717 only having 1 video pipe -+ */ -+#define MAX96717_FRONTTOP_0 (0x308) -+#define MAX96717_FRONTTOP_0_SEL_CSI_FIELD(pipe_id) BIT(pipe_id + 2) -+#define MAX96717_FRONTTOP_0_START_CSI_FIELD(csi_id) BIT((csi_id) + 5) -+#define MAX96717_FRONTTOP_9 (0x311) -+#define MAX96717_FRONTTOP_9_START_VIDEO_FIELD(pipe_id, csi_id) BIT((pipe_id + 2) + 4 * (csi_id + 1)) -+#define MAX96717_FRONTTOP_10 (0x312) -+#define MAX96717_FRONTTOP_10_DBL8_FIELD(pipe_id) BIT(2) -+#define MAX96717_FRONTTOP_11 (0x313) -+#define MAX96717_FRONTTOP_11_DBL10_FIELD(pipe_id) BIT(2) -+#define MAX96717_FRONTTOP_11_DBL12_FIELD(pipe_id) BIT(6) -+#define MAX96717_FRONTTOP_16 (0x318) -+#define MAX96717_FRONTTOP_16_FIELD GENMASK(5, 0) -+#define MAX96717_FRONTTOP_16_ENABLE BIT(6) -+#define MAX96717_FRONTTOP_2X(pipe_id) (0x31E) -+#define MAX96717_FRONTTOP_2X_BPP_EN_FIELD BIT(5) -+#define MAX96717_FRONTTOP_2X_BPP_FIELD GENMASK(4, 0) -+ -+#define MAX96717_MIPI_RX (0x330) -+#define MAX96717_MIPI_RX_1 (MAX96717_MIPI_RX + 1) -+#define MAX96717_MIPI_RX_1_SEL_CSI_LANES_FIELD(csi_id) (GENMASK(1, 0) << ((csi_id + 1) * 4)) -+ -+#define MAX96717_EXT11 (0x383) -+#define MAX96717_TUNNEL_MODE BIT(7) -+ -+#endif /* _MAX96717_H_ */ -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -new file mode 100644 -index 000000000000..1c1059c0c9de ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -0,0 +1,1169 @@ -+/* -+ * max96724.c - Maxim MAX96724 GMSL2/GMSL1 to CSI-2 Deserializer -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "max96724.h" -+ -+// Params -+int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -+module_param(max96724_serial_link_timeout_ms, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -+MODULE_PARM_DESC(max96724_serial_link_timeout_ms, "Timeout for serial link in milliseconds"); -+ -+// Declarations -+static int max96724_set_phy_mode(struct max9x_common *common, unsigned int phy_mode); -+static int max96724_set_phy_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max96724_set_phy_lane_map(struct max9x_common *common, unsigned int csi_id, unsigned int phy_lane_map); -+static int max96724_set_phy_dpll_freq(struct max9x_common *common, unsigned int csi_id, unsigned int freq_mhz); -+static int max96724_set_mipi_lane_cnt(struct max9x_common *common, unsigned int csi_id, int num_lanes); -+static int max96724_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max96724_configure_csi_dphy(struct max9x_common *common); -+static int max96724_enable(struct max9x_common *common); -+static int max96724_max_elements(struct max9x_common *common, enum max9x_element_type element); -+static int max96724_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked); -+static int max96724_set_serial_link_state(struct max9x_common *common, unsigned int link_id, bool enable); -+static int max96724_serial_link_reset(struct max9x_common *common, unsigned int link_id); -+static int max96724_set_serial_link_rate(struct max9x_common *common, unsigned int link_id); -+static int max96724_set_video_pipe_src(struct max9x_common *common, unsigned int pipe_id, -+ unsigned int link_id, unsigned int src_pipe); -+static int max96724_set_video_pipe_maps_enabled(struct max9x_common *common, unsigned int pipe_id, int num_maps); -+static int max96724_set_video_pipe_map(struct max9x_common *common, unsigned int pipe_id, unsigned int map_id, -+ struct max9x_serdes_mipi_map *mipi_map); -+static int max96724_set_csi_link_enabled(struct max9x_common *common, unsigned int csi_id, bool enable); -+static int max96724_csi_double_pixel(struct max9x_common *common, unsigned int csi_id, unsigned int bpp); -+static int max96724_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable); -+static int max96724_set_serial_link_routing(struct max9x_common *common, unsigned int link_id); -+static int max96724_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max96724_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max96724_set_remote_control_channel_enabled(struct max9x_common *common, unsigned int link_id, bool enabled); -+static int max96724_select_serial_link(struct max9x_common *common, unsigned int link); -+static int max96724_deselect_serial_link(struct max9x_common *common, unsigned int link); -+static int max96724_enable_native_frame_sync(struct max9x_common *common); -+static int max96724_enable_gpio_frame_sync(struct max9x_common *common); -+static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line); -+static int max96724_enable_line_fault(struct max9x_common *common, unsigned int line); -+static int max96724_set_line_fault(struct max9x_common *common, unsigned int line, bool enable); -+static int max96724_get_line_fault(struct max9x_common *common, unsigned int line); -+ -+// Functions -+static int max96724_set_phy_mode(struct max9x_common *common, unsigned int phy_mode) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI: phy_mode=%d", phy_mode); -+ -+ return regmap_update_bits(map, MAX96724_MIPI_PHY0, -+ MAX96724_MIPI_PHY0_MODE_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_PHY0_MODE_FIELD, phy_mode)); -+} -+ -+static int max96724_set_phy_enabled(struct max9x_common *common, -+ unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "CSI link %d: %s", csi_id, (enable ? "enable" : "disable")); -+ -+ ret = regmap_update_bits(map, MAX96724_MIPI_PHY_ENABLE, -+ MAX96724_MIPI_PHY_ENABLE_FIELD(csi_id), -+ MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_ENABLE_FIELD(csi_id), enable ? 1U : 0U)); -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_phy_lane_map(struct max9x_common *common, -+ unsigned int csi_id, unsigned int phy_lane_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: phy_lane_map=0x%04x", csi_id, phy_lane_map); -+ -+ return regmap_update_bits(map, MAX96724_MIPI_PHY_LANE_MAP(csi_id), -+ MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi_id, 0) -+ | MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi_id, 1), -+ phy_lane_map); -+} -+ -+static int max96724_set_phy_dpll_freq(struct max9x_common *common, -+ unsigned int csi_id, unsigned int freq_mhz) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "CSI link %d: freq %u MHz, %u mult", csi_id, freq_mhz, -+ freq_mhz / MAX96724_DPLL_FREQ_MHZ_MULTIPLE); -+ -+ return regmap_update_bits(map, MAX96724_DPLL_FREQ(csi_id), -+ MAX96724_DPLL_FREQ_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_DPLL_FREQ_FIELD, freq_mhz / MAX96724_DPLL_FREQ_MHZ_MULTIPLE)); -+} -+ -+static int max96724_set_initial_deskew(struct max9x_common *common, unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val; -+ unsigned int width; -+ -+ dev_dbg(dev, "CSI link %d: Initial deskew %s", csi_id, enable ? "enabled" : "disabled"); -+ -+ val = MAX9X_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_INIT_AUTO_EN, enable); -+ -+ if (enable) { -+ width = common->csi_link[csi_id].config.initial_deskew_width; -+ dev_dbg(dev, "Initial deskew width: 0x%03x", width); -+ -+ if (width > MAX96724_INIT_DESKEW_WIDTH_MAX) { -+ dev_err(dev, "Unsupported initial deskew width!"); -+ return -EINVAL; -+ } -+ -+ val |= MAX9X_FIELD_PREP(MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD, width); -+ } -+ -+ return regmap_write(map, MAX96724_MIPI_TX_DESKEW_INIT(csi_id), val); -+} -+ -+static int max96724_set_mipi_lane_cnt(struct max9x_common *common, -+ unsigned int csi_id, int num_lanes) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ -+ dev_dbg(dev, "CSI link %d: %d lanes", csi_id, num_lanes); -+ -+ ret = regmap_update_bits(map, MAX96724_MIPI_TX_LANE_CNT(csi_id), -+ MAX96724_MIPI_TX_VCX_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_TX_VCX_EN_FIELD, MAX96724_VC_2_BITS)); -+ -+ if (ret) { -+ dev_err(dev, "Failed to configure virtual channel extension!"); -+ return ret; -+ } -+ -+ ret = regmap_update_bits( -+ map, MAX96724_MIPI_TX_LANE_CNT(csi_id), -+ MAX96724_MIPI_TX_CPHY_EN_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_TX_CPHY_EN_FIELD, -+ common->csi_link[csi_id].config.bus_type == -+ V4L2_MBUS_CSI2_CPHY ? -+ 1 : -+ 0)); -+ -+ if (ret) { -+ dev_err(dev, "Failed to enable CSI2 %d to CPHY mode!", csi_id); -+ return ret; -+ } -+ -+ if (num_lanes > 0) { -+ ret = regmap_update_bits( -+ map, MAX96724_MIPI_TX_LANE_CNT(csi_id), -+ MAX96724_MIPI_TX_LANE_CNT_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_MIPI_TX_LANE_CNT_FIELD, -+ (num_lanes - 1))); -+ -+ if (ret) { -+ dev_err(dev, -+ " Failed to set CSI2 controller %d with %d lanes !", -+ csi_id, num_lanes); -+ return ret; -+ } -+ } -+ -+ return ret; -+} -+ -+static int max96724_configure_csi_dphy(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phy_mode; -+ unsigned int phy_lane_map[MAX96724_NUM_CSI_LINKS]; -+ unsigned int csi_id; -+ int ret; -+ -+ for (csi_id = 0; csi_id < MAX96724_NUM_CSI_LINKS; csi_id++) { -+ dev_dbg(dev, "CSI link %d: enabled=%d, num_lanes=%d, freq_mhz=%d, init_deskew=%d", -+ csi_id, -+ common->csi_link[csi_id].enabled, -+ common->csi_link[csi_id].config.num_lanes, -+ common->csi_link[csi_id].config.freq_mhz, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ } -+ -+ //TODO: Allow DT to override lane mapping? -+ -+ // Determine correct phy_mode and associate lane mapping -+ if (common->csi_link[0].config.num_lanes <= 2 -+ && common->csi_link[1].config.num_lanes <= 2 -+ && common->csi_link[2].config.num_lanes <= 2 -+ && common->csi_link[3].config.num_lanes <= 2) { -+ -+ phy_mode = MAX96724_MIPI_PHY_4X2; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 1); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 1); -+ -+ } else if (common->csi_link[0].config.num_lanes == 0 -+ && common->csi_link[1].config.num_lanes >= 3 -+ && common->csi_link[2].config.num_lanes >= 3 -+ && common->csi_link[3].config.num_lanes == 0) { -+ -+ phy_mode = MAX96724_MIPI_PHY_2X4; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 3); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 3); -+ -+ } else if (common->csi_link[0].config.num_lanes == 0 -+ && common->csi_link[1].config.num_lanes >= 3 -+ && common->csi_link[2].config.num_lanes <= 2 -+ && common->csi_link[3].config.num_lanes <= 2) { -+ -+ phy_mode = MAX96724_MIPI_PHY_1X4A_22; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 3); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 1); -+ -+ } else if (common->csi_link[0].config.num_lanes <= 2 -+ && common->csi_link[1].config.num_lanes <= 2 -+ && common->csi_link[2].config.num_lanes >= 3 -+ && common->csi_link[3].config.num_lanes == 0) { -+ -+ phy_mode = MAX96724_MIPI_PHY_1X4B_22; -+ -+ phy_lane_map[0] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(0, 1), 1); -+ phy_lane_map[1] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(1, 1), 1); -+ phy_lane_map[2] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 0), 0) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(2, 1), 1); -+ phy_lane_map[3] = MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 0), 2) -+ | MAX9X_FIELD_PREP(MAX96724_MIPI_PHY_LANE_MAP_FIELD(3, 1), 3); -+ -+ } else { -+ dev_err(dev, "Invalid CSI configuration! Supported modes: 4x2, 2x4, 1x4+2x2, 2x2+1x4"); -+ return -EINVAL; -+ } -+ -+ ret = max96724_set_phy_mode(common, phy_mode); -+ if (ret) -+ return ret; -+ -+ for (csi_id = 0; csi_id < MAX96724_NUM_CSI_LINKS; csi_id++) { -+ struct max9x_serdes_csi_config *config = &common->csi_link[csi_id].config; -+ -+ ret = max96724_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_phy_lane_map(common, csi_id, phy_lane_map[csi_id]); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_mipi_lane_cnt(common, csi_id, -+ common->csi_link[csi_id].config.num_lanes); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_initial_deskew(common, csi_id, -+ common->csi_link[csi_id].config.auto_init_deskew_enabled); -+ if (ret) -+ return ret; -+ -+ if (WARN_ONCE(config->freq_mhz > 0 && config->freq_mhz < MAX96724_DPLL_FREQ_MHZ_MULTIPLE, -+ "CSI frequency must be greater than %d MHz", MAX96724_DPLL_FREQ_MHZ_MULTIPLE)) -+ return -EINVAL; -+ -+ ret = max96724_set_phy_dpll_freq(common, csi_id, config->freq_mhz); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96724_set_all_reset(struct max9x_common *common, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); -+ -+ return regmap_update_bits(map, MAX96724_RESET_ALL, -+ MAX96724_RESET_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); -+} -+ -+static int max96724_soft_reset(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ dev_dbg(dev, "Soft reset"); -+ -+ ret = max96724_set_all_reset(common, 1); -+ if (ret) -+ return ret; -+ -+ /* Wait for hardware available after soft reset */ -+ /* TODO: Optimize sleep time 45 ms */ -+ msleep(45); -+ -+ ret = max96724_set_all_reset(common, 0); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max96724_max_elements(struct max9x_common *common, enum max9x_element_type element) -+{ -+ switch (element) { -+ case MAX9X_SERIAL_LINK: -+ return MAX96724_NUM_SERIAL_LINKS; -+ case MAX9X_VIDEO_PIPE: -+ return MAX96724_NUM_VIDEO_PIPES; -+ case MAX9X_MIPI_MAP: -+ return MAX96724_NUM_MIPI_MAPS; -+ case MAX9X_CSI_LINK: -+ return MAX96724_NUM_CSI_LINKS; -+ case MAX9X_LINE_FAULT: -+ return MAX96724_NUM_LINE_FAULTS; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static struct max9x_common_ops max96724_common_ops = { -+ .enable = max96724_enable, -+ .soft_reset = max96724_soft_reset, -+ .max_elements = max96724_max_elements, -+}; -+ -+static int max96724_get_serial_link_lock(struct max9x_common *common, unsigned int link_id, bool *locked) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val; -+ int ret; -+ -+ ret = regmap_read(map, MAX96724_PHY_LOCKED(link_id), &val); -+ if (ret) { -+ dev_err(dev, "failed to read link lock with err %d", ret); -+ return ret; -+ } -+ -+ if (FIELD_GET(MAX96724_PHY_LOCKED_FIELD, val) != 0) -+ *locked = true; -+ else -+ *locked = false; -+ -+ return 0; -+} -+ -+static int max96724_set_serial_link_state(struct max9x_common *common, unsigned int link_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ enum max9x_serdes_link_type link_type = common->serial_link[link_id].config.link_type; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Serial-link %d: %s", link_id, (enable ? "up" : "down")); -+ -+ ret = regmap_update_bits(map, MAX96724_LINK_CTRL, -+ MAX96724_LINK_CTRL_EN_FIELD(link_id) -+ | MAX96724_LINK_CTRL_GMSL_FIELD(link_id), -+ MAX9X_FIELD_PREP(MAX96724_LINK_CTRL_EN_FIELD(link_id), enable ? 1U : 0U) -+ | MAX9X_FIELD_PREP(MAX96724_LINK_CTRL_GMSL_FIELD(link_id), link_type == MAX9X_LINK_TYPE_GMSL2 ? 1U : 0U)); -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_serial_link_reset(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Serial-link %d reset", link_id); -+ -+ ret = regmap_update_bits(map, MAX96724_RESET_CTRL, -+ MAX96724_RESET_CTRL_FIELD(link_id), -+ MAX9X_FIELD_PREP(MAX96724_RESET_CTRL_FIELD(link_id), 1U)); -+ mutex_unlock(lock); -+ -+ return ret; -+} -+ -+static int max96724_set_serial_link_rate(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -+ unsigned int tx_rate, rx_rate; -+ -+ tx_rate = max9x_serdes_mhz_to_rate(max96724_tx_rates, ARRAY_SIZE(max96724_tx_rates), config->tx_freq_mhz); -+ if (tx_rate < 0) -+ return tx_rate; -+ -+ rx_rate = max9x_serdes_mhz_to_rate(max96724_rx_rates, ARRAY_SIZE(max96724_rx_rates), config->rx_freq_mhz); -+ if (rx_rate < 0) -+ return rx_rate; -+ -+ dev_dbg(dev, "Serial-link %d: TX=%d MHz RX=%d MHz", link_id, config->tx_freq_mhz, config->rx_freq_mhz); -+ -+ return regmap_update_bits(map, MAX96724_PHY_RATE_CTRL(link_id), -+ MAX96724_PHY_RATE_CTRL_TX_FIELD(link_id) -+ | MAX96724_PHY_RATE_CTRL_RX_FIELD(link_id), -+ MAX9X_FIELD_PREP(MAX96724_PHY_RATE_CTRL_TX_FIELD(link_id), tx_rate) -+ | MAX9X_FIELD_PREP(MAX96724_PHY_RATE_CTRL_RX_FIELD(link_id), rx_rate)); -+} -+ -+static int max96724_set_video_pipe_src(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int link_id, unsigned int src_pipe) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ dev_dbg(dev, "Video-pipe %d: src_link=%u, src_pipe=%u", pipe_id, link_id, src_pipe); -+ -+ return regmap_update_bits(map, MAX96724_VIDEO_PIPE_SEL(pipe_id), -+ MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id) -+ | MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(pipe_id), link_id) -+ | MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(pipe_id), src_pipe)); -+} -+ -+static int max96724_set_video_pipe_maps_enabled(struct max9x_common *common, -+ unsigned int pipe_id, int num_maps) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int val = 0; -+ int ret = 0; -+ struct mutex *lock = &common->link_mutex; -+ -+ if (num_maps > 0) -+ val = GENMASK(num_maps - 1, 0); -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Video-pipe %d: num_maps=%u", pipe_id, num_maps); -+ -+ ret = regmap_write(map, MAX96724_MAP_EN_L(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_EN_FIELD, val)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_write(map, MAX96724_MAP_EN_H(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_EN_FIELD, val >> 8)); -+ if (ret) -+ goto unlock_exit; -+ -+unlock_exit: -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_video_pipe_map(struct max9x_common *common, -+ unsigned int pipe_id, unsigned int map_id, -+ struct max9x_serdes_mipi_map *mipi_map) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret = 0; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Video-pipe %d, map %d: VC%d:DT%02x->VC%d:DT%02x, dst_csi=%d ", -+ pipe_id, map_id, -+ mipi_map->src_vc, -+ mipi_map->src_dt, -+ mipi_map->dst_vc, -+ mipi_map->dst_dt, -+ mipi_map->dst_csi); -+ -+ ret = regmap_write(map, MAX96724_MAP_SRC_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_SRC_L_VC_FIELD, mipi_map->src_vc) -+ | MAX9X_FIELD_PREP(MAX96724_MAP_SRC_L_DT_FIELD, mipi_map->src_dt)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_write(map, MAX96724_MAP_DST_L(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_DST_L_VC_FIELD, mipi_map->dst_vc) -+ | MAX9X_FIELD_PREP(MAX96724_MAP_DST_L_DT_FIELD, mipi_map->dst_dt)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_write(map, MAX96724_MAP_SRCDST_H(pipe_id, map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_SRCDST_H_SRC_VC_FIELD, mipi_map->src_vc) -+ | MAX9X_FIELD_PREP(MAX96724_MAP_SRCDST_H_DST_VC_FIELD, mipi_map->dst_vc)); -+ if (ret) -+ goto unlock_exit; -+ -+ ret = regmap_update_bits(map, MAX96724_MAP_DPHY_DEST(pipe_id, map_id), -+ MAX96724_MAP_DPHY_DEST_FIELD(map_id), -+ MAX9X_FIELD_PREP(MAX96724_MAP_DPHY_DEST_FIELD(map_id), mipi_map->dst_csi)); -+ if (ret) -+ goto unlock_exit; -+ -+unlock_exit: -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_csi_link_enabled(struct max9x_common *common, -+ unsigned int csi_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct max9x_serdes_csi_link *csi_link; -+ int ret; -+ -+ if (csi_id > common->num_csi_links) -+ return -EINVAL; -+ -+ csi_link = &common->csi_link[csi_id]; -+ -+ if (WARN_ONCE(enable && csi_link->enabled == false, -+ "Tried to enable a disabled CSI port???")) -+ return -EINVAL; -+ -+ if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, -+ "Tried to enable CSI port with no lanes???")) -+ return -EINVAL; -+ -+ // Keep track of number of enabled maps using this CSI link -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+ dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, -+ (enable ? "enable" : "disable"), csi_link->usecount); -+ -+ if (enable && csi_link->usecount == 1) { -+ // Enable && first user -+ -+ ret = max96724_set_phy_enabled(common, csi_id, true); -+ if (ret) -+ return ret; -+ -+ } else if (!enable && csi_link->usecount == 0) { -+ // Disable && no more users -+ -+ ret = max96724_set_phy_enabled(common, csi_id, false); -+ if (ret) -+ return ret; -+ -+ } -+ -+ return 0; -+} -+ -+static int max96724_csi_double_pixel(struct max9x_common *common, -+ unsigned int csi_id, -+ unsigned int bpp) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ unsigned int value; -+ int ret; -+ -+ dev_info(dev, "CSI ALT Mem mapping for %u bpp on csi %i", bpp, csi_id); -+ -+ switch (bpp) { -+ case 0: -+ value = 0; -+ break; -+ case 8: -+ value = FIELD_PREP(MAX96724_MIPI_TX_ALT_MEM_8BPP, 1U); -+ dev_err(dev, "8 BPP currently unsupported for pixel doubling"); -+ return -EINVAL; -+ case 10: -+ value = FIELD_PREP(MAX96724_MIPI_TX_ALT_MEM_10BPP, 1U); -+ break; -+ case 12: -+ value = FIELD_PREP(MAX96724_MIPI_TX_ALT_MEM_12BPP, 1U); -+ break; -+ default: -+ dev_err(dev, "Unsupported BPP for pixel doubling: %u", bpp); -+ return -EINVAL; -+ } -+ -+ // Enable alt mem mapping -+ ret = regmap_update_bits( -+ map, MAX96724_MIPI_TX_ALT_MEM(csi_id), -+ MAX96724_MIPI_TX_ALT_MEM_FIELD, value); -+ -+ return ret; -+ -+} -+ -+static int max96724_set_video_pipe_enabled(struct max9x_common *common, -+ unsigned int pipe_id, bool enable) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret = 0; -+ struct mutex *lock = &common->link_mutex; -+ -+ mutex_lock(lock); -+ dev_dbg(dev, "Video-pipe %d: %s", pipe_id, (enable ? "enable" : "disable")); -+ -+ // Enable max96712 "legacy" mode -+ // Non "legacy" mode ignores pipe mapping, and selects all streams for pipe -+ // 0. The Jetson doesn't know what to do with that and throws spurious data -+ // stream errors. -+ ret = regmap_update_bits(map, MAX96724_VIDEO_PIPE_EN, -+ MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, -+ MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD, -+ MAX96724_VIDEO_PIPE_LEGACY_MODE)); -+ if (ret) { -+ dev_err(dev, "Failed to clear select all streams bit"); -+ goto unlock_exit; -+ } -+ -+ ret = regmap_update_bits(map, MAX96724_VIDEO_PIPE_EN, -+ MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), -+ MAX9X_FIELD_PREP(MAX96724_VIDEO_PIPE_EN_FIELD(pipe_id), enable ? 1U : 0U)); -+ -+unlock_exit: -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_set_serial_link_routing(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max96724_set_video_pipe_src(common, pipe_id, -+ config->src_link, -+ config->src_pipe); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_video_pipe_maps_enabled(common, pipe_id, -+ config->num_maps); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max96724_set_video_pipe_map(common, pipe_id, map_id, -+ &config->map[map_id]); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_csi_link_enabled(common, -+ config->map[map_id].dst_csi, -+ true); -+ if (ret) -+ return ret; -+ -+ ret = max96724_csi_double_pixel(common, -+ config->map[map_id].dst_csi, -+ config->dbl_pixel_bpp); -+ if (ret) -+ return ret; -+ } -+ -+ ret = max96724_set_video_pipe_enabled(common, pipe_id, true); -+ if (ret) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static int max96724_disable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ unsigned int pipe_id; -+ unsigned int map_id; -+ int ret; -+ -+ for (pipe_id = 0; pipe_id < common->num_video_pipes; pipe_id++) { -+ struct max9x_serdes_pipe_config *config; -+ -+ if (common->video_pipe[pipe_id].enabled == false) -+ continue; -+ -+ config = &common->video_pipe[pipe_id].config; -+ if (config->src_link != link_id) -+ continue; -+ -+ ret = max96724_set_video_pipe_enabled(common, pipe_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_video_pipe_maps_enabled(common, pipe_id, 0); -+ if (ret) -+ return ret; -+ -+ for (map_id = 0; map_id < config->num_maps; map_id++) { -+ ret = max96724_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ /* TODO: if disabling serial link, serializer can't perform i2c communication. */ -+ // ret = max96724_set_serial_link_state(common, link_id, false); -+ // if (ret) -+ // return ret; -+ -+ return 0; -+} -+ -+static int max96724_enable_serial_link(struct max9x_common *common, -+ unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ bool locked; -+ unsigned long timeout; -+ -+ if (WARN_ON_ONCE(link_id >= common->num_serial_links)) -+ return -EINVAL; -+ -+ if (WARN_ONCE(common->serial_link[link_id].config.link_type != MAX9X_LINK_TYPE_GMSL2, -+ "Only GMSL2 is supported!")) -+ return -EINVAL; -+ -+ // GMSL2 -+ ret = max96724_set_remote_control_channel_enabled(common, link_id, false); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_serial_link_state(common, link_id, true); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_serial_link_rate(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max96724_serial_link_reset(common, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max96724_set_serial_link_routing(common, link_id); -+ if (ret) -+ return ret; -+ -+ timeout = jiffies + msecs_to_jiffies(max96724_serial_link_timeout_ms); -+ do { -+ usleep_range(10 * 1000, 25 * 1000); // 10 to 25 ms -+ ret = max96724_get_serial_link_lock(common, link_id, &locked); -+ if (ret) -+ return ret; -+ } while (!locked && time_before(jiffies, timeout)); -+ -+ if (!locked) { -+ dev_err(dev, "Serial-link %d: Failed to lock!", link_id); -+ return -ETIMEDOUT; -+ } -+ -+ return 0; -+} -+ -+static int max96724_set_remote_control_channel_enabled(struct max9x_common *common, -+ unsigned int link_id, -+ bool enabled) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret; -+ struct mutex *lock = &common->link_mutex; -+ //TODO: Allow DT to choose which port gets enabled: bit 0 disables port 0, bit 1 disables port 1 -+ //See also 0x0E REG14 DIS_REM_CC_P2[3:0] -+ -+ // Note that the register value 1 *disables* port-to-remote command & control -+ mutex_lock(lock); -+ dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); -+ if (enabled) { -+ ret = regmap_write(map, MAX96724_REM_CC, -+ ~(MAX9X_FIELD_PREP(MAX96724_REM_CC_DIS_PORT_FIELD(link_id, 0), enabled ? 1 : 0))); -+ } else { -+ ret = regmap_write(map, MAX96724_REM_CC, ~(0)); -+ } -+ -+ mutex_unlock(lock); -+ return ret; -+} -+ -+static int max96724_select_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max96724_set_remote_control_channel_enabled(common, link, true); -+} -+ -+static int max96724_deselect_serial_link(struct max9x_common *common, unsigned int link) -+{ -+ return max96724_set_remote_control_channel_enabled(common, link, false); -+} -+ -+static struct max9x_serial_link_ops max96724_serial_link_ops = { -+ .enable = max96724_enable_serial_link, -+ .disable = max96724_disable_serial_link, -+ .select = max96724_select_serial_link, -+ .deselect = max96724_deselect_serial_link, -+ .get_locked = max96724_get_serial_link_lock, -+}; -+ -+static int max96724_enable_native_frame_sync(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int ret, i; -+ unsigned int val; -+ enum max96724_fsync_pin pin; -+ unsigned int fsync_freq; -+ unsigned int pclk_freq; -+ unsigned int fsync_period; -+ unsigned int fsync_tx_id; -+ bool fsync_master; -+ -+ if (!of_property_read_bool(node, "frame-sync-enable")) { -+ dev_info(dev, "Native frame sync not enabled"); -+ return regmap_write(map, MAX96724_FSYNC_0, -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -+ MAX96724_FSYNC_GEN_OFF_GPIO_OFF)); -+ } -+ -+ fsync_master = of_property_read_bool(node, "frame-sync-master"); -+ if (fsync_master) -+ dev_dbg(dev, "Frame sync master mode"); -+ else -+ dev_dbg(dev, "Frame sync slave mode"); -+ -+ ret = of_property_read_u32(node, "frame-sync-pin", &val); -+ if (ret) { -+ dev_err(dev, "Missing property: frame-sync-pin"); -+ return ret; -+ } -+ -+ // check value of pin -+ switch (val) { -+ case MAX96724_FSYNC_PIN_MFP0: -+ case MAX96724_FSYNC_PIN_MFP7: -+ pin = val; -+ break; -+ -+ default: -+ dev_err(dev, "Invalid frame-sync-pin"); -+ return -EINVAL; -+ }; -+ -+ ret = of_property_read_u32(node, "frame-sync-tx-id", &val); -+ if (ret) { -+ dev_err(dev, "Missing property: frame-sync-tx-id"); -+ return -EINVAL; -+ } -+ -+ // check value of frame-sync-tx-id -+ fsync_tx_id = val & 0x1F; -+ if (fsync_tx_id != val) -+ dev_warn(dev, "Truncated frame-sync-tx-id to 5 bits!"); -+ -+ ret = of_property_read_u32(node, "pclk-freq", &pclk_freq); -+ if (ret) { -+ dev_err(dev, "Missing property: pclk-freq"); -+ return -EINVAL; -+ } -+ -+ ret = of_property_read_u32(node, "frame-sync-freq", &fsync_freq); -+ if (ret) { -+ dev_err(dev, "Missing property: frame-sync-freq;"); -+ return -EINVAL; -+ } -+ -+ // Reset register to known state -+ ret = regmap_write(map, MAX96724_FSYNC_15, 0xDF); -+ if (ret) { -+ dev_dbg(dev, "Failed to reset FSYNC state"); -+ return ret; -+ } -+ -+ // Disable AUTO FS links -+ val = MAX9X_FIELD_PREP(MAX96724_FS_GPIO_TYPE_FIELD, MAX96724_FS_GPIO_TYPE_GMSL2) | -+ MAX9X_FIELD_PREP(MAX96724_FS_USE_XTAL_FIELD, true) | -+ MAX9X_FIELD_PREP(MAX96724_AUTO_FS_LINKS_FIELD, 0); -+ // Enable all FS links manually -+ for (i = 0; i < 4; ++i) -+ val |= MAX9X_FIELD_PREP(MAX96724_FS_LINK_FIELD(i), 1); -+ -+ ret = regmap_write(map, MAX96724_FSYNC_15, val); -+ if (ret) { -+ dev_dbg(dev, "Failed to write FSYNC_15"); -+ return ret; -+ } -+ -+ // Calculate value of FSYNC_PERIOD registers -+ // FSYNC_PERIOD = number of pclk cycles per fsync period -+ fsync_period = pclk_freq / fsync_freq; -+ dev_dbg(dev, "Calculated FSYNC_PERIOD: 0x%06x", fsync_period); -+ -+ for (val = MAX96724_FSYNC_5; val <= MAX96724_FSYNC_7; ++val) { -+ ret = regmap_write(map, val, (uint8_t) fsync_period); -+ if (ret) { -+ dev_err(dev, "Failed to write FSYNC_PERIOD registers to 0x%03x", val); -+ return ret; -+ } -+ -+ fsync_period = fsync_period >> 8; -+ } -+ -+ ret = regmap_write(map, MAX96724_FSYNC_17, -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_TX_ID_FIELD, fsync_tx_id) | -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_ERR_THR_FIELD, 0)); -+ if (ret) { -+ dev_err(dev, "Failed to set FSYNC_17"); -+ return ret; -+ } -+ -+ ret = regmap_write(map, MAX96724_FSYNC_0, -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_OUT_PIN_FIELD, pin) | -+ MAX9X_FIELD_PREP(MAX96724_EN_VS_GEN_FIELD, 0) | -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -+ (fsync_master ? MAX96724_FSYNC_GEN_ON_GPIO_OUTPUT : MAX96724_FSYNC_GEN_OFF_GPIO_INPUT)) | -+ MAX9X_FIELD_PREP(MAX96724_FSYNC_METH_FIELD, MAX96724_FSYNC_METHOD_MANUAL)); -+ -+ return 0; -+} -+ -+static int max96724_enable_gpio_frame_sync(struct max9x_common *common) -+{ -+ struct device_node *node = common->dev->of_node; -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ -+ u32 fsync_gpios[MAX96724_NUM_GPIOS]; -+ int num_fsync_gpios; -+ int i, gpio, gpio_tx_val, ret; -+ -+ // Clean up any previous values in the event the chip was not reset -+ // or GPIO forwarding needs to be toggled off -+ dev_dbg(dev, "Setting GPIO registers to default value"); -+ for (i = 0; i < MAX96724_NUM_GPIOS; i++) { -+ // Default values per the datasheet -+ TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), (BIT(7) | BIT(0)))); -+ -+ // Link A register has different fields from Links B, C, D -+ TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), (BIT(7) | BIT(5) | i))); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), i)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), i)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), i)); -+ } -+ -+ // Read DT to find fsync GPIOs -+ ret = of_property_read_variable_u32_array(node, "frame-sync-ports", -+ fsync_gpios, 0, MAX96724_NUM_GPIOS); -+ -+ if (ret == -ENODATA || ret == -EINVAL) { -+ dev_dbg(dev, "No frame sync GPIOs specified in DT"); -+ return 0; -+ } -+ -+ if (ret < 0) { -+ dev_err(dev, "Failed to parse DT frame-sync-ports, error %d", ret); -+ return ret; -+ } -+ -+ num_fsync_gpios = ret; -+ dev_info(dev, "Enabling %d frame sync GPIOs", num_fsync_gpios); -+ -+ // Configure MAX96724 to forward specified GPIOs -+ for (i = 0; i < num_fsync_gpios; i++) { -+ gpio = fsync_gpios[i]; -+ -+ if (gpio >= MAX96724_NUM_GPIOS) { -+ dev_warn(dev, "Skipping invalid GPIO %d in DT", gpio); -+ continue; -+ } -+ -+ // See: MAX96724 Users Guide "Configuring GPIO forwarding" -+ -+ // Enable GPIO for transmission -+ TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), -+ MAX96724_GPIO_RES_CFG | MAX96724_GPIO_TX_ENABLE | MAX96724_GPIO_OUTDRV_DISABLE)); -+ -+ // Configure transmission registers on Links A-D. -+ gpio_tx_val = MAX96724_GPIO_PUSH_PULL | gpio; -+ -+ TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), gpio_tx_val)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), gpio_tx_val)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), gpio_tx_val)); -+ TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), gpio_tx_val)); -+ } -+ -+ return 0; -+} -+ -+static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line) -+{ -+ return max96724_set_line_fault(common, line, false); -+} -+ -+static int max96724_enable_line_fault(struct max9x_common *common, unsigned int line) -+{ -+ return max96724_set_line_fault(common, line, true); -+} -+ -+static int max96724_set_line_fault(struct max9x_common *common, unsigned int line, bool enable) -+{ -+ int ret; -+ -+ ret = regmap_update_bits(common->map, -+ MAX96724_PU_LF, -+ MAX96724_PU_LF_FIELD(line), -+ enable ? 0xFF : 0x00); -+ -+ return ret; -+} -+ -+static int max96724_get_line_fault(struct max9x_common *common, unsigned int line) -+{ -+ unsigned int val = 0; -+ int ret = regmap_read(common->map, MAX96724_LF(line), &val); -+ -+ if (ret < 0) -+ return ret; -+ -+ return MAX96724_LF_VAL(val, line); -+} -+ -+static struct max9x_line_fault_ops max96724_line_fault_ops = { -+ .enable = max96724_enable_line_fault, -+ .disable = max96724_disable_line_fault, -+ .get_status = max96724_get_line_fault, -+}; -+ -+static int max96724_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ int link_id; -+ int ret; -+ -+ dev_dbg(dev, "Enable"); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ ret = max96724_disable_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ } -+ -+ // Apply errata tuning -+ ret = regmap_multi_reg_write(map, max96724_errata_rev1, ARRAY_SIZE(max96724_errata_rev1)); -+ if (ret) -+ return ret; -+ -+ ret = max96724_configure_csi_dphy(common); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int max96724_enable_csi_link(struct max9x_common *common, -+ unsigned int csi_link_id) -+{ -+ return max96724_set_csi_link_enabled(common, csi_link_id, true); -+} -+ -+static int max96724_disable_csi_link(struct max9x_common *common, -+ unsigned int csi_link_id) -+{ -+ return max96724_set_csi_link_enabled(common, csi_link_id, false); -+} -+ -+static struct max9x_csi_link_ops max96724_csi_link_ops = { -+ .enable = max96724_enable_csi_link, -+ .disable = max96724_disable_csi_link, -+}; -+ -+int max96724_get_ops(struct max9x_common_ops **common_ops, struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ (*common_ops) = &max96724_common_ops; -+ (*serial_ops) = &max96724_serial_link_ops; -+ (*csi_ops) = &max96724_csi_link_ops; -+ (*lf_ops) = &max96724_line_fault_ops; -+ (*trans_ops) = NULL; -+ return 0; -+} -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Cody Burrows "); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("He Jiabin "); -+MODULE_DESCRIPTION("Maxim MAX96724 Quad GMSL2/GMSL1 to CSI-2 Deserializer driver"); -diff --git a/drivers/media/i2c/max9x/max96724.h b/drivers/media/i2c/max9x/max96724.h -new file mode 100644 -index 000000000000..5cd1bd289b1f ---- /dev/null -+++ b/drivers/media/i2c/max9x/max96724.h -@@ -0,0 +1,241 @@ -+/* -+ * max96724.h - Maxim MAX96724 registers and constants. -+ * -+ * Copyright (c) 2023-2024, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX96724_H_ -+#define _MAX96724_H_ -+ -+#include -+#include -+#include "serdes.h" -+ -+enum max96724_phy_mode { -+ MAX96724_MIPI_PHY_4X2 = BIT(0), // four 2-lane ports -+ MAX96724_MIPI_PHY_2X4 = BIT(2), // two 4-lane ports (CSI1 is master for port A, CSI2 for port B) -+ MAX96724_MIPI_PHY_1X4A_22 = BIT(3), // one 4-lane (PHY0+PHY1, CSI1 for port A) port and two 2-lane ports -+ MAX96724_MIPI_PHY_1X4B_22 = BIT(4), // one 4-lane (PHY2+PHY3, CSI2 for port B) port and two 2-lane ports -+}; -+ -+// described in detail on page 340 of the datasheet & reg doc -+enum max96724_initial_deskew_width { -+ MAX96724_INIT_DESKEW_1x32UI = 0, -+ MAX96724_INIT_DESKEW_2x32UI, -+ MAX96724_INIT_DESKEW_3x32UI, -+ MAX96724_INIT_DESKEW_4x32UI, -+ MAX96724_INIT_DESKEW_5x32UI, -+ MAX96724_INIT_DESKEW_6x32UI, -+ MAX96724_INIT_DESKEW_7x32UI, -+ MAX96724_INIT_DESKEW_8x32UI, -+ MAX96724_INIT_DESKEW_WIDTH_MAX = MAX96724_INIT_DESKEW_8x32UI, -+}; -+ -+enum max96724_fsync_pin { -+ MAX96724_FSYNC_PIN_MFP0 = 0, -+ MAX96724_FSYNC_PIN_MFP7 = 1, -+}; -+ -+enum max96724_fsync_mode { -+ MAX96724_FSYNC_GEN_ON_GPIO_OFF = 0, -+ MAX96724_FSYNC_GEN_ON_GPIO_OUTPUT, -+ MAX96724_FSYNC_GEN_OFF_GPIO_INPUT, -+ MAX96724_FSYNC_GEN_OFF_GPIO_OFF, -+}; -+ -+enum max96724_fsync_method { -+ MAX96724_FSYNC_METHOD_MANUAL = 0, -+ MAX96724_FSYNC_METHOD_SEMI_AUTO, -+ MAX96724_FSYNC_METHOD_AUTO, -+}; -+ -+enum max96724_fsync_gpio_type { -+ MAX96724_FS_GPIO_TYPE_GMSL1 = 0, -+ MAX96724_FS_GPIO_TYPE_GMSL2, -+}; -+ -+enum max96724_virtual_channel_size { -+ MAX96724_VC_2_BITS = 0, -+ MAX96724_VC_4_BITS = 1, -+}; -+ -+#define MAX96724_NUM_SERIAL_LINKS 4 -+#define MAX96724_NUM_VIDEO_PIPES 4 -+#define MAX96724_NUM_MIPI_MAPS 16 -+#define MAX96724_NUM_CSI_LINKS 4 -+#define MAX96724_NUM_LINE_FAULTS 4 -+// There are 16 GPIO registers and 11 GPIO TX/RX forwarding registers, but only 9 pins exposed -+#define MAX96724_NUM_GPIOS 9 -+ -+#define MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS 200 -+ -+#define MAX96724_DPLL_FREQ_MHZ_MULTIPLE 100 -+ -+#define MAX96724_FLD_OFS(n, bits_per_field, count) (((n) % (count)) * (bits_per_field)) -+#define MAX96724_OFFSET_GENMASK(offset, h, l) GENMASK(offset + h, offset + l) -+ -+#define MAX96724_REM_CC 0x03 -+#define MAX96724_REM_CC_DIS_PORT_FIELD(link, port) BIT(MAX96724_FLD_OFS(link, 2, 4) + (port % 2)) -+#define MAX96724_LINK_CTRL 0x06 -+#define MAX96724_LINK_CTRL_GMSL_FIELD(link) (BIT(link) << 4) -+#define MAX96724_LINK_CTRL_EN_FIELD(link) BIT(link) -+#define MAX96724_PHY_RATE_CTRL(link) (0x10 + ((link) / 2)) -+#define MAX96724_PHY_RATE_CTRL_TX_FIELD(link) (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2)) -+#define MAX96724_PHY_RATE_CTRL_RX_FIELD(link) (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 0)) -+ -+// Link lock registers -+#define MAX96724_PHY_LOCKED(link) ((link) == 0 ? 0x1A : 0x0A + ((link) - 1)) -+#define MAX96724_PHY_LOCKED_FIELD BIT(3) -+#define MAX96724_RESET_ALL 0x13 -+#define MAX96724_RESET_ALL_FIELD BIT(6) -+#define MAX96724_RESET_CTRL 0x18 -+#define MAX96724_RESET_CTRL_FIELD(link) BIT(link) -+#define MAX96724_DEV_REV 0x4C -+#define MAX96724_DEV_REV_FIELD GENMASK(3, 0) -+ -+#define MAX96724_VIDEO_PIPE_SEL(pipe) (0xF0 + ((pipe) / 2)) -+#define MAX96724_VIDEO_PIPE_SEL_LINK_FIELD(link) \ -+ (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 2)) -+#define MAX96724_VIDEO_PIPE_SEL_INPUT_FIELD(link) \ -+ (GENMASK(1, 0) << (MAX96724_FLD_OFS(link, 4, 2) + 0)) -+ -+#define MAX96724_VIDEO_PIPE_EN 0xF4 -+#define MAX96724_VIDEO_PIPE_EN_FIELD(pipe) BIT(pipe) -+#define MAX96724_VIDEO_PIPE_STREAM_SEL_ALL_FIELD BIT(4) -+#define MAX96724_VIDEO_PIPE_LEGACY_MODE 0 -+ -+#define MAX96724_DPLL_FREQ(phy) (0x415 + ((phy) * 3)) -+#define MAX96724_DPLL_FREQ_FIELD GENMASK(4, 0) -+ -+#define MAX96724_MIPI_TX_EXT(pipe) (0x800 + ((pipe) * 0x10)) -+ -+#define MAX96724_MIPI_PHY0 0x8A0 -+#define MAX96724_MIPI_PHY0_MODE_FIELD GENMASK(4, 0) -+#define MAX96724_MIPI_PHY_ENABLE 0x8A2 -+#define MAX96724_MIPI_PHY_ENABLE_FIELD(csi) BIT((csi) + 4) -+#define MAX96724_MIPI_PHY_LANE_MAP(csi) (0x8A3 + (csi) / 2) -+#define MAX96724_MIPI_PHY_LANE_MAP_FIELD(csi, lane) \ -+ (GENMASK(1, 0) << (MAX96724_FLD_OFS(csi, 4, 2) + MAX96724_FLD_OFS(lane, 2, 2))) -+ -+// Note that CSIs and pipes overlap: -+// 0x901 through 0x90A and 0x933 through 0x934 are CSIs, repeated every 0x40 up to 4 times -+// 0x90B through 0x932 are pipes, repeated every 0x40 up to 4 times -+#define MAX96724_MIPI_TX(pipe) (0x900 + ((pipe) * 0x40)) -+#define MAX96724_MIPI_TX_LANE_CNT(csi) (MAX96724_MIPI_TX(csi) + 0x0A) -+#define MAX96724_MIPI_TX_LANE_CNT_FIELD GENMASK(7, 6) -+#define MAX96724_MIPI_TX_CPHY_EN_FIELD BIT(5) -+#define MAX96724_MIPI_TX_VCX_EN_FIELD BIT(4) -+#define MAX96724_MIPI_TX_DESKEW_INIT(csi) (MAX96724_MIPI_TX(csi) + 0x03) -+#define MAX96724_MIPI_TX_DESKEW_INIT_AUTO_EN BIT(7) -+#define MAX96724_MIPI_TX_DESKEW_WIDTH_FIELD GENMASK(2, 0) -+#define MAX96724_MAP_EN_L(pipe) (MAX96724_MIPI_TX(pipe) + 0x0B) -+#define MAX96724_MAP_EN_H(pipe) (MAX96724_MIPI_TX(pipe) + 0x0C) -+#define MAX96724_MAP_EN_FIELD GENMASK(7, 0) -+#define MAX96724_MAP_SRC_L(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x0D + ((map) * 2)) -+#define MAX96724_MAP_SRC_L_VC_FIELD GENMASK(7, 6) -+#define MAX96724_MAP_SRC_L_DT_FIELD GENMASK(5, 0) -+#define MAX96724_MAP_DST_L(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x0D + ((map) * 2) + 1) -+#define MAX96724_MAP_DST_L_VC_FIELD GENMASK(7, 6) -+#define MAX96724_MAP_DST_L_DT_FIELD GENMASK(5, 0) -+#define MAX96724_MAP_SRCDST_H(pipe, map) (MAX96724_MIPI_TX_EXT(pipe) + (map)) -+#define MAX96724_MAP_SRCDST_H_SRC_VC_FIELD GENMASK(7, 5) -+#define MAX96724_MAP_SRCDST_H_DST_VC_FIELD GENMASK(4, 2) -+#define MAX96724_MAP_DPHY_DEST(pipe, map) (MAX96724_MIPI_TX(pipe) + 0x2D + ((map) / 4)) -+#define MAX96724_MAP_DPHY_DEST_FIELD(map) (GENMASK(1, 0) << MAX96724_FLD_OFS(map, 2, 4)) -+ -+#define MAX96724_MIPI_TX_ALT_MEM(csi) (MAX96724_MIPI_TX(csi) + 0x33) -+#define MAX96724_MIPI_TX_ALT_MEM_FIELD GENMASK(2, 0) -+#define MAX96724_MIPI_TX_ALT_MEM_8BPP BIT(1) -+#define MAX96724_MIPI_TX_ALT_MEM_10BPP BIT(2) -+#define MAX96724_MIPI_TX_ALT_MEM_12BPP BIT(0) -+ -+#define MAX96724_VID_TX(pipe) (0x100 + (0x12 * (pipe))) -+ -+// GPIO Registers -+#define MAX96724_GPIO_REG(n) (0x300 + 3 * n) -+#define MAX96724_GPIO_RES_CFG BIT(7) -+#define MAX96724_GPIO_TX_ENABLE BIT(1) -+#define MAX96724_GPIO_OUTDRV_DISABLE BIT(0) -+ -+#define MAX96724_GPIO_A_REG(n) (MAX96724_GPIO_REG(n) + 1) -+ -+#define MAX96724_GPIO_B_REG(n) \ -+ ((n <= 2) ? (0x337 + 3 * n) : (n <= 7) ? (0x341 + 3 * (n - 3)) \ -+ : (0x351 + 3 * (n - 8))) -+ -+#define MAX96724_GPIO_C_REG(n) \ -+ ((n == 0) ? (0x36D) : (n <= 5) ? (0x371 + 3 * (n - 1)) \ -+ : (0x381 + 3 * (n - 6))) -+ -+#define MAX96724_GPIO_D_REG(n) \ -+ ((n <= 3) ? (0x3A4 + 3 * n) : (n <= 8) ? (0x3B1 + 3 * (n - 4)) \ -+ : (0x3C1 + 3 * (n - 9))) -+ -+#define MAX96724_GPIO_PUSH_PULL BIT(5) -+ -+// Native frame sync registers -+#define MAX96724_FSYNC_0 (0x4A0) -+#define MAX96724_FSYNC_OUT_PIN_FIELD BIT(5) -+#define MAX96724_EN_VS_GEN_FIELD BIT(6) -+#define MAX96724_FSYNC_MODE_FIELD GENMASK(3, 2) -+#define MAX96724_FSYNC_METH_FIELD GENMASK(1, 0) -+ -+#define MAX96724_FSYNC_5 (0x4A5) -+#define MAX96724_FSYNC_PERIOD_L_FIELD GENMASK(7, 0) -+#define MAX96724_FSYNC_6 (0x4A6) -+#define MAX96724_FSYNC_PERIOD_M_FIELD GENMASK(7, 0) -+#define MAX96724_FSYNC_7 (0x4A7) -+#define MAX96724_FSYNC_PERIOD_H_FIELD GENMASK(7, 0) -+ -+#define MAX96724_FSYNC_15 (0x4AF) -+#define MAX96724_FS_GPIO_TYPE_FIELD BIT(7) -+#define MAX96724_FS_USE_XTAL_FIELD BIT(6) -+#define MAX96724_AUTO_FS_LINKS_FIELD BIT(4) -+#define MAX96724_FS_LINK_FIELD(link) BIT(link) -+ -+#define MAX96724_FSYNC_17 (0x4B1) -+#define MAX96724_FSYNC_TX_ID_FIELD GENMASK(7, 3) -+#define MAX96724_FSYNC_ERR_THR_FIELD GENMASK(2, 0) -+ -+// Line fault status registers -+#define MAX96724_LF(link) (0xE1 + ((link) / 2)) -+#define MAX96724_LF_VAL(val, link) ((val >> ((link % 2) * 4)) & GENMASK(2, 0)) -+ -+#define MAX96724_PU_LF (0xE0) -+#define MAX96724_PU_LF_FIELD(line) BIT(line) -+ -+static struct max9x_serdes_rate_table max96724_rx_rates[] = { -+ { .val = 1, .freq_mhz = 3000 }, // 3 Gbps -+ { .val = 2, .freq_mhz = 6000 }, // 6 Gbps -+}; -+ -+static struct max9x_serdes_rate_table max96724_tx_rates[] = { -+ { .val = 0, .freq_mhz = 187 }, // 187.5 Mbps -+}; -+ -+// See: Errata for MAX96724/MAX96724F/MAX96724R (Rev. 1) document -+// https://www.analog.com/media/en/technical-documentation/data-sheets/max96724-f-r-rev1-b-0a-errata.pdf -+static const struct reg_sequence max96724_errata_rev1[] = { -+ // Errata #5 - GMSL2 Link requires register writes for robust 6 Gbps operation -+ { 0x1449, 0x75, }, -+ { 0x1549, 0x75, }, -+ { 0x1649, 0x75, }, -+ { 0x1749, 0x75, }, -+}; -+ -+#endif /* _MAX96724_H_ */ -+ -diff --git a/drivers/media/i2c/max9x/max9x_pdata.h b/drivers/media/i2c/max9x/max9x_pdata.h -new file mode 100644 -index 000000000000..874e7412315f ---- /dev/null -+++ b/drivers/media/i2c/max9x/max9x_pdata.h -@@ -0,0 +1,103 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _MAX9X_PDATA_H_ -+#define _MAX9X_PDATA_H_ -+ -+#include -+#include -+ -+struct max9x_common; -+ -+enum max9x_serdes_link_type { -+ MAX9X_LINK_TYPE_GMSL1 = 0, -+ MAX9X_LINK_TYPE_GMSL2, -+ MAX9X_LINK_TYPE_FPDLINK, -+}; -+ -+struct max9x_subdev_pdata { -+ unsigned int serial_link_id; // DES identify GMSL link/pipe id -+ struct i2c_board_info board_info; -+ unsigned short phys_addr; // Remap or translate subdev -+}; -+ -+struct max9x_serial_link_pdata { -+ unsigned int link_id; -+ enum max9x_serdes_link_type link_type; -+ unsigned int rx_freq_mhz; -+ unsigned int tx_freq_mhz; -+ char poc_regulator[32]; -+ -+ // SER -+ struct i2c_client *des_client; -+ unsigned int des_link_id; -+}; -+ -+struct max9x_serdes_mipi_map { -+ u16 src_vc; -+ u16 src_dt; -+ u16 dst_vc; -+ u16 dst_dt; -+ u16 dst_csi; -+}; -+ -+struct max9x_video_pipe_pdata { -+ unsigned int serial_link_id; // DES: src-link, SER: dst-link -+ unsigned int pipe_id; // X, Y, Z, U -+ -+ // DES -+ struct max9x_serdes_mipi_map *maps; -+ unsigned int num_maps; -+ unsigned int src_pipe_id; -+ -+ // SER -+ unsigned int src_csi_id; -+ unsigned int *data_types; -+ unsigned int num_data_types; -+}; -+ -+struct max9x_serdes_phy_map { -+ u8 int_csi; // Internal CSI lane -+ u8 phy_ind; // PHY index -+ u8 phy_lane; // PHY lane index -+}; -+ -+struct max9x_csi_link_pdata { -+ unsigned int link_id; -+ enum v4l2_mbus_type bus_type; -+ unsigned int num_lanes; -+ struct max9x_serdes_phy_map *maps; -+ unsigned int num_maps; -+ unsigned int tx_rate_mbps; -+ bool auto_initial_deskew; -+ unsigned int initial_deskew_width; -+ bool auto_start; -+}; -+ -+struct max9x_gpio_pdata { -+ const char *label; -+ const char *const *names; -+}; -+ -+struct max9x_pdata { -+ unsigned short phys_addr; // Remap self -+ char suffix[5]; -+ -+ struct max9x_subdev_pdata *subdevs; // DES: the serializers, 1 per link; SER: sensor, eeprom, etc -+ unsigned int num_subdevs; -+ -+ struct max9x_serial_link_pdata *serial_links; // DES only. SER is currently presumed to have only 1 active link -+ unsigned int num_serial_links; -+ -+ struct max9x_video_pipe_pdata *video_pipes; -+ unsigned int num_video_pipes; -+ -+ struct max9x_csi_link_pdata *csi_links; -+ unsigned int num_csi_links; -+ -+ struct max9x_gpio_pdata gpio; -+ -+ bool external_refclk_enable; -+}; -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -new file mode 100644 -index 000000000000..633c55504273 ---- /dev/null -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -0,0 +1,2750 @@ -+/* -+ * serdes.c -+ * -+ * Copyright (c) 2018-2020 D3 Engineering. All rights reserved. -+ * Copyright (c) 2023, Define Design Deploy Corp. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2025 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "serdes.h" -+ -+static const s64 max9x_op_sys_clock[] = { -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(2000), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1900), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1800), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1700), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1600), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(1000), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(900), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(800), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(700), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(600), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(500), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(400), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(300), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(200), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(100), -+ MAX9X_LINK_FREQ_MBPS_TO_HZ(80), -+}; -+ -+int max9x_get_ops(char dev_id, -+ struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops) -+{ -+ int rval = 0; -+ -+ switch (dev_id) { -+ case MAX9296: -+ rval = max9296_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ case MAX96724: -+ rval = max96724_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ case MAX9295: -+ rval = max9295_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ case MAX96717: -+ rval = max96717_get_ops(common_ops, serial_ops, csi_ops, lf_ops, trans_ops); -+ break; -+ default: -+ break; -+ } -+ -+ return rval; -+} -+ -+static struct max9x_desc max9x_chips[] = { -+ [MAX9296] = { -+ .dev_id = 0x94, -+ .rev_reg = 0xE, -+ .serdes_type = MAX9X_DESERIALIZER, -+ .chip_type = MAX9296, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+ [MAX96724] = { -+ .dev_id = 0xA2, -+ .rev_reg = 0x4C, -+ .serdes_type = MAX9X_DESERIALIZER, -+ .chip_type = MAX96724, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+ [MAX9295] = { -+ .dev_id = 0x91, -+ .rev_reg = 0xE, -+ .serdes_type = MAX9X_SERIALIZER, -+ .chip_type = MAX9295, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+ /*need to check dev_id and others when used*/ -+ [MAX96717] = { -+ .dev_id = 0x91, -+ .rev_reg = 0xE, -+ .serdes_type = MAX9X_SERIALIZER, -+ .chip_type = MAX96717, -+ .get_max9x_ops = max9x_get_ops, -+ }, -+}; -+ -+static const struct of_device_id max9x_of_match[] = { -+ { .compatible = "max9x,max9296", .data = &max9x_chips[MAX9296] }, -+ { .compatible = "max9x,max96724", .data = &max9x_chips[MAX96724] }, -+ { .compatible = "max9x,max9295", .data = &max9x_chips[MAX9295] }, -+ { .compatible = "max9x,max96717", .data = &max9x_chips[MAX96717] }, -+ {} -+}; -+MODULE_DEVICE_TABLE(of, max9x_of_match); -+ -+static const struct i2c_device_id max9x_id[] = { -+ { "max9x", MAX9296 }, -+ { "max9296", MAX9296 }, -+ { "max96724", MAX96724 }, -+ { "max9295", MAX9295 }, -+ { "max96717", MAX96717 }, -+ { } -+}; -+MODULE_DEVICE_TABLE(i2c, max9x_id); -+ -+typedef int (*max9x_serdes_parse_child_func)(struct max9x_common *common, struct device_node *node); -+ -+static int max9x_soft_reset(struct max9x_common *common); -+static int max9x_remap_addr(struct max9x_common *common); -+static int max9x_setup_gpio(struct max9x_common *common); -+static int max9x_enable(struct max9x_common *common); -+static int max9x_disable(struct max9x_common *common); -+static int max9x_verify_devid(struct max9x_common *common); -+ -+static int max9x_enable_resume(struct max9x_common *common); -+static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned int link_id); -+static int max9x_create_adapters_resume(struct max9x_common *common); -+ -+static int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id); -+static int max9x_create_adapters(struct max9x_common *common); -+static int max9x_csi_link_to_pad(struct max9x_common *common, int csi_id); -+static int max9x_serial_link_to_pad(struct max9x_common *common, int link_id); -+static int max9x_register_v4l_subdev(struct max9x_common *common); -+static int max9x_enable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_sysfs_create_get_link(struct max9x_common *common, unsigned int link_id); -+static void max9x_sysfs_destroy_get_link(struct max9x_common *common, unsigned int link_id); -+ -+static int max9x_enable_line_faults(struct max9x_common *common); -+static int max9x_disable_line_faults(struct max9x_common *common); -+static void max9x_sysfs_destroy_line_fault_status(struct max9x_common *common, unsigned int line); -+ -+static int max9x_parse_pdata(struct max9x_common *common, struct max9x_pdata *pdata); -+static int max9x_parse_serial_link_pdata(struct max9x_common *common, -+ struct max9x_serial_link_pdata *max9x_serial_link_pdata); -+static int max9x_parse_video_pipe_pdata(struct max9x_common *common, -+ struct max9x_video_pipe_pdata *video_pipe_pdata); -+static int max9x_parse_csi_link_pdata(struct max9x_common *common, struct max9x_csi_link_pdata *csi_link_pdata); -+static int max9x_parse_subdev_pdata(struct max9x_common *common, struct max9x_subdev_pdata *subdev_pdata); -+ -+static int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id); -+static int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id); -+ -+static int max9x_des_isolate_serial_link(struct max9x_common *common, unsigned int link_id); -+static int max9x_des_deisolate_serial_link(struct max9x_common *common, unsigned int link_id); -+ -+static ssize_t max9x_link_status_show(struct device *dev, struct device_attribute *attr, char *buf); -+ -+static int max9x_setup_translations(struct max9x_common *common); -+static int max9x_disable_translations(struct max9x_common *common); -+ -+#define MAX9X_ALLOCATE_ELEMENTS(common, element_type, elements, num_elements) ({ \ -+ struct device *dev = common->dev; \ -+ int allocate_ret = 0; \ -+ (common)->num_elements = 0; \ -+ (common)->elements = NULL; \ -+ if ((common)->common_ops && (common)->common_ops->max_elements) { \ -+ (common)->num_elements = (common)->common_ops->max_elements((common), element_type); \ -+ if ((common)->num_elements > 0) { \ -+ (common)->elements = devm_kzalloc((common)->dev, \ -+ (common)->num_elements * sizeof(typeof(*((common)->elements))), GFP_KERNEL); \ -+ if (!(common)->elements) { \ -+ dev_err(dev, "Failed to allocated memory for " # element_type); \ -+ allocate_ret = -ENOMEM; \ -+ } \ -+ } \ -+ } \ -+ allocate_ret; \ -+}) -+ -+static struct max9x_pdata *pdata_ser(struct device *dev, struct max9x_subdev_pdata *sdinfo, const char *name, -+ unsigned int phys_addr, unsigned int virt_addr) -+{ -+ struct max9x_pdata *pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -+ -+ dev_info(dev, "ser %s phys %02x virt %02x\n", name, phys_addr, virt_addr); -+ -+ sdinfo->board_info.platform_data = pdata; -+ strscpy(sdinfo->board_info.type, name, I2C_NAME_SIZE); -+ sdinfo->board_info.addr = virt_addr; -+ sdinfo->phys_addr = pdata->phys_addr = phys_addr; -+ -+ return pdata; -+} -+ -+static struct max9x_pdata *pdata_sensor(struct device *dev, struct max9x_subdev_pdata *sdinfo, const char *name, -+ unsigned int phys_addr, unsigned int virt_addr) -+{ -+ dev_info(dev, "sen %s phys %02x virt %02x\n", name, phys_addr, virt_addr); -+ -+ strscpy(sdinfo->board_info.type, name, I2C_NAME_SIZE); -+ sdinfo->board_info.addr = virt_addr; -+ sdinfo->phys_addr = phys_addr; -+ -+ return NULL; -+} -+ -+static struct max9x_pdata *parse_ser_pdata(struct device *dev, const char *ser_name, char *suffix, -+ unsigned int ser_nlanes, unsigned int phys_addr, -+ unsigned int virt_addr, struct max9x_subdev_pdata *ser_sdinfo, -+ unsigned int sensor_dt) -+{ -+ struct max9x_pdata *ser_pdata = pdata_ser(dev, ser_sdinfo, ser_name, phys_addr, virt_addr); -+ struct max9x_serial_link_pdata *ser_serial_link; -+ struct max9x_video_pipe_pdata *ser_video_pipe; -+ -+ snprintf(ser_pdata->suffix, sizeof(ser_pdata->suffix), "%s", suffix); -+ -+ ser_pdata->num_serial_links = 1; -+ ser_pdata->serial_links = devm_kzalloc(dev, ser_pdata->num_serial_links * sizeof(*ser_pdata->serial_links), -+ GFP_KERNEL); -+ -+ ser_serial_link = &ser_pdata->serial_links[0]; -+ ser_serial_link->link_id = 0; -+ ser_serial_link->link_type = MAX9X_LINK_TYPE_GMSL2; -+ ser_serial_link->rx_freq_mhz = 6000; -+ ser_serial_link->tx_freq_mhz = 187; -+ -+ ser_pdata->num_video_pipes = 1; -+ ser_pdata->video_pipes = devm_kzalloc(dev, -+ ser_pdata->num_video_pipes * sizeof(*ser_pdata->video_pipes), GFP_KERNEL); -+ -+ ser_video_pipe = &ser_pdata->video_pipes[0]; -+ ser_video_pipe->serial_link_id = 0; -+ ser_video_pipe->pipe_id = ser_sdinfo->serial_link_id; -+ ser_video_pipe->src_csi_id = 1; /* PHY B typically */ -+ -+ ser_video_pipe->num_data_types = 1; -+ ser_video_pipe->data_types = devm_kzalloc(dev, -+ ser_video_pipe->num_data_types * sizeof(*ser_video_pipe->data_types), GFP_KERNEL); -+ ser_video_pipe->data_types[0] = sensor_dt; -+ -+ ser_pdata->num_csi_links = 1; -+ ser_pdata->csi_links = devm_kzalloc(dev, ser_pdata->num_csi_links * sizeof(*ser_pdata->csi_links), GFP_KERNEL); -+ -+ struct max9x_csi_link_pdata *csi_link = &ser_pdata->csi_links[0]; -+ -+ csi_link->link_id = 1; -+ csi_link->num_lanes = ser_nlanes; -+ -+ return ser_pdata; -+} -+ -+static void parse_sensor_pdata(struct device *dev, const char *sensor_name, char *suffix, unsigned int ser_nlanes, -+ unsigned int phys_addr, unsigned int virt_addr, struct max9x_subdev_pdata *ser_sdinfo, -+ struct max9x_pdata *ser_pdata) -+{ -+ ser_pdata->num_subdevs = 1; -+ ser_pdata->subdevs = devm_kzalloc(dev, ser_pdata->num_subdevs * sizeof(*ser_pdata->subdevs), GFP_KERNEL); -+ pdata_sensor(dev, &ser_pdata->subdevs[0], sensor_name, phys_addr, virt_addr); -+ -+ /* NOTE: i2c_dev_set_name() will prepend "i2c-" to this name */ -+ char *dev_name = devm_kzalloc(dev, I2C_NAME_SIZE, GFP_KERNEL); -+ -+ snprintf(dev_name, I2C_NAME_SIZE, "%s %s", sensor_name, suffix); -+ ser_pdata->subdevs[0].board_info.dev_name = dev_name; -+ -+ struct isx031_platform_data *sen_pdata = devm_kzalloc(dev, sizeof(*sen_pdata), GFP_KERNEL); -+ -+ if (!sen_pdata) -+ return; -+ -+ ser_pdata->subdevs[0].board_info.platform_data = sen_pdata; -+ sen_pdata->lanes = ser_nlanes; -+ sen_pdata->irq_pin_flags = 1; //workaround for identify D3. -+ snprintf(sen_pdata->suffix, sizeof(sen_pdata->suffix), "%s", suffix); -+} -+ -+static void *parse_serdes_pdata(struct device *dev) -+{ -+ /* -+ * Assumptions: -+ * - All ports have same model of sensor -+ * - Single-port usage will always be port 0, never port 1 -+ * - Serializer always uses serial link 0 -+ */ -+ struct serdes_platform_data *serdes_pdata = dev->platform_data; -+ unsigned int num_ports = serdes_pdata->subdev_num; -+ unsigned int csi_port = (serdes_pdata->des_port / 90); -+ struct max9x_pdata *des_pdata = devm_kzalloc(dev, sizeof(*des_pdata), GFP_KERNEL); -+ -+ snprintf(des_pdata->suffix, sizeof(des_pdata->suffix), "%c", serdes_pdata->suffix); -+ des_pdata->num_serial_links = num_ports; -+ des_pdata->serial_links = devm_kzalloc(dev, -+ des_pdata->num_serial_links * sizeof(*des_pdata->serial_links), GFP_KERNEL); -+ -+ des_pdata->num_subdevs = num_ports; -+ des_pdata->subdevs = devm_kzalloc(dev, des_pdata->num_subdevs * sizeof(*des_pdata->subdevs), GFP_KERNEL); -+ -+ des_pdata->num_video_pipes = num_ports; -+ des_pdata->video_pipes = devm_kzalloc(dev, -+ des_pdata->num_video_pipes * sizeof(*des_pdata->video_pipes), GFP_KERNEL); -+ -+ for (unsigned int serial_link_id = 0; serial_link_id < des_pdata->num_serial_links; serial_link_id++) { -+ struct max9x_serial_link_pdata *serial_link = &des_pdata->serial_links[serial_link_id]; -+ unsigned int video_pipe_id = serial_link_id; -+ struct serdes_subdev_info *serdes_sdinfo = &serdes_pdata->subdev_info[serial_link_id]; -+ struct max9x_video_pipe_pdata *des_video_pipe = &des_pdata->video_pipes[video_pipe_id]; -+ struct max9x_subdev_pdata *ser_sdinfo = &des_pdata->subdevs[serial_link_id]; -+ const char *ser_name = serdes_pdata->ser_name; -+ const char *sensor_name = serdes_sdinfo->board_info.type; -+ unsigned int ser_alias = serdes_sdinfo->ser_alias; -+ unsigned int sensor_alias = serdes_sdinfo->board_info.addr; -+ unsigned int ser_phys_addr = serdes_sdinfo->ser_phys_addr; -+ unsigned int sensor_phys_addr = serdes_sdinfo->phy_i2c_addr; -+ unsigned int lanes = serdes_pdata->ser_nlanes; -+ unsigned int dt = serdes_sdinfo->sensor_dt; -+ -+ serial_link->link_id = serial_link_id; -+ serial_link->link_type = MAX9X_LINK_TYPE_GMSL2; -+ serial_link->rx_freq_mhz = 6000; -+ serial_link->tx_freq_mhz = 187; -+ -+ des_video_pipe->serial_link_id = serial_link_id; -+ des_video_pipe->pipe_id = video_pipe_id; -+ des_video_pipe->src_pipe_id = video_pipe_id; -+ des_video_pipe->num_maps = 3; -+ des_video_pipe->maps = devm_kzalloc(dev, -+ des_video_pipe->num_maps * sizeof(*des_video_pipe->maps), GFP_KERNEL); -+ -+ ser_sdinfo->serial_link_id = serial_link_id; -+ -+ SET_CSI_MAP(des_video_pipe->maps, 0, 0, 0x00, video_pipe_id, 0x00, csi_port); -+ SET_CSI_MAP(des_video_pipe->maps, 1, 0, 0x01, video_pipe_id, 0x01, csi_port); -+ SET_CSI_MAP(des_video_pipe->maps, 2, 0, dt, video_pipe_id, dt, csi_port); /* YUV422 8-bit */ -+ -+ struct max9x_pdata *ser_pdata = parse_ser_pdata(dev, ser_name, serdes_sdinfo->suffix, lanes, -+ ser_phys_addr, ser_alias, ser_sdinfo, dt); -+ -+ parse_sensor_pdata(dev, sensor_name, serdes_sdinfo->suffix, lanes, sensor_phys_addr, sensor_alias, -+ ser_sdinfo, ser_pdata); -+ -+ } -+ -+ des_pdata->num_csi_links = 1; -+ des_pdata->csi_links = devm_kzalloc(dev, des_pdata->num_csi_links * sizeof(*des_pdata->csi_links), GFP_KERNEL); -+ -+ do { -+ struct max9x_csi_link_pdata *csi_link = &des_pdata->csi_links[0]; -+ -+ csi_link->link_id = csi_port; -+ csi_link->bus_type = serdes_pdata->bus_type; -+ csi_link->num_lanes = serdes_pdata->deser_nlanes; -+ csi_link->tx_rate_mbps = 2000; -+ csi_link->auto_initial_deskew = true; -+ csi_link->initial_deskew_width = 7; -+ csi_link->auto_start = false; -+ csi_link->num_maps = 2; -+ csi_link->maps = devm_kzalloc(dev, csi_link->num_maps * sizeof(*csi_link->maps), GFP_KERNEL); -+ if (csi_port == 1) { -+ SET_PHY_MAP(csi_link->maps, 0, 0, 1, 0); /* 0 (DA0) -> PHY1.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 1, 1, 1); /* 1 (DA1) -> PHY1.1 */ -+ } else if (csi_port == 2) { -+ SET_PHY_MAP(csi_link->maps, 0, 2, 2, 0); /* 0 (DA0) -> PHY2.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 2, 2, 1); /* 1 (DA1) -> PHY2.1 */ -+ } -+ } while (0); -+ -+ return des_pdata; -+} -+ -+static int regmap_read_retry(struct regmap *map, unsigned int reg, -+ unsigned int *val) -+{ -+ int ret = 0; -+ int i = 0; -+ -+ for (i = 0; i < 50; i++) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+static int max9x_enable_resume(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (common->regulator_enabled) -+ return 0; -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_enable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ common->regulator_enabled = true; -+ } -+ -+ if (common->common_ops && common->common_ops->enable) { -+ ret = common->common_ops->enable(common); -+ if (ret) -+ goto err; -+ } -+ -+ return 0; -+ -+err: -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (common->regulator_enabled && !IS_ERR_OR_NULL(common->vdd_regulator)) -+ regulator_disable(common->vdd_regulator); -+ -+ common->regulator_enabled = false; -+ -+ return ret; -+} -+ -+static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[link_id]; -+ unsigned int phys_addr, virt_addr; -+ struct i2c_client *phys_client; -+ struct regmap *phys_map, *virt_map; -+ unsigned int val; -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (!serial_link->remote.pdata) -+ return 0; -+ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ phys_addr = serial_link->remote.pdata->phys_addr; -+ virt_addr = serial_link->remote.pdata->board_info.addr; -+ if (phys_addr == virt_addr) -+ return 0; -+ -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ -+ phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(phys_client)) { -+ dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ ret = PTR_ERR(phys_client); -+ return ret; -+ } -+ -+ phys_map = regmap_init_i2c(phys_client, ®map_config); -+ if (IS_ERR_OR_NULL(phys_map)) { -+ dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ ret = PTR_ERR(phys_map); -+ goto err_client; -+ } -+ -+ struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); -+ -+ virt_map = ser_common->map; -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (!ret) { -+ dev_info(common->dev, "Remap not necessary"); -+ ret = 0; -+ goto err_regmap; -+ } -+ -+ ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ goto err_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ } -+ -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ if (ret) { -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ phys_addr, virt_addr, ret); -+ goto err_regmap; -+ } -+ -+ msleep(100); -+ -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ goto err_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ } -+ -+err_regmap: -+ regmap_exit(phys_map); -+err_client: -+ i2c_unregister_device(phys_client); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ -+ return ret; -+} -+ -+static int max9x_create_adapters_resume(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int link_id; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ int err = 0; -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ dev_info(dev, "RESUME: Serial-link %d: %senabled", link_id, -+ (common->serial_link[link_id].enabled ? "" : "not ")); -+ -+ if (!common->serial_link[link_id].enabled) -+ continue; -+ -+ /* This exponential retry works around a current problem in the locking code. */ -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ err = max9x_enable_serial_link(common, link_id); -+ if (!err) -+ break; -+ -+ dev_warn(dev, -+ "enable link %d failed, trying again (waiting %d ms)", -+ link_id, ms); -+ msleep(ms); -+ } -+ if (ms > RETRY_MS_MAX) { -+ dev_err(dev, "failed to enable link %d after multiple retries", -+ link_id); -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ } -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) -+ max9x_setup_translations(common); -+ -+ return 0; -+} -+ -+int max9x_common_resume(struct max9x_common *common) -+{ -+ struct max9x_common *des_common = NULL; -+ struct device *dev = common->dev; -+ u32 des_link; -+ u32 phys_addr, virt_addr; -+ int ret = 0; -+ int retry = 50; -+ -+ if (dev->platform_data) { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ return ret; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_reset_serializer; -+ } -+ } -+ } -+ -+ while (retry--) { -+ ret = max9x_verify_devid(common); -+ if (ret) -+ msleep(100); -+ else -+ break; -+ } -+ if (ret) { -+ dev_err(dev, "can't get devid after resume"); -+ goto err_deisolate; -+ } -+ -+ ret = max9x_enable_resume(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable"); -+ goto err_disable; -+ } -+ -+ ret = max9x_create_adapters_resume(common); -+ if (ret) { -+ dev_err(dev, "Failed to create adapters"); -+ goto err_disable; -+ } -+ -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ -+ return 0; -+ -+err_disable: -+ max9x_disable(common); -+ -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ -+err_reset_serializer: -+ if (common->type == MAX9X_SERIALIZER) { -+ if (common->common_ops && common->common_ops->remap_reset) { -+ ret = common->common_ops->remap_reset(common); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return ret; -+} -+ -+int max9x_common_suspend(struct max9x_common *common) -+{ -+ unsigned int link_id; -+ -+ dev_dbg(common->dev, "try to suspend"); -+ -+ max9x_disable_translations(common); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) -+ max9x_disable_serial_link(common, link_id); -+ -+ max9x_disable(common); -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (dev->platform_data) { -+ if (common->common_ops && common->common_ops->remap_reset) { -+ ret = common->common_ops->remap_reset(common); -+ if (ret) -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+int max9x_common_init_i2c_client(struct max9x_common *common, -+ struct i2c_client *client, -+ const struct regmap_config *regmap_config, -+ struct max9x_common_ops *common_ops, -+ struct max9x_serial_link_ops *serial_link_ops, -+ struct max9x_csi_link_ops *csi_link_ops, -+ struct max9x_line_fault_ops *lf_ops) -+{ -+ struct device *dev = &client->dev; -+ struct i2c_adapter *adap = to_i2c_adapter(dev->parent); -+ struct max9x_pdata *pdata = NULL; -+ u32 phys_addr, virt_addr; -+ int ret; -+ -+ common->dev = dev; -+ common->client = client; -+ -+ /* If no GPIO is found this will return NULL, and will not error */ -+ common->reset_gpio = devm_gpiod_get_optional(dev, MAX9X_RESET_GPIO_NAME, GPIOD_OUT_HIGH); -+ if (IS_ERR(common->reset_gpio)) { -+ dev_err(dev, "gpiod_get failed with error: %ld", PTR_ERR(common->reset_gpio)); -+ return PTR_ERR(common->reset_gpio); -+ } -+ -+ common->vdd_regulator = devm_regulator_get_optional(dev, MAX9X_VDD_REGULATOR_NAME); -+ if (IS_ERR_OR_NULL(common->vdd_regulator)) -+ dev_info(dev, "Missing VDD regulator"); -+ -+ common->map = devm_regmap_init_i2c(client, regmap_config); -+ if (IS_ERR_OR_NULL(common->map)) { -+ dev_err(dev, "Failed to create regmap."); -+ return PTR_ERR(common->map); -+ } -+ -+ if (dev->platform_data) { -+ pdata = dev->platform_data; -+ /* -+ * for serializer, the i2c phys_addr supposed to be updated as virt_addr -+ * when deserializer do max9x_remap_serializer, -+ * check here to avoid failure when directly read chipid from virt_addr -+ */ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (phys_addr != virt_addr) { -+ common->phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(common->phys_client)) { -+ dev_err(dev, "Failed to create dummy device for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ -+ common->phys_map = regmap_init_i2c(common->phys_client, regmap_config); -+ if (IS_ERR_OR_NULL(common->phys_map)) { -+ dev_err(dev, "Failed to create dummy device regmap for phys_addr"); -+ ret = PTR_ERR(common->phys_client); -+ goto enable_err; -+ } -+ } -+ } -+ -+ ret = max9x_verify_devid(common); -+ if (ret) -+ return ret; -+ -+ ret = common->des->get_max9x_ops(common->des->chip_type, &(common->common_ops), -+ &(common->serial_link_ops), &(common->csi_link_ops), &(common->line_fault_ops), -+ &(common->translation_ops)); -+ if (ret) { -+ dev_err(dev, "Failed to get ops."); -+ return ret; -+ } -+ -+ mutex_init(&common->link_mutex); -+ mutex_init(&common->isolate_mutex); -+ common->isolated_link = -1; -+ common->selected_link = -1; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_CSI_LINK, csi_link, num_csi_links); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_VIDEO_PIPE, video_pipe, num_video_pipes); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_SERIAL_LINK, serial_link, num_serial_links); -+ if (ret) -+ return ret; -+ -+ ret = MAX9X_ALLOCATE_ELEMENTS(common, MAX9X_LINE_FAULT, line_fault, num_line_faults); -+ if (ret) -+ return ret; -+ -+ common->muxc = i2c_mux_alloc( -+ adap, dev, -+ common->num_serial_links, -+ 0, -+ I2C_MUX_LOCKED, -+ max9x_select_i2c_chan, -+ max9x_deselect_i2c_chan); -+ if (IS_ERR_OR_NULL(common->muxc)) { -+ dev_err(dev, "Failed to allocate mux core"); -+ return -ENOMEM; -+ } -+ common->muxc->priv = common; -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ dev->platform_data = parse_serdes_pdata(dev); -+ -+ if (dev->platform_data) { -+ pdata = dev->platform_data; -+ -+ dev_dbg(dev, "Parse pdata"); -+ -+ ret = max9x_parse_pdata(common, pdata); -+ if (ret) -+ return ret; -+ } -+ -+ dev_dbg(dev, "Enable"); -+ -+ ret = max9x_enable(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable"); -+ goto enable_err; -+ } -+ -+enable_err: -+ if (common->phys_map) { -+ regmap_exit(common->phys_map); -+ common->phys_map = NULL; -+ } -+ -+ if (common->phys_client) { -+ i2c_unregister_device(common->phys_client); -+ common->phys_client = NULL; -+ } -+ -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "Enable gpio"); -+ ret = max9x_setup_gpio(common); -+ if (ret) -+ goto err_enable; -+ -+ dev_dbg(dev, "Enable line faults"); -+ -+ ret = max9x_enable_line_faults(common); -+ if (ret) { -+ dev_err(dev, "Failed to enable line faults"); -+ goto err_enable; -+ } -+ -+ dev_dbg(dev, "Enable links"); -+ -+ ret = max9x_create_adapters(common); -+ if (ret) -+ goto err_enable; -+ -+ ret = max9x_register_v4l_subdev(common); -+ if (ret) -+ goto err_adapters; -+ -+ return 0; -+ -+err_adapters: -+ i2c_mux_del_adapters(common->muxc); -+ -+err_enable: -+ max9x_disable(common); -+ -+ return ret; -+} -+EXPORT_SYMBOL(max9x_common_init_i2c_client); -+ -+void max9x_destroy(struct max9x_common *common) -+{ -+ unsigned int link_id; -+ unsigned int line; -+ -+ dev_dbg(common->dev, "Destroy"); -+ -+ max9x_disable_translations(common); -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ max9x_disable_serial_link(common, link_id); -+ max9x_sysfs_destroy_get_link(common, link_id); -+ } -+ -+ for (line = 0; line < common->num_line_faults; line++) -+ max9x_sysfs_destroy_line_fault_status(common, line); -+ -+ max9x_disable(common); -+ -+ /* unregister devices? */ -+ -+ v4l2_async_unregister_subdev(&common->v4l.sd); -+ media_entity_cleanup(&common->v4l.sd.entity); -+ -+ i2c_mux_del_adapters(common->muxc); -+ mutex_destroy(&common->link_mutex); -+ mutex_destroy(&common->isolate_mutex); -+} -+EXPORT_SYMBOL(max9x_destroy); -+ -+/* -+ * max9x_serdes_mhz_to_rate()- Lookup register value for given frequency -+ * -+ * Return: Positive value on success, negative error on failure -+ */ -+int max9x_serdes_mhz_to_rate(struct max9x_serdes_rate_table *table, -+ int len, unsigned int freq_mhz) -+{ -+ int i; -+ -+ for (i = 0; i < len; i++) -+ if (table[i].freq_mhz == freq_mhz) -+ return table[i].val; -+ -+ WARN_ONCE(true, "Invalid GMSL frequency: %d MHz", freq_mhz); -+ -+ return -EINVAL; -+} -+EXPORT_SYMBOL(max9x_serdes_mhz_to_rate); -+ -+struct max9x_common *max9x_sd_to_common(struct v4l2_subdev *sd) -+{ -+ if (!sd) -+ return NULL; -+ -+ struct max9x_serdes_v4l *v4l = container_of(sd, struct max9x_serdes_v4l, sd); -+ -+ return container_of(v4l, struct max9x_common, v4l); -+} -+EXPORT_SYMBOL(max9x_sd_to_common); -+ -+struct max9x_common *max9x_client_to_common(struct i2c_client *client) -+{ -+ if (!client) -+ return NULL; -+ -+ return max9x_sd_to_common(i2c_get_clientdata(client)); -+} -+EXPORT_SYMBOL(max9x_client_to_common); -+ -+int max9x_soft_reset(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->soft_reset) -+ return common->common_ops->soft_reset(common); -+ -+ return 0; -+} -+ -+int max9x_remap_addr(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->remap_addr) -+ return common->common_ops->remap_addr(common); -+ -+ return 0; -+} -+ -+int max9x_setup_gpio(struct max9x_common *common) -+{ -+ if (common->common_ops && common->common_ops->remap_addr) -+ return common->common_ops->setup_gpio(common); -+ -+ return 0; -+} -+ -+/* -+ * max9x_enable() - Enable reset and power to des. -+ */ -+int max9x_enable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int phys_addr, virt_addr; -+ int ret; -+ -+ if (common->regulator_enabled) -+ return 0; -+ -+ virt_addr = common->client->addr; -+ phys_addr = (common->phys_client ? common->phys_client->addr : virt_addr); -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_enable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ common->regulator_enabled = true; -+ } -+ -+ /* Ensure device is reset */ -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) { -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ usleep_range(1000, 1050); -+ gpiod_set_value_cansleep(common->reset_gpio, 0); -+ } else { -+ /* No reset_gpio, device requires soft-reset */ -+ if (phys_addr == virt_addr) { -+ /* No remapping necessary */ -+ ret = max9x_soft_reset(common); -+ if (ret) -+ goto err; -+ } else if (phys_addr != virt_addr) { -+ /* Try virt address first */ -+ ret = max9x_soft_reset(common); -+ if (ret) { -+ /* Nope? Try phys address next */ -+ struct regmap *virt_map = common->map; -+ -+ common->map = common->phys_map; -+ ret = max9x_soft_reset(common); -+ common->map = virt_map; -+ if (ret) -+ goto err; -+ } -+ } -+ } -+ -+ dev_info(dev, "sleep for startup after soft reset"); -+ usleep_range(100000, 100050); -+ -+ if (phys_addr != virt_addr) { -+ /* Device is now reset, but requires remap */ -+ ret = max9x_remap_addr(common); -+ if (ret) -+ goto err; -+ } -+ -+ if (common->common_ops && common->common_ops->enable) { -+ ret = common->common_ops->enable(common); -+ if (ret) -+ goto err; -+ } -+ -+ return 0; -+ -+err: -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (common->regulator_enabled && !IS_ERR_OR_NULL(common->vdd_regulator)) -+ regulator_disable(common->vdd_regulator); -+ -+ common->regulator_enabled = false; -+ -+ return ret; -+} -+ -+/* -+ * max9x_disable() - Disable reset and power to des. -+ */ -+int max9x_disable(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ -+ ret = max9x_disable_line_faults(common); -+ if (ret) { -+ dev_err(dev, "Failed to disable line faults"); -+ return ret; -+ } -+ -+ if (common->regulator_enabled) { -+ if (common->common_ops && common->common_ops->disable) -+ return common->common_ops->disable(common); -+ -+ common->regulator_enabled = false; -+ -+ if (!IS_ERR_OR_NULL(common->reset_gpio)) -+ gpiod_set_value_cansleep(common->reset_gpio, 1); -+ -+ if (!IS_ERR_OR_NULL(common->vdd_regulator)) { -+ ret = regulator_disable(common->vdd_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to disable %s", MAX9X_VDD_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ } -+ return 0; -+} -+ -+static int max9x_get_chip_type(unsigned int dev_id) -+{ -+ unsigned int des_num = sizeof(max9x_chips) / sizeof(struct max9x_desc); -+ unsigned int i = 0; -+ -+ for (i = 0; i < des_num; i++) { -+ if (dev_id == max9x_chips[i].dev_id) -+ return i; -+ } -+ -+ return -1; -+} -+ -+int max9x_verify_devid(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ struct regmap *map = common->map; -+ struct regmap *phys_map = common->phys_map; -+ unsigned int dev_id, dev_rev, chip_type; -+ int ret; -+ -+ /* -+ * Fetch and output chip name + revision -+ * try both virtual address and physical address -+ */ -+ ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); -+ if (ret) { -+ dev_warn(dev, "Failed to read chip ID from virtual address"); -+ if (phys_map) { -+ ret = regmap_read(phys_map, MAX9X_DEV_ID, &dev_id); -+ if (ret) { -+ dev_err(dev, "Failed to read chip ID from phys address"); -+ return ret; -+ } -+ } else -+ return ret; -+ } -+ -+ chip_type = max9x_get_chip_type(dev_id); -+ if (chip_type == -1) { -+ dev_warn(dev, "Unknown chip ID 0x%x", dev_id); -+ return -1; -+ } -+ common->des = &max9x_chips[chip_type]; -+ common->type = common->des->serdes_type; -+ TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); -+ dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); -+ -+ dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -+ return 0; -+} -+ -+/* TODO: remap not hardcode according to pdata */ -+int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[link_id]; -+ unsigned int phys_addr, virt_addr; -+ struct i2c_client *phys_client, *virt_client; -+ struct regmap *phys_map, *virt_map; -+ unsigned int val; -+ const struct regmap_config regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+ }; -+ -+ if (!serial_link->remote.pdata) -+ return 0; -+ -+ ret = max9x_select_i2c_chan(common->muxc, link_id); -+ if (ret) -+ return ret; -+ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ phys_addr = serial_link->remote.pdata->phys_addr; -+ virt_addr = serial_link->remote.pdata->board_info.addr; -+ if (phys_addr == virt_addr) -+ return 0; -+ -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ -+ phys_client = i2c_new_dummy_device(common->client->adapter, phys_addr); -+ if (IS_ERR_OR_NULL(phys_client)) { -+ dev_err(common->dev, "Failed to create dummy client for phys_addr"); -+ return PTR_ERR(phys_client); -+ } -+ -+ phys_map = regmap_init_i2c(phys_client, ®map_config); -+ if (IS_ERR_OR_NULL(phys_map)) -+ goto err_client; -+ -+ virt_client = i2c_new_dummy_device(common->client->adapter, virt_addr); -+ if (IS_ERR_OR_NULL(virt_client)) -+ goto err_regmap; -+ -+ virt_map = regmap_init_i2c(virt_client, ®map_config); -+ if (IS_ERR_OR_NULL(virt_map)) -+ goto err_virt_client; -+ -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (!ret) { -+ dev_info(common->dev, "Remap not necessary"); -+ ret = 0; -+ goto err_virt_regmap; -+ } -+ -+ ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ goto err_virt_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ } -+ -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ if (ret) { -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ phys_addr, virt_addr, ret); -+ goto err_virt_regmap; -+ } -+ -+ usleep_range(1000, 1050); -+ -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ if (ret) { -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ goto err_virt_regmap; -+ } else { -+ dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ } -+ -+err_virt_regmap: -+ regmap_exit(virt_map); -+err_virt_client: -+ i2c_unregister_device(virt_client); -+ -+err_regmap: -+ regmap_exit(phys_map); -+err_client: -+ i2c_unregister_device(phys_client); -+ -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ -+ return ret; -+} -+ -+int max9x_create_adapters(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ unsigned int link_id; -+ const unsigned int RETRY_MS_MIN = 32; -+ const unsigned int RETRY_MS_MAX = 512; -+ unsigned int ms; -+ int err = 0; -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ err = max9x_sysfs_create_get_link(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to create sysfs lock status file for link %d", -+ link_id); -+ continue; -+ } -+ -+ dev_info(dev, "Serial-link %d: %senabled", -+ link_id, (common->serial_link[link_id].enabled ? "" : "not ")); -+ -+ if (!common->serial_link[link_id].enabled) -+ continue; -+ -+ /* This exponential retry works around a current problem in the locking code. */ -+ for (ms = RETRY_MS_MIN; ms <= RETRY_MS_MAX; ms <<= 1) { -+ err = max9x_enable_serial_link(common, link_id); -+ if (!err) -+ break; -+ -+ dev_warn(dev, -+ "enable link %d failed, trying again (waiting %d ms)", -+ link_id, ms); -+ msleep(ms); -+ } -+ if (ms > RETRY_MS_MAX) { -+ dev_err(dev, "failed to enable link %d after multiple retries", -+ link_id); -+ goto err_disable; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ err = max9x_remap_serializers(common, link_id); -+ if (err) { -+ dev_err(dev, "failed to remap serializers on link %d", link_id); -+ goto err_disable; -+ } -+ } -+ -+ continue; -+err_disable: -+ max9x_disable_serial_link(common, link_id); -+ common->serial_link[link_id].enabled = false; -+ } -+ -+ for (link_id = 0; link_id < common->num_serial_links; link_id++) { -+ max9x_setup_translations(common); -+ -+ err = i2c_mux_add_adapter(common->muxc, 0, link_id); -+ if (err) { -+ dev_err(dev, "failed to add adapter for link %d", -+ link_id); -+ max9x_disable_serial_link(common, link_id); -+ continue; -+ } -+ } -+ -+ return 0; -+} -+ -+static void max9x_des_s_csi_link(struct max9x_common *common, -+ unsigned int serial_link_id, int enable) -+{ -+ unsigned int video_pipe_id; -+ int err = 0; -+ -+ for (video_pipe_id = 0; video_pipe_id < common->num_video_pipes; -+ video_pipe_id++) { -+ struct max9x_serdes_video_pipe *video_pipe = -+ &common->video_pipe[video_pipe_id]; -+ unsigned int map_id; -+ -+ if (!video_pipe->enabled) -+ continue; -+ -+ if (video_pipe->config.src_link != serial_link_id) -+ continue; -+ -+ for (map_id = 0; map_id < video_pipe->config.num_maps; -+ map_id++) { -+ unsigned int csi_link_id = -+ video_pipe->config.map[map_id].dst_csi; -+ -+ if (common->csi_link[csi_link_id].config.auto_start) -+ continue; /* Already started at probe */ -+ -+ if (enable) { -+ if (common->csi_link_ops->enable) { -+ err = common->csi_link_ops->enable( -+ common, csi_link_id); -+ if (err) -+ dev_warn( -+ common->dev, -+ "csi_link_ops->enable CSI %d failed: %d", -+ csi_link_id, err); -+ } -+ } else { -+ if (common->csi_link_ops->disable) { -+ err = common->csi_link_ops->disable( -+ common, csi_link_id); -+ if (err) -+ dev_warn( -+ common->dev, -+ "csi_link_ops->disable CSI %d failed: %d", -+ csi_link_id, err); -+ } -+ } -+ } -+ } -+} -+ -+static int _max9x_s_remote_stream(struct max9x_common *common, u32 sink_pad, -+ u32 sink_stream, int enable) -+{ -+ struct media_pad *remote_pad; -+ struct v4l2_subdev *remote_sd; -+ int ret = 0; -+ -+ if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ remote_pad = media_pad_remote_pad_first(&common->v4l.pads[sink_pad]); -+ if (IS_ERR_OR_NULL(remote_pad)) { -+ dev_err(common->dev, "Failed to find remote pad for %s %u", -+ common->v4l.sd.entity.name, sink_pad); -+ return IS_ERR(remote_pad) ? PTR_ERR(remote_pad) : -ENODEV; -+ } -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ if (!remote_sd) { -+ dev_err(common->dev, "Failed to resolve entity %s to subdev", -+ remote_pad->entity->name); -+ return -ENODEV; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ ret = enable ? v4l2_subdev_enable_streams(remote_sd, -+ remote_pad->index, -+ BIT(sink_stream)) : -+ v4l2_subdev_disable_streams(remote_sd, -+ remote_pad->index, -+ BIT(sink_stream)); -+ -+ } else { -+ ret = v4l2_subdev_call(remote_sd, video, s_stream, enable); -+ } -+ -+ if (ret) { -+ dev_err(common->dev, "Failed to %s stream %s %u:%u", -+ enable ? "enable" : "disable", remote_sd->entity.name, -+ remote_pad->index, sink_stream); -+ return ret; -+ } -+ -+ return ret; -+} -+ -+static int _max9x_des_set_stream(struct max9x_common *common, u32 sink_pad, -+ u32 sink_stream, int enable) -+{ -+ u32 rxport; -+ int ret = 0; -+ -+ if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ rxport = sink_pad - common->num_csi_links; -+ if (rxport < 0 || rxport >= common->num_serial_links) { -+ dev_err(common->dev, "Failed to get rxport for pad: %u!", -+ sink_pad); -+ return -EINVAL; -+ } -+ if (enable) -+ max9x_des_s_csi_link(common, rxport, enable); -+ -+ ret = _max9x_s_remote_stream(common, sink_pad, sink_stream, enable); -+ if (ret) { -+ dev_err(common->dev, -+ "Failed to %s remote stream for sink %s %u:%u", -+ enable ? "enable" : "disable", -+ common->v4l.sd.entity.name, sink_pad, sink_stream); -+ return ret; -+ } -+ if (!enable) -+ max9x_des_s_csi_link(common, rxport, enable); -+ -+ return 0; -+} -+ -+static int _max9x_ser_set_stream(struct max9x_common *common, u32 sink_pad, -+ u32 sink_stream, int enable) -+{ -+ return _max9x_s_remote_stream(common, sink_pad, sink_stream, enable); -+} -+ -+static int max9x_streams_mask_to_stream(u64 streams_mask, u32 *stream) -+{ -+ int ret = -EINVAL; -+ -+ for (int i = 0; i < 64; i++) { -+ if (streams_mask & BIT(i)) { -+ *stream = i; -+ ret = 0; -+ break; -+ } -+ } -+ return ret; -+} -+ -+static int max9x_get_corresponding_pad_and_stream(struct v4l2_subdev_state *state, u32 pad, -+ u32 stream, u32 *another_pad, -+ u32 *another_stream) -+{ -+ struct v4l2_subdev_route *route; -+ -+ for_each_active_route(&state->routing, route) { -+ if (route->sink_pad == pad && route->sink_stream == stream) { -+ *another_pad = route->source_pad; -+ *another_stream = route->source_stream; -+ return 0; -+ } else if (route->source_pad == pad && -+ route->source_stream == stream) { -+ *another_pad = route->sink_pad; -+ *another_stream = route->sink_stream; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int _max9x_set_stream(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, u32 pad, -+ u64 streams_mask, int enable) -+{ -+ struct max9x_common *common = max9x_sd_to_common(subdev); -+ u32 sink_pad; -+ u32 sink_stream; -+ u32 source_stream; -+ int ret = 0; -+ -+ ret = max9x_streams_mask_to_stream(streams_mask, &source_stream); -+ if (ret) { -+ dev_err(common->dev, "Failed to get source stream!"); -+ return ret; -+ } -+ -+ ret = max9x_get_corresponding_pad_and_stream(state, pad, source_stream, -+ &sink_pad, &sink_stream); -+ if (ret) { -+ dev_err(common->dev, -+ "Failed to get sink pad and sink stream for %s %u:%u", -+ common->v4l.sd.entity.name, pad, source_stream); -+ return -EINVAL; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ ret = _max9x_des_set_stream(common, sink_pad, sink_stream, -+ enable); -+ else -+ ret = _max9x_ser_set_stream(common, sink_pad, sink_stream, -+ enable); -+ -+ return ret; -+} -+ -+static int max9x_enable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return _max9x_set_stream(subdev, state, pad, streams_mask, true); -+} -+ -+static int max9x_disable_streams(struct v4l2_subdev *subdev, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ return _max9x_set_stream(subdev, state, pad, streams_mask, false); -+} -+ -+static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ if (IS_ERR_OR_NULL(fmt)) { -+ dev_err(common->dev, "Invalid fmt %p", fmt); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (fmt->pad < 0 || fmt->pad >= common->v4l.num_pads) { -+ dev_err(sd->dev, "%s invalid pad %d", __func__, fmt->pad); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) -+ return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); -+ -+ if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -+ return &common->v4l.ffmts[fmt->pad]; -+ -+ return ERR_PTR(-EINVAL); -+} -+ -+static int max9x_get_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_mbus_framefmt *ffmt; -+ -+ mutex_lock(&v4l->lock); -+ -+ ffmt = __max9x_get_ffmt(sd, v4l2_state, fmt); -+ if (IS_ERR_OR_NULL(ffmt)) { -+ mutex_unlock(&v4l->lock); -+ return -EINVAL; -+ } -+ -+ fmt->format = *ffmt; -+ mutex_unlock(&v4l->lock); -+ -+ dev_info(sd->dev, "framefmt: width: %d, height: %d, code: 0x%x.", -+ fmt->format.width, fmt->format.height, fmt->format.code); -+ -+ return 0; -+} -+ -+static int max9x_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_format *fmt) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_mbus_framefmt *ffmt; -+ -+ mutex_lock(&v4l->lock); -+ -+ ffmt = __max9x_get_ffmt(sd, v4l2_state, fmt); -+ if (IS_ERR_OR_NULL(ffmt)) { -+ mutex_unlock(&v4l->lock); -+ return -EINVAL; -+ } -+ -+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { -+ ffmt->width = fmt->format.width; -+ ffmt->height = fmt->format.height; -+ ffmt->code = fmt->format.code; -+ } -+ fmt->format = *ffmt; -+ -+ mutex_unlock(&v4l->lock); -+ -+ return 0; -+} -+ -+static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -+ struct v4l2_mbus_frame_desc *desc) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ int ret = -EINVAL; -+ -+ if (((common->type != MAX9X_DESERIALIZER) && -+ (common->type != MAX9X_SERIALIZER)) || -+ pad < 0 || pad >= common->v4l.num_pads) -+ return -EINVAL; -+ -+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -+ -+ struct v4l2_subdev_state *state = -+ v4l2_subdev_lock_and_get_active_state(sd); -+ struct v4l2_subdev_route *route; -+ -+ for_each_active_route(&state->routing, route) { -+ if (route->source_pad != pad) -+ continue; -+ if (route->sink_pad >= common->v4l.num_pads) { -+ ret = -EINVAL; -+ dev_err(common->dev, "Found invalid route sink_pad!"); -+ goto out_unlock; -+ } -+ -+ struct media_pad *remote_pad = media_pad_remote_pad_first( -+ &common->v4l.pads[route->sink_pad]); -+ struct v4l2_mbus_frame_desc source_desc; -+ -+ ret = v4l2_subdev_call( -+ media_entity_to_v4l2_subdev(remote_pad->entity), pad, -+ get_frame_desc, remote_pad->index, &source_desc); -+ if (ret) { -+ dev_err(common->dev, -+ "Failed to get sink pad %u remote frame desc!", -+ route->sink_pad); -+ goto out_unlock; -+ } -+ -+ struct v4l2_mbus_frame_desc_entry *source_desc_entry; -+ -+ for (int i = 0; i < source_desc.num_entries; i++) { -+ if (source_desc.entry[i].stream == route->sink_stream) { -+ source_desc_entry = &source_desc.entry[i]; -+ break; -+ } -+ } -+ if (!source_desc_entry) { -+ ret = -EPIPE; -+ dev_err(common->dev, -+ "Failed to get source desc entry for pad: %u, stream: %u", -+ route->sink_pad, route->sink_stream); -+ goto out_unlock; -+ } -+ desc->entry[desc->num_entries].flags = source_desc_entry->flags; -+ desc->entry[desc->num_entries].stream = route->source_stream; -+ desc->entry[desc->num_entries].pixelcode = -+ source_desc_entry->pixelcode; -+ desc->entry[desc->num_entries].length = -+ source_desc_entry->length; -+ if (common->type == MAX9X_DESERIALIZER) -+ desc->entry[desc->num_entries].bus.csi2.vc = -+ route->sink_pad - common->num_csi_links; -+ desc->entry[desc->num_entries].bus.csi2.dt = -+ source_desc_entry->bus.csi2.dt; -+ desc->num_entries++; -+ } -+ -+out_unlock: -+ v4l2_subdev_unlock_state(state); -+ return ret; -+} -+ -+static int max9x_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *v4l2_state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ if (mbus_code_to_csi_dt(code->code) < 0) -+ return -EINVAL; -+ return 0; -+} -+ -+static int _max9x_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_krouting *routing) -+{ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1536, -+ .code = MEDIA_BUS_FMT_UYVY8_1X16, -+ }; -+ int ret; -+ -+ /* -+ * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until -+ * frame desc is made dynamically allocated. -+ */ -+ -+ if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) -+ return -E2BIG; -+ -+ ret = v4l2_subdev_routing_validate(sd, routing, -+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 | -+ V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX); -+ if (ret) -+ return ret; -+ -+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format); -+ if (ret) -+ return ret; -+ return 0; -+} -+ -+static int max9x_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing) -+{ -+ _max9x_set_routing(sd, state, routing); -+ -+ return 0; -+} -+ -+static int max9x_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ -+ struct v4l2_subdev_route des_routes[] = { -+ { -+ .sink_pad = 5, -+ .sink_stream = 0, -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }, -+ }; -+ struct v4l2_subdev_route ser_routes[] = { -+ { -+ .sink_pad = 0, -+ .sink_stream = 0, -+ .source_pad = 2, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }, -+ }; -+ struct v4l2_subdev_krouting des_routing = { -+ .num_routes = ARRAY_SIZE(des_routes), -+ .routes = des_routes, -+ }; -+ struct v4l2_subdev_krouting ser_routing = { -+ .num_routes = ARRAY_SIZE(ser_routes), -+ .routes = ser_routes, -+ }; -+ -+ if (common->type == MAX9X_DESERIALIZER) -+ return _max9x_set_routing(sd, state, &des_routing); -+ else -+ return _max9x_set_routing(sd, state, &ser_routing); -+} -+ -+static const struct v4l2_subdev_pad_ops max9x_sd_pad_ops = { -+ .get_fmt = max9x_get_fmt, -+ .set_fmt = max9x_set_fmt, -+ .get_frame_desc = max9x_get_frame_desc, -+ .enum_mbus_code = max9x_enum_mbus_code, -+ .set_routing = max9x_set_routing, -+ .enable_streams = max9x_enable_streams, -+ .disable_streams = max9x_disable_streams, -+}; -+ -+static struct v4l2_subdev_ops max9x_sd_ops = { -+ .pad = &max9x_sd_pad_ops, -+}; -+ -+static int max9x_registered(struct v4l2_subdev *sd) -+{ -+ struct max9x_common *common = max9x_sd_to_common(sd); -+ struct device *dev = common->dev; -+ int ret; -+ -+ for (unsigned int link_id = 0; link_id < common->num_serial_links; link_id++) { -+ if (!common->serial_link[link_id].enabled) { -+ dev_dbg(dev, "Serial-link %d not enabled, skipping subdevs", link_id); -+ continue; -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ struct max9x_subdev_pdata *subdev_pdata = -+ common->serial_link[link_id].remote.pdata; -+ -+ if (subdev_pdata) { -+ struct max9x_pdata *ser_pdata = -+ subdev_pdata->board_info.platform_data; -+ -+ WARN_ON(ser_pdata->num_serial_links < 1); -+ -+ ser_pdata->serial_links[0].des_client = common->client; -+ ser_pdata->serial_links[0].des_link_id = link_id; -+ /* -+ * Isolate this link until after reset and potential address remapping, -+ * avoiding a race condition with two serializers resetting same -+ * physical i2c at the same time -+ */ -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); -+ -+ ret = max9x_des_deisolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ -+ if (IS_ERR_OR_NULL(subdev)) { -+ dev_err(dev, "Failure registering serializer %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ return PTR_ERR(subdev); -+ } -+ -+ dev_dbg(dev, "Registered serializer %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ -+ struct max9x_common *ser_common = max9x_sd_to_common(subdev); -+ -+ int remote_pad = max9x_serial_link_to_pad(ser_common, 0); -+ int local_pad = max9x_serial_link_to_pad(common, link_id); -+ -+ dev_dbg(dev, "Create link from ser link 0 (pad %d) -> des link %d (pad %d)", -+ remote_pad, link_id, local_pad); -+ -+ ret = media_create_pad_link(&subdev->entity, remote_pad, -+ &sd->entity, local_pad, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) { -+ dev_err(dev, "Failed creating pad link to serializer"); -+ return ret; -+ } -+ -+ common->serial_link[link_id].remote.client = ser_common->client; -+ } -+ } else { -+ struct max9x_pdata *pdata = dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ -+ if (subdev_pdata->serial_link_id == link_id) { -+ char dev_id[I2C_NAME_SIZE]; -+ -+ snprintf(dev_id, sizeof(dev_id), "i2c-%s", -+ subdev_pdata->board_info.dev_name); -+ -+ dev_dbg(dev, "Registering sensor %s (%s)...", -+ subdev_pdata->board_info.type, dev_id); -+ -+ static struct gpiod_lookup_table sensor_gpios = { -+ .dev_id = "", -+ .table = { -+ GPIO_LOOKUP("", 0, "reset", -+ GPIO_ACTIVE_HIGH), -+ {} -+ }, -+ }; -+ -+ sensor_gpios.dev_id = dev_id; -+ sensor_gpios.table[0].key = common->gpio_chip.label; -+ -+ gpiod_add_lookup_table(&sensor_gpios); -+ -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); -+ -+ gpiod_remove_lookup_table(&sensor_gpios); -+ -+ if (IS_ERR_OR_NULL(subdev)) { -+ dev_err(dev, -+ "Failure registering sensor %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ return PTR_ERR(subdev); -+ } -+ -+ dev_dbg(dev, "Registered sensor %s (0x%02x)", -+ subdev_pdata->board_info.type, -+ subdev_pdata->board_info.addr); -+ -+ int remote_pad = media_get_pad_index(&subdev->entity, -+ MEDIA_PAD_FL_SOURCE, -+ PAD_SIGNAL_DEFAULT); -+ int local_pad = max9x_csi_link_to_pad(common, 0); -+ -+ dev_dbg(dev, "Create link from sen pad %d -> ser link %d (pad %d)", -+ remote_pad, link_id, -+ local_pad); -+ -+ ret = media_create_pad_link(&subdev->entity, remote_pad, -+ &sd->entity, local_pad, -+ MEDIA_LNK_FL_IMMUTABLE | -+ MEDIA_LNK_FL_ENABLED); -+ if (ret) { -+ dev_err(dev, "Failed creating pad link to serializer"); -+ return ret; -+ } -+ -+ } -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+static struct v4l2_subdev_internal_ops max9x_sd_internal_ops = { -+ .registered = max9x_registered, -+ .init_state = max9x_init_state, -+}; -+ -+static int max9x_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct max9x_serdes_v4l *v4l = container_of(ctrl->handler, struct max9x_serdes_v4l, ctrl_handler); -+ struct max9x_common *common = container_of(v4l, struct max9x_common, v4l); -+ struct device *dev = common->dev; -+ -+ dev_dbg(dev, "s_ctrl"); -+ -+ switch (ctrl->id) { -+ case V4L2_CID_LINK_FREQ: { -+ if (ctrl->p_new.p_u8) { -+ if (*ctrl->p_new.p_u8 <= (ARRAY_SIZE(max9x_op_sys_clock) - 1)) { -+ unsigned int csi_link_id; -+ struct v4l2_ctrl *link_freq = v4l->link_freq; -+ -+ dev_info(dev, "user-modified %s index val=%d to user-val=%d", -+ ctrl->name, (unsigned int)link_freq->val, -+ (unsigned int)*ctrl->p_new.p_u8); -+ -+ link_freq->val = (s32) *ctrl->p_new.p_u8; -+ -+ for (csi_link_id = 0; csi_link_id < common->num_csi_links; csi_link_id++) { -+ if (!common->csi_link[csi_link_id].enabled) -+ continue; -+ -+ common->csi_link[csi_link_id].config.freq_mhz = -+ MAX9X_LINK_FREQ_HZ_TO_MBPS(max9x_op_sys_clock[link_freq->val]); -+ -+ if (common->csi_link[csi_link_id].config.freq_mhz > 1500) { -+ common->csi_link[csi_link_id].config.auto_init_deskew_enabled = true; -+ common->csi_link[csi_link_id].config.initial_deskew_width = 7; -+ } -+ } -+ } -+ } -+ } -+ break; -+ -+ default: -+ dev_info(dev, "unknown control id: 0x%X", ctrl->id); -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops max9x_ctrl_ops = { -+ .s_ctrl = max9x_s_ctrl, -+}; -+ -+static struct v4l2_ctrl_config max9x_v4l2_controls[] = { -+ { -+ .ops = &max9x_ctrl_ops, -+ .id = V4L2_CID_LINK_FREQ, -+ .name = "V4L2_CID_LINK_FREQ", -+ .type = V4L2_CTRL_TYPE_INTEGER_MENU, -+ .min = 0, -+ .max = ARRAY_SIZE(max9x_op_sys_clock) - 1, -+ .def = 2, -+ .menu_skip_mask = 0, -+ .qmenu_int = max9x_op_sys_clock, -+ }, -+}; -+ -+static int max9x_csi_link_to_pad(struct max9x_common *common, int csi_id) -+{ -+ return (csi_id < common->num_csi_links) ? csi_id : -EINVAL; -+} -+ -+static int max9x_serial_link_to_pad(struct max9x_common *common, int link_id) -+{ -+ dev_dbg(common->dev, "link_id %d < num_serial_links %d ? num_csi_links %d + link_id %d : -EINVAL", -+ link_id, common->num_serial_links, common->num_csi_links, link_id); -+ return (link_id < common->num_serial_links) ? common->num_csi_links + link_id : -EINVAL; -+} -+ -+static int max9x_register_v4l_subdev(struct max9x_common *common) -+{ -+ struct i2c_client *client = common->client; -+ struct device *dev = common->dev; -+ struct max9x_serdes_v4l *v4l = &common->v4l; -+ struct v4l2_subdev *sd = &v4l->sd; -+ struct v4l2_ctrl_handler *ctrl_handler = &v4l->ctrl_handler; -+ struct max9x_pdata *pdata = dev->platform_data; -+ int ret; -+ -+ mutex_init(&v4l->lock); -+ -+ v4l2_i2c_subdev_init(sd, client, &max9x_sd_ops); -+ snprintf(sd->name, sizeof(sd->name), "%s %s", client->name, pdata->suffix); -+ -+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; -+ sd->internal_ops = &max9x_sd_internal_ops; -+ sd->entity.function = MEDIA_ENT_F_VID_MUX; -+ -+ v4l->num_pads = common->num_csi_links + common->num_serial_links; -+ v4l->pads = devm_kzalloc(dev, v4l->num_pads * sizeof(*v4l->pads), GFP_KERNEL); -+ v4l->ffmts = devm_kzalloc(dev, v4l->num_pads * sizeof(*v4l->ffmts), GFP_KERNEL); -+ -+ /* change sink/source turn */ -+ for (unsigned int p = 0; p < v4l->num_pads; p++) { -+ struct media_pad *pad = &v4l->pads[p]; -+ -+ if (p < common->num_csi_links) { -+ unsigned int c = p; -+ -+ dev_dbg(dev, "pad %d/%d -> csi %d", p, v4l->num_pads, c); -+ -+ pad->flags = (common->type == MAX9X_SERIALIZER) ? MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; -+ } else if (p >= common->num_csi_links) { -+ unsigned int s = p - common->num_csi_links; -+ -+ dev_dbg(dev, "pad %d/%d -> serial %d", p, v4l->num_pads, s); -+ -+ pad->flags = (common->type == MAX9X_SERIALIZER) ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; -+ } -+ } -+ -+ ret = v4l2_ctrl_handler_init(ctrl_handler, ARRAY_SIZE(max9x_v4l2_controls)); -+ if (ret) { -+ dev_err(dev, "Failed to init V4L2 controls: %d", ret); -+ return ret; -+ } -+ -+ for (int i = 0; i < ARRAY_SIZE(max9x_v4l2_controls); i++) { -+ struct v4l2_ctrl_config *ctrl_config = &max9x_v4l2_controls[i]; -+ struct v4l2_ctrl *ctrl; -+ -+ if (ctrl_config->id == V4L2_CID_LINK_FREQ) { -+ unsigned int link_freq_n; -+ unsigned int csi_link_id; -+ -+ for (link_freq_n = 0; link_freq_n < ARRAY_SIZE(max9x_op_sys_clock); link_freq_n++) { -+ unsigned int link_freq = MAX9X_LINK_FREQ_HZ_TO_MBPS(max9x_op_sys_clock[link_freq_n]); -+ -+ for (csi_link_id = 0; csi_link_id < common->num_csi_links; csi_link_id++) { -+ if (!common->csi_link[csi_link_id].enabled) -+ continue; -+ if (common->csi_link[csi_link_id].config.freq_mhz == link_freq) { -+ ctrl_config->def = link_freq_n; -+ break; -+ } -+ } -+ } -+ } -+ -+ ctrl = v4l2_ctrl_new_custom(ctrl_handler, ctrl_config, common); -+ if (!ctrl) { -+ ret = ctrl_handler->error; -+ dev_err(dev, "Failed to create V4L2 control %s: %d", ctrl_config->name, ret); -+ goto probe_error_v4l2_ctrl_handler_free; -+ } -+ } -+ -+ v4l->link_freq = v4l2_ctrl_find(ctrl_handler, V4L2_CID_LINK_FREQ); -+ v4l->sd.ctrl_handler = ctrl_handler; -+ -+ ret = media_entity_pads_init(&v4l->sd.entity, v4l->num_pads, v4l->pads); -+ if (ret) { -+ dev_err(dev, "Failed to init media entity: %d", ret); -+ goto probe_error_v4l2_ctrl_handler_free; -+ } -+ -+ ret = v4l2_subdev_init_finalize(&v4l->sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev: %d\n", ret); -+ media_entity_cleanup(&v4l->sd.entity); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ ret = v4l2_async_register_subdev(&v4l->sd); -+ if (ret) { -+ dev_err(dev, "v4l register failed: %d", ret); -+ goto probe_error_media_entity_cleanup; -+ } -+ -+ return 0; -+ -+probe_error_media_entity_cleanup: -+ media_entity_cleanup(&v4l->sd.entity); -+probe_error_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(ctrl_handler); -+ return ret; -+} -+ -+/* -+ * max9x_enable_serial_link() - Enable power and logic to link. -+ */ -+int max9x_enable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct max9x_serdes_serial_link *serial_link; -+ struct device *dev = common->dev; -+ int ret; -+ -+ serial_link = &common->serial_link[link_id]; -+ -+ if (!serial_link->regulator_enabled) { -+ if (!IS_ERR_OR_NULL(serial_link->poc_regulator)) { -+ ret = regulator_enable(serial_link->poc_regulator); -+ if (ret) { -+ dev_err(dev, "Failed to enable %s", MAX9X_POC_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ -+ serial_link->regulator_enabled = true; -+ } -+ -+ if (common->serial_link_ops && common->serial_link_ops->enable) -+ return common->serial_link_ops->enable(common, link_id); -+ -+ return 0; -+} -+ -+/* -+ * max9x_disable_serial_link() - Disable power and logic to link. -+ */ -+int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct max9x_serdes_serial_link *serial_link; -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (link_id >= common->num_serial_links) -+ return 0; -+ -+ serial_link = &common->serial_link[link_id]; -+ -+ if (serial_link->regulator_enabled) { -+ if (common->serial_link_ops && common->serial_link_ops->disable) -+ common->serial_link_ops->disable(common, link_id); -+ -+ serial_link->regulator_enabled = false; -+ -+ if (!IS_ERR_OR_NULL(serial_link->poc_regulator)) { -+ ret = regulator_disable(serial_link->poc_regulator); -+ -+ if (ret) { -+ dev_err(dev, "Failed to disable %s", MAX9X_POC_REGULATOR_NAME); -+ return ret; -+ } -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * max9x_sysfs_create_get_link() - Creates a sysfs virtual file to check link lock status -+ */ -+int max9x_sysfs_create_get_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ char *attr_name; -+ -+ if (common->serial_link_ops && common->serial_link_ops->get_locked) { -+ struct device_attribute *link_lock_status = -+ devm_kzalloc(dev, sizeof(struct device_attribute), GFP_KERNEL); -+ -+ if (!link_lock_status) { -+ dev_err(dev, "Failed to allocate memory for link lock status"); -+ return -ENOMEM; -+ } -+ -+ attr_name = (char *)devm_kzalloc(dev, sizeof(char) * ATTR_NAME_LEN, GFP_KERNEL); -+ if (!attr_name) { -+ dev_err(dev, "Failed to allocate memory link lock attribute name"); -+ return -ENOMEM; -+ } -+ -+ ret = snprintf(attr_name, ATTR_NAME_LEN, "link-lock_%d", link_id); -+ if (ret < 0) -+ return ret; -+ -+ link_lock_status->attr.name = attr_name; -+ link_lock_status->attr.mode = ATTR_READ_ONLY; -+ link_lock_status->show = max9x_link_status_show; -+ -+ ret = device_create_file(dev, link_lock_status); -+ if (ret < 0) -+ return ret; -+ -+ common->serial_link[link_id].link_lock_status = link_lock_status; -+ } -+ -+ return 0; -+} -+ -+/* -+ * max9x_sysfs_destroy_get_link() - Destroys the sysfs device attribute for link lock status -+ */ -+static void max9x_sysfs_destroy_get_link(struct max9x_common *common, unsigned int link_id) -+{ -+ struct device *dev = common->dev; -+ -+ if (common->serial_link[link_id].link_lock_status) -+ device_remove_file(dev, common->serial_link[link_id].link_lock_status); -+} -+ -+/* -+ * max9x_enable_line_faults() - Enables all the line fault monitors using the device tree -+ */ -+int max9x_enable_line_faults(struct max9x_common *common) -+{ -+ return 0; -+} -+ -+/* -+ * max9x_disable_line_faults() - Disables all currently stored line fault monitors -+ */ -+int max9x_disable_line_faults(struct max9x_common *common) -+{ -+ struct device *dev = common->dev; -+ int ret; -+ int line; -+ int final_ret = 0; -+ -+ if (common->line_fault && -+ common->line_fault_ops && -+ common->line_fault_ops->disable) { -+ -+ for (line = 0; line < common->num_line_faults; line++) { -+ ret = common->line_fault_ops->disable(common, line); -+ -+ if (ret) { -+ dev_err(dev, "Failed to disable line fault %d", line); -+ final_ret = ret; -+ } -+ -+ common->line_fault[line].enabled = false; -+ } -+ } -+ -+ return final_ret; -+} -+ -+static void max9x_sysfs_destroy_line_fault_status(struct max9x_common *common, unsigned int line) -+{ -+ struct device *dev = common->dev; -+ -+ if (common->line_fault[line].line_fault_status) -+ device_remove_file(dev, common->line_fault[line].line_fault_status); -+} -+ -+static int max9x_parse_pdata(struct max9x_common *common, struct max9x_pdata *pdata) -+{ -+ int ret; -+ -+ for (int i = 0; i < pdata->num_serial_links; i++) { -+ ret = max9x_parse_serial_link_pdata(common, &pdata->serial_links[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_video_pipes; i++) { -+ ret = max9x_parse_video_pipe_pdata(common, &pdata->video_pipes[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_csi_links; i++) { -+ ret = max9x_parse_csi_link_pdata(common, &pdata->csi_links[i]); -+ if (ret) -+ goto err; -+ } -+ -+ for (int i = 0; i < pdata->num_subdevs; i++) { -+ ret = max9x_parse_subdev_pdata(common, &pdata->subdevs[i]); -+ if (ret) -+ goto err; -+ } -+ -+ common->external_refclk_enable = pdata->external_refclk_enable; -+ -+err: -+ return ret; -+} -+ -+static int max9x_parse_serial_link_pdata(struct max9x_common *common, -+ struct max9x_serial_link_pdata *serial_link_pdata) -+{ -+ struct device *dev = common->dev; -+ unsigned int serial_link_id = serial_link_pdata->link_id; -+ -+ if (serial_link_id > common->num_serial_links) { -+ dev_err(dev, "Serial link pdata: Invalid link id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[serial_link_id]; -+ -+ serial_link->enabled = true; -+ -+ serial_link->config.link_type = serial_link_pdata->link_type; -+ serial_link->config.rx_freq_mhz = serial_link_pdata->rx_freq_mhz; -+ serial_link->config.tx_freq_mhz = serial_link_pdata->tx_freq_mhz; -+ -+ if (serial_link_pdata->poc_regulator[0] != 0) { -+ serial_link->poc_regulator = devm_regulator_get_optional(dev, serial_link_pdata->poc_regulator); -+ -+ if (PTR_ERR(serial_link->poc_regulator) == -EPROBE_DEFER) { -+ dev_dbg(dev, "POC regulator not ready deferring..."); -+ return -EPROBE_DEFER; -+ } -+ if (IS_ERR_OR_NULL(serial_link->poc_regulator)) -+ dev_dbg(dev, "Missing POC regulator"); -+ } -+ -+ return 0; -+} -+ -+static int max9x_parse_video_pipe_pdata(struct max9x_common *common, -+ struct max9x_video_pipe_pdata *video_pipe_pdata) -+{ -+ struct device *dev = common->dev; -+ unsigned int serial_link_id = video_pipe_pdata->serial_link_id; -+ unsigned int pipe_id = video_pipe_pdata->pipe_id; -+ unsigned int max_maps; -+ unsigned int max_data_types; -+ -+ if (serial_link_id > common->num_serial_links) { -+ dev_err(dev, "Video pdata: Invalid serial link id"); -+ return -EINVAL; -+ } -+ if (pipe_id > common->num_video_pipes) { -+ dev_err(dev, "Video pdata: Invalid video pipe id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_video_pipe *pipe = &common->video_pipe[pipe_id]; -+ -+ pipe->enabled = true; -+ -+ max_maps = 0; -+ max_data_types = 0; -+ if (common->common_ops && common->common_ops->max_elements) { -+ max_maps = common->common_ops->max_elements(common, MAX9X_MIPI_MAP); -+ max_data_types = common->common_ops->max_elements(common, MAX9X_DATA_TYPES); -+ } -+ -+ if (common->type == MAX9X_DESERIALIZER) { -+ if (video_pipe_pdata->num_maps > max_maps) { -+ dev_err(dev, "Video pdata: Too many maps"); -+ return -EINVAL; -+ } -+ -+ pipe->config.map = devm_kzalloc(dev, -+ video_pipe_pdata->num_maps * sizeof(*pipe->config.map), -+ GFP_KERNEL); -+ if (!pipe->config.map) { -+ dev_err(dev, "Video pdata: Failed t oallocate mmeory for maps"); -+ return -ENOMEM; -+ } -+ -+ pipe->config.src_link = video_pipe_pdata->serial_link_id; -+ pipe->config.src_pipe = video_pipe_pdata->src_pipe_id; -+ -+ for (unsigned int i = 0; i < video_pipe_pdata->num_maps; i++) { -+ struct max9x_serdes_mipi_map *map = &pipe->config.map[i]; -+ struct max9x_serdes_mipi_map *map_pdata = &video_pipe_pdata->maps[i]; -+ -+ map->src_vc = map_pdata->src_vc; -+ map->src_dt = map_pdata->src_dt; -+ map->dst_vc = map_pdata->dst_vc; -+ map->dst_dt = map_pdata->dst_dt; -+ map->dst_csi = map_pdata->dst_csi; -+ } -+ pipe->config.num_maps = video_pipe_pdata->num_maps; -+ } else if (common->type == MAX9X_SERIALIZER) { -+ if (video_pipe_pdata->num_data_types > max_data_types) { -+ dev_err(dev, "Video pdata: Too many maps"); -+ return -EINVAL; -+ } -+ -+ pipe->config.data_type = devm_kzalloc(dev, -+ video_pipe_pdata->num_data_types * sizeof(*pipe->config.map), -+ GFP_KERNEL); -+ if (!pipe->config.data_type) { -+ dev_err(dev, "Video pdata: Failed t oallocate mmeory for data types"); -+ return -ENOMEM; -+ } -+ -+ pipe->config.src_csi = video_pipe_pdata->src_csi_id; -+ -+ for (unsigned int i = 0; i < video_pipe_pdata->num_data_types; i++) { -+ pipe->config.data_type[i] = video_pipe_pdata->data_types[i]; -+ } -+ pipe->config.num_data_types = video_pipe_pdata->num_data_types; -+ } -+ -+ return 0; -+} -+ -+static int max9x_parse_csi_link_pdata(struct max9x_common *common, -+ struct max9x_csi_link_pdata *csi_link_pdata) -+{ -+ unsigned int csi_link_id = csi_link_pdata->link_id; -+ -+ if (csi_link_id > common->num_csi_links) { -+ dev_err(common->dev, "CSI link pdata: Invalid link id"); -+ return -EINVAL; -+ } -+ -+ struct max9x_serdes_csi_link *csi_link = &common->csi_link[csi_link_id]; -+ -+ csi_link->enabled = true; -+ -+ csi_link->config.num_maps = csi_link_pdata->num_maps; -+ csi_link->config.map = -+ devm_kzalloc(common->dev, -+ csi_link->config.num_maps * sizeof(*csi_link->config.map), -+ GFP_KERNEL); -+ memcpy(csi_link->config.map, csi_link_pdata->maps, -+ csi_link->config.num_maps * sizeof(*csi_link->config.map)); -+ csi_link->config.bus_type = csi_link_pdata->bus_type; -+ csi_link->config.num_lanes = csi_link_pdata->num_lanes; -+ csi_link->config.freq_mhz = csi_link_pdata->tx_rate_mbps; -+ csi_link->config.auto_init_deskew_enabled = csi_link_pdata->auto_initial_deskew; -+ -+ if (csi_link->config.auto_init_deskew_enabled) -+ csi_link->config.initial_deskew_width = csi_link_pdata->initial_deskew_width; -+ csi_link->config.auto_start = csi_link_pdata->auto_start; -+ -+ return 0; -+} -+ -+static int max9x_parse_subdev_pdata(struct max9x_common *common, -+ struct max9x_subdev_pdata *subdev_pdata) -+{ -+ unsigned int serial_link_id = subdev_pdata->serial_link_id; -+ struct max9x_serdes_serial_link *serial_link = &common->serial_link[serial_link_id]; -+ -+ if (!serial_link->enabled) -+ return 0; -+ -+ serial_link->remote.pdata = subdev_pdata; -+ -+ return 0; -+} -+ -+int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) -+{ -+ struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; -+ int ret = 0; -+ unsigned long timeout = jiffies + msecs_to_jiffies(10000); -+ -+ dev_dbg(common->dev, "try to select %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ -+ if (unlikely(chan_id > common->num_serial_links)) -+ return -EINVAL; -+ -+ do { -+ mutex_lock(&common->isolate_mutex); -+ if (common->selected_link < 0 || chan_id == common->selected_link) -+ break; -+ -+ mutex_unlock(&common->isolate_mutex); -+ -+ usleep_range(1000, 1050); -+ -+ if (time_is_before_jiffies(timeout)) { -+ dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ return -ETIMEDOUT; -+ } -+ } while (1); -+ -+ common->selected_link = chan_id; -+ -+ if (common->serial_link_ops && common->serial_link_ops->select) -+ ret = common->serial_link_ops->select(common, chan_id); -+ -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) -+{ -+ struct max9x_common *common = i2c_mux_priv(muxc); -+ struct i2c_client *client = common->serial_link[chan_id].remote.client; -+ int ret = 0; -+ -+ dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -+ client ? dev_name(&client->dev) : ""); -+ -+ if (unlikely(chan_id > common->num_serial_links)) -+ return -EINVAL; -+ -+ mutex_lock(&common->isolate_mutex); -+ if (common->serial_link_ops && common->serial_link_ops->deselect) -+ ret = common->serial_link_ops->deselect(common, chan_id); -+ -+ common->selected_link = -1; -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+int max9x_des_isolate_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret = 0; -+ unsigned long timeout = jiffies + msecs_to_jiffies(10000); -+ -+ if (link_id >= common->num_serial_links) { -+ dev_err(common->dev, "link_id %d outside of num_serial_links %d", link_id, common->num_serial_links); -+ return -EINVAL; -+ } -+ -+ dev_info(common->dev, "Isolate %d", link_id); -+ -+ do { -+ mutex_lock(&common->isolate_mutex); -+ if ((common->isolated_link < 0) && (common->selected_link < 0 || link_id == common->selected_link)) -+ break; -+ -+ if (common->isolated_link == link_id) { -+ dev_warn(common->dev, "Link %d is already isolated", link_id); -+ mutex_unlock(&common->isolate_mutex); -+ return -EINVAL; -+ } -+ mutex_unlock(&common->isolate_mutex); -+ -+ usleep_range(1000, 1050); -+ -+ if (time_is_before_jiffies(timeout)) -+ return -ETIMEDOUT; -+ } while (1); -+ -+ common->isolated_link = link_id; -+ if (common->serial_link_ops && common->serial_link_ops->isolate) -+ ret = common->serial_link_ops->isolate(common, link_id); -+ -+ mutex_unlock(&common->isolate_mutex); -+ dev_info(common->dev, "Isolate %d complete", link_id); -+ -+ return ret; -+} -+ -+int max9x_des_deisolate_serial_link(struct max9x_common *common, unsigned int link_id) -+{ -+ int ret = 0; -+ -+ if (link_id >= common->num_serial_links) -+ return -EINVAL; -+ -+ dev_info(common->dev, "Deisolate %d", link_id); -+ -+ mutex_lock(&common->isolate_mutex); -+ if (common->serial_link_ops && common->serial_link_ops->deisolate) -+ ret = common->serial_link_ops->deisolate(common, link_id); -+ -+ common->isolated_link = -1; -+ dev_info(common->dev, "Deisolate %d complete", link_id); -+ mutex_unlock(&common->isolate_mutex); -+ -+ return ret; -+} -+ -+ssize_t max9x_link_status_show(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct max9x_common *common = dev_get_drvdata(dev); -+ int link; -+ int ret; -+ bool locked; -+ -+ ret = sscanf(attr->attr.name, "link-lock_%d", &link); -+ if (ret < 0) -+ return ret; -+ -+ if (common->serial_link_ops && common->serial_link_ops->get_locked) { -+ ret = common->serial_link_ops->get_locked(common, link, &locked); -+ if (ret < 0) -+ return ret; -+ -+ return sysfs_emit(buf, "%d", !!locked); -+ } -+ -+ dev_err(dev, "get_locked not defined"); -+ return -EINVAL; -+} -+ -+int max9x_setup_translations(struct max9x_common *common) -+{ -+ int err = 0; -+ -+ if (!common->translation_ops) -+ return 0; -+ -+ /* -+ * Translation is currently only supported on serializer side -+ * (translating requests from SOC to remote sensor module) -+ */ -+ if (common->type != MAX9X_SERIALIZER) -+ return 0; -+ -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ unsigned int virt_addr = subdev_pdata->board_info.addr; -+ unsigned int phys_addr = subdev_pdata->phys_addr ? subdev_pdata->phys_addr : virt_addr; -+ -+ if (virt_addr == phys_addr || common->translation_ops->add == NULL) -+ continue; -+ -+ /* Only I2C 1 is supported at this time */ -+ err = common->translation_ops->add(common, 0, virt_addr, phys_addr); -+ if (err) -+ dev_warn(common->dev, "Failed to add translation for i2c address 0x%02x -> 0x%02x: %d", -+ virt_addr, phys_addr, err); -+ break; -+ } -+ -+ msleep(10); -+ -+ return err; -+} -+ -+int max9x_disable_translations(struct max9x_common *common) -+{ -+ int err = 0; -+ -+ if (!common->translation_ops) -+ return 0; -+ -+ /* -+ * Translation is currently only supported on serializer side -+ * (translating requests from SOC to remote sensor module) -+ */ -+ if (common->type != MAX9X_SERIALIZER) -+ return 0; -+ -+ struct max9x_pdata *pdata = common->dev->platform_data; -+ -+ for (unsigned int i = 0; i < pdata->num_subdevs; i++) { -+ struct max9x_subdev_pdata *subdev_pdata = &pdata->subdevs[i]; -+ unsigned int virt_addr = subdev_pdata->board_info.addr; -+ unsigned int phys_addr = subdev_pdata->phys_addr ? subdev_pdata->phys_addr : virt_addr; -+ -+ if (virt_addr != phys_addr) { -+ if (common->translation_ops->remove) { -+ /* Only I2C 1 is supported at this time */ -+ err = common->translation_ops->remove(common, 0, virt_addr, phys_addr); -+ if (err) -+ dev_err(common->dev, "Failed to remove translation for i2c address 0x%02x -> 0x%02x: %d", -+ virt_addr, phys_addr, err); -+ break; -+ } -+ } -+ } -+ -+ return err; -+} -+ -+static int max9x_des_probe(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *des = NULL; -+ int ret = 0; -+ -+ dev_info(dev, "Probing"); -+ -+ des = devm_kzalloc(dev, sizeof(*des), GFP_KERNEL); -+ if (!des) { -+ dev_err(dev, "Failed to allocate memory."); -+ return -ENOMEM; -+ } -+ -+ TRY(ret, max9x_common_init_i2c_client( -+ des, -+ client, -+ &max9x_regmap_config, -+ NULL, NULL, NULL, NULL)); -+ -+ dev_info(dev, "probe successful"); -+ return 0; -+} -+ -+static void max9x_des_remove(struct i2c_client *client) -+{ -+ struct device *dev = &client->dev; -+ struct max9x_common *des = NULL; -+ -+ dev_info(dev, "Removing"); -+ -+ des = max9x_client_to_common(client); -+ -+ max9x_destroy(des); -+ -+} -+ -+static int max9x_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_resume(common); -+} -+ -+static int max9x_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct max9x_common *common = max9x_client_to_common(client); -+ -+ return max9x_common_suspend(common); -+} -+ -+static const struct dev_pm_ops max9x_des_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(max9x_suspend, max9x_resume) -+}; -+ -+static struct i2c_driver max9x_driver = { -+ .driver = { -+ .name = "max9x", -+ .of_match_table = max9x_of_match, -+ .pm = &max9x_des_pm_ops, -+ }, -+ .probe = max9x_des_probe, -+ .remove = max9x_des_remove, -+ .id_table = max9x_id, -+}; -+ -+module_i2c_driver(max9x_driver); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Josh Watts "); -+MODULE_AUTHOR("Yan, Dongcheng "); -+MODULE_DESCRIPTION("Common logic for Maxim GMSL serializers & deserializers"); -diff --git a/drivers/media/i2c/max9x/serdes.h b/drivers/media/i2c/max9x/serdes.h -new file mode 100644 -index 000000000000..227f61e03229 ---- /dev/null -+++ b/drivers/media/i2c/max9x/serdes.h -@@ -0,0 +1,460 @@ -+/* -+ * serdes.h -+ * -+ * Copyright (c) 2018-2020 D3 Engineering. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms and conditions of the GNU General Public License, -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope it will be useful, but WITHOUT -+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -+ * more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program. If not, see . -+ */ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (c) 2025 Intel Corporation. -+ -+#ifndef _SERDES_H_ -+#define _SERDES_H_ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) -+#include "ipu6-isys.h" -+#endif -+#include -+#include "max9x_pdata.h" -+ -+#define MAX9X_VDD_REGULATOR_NAME "vdd" -+#define MAX9X_POC_REGULATOR_NAME "poc" -+#define MAX9X_RESET_GPIO_NAME "reset" -+#define MAX9X_DEV_ID 0xD -+#define MAX9X_DEV_REV_FIELD GENMASK(3, 0) -+ -+/*Used for device attributes*/ -+#define ATTR_NAME_LEN (30) /* arbitrary number used to allocate an attribute */ -+#define ATTR_READ_ONLY (0444) -+ -+#define MAX9X_LINK_FREQ_MBPS_TO_HZ(mbps) (((unsigned long long)(mbps)*1000000ULL)/2ULL) -+#define MAX9X_LINK_FREQ_HZ_TO_MBPS(hz) (((unsigned long long)(hz)*2ULL)/1000000ULL) -+#define MAX9X_LINK_FREQ_MBPS_TO_REG(mbps) ((mbps)/100U) -+ -+#define MAX9X_FIELD_PREP(_mask, _val) \ -+ ({ \ -+ ((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \ -+ }) -+ -+#define TRY(err, expr) \ -+ do {\ -+ err = expr; \ -+ if (err) { \ -+ return err; \ -+ } \ -+ } while (0) -+ -+#define TRY_DEV_HERE(err, expr, dev) \ -+ do { \ -+ err = expr; \ -+ if (err) { \ -+ dev_err((dev), \ -+ "%s failed (%d) in %s() at %s:%d", #expr, \ -+ (err), __func__, __FILE__, __LINE__); \ -+ return err; \ -+ } \ -+ } while (0) -+ -+/* dt is defined in soft_dt_x, such as BACKTOP15(0x316).[5:0] */ -+#define MIPI_CSI2_TYPE_YUV422_8 0x1e -+#define MIPI_CSI2_TYPE_YUV422_10 0x1f -+#define MIPI_CSI2_TYPE_RGB565 0x22 -+#define MIPI_CSI2_TYPE_RGB888 0x24 -+#define MIPI_CSI2_TYPE_RAW8 0x2a -+#define MIPI_CSI2_TYPE_RAW10 0x2b -+#define MIPI_CSI2_TYPE_RAW12 0x2c -+ -+static inline int mbus_code_to_csi_dt(int code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return MIPI_CSI2_TYPE_RGB565; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return MIPI_CSI2_TYPE_RGB888; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return MIPI_CSI2_TYPE_YUV422_10; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ case MEDIA_BUS_FMT_VYUY8_1X16: -+ return MIPI_CSI2_TYPE_YUV422_8; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return MIPI_CSI2_TYPE_RAW12; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return MIPI_CSI2_TYPE_RAW10; -+ case MEDIA_BUS_FMT_Y8_1X8: -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return MIPI_CSI2_TYPE_RAW8; -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct regmap_config max9x_regmap_config = { -+ .reg_bits = 16, -+ .val_bits = 8, -+}; -+ -+enum max9x_chip_type { -+ MAX9296 = 0, -+ MAX96724 = 1, -+ MAX9295, -+ MAX96717, -+ MAX96724F, -+ MAX96724R, -+}; -+ -+enum max9x_serdes_type { -+ MAX9X_DESERIALIZER = 0, -+ MAX9X_SERIALIZER = 1, -+}; -+ -+struct max9x_serdes_rate_table { -+ unsigned int val; -+ unsigned int freq_mhz; -+}; -+ -+struct max9x_serdes_csi_config { -+ struct max9x_serdes_phy_map *map; -+ unsigned int num_maps; -+ enum v4l2_mbus_type bus_type; -+ unsigned int num_lanes; -+ unsigned int freq_mhz; -+ unsigned int initial_deskew_width; -+ bool auto_init_deskew_enabled; -+ bool auto_start; -+}; -+ -+struct max9x_serdes_pipe_config { -+ // Deserializer -+ unsigned int src_link; -+ unsigned int src_pipe; -+ struct max9x_serdes_mipi_map *map; -+ unsigned int num_maps; -+ -+ // Serializer -+ //TODO: dst_link? -+ unsigned int src_csi; -+ unsigned int *data_type; -+ unsigned int num_data_types; -+ //TODO: MIPI VC filter mask -+ -+ /* -+ * Which bpp value to double i.e. dbl_pixel_bpp = 10 will -+ * cause 10 bit data to be transmitted together. -+ */ -+ //TODO: Multiple dbl bpp values -+ unsigned int dbl_pixel_bpp; -+ -+ /* -+ * Software override for bits per pixel. This is compatible with -+ * double bpp. These are the min/max values before padding -+ * and after doubling. Leave either 0 to disable. -+ */ -+ unsigned int soft_min_pixel_bpp; -+ unsigned int soft_max_pixel_bpp; -+ -+ //TODO: MIPI DT override -+}; -+ -+struct max9x_serdes_serial_config { -+ enum max9x_serdes_link_type link_type; -+ unsigned int rx_freq_mhz; // Previously forward_freq_mhz -+ unsigned int tx_freq_mhz; // Previously back_freq_mhz -+}; -+ -+struct max9x_serdes_v4l { -+ struct mutex lock; -+ struct max9x_common *common; -+ struct v4l2_subdev sd; -+ struct v4l2_ctrl_handler ctrl_handler; -+ struct media_pad *pads; -+ int num_pads; -+ -+ struct v4l2_ctrl *link_freq; // CSI link frequency, used to determine ISP clock -+ struct v4l2_mbus_framefmt *ffmts; -+ int ref_count; -+}; -+ -+struct max9x_serdes_csi_link { -+ bool enabled; -+ unsigned int usecount; -+ struct max9x_serdes_csi_config config; -+}; -+ -+struct max9x_serdes_video_pipe { -+ bool enabled; -+ //TODO: Anything else need to be tracked? -+ struct max9x_serdes_pipe_config config; -+}; -+ -+struct max9x_serdes_serial_link { -+ bool enabled; -+ bool detected; -+ struct regulator *poc_regulator; /* feeds the serializer, imager */ -+ bool regulator_enabled; -+ struct { -+ struct i2c_client *client; -+ struct max9x_subdev_pdata *pdata; -+ } remote; -+ struct regmap *map; -+ struct max9x_serdes_serial_config config; -+ struct device_attribute *link_lock_status; -+}; -+ -+struct max9x_serdes_line_fault { -+ bool enabled; -+ struct device_attribute *line_fault_status; -+}; -+ -+struct max9x_common { -+ struct max9x_desc *des; -+ struct device *dev; -+ struct i2c_client *client; -+ struct regmap *map; -+ struct i2c_client *phys_client; -+ struct regmap *phys_map; -+ struct i2c_mux_core *muxc; -+ struct gpio_chip gpio_chip; -+ enum max9x_serdes_type type; -+ -+ struct gpio_desc *reset_gpio; -+ struct regulator *vdd_regulator; -+ bool regulator_enabled; -+ -+ struct max9x_common_ops *common_ops; -+ struct max9x_serial_link_ops *serial_link_ops; -+ struct max9x_csi_link_ops *csi_link_ops; -+ struct max9x_line_fault_ops *line_fault_ops; -+ struct max9x_translation_ops *translation_ops; -+ -+ struct max9x_serdes_csi_link *csi_link; -+ int num_csi_links; -+ -+ struct max9x_serdes_video_pipe *video_pipe; -+ int num_video_pipes; -+ -+ struct max9x_serdes_serial_link *serial_link; -+ int num_serial_links; -+ -+ struct max9x_serdes_line_fault *line_fault; -+ int num_line_faults; -+ -+ struct max9x_serdes_v4l v4l; -+ -+ struct mutex link_mutex; -+ struct mutex isolate_mutex; -+ int isolated_link; -+ int selected_link; -+ bool external_refclk_enable; -+}; -+ -+int max9x_serdes_mhz_to_rate(struct max9x_serdes_rate_table *table, int len, unsigned int freq_mhz); -+struct max9x_common *max9x_sd_to_common(struct v4l2_subdev *sd); -+struct max9x_common *max9x_client_to_common(struct i2c_client *client); -+ -+enum max9x_element_type { -+ MAX9X_SERIAL_LINK = 0, -+ MAX9X_VIDEO_PIPE, -+ MAX9X_MIPI_MAP, -+ MAX9X_CSI_LINK, -+ MAX9X_LINE_FAULT, -+ MAX9X_DATA_TYPES -+}; -+ -+struct max9x_common_ops { -+ int (*soft_reset)(struct max9x_common *common); -+ int (*enable)(struct max9x_common *common); -+ int (*disable)(struct max9x_common *common); -+ int (*max_elements)(struct max9x_common *common, enum max9x_element_type element); -+ int (*verify_devid)(struct max9x_common *common); -+ int (*remap_addr)(struct max9x_common *common); -+ int (*remap_reset)(struct max9x_common *common); -+ int (*setup_gpio)(struct max9x_common *common); -+}; -+ -+struct max9x_serial_link_ops { -+ int (*enable)(struct max9x_common *common, unsigned int link); -+ int (*disable)(struct max9x_common *common, unsigned int link); -+ int (*select)(struct max9x_common *common, unsigned int link); -+ int (*deselect)(struct max9x_common *common, unsigned int link); -+ int (*get_locked)(struct max9x_common *common, unsigned int link, bool *locked); -+ int (*isolate)(struct max9x_common *common, unsigned int link); -+ int (*deisolate)(struct max9x_common *common, unsigned int link); -+}; -+ -+struct max9x_csi_link_ops { -+ int (*enable)(struct max9x_common *common, unsigned int link); -+ int (*disable)(struct max9x_common *common, unsigned int link); -+}; -+ -+struct max9x_translation_ops { -+ int (*add)(struct max9x_common *common, unsigned int i2c_id, unsigned int src, unsigned int dst); -+ int (*remove)(struct max9x_common *common, unsigned int i2c_id, unsigned int src, unsigned int dst); -+}; -+ -+struct max9x_line_fault_ops { -+ int (*enable)(struct max9x_common *common, unsigned int line); -+ int (*disable)(struct max9x_common *common, unsigned int line); -+ int (*get_status)(struct max9x_common *common, unsigned int line); -+}; -+ -+struct max9x_desc { -+ char dev_id; -+ char rev_reg; -+ enum max9x_serdes_type serdes_type; -+ enum max9x_chip_type chip_type; -+ int (*get_max9x_ops)(char dev_id, -+ struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+}; -+ -+int max9x_common_init_i2c_client(struct max9x_common *common, -+ struct i2c_client *client, -+ const struct regmap_config *regmap_config, -+ struct max9x_common_ops *common_ops, -+ struct max9x_serial_link_ops *serial_link_ops, -+ struct max9x_csi_link_ops *csi_link_ops, -+ struct max9x_line_fault_ops *lf_ops); -+void max9x_destroy(struct max9x_common *common); -+int max9x_common_resume(struct max9x_common *common); -+int max9x_common_suspend(struct max9x_common *common); -+int max9x_get_ops(char dev_id, struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max96724_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max96717_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max9296_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+extern int max9295_get_ops(struct max9x_common_ops **common_ops, -+ struct max9x_serial_link_ops **serial_ops, -+ struct max9x_csi_link_ops **csi_ops, -+ struct max9x_line_fault_ops **lf_ops, -+ struct max9x_translation_ops **trans_ops); -+/* -+ * Both DES and SER have their own i2c_mux_core: -+ * 1. SER's muxc does not provide select/deselect and has at most one link -+ * (which itself has all children of the SER/DES link) -+ * 2. DER's muxc has driver specific select/deselect, one or more links, -+ * each of which must have exactly one SER -+ * 3. SER's probe() performs re-numbering and address translation (if needed: -+ * i2c-mux means this is no longer strictly required) -+ * -+ * des -> i2c-mux -> link@0 -> ser -> sensor, eeprom, etc -+ * link@n -> ser -> sensor, eeprom, etc -+ * -+ * +-----+ -+ * | des | -+ * +--+--+ -+ * | -+ * | +--------+ -+ * +--+ link@0 | -+ * | +------+-+ -+ * | | +-----+ -+ * | +----+ ser | -+ * | +--+--+ -+ * | | -+ * | | +--------+ -+ * | +--+ link@0 | -+ * | +-----+--+ -+ * | | -+ * | | +--------+ -+ * | +----+ sensor | -+ * | | +--------+ -+ * | | -+ * | | +--------+ -+ * | +----+ eeprom | -+ * | +--------+ -+ * | -+ * | +--------+ -+ * +--+ link@n | -+ * +------+-+ -+ * | +-----+ -+ * +----+ ser | -+ * +--+--+ -+ * | -+ * | +--------+ -+ * +--+ link@0 | -+ * +-----+--+ -+ * | -+ * | +--------+ -+ * +----+ sensor | -+ * | +--------+ -+ * | -+ * | +--------+ -+ * +----+ eeprom | -+ * +--------+ -+ */ -+ -+// for pdata parse -+ -+#define SET_CSI_MAP(maps, i, _src_vc, _src_dt, _dst_vc, _dst_dt, _dst_csi) do { \ -+ (maps)[i].src_vc = _src_vc; \ -+ (maps)[i].src_dt = _src_dt; \ -+ (maps)[i].dst_vc = _dst_vc; \ -+ (maps)[i].dst_dt = _dst_dt; \ -+ (maps)[i].dst_csi = _dst_csi; \ -+} while (0) -+ -+#define SET_PHY_MAP(maps, i, _int_csi, _phy_ind, _phy_lane) do { \ -+ (maps)[i].int_csi = _int_csi; \ -+ (maps)[i].phy_ind = _phy_ind; \ -+ (maps)[i].phy_lane = _phy_lane; \ -+} while (0) -+ -+#endif // _SERDES_H_ -diff --git a/drivers/media/pci/intel/ipu7/Kconfig b/drivers/media/pci/intel/ipu7/Kconfig -new file mode 100644 -index 000000000000..91954fdadc8b ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/Kconfig -@@ -0,0 +1,45 @@ -+config VIDEO_INTEL_IPU7 -+ tristate "Intel IPU7 driver" -+ depends on ACPI || COMPILE_TEST -+ depends on VIDEO_DEV -+ depends on X86 && HAS_DMA -+ depends on IPU_BRIDGE || !IPU_BRIDGE -+ # -+ # This driver incorrectly tries to override the dma_ops. It should -+ # never have done that, but for now keep it working on architectures -+ # that use dma ops -+ # -+ depends on ARCH_HAS_DMA_OPS -+ select AUXILIARY_BUS -+ select IOMMU_IOVA -+ select VIDEO_V4L2_SUBDEV_API -+ select MEDIA_CONTROLLER -+ select VIDEOBUF2_DMA_SG -+ select V4L2_FWNODE -+ help -+ This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs -+ and used for capturing images and video from camera sensors. -+ -+ To compile this driver, say Y here! It contains 3 modules - -+ intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. -+ -+config VIDEO_INTEL_IPU7_MGC -+ bool "Compile for IPU7 MGC driver" -+ depends on VIDEO_INTEL_IPU7 -+ help -+ If selected, MGC device nodes would be created. -+ -+ Recommended for driver developers only. -+ -+ If you want to the MGC devices exposed to user as media entity, -+ you must select this option, otherwise no. -+ -+config VIDEO_INTEL_IPU7_ISYS_RESET -+ bool "IPU7 ISYS RESET" -+ depends on VIDEO_INTEL_IPU7 -+ default n -+ help -+ This option enables IPU7 ISYS reset feature to support -+ HDMI-MIPI converter hot-plugging. -+ -+ If doubt, say N here. -diff --git a/drivers/media/pci/intel/ipu7/Makefile b/drivers/media/pci/intel/ipu7/Makefile -new file mode 100644 -index 000000000000..50417017da3a ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/Makefile -@@ -0,0 +1,37 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2017 - 2025 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+intel-ipu7-objs += ipu7.o \ -+ ipu7-bus.o \ -+ ipu7-dma.o \ -+ ipu7-mmu.o \ -+ ipu7-buttress.o \ -+ ipu7-cpd.o \ -+ ipu7-syscom.o \ -+ ipu7-boot.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7.o -+ -+intel-ipu7-isys-objs += ipu7-isys.o \ -+ ipu7-isys-csi2.o \ -+ ipu7-isys-csi-phy.o \ -+ ipu7-fw-isys.o \ -+ ipu7-isys-video.o \ -+ ipu7-isys-queue.o \ -+ ipu7-isys-subdev.o -+ -+ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+intel-ipu7-isys-objs += ipu7-isys-tpg.o -+endif -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ -+ -+ccflags-y += -I$(src)/ -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -new file mode 100644 -index 000000000000..a1519c4fe661 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_boot_abi.h -@@ -0,0 +1,163 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_BOOT_ABI_H -+#define IPU7_FW_BOOT_ABI_H -+ -+#include "ipu7_fw_common_abi.h" -+#include "ipu7_fw_syscom_abi.h" -+ -+#define IA_GOFO_FWLOG_SEVERITY_CRIT (0U) -+#define IA_GOFO_FWLOG_SEVERITY_ERROR (1U) -+#define IA_GOFO_FWLOG_SEVERITY_WARNING (2U) -+#define IA_GOFO_FWLOG_SEVERITY_INFO (3U) -+#define IA_GOFO_FWLOG_SEVERITY_DEBUG (4U) -+#define IA_GOFO_FWLOG_SEVERITY_VERBOSE (5U) -+#define IA_GOFO_FWLOG_MAX_LOGGER_SOURCES (64U) -+ -+#define LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK BIT(0) -+#define LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK BIT(1) -+#define LOGGER_CONFIG_CHANNEL_ENABLE_ALL_BITMASK \ -+ (LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK | \ -+ LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK) -+ -+struct ia_gofo_logger_config { -+ u8 use_source_severity; -+ u8 source_severity[IA_GOFO_FWLOG_MAX_LOGGER_SOURCES]; -+ u8 use_channels_enable_bitmask; -+ u8 channels_enable_bitmask; -+ u8 padding[1]; -+ ia_gofo_addr_t hw_printf_buffer_base_addr; -+ u32 hw_printf_buffer_size_bytes; -+}; -+ -+#pragma pack(push, 1) -+ -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP \ -+ ((u32)IA_GOFO_FW_BOOT_ID_MAX) -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET (0U) -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PS_OFFSET \ -+ ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \ -+ (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)) -+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U) -+#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U) -+#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U) -+#define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP) -+#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) -+#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \ -+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U) -+ -+#define IA_GOFO_BOOT_RESERVED_SIZE (58U) -+#define IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE (IA_GOFO_BOOT_RESERVED_SIZE) -+#define IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS \ -+ (sizeof(ia_gofo_addr_t) + sizeof(ia_gofo_addr_t) + sizeof(u32)) -+ -+enum ia_gofo_buttress_reg_id { -+ IA_GOFO_FW_BOOT_CONFIG_ID = 0, -+ IA_GOFO_FW_BOOT_STATE_ID = 1, -+ IA_GOFO_FW_BOOT_RESERVED1_ID = IA_GOFO_FW_BOOT_STATE_ID, -+ IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2, -+ IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3, -+ IA_GOFO_FW_BOOT_RESERVED0_ID = IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID, -+ IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID = 4, -+ IA_GOFO_FW_BOOT_ID_MAX -+}; -+ -+enum ia_gofo_boot_uc_tile_frequency_units { -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ = 0, -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_HZ = 1, -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_N -+}; -+ -+#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \ -+ (0xdead0000U == ((boot_state) & 0xffff0000U)) -+ -+struct ia_gofo_boot_config { -+ u32 length; -+ struct ia_gofo_version_s config_version; -+ struct ia_gofo_msg_version_list client_version_support; -+ ia_gofo_addr_t pkg_dir; -+ ia_gofo_addr_t subsys_config; -+ u32 uc_tile_frequency; -+ u16 checksum; -+ u8 uc_tile_frequency_units; -+ u8 padding[1]; -+ u32 reserved[IA_GOFO_BOOT_RESERVED_SIZE]; -+ struct syscom_config_s syscom_context_config; -+}; -+ -+struct ia_gofo_secondary_boot_config { -+ u32 length; -+ struct ia_gofo_version_s config_version; -+ struct ia_gofo_msg_version_list client_version_support; -+ u8 reserved1[IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS]; -+ u16 checksum; -+ u8 padding[2]; -+ u32 reserved2[IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE]; -+ struct syscom_config_s syscom_context_config; -+}; -+ -+#pragma pack(pop) -+ -+#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U -+#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U -+#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U -+#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U -+#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U -+#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U -+#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U -+#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U -+#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U -+ -+enum ia_gofo_boot_state { -+ IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U, -+ IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U, -+ IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U, -+ IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U, -+ IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U, -+ IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U, -+ IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U, -+ IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U, -+ IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U, -+ IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR, -+ IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U, -+ IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U, -+ IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL = -+ IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DIRTY = -+ IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DTAG = -+ IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_CACHE = -+ IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_DOUBLE_EXCEPTION = -+ IA_GOFO_DOUBLE_EXCEPTION_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR = -+ IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR, -+ IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE = -+ 0xdead1012U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, -+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, -+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, -+ IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, -+ IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U, -+ IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -new file mode 100644 -index 000000000000..7bb6fac585a3 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_common_abi.h -@@ -0,0 +1,175 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_COMMOM_ABI_H -+#define IPU7_FW_COMMOM_ABI_H -+ -+#include -+ -+#pragma pack(push, 1) -+typedef u32 ia_gofo_addr_t; -+ -+#define IA_GOFO_ADDR_NULL (0U) -+ -+struct ia_gofo_version_s { -+ u8 patch; -+ u8 subminor; -+ u8 minor; -+ u8 major; -+}; -+ -+#define IA_GOFO_MSG_VERSION_INIT(major_val, minor_val, subminor_val, patch_val)\ -+ {.major = (major_val), .minor = (minor_val), .subminor = \ -+ (subminor_val), .patch = (patch_val)} -+ -+#define IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES (3U) -+#define IA_GOFO_MSG_RESERVED_SIZE (3U) -+ -+struct ia_gofo_msg_version_list { -+ u8 num_versions; -+ u8 reserved[IA_GOFO_MSG_RESERVED_SIZE]; -+ struct ia_gofo_version_s versions[IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES]; -+}; -+ -+#pragma pack(pop) -+ -+#define TLV_TYPE_PADDING (0U) -+ -+#pragma pack(push, 1) -+ -+#define IA_GOFO_ABI_BITS_PER_BYTE (8U) -+ -+struct ia_gofo_tlv_header { -+ u16 tlv_type; -+ u16 tlv_len32; -+}; -+ -+struct ia_gofo_tlv_list { -+ u16 num_elems; -+ u16 head_offset; -+}; -+ -+#define TLV_ITEM_ALIGNMENT ((u32)sizeof(u32)) -+#define TLV_MSG_ALIGNMENT ((u32)sizeof(u64)) -+#define TLV_LIST_ALIGNMENT TLV_ITEM_ALIGNMENT -+#pragma pack(pop) -+ -+#define IA_GOFO_MODULO(dividend, divisor) ((dividend) % (divisor)) -+ -+#define IA_GOFO_MSG_ERR_MAX_DETAILS (4U) -+#define IA_GOFO_MSG_ERR_OK (0U) -+#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU) -+#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U) -+#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code) -+ -+#pragma pack(push, 1) -+struct ia_gofo_msg_err { -+ u32 err_group; -+ u32 err_code; -+ u32 err_detail[IA_GOFO_MSG_ERR_MAX_DETAILS]; -+}; -+ -+#pragma pack(pop) -+ -+#define IA_GOFO_MSG_ERR_GROUP_APP_EXT_START (16U) -+#define IA_GOFO_MSG_ERR_GROUP_MAX (31U) -+#define IA_GOFO_MSG_ERR_GROUP_INTERNAL_START (IA_GOFO_MSG_ERR_GROUP_MAX + 1U) -+#define IA_GOFO_MSG_ERR_GROUP_RESERVED IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED -+#define IA_GOFO_MSG_ERR_GROUP_GENERAL 1 -+ -+enum ia_gofo_msg_err_general { -+ IA_GOFO_MSG_ERR_GENERAL_OK = IA_GOFO_MSG_ERR_OK, -+ IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_SMALL = 1, -+ IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_LARGE = 2, -+ IA_GOFO_MSG_ERR_GENERAL_DEVICE_STATE = 3, -+ IA_GOFO_MSG_ERR_GENERAL_ALIGNMENT = 4, -+ IA_GOFO_MSG_ERR_GENERAL_INDIRECT_REF_PTR_INVALID = 5, -+ IA_GOFO_MSG_ERR_GENERAL_INVALID_MSG_TYPE = 6, -+ IA_GOFO_MSG_ERR_GENERAL_SYSCOM_FAIL = 7, -+ IA_GOFO_MSG_ERR_GENERAL_N -+}; -+ -+#pragma pack(push, 1) -+#define IA_GOFO_MSG_TYPE_RESERVED 0 -+#define IA_GOFO_MSG_TYPE_INDIRECT 1 -+#define IA_GOFO_MSG_TYPE_LOG 2 -+#define IA_GOFO_MSG_TYPE_GENERAL_ERR 3 -+ -+struct ia_gofo_msg_header { -+ struct ia_gofo_tlv_header tlv_header; -+ struct ia_gofo_tlv_list msg_options; -+ u64 user_token; -+}; -+ -+struct ia_gofo_msg_header_ack { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_msg_err err; -+ -+}; -+ -+struct ia_gofo_msg_general_err { -+ struct ia_gofo_msg_header_ack header; -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+enum ia_gofo_msg_link_streaming_mode { -+ IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF = 0, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_DOFF = 1, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_BCLM = 2, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_BCSM_FIX = 3, -+ IA_GOFO_MSG_LINK_STREAMING_MODE_N -+}; -+ -+enum ia_gofo_soc_pbk_instance_id { -+ IA_GOFO_SOC_PBK_ID0 = 0, -+ IA_GOFO_SOC_PBK_ID1 = 1, -+ IA_GOFO_SOC_PBK_ID_N -+}; -+ -+#define IA_GOFO_MSG_LINK_PBK_MAX_SLOTS (2U) -+ -+struct ia_gofo_msg_indirect { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_tlv_header ref_header; -+ ia_gofo_addr_t ref_msg_ptr; -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+#define IA_GOFO_MSG_LOG_MAX_PARAMS (4U) -+#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U) -+ -+#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U) -+#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU) -+ -+struct ia_gofo_msg_log_info { -+ u16 log_counter; -+ u8 msg_parameter_types; -+ /* [0:0] is_out_of_order, [1:3] logger_channel, [4:7] reserved */ -+ u8 logger_opts; -+ u32 fmt_id; -+ u32 params[IA_GOFO_MSG_LOG_MAX_PARAMS]; -+}; -+ -+struct ia_gofo_msg_log_info_ts { -+ u64 msg_ts; -+ struct ia_gofo_msg_log_info log_info; -+}; -+ -+struct ia_gofo_msg_log { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_msg_log_info_ts log_info_ts; -+}; -+ -+#pragma pack(pop) -+ -+#define IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID (0U) -+#define IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID (1U) -+#define IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID (2U) -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -new file mode 100644 -index 000000000000..c3f62aaedd86 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_config_abi.h -@@ -0,0 +1,19 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2021 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_CONFIG_ABI_H -+#define IPU7_FW_CONFIG_ABI_H -+ -+#include -+ -+#define IPU_CONFIG_ABI_WDT_TIMER_DISABLED 0U -+#define IPU_CONFIG_ABI_CMD_TIMER_DISABLED 0U -+ -+struct ipu7_wdt_abi { -+ u32 wdt_timer1_us; -+ u32 wdt_timer2_us; -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -new file mode 100644 -index 000000000000..f161a605c500 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_insys_config_abi.h -@@ -0,0 +1,19 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2021 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_INSYS_CONFIG_ABI_H -+#define IPU7_FW_INSYS_CONFIG_ABI_H -+ -+#include "ipu7_fw_boot_abi.h" -+#include "ipu7_fw_config_abi.h" -+#include "ipu7_fw_isys_abi.h" -+ -+struct ipu7_insys_config { -+ u32 timeout_val_ms; -+ struct ia_gofo_logger_config logger_config; -+ struct ipu7_wdt_abi wdt_config; -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -new file mode 100644 -index 000000000000..45db85eb13ec ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_isys_abi.h -@@ -0,0 +1,459 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_ISYS_ABI_H -+#define IPU7_FW_ISYS_ABI_H -+ -+#include "ipu7_fw_common_abi.h" -+#include "ipu7_fw_isys_abi.h" -+ -+#define IPU_INSYS_MAX_OUTPUT_QUEUES (3U) -+#define IPU_INSYS_STREAM_ID_MAX (16U) -+ -+#define IPU_INSYS_MAX_INPUT_QUEUES (IPU_INSYS_STREAM_ID_MAX + 1U) -+#define IPU_INSYS_OUTPUT_FIRST_QUEUE (0U) -+#define IPU_INSYS_OUTPUT_LAST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES - 1U) -+#define IPU_INSYS_OUTPUT_MSG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE) -+#define IPU_INSYS_OUTPUT_LOG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE + 1U) -+#define IPU_INSYS_OUTPUT_RESERVED_QUEUE (IPU_INSYS_OUTPUT_LAST_QUEUE) -+#define IPU_INSYS_INPUT_FIRST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES) -+#define IPU_INSYS_INPUT_LAST_QUEUE \ -+ (IPU_INSYS_INPUT_FIRST_QUEUE + IPU_INSYS_MAX_INPUT_QUEUES - 1U) -+#define IPU_INSYS_INPUT_DEV_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE) -+#define IPU_INSYS_INPUT_MSG_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE + 1U) -+#define IPU_INSYS_INPUT_MSG_MAX_QUEUE (IPU_INSYS_MAX_INPUT_QUEUES - 1U) -+ -+#define MAX_OPINS_FOR_SINGLE_IPINS (3U) -+#define DEV_SEND_QUEUE_SIZE (IPU_INSYS_STREAM_ID_MAX) -+ -+#define PIN_PLANES_MAX (4U) -+ -+#define INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_INPUT \ -+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES -+ -+typedef u64 ipu7_insys_return_token; -+ -+enum ipu7_insys_resp_type { -+ IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0, -+ IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1, -+ IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2, -+ IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3, -+ IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4, -+ IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5, -+ IPU_INSYS_RESP_TYPE_PIN_DATA_READY = 6, -+ IPU_INSYS_RESP_TYPE_FRAME_SOF = 7, -+ IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, -+ IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, -+ IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, -+ IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, -+ N_IPU_INSYS_RESP_TYPE -+}; -+ -+enum ipu7_insys_send_type { -+ IPU_INSYS_SEND_TYPE_STREAM_OPEN = 0, -+ IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1, -+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE = 2, -+ IPU_INSYS_SEND_TYPE_STREAM_ABORT = 3, -+ IPU_INSYS_SEND_TYPE_STREAM_FLUSH = 4, -+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE = 5, -+ N_IPU_INSYS_SEND_TYPE -+}; -+ -+enum ipu7_insys_mipi_vc { -+ IPU_INSYS_MIPI_VC_0 = 0, -+ IPU_INSYS_MIPI_VC_1 = 1, -+ IPU_INSYS_MIPI_VC_2 = 2, -+ IPU_INSYS_MIPI_VC_3 = 3, -+ IPU_INSYS_MIPI_VC_4 = 4, -+ IPU_INSYS_MIPI_VC_5 = 5, -+ IPU_INSYS_MIPI_VC_6 = 6, -+ IPU_INSYS_MIPI_VC_7 = 7, -+ IPU_INSYS_MIPI_VC_8 = 8, -+ IPU_INSYS_MIPI_VC_9 = 9, -+ IPU_INSYS_MIPI_VC_10 = 10, -+ IPU_INSYS_MIPI_VC_11 = 11, -+ IPU_INSYS_MIPI_VC_12 = 12, -+ IPU_INSYS_MIPI_VC_13 = 13, -+ IPU_INSYS_MIPI_VC_14 = 14, -+ IPU_INSYS_MIPI_VC_15 = 15, -+ N_IPU_INSYS_MIPI_VC -+}; -+ -+enum ipu7_insys_mipi_port { -+ IPU_INSYS_MIPI_PORT_0 = 0, -+ IPU_INSYS_MIPI_PORT_1 = 1, -+ IPU_INSYS_MIPI_PORT_2 = 2, -+ IPU_INSYS_MIPI_PORT_3 = 3, -+ IPU_INSYS_MIPI_PORT_4 = 4, -+ IPU_INSYS_MIPI_PORT_5 = 5, -+ NA_IPU_INSYS_MIPI_PORT -+}; -+ -+enum ipu7_insys_frame_format_type { -+ IPU_INSYS_FRAME_FORMAT_NV11 = 0, -+ IPU_INSYS_FRAME_FORMAT_NV12 = 1, -+ IPU_INSYS_FRAME_FORMAT_NV12_16 = 2, -+ IPU_INSYS_FRAME_FORMAT_NV12_TILEY = 3, -+ IPU_INSYS_FRAME_FORMAT_NV16 = 4, -+ IPU_INSYS_FRAME_FORMAT_NV21 = 5, -+ IPU_INSYS_FRAME_FORMAT_NV61 = 6, -+ IPU_INSYS_FRAME_FORMAT_YV12 = 7, -+ IPU_INSYS_FRAME_FORMAT_YV16 = 8, -+ IPU_INSYS_FRAME_FORMAT_YUV420 = 9, -+ IPU_INSYS_FRAME_FORMAT_YUV420_10 = 10, -+ IPU_INSYS_FRAME_FORMAT_YUV420_12 = 11, -+ IPU_INSYS_FRAME_FORMAT_YUV420_14 = 12, -+ IPU_INSYS_FRAME_FORMAT_YUV420_16 = 13, -+ IPU_INSYS_FRAME_FORMAT_YUV422 = 14, -+ IPU_INSYS_FRAME_FORMAT_YUV422_16 = 15, -+ IPU_INSYS_FRAME_FORMAT_UYVY = 16, -+ IPU_INSYS_FRAME_FORMAT_YUYV = 17, -+ IPU_INSYS_FRAME_FORMAT_YUV444 = 18, -+ IPU_INSYS_FRAME_FORMAT_YUV_LINE = 19, -+ IPU_INSYS_FRAME_FORMAT_RAW8 = 20, -+ IPU_INSYS_FRAME_FORMAT_RAW10 = 21, -+ IPU_INSYS_FRAME_FORMAT_RAW12 = 22, -+ IPU_INSYS_FRAME_FORMAT_RAW14 = 23, -+ IPU_INSYS_FRAME_FORMAT_RAW16 = 24, -+ IPU_INSYS_FRAME_FORMAT_RGB565 = 25, -+ IPU_INSYS_FRAME_FORMAT_PLANAR_RGB888 = 26, -+ IPU_INSYS_FRAME_FORMAT_RGBA888 = 27, -+ IPU_INSYS_FRAME_FORMAT_QPLANE6 = 28, -+ IPU_INSYS_FRAME_FORMAT_BINARY_8 = 29, -+ IPU_INSYS_FRAME_FORMAT_Y_8 = 30, -+ IPU_INSYS_FRAME_FORMAT_ARGB888 = 31, -+ IPU_INSYS_FRAME_FORMAT_BGRA888 = 32, -+ IPU_INSYS_FRAME_FORMAT_ABGR888 = 33, -+ N_IPU_INSYS_FRAME_FORMAT -+}; -+ -+#define IPU_INSYS_FRAME_FORMAT_RAW (IPU_INSYS_FRAME_FORMAT_RAW16) -+#define N_IPU_INSYS_MIPI_DATA_TYPE 0x40 -+ -+enum ipu7_insys_mipi_dt_rename_mode { -+ IPU_INSYS_MIPI_DT_NO_RENAME = 0, -+ IPU_INSYS_MIPI_DT_RENAMED_MODE = 1, -+ N_IPU_INSYS_MIPI_DT_MODE -+}; -+ -+#define IPU_INSYS_SEND_MSG_ENABLED 1U -+#define IPU_INSYS_SEND_MSG_DISABLED 0U -+ -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6) -+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7) -+#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_RESP ( \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED) -+#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_IRQ ( \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED | \ -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED) -+ -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7) -+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8) -+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9) -+#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK) -+#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \ -+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK) -+ -+#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0) -+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1) -+#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2) -+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3) -+#define IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4) -+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5) -+#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \ -+ IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \ -+ IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \ -+ IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY) -+#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \ -+ IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \ -+ IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \ -+ IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY) -+ -+enum ipu7_insys_output_link_dest { -+ IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, -+ IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, -+ IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 -+}; -+ -+enum ipu7_insys_dpcm_type { -+ IPU_INSYS_DPCM_TYPE_DISABLED = 0, -+ IPU_INSYS_DPCM_TYPE_10_8_10 = 1, -+ IPU_INSYS_DPCM_TYPE_12_8_12 = 2, -+ IPU_INSYS_DPCM_TYPE_12_10_12 = 3, -+ N_IPU_INSYS_DPCM_TYPE -+}; -+ -+enum ipu7_insys_dpcm_predictor { -+ IPU_INSYS_DPCM_PREDICTOR_1 = 0, -+ IPU_INSYS_DPCM_PREDICTOR_2 = 1, -+ N_IPU_INSYS_DPCM_PREDICTOR -+}; -+ -+enum ipu7_insys_send_queue_token_flag { -+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, -+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 -+}; -+ -+#pragma pack(push, 1) -+struct ipu7_insys_resolution { -+ u32 width; -+ u32 height; -+}; -+ -+struct ipu7_insys_capture_output_pin_payload { -+ u64 user_token; -+ ia_gofo_addr_t addr; -+ u8 pad[4]; -+}; -+ -+struct ipu7_insys_output_link { -+ u32 buffer_lines; -+ u16 foreign_key; -+ u16 granularity_pointer_update; -+ u8 msg_link_streaming_mode; -+ u8 pbk_id; -+ u8 pbk_slot_id; -+ u8 dest; -+ u8 use_sw_managed; -+ u8 is_snoop; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_output_cropping { -+ u16 line_top; -+ u16 line_bottom; -+#ifdef IPU8_INSYS_NEW_ABI -+ u16 column_left; -+ u16 column_right; -+#endif -+}; -+ -+struct ipu7_insys_output_dpcm { -+ u8 enable; -+ u8 type; -+ u8 predictor; -+ u8 pad; -+}; -+ -+#ifdef IPU8_INSYS_NEW_ABI -+enum ipu_insys_cfa_dim { -+ IPU_INSYS_CFA_DIM_2x2 = 0, -+ IPU_INSYS_CFA_DIM_4x4 = 1, -+ N_IPU_INSYS_CFA_DIM -+}; -+ -+#define IPU_INSYS_MAX_BINNING_FACTOR (4U) -+#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) -+#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) -+#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) -+#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) -+ -+struct ipu7_insys_upipe_output_pin { -+ ia_gofo_addr_t opaque_pin_cfg; -+ u16 plane_offset_1; -+ u16 plane_offset_2; -+ u8 single_uob_fifo; -+ u8 shared_uob_fifo; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_capture_output_pin_cfg { -+ struct ipu7_insys_capture_output_pin_payload pin_payload; -+ ia_gofo_addr_t upipe_capture_cfg; -+}; -+ -+#endif -+struct ipu7_insys_output_pin { -+ struct ipu7_insys_output_link link; -+ struct ipu7_insys_output_cropping crop; -+ struct ipu7_insys_output_dpcm dpcm; -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_upipe_output_pin upipe_pin_cfg; -+#endif -+ u32 stride; -+ u16 ft; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 upipe_enable; -+#endif -+ u8 send_irq; -+ u8 input_pin_id; -+ u8 early_ack_en; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 cfa_dim; -+ u8 binning_factor; -+#else -+ u8 pad[3]; -+#endif -+}; -+ -+struct ipu7_insys_input_pin { -+ struct ipu7_insys_resolution input_res; -+ u16 sync_msg_map; -+ u8 dt; -+ u8 disable_mipi_unpacking; -+ u8 dt_rename_mode; -+ u8 mapped_dt; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_stream_cfg { -+ struct ipu7_insys_input_pin input_pins[4]; -+ struct ipu7_insys_output_pin output_pins[4]; -+ u16 stream_msg_map; -+ u8 port_id; -+ u8 vc; -+ u8 nof_input_pins; -+ u8 nof_output_pins; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_buffset { -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_capture_output_pin_cfg output_pins[4]; -+#else -+ struct ipu7_insys_capture_output_pin_payload output_pins[4]; -+#endif -+ u8 capture_msg_map; -+ u8 frame_id; -+ u8 skip_frame; -+ u8 pad[5]; -+}; -+ -+struct ipu7_insys_resp { -+ u64 buf_id; -+ struct ipu7_insys_capture_output_pin_payload pin; -+ struct ia_gofo_msg_err error_info; -+ u32 timestamp[2]; -+ u8 type; -+ u8 msg_link_streaming_mode; -+ u8 stream_id; -+ u8 pin_id; -+ u8 frame_id; -+ u8 skip_frame; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_resp_queue_token { -+ struct ipu7_insys_resp resp_info; -+}; -+ -+struct ipu7_insys_send_queue_token { -+ u64 buf_handle; -+ ia_gofo_addr_t addr; -+ u16 stream_id; -+ u8 send_type; -+ u8 flag; -+}; -+ -+#pragma pack(pop) -+ -+enum insys_msg_err_stream { -+ INSYS_MSG_ERR_STREAM_OK = IA_GOFO_MSG_ERR_OK, -+ INSYS_MSG_ERR_STREAM_STREAM_ID = 1, -+ INSYS_MSG_ERR_STREAM_MAX_OPINS = 2, -+ INSYS_MSG_ERR_STREAM_MAX_IPINS = 3, -+ INSYS_MSG_ERR_STREAM_STREAM_MESSAGES_MAP = 4, -+ INSYS_MSG_ERR_STREAM_SYNC_MESSAGES_MAP = 5, -+ INSYS_MSG_ERR_STREAM_SENSOR_TYPE = 6, -+ INSYS_MSG_ERR_STREAM_FOREIGN_KEY = 7, -+ INSYS_MSG_ERR_STREAM_STREAMING_MODE = 8, -+ INSYS_MSG_ERR_STREAM_DPCM_EN = 9, -+ INSYS_MSG_ERR_STREAM_DPCM_TYPE = 10, -+ INSYS_MSG_ERR_STREAM_DPCM_PREDICTOR = 11, -+ INSYS_MSG_ERR_STREAM_GRANULARITY_POINTER_UPDATE = 12, -+ INSYS_MSG_ERR_STREAM_MPF_LUT_ENTRY_RESOURCES_BUSY = 13, -+ INSYS_MSG_ERR_STREAM_MPF_DEV_ID = 14, -+ INSYS_MSG_ERR_STREAM_BUFFER_LINES = 15, -+ INSYS_MSG_ERR_STREAM_IPIN_ID = 16, -+ INSYS_MSG_ERR_STREAM_DATA_TYPE = 17, -+ INSYS_MSG_ERR_STREAM_STREAMING_PROTOCOL_STATE = 18, -+ INSYS_MSG_ERR_STREAM_SYSCOM_FLUSH = 19, -+ INSYS_MSG_ERR_STREAM_MIPI_VC = 20, -+ INSYS_MSG_ERR_STREAM_STREAM_SRC = 21, -+ INSYS_MSG_ERR_STREAM_PBK_ID = 22, -+ INSYS_MSG_ERR_STREAM_CMD_QUEUE_DEALLOCATE = 23, -+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES = 24, -+ INSYS_MSG_ERR_STREAM_IPIN_CONFIGURATION = 25, -+ INSYS_MSG_ERR_STREAM_INVALID_STATE = 26, -+ INSYS_MSG_ERR_STREAM_SW_MANAGED = 27, -+ INSYS_MSG_ERR_STREAM_PBK_SLOT_ID = 28, -+ INSYS_MSG_ERR_STREAM_FLUSH_TIMEOUT = 29, -+ INSYS_MSG_ERR_STREAM_IPIN_WIDTH = 30, -+ INSYS_MSG_ERR_STREAM_IPIN_HEIGHT = 31, -+ INSYS_MSG_ERR_STREAM_OUTPUT_PIN_EARLY_ACK_EN = 32, -+ INSYS_MSG_ERR_STREAM_INCONSISTENT_PARAMS = 33, -+ INSYS_MSG_ERR_STREAM_PLANE_COUNT = 34, -+ INSYS_MSG_ERR_STREAM_FRAME_FORMAT_TYPE = 35, -+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36, -+ INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37, -+ INSYS_MSG_ERR_STREAM_CLOSED = 38, -+ INSYS_MSG_ERR_STREAM_N -+}; -+ -+enum insys_msg_err_capture { -+ INSYS_MSG_ERR_CAPTURE_OK = IA_GOFO_MSG_ERR_OK, -+ INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1, -+ INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2, -+ INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3, -+ INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4, -+ INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5, -+ INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6, -+ INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7, -+ INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8, -+ INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9, -+ INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10, -+ INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11, -+ INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12, -+ INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20, -+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21, -+ INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22, -+ INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23, -+ INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24, -+ INSYS_MSG_ERR_CAPTURE_N -+}; -+ -+enum insys_msg_err_groups { -+ INSYS_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -+ INSYS_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -+ INSYS_MSG_ERR_GROUP_STREAM = 2, -+ INSYS_MSG_ERR_GROUP_CAPTURE = 3, -+ INSYS_MSG_ERR_GROUP_N, -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -new file mode 100644 -index 000000000000..8a78dd0936df ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_msg_abi.h -@@ -0,0 +1,465 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_MSG_ABI_H -+#define IPU7_FW_MSG_ABI_H -+ -+#include "ipu7_fw_common_abi.h" -+ -+#pragma pack(push, 1) -+enum ipu7_msg_type { -+ IPU_MSG_TYPE_RESERVED = IA_GOFO_MSG_TYPE_RESERVED, -+ IPU_MSG_TYPE_INDIRECT = IA_GOFO_MSG_TYPE_INDIRECT, -+ IPU_MSG_TYPE_DEV_LOG = IA_GOFO_MSG_TYPE_LOG, -+ IPU_MSG_TYPE_GENERAL_ERR = IA_GOFO_MSG_TYPE_GENERAL_ERR, -+ IPU_MSG_TYPE_DEV_OPEN = 4, -+ IPU_MSG_TYPE_DEV_OPEN_ACK = 5, -+ IPU_MSG_TYPE_GRAPH_OPEN = 6, -+ IPU_MSG_TYPE_GRAPH_OPEN_ACK = 7, -+ IPU_MSG_TYPE_TASK_REQ = 8, -+ IPU_MSG_TYPE_TASK_DONE = 9, -+ IPU_MSG_TYPE_GRAPH_CLOSE = 10, -+ IPU_MSG_TYPE_GRAPH_CLOSE_ACK = 11, -+ IPU_MSG_TYPE_DEV_CLOSE = 12, -+ IPU_MSG_TYPE_DEV_CLOSE_ACK = 13, -+ IPU_MSG_TYPE_TERM_EVENT = 14, -+ IPU_MSG_TYPE_N, -+}; -+ -+#define IPU_MSG_MAX_NODE_TERMS (64U) -+#define IPU_MSG_MAX_FRAGS (7U) -+ -+enum ipu7_msg_node_type { -+ IPU_MSG_NODE_TYPE_PAD = 0, -+ IPU_MSG_NODE_TYPE_BASE, -+ IPU_MSG_NODE_TYPE_N -+}; -+ -+#define IPU_MSG_NODE_MAX_DEVICES (128U) -+#define DEB_NUM_UINT32 (IPU_MSG_NODE_MAX_DEVICES / (sizeof(u32) * 8U)) -+ -+typedef u32 ipu7_msg_teb_t[2]; -+typedef u32 ipu7_msg_deb_t[DEB_NUM_UINT32]; -+ -+#define IPU_MSG_NODE_MAX_ROUTE_ENABLES (128U) -+#define RBM_NUM_UINT32 (IPU_MSG_NODE_MAX_ROUTE_ENABLES / (sizeof(u32) * 8U)) -+ -+typedef u32 ipu7_msg_rbm_t[RBM_NUM_UINT32]; -+ -+enum ipu7_msg_node_profile_type { -+ IPU_MSG_NODE_PROFILE_TYPE_PAD = 0, -+ IPU_MSG_NODE_PROFILE_TYPE_BASE, -+ IPU_MSG_NODE_PROFILE_TYPE_CB, -+ IPU_MSG_NODE_PROFILE_TYPE_N -+}; -+ -+struct ipu7_msg_node_profile { -+ struct ia_gofo_tlv_header tlv_header; -+ ipu7_msg_teb_t teb; -+}; -+ -+struct ipu7_msg_cb_profile { -+ struct ipu7_msg_node_profile profile_base; -+ ipu7_msg_deb_t deb; -+ ipu7_msg_rbm_t rbm; -+ ipu7_msg_rbm_t reb; -+}; -+ -+#define IPU_MSG_NODE_MAX_PROFILES (2U) -+#define IPU_MSG_NODE_DEF_PROFILE_IDX (0U) -+#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU) -+ -+#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU) -+#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU) -+#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU) -+ -+struct ipu7_msg_node { -+ struct ia_gofo_tlv_header tlv_header; -+ u8 node_rsrc_id; -+ u8 node_ctx_id; -+ u8 num_frags; -+ u8 reserved[1]; -+ struct ia_gofo_tlv_list profiles_list; -+ struct ia_gofo_tlv_list terms_list; -+ struct ia_gofo_tlv_list node_options; -+}; -+ -+enum ipu7_msg_node_option_types { -+ IPU_MSG_NODE_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_NODE_OPTION_TYPES_N -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+ -+enum ipu7_msg_link_type { -+ IPU_MSG_LINK_TYPE_PAD = 0, -+ IPU_MSG_LINK_TYPE_GENERIC = 1, -+ IPU_MSG_LINK_TYPE_N -+}; -+ -+enum ipu7_msg_link_option_types { -+ IPU_MSG_LINK_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_LINK_OPTION_TYPES_CMPRS = 1, -+ IPU_MSG_LINK_OPTION_TYPES_N -+}; -+ -+enum ipu7_msg_link_cmprs_option_bit_depth { -+ IPU_MSG_LINK_CMPRS_OPTION_8BPP = 0, -+ IPU_MSG_LINK_CMPRS_OPTION_10BPP = 1, -+ IPU_MSG_LINK_CMPRS_OPTION_12BPP = 2, -+}; -+ -+#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM (128U) -+#define IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE (5U) -+#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_NUM_MAX \ -+ (IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM - 1U) -+ -+struct ipu7_msg_link_cmprs_plane_desc { -+ u8 plane_enable; -+ u8 cmprs_enable; -+ u8 encoder_plane_id; -+ u8 decoder_plane_id; -+ u8 cmprs_is_lossy; -+ u8 cmprs_is_footprint; -+ u8 bit_depth; -+ u8 space_saving_numerator; -+ u32 pixels_offset; -+ u32 ts_offset; -+ u32 tile_row_to_tile_row_stride; -+ u32 rows_of_tiles; -+ u32 lossy_cfg[IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE]; -+}; -+ -+#define IPU_MSG_LINK_CMPRS_MAX_PLANES (2U) -+#define IPU_MSG_LINK_CMPRS_NO_ALIGN_INTERVAL (0U) -+#define IPU_MSG_LINK_CMPRS_MIN_ALIGN_INTERVAL (16U) -+#define IPU_MSG_LINK_CMPRS_MAX_ALIGN_INTERVAL (1024U) -+struct ipu7_msg_link_cmprs_option { -+ struct ia_gofo_tlv_header header; -+ u32 cmprs_buf_size; -+ u16 align_interval; -+ u8 reserved[2]; -+ struct ipu7_msg_link_cmprs_plane_desc plane_descs[2]; -+}; -+ -+struct ipu7_msg_link_ep { -+ u8 node_ctx_id; -+ u8 term_id; -+}; -+ -+struct ipu7_msg_link_ep_pair { -+ struct ipu7_msg_link_ep ep_src; -+ struct ipu7_msg_link_ep ep_dst; -+}; -+ -+#define IPU_MSG_LINK_FOREIGN_KEY_NONE (65535U) -+#define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U) -+#define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U) -+#define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U) -+#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU) -+ -+struct ipu7_msg_link { -+ struct ia_gofo_tlv_header tlv_header; -+ struct ipu7_msg_link_ep_pair endpoints; -+ u16 foreign_key; -+ u8 streaming_mode; -+ u8 pbk_id; -+ u8 pbk_slot_id; -+ u8 delayed_link; -+ u8 reserved[2]; -+ struct ia_gofo_tlv_list link_options; -+}; -+ -+#pragma pack(pop) -+ -+enum ipu7_msg_dev_state { -+ IPU_MSG_DEV_STATE_CLOSED = 0, -+ IPU_MSG_DEV_STATE_OPEN_WAIT = 1, -+ IPU_MSG_DEV_STATE_OPEN = 2, -+ IPU_MSG_DEV_STATE_CLOSE_WAIT = 3, -+ IPU_MSG_DEV_STATE_N -+}; -+ -+enum ipu7_msg_graph_state { -+ IPU_MSG_GRAPH_STATE_CLOSED = 0, -+ IPU_MSG_GRAPH_STATE_OPEN_WAIT = 1, -+ IPU_MSG_GRAPH_STATE_OPEN = 2, -+ IPU_MSG_GRAPH_STATE_CLOSE_WAIT = 3, -+ IPU_MSG_GRAPH_STATE_N -+}; -+ -+enum ipu7_msg_task_state { -+ IPU_MSG_TASK_STATE_DONE = 0, -+ IPU_MSG_TASK_STATE_WAIT_DONE = 1, -+ IPU_MSG_TASK_STATE_N -+}; -+ -+enum ipu7_msg_err_groups { -+ IPU_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED, -+ IPU_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL, -+ IPU_MSG_ERR_GROUP_DEVICE = 2, -+ IPU_MSG_ERR_GROUP_GRAPH = 3, -+ IPU_MSG_ERR_GROUP_TASK = 4, -+ IPU_MSG_ERR_GROUP_N, -+}; -+ -+#pragma pack(push, 1) -+struct ipu7_msg_task { -+ struct ia_gofo_msg_header header; -+ u8 graph_id; -+ u8 profile_idx; -+ u8 node_ctx_id; -+ u8 frame_id; -+ u8 frag_id; -+ u8 req_done_msg; -+ u8 req_done_irq; -+ u8 reserved[1]; -+ ipu7_msg_teb_t payload_reuse_bm; -+ ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; -+}; -+ -+struct ipu7_msg_task_done { -+ struct ia_gofo_msg_header_ack header; -+ u8 graph_id; -+ u8 frame_id; -+ u8 node_ctx_id; -+ u8 profile_idx; -+ u8 frag_id; -+ u8 reserved[3]; -+}; -+ -+enum ipu7_msg_err_task { -+ IPU_MSG_ERR_TASK_OK = IA_GOFO_MSG_ERR_OK, -+ IPU_MSG_ERR_TASK_GRAPH_ID = 1, -+ IPU_MSG_ERR_TASK_NODE_CTX_ID = 2, -+ IPU_MSG_ERR_TASK_PROFILE_IDX = 3, -+ IPU_MSG_ERR_TASK_CTX_MEMORY_TASK = 4, -+ IPU_MSG_ERR_TASK_TERM_PAYLOAD_PTR = 5, -+ IPU_MSG_ERR_TASK_FRAME_ID = 6, -+ IPU_MSG_ERR_TASK_FRAG_ID = 7, -+ IPU_MSG_ERR_TASK_EXEC_EXT = 8, -+ IPU_MSG_ERR_TASK_EXEC_SBX = 9, -+ IPU_MSG_ERR_TASK_EXEC_INT = 10, -+ IPU_MSG_ERR_TASK_EXEC_UNKNOWN = 11, -+ IPU_MSG_ERR_TASK_PRE_EXEC = 12, -+ IPU_MSG_ERR_TASK_N -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+enum ipu7_msg_term_type { -+ IPU_MSG_TERM_TYPE_PAD = 0, -+ IPU_MSG_TERM_TYPE_BASE, -+ IPU_MSG_TERM_TYPE_N, -+}; -+ -+#define IPU_MSG_TERM_EVENT_TYPE_NONE 0U -+#define IPU_MSG_TERM_EVENT_TYPE_PROGRESS 1U -+#define IPU_MSG_TERM_EVENT_TYPE_N (IPU_MSG_TERM_EVENT_TYPE_PROGRESS + 1U) -+ -+struct ipu7_msg_term { -+ struct ia_gofo_tlv_header tlv_header; -+ u8 term_id; -+ u8 event_req_bm; -+ u8 reserved[2]; -+ u32 payload_size; -+ struct ia_gofo_tlv_list term_options; -+}; -+ -+enum ipu7_msg_term_option_types { -+ IPU_MSG_TERM_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_TERM_OPTION_TYPES_N -+}; -+ -+struct ipu7_msg_term_event { -+ struct ia_gofo_msg_header header; -+ u8 graph_id; -+ u8 frame_id; -+ u8 node_ctx_id; -+ u8 profile_idx; -+ u8 frag_id; -+ u8 term_id; -+ u8 event_type; -+ u8 reserved[1]; -+ u64 event_ts; -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+#define IPU_MSG_DEVICE_SEND_MSG_ENABLED 1U -+#define IPU_MSG_DEVICE_SEND_MSG_DISABLED 0U -+ -+#define IPU_MSG_DEVICE_OPEN_SEND_RESP BIT(0) -+#define IPU_MSG_DEVICE_OPEN_SEND_IRQ BIT(1) -+ -+#define IPU_MSG_DEVICE_CLOSE_SEND_RESP BIT(0) -+#define IPU_MSG_DEVICE_CLOSE_SEND_IRQ BIT(1) -+ -+struct ipu7_msg_dev_open { -+ struct ia_gofo_msg_header header; -+ u32 max_graphs; -+ u8 dev_msg_map; -+ u8 enable_power_gating; -+ u8 reserved[2]; -+}; -+ -+struct ipu7_msg_dev_open_ack { -+ struct ia_gofo_msg_header_ack header; -+}; -+ -+struct ipu7_msg_dev_close { -+ struct ia_gofo_msg_header header; -+ u8 dev_msg_map; -+ u8 reserved[7]; -+}; -+ -+struct ipu7_msg_dev_close_ack { -+ struct ia_gofo_msg_header_ack header; -+}; -+ -+enum ipu7_msg_err_device { -+ IPU_MSG_ERR_DEVICE_OK = IA_GOFO_MSG_ERR_OK, -+ IPU_MSG_ERR_DEVICE_MAX_GRAPHS = 1, -+ IPU_MSG_ERR_DEVICE_MSG_MAP = 2, -+ IPU_MSG_ERR_DEVICE_N -+}; -+ -+#pragma pack(pop) -+ -+#pragma pack(push, 1) -+#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU) -+#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U -+#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U -+ -+#define IPU_MSG_GRAPH_OPEN_SEND_RESP BIT(0) -+#define IPU_MSG_GRAPH_OPEN_SEND_IRQ BIT(1) -+ -+#define IPU_MSG_GRAPH_CLOSE_SEND_RESP BIT(0) -+#define IPU_MSG_GRAPH_CLOSE_SEND_IRQ BIT(1) -+ -+struct ipu7_msg_graph_open { -+ struct ia_gofo_msg_header header; -+ struct ia_gofo_tlv_list nodes; -+ struct ia_gofo_tlv_list links; -+ u8 graph_id; -+ u8 graph_msg_map; -+ u8 reserved[6]; -+}; -+ -+enum ipu7_msg_graph_ack_option_types { -+ IPU_MSG_GRAPH_ACK_OPTION_TYPES_PADDING = 0, -+ IPU_MSG_GRAPH_ACK_TASK_Q_INFO, -+ IPU_MSG_GRAPH_ACK_OPTION_TYPES_N -+}; -+ -+struct ipu7_msg_graph_open_ack_task_q_info { -+ struct ia_gofo_tlv_header header; -+ u8 node_ctx_id; -+ u8 q_id; -+ u8 reserved[2]; -+}; -+ -+struct ipu7_msg_graph_open_ack { -+ struct ia_gofo_msg_header_ack header; -+ u8 graph_id; -+ u8 reserved[7]; -+}; -+ -+struct ipu7_msg_graph_close { -+ struct ia_gofo_msg_header header; -+ u8 graph_id; -+ u8 graph_msg_map; -+ u8 reserved[6]; -+}; -+ -+struct ipu7_msg_graph_close_ack { -+ struct ia_gofo_msg_header_ack header; -+ u8 graph_id; -+ u8 reserved[7]; -+}; -+ -+enum ipu7_msg_err_graph { -+ IPU_MSG_ERR_GRAPH_OK = IA_GOFO_MSG_ERR_OK, -+ IPU_MSG_ERR_GRAPH_GRAPH_STATE = 1, -+ IPU_MSG_ERR_GRAPH_MAX_GRAPHS = 2, -+ IPU_MSG_ERR_GRAPH_GRAPH_ID = 3, -+ IPU_MSG_ERR_GRAPH_NODE_CTX_ID = 4, -+ IPU_MSG_ERR_GRAPH_NODE_RSRC_ID = 5, -+ IPU_MSG_ERR_GRAPH_PROFILE_IDX = 6, -+ IPU_MSG_ERR_GRAPH_TERM_ID = 7, -+ IPU_MSG_ERR_GRAPH_TERM_PAYLOAD_SIZE = 8, -+ IPU_MSG_ERR_GRAPH_LINK_NODE_CTX_ID = 9, -+ IPU_MSG_ERR_GRAPH_LINK_TERM_ID = 10, -+ IPU_MSG_ERR_GRAPH_PROFILE_TYPE = 11, -+ IPU_MSG_ERR_GRAPH_NUM_FRAGS = 12, -+ IPU_MSG_ERR_GRAPH_QUEUE_ID_USAGE = 13, -+ IPU_MSG_ERR_GRAPH_QUEUE_OPEN = 14, -+ IPU_MSG_ERR_GRAPH_QUEUE_CLOSE = 15, -+ IPU_MSG_ERR_GRAPH_QUEUE_ID_TASK_REQ_MISMATCH = 16, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_FGRAPH = 17, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE = 18, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE_PROFILE = 19, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_TERM = 20, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_LINK = 21, -+ IPU_MSG_ERR_GRAPH_CTX_MSG_MAP = 22, -+ IPU_MSG_ERR_GRAPH_CTX_FOREIGN_KEY = 23, -+ IPU_MSG_ERR_GRAPH_CTX_STREAMING_MODE = 24, -+ IPU_MSG_ERR_GRAPH_CTX_PBK_RSRC = 25, -+ IPU_MSG_ERR_GRAPH_UNSUPPORTED_EVENT_TYPE = 26, -+ IPU_MSG_ERR_GRAPH_TOO_MANY_EVENTS = 27, -+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_CMPRS = 28, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_ALIGN_INTERVAL = 29, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_PLANE_ID = 30, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_UNSUPPORTED_MODE = 31, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_BIT_DEPTH = 32, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_STRIDE_ALIGNMENT = 33, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_SUB_BUFFER_ALIGNMENT = 34, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_ORDER = 35, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_OVERLAP = 36, -+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_BUFFER_TOO_SMALL = 37, -+ IPU_MSG_ERR_GRAPH_CTX_DELAYED_LINK = 38, -+ IPU_MSG_ERR_GRAPH_N -+}; -+ -+#pragma pack(pop) -+ -+#define FWPS_MSG_ABI_MAX_INPUT_QUEUES (60U) -+#define FWPS_MSG_ABI_MAX_OUTPUT_QUEUES (2U) -+#define FWPS_MSG_ABI_MAX_QUEUES \ -+ (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES + FWPS_MSG_ABI_MAX_INPUT_QUEUES) -+ -+#define FWPS_MSG_ABI_OUT_ACK_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID) -+#define FWPS_MSG_ABI_OUT_LOG_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID) -+#if (FWPS_MSG_ABI_OUT_LOG_QUEUE_ID >= FWPS_MSG_ABI_MAX_OUTPUT_QUEUES) -+#error "Maximum output queues configuration is too small to fit ACK and LOG \ -+queues" -+#endif -+#define FWPS_MSG_ABI_IN_DEV_QUEUE_ID (IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID) -+#define FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID (3U) -+#define FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID \ -+ (FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID + 1U) -+ -+#if (FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID >= FWPS_MSG_ABI_MAX_INPUT_QUEUES) -+#error "Maximum queues configuration is too small to fit minimum number of \ -+useful queues" -+#endif -+ -+#define FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID (FWPS_MSG_ABI_MAX_QUEUES - 1U) -+#define FWPS_MSG_ABI_IN_MAX_TASK_QUEUES \ -+ (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID - \ -+ FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID + 1U) -+#define FWPS_MSG_ABI_OUT_FIRST_QUEUE_ID (FWPS_MSG_ABI_OUT_ACK_QUEUE_ID) -+#define FWPS_MSG_ABI_OUT_LAST_QUEUE_ID (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES - 1U) -+#define FWPS_MSG_ABI_IN_FIRST_QUEUE_ID (FWPS_MSG_ABI_IN_DEV_QUEUE_ID) -+#define FWPS_MSG_ABI_IN_LAST_QUEUE_ID (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID) -+ -+#define FWPS_MSG_HOST2FW_MAX_SIZE (2U * 1024U) -+#define FWPS_MSG_FW2HOST_MAX_SIZE (256U) -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -new file mode 100644 -index 000000000000..0af04c8c6a88 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_psys_config_abi.h -@@ -0,0 +1,24 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ -+#define IPU7_PSYS_CONFIG_ABI_H_INCLUDED__ -+ -+#include -+ -+#include "ipu7_fw_boot_abi.h" -+#include "ipu7_fw_config_abi.h" -+ -+struct ipu7_psys_config { -+ u32 use_debug_manifest; -+ u32 timeout_val_ms; -+ u32 compression_support_enabled; -+ struct ia_gofo_logger_config logger_config; -+ struct ipu7_wdt_abi wdt_config; -+ u8 ipu_psys_debug_bitmask; -+ u8 padding[3]; -+}; -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -new file mode 100644 -index 000000000000..bfa5258d5b97 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/abi/ipu7_fw_syscom_abi.h -@@ -0,0 +1,49 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_SYSCOM_ABI_H -+#define IPU7_FW_SYSCOM_ABI_H -+ -+#include -+ -+#include "ipu7_fw_common_abi.h" -+ -+#pragma pack(push, 1) -+#define SYSCOM_QUEUE_MIN_CAPACITY 2U -+ -+struct syscom_queue_params_config { -+ ia_gofo_addr_t token_array_mem; -+ u16 token_size_in_bytes; -+ u16 max_capacity; -+}; -+ -+struct syscom_config_s { -+ u16 max_output_queues; -+ u16 max_input_queues; -+}; -+ -+#pragma pack(pop) -+ -+static inline struct syscom_queue_params_config * -+syscom_config_get_queue_configs(struct syscom_config_s *config) -+{ -+ return (struct syscom_queue_params_config *)(&config[1]); -+} -+ -+static inline const struct syscom_queue_params_config * -+syscom_config_get_queue_configs_const(const struct syscom_config_s *config) -+{ -+ return (const struct syscom_queue_params_config *)(&config[1]); -+} -+ -+#pragma pack(push, 1) -+struct syscom_queue_indices_s { -+ u32 read_index; -+ u32 write_index; -+}; -+ -+#pragma pack(pop) -+ -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.c b/drivers/media/pci/intel/ipu7/ipu7-boot.c -new file mode 100644 -index 000000000000..a88182d70173 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-boot.c -@@ -0,0 +1,431 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2022 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_boot_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-dma.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+ -+#define IPU_FW_START_STOP_TIMEOUT 2000 -+#define IPU_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC) -+#define BOOT_STATE_IS_CRITICAL(s) IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s) -+#define BOOT_STATE_IS_READY(s) ((s) == IA_GOFO_FW_BOOT_STATE_READY) -+#define BOOT_STATE_IS_INACTIVE(s) ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE) -+ -+struct ipu7_boot_context { -+ u32 base; -+ u32 dmem_address; -+ u32 status_ctrl_reg; -+ u32 fw_start_address_reg; -+ u32 fw_code_base_reg; -+}; -+ -+static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = { -+ { -+ /* ISYS */ -+ .dmem_address = IPU_ISYS_DMEM_OFFSET, -+ .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS, -+ .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR, -+ .fw_code_base_reg = IS_UC_CTRL_BASE -+ }, -+ { -+ /* PSYS */ -+ .dmem_address = IPU_PSYS_DMEM_OFFSET, -+ .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS, -+ .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR, -+ .fw_code_base_reg = PS_UC_CTRL_BASE -+ } -+}; -+ -+static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev, -+ enum ia_gofo_buttress_reg_id reg) -+{ -+ u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX; -+ -+ return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg); -+} -+ -+static void write_fw_boot_param(const struct ipu7_bus_device *adev, -+ enum ia_gofo_buttress_reg_id reg, -+ u32 val) -+{ -+ void __iomem *base = adev->isp->base; -+ -+ dev_dbg(&adev->auxdev.dev, -+ "write boot param reg: %d addr: %x val: 0x%x\n", -+ reg, get_fw_boot_reg_addr(adev, reg), val); -+ writel(val, base + get_fw_boot_reg_addr(adev, reg)); -+} -+ -+static u32 read_fw_boot_param(const struct ipu7_bus_device *adev, -+ enum ia_gofo_buttress_reg_id reg) -+{ -+ void __iomem *base = adev->isp->base; -+ -+ return readl(base + get_fw_boot_reg_addr(adev, reg)); -+} -+ -+static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ const struct device *dev = &adev->auxdev.dev; -+ u32 ucx_ctrl_status = ctx->status_ctrl_reg; -+ u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT; -+ void __iomem *base = adev->isp->base; -+ u32 val, val2; -+ int ret; -+ -+ dev_dbg(dev, "cell enter reset...\n"); -+ val = readl(base + ucx_ctrl_status); -+ dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n", -+ ucx_ctrl_status, val); -+ -+ dev_dbg(dev, "force cell reset...\n"); -+ val |= UCX_CTL_RESET; -+ val &= ~UCX_CTL_RUN; -+ -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ucx_ctrl_status, val); -+ writel(val, base + ucx_ctrl_status); -+ -+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -+ (val2 & 0x3U) == (val & 0x3U), 100, timeout); -+ if (ret) { -+ dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2); -+ return -ETIMEDOUT; -+ } -+ -+ dev_dbg(dev, "cell exit reset...\n"); -+ val = readl(base + ucx_ctrl_status); -+ WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN), -+ "cell status 0x%x", val); -+ -+ val &= ~(UCX_CTL_RESET | UCX_CTL_RUN); -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ucx_ctrl_status, val); -+ writel(val, base + ucx_ctrl_status); -+ -+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2, -+ (val2 & 0x3U) == (val & 0x3U), 100, timeout); -+ if (ret) { -+ dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2); -+ return -ETIMEDOUT; -+ } -+ -+ return 0; -+} -+ -+static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ void __iomem *base = adev->isp->base; -+ const struct device *dev = &adev->auxdev.dev; -+ u32 val; -+ -+ dev_dbg(dev, "starting cell...\n"); -+ val = readl(base + ctx->status_ctrl_reg); -+ WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN)); -+ -+ val &= ~UCX_CTL_RESET; -+ val |= UCX_CTL_RUN; -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ctx->status_ctrl_reg, val); -+ writel(val, base + ctx->status_ctrl_reg); -+} -+ -+static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ void __iomem *base = adev->isp->base; -+ const struct device *dev = &adev->auxdev.dev; -+ u32 val; -+ -+ dev_dbg(dev, "stopping cell...\n"); -+ -+ val = readl(base + ctx->status_ctrl_reg); -+ val &= ~UCX_CTL_RUN; -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ctx->status_ctrl_reg, val); -+ writel(val, base + ctx->status_ctrl_reg); -+ -+ /* Wait for uC transactions complete */ -+ usleep_range(10, 20); -+ -+ val = readl(base + ctx->status_ctrl_reg); -+ val |= UCX_CTL_RESET; -+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n", -+ ctx->status_ctrl_reg, val); -+ writel(val, base + ctx->status_ctrl_reg); -+} -+ -+static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev) -+{ -+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys]; -+ void __iomem *base = adev->isp->base; -+ -+ dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n", -+ ctx->fw_start_address_reg, adev->fw_entry); -+ writel(adev->fw_entry, base + ctx->fw_start_address_reg); -+ -+ return ipu7_boot_cell_reset(adev); -+} -+ -+static void init_boot_config(struct ia_gofo_boot_config *boot_config, -+ u32 length, u8 major) -+{ -+ /* syscom version, new syscom2 version */ -+ boot_config->length = length; -+ boot_config->config_version.major = 1U; -+ boot_config->config_version.minor = 0U; -+ boot_config->config_version.subminor = 0U; -+ boot_config->config_version.patch = 0U; -+ -+ /* msg version for task interface */ -+ boot_config->client_version_support.num_versions = 1U; -+ boot_config->client_version_support.versions[0].major = major; -+ boot_config->client_version_support.versions[0].minor = 0U; -+ boot_config->client_version_support.versions[0].subminor = 0U; -+ boot_config->client_version_support.versions[0].patch = 0U; -+} -+ -+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -+ struct syscom_queue_config *qconfigs, -+ int num_queues, u32 uc_freq, -+ dma_addr_t subsys_config, u8 major) -+{ -+ u32 total_queue_size_aligned = 0; -+ struct ipu7_syscom_context *syscom = adev->syscom; -+ struct ia_gofo_boot_config *boot_config; -+ struct syscom_queue_params_config *cfgs; -+ struct device *dev = &adev->auxdev.dev; -+ struct syscom_config_s *syscfg; -+ dma_addr_t queue_mem_dma_ptr; -+ void *queue_mem_ptr; -+ unsigned int i; -+ -+ dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n", -+ num_queues, uc_freq, &subsys_config); -+ /* Allocate boot config. */ -+ adev->boot_config_size = -+ sizeof(*cfgs) * num_queues + sizeof(*boot_config); -+ adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size, -+ &adev->boot_config_dma_addr, -+ GFP_KERNEL, 0); -+ if (!adev->boot_config) { -+ dev_err(dev, "Failed to allocate boot config.\n"); -+ return -ENOMEM; -+ } -+ -+ boot_config = adev->boot_config; -+ memset(boot_config, 0, sizeof(struct ia_gofo_boot_config)); -+ init_boot_config(boot_config, adev->boot_config_size, major); -+ boot_config->subsys_config = subsys_config; -+ -+ boot_config->uc_tile_frequency = uc_freq; -+ boot_config->uc_tile_frequency_units = -+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ; -+ boot_config->syscom_context_config.max_output_queues = -+ syscom->num_output_queues; -+ boot_config->syscom_context_config.max_input_queues = -+ syscom->num_input_queues; -+ -+ ipu7_dma_sync_single(adev, adev->boot_config_dma_addr, -+ adev->boot_config_size); -+ -+ for (i = 0; i < num_queues; i++) { -+ u32 queue_size = qconfigs[i].max_capacity * -+ qconfigs[i].token_size_in_bytes; -+ -+ queue_size = ALIGN(queue_size, 64U); -+ total_queue_size_aligned += queue_size; -+ qconfigs[i].queue_size = queue_size; -+ } -+ -+ /* Allocate queue memory */ -+ syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned, -+ &syscom->queue_mem_dma_addr, -+ GFP_KERNEL, 0); -+ if (!syscom->queue_mem) { -+ dev_err(dev, "Failed to allocate queue memory.\n"); -+ return -ENOMEM; -+ } -+ syscom->queue_mem_size = total_queue_size_aligned; -+ -+ syscfg = &boot_config->syscom_context_config; -+ cfgs = ipu7_syscom_get_queue_config(syscfg); -+ queue_mem_ptr = syscom->queue_mem; -+ queue_mem_dma_ptr = syscom->queue_mem_dma_addr; -+ for (i = 0; i < num_queues; i++) { -+ cfgs[i].token_array_mem = queue_mem_dma_ptr; -+ cfgs[i].max_capacity = qconfigs[i].max_capacity; -+ cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes; -+ qconfigs[i].token_array_mem = queue_mem_ptr; -+ queue_mem_dma_ptr += qconfigs[i].queue_size; -+ queue_mem_ptr += qconfigs[i].queue_size; -+ } -+ -+ ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr, -+ total_queue_size_aligned); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7"); -+ -+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_syscom_context *syscom = adev->syscom; -+ -+ if (syscom->queue_mem) { -+ ipu7_dma_free(adev, syscom->queue_mem_size, -+ syscom->queue_mem, -+ syscom->queue_mem_dma_addr, 0); -+ syscom->queue_mem = NULL; -+ syscom->queue_mem_dma_addr = 0; -+ } -+ -+ if (adev->boot_config) { -+ ipu7_dma_free(adev, adev->boot_config_size, -+ adev->boot_config, -+ adev->boot_config_dma_addr, 0); -+ adev->boot_config = NULL; -+ adev->boot_config_dma_addr = 0; -+ } -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7"); -+ -+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev) -+{ -+ const struct device *dev = &adev->auxdev.dev; -+ u32 timeout = IPU_FW_START_STOP_TIMEOUT; -+ void __iomem *base = adev->isp->base; -+ u32 boot_state, last_boot_state; -+ u32 indices_addr, msg_ver, id; -+ int ret; -+ -+ ret = ipu7_boot_cell_init(adev); -+ if (ret) -+ return ret; -+ -+ dev_dbg(dev, "start booting fw...\n"); -+ /* store "uninit" state to syscom/boot state reg */ -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -+ IA_GOFO_FW_BOOT_STATE_UNINIT); -+ /* -+ * Set registers to zero -+ * (not strictly required, but recommended for diagnostics) -+ */ -+ write_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0); -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0); -+ /* store firmware configuration address */ -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID, -+ adev->boot_config_dma_addr); -+ -+ /* Kick uC, then wait for boot complete */ -+ ipu7_boot_cell_start(adev); -+ -+ last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT; -+ while (timeout--) { -+ boot_state = read_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_STATE_ID); -+ if (boot_state != last_boot_state) { -+ dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n", -+ last_boot_state, boot_state); -+ last_boot_state = boot_state; -+ } -+ if (BOOT_STATE_IS_CRITICAL(boot_state) || -+ BOOT_STATE_IS_READY(boot_state)) -+ break; -+ usleep_range(1000, 1200); -+ } -+ -+ if (BOOT_STATE_IS_CRITICAL(boot_state)) { -+ ipu7_dump_fw_error_log(adev); -+ dev_err(dev, "critical boot state error 0x%x\n", boot_state); -+ return -EINVAL; -+ } else if (!BOOT_STATE_IS_READY(boot_state)) { -+ dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state); -+ return -ETIMEDOUT; -+ } -+ dev_dbg(dev, "fw boot done.\n"); -+ -+ /* Get FW syscom queue indices addr */ -+ id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID; -+ indices_addr = read_fw_boot_param(adev, id); -+ adev->syscom->queue_indices = base + indices_addr; -+ dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr); -+ -+ /* Get message version. */ -+ msg_ver = read_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID); -+ dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7"); -+ -+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev) -+{ -+ const struct device *dev = &adev->auxdev.dev; -+ u32 timeout = IPU_FW_START_STOP_TIMEOUT; -+ u32 boot_state; -+ -+ boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); -+ if (BOOT_STATE_IS_CRITICAL(boot_state) || -+ !BOOT_STATE_IS_READY(boot_state)) { -+ dev_err(dev, "fw not ready for shutdown, state 0x%x\n", -+ boot_state); -+ return -EBUSY; -+ } -+ -+ /* Issue shutdown to start shutdown process */ -+ dev_dbg(dev, "stopping fw...\n"); -+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID, -+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD); -+ while (timeout--) { -+ boot_state = read_fw_boot_param(adev, -+ IA_GOFO_FW_BOOT_STATE_ID); -+ if (BOOT_STATE_IS_CRITICAL(boot_state) || -+ BOOT_STATE_IS_INACTIVE(boot_state)) -+ break; -+ usleep_range(1000, 1200); -+ } -+ -+ if (BOOT_STATE_IS_CRITICAL(boot_state)) { -+ ipu7_dump_fw_error_log(adev); -+ dev_err(dev, "critical boot state error 0x%x\n", boot_state); -+ return -EINVAL; -+ } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) { -+ dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state); -+ return -ETIMEDOUT; -+ } -+ -+ ipu7_boot_cell_stop(adev); -+ dev_dbg(dev, "stop fw done.\n"); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7"); -+ -+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev) -+{ -+ return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-boot.h b/drivers/media/pci/intel/ipu7/ipu7-boot.h -new file mode 100644 -index 000000000000..5600be849931 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-boot.h -@@ -0,0 +1,25 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2022 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BOOT_H -+#define IPU7_BOOT_H -+ -+#include -+ -+struct ipu7_bus_device; -+struct syscom_queue_config; -+ -+#define FW_QUEUE_CONFIG_SIZE(num_queues) \ -+ (sizeof(struct syscom_queue_config) * (num_queues)) -+ -+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev, -+ struct syscom_queue_config *qconfigs, -+ int num_queues, u32 uc_freq, -+ dma_addr_t subsys_config, u8 major); -+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev); -+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev); -+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev); -+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.c b/drivers/media/pci/intel/ipu7/ipu7-bus.c -new file mode 100644 -index 000000000000..7da44fde002a ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-bus.c -@@ -0,0 +1,158 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-boot.h" -+#include "ipu7-dma.h" -+ -+static int bus_pm_runtime_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ int ret; -+ -+ ret = pm_generic_runtime_suspend(dev); -+ if (ret) -+ return ret; -+ -+ ret = ipu_buttress_powerdown(dev, adev->ctrl); -+ if (!ret) -+ return 0; -+ -+ dev_err(dev, "power down failed!\n"); -+ -+ /* Powering down failed, attempt to resume device now */ -+ ret = pm_generic_runtime_resume(dev); -+ if (!ret) -+ return -EBUSY; -+ -+ return -EIO; -+} -+ -+static int bus_pm_runtime_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ int ret; -+ -+ ret = ipu_buttress_powerup(dev, adev->ctrl); -+ if (ret) -+ return ret; -+ -+ ret = pm_generic_runtime_resume(dev); -+ if (ret) -+ goto out_err; -+ -+ return 0; -+ -+out_err: -+ ipu_buttress_powerdown(dev, adev->ctrl); -+ -+ return -EBUSY; -+} -+ -+static struct dev_pm_domain ipu7_bus_pm_domain = { -+ .ops = { -+ .runtime_suspend = bus_pm_runtime_suspend, -+ .runtime_resume = bus_pm_runtime_resume, -+ }, -+}; -+ -+static DEFINE_MUTEX(ipu7_bus_mutex); -+static void ipu7_bus_release(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ -+ kfree(adev->pdata); -+ kfree(adev); -+} -+ -+struct ipu7_bus_device * -+ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -+ void *pdata, const struct ipu_buttress_ctrl *ctrl, -+ const char *name) -+{ -+ struct auxiliary_device *auxdev; -+ struct ipu7_bus_device *adev; -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ int ret; -+ -+ adev = kzalloc(sizeof(*adev), GFP_KERNEL); -+ if (!adev) -+ return ERR_PTR(-ENOMEM); -+ -+ adev->isp = isp; -+ adev->ctrl = ctrl; -+ adev->pdata = pdata; -+ auxdev = &adev->auxdev; -+ auxdev->name = name; -+ auxdev->id = (pci_domain_nr(pdev->bus) << 16) | -+ PCI_DEVID(pdev->bus->number, pdev->devfn); -+ -+ auxdev->dev.parent = parent; -+ auxdev->dev.release = ipu7_bus_release; -+ -+ ret = auxiliary_device_init(auxdev); -+ if (ret < 0) { -+ dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n", -+ ret); -+ kfree(adev); -+ return ERR_PTR(ret); -+ } -+ -+ dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain); -+ -+ pm_runtime_forbid(&adev->auxdev.dev); -+ pm_runtime_enable(&adev->auxdev.dev); -+ -+ return adev; -+} -+ -+int ipu7_bus_add_device(struct ipu7_bus_device *adev) -+{ -+ struct auxiliary_device *auxdev = &adev->auxdev; -+ int ret; -+ -+ ret = auxiliary_device_add(auxdev); -+ if (ret) { -+ auxiliary_device_uninit(auxdev); -+ return ret; -+ } -+ -+ mutex_lock(&ipu7_bus_mutex); -+ list_add(&adev->list, &adev->isp->devices); -+ mutex_unlock(&ipu7_bus_mutex); -+ -+ pm_runtime_allow(&auxdev->dev); -+ -+ return 0; -+} -+ -+void ipu7_bus_del_devices(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev, *save; -+ -+ mutex_lock(&ipu7_bus_mutex); -+ -+ list_for_each_entry_safe(adev, save, &isp->devices, list) { -+ pm_runtime_disable(&adev->auxdev.dev); -+ list_del(&adev->list); -+ auxiliary_device_delete(&adev->auxdev); -+ auxiliary_device_uninit(&adev->auxdev); -+ } -+ -+ mutex_unlock(&ipu7_bus_mutex); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-bus.h b/drivers/media/pci/intel/ipu7/ipu7-bus.h -new file mode 100644 -index 000000000000..45157df16e90 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-bus.h -@@ -0,0 +1,69 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BUS_H -+#define IPU7_BUS_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_boot_abi.h" -+ -+#include "ipu7-syscom.h" -+ -+struct pci_dev; -+struct ipu_buttress_ctrl; -+struct ipu7_mmu; -+struct ipu7_device; -+ -+enum ipu7_subsys { -+ IPU_IS = 0, -+ IPU_PS = 1, -+ IPU_SUBSYS_NUM = 2, -+}; -+ -+struct ipu7_bus_device { -+ struct auxiliary_device auxdev; -+ const struct auxiliary_driver *auxdrv; -+ const struct ipu7_auxdrv_data *auxdrv_data; -+ struct list_head list; -+ enum ipu7_subsys subsys; -+ void *pdata; -+ struct ipu7_mmu *mmu; -+ struct ipu7_device *isp; -+ const struct ipu_buttress_ctrl *ctrl; -+ u64 dma_mask; -+ struct sg_table fw_sgt; -+ u32 fw_entry; -+ struct ipu7_syscom_context *syscom; -+ struct ia_gofo_boot_config *boot_config; -+ dma_addr_t boot_config_dma_addr; -+ u32 boot_config_size; -+}; -+ -+struct ipu7_auxdrv_data { -+ irqreturn_t (*isr)(struct ipu7_bus_device *adev); -+ irqreturn_t (*isr_threaded)(struct ipu7_bus_device *adev); -+ bool wake_isr_thread; -+}; -+ -+#define to_ipu7_bus_device(_dev) \ -+ container_of(to_auxiliary_dev(_dev), struct ipu7_bus_device, auxdev) -+#define auxdev_to_adev(_auxdev) \ -+ container_of(_auxdev, struct ipu7_bus_device, auxdev) -+#define ipu7_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev) -+ -+struct ipu7_bus_device * -+ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent, -+ void *pdata, const struct ipu_buttress_ctrl *ctrl, -+ const char *name); -+int ipu7_bus_add_device(struct ipu7_bus_device *adev); -+void ipu7_bus_del_devices(struct pci_dev *pdev); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h b/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -new file mode 100644 -index 000000000000..3eafd6a3813d ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-buttress-regs.h -@@ -0,0 +1,461 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BUTTRESS_REGS_H -+#define IPU7_BUTTRESS_REGS_H -+ -+#define BUTTRESS_REG_IRQ_STATUS 0x2000 -+#define BUTTRESS_REG_IRQ_STATUS_UNMASKED 0x2004 -+#define BUTTRESS_REG_IRQ_ENABLE 0x2008 -+#define BUTTRESS_REG_IRQ_CLEAR 0x200c -+#define BUTTRESS_REG_IRQ_MASK 0x2010 -+#define BUTTRESS_REG_TSC_CMD 0x2014 -+#define BUTTRESS_REG_TSC_CTL 0x2018 -+#define BUTTRESS_REG_TSC_LO 0x201c -+#define BUTTRESS_REG_TSC_HI 0x2020 -+ -+/* valid for PTL */ -+#define BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030 -+#define BUTTRESS_REG_PB_TIMESTAMP_HI 0x2034 -+#define BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038 -+ -+#define BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100 -+#define BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104 -+#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ 0x2108 -+#define BUTTRESS_REG_PS_DOMAINS_STATUS 0x2110 -+#define BUTTRESS_REG_PWR_STATUS 0x2114 -+#define BUTTRESS_REG_PS_WORKPOINT_REQ_SHADOW 0x2120 -+#define BUTTRESS_REG_IS_WORKPOINT_REQ_SHADOW 0x2124 -+#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ_SHADOW 0x2128 -+#define BUTTRESS_REG_ISPS_WORKPOINT_DOWNLOAD 0x212c -+#define BUTTRESS_REG_PG_FLOW_OVERRIDE 0x2180 -+#define BUTTRESS_REG_GLOBAL_OVERRIDE_UNGATE_CTL 0x2184 -+#define BUTTRESS_REG_PWR_FSM_CTL 0x2188 -+#define BUTTRESS_REG_IDLE_WDT 0x218c -+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_EN 0x2190 -+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_ADDR 0x2194 -+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_DATA 0x2198 -+#define BUTTRESS_REG_POWER_EN_DELAY 0x219c -+#define IPU7_BUTTRESS_REG_LTR_CONTROL 0x21a0 -+#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4 -+#define IPU7_BUTTRESS_REG_INT_FRM_PUNIT 0x21a8 -+#define IPU8_BUTTRESS_REG_LTR_CONTROL 0x21a4 -+#define IPU8_BUTTRESS_REG_NDE_CONTROL 0x21a8 -+#define IPU8_BUTTRESS_REG_INT_FRM_PUNIT 0x21ac -+#define BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0 -+#define BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4 -+#define BUTTRESS_REG_DVFS_FSM_STATUS 0x21b8 -+#define BUTTRESS_REG_PS_PLL_ENABLE 0x21bc -+#define BUTTRESS_REG_D2D_CTL 0x21d4 -+#define BUTTRESS_REG_IB_CLK_CTL 0x21d8 -+#define BUTTRESS_REG_IB_CRO_CLK_CTL 0x21dc -+#define BUTTRESS_REG_FUNC_FUSES 0x21e0 -+#define BUTTRESS_REG_ISOCH_CTL 0x21e4 -+#define BUTTRESS_REG_WORKPOINT_CTL 0x21f0 -+#define BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS 0x2200 -+#define BUTTRESS_REG_DRV_IS_UCX_START_ADDR 0x2204 -+#define BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS 0x2208 -+#define BUTTRESS_REG_DRV_PS_UCX_START_ADDR 0x220c -+#define BUTTRESS_REG_DRV_UCX_RESET_CFG 0x2210 -+ -+/* configured by CSE */ -+#define BUTTRESS_REG_CSE_IS_UCX_CONTROL_STATUS 0x2300 -+#define BUTTRESS_REG_CSE_IS_UCX_START_ADDR 0x2304 -+#define BUTTRESS_REG_CSE_PS_UCX_CONTROL_STATUS 0x2308 -+#define BUTTRESS_REG_CSE_PS_UCX_START_ADDR 0x230c -+ -+#define BUTTRESS_REG_CAMERA_MASK 0x2310 -+#define BUTTRESS_REG_FW_CTL 0x2314 -+#define BUTTRESS_REG_SECURITY_CTL 0x2318 -+#define BUTTRESS_REG_FUNCTIONAL_FW_SETUP 0x231c -+#define BUTTRESS_REG_FW_BASE 0x2320 -+#define BUTTRESS_REG_FW_BASE_LIMIT 0x2324 -+#define BUTTRESS_REG_FW_SCRATCH_BASE 0x2328 -+#define BUTTRESS_REG_FW_SCRATCH_LIMIT 0x232c -+#define BUTTRESS_REG_CSE_ACTION 0x2330 -+ -+/* configured by SW */ -+#define BUTTRESS_REG_FW_RESET_CTL 0x2334 -+#define BUTTRESS_REG_FW_SOURCE_SIZE 0x2338 -+#define BUTTRESS_REG_FW_SOURCE_BASE 0x233c -+ -+#define BUTTRESS_REG_IPU_SEC_CP_LSB 0x2400 -+#define BUTTRESS_REG_IPU_SEC_CP_MSB 0x2404 -+#define BUTTRESS_REG_IPU_SEC_WAC_LSB 0x2408 -+#define BUTTRESS_REG_IPU_SEC_WAC_MSB 0x240c -+#define BUTTRESS_REG_IPU_SEC_RAC_LSB 0x2410 -+#define BUTTRESS_REG_IPU_SEC_RAC_MSB 0x2414 -+#define BUTTRESS_REG_IPU_DRV_CP_LSB 0x2418 -+#define BUTTRESS_REG_IPU_DRV_CP_MSB 0x241c -+#define BUTTRESS_REG_IPU_DRV_WAC_LSB 0x2420 -+#define BUTTRESS_REG_IPU_DRV_WAC_MSB 0x2424 -+#define BUTTRESS_REG_IPU_DRV_RAC_LSB 0x2428 -+#define BUTTRESS_REG_IPU_DRV_RAC_MSB 0x242c -+#define BUTTRESS_REG_IPU_FW_CP_LSB 0x2430 -+#define BUTTRESS_REG_IPU_FW_CP_MSB 0x2434 -+#define BUTTRESS_REG_IPU_FW_WAC_LSB 0x2438 -+#define BUTTRESS_REG_IPU_FW_WAC_MSB 0x243c -+#define BUTTRESS_REG_IPU_FW_RAC_LSB 0x2440 -+#define BUTTRESS_REG_IPU_FW_RAC_MSB 0x2444 -+#define BUTTRESS_REG_IPU_BIOS_SEC_CP_LSB 0x2448 -+#define BUTTRESS_REG_IPU_BIOS_SEC_CP_MSB 0x244c -+#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_LSB 0x2450 -+#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_MSB 0x2454 -+#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_LSB 0x2458 -+#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_MSB 0x245c -+#define BUTTRESS_REG_IPU_DFD_CP_LSB 0x2460 -+#define BUTTRESS_REG_IPU_DFD_CP_MSB 0x2464 -+#define BUTTRESS_REG_IPU_DFD_WAC_LSB 0x2468 -+#define BUTTRESS_REG_IPU_DFD_WAC_MSB 0x246c -+#define BUTTRESS_REG_IPU_DFD_RAC_LSB 0x2470 -+#define BUTTRESS_REG_IPU_DFD_RAC_MSB 0x2474 -+#define BUTTRESS_REG_CSE2IUDB0 0x2500 -+#define BUTTRESS_REG_CSE2IUDATA0 0x2504 -+#define BUTTRESS_REG_CSE2IUCSR 0x2508 -+#define BUTTRESS_REG_IU2CSEDB0 0x250c -+#define BUTTRESS_REG_IU2CSEDATA0 0x2510 -+#define BUTTRESS_REG_IU2CSECSR 0x2514 -+#define BUTTRESS_REG_CSE2IUDB0_CR_SHADOW 0x2520 -+#define BUTTRESS_REG_CSE2IUDATA0_CR_SHADOW 0x2524 -+#define BUTTRESS_REG_CSE2IUCSR_CR_SHADOW 0x2528 -+#define BUTTRESS_REG_IU2CSEDB0_CR_SHADOW 0x252c -+#define BUTTRESS_REG_DVFS_FSM_SURVIVABILITY 0x2900 -+#define BUTTRESS_REG_FLOWS_FSM_SURVIVABILITY 0x2904 -+#define BUTTRESS_REG_FABRICS_FSM_SURVIVABILITY 0x2908 -+#define BUTTRESS_REG_PS_SUB1_PM_FSM_SURVIVABILITY 0x290c -+#define BUTTRESS_REG_PS_SUB0_PM_FSM_SURVIVABILITY 0x2910 -+#define BUTTRESS_REG_PS_PM_FSM_SURVIVABILITY 0x2914 -+#define BUTTRESS_REG_IS_PM_FSM_SURVIVABILITY 0x2918 -+#define BUTTRESS_REG_FLR_RST_FSM_SURVIVABILITY 0x291c -+#define BUTTRESS_REG_FW_RST_FSM_SURVIVABILITY 0x2920 -+#define BUTTRESS_REG_RESETPREP_FSM_SURVIVABILITY 0x2924 -+#define BUTTRESS_REG_POWER_FSM_DOMAIN_STATUS 0x3000 -+#define BUTTRESS_REG_IDLEREQ_STATUS1 0x3004 -+#define BUTTRESS_REG_POWER_FSM_STATUS_IS_PS 0x3008 -+#define BUTTRESS_REG_POWER_ACK_B_STATUS 0x300c -+#define BUTTRESS_REG_DOMAIN_RETENTION_CTL 0x3010 -+#define BUTTRESS_REG_CG_CTRL_BITS 0x3014 -+#define BUTTRESS_REG_IS_IFC_STATUS0 0x3018 -+#define BUTTRESS_REG_IS_IFC_STATUS1 0x301c -+#define BUTTRESS_REG_PS_IFC_STATUS0 0x3020 -+#define BUTTRESS_REG_PS_IFC_STATUS1 0x3024 -+#define BUTTRESS_REG_BTRS_IFC_STATUS0 0x3028 -+#define BUTTRESS_REG_BTRS_IFC_STATUS1 0x302c -+#define BUTTRESS_REG_IPU_SKU 0x3030 -+#define BUTTRESS_REG_PS_IDLEACK 0x3034 -+#define BUTTRESS_REG_IS_IDLEACK 0x3038 -+#define BUTTRESS_REG_SPARE_REGS_0 0x303c -+#define BUTTRESS_REG_SPARE_REGS_1 0x3040 -+#define BUTTRESS_REG_SPARE_REGS_2 0x3044 -+#define BUTTRESS_REG_SPARE_REGS_3 0x3048 -+#define BUTTRESS_REG_IUNIT_ACV 0x304c -+#define BUTTRESS_REG_CHICKEN_BITS 0x3050 -+#define BUTTRESS_REG_SBENDPOINT_CFG 0x3054 -+#define BUTTRESS_REG_ECC_ERR_LOG 0x3058 -+#define BUTTRESS_REG_POWER_FSM_STATUS 0x3070 -+#define BUTTRESS_REG_RESET_FSM_STATUS 0x3074 -+#define BUTTRESS_REG_IDLE_STATUS 0x3078 -+#define BUTTRESS_REG_IDLEACK_STATUS 0x307c -+#define BUTTRESS_REG_IPU_DEBUG 0x3080 -+ -+#define BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000 -+#define BUTTRESS_REG_FW_BOOT_PARAMS1 0x4004 -+#define BUTTRESS_REG_FW_BOOT_PARAMS2 0x4008 -+#define BUTTRESS_REG_FW_BOOT_PARAMS3 0x400c -+#define BUTTRESS_REG_FW_BOOT_PARAMS4 0x4010 -+#define BUTTRESS_REG_FW_BOOT_PARAMS5 0x4014 -+#define BUTTRESS_REG_FW_BOOT_PARAMS6 0x4018 -+#define BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c -+#define BUTTRESS_REG_FW_BOOT_PARAMS8 0x4020 -+#define BUTTRESS_REG_FW_BOOT_PARAMS9 0x4024 -+#define BUTTRESS_REG_FW_BOOT_PARAMS10 0x4028 -+#define BUTTRESS_REG_FW_BOOT_PARAMS11 0x402c -+#define BUTTRESS_REG_FW_BOOT_PARAMS12 0x4030 -+#define BUTTRESS_REG_FW_BOOT_PARAMS13 0x4034 -+#define BUTTRESS_REG_FW_BOOT_PARAMS14 0x4038 -+#define BUTTRESS_REG_FW_BOOT_PARAMS15 0x403c -+ -+#define BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \ -+ (BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U)) -+#define BUTTRESS_REG_FW_GP(i) (0x4040 + 0x4 * (i)) -+#define BUTTRESS_REG_FPGA_SUPPORT(i) (0x40c0 + 0x4 * (i)) -+ -+#define BUTTRESS_REG_FW_GP8 0x4060 -+#define BUTTRESS_REG_FW_GP24 0x40a0 -+ -+#define BUTTRESS_REG_GPIO_0_PADCFG_ADDR_CR 0x4100 -+#define BUTTRESS_REG_GPIO_1_PADCFG_ADDR_CR 0x4104 -+#define BUTTRESS_REG_GPIO_2_PADCFG_ADDR_CR 0x4108 -+#define BUTTRESS_REG_GPIO_3_PADCFG_ADDR_CR 0x410c -+#define BUTTRESS_REG_GPIO_4_PADCFG_ADDR_CR 0x4110 -+#define BUTTRESS_REG_GPIO_5_PADCFG_ADDR_CR 0x4114 -+#define BUTTRESS_REG_GPIO_6_PADCFG_ADDR_CR 0x4118 -+#define BUTTRESS_REG_GPIO_7_PADCFG_ADDR_CR 0x411c -+#define BUTTRESS_REG_GPIO_ENABLE 0x4140 -+#define BUTTRESS_REG_GPIO_VALUE_CR 0x4144 -+ -+#define BUTTRESS_REG_IS_MEM_CORRECTABLE_ERROR_STATUS 0x5000 -+#define BUTTRESS_REG_IS_MEM_FATAL_ERROR_STATUS 0x5004 -+#define BUTTRESS_REG_IS_MEM_NON_FATAL_ERROR_STATUS 0x5008 -+#define BUTTRESS_REG_IS_MEM_CHECK_PASSED 0x500c -+#define BUTTRESS_REG_IS_MEM_ERROR_INJECT 0x5010 -+#define BUTTRESS_REG_IS_MEM_ERROR_CLEAR 0x5014 -+#define BUTTRESS_REG_PS_MEM_CORRECTABLE_ERROR_STATUS 0x5040 -+#define BUTTRESS_REG_PS_MEM_FATAL_ERROR_STATUS 0x5044 -+#define BUTTRESS_REG_PS_MEM_NON_FATAL_ERROR_STATUS 0x5048 -+#define BUTTRESS_REG_PS_MEM_CHECK_PASSED 0x504c -+#define BUTTRESS_REG_PS_MEM_ERROR_INJECT 0x5050 -+#define BUTTRESS_REG_PS_MEM_ERROR_CLEAR 0x5054 -+ -+#define BUTTRESS_REG_IS_AB_REGION_MIN_ADDRESS(i) (0x6000 + 0x8 * (i)) -+#define BUTTRESS_REG_IS_AB_REGION_MAX_ADDRESS(i) (0x6004 + 0x8 * (i)) -+#define BUTTRESS_REG_IS_AB_VIOLATION_LOG0 0x6080 -+#define BUTTRESS_REG_IS_AB_VIOLATION_LOG1 0x6084 -+#define BUTTRESS_REG_PS_AB_REGION_MIN_ADDRESS(i) (0x6100 + 0x8 * (i)) -+#define BUTTRESS_REG_PS_AB_REGION_MAX_ADDRESS0 (0x6104 + 0x8 * (i)) -+#define BUTTRESS_REG_PS_AB_VIOLATION_LOG0 0x6180 -+#define BUTTRESS_REG_PS_AB_VIOLATION_LOG1 0x6184 -+#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG0 0x6200 -+#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG1 0x6204 -+#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG0 0x6208 -+#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG1 0x620c -+#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG0 0x6210 -+#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG1 0x6214 -+#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG0 0x6218 -+#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG1 0x621c -+#define BUTTRESS_REG_AB_ENABLE 0x6220 -+#define BUTTRESS_REG_AB_DEFAULT_ACCESS 0x6230 -+ -+/* Indicates CSE has received an IPU driver IPC transaction */ -+#define BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0) -+/* Indicates an IPC transaction from CSE has arrived */ -+#define BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1) -+/* Indicates a CSR update from CSE has arrived */ -+#define BUTTRESS_IRQ_CSE_CSR_SET BIT(2) -+/* Indicates an interrupt set by Punit (not in use at this time) */ -+#define BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ BIT(3) -+/* Indicates an SAI violation was detected on access to IB registers */ -+#define BUTTRESS_IRQ_SAI_VIOLATION BIT(4) -+/* Indicates a transaction to IS was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_IS_AB_VIOLATION BIT(5) -+/* Indicates a transaction to PS was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_PS_AB_VIOLATION BIT(6) -+/* Indicates an error response was detected by the IB config NoC */ -+#define BUTTRESS_IRQ_IB_CFG_NOC_ERR_IRQ BIT(7) -+/* Indicates an error response was detected by the IB data NoC */ -+#define BUTTRESS_IRQ_IB_DATA_NOC_ERR_IRQ BIT(8) -+/* Transaction to DVP regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_IB_DVP_AB_VIOLATION BIT(9) -+/* Transaction to ATB2DTF regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_ATB2DTF_AB_VIOLATION BIT(10) -+/* Transaction to IS debug regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_IS_DEBUG_AB_VIOLATION BIT(11) -+/* Transaction to PS debug regs was not able to pass the access blocker */ -+#define BUTTRESS_IRQ_PS_DEBUG_AB_VIOLATION BIT(12) -+/* Indicates timeout occurred waiting for a response from a target */ -+#define BUTTRESS_IRQ_IB_CFG_NOC_TIMEOUT_IRQ BIT(13) -+/* Set when any correctable ECC error input wire to buttress is set */ -+#define BUTTRESS_IRQ_ECC_CORRECTABLE BIT(14) -+/* Any noncorrectable-nonfatal ECC error input wire to buttress is set */ -+#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_NONFATAL BIT(15) -+/* Set when any noncorrectable-fatal ECC error input wire to buttress is set */ -+#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_FATAL BIT(16) -+/* Set when timeout occurred waiting for a response from a target */ -+#define BUTTRESS_IRQ_IS_CFG_NOC_TIMEOUT_IRQ BIT(17) -+#define BUTTRESS_IRQ_PS_CFG_NOC_TIMEOUT_IRQ BIT(18) -+#define BUTTRESS_IRQ_LB_CFG_NOC_TIMEOUT_IRQ BIT(19) -+/* IS FW double exception event */ -+#define BUTTRESS_IRQ_IS_UC_PFATAL_ERROR BIT(26) -+/* PS FW double exception event */ -+#define BUTTRESS_IRQ_PS_UC_PFATAL_ERROR BIT(27) -+/* IS FW watchdog event */ -+#define BUTTRESS_IRQ_IS_WATCHDOG BIT(28) -+/* PS FW watchdog event */ -+#define BUTTRESS_IRQ_PS_WATCHDOG BIT(29) -+/* IS IRC irq out */ -+#define BUTTRESS_IRQ_IS_IRQ BIT(30) -+/* PS IRC irq out */ -+#define BUTTRESS_IRQ_PS_IRQ BIT(31) -+ -+/* buttress irq */ -+#define BUTTRESS_PWR_STATUS_HH_STATE_IDLE 0U -+#define BUTTRESS_PWR_STATUS_HH_STATE_IN_PRGS 1U -+#define BUTTRESS_PWR_STATUS_HH_STATE_DONE 2U -+#define BUTTRESS_PWR_STATUS_HH_STATE_ERR 3U -+ -+#define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0) -+#define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11 -+#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11) -+#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8) -+/* new for PTL */ -+#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9) -+#define BUTTRESS_IRQS (BUTTRESS_IRQ_IS_IRQ | \ -+ BUTTRESS_IRQ_PS_IRQ | \ -+ BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \ -+ BUTTRESS_IRQ_CSE_CSR_SET | \ -+ BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE | \ -+ BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -+ -+/* Iunit to CSE regs */ -+#define BUTTRESS_IU2CSEDB0_BUSY BIT(31) -+#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27 -+#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10 -+#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2 -+ -+#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1 -+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2 -+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3 -+#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16 -+ -+#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0) -+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1) -+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2) -+#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4) -+ -+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4) -+#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5) -+ -+/* 0x20 == NACK, 0xf == unknown command */ -+#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20 -+#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff -+ -+/* IS/PS freq control */ -+#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU -+#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU -+ -+#define IPU7_IS_FREQ_MAX 450 -+#define IPU7_IS_FREQ_MIN 50 -+#define IPU7_PS_FREQ_MAX 750 -+#define BUTTRESS_PS_FREQ_RATIO_STEP 25U -+/* valid for IPU8 */ -+#define BUTTRESS_IS_FREQ_RATIO_STEP 25U -+ -+/* IS: 400mhz, PS: 500mhz */ -+#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b -+#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14 -+/* IS: 400mhz, PS: 400mhz */ -+#define IPU8_IS_FREQ_CTL_DEFAULT_RATIO 0x10 -+#define IPU8_PS_FREQ_CTL_DEFAULT_RATIO 0x10 -+ -+#define IPU_FREQ_CTL_CDYN 0x80 -+#define IPU_FREQ_CTL_RATIO_SHIFT 0x0 -+#define IPU_FREQ_CTL_CDYN_SHIFT 0x8 -+ -+/* buttree power status */ -+#define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0 -+#define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \ -+ (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT) -+ -+#define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4 -+#define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \ -+ (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT) -+ -+#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0 -+#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1 -+#define IPU_BUTTRESS_PWR_STATE_DN_PROCESS 0x2 -+#define IPU_BUTTRESS_PWR_STATE_UP_DONE 0x3 -+ -+#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3 -+#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3 << 3) -+ -+#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6 -+#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3 << 6) -+ -+#define PS_FSM_CG BIT(3) -+ -+#define BUTTRESS_OVERRIDE_IS_CLK BIT(1) -+#define BUTTRESS_OVERRIDE_PS_CLK BIT(2) -+/* ps_pll only valid for ipu8 */ -+#define BUTTRESS_OWN_ACK_PS_PLL BIT(8) -+#define BUTTRESS_OWN_ACK_IS_CLK BIT(9) -+#define BUTTRESS_OWN_ACK_PS_CLK BIT(10) -+ -+/* FW reset ctrl */ -+#define BUTTRESS_FW_RESET_CTL_START BIT(0) -+#define BUTTRESS_FW_RESET_CTL_DONE BIT(1) -+ -+/* security */ -+#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16) -+#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0) -+ -+#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0) -+#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1) -+#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3) -+ -+/* D2D */ -+#define BUTTRESS_D2D_PWR_EN BIT(0) -+#define BUTTRESS_D2D_PWR_ACK BIT(4) -+ -+/* NDE */ -+#define NDE_VAL_MASK GENMASK(9, 0) -+#define NDE_SCALE_MASK GENMASK(12, 10) -+#define NDE_VALID_MASK BIT(13) -+#define NDE_RESVEC_MASK GENMASK(19, 16) -+#define NDE_IN_VBLANK_DIS_MASK BIT(31) -+ -+#define BUTTRESS_NDE_VAL_ACTIVE 48 -+#define BUTTRESS_NDE_SCALE_ACTIVE 2 -+#define BUTTRESS_NDE_VALID_ACTIVE 1 -+ -+#define BUTTRESS_NDE_VAL_DEFAULT 1023 -+#define BUTTRESS_NDE_SCALE_DEFAULT 2 -+#define BUTTRESS_NDE_VALID_DEFAULT 0 -+ -+/* IS and PS UCX control */ -+#define UCX_CTL_RESET BIT(0) -+#define UCX_CTL_RUN BIT(1) -+#define UCX_CTL_WAKEUP BIT(2) -+#define UCX_CTL_SPARE GENMASK(7, 3) -+#define UCX_STS_PWR GENMASK(17, 16) -+#define UCX_STS_SLEEPING BIT(18) -+ -+/* offset from PHY base */ -+#define PHY_CSI_CFG 0xc0 -+#define PHY_CSI_RCOMP_CONTROL 0xc8 -+#define PHY_CSI_BSCAN_EXCLUDE 0xd8 -+ -+#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x)) -+#define PHY_DPHY_DLL_OVRD(x) (0x14c + 0x100 * (x)) -+#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x)) -+#define PHY_CPHY_RX_CONTROL2(x) (0x114 + 0x100 * (x)) -+#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x)) -+#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x)) -+ -+/* PB registers */ -+#define INTERRUPT_STATUS 0x0 -+#define BTRS_LOCAL_INTERRUPT_MASK 0x4 -+#define GLOBAL_INTERRUPT_MASK 0x8 -+#define HM_ATS 0xc -+#define ATS_ERROR_LOG1 0x10 -+#define ATS_ERROR_LOG2 0x14 -+#define ATS_ERROR_CLEAR 0x18 -+#define CFI_0_ERROR_LOG 0x1c -+#define CFI_0_ERROR_CLEAR 0x20 -+#define HASH_CONFIG 0x2c -+#define TLBID_HASH_ENABLE_31_0 0x30 -+#define TLBID_HASH_ENABLE_63_32 0x34 -+#define TLBID_HASH_ENABLE_95_64 0x38 -+#define TLBID_HASH_ENABLE_127_96 0x3c -+#define CFI_1_ERROR_LOGGING 0x40 -+#define CFI_1_ERROR_CLEAR 0x44 -+#define IMR_ERROR_LOGGING_LOW 0x48 -+#define IMR_ERROR_LOGGING_HIGH 0x4c -+#define IMR_ERROR_CLEAR 0x50 -+#define PORT_ARBITRATION_WEIGHTS 0x54 -+#define IMR_ERROR_LOGGING_CFI_1_LOW 0x58 -+#define IMR_ERROR_LOGGING_CFI_1_HIGH 0x5c -+#define IMR_ERROR_CLEAR_CFI_1 0x60 -+#define BAR2_MISC_CONFIG 0x64 -+#define RSP_ID_CONFIG_AXI2CFI_0 0x68 -+#define RSP_ID_CONFIG_AXI2CFI_1 0x6c -+#define PB_DRIVER_PCODE_MAILBOX_STATUS 0x70 -+#define PB_DRIVER_PCODE_MAILBOX_INTERFACE 0x74 -+#define PORT_ARBITRATION_WEIGHTS_ATS 0x78 -+ -+#endif /* IPU7_BUTTRESS_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.c b/drivers/media/pci/intel/ipu7/ipu7-buttress.c -new file mode 100644 -index 000000000000..b350ca5678d0 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-buttress.c -@@ -0,0 +1,1193 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-buttress-regs.h" -+ -+#define BOOTLOADER_STATUS_OFFSET BUTTRESS_REG_FW_BOOT_PARAMS7 -+ -+#define BOOTLOADER_MAGIC_KEY 0xb00710adU -+ -+#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 -+#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 -+#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE -+ -+#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10U -+ -+#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC) -+#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC) -+#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC) -+ -+#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC -+#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC -+#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC) -+#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC) -+ -+#define BUTTRESS_IPC_RESET_RETRY 2000U -+#define BUTTRESS_CSE_IPC_RESET_RETRY 4U -+#define BUTTRESS_IPC_CMD_SEND_RETRY 1U -+ -+struct ipu7_ipc_buttress_msg { -+ u32 cmd; -+ u32 expected_resp; -+ bool require_resp; -+ u8 cmd_size; -+}; -+ -+static const u32 ipu7_adev_irq_mask[2] = { -+ BUTTRESS_IRQ_IS_IRQ, -+ BUTTRESS_IRQ_PS_IRQ -+}; -+ -+int ipu_buttress_ipc_reset(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc) -+{ -+ unsigned int retries = BUTTRESS_IPC_RESET_RETRY; -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ u32 val = 0, csr_in_clr; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(dev, "Skip IPC reset for non-secure mode\n"); -+ return 0; -+ } -+ -+ mutex_lock(&b->ipc_mutex); -+ -+ /* Clear-by-1 CSR (all bits), corresponding internal states. */ -+ val = readl(isp->base + ipc->csr_in); -+ writel(val, isp->base + ipc->csr_in); -+ -+ /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */ -+ writel(ENTRY, isp->base + ipc->csr_out); -+ /* -+ * Clear-by-1 all CSR bits EXCEPT following -+ * bits: -+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * C. Possibly custom bits, depending on -+ * their role. -+ */ -+ csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ | -+ BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID | -+ BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY; -+ -+ do { -+ usleep_range(400, 500); -+ val = readl(isp->base + ipc->csr_in); -+ switch (val) { -+ case ENTRY | EXIT: -+ case ENTRY | EXIT | QUERY: -+ /* -+ * 1) Clear-by-1 CSR bits -+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2). -+ * 2) Set peer CSR bit -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -+ */ -+ writel(ENTRY | EXIT, isp->base + ipc->csr_in); -+ writel(QUERY, isp->base + ipc->csr_out); -+ break; -+ case ENTRY: -+ case ENTRY | QUERY: -+ /* -+ * 1) Clear-by-1 CSR bits -+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1, -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE). -+ * 2) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ */ -+ writel(ENTRY | QUERY, isp->base + ipc->csr_in); -+ writel(ENTRY, isp->base + ipc->csr_out); -+ break; -+ case EXIT: -+ case EXIT | QUERY: -+ /* -+ * Clear-by-1 CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * 1) Clear incoming doorbell. -+ * 2) Clear-by-1 all CSR bits EXCEPT following -+ * bits: -+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1. -+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ * C. Possibly custom bits, depending on -+ * their role. -+ * 3) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2. -+ */ -+ writel(EXIT, isp->base + ipc->csr_in); -+ writel(0, isp->base + ipc->db0_in); -+ writel(csr_in_clr, isp->base + ipc->csr_in); -+ writel(EXIT, isp->base + ipc->csr_out); -+ -+ /* -+ * Read csr_in again to make sure if RST_PHASE2 is done. -+ * If csr_in is QUERY, it should be handled again. -+ */ -+ usleep_range(200, 300); -+ val = readl(isp->base + ipc->csr_in); -+ if (val & QUERY) { -+ dev_dbg(dev, -+ "RST_PHASE2 retry csr_in = %x\n", val); -+ break; -+ } -+ mutex_unlock(&b->ipc_mutex); -+ return 0; -+ case QUERY: -+ /* -+ * 1) Clear-by-1 CSR bit -+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE. -+ * 2) Set peer CSR bit -+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1 -+ */ -+ writel(QUERY, isp->base + ipc->csr_in); -+ writel(ENTRY, isp->base + ipc->csr_out); -+ break; -+ default: -+ dev_dbg_ratelimited(dev, "Unexpected CSR 0x%x\n", val); -+ break; -+ } -+ } while (retries--); -+ -+ mutex_unlock(&b->ipc_mutex); -+ dev_err(dev, "Timed out while waiting for CSE\n"); -+ -+ return -ETIMEDOUT; -+} -+ -+static void ipu_buttress_ipc_validity_close(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc) -+{ -+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ, -+ isp->base + ipc->csr_out); -+} -+ -+static int -+ipu_buttress_ipc_validity_open(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc) -+{ -+ unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID; -+ void __iomem *addr; -+ int ret; -+ u32 val; -+ -+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ, -+ isp->base + ipc->csr_out); -+ -+ addr = isp->base + ipc->csr_in; -+ ret = readl_poll_timeout(addr, val, val & mask, 200, -+ BUTTRESS_IPC_VALIDITY_TIMEOUT_US); -+ if (ret) { -+ dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val); -+ ipu_buttress_ipc_validity_close(isp, ipc); -+ } -+ -+ return ret; -+} -+ -+static void ipu_buttress_ipc_recv(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc, u32 *ipc_msg) -+{ -+ if (ipc_msg) -+ *ipc_msg = readl(isp->base + ipc->data0_in); -+ writel(0, isp->base + ipc->db0_in); -+} -+ -+static int ipu_buttress_ipc_send_msg(struct ipu7_device *isp, -+ struct ipu7_ipc_buttress_msg *msg) -+{ -+ unsigned long tx_timeout_jiffies, rx_timeout_jiffies; -+ unsigned int retry = BUTTRESS_IPC_CMD_SEND_RETRY; -+ struct ipu_buttress *b = &isp->buttress; -+ struct ipu_buttress_ipc *ipc = &b->cse; -+ struct device *dev = &isp->pdev->dev; -+ int tout; -+ u32 val; -+ int ret; -+ -+ mutex_lock(&b->ipc_mutex); -+ -+ ret = ipu_buttress_ipc_validity_open(isp, ipc); -+ if (ret) { -+ dev_err(dev, "IPC validity open failed\n"); -+ goto out; -+ } -+ -+ tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS); -+ rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS); -+ -+try: -+ reinit_completion(&ipc->send_complete); -+ if (msg->require_resp) -+ reinit_completion(&ipc->recv_complete); -+ -+ dev_dbg(dev, "IPC command: 0x%x\n", msg->cmd); -+ writel(msg->cmd, isp->base + ipc->data0_out); -+ val = BUTTRESS_IU2CSEDB0_BUSY | msg->cmd_size; -+ writel(val, isp->base + ipc->db0_out); -+ -+ tout = wait_for_completion_timeout(&ipc->send_complete, -+ tx_timeout_jiffies); -+ if (!tout) { -+ dev_err(dev, "send IPC response timeout\n"); -+ if (!retry--) { -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ /* Try again if CSE is not responding on first try */ -+ writel(0, isp->base + ipc->db0_out); -+ goto try; -+ } -+ -+ if (!msg->require_resp) { -+ ret = -EIO; -+ goto out; -+ } -+ -+ tout = wait_for_completion_timeout(&ipc->recv_complete, -+ rx_timeout_jiffies); -+ if (!tout) { -+ dev_err(dev, "recv IPC response timeout\n"); -+ ret = -ETIMEDOUT; -+ goto out; -+ } -+ -+ if (ipc->nack_mask && -+ (ipc->recv_data & ipc->nack_mask) == ipc->nack) { -+ dev_err(dev, "IPC NACK for cmd 0x%x\n", msg->cmd); -+ ret = -EIO; -+ goto out; -+ } -+ -+ if (ipc->recv_data != msg->expected_resp) { -+ dev_err(dev, -+ "expected resp: 0x%x, IPC response: 0x%x\n", -+ msg->expected_resp, ipc->recv_data); -+ ret = -EIO; -+ goto out; -+ } -+ -+ dev_dbg(dev, "IPC commands done\n"); -+ -+out: -+ ipu_buttress_ipc_validity_close(isp, ipc); -+ mutex_unlock(&b->ipc_mutex); -+ -+ return ret; -+} -+ -+static int ipu_buttress_ipc_send(struct ipu7_device *isp, -+ u32 ipc_msg, u32 size, bool require_resp, -+ u32 expected_resp) -+{ -+ struct ipu7_ipc_buttress_msg msg = { -+ .cmd = ipc_msg, -+ .cmd_size = size, -+ .require_resp = require_resp, -+ .expected_resp = expected_resp, -+ }; -+ -+ return ipu_buttress_ipc_send_msg(isp, &msg); -+} -+ -+static irqreturn_t ipu_buttress_call_isr(struct ipu7_bus_device *adev) -+{ -+ irqreturn_t ret = IRQ_WAKE_THREAD; -+ -+ if (!adev || !adev->auxdrv || !adev->auxdrv_data) -+ return IRQ_NONE; -+ -+ if (adev->auxdrv_data->isr) -+ ret = adev->auxdrv_data->isr(adev); -+ -+ if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded) -+ ret = IRQ_NONE; -+ -+ return ret; -+} -+ -+irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr) -+{ -+ struct ipu7_device *isp = isp_ptr; -+ struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ irqreturn_t ret = IRQ_NONE; -+ u32 pb_irq, pb_local_irq; -+ u32 disable_irqs = 0; -+ u32 irq_status; -+ unsigned int i; -+ -+ pm_runtime_get_noresume(dev); -+ -+ pb_irq = readl(isp->pb_base + INTERRUPT_STATUS); -+ writel(pb_irq, isp->pb_base + INTERRUPT_STATUS); -+ -+ /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */ -+ pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK); -+ if (pb_local_irq & ~BIT(0)) { -+ dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq, -+ pb_local_irq); -+ dev_warn(dev, "Details: %x %x %x %x %x %x %x %x\n", -+ readl(isp->pb_base + ATS_ERROR_LOG1), -+ readl(isp->pb_base + ATS_ERROR_LOG2), -+ readl(isp->pb_base + CFI_0_ERROR_LOG), -+ readl(isp->pb_base + CFI_1_ERROR_LOGGING), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_LOW), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_HIGH), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_LOW), -+ readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_HIGH)); -+ } -+ -+ irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -+ if (!irq_status) { -+ pm_runtime_put_noidle(dev); -+ return IRQ_NONE; -+ } -+ -+ do { -+ writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR); -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask); i++) { -+ irqreturn_t r = ipu_buttress_call_isr(adev[i]); -+ -+ if (!(irq_status & ipu7_adev_irq_mask[i])) -+ continue; -+ -+ if (r == IRQ_WAKE_THREAD) { -+ ret = IRQ_WAKE_THREAD; -+ disable_irqs |= ipu7_adev_irq_mask[i]; -+ } else if (ret == IRQ_NONE && r == IRQ_HANDLED) { -+ ret = IRQ_HANDLED; -+ } -+ } -+ -+ if (irq_status & (BUTTRESS_IRQS | BUTTRESS_IRQ_SAI_VIOLATION) && -+ ret == IRQ_NONE) -+ ret = IRQ_HANDLED; -+ -+ if (irq_status & BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING) { -+ dev_dbg(dev, "BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING\n"); -+ ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data); -+ complete(&b->cse.recv_complete); -+ } -+ -+ if (irq_status & BUTTRESS_IRQ_CSE_CSR_SET) -+ dev_dbg(dev, "BUTTRESS_IRQ_CSE_CSR_SET\n"); -+ -+ if (irq_status & BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) { -+ dev_dbg(dev, "BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE\n"); -+ complete(&b->cse.send_complete); -+ } -+ -+ if (irq_status & BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ) -+ dev_dbg(dev, "BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ\n"); -+ -+ if (irq_status & BUTTRESS_IRQ_SAI_VIOLATION && -+ ipu_buttress_get_secure_mode(isp)) -+ dev_err(dev, "BUTTRESS_IRQ_SAI_VIOLATION\n"); -+ -+ irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS); -+ } while (irq_status); -+ -+ if (disable_irqs) -+ writel(BUTTRESS_IRQS & ~disable_irqs, -+ isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ -+ pm_runtime_put(dev); -+ -+ return ret; -+} -+ -+irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr) -+{ -+ struct ipu7_device *isp = isp_ptr; -+ struct ipu7_bus_device *adev[] = { isp->isys, isp->psys }; -+ const struct ipu7_auxdrv_data *drv_data = NULL; -+ irqreturn_t ret = IRQ_NONE; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask) && adev[i]; i++) { -+ drv_data = adev[i]->auxdrv_data; -+ if (!drv_data) -+ continue; -+ -+ if (drv_data->wake_isr_thread && -+ drv_data->isr_threaded(adev[i]) == IRQ_HANDLED) -+ ret = IRQ_HANDLED; -+ } -+ -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ -+ return ret; -+} -+ -+static int isys_d2d_power(struct device *dev, bool on) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ int ret = 0; -+ u32 target = on ? BUTTRESS_D2D_PWR_ACK : 0U; -+ u32 val; -+ -+ dev_dbg(dev, "power %s isys d2d.\n", on ? "UP" : "DOWN"); -+ val = readl(isp->base + BUTTRESS_REG_D2D_CTL); -+ if ((val & BUTTRESS_D2D_PWR_ACK) == target) { -+ dev_info(dev, "d2d already in %s state.\n", -+ on ? "UP" : "DOWN"); -+ return 0; -+ } -+ -+ val = on ? val | BUTTRESS_D2D_PWR_EN : val & (~BUTTRESS_D2D_PWR_EN); -+ writel(val, isp->base + BUTTRESS_REG_D2D_CTL); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_D2D_CTL, -+ val, (val & BUTTRESS_D2D_PWR_ACK) == target, -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_err(dev, "power %s d2d timeout. status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+ -+ return ret; -+} -+ -+static void isys_nde_control(struct device *dev, bool on) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, value, scale, valid, resvec; -+ u32 nde_reg; -+ -+ if (on) { -+ value = BUTTRESS_NDE_VAL_ACTIVE; -+ scale = BUTTRESS_NDE_SCALE_ACTIVE; -+ valid = BUTTRESS_NDE_VALID_ACTIVE; -+ } else { -+ value = BUTTRESS_NDE_VAL_DEFAULT; -+ scale = BUTTRESS_NDE_SCALE_DEFAULT; -+ valid = BUTTRESS_NDE_VALID_DEFAULT; -+ } -+ -+ /* only set the fabrics resource ownership for ipu8 */ -+ nde_reg = is_ipu8(isp->hw_ver) ? IPU8_BUTTRESS_REG_NDE_CONTROL : -+ IPU7_BUTTRESS_REG_NDE_CONTROL; -+ resvec = is_ipu8(isp->hw_ver) ? 0x2 : 0xe; -+ val = FIELD_PREP(NDE_VAL_MASK, value) | -+ FIELD_PREP(NDE_SCALE_MASK, scale) | -+ FIELD_PREP(NDE_VALID_MASK, valid) | -+ FIELD_PREP(NDE_RESVEC_MASK, resvec); -+ -+ writel(val, isp->base + nde_reg); -+} -+ -+static int ipu7_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ -+ exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -+ if (ctrl->subsys_id == IPU_IS) { -+ ret = isys_d2d_power(dev, true); -+ if (ret) -+ goto out_power; -+ isys_nde_control(dev, true); -+ } -+ -+ /* request clock resource ownership */ -+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ val |= ctrl->ovrd_clk; -+ writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS, -+ val, (val & ctrl->own_clk_ack), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_warn(dev, "request clk ownership timeout. status 0x%x\n", -+ val); -+ -+ val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -+ -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power up timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ -+ /* release clock resource ownership */ -+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ val &= ~ctrl->ovrd_clk; -+ writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG); -+ -+out_power: -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+static int ipu7_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ -+ exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -+ val = 0x8U << ctrl->ratio_shift; -+ -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power down timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+out_power: -+ if (ctrl->subsys_id == IPU_IS && !ret) { -+ isys_d2d_power(dev, false); -+ isys_nde_control(dev, false); -+ } -+ -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+static int ipu8_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 sleep_level_reg = BUTTRESS_REG_SLEEP_LEVEL_STS; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift; -+ if (ctrl->subsys_id == IPU_IS) { -+ ret = isys_d2d_power(dev, true); -+ if (ret) -+ goto out_power; -+ isys_nde_control(dev, true); -+ } -+ -+ /* request ps_pll when psys freq > 400Mhz */ -+ if (ctrl->subsys_id == IPU_PS && ctrl->ratio > 0x10) { -+ writel(1, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -+ ret = readl_poll_timeout(isp->base + sleep_level_reg, -+ val, (val & ctrl->own_clk_ack), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) -+ dev_warn(dev, "ps_pll req ack timeout. status 0x%x\n", -+ val); -+ } -+ -+ val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift; -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power up timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power up successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+out_power: -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+static int ipu8_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ u32 val, exp_sts; -+ int ret = 0; -+ -+ if (!ctrl) -+ return 0; -+ -+ mutex_lock(&isp->buttress.power_mutex); -+ exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift; -+ -+ if (ctrl->subsys_id == IPU_PS) -+ val = 0x10U << ctrl->ratio_shift; -+ else -+ val = 0x8U << ctrl->ratio_shift; -+ -+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val, -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS"); -+ writel(val, isp->base + ctrl->freq_ctl); -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, -+ val, ((val & ctrl->pwr_sts_mask) == exp_sts), -+ 100, BUTTRESS_POWER_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "%s power down timeout with status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+ goto out_power; -+ } -+ -+ dev_dbg(dev, "%s power down successfully. status: 0x%x\n", -+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val); -+out_power: -+ if (ctrl->subsys_id == IPU_IS && !ret) { -+ isys_d2d_power(dev, false); -+ isys_nde_control(dev, false); -+ } -+ -+ if (ctrl->subsys_id == IPU_PS) { -+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS); -+ if (val & ctrl->own_clk_ack) -+ writel(0, isp->base + BUTTRESS_REG_PS_PLL_ENABLE); -+ } -+ mutex_unlock(&isp->buttress.power_mutex); -+ -+ return ret; -+} -+ -+int ipu_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ -+ if (is_ipu8(isp->hw_ver)) -+ return ipu8_buttress_powerup(dev, ctrl); -+ -+ return ipu7_buttress_powerup(dev, ctrl); -+} -+ -+int ipu_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl) -+{ -+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp; -+ -+ if (is_ipu8(isp->hw_ver)) -+ return ipu8_buttress_powerdown(dev, ctrl); -+ -+ return ipu7_buttress_powerdown(dev, ctrl); -+} -+ -+bool ipu_buttress_get_secure_mode(struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -+ -+ return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE; -+} -+ -+bool ipu_buttress_auth_done(struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ if (!isp->secure_mode) -+ return true; -+ -+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL); -+ val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val); -+ -+ return val == BUTTRESS_SECURITY_CTL_AUTH_DONE; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_auth_done, "INTEL_IPU7"); -+ -+int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq) -+{ -+ u32 reg_val; -+ int ret; -+ -+ ret = pm_runtime_get_sync(&isp->isys->auxdev.dev); -+ if (ret < 0) { -+ pm_runtime_put(&isp->isys->auxdev.dev); -+ dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -+ return ret; -+ } -+ -+ reg_val = readl(isp->base + BUTTRESS_REG_IS_WORKPOINT_REQ); -+ -+ pm_runtime_put(&isp->isys->auxdev.dev); -+ -+ if (is_ipu8(isp->hw_ver)) -+ *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 25; -+ else -+ *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 50 / 3; -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_isys_freq, "INTEL_IPU7"); -+ -+int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq) -+{ -+ u32 reg_val; -+ int ret; -+ -+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret); -+ return ret; -+ } -+ -+ reg_val = readl(isp->base + BUTTRESS_REG_PS_WORKPOINT_REQ); -+ -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ -+ reg_val &= BUTTRESS_PS_FREQ_CTL_RATIO_MASK; -+ *freq = BUTTRESS_PS_FREQ_RATIO_STEP * reg_val; -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_psys_freq, "INTEL_IPU7"); -+ -+int ipu_buttress_reset_authentication(struct ipu7_device *isp) -+{ -+ struct device *dev = &isp->pdev->dev; -+ int ret; -+ u32 val; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(dev, "Skip auth for non-secure mode\n"); -+ return 0; -+ } -+ -+ writel(BUTTRESS_FW_RESET_CTL_START, isp->base + -+ BUTTRESS_REG_FW_RESET_CTL); -+ -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val, -+ val & BUTTRESS_FW_RESET_CTL_DONE, 500, -+ BUTTRESS_CSE_FWRESET_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "Time out while resetting authentication state\n"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "FW reset for authentication done\n"); -+ writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL); -+ /* leave some time for HW restore */ -+ usleep_range(800, 1000); -+ -+ return 0; -+} -+ -+int ipu_buttress_authenticate(struct ipu7_device *isp) -+{ -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ u32 data, mask, done, fail; -+ int ret; -+ -+ if (!isp->secure_mode) { -+ dev_dbg(dev, "Skip auth for non-secure mode\n"); -+ return 0; -+ } -+ -+ mutex_lock(&b->auth_mutex); -+ -+ if (ipu_buttress_auth_done(isp)) { -+ ret = 0; -+ goto out_unlock; -+ } -+ -+ /* -+ * BUTTRESS_REG_FW_SOURCE_BASE needs to be set with FW CPD -+ * package address for secure mode. -+ */ -+ -+ writel(isp->cpd_fw->size, isp->base + BUTTRESS_REG_FW_SOURCE_SIZE); -+ writel(sg_dma_address(isp->psys->fw_sgt.sgl), -+ isp->base + BUTTRESS_REG_FW_SOURCE_BASE); -+ -+ /* -+ * Write boot_load into IU2CSEDATA0 -+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -+ */ -+ dev_info(dev, "Sending BOOT_LOAD to CSE\n"); -+ ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD, -+ 1, true, -+ BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE); -+ if (ret) { -+ dev_err(dev, "CSE boot_load failed\n"); -+ goto out_unlock; -+ } -+ -+ mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK; -+ done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE; -+ fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -+ ((data & mask) == done || -+ (data & mask) == fail), 500, -+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "CSE boot_load timeout\n"); -+ goto out_unlock; -+ } -+ -+ if ((data & mask) == fail) { -+ dev_err(dev, "CSE auth failed\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ ret = readl_poll_timeout(isp->base + BOOTLOADER_STATUS_OFFSET, -+ data, data == BOOTLOADER_MAGIC_KEY, 500, -+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "Unexpected magic number 0x%x\n", data); -+ goto out_unlock; -+ } -+ -+ /* -+ * Write authenticate_run into IU2CSEDATA0 -+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to -+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as -+ */ -+ dev_info(dev, "Sending AUTHENTICATE_RUN to CSE\n"); -+ ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN, -+ 1, true, -+ BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE); -+ if (ret) { -+ dev_err(dev, "CSE authenticate_run failed\n"); -+ goto out_unlock; -+ } -+ -+ done = BUTTRESS_SECURITY_CTL_AUTH_DONE; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data, -+ ((data & mask) == done || -+ (data & mask) == fail), 500, -+ BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US); -+ if (ret) { -+ dev_err(dev, "CSE authenticate timeout\n"); -+ goto out_unlock; -+ } -+ -+ if ((data & mask) == fail) { -+ dev_err(dev, "CSE boot_load failed\n"); -+ ret = -EINVAL; -+ goto out_unlock; -+ } -+ -+ dev_info(dev, "CSE authenticate_run done\n"); -+ -+out_unlock: -+ mutex_unlock(&b->auth_mutex); -+ -+ return ret; -+} -+ -+static int ipu_buttress_send_tsc_request(struct ipu7_device *isp) -+{ -+ u32 val, mask, done; -+ int ret; -+ -+ mask = BUTTRESS_PWR_STATUS_HH_STATUS_MASK; -+ -+ writel(BUTTRESS_TSC_CMD_START_TSC_SYNC, -+ isp->base + BUTTRESS_REG_TSC_CMD); -+ -+ val = readl(isp->base + BUTTRESS_REG_PWR_STATUS); -+ val = FIELD_GET(mask, val); -+ if (val == BUTTRESS_PWR_STATUS_HH_STATE_ERR) { -+ dev_err(&isp->pdev->dev, "Start tsc sync failed\n"); -+ return -EINVAL; -+ } -+ -+ done = BUTTRESS_PWR_STATUS_HH_STATE_DONE; -+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, val, -+ FIELD_GET(mask, val) == done, 500, -+ BUTTRESS_TSC_SYNC_TIMEOUT_US); -+ if (ret) -+ dev_err(&isp->pdev->dev, "Start tsc sync timeout\n"); -+ -+ return ret; -+} -+ -+int ipu_buttress_start_tsc_sync(struct ipu7_device *isp) -+{ -+ void __iomem *base = isp->base; -+ unsigned int i; -+ u32 val; -+ -+ if (is_ipu8(isp->hw_ver)) { -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -+ if (val == 1) -+ return 0; -+ usleep_range(40, 50); -+ } -+ -+ dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -+ return -ETIMEDOUT; -+ } -+ -+ if (is_ipu7p5(isp->hw_ver)) { -+ val = readl(base + BUTTRESS_REG_TSC_CTL); -+ val |= BUTTRESS_SEL_PB_TIMESTAMP; -+ writel(val, base + BUTTRESS_REG_TSC_CTL); -+ -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID); -+ if (val == 1) -+ return 0; -+ usleep_range(40, 50); -+ } -+ -+ dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val); -+ -+ return -ETIMEDOUT; -+ } -+ -+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) { -+ int ret; -+ -+ ret = ipu_buttress_send_tsc_request(isp); -+ if (ret != -ETIMEDOUT) -+ return ret; -+ -+ val = readl(base + BUTTRESS_REG_TSC_CTL); -+ val = val | BUTTRESS_TSW_WA_SOFT_RESET; -+ writel(val, base + BUTTRESS_REG_TSC_CTL); -+ val = val & (~BUTTRESS_TSW_WA_SOFT_RESET); -+ writel(val, base + BUTTRESS_REG_TSC_CTL); -+ } -+ -+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n"); -+ -+ return -ETIMEDOUT; -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_start_tsc_sync, "INTEL_IPU7"); -+ -+void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val) -+{ -+ unsigned long flags; -+ u32 tsc_hi, tsc_lo; -+ -+ local_irq_save(flags); -+ if (is_ipu7(isp->hw_ver)) { -+ tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO); -+ tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI); -+ } else { -+ tsc_lo = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_LO); -+ tsc_hi = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_HI); -+ } -+ *val = (u64)tsc_hi << 32 | tsc_lo; -+ local_irq_restore(flags); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_read, "INTEL_IPU7"); -+ -+u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp) -+{ -+ u64 ns = ticks * 10000; -+ -+ /* -+ * converting TSC tick count to ns is calculated by: -+ * Example (TSC clock frequency is 19.2MHz): -+ * ns = ticks * 1000 000 000 / 19.2Mhz -+ * = ticks * 1000 000 000 / 19200000Hz -+ * = ticks * 10000 / 192 ns -+ */ -+ return div_u64(ns, isp->buttress.ref_clk); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_ticks_to_ns, "INTEL_IPU7"); -+ -+/* trigger uc control to wakeup fw */ -+void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); -+ val |= UCX_CTL_WAKEUP; -+ writel(val, isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_is_uc, "INTEL_IPU7"); -+ -+void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp) -+{ -+ u32 val; -+ -+ val = readl(isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); -+ val |= UCX_CTL_WAKEUP; -+ writel(val, isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS); -+} -+EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_ps_uc, "INTEL_IPU7"); -+ -+static const struct x86_cpu_id ipu_misc_cfg_exclusion[] = { -+ X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0), -+ {}, -+}; -+ -+static void ipu_buttress_setup(struct ipu7_device *isp) -+{ -+ struct device *dev = &isp->pdev->dev; -+ u32 val; -+ -+ /* program PB BAR */ -+#define WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19) -+ writel(0, isp->pb_base + GLOBAL_INTERRUPT_MASK); -+ val = readl(isp->pb_base + BAR2_MISC_CONFIG); -+ if (is_ipu7(isp->hw_ver) || x86_match_cpu(ipu_misc_cfg_exclusion)) -+ val |= 0x100U; -+ else -+ val |= FIELD_PREP(WRXREQOP_OVRD_VAL_MASK, 0xf) | -+ BIT(18) | 0x100U; -+ -+ writel(val, isp->pb_base + BAR2_MISC_CONFIG); -+ val = readl(isp->pb_base + BAR2_MISC_CONFIG); -+ -+ if (is_ipu8(isp->hw_ver)) { -+ writel(BIT(13), isp->pb_base + TLBID_HASH_ENABLE_63_32); -+ writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -+ dev_dbg(dev, "IPU8 TLBID_HASH %x %x\n", -+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -+ readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -+ } else if (is_ipu7p5(isp->hw_ver)) { -+ writel(BIT(14), isp->pb_base + TLBID_HASH_ENABLE_63_32); -+ writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64); -+ dev_dbg(dev, "IPU7P5 TLBID_HASH %x %x\n", -+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -+ readl(isp->pb_base + TLBID_HASH_ENABLE_95_64)); -+ } else { -+ writel(BIT(22), isp->pb_base + TLBID_HASH_ENABLE_63_32); -+ writel(BIT(1), isp->pb_base + TLBID_HASH_ENABLE_127_96); -+ dev_dbg(dev, "TLBID_HASH %x %x\n", -+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32), -+ readl(isp->pb_base + TLBID_HASH_ENABLE_127_96)); -+ } -+ -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_CLEAR); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_MASK); -+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ /* LNL SW workaround for PS PD hang when PS sub-domain during PD */ -+ writel(PS_FSM_CG, isp->base + BUTTRESS_REG_CG_CTRL_BITS); -+} -+ -+void ipu_buttress_restore(struct ipu7_device *isp) -+{ -+ struct ipu_buttress *b = &isp->buttress; -+ -+ ipu_buttress_setup(isp); -+ -+ writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_IDLE_WDT); -+} -+ -+int ipu_buttress_init(struct ipu7_device *isp) -+{ -+ int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY; -+ struct ipu_buttress *b = &isp->buttress; -+ struct device *dev = &isp->pdev->dev; -+ u32 val; -+ -+ mutex_init(&b->power_mutex); -+ mutex_init(&b->auth_mutex); -+ mutex_init(&b->cons_mutex); -+ mutex_init(&b->ipc_mutex); -+ init_completion(&b->cse.send_complete); -+ init_completion(&b->cse.recv_complete); -+ -+ b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK; -+ b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK; -+ b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR; -+ b->cse.csr_out = BUTTRESS_REG_IU2CSECSR; -+ b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0; -+ b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0; -+ b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0; -+ b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0; -+ -+ isp->secure_mode = ipu_buttress_get_secure_mode(isp); -+ val = readl(isp->base + BUTTRESS_REG_IPU_SKU); -+ dev_info(dev, "IPU%u SKU %u in %s mode mask 0x%x\n", val & 0xfU, -+ (val >> 4) & 0x7U, isp->secure_mode ? "secure" : "non-secure", -+ readl(isp->base + BUTTRESS_REG_CAMERA_MASK)); -+ b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_IDLE_WDT); -+ b->ref_clk = 384; -+ -+ ipu_buttress_setup(isp); -+ -+ /* Retry couple of times in case of CSE initialization is delayed */ -+ do { -+ ret = ipu_buttress_ipc_reset(isp, &b->cse); -+ if (ret) { -+ dev_warn(dev, "IPC reset protocol failed, retrying\n"); -+ } else { -+ dev_dbg(dev, "IPC reset done\n"); -+ return 0; -+ } -+ } while (ipc_reset_retry--); -+ -+ dev_err(dev, "IPC reset protocol failed\n"); -+ -+ mutex_destroy(&b->power_mutex); -+ mutex_destroy(&b->auth_mutex); -+ mutex_destroy(&b->cons_mutex); -+ mutex_destroy(&b->ipc_mutex); -+ -+ return ret; -+} -+ -+void ipu_buttress_exit(struct ipu7_device *isp) -+{ -+ struct ipu_buttress *b = &isp->buttress; -+ -+ writel(0, isp->base + BUTTRESS_REG_IRQ_ENABLE); -+ mutex_destroy(&b->power_mutex); -+ mutex_destroy(&b->auth_mutex); -+ mutex_destroy(&b->cons_mutex); -+ mutex_destroy(&b->ipc_mutex); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-buttress.h b/drivers/media/pci/intel/ipu7/ipu7-buttress.h -new file mode 100644 -index 000000000000..8da7dd612575 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-buttress.h -@@ -0,0 +1,77 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_BUTTRESS_H -+#define IPU7_BUTTRESS_H -+ -+#include -+#include -+#include -+#include -+ -+struct device; -+struct ipu7_device; -+ -+struct ipu_buttress_ctrl { -+ u32 subsys_id; -+ u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off; -+ u32 ratio; -+ u32 ratio_shift; -+ u32 cdyn; -+ u32 cdyn_shift; -+ u32 ovrd_clk; -+ u32 own_clk_ack; -+}; -+ -+struct ipu_buttress_ipc { -+ struct completion send_complete; -+ struct completion recv_complete; -+ u32 nack; -+ u32 nack_mask; -+ u32 recv_data; -+ u32 csr_out; -+ u32 csr_in; -+ u32 db0_in; -+ u32 db0_out; -+ u32 data0_out; -+ u32 data0_in; -+}; -+ -+struct ipu_buttress { -+ struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex; -+ struct ipu_buttress_ipc cse; -+ u32 psys_min_freq; -+ u32 wdt_cached_value; -+ u8 psys_force_ratio; -+ bool force_suspend; -+ u32 ref_clk; -+}; -+ -+int ipu_buttress_ipc_reset(struct ipu7_device *isp, -+ struct ipu_buttress_ipc *ipc); -+int ipu_buttress_powerup(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl); -+int ipu_buttress_powerdown(struct device *dev, -+ const struct ipu_buttress_ctrl *ctrl); -+bool ipu_buttress_get_secure_mode(struct ipu7_device *isp); -+int ipu_buttress_authenticate(struct ipu7_device *isp); -+int ipu_buttress_reset_authentication(struct ipu7_device *isp); -+bool ipu_buttress_auth_done(struct ipu7_device *isp); -+int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq); -+int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq); -+int ipu_buttress_start_tsc_sync(struct ipu7_device *isp); -+void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val); -+u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp); -+ -+irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr); -+irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr); -+int ipu_buttress_init(struct ipu7_device *isp); -+void ipu_buttress_exit(struct ipu7_device *isp); -+void ipu_buttress_csi_port_config(struct ipu7_device *isp, -+ u32 legacy, u32 combo); -+void ipu_buttress_restore(struct ipu7_device *isp); -+void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp); -+void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp); -+#endif /* IPU7_BUTTRESS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.c b/drivers/media/pci/intel/ipu7/ipu7-cpd.c -new file mode 100644 -index 000000000000..25dd71e1809e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-cpd.c -@@ -0,0 +1,277 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2015 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-cpd.h" -+ -+/* $CPD */ -+#define CPD_HDR_MARK 0x44504324 -+ -+/* Maximum size is 4K DWORDs or 16KB */ -+#define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32)) -+ -+#define CPD_MANIFEST_IDX 0 -+#define CPD_BINARY_START_IDX 1U -+#define CPD_METADATA_START_IDX 2U -+#define CPD_BINARY_NUM 2U /* ISYS + PSYS */ -+/* -+ * Entries include: -+ * 1 manifest entry. -+ * 1 metadata entry for each sub system(ISYS and PSYS). -+ * 1 binary entry for each sub system(ISYS and PSYS). -+ */ -+#define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U) -+ -+#define CPD_METADATA_ATTR 0xa -+#define CPD_METADATA_IPL 0x1c -+#define ONLINE_METADATA_SIZE 128U -+#define ONLINE_METADATA_LINES 6U -+ -+struct ipu7_cpd_hdr { -+ u32 hdr_mark; -+ u32 ent_cnt; -+ u8 hdr_ver; -+ u8 ent_ver; -+ u8 hdr_len; -+ u8 rsvd; -+ u8 partition_name[4]; -+ u32 crc32; -+} __packed; -+ -+struct ipu7_cpd_ent { -+ u8 name[12]; -+ u32 offset; -+ u32 len; -+ u8 rsvd[4]; -+} __packed; -+ -+struct ipu7_cpd_metadata_hdr { -+ u32 type; -+ u32 len; -+} __packed; -+ -+struct ipu7_cpd_metadata_attr { -+ struct ipu7_cpd_metadata_hdr hdr; -+ u8 compression_type; -+ u8 encryption_type; -+ u8 rsvd[2]; -+ u32 uncompressed_size; -+ u32 compressed_size; -+ u32 module_id; -+ u8 hash[48]; -+} __packed; -+ -+struct ipu7_cpd_metadata_ipl { -+ struct ipu7_cpd_metadata_hdr hdr; -+ u32 param[4]; -+ u8 rsvd[8]; -+} __packed; -+ -+struct ipu7_cpd_metadata { -+ struct ipu7_cpd_metadata_attr attr; -+ struct ipu7_cpd_metadata_ipl ipl; -+} __packed; -+ -+static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx) -+{ -+ const struct ipu7_cpd_hdr *cpd_hdr = cpd; -+ -+ return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx; -+} -+ -+#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0) -+ -+static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx) -+{ -+ struct ipu7_cpd_ent *cpd_ent = -+ ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -+ -+ return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset); -+} -+ -+static int ipu7_cpd_validate_cpd(struct ipu7_device *isp, -+ const void *cpd, unsigned long data_size) -+{ -+ const struct ipu7_cpd_hdr *cpd_hdr = cpd; -+ struct device *dev = &isp->pdev->dev; -+ struct ipu7_cpd_ent *ent; -+ unsigned int i; -+ u8 len; -+ -+ len = cpd_hdr->hdr_len; -+ -+ /* Ensure cpd hdr is within moduledata */ -+ if (data_size < len) { -+ dev_err(dev, "Invalid CPD moduledata size\n"); -+ return -EINVAL; -+ } -+ -+ /* Check for CPD file marker */ -+ if (cpd_hdr->hdr_mark != CPD_HDR_MARK) { -+ dev_err(dev, "Invalid CPD header marker\n"); -+ return -EINVAL; -+ } -+ -+ /* Sanity check for CPD entry header */ -+ if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) { -+ dev_err(dev, "Invalid CPD entry number %d\n", -+ cpd_hdr->ent_cnt); -+ return -EINVAL; -+ } -+ if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) { -+ dev_err(dev, "Invalid CPD entry headers\n"); -+ return -EINVAL; -+ } -+ -+ /* Ensure that all entries are within moduledata */ -+ ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len); -+ for (i = 0; i < cpd_hdr->ent_cnt; i++) { -+ if (data_size < ent->offset || -+ data_size - ent->offset < ent->len) { -+ dev_err(dev, "Invalid CPD entry %d\n", i); -+ return -EINVAL; -+ } -+ ent++; -+ } -+ -+ return 0; -+} -+ -+static int ipu7_cpd_validate_metadata(struct ipu7_device *isp, -+ const void *cpd, int idx) -+{ -+ const struct ipu7_cpd_ent *cpd_ent = -+ ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2); -+ const struct ipu7_cpd_metadata *metadata = -+ ipu7_cpd_get_metadata(cpd, idx); -+ struct device *dev = &isp->pdev->dev; -+ -+ /* Sanity check for metadata size */ -+ if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) { -+ dev_err(dev, "Invalid metadata size\n"); -+ return -EINVAL; -+ } -+ -+ /* Validate type and length of metadata sections */ -+ if (metadata->attr.hdr.type != CPD_METADATA_ATTR) { -+ dev_err(dev, "Invalid metadata attr type (%d)\n", -+ metadata->attr.hdr.type); -+ return -EINVAL; -+ } -+ if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) { -+ dev_err(dev, "Invalid metadata attr size (%d)\n", -+ metadata->attr.hdr.len); -+ return -EINVAL; -+ } -+ if (metadata->ipl.hdr.type != CPD_METADATA_IPL) { -+ dev_err(dev, "Invalid metadata ipl type (%d)\n", -+ metadata->ipl.hdr.type); -+ return -EINVAL; -+ } -+ if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) { -+ dev_err(dev, "Invalid metadata ipl size (%d)\n", -+ metadata->ipl.hdr.len); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file, -+ unsigned long cpd_file_size) -+{ -+ struct device *dev = &isp->pdev->dev; -+ struct ipu7_cpd_ent *ent; -+ unsigned int i; -+ int ret; -+ char *buf; -+ -+ ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size); -+ if (ret) { -+ dev_err(dev, "Invalid CPD in file\n"); -+ return -EINVAL; -+ } -+ -+ /* Sanity check for manifest size */ -+ ent = ipu7_cpd_get_manifest(cpd_file); -+ if (ent->len > MAX_MANIFEST_SIZE) { -+ dev_err(dev, "Invalid manifest size\n"); -+ return -EINVAL; -+ } -+ -+ /* Validate metadata */ -+ for (i = 0; i < CPD_BINARY_NUM; i++) { -+ ret = ipu7_cpd_validate_metadata(isp, cpd_file, i); -+ if (ret) { -+ dev_err(dev, "Invalid metadata%d\n", i); -+ return ret; -+ } -+ } -+ -+ /* Get fw binary version. */ -+ buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ for (i = 0; i < CPD_BINARY_NUM; i++) { -+ char *lines[ONLINE_METADATA_LINES]; -+ char *info = buf; -+ unsigned int l; -+ -+ ent = ipu7_cpd_get_entry(cpd_file, -+ CPD_BINARY_START_IDX + i * 2U); -+ memcpy(info, (u8 *)cpd_file + ent->offset + ent->len - -+ ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE); -+ for (l = 0; l < ONLINE_METADATA_LINES; l++) { -+ lines[l] = strsep((char **)&info, "\n"); -+ if (!lines[l]) -+ break; -+ } -+ if (l < ONLINE_METADATA_LINES) { -+ dev_err(dev, "Failed to parse fw binary%d info.\n", i); -+ continue; -+ } -+ dev_info(dev, "FW binary%d info:\n", i); -+ dev_info(dev, "Name: %s\n", lines[1]); -+ dev_info(dev, "Version: %s\n", lines[2]); -+ dev_info(dev, "Timestamp: %s\n", lines[3]); -+ dev_info(dev, "Commit: %s\n", lines[4]); -+ } -+ kfree(buf); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7"); -+ -+int ipu7_cpd_copy_binary(const void *cpd, const char *name, -+ void *code_region, u32 *entry) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < CPD_BINARY_NUM; i++) { -+ const struct ipu7_cpd_ent *binary = -+ ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U); -+ const struct ipu7_cpd_metadata *metadata = -+ ipu7_cpd_get_metadata(cpd, i); -+ -+ if (!strncmp(binary->name, name, sizeof(binary->name))) { -+ memcpy(code_region + metadata->ipl.param[0], -+ cpd + binary->offset, binary->len); -+ *entry = metadata->ipl.param[2]; -+ return 0; -+ } -+ } -+ -+ return -ENOENT; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-cpd.h b/drivers/media/pci/intel/ipu7/ipu7-cpd.h -new file mode 100644 -index 000000000000..b4178848c6b9 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-cpd.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2015 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_CPD_H -+#define IPU7_CPD_H -+ -+struct ipu7_device; -+ -+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, -+ const void *cpd_file, -+ unsigned long cpd_file_size); -+int ipu7_cpd_copy_binary(const void *cpd, const char *name, -+ void *code_region, u32 *entry); -+#endif /* IPU7_CPD_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.c b/drivers/media/pci/intel/ipu7/ipu7-dma.c -new file mode 100644 -index 000000000000..d974e13e7933 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-dma.c -@@ -0,0 +1,478 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-mmu.h" -+ -+struct vm_info { -+ struct list_head list; -+ struct page **pages; -+ dma_addr_t ipu7_iova; -+ void *vaddr; -+ unsigned long size; -+}; -+ -+static struct vm_info *get_vm_info(struct ipu7_mmu *mmu, dma_addr_t iova) -+{ -+ struct vm_info *info, *save; -+ -+ list_for_each_entry_safe(info, save, &mmu->vma_list, list) { -+ if (iova >= info->ipu7_iova && -+ iova < (info->ipu7_iova + info->size)) -+ return info; -+ } -+ -+ return NULL; -+} -+ -+static void __clear_buffer(struct page *page, size_t size, unsigned long attrs) -+{ -+ void *ptr; -+ -+ if (!page) -+ return; -+ /* -+ * Ensure that the allocated pages are zeroed, and that any data -+ * lurking in the kernel direct-mapped region is invalidated. -+ */ -+ ptr = page_address(page); -+ memset(ptr, 0, size); -+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0) -+ clflush_cache_range(ptr, size); -+} -+ -+static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs) -+{ -+ unsigned int count = PHYS_PFN(size); -+ unsigned int array_size = count * sizeof(struct page *); -+ struct page **pages; -+ int i = 0; -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ return NULL; -+ -+ gfp |= __GFP_NOWARN; -+ -+ while (count) { -+ int j, order = __fls(count); -+ -+ pages[i] = alloc_pages(gfp, order); -+ while (!pages[i] && order) -+ pages[i] = alloc_pages(gfp, --order); -+ if (!pages[i]) -+ goto error; -+ -+ if (order) { -+ split_page(pages[i], order); -+ j = 1U << order; -+ while (j--) -+ pages[i + j] = pages[i] + j; -+ } -+ -+ __clear_buffer(pages[i], PAGE_SIZE << order, attrs); -+ i += 1U << order; -+ count -= 1U << order; -+ } -+ -+ return pages; -+error: -+ while (i--) -+ if (pages[i]) -+ __free_pages(pages[i], 0); -+ kvfree(pages); -+ return NULL; -+} -+ -+static void __free_buffer(struct page **pages, size_t size, unsigned long attrs) -+{ -+ unsigned int count = PHYS_PFN(size); -+ unsigned int i; -+ -+ for (i = 0; i < count && pages[i]; i++) { -+ __clear_buffer(pages[i], PAGE_SIZE, attrs); -+ __free_pages(pages[i], 0); -+ } -+ -+ kvfree(pages); -+} -+ -+void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -+ size_t size) -+{ -+ void *vaddr; -+ u32 offset; -+ struct vm_info *info; -+ struct ipu7_mmu *mmu = sys->mmu; -+ -+ info = get_vm_info(mmu, dma_handle); -+ if (WARN_ON(!info)) -+ return; -+ -+ offset = dma_handle - info->ipu7_iova; -+ if (WARN_ON(size > (info->size - offset))) -+ return; -+ -+ vaddr = info->vaddr + offset; -+ clflush_cache_range(vaddr, size); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_single, "INTEL_IPU7"); -+ -+void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ unsigned int nents) -+{ -+ struct scatterlist *sg; -+ int i; -+ -+ for_each_sg(sglist, sg, nents, i) -+ clflush_cache_range(sg_virt(sg), sg->length); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sg, "INTEL_IPU7"); -+ -+void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt) -+{ -+ ipu7_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sgtable, "INTEL_IPU7"); -+ -+void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -+ dma_addr_t *dma_handle, gfp_t gfp, -+ unsigned long attrs) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct pci_dev *pdev = sys->isp->pdev; -+ dma_addr_t pci_dma_addr, ipu7_iova; -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct vm_info *info; -+ unsigned long count; -+ struct page **pages; -+ struct iova *iova; -+ unsigned int i; -+ int ret; -+ -+ info = kzalloc(sizeof(*info), GFP_KERNEL); -+ if (!info) -+ return NULL; -+ -+ size = PAGE_ALIGN(size); -+ count = PHYS_PFN(size); -+ -+ iova = alloc_iova(&mmu->dmap->iovad, count, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -+ if (!iova) -+ goto out_kfree; -+ -+ pages = __alloc_buffer(size, gfp, attrs); -+ if (!pages) -+ goto out_free_iova; -+ -+ dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n", -+ size, iova->pfn_lo, iova->pfn_hi); -+ for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) { -+ pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0, -+ PAGE_SIZE, DMA_BIDIRECTIONAL, -+ attrs); -+ dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n", -+ &pci_dma_addr); -+ if (dma_mapping_error(&pdev->dev, pci_dma_addr)) { -+ dev_err(dev, "pci_dma_mapping for page[%d] failed", i); -+ goto out_unmap; -+ } -+ -+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, -+ PFN_PHYS(iova->pfn_lo + i), pci_dma_addr, -+ PAGE_SIZE); -+ if (ret) { -+ dev_err(dev, "ipu7_mmu_map for pci_dma[%d] %pad failed", -+ i, &pci_dma_addr); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, -+ PAGE_SIZE, DMA_BIDIRECTIONAL, -+ attrs); -+ goto out_unmap; -+ } -+ } -+ -+ info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); -+ if (!info->vaddr) -+ goto out_unmap; -+ -+ *dma_handle = PFN_PHYS(iova->pfn_lo); -+ -+ info->pages = pages; -+ info->ipu7_iova = *dma_handle; -+ info->size = size; -+ list_add(&info->list, &mmu->vma_list); -+ -+ return info->vaddr; -+ -+out_unmap: -+ while (i--) { -+ ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ ipu7_iova); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -+ DMA_BIDIRECTIONAL, attrs); -+ -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, ipu7_iova, PAGE_SIZE); -+ } -+ -+ __free_buffer(pages, size, attrs); -+ -+out_free_iova: -+ __free_iova(&mmu->dmap->iovad, iova); -+out_kfree: -+ kfree(info); -+ -+ return NULL; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_alloc, "INTEL_IPU7"); -+ -+void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -+ dma_addr_t dma_handle, unsigned long attrs) -+{ -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct pci_dev *pdev = sys->isp->pdev; -+ struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle)); -+ dma_addr_t pci_dma_addr, ipu7_iova; -+ struct vm_info *info; -+ struct page **pages; -+ unsigned int i; -+ -+ if (WARN_ON(!iova)) -+ return; -+ -+ info = get_vm_info(mmu, dma_handle); -+ if (WARN_ON(!info)) -+ return; -+ -+ if (WARN_ON(!info->vaddr)) -+ return; -+ -+ if (WARN_ON(!info->pages)) -+ return; -+ -+ list_del(&info->list); -+ -+ size = PAGE_ALIGN(size); -+ -+ pages = info->pages; -+ -+ vunmap(vaddr); -+ -+ for (i = 0; i < PHYS_PFN(size); i++) { -+ ipu7_iova = PFN_PHYS(iova->pfn_lo + i); -+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ ipu7_iova); -+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE, -+ DMA_BIDIRECTIONAL, attrs); -+ } -+ -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ -+ __free_buffer(pages, size, attrs); -+ -+ mmu->tlb_invalidate(mmu); -+ -+ __free_iova(&mmu->dmap->iovad, iova); -+ -+ kfree(info); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_free, "INTEL_IPU7"); -+ -+int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -+ void *addr, dma_addr_t iova, size_t size, -+ unsigned long attrs) -+{ -+ struct ipu7_mmu *mmu = sys->mmu; -+ size_t count = PFN_UP(size); -+ struct vm_info *info; -+ size_t i; -+ int ret; -+ -+ info = get_vm_info(mmu, iova); -+ if (!info) -+ return -EFAULT; -+ -+ if (!info->vaddr) -+ return -EFAULT; -+ -+ if (vma->vm_start & ~PAGE_MASK) -+ return -EINVAL; -+ -+ if (size > info->size) -+ return -EFAULT; -+ -+ for (i = 0; i < count; i++) { -+ ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i), -+ info->pages[i]); -+ if (ret < 0) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct iova *iova = find_iova(&mmu->dmap->iovad, -+ PHYS_PFN(sg_dma_address(sglist))); -+ struct scatterlist *sg; -+ dma_addr_t pci_dma_addr; -+ unsigned int i; -+ -+ if (!nents) -+ return; -+ -+ if (WARN_ON(!iova)) -+ return; -+ -+ /* -+ * Before IPU7 mmu unmap, return the pci dma address back to sg -+ * assume the nents is less than orig_nents as the least granule -+ * is 1 SZ_4K page -+ */ -+ dev_dbg(dev, "trying to unmap concatenated %u ents\n", nents); -+ for_each_sg(sglist, sg, nents, i) { -+ dev_dbg(dev, "unmap sg[%d] %pad size %u\n", i, -+ &sg_dma_address(sg), sg_dma_len(sg)); -+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info, -+ sg_dma_address(sg)); -+ dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n", -+ &pci_dma_addr, i); -+ sg_dma_address(sg) = pci_dma_addr; -+ } -+ -+ dev_dbg(dev, "ipu7_mmu_unmap low pfn %lu high pfn %lu\n", -+ iova->pfn_lo, iova->pfn_hi); -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ -+ mmu->tlb_invalidate(mmu); -+ __free_iova(&mmu->dmap->iovad, iova); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7"); -+ -+int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_mmu *mmu = sys->mmu; -+ struct scatterlist *sg; -+ struct iova *iova; -+ size_t npages = 0; -+ unsigned long iova_addr; -+ int i; -+ -+ for_each_sg(sglist, sg, nents, i) { -+ if (sg->offset) { -+ dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n", -+ i, sg->offset); -+ return -EFAULT; -+ } -+ } -+ -+ for_each_sg(sglist, sg, nents, i) -+ npages += PFN_UP(sg_dma_len(sg)); -+ -+ dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n", -+ nents, npages); -+ -+ if (attrs & DMA_ATTR_RESERVE_REGION) { -+ /* -+ * Reserve iova with size aligned to IPU_FW_CODE_REGION_SIZE. -+ * Only apply for non-secure mode. -+ */ -+ unsigned long lo, hi; -+ -+ lo = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_START); -+ hi = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_END) - 1U; -+ iova = reserve_iova(&mmu->dmap->iovad, lo, hi); -+ if (!iova) { -+ dev_err(dev, "Reserve iova[%lx:%lx] failed.\n", lo, hi); -+ return -ENOMEM; -+ } -+ dev_dbg(dev, "iova[%lx:%lx] reserved for FW code.\n", lo, hi); -+ } else { -+ iova = alloc_iova(&mmu->dmap->iovad, npages, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), -+ 0); -+ if (!iova) -+ return 0; -+ } -+ -+ dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo, -+ iova->pfn_hi); -+ -+ iova_addr = iova->pfn_lo; -+ for_each_sg(sglist, sg, nents, i) { -+ phys_addr_t iova_pa; -+ int ret; -+ -+ iova_pa = PFN_PHYS(iova_addr); -+ dev_dbg(dev, "mapping entry %d: iova %pap phy %pap size %d\n", -+ i, &iova_pa, &sg_dma_address(sg), sg_dma_len(sg)); -+ -+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -+ sg_dma_address(sg), -+ PAGE_ALIGN(sg_dma_len(sg))); -+ if (ret) -+ goto out_fail; -+ -+ sg_dma_address(sg) = PFN_PHYS(iova_addr); -+ -+ iova_addr += PFN_UP(sg_dma_len(sg)); -+ } -+ -+ dev_dbg(dev, "dmamap %d ents %zu pages mapped\n", nents, npages); -+ -+ return nents; -+ -+out_fail: -+ ipu7_dma_unmap_sg(sys, sglist, i, dir, attrs); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sg, "INTEL_IPU7"); -+ -+int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs) -+{ -+ int nents; -+ -+ nents = ipu7_dma_map_sg(sys, sgt->sgl, sgt->nents, dir, attrs); -+ if (nents < 0) -+ return nents; -+ -+ sgt->nents = nents; -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sgtable, "INTEL_IPU7"); -+ -+void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs) -+{ -+ ipu7_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sgtable, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-dma.h b/drivers/media/pci/intel/ipu7/ipu7-dma.h -new file mode 100644 -index 000000000000..fe789af5e664 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-dma.h -@@ -0,0 +1,46 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* Copyright (C) 2013--2025 Intel Corporation */ -+ -+#ifndef IPU7_DMA_H -+#define IPU7_DMA_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7-bus.h" -+ -+#define DMA_ATTR_RESERVE_REGION BIT(31) -+struct ipu7_mmu_info; -+ -+struct ipu7_dma_mapping { -+ struct ipu7_mmu_info *mmu_info; -+ struct iova_domain iovad; -+}; -+ -+void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle, -+ size_t size); -+void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ unsigned int nents); -+void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt); -+void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, -+ dma_addr_t *dma_handle, gfp_t gfp, -+ unsigned long attrs); -+void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr, -+ dma_addr_t dma_handle, unsigned long attrs); -+int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma, -+ void *addr, dma_addr_t iova, size_t size, -+ unsigned long attrs); -+int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs); -+void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist, -+ int nents, enum dma_data_direction dir, -+ unsigned long attrs); -+int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs); -+void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt, -+ enum dma_data_direction dir, unsigned long attrs); -+#endif /* IPU7_DMA_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -new file mode 100644 -index 000000000000..2958e39a359e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.c -@@ -0,0 +1,389 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_insys_config_abi.h" -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-isys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+ -+static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = { -+ "STREAM_OPEN", -+ "STREAM_START_AND_CAPTURE", -+ "STREAM_CAPTURE", -+ "STREAM_ABORT", -+ "STREAM_FLUSH", -+ "STREAM_CLOSE" -+}; -+ -+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, -+ void *cpu_mapped_buf, -+ dma_addr_t dma_mapped_buf, -+ size_t size, u16 send_type) -+{ -+ struct ipu7_syscom_context *ctx = isys->adev->syscom; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu7_insys_send_queue_token *token; -+ -+ if (send_type >= N_IPU_INSYS_SEND_TYPE) -+ return -EINVAL; -+ -+ dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]); -+ -+ /* -+ * Time to flush cache in case we have some payload. Not all messages -+ * have that -+ */ -+ if (cpu_mapped_buf) -+ clflush_cache_range(cpu_mapped_buf, size); -+ -+ token = ipu7_syscom_get_token(ctx, stream_handle + -+ IPU_INSYS_INPUT_MSG_QUEUE); -+ if (!token) -+ return -EBUSY; -+ -+ token->addr = dma_mapped_buf; -+ token->buf_handle = (unsigned long)cpu_mapped_buf; -+ token->send_type = send_type; -+ token->stream_id = stream_handle; -+ token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE; -+ -+ ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE); -+ /* now wakeup FW */ -+ ipu_buttress_wakeup_is_uc(isys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, u16 send_type) -+{ -+ return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0, -+ send_type); -+} -+ -+int ipu7_fw_isys_init(struct ipu7_isys *isys) -+{ -+ struct syscom_queue_config *queue_configs; -+ struct ipu7_bus_device *adev = isys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_insys_config *isys_config; -+ struct ipu7_syscom_context *syscom; -+ dma_addr_t isys_config_dma_addr; -+ unsigned int i, num_queues; -+ u32 freq; -+ u8 major; -+ int ret; -+ -+ /* Allocate and init syscom context. */ -+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -+ GFP_KERNEL); -+ if (!syscom) -+ return -ENOMEM; -+ -+ adev->syscom = syscom; -+ syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES; -+ syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES; -+ num_queues = syscom->num_input_queues + syscom->num_output_queues; -+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -+ GFP_KERNEL); -+ if (!queue_configs) { -+ ipu7_fw_isys_release(isys); -+ return -ENOMEM; -+ } -+ syscom->queue_configs = queue_configs; -+ queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity = -+ IPU_ISYS_SIZE_RECV_QUEUE; -+ queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes = -+ sizeof(struct ipu7_insys_resp); -+ queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity = -+ IPU_ISYS_SIZE_LOG_QUEUE; -+ queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes = -+ sizeof(struct ipu7_insys_resp); -+ queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0; -+ queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0; -+ -+ queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity = -+ IPU_ISYS_MAX_STREAMS; -+ queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes = -+ sizeof(struct ipu7_insys_send_queue_token); -+ -+ for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) { -+ queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE; -+ queue_configs[i].token_size_in_bytes = -+ sizeof(struct ipu7_insys_send_queue_token); -+ } -+ -+ /* Allocate ISYS subsys config. */ -+ isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config), -+ &isys_config_dma_addr, GFP_KERNEL, 0); -+ if (!isys_config) { -+ dev_err(dev, "Failed to allocate isys subsys config.\n"); -+ ipu7_fw_isys_release(isys); -+ return -ENOMEM; -+ } -+ isys->subsys_config = isys_config; -+ isys->subsys_config_dma_addr = isys_config_dma_addr; -+ memset(isys_config, 0, sizeof(struct ipu7_insys_config)); -+ isys_config->logger_config.use_source_severity = 0; -+ isys_config->logger_config.use_channels_enable_bitmask = 1; -+ isys_config->logger_config.channels_enable_bitmask = -+ LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK; -+ isys_config->logger_config.hw_printf_buffer_base_addr = 0U; -+ isys_config->logger_config.hw_printf_buffer_size_bytes = 0U; -+ isys_config->wdt_config.wdt_timer1_us = 0; -+ isys_config->wdt_config.wdt_timer2_us = 0; -+ ret = ipu_buttress_get_isys_freq(adev->isp, &freq); -+ if (ret) { -+ dev_err(dev, "Failed to get ISYS frequency.\n"); -+ ipu7_fw_isys_release(isys); -+ return ret; -+ } -+ -+ ipu7_dma_sync_single(adev, isys_config_dma_addr, -+ sizeof(struct ipu7_insys_config)); -+ -+ major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U; -+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -+ freq, isys_config_dma_addr, major); -+ if (ret) -+ ipu7_fw_isys_release(isys); -+ -+ return ret; -+} -+ -+void ipu7_fw_isys_release(struct ipu7_isys *isys) -+{ -+ struct ipu7_bus_device *adev = isys->adev; -+ -+ ipu7_boot_release_boot_config(adev); -+ if (isys->subsys_config) { -+ ipu7_dma_free(adev, -+ sizeof(struct ipu7_insys_config), -+ isys->subsys_config, -+ isys->subsys_config_dma_addr, 0); -+ isys->subsys_config = NULL; -+ isys->subsys_config_dma_addr = 0; -+ } -+} -+ -+int ipu7_fw_isys_open(struct ipu7_isys *isys) -+{ -+ return ipu7_boot_start_fw(isys->adev); -+} -+ -+int ipu7_fw_isys_close(struct ipu7_isys *isys) -+{ -+ return ipu7_boot_stop_fw(isys->adev); -+} -+ -+struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys) -+{ -+ return (struct ipu7_insys_resp *) -+ ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_MSG_QUEUE); -+} -+ -+void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) -+{ -+ ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); -+} -+ -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys) -+{ -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ void *token; -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ }; -+ -+ return 0; -+} -+ -+#endif -+void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -+ struct ipu7_insys_stream_cfg *cfg) -+{ -+ unsigned int i; -+ -+ dev_dbg(dev, "---------------------------\n"); -+ dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n"); -+ -+ dev_dbg(dev, ".port id %d\n", cfg->port_id); -+ dev_dbg(dev, ".vc %d\n", cfg->vc); -+ dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins); -+ dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins); -+ dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map); -+ -+ for (i = 0; i < cfg->nof_input_pins; i++) { -+ dev_dbg(dev, ".input_pin[%d]:\n", i); -+ dev_dbg(dev, "\t.dt = 0x%0x\n", -+ cfg->input_pins[i].dt); -+ dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n", -+ cfg->input_pins[i].disable_mipi_unpacking); -+ dev_dbg(dev, "\t.dt_rename_mode = %d\n", -+ cfg->input_pins[i].dt_rename_mode); -+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n", -+ cfg->input_pins[i].mapped_dt); -+ dev_dbg(dev, "\t.input_res = %d x %d\n", -+ cfg->input_pins[i].input_res.width, -+ cfg->input_pins[i].input_res.height); -+ dev_dbg(dev, "\t.sync_msg_map = 0x%x\n", -+ cfg->input_pins[i].sync_msg_map); -+ } -+ -+ for (i = 0; i < cfg->nof_output_pins; i++) { -+ dev_dbg(dev, ".output_pin[%d]:\n", i); -+ dev_dbg(dev, "\t.input_pin_id = %d\n", -+ cfg->output_pins[i].input_pin_id); -+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride); -+ dev_dbg(dev, "\t.send_irq = %d\n", -+ cfg->output_pins[i].send_irq); -+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft); -+ -+ dev_dbg(dev, "\t.link.buffer_lines = %d\n", -+ cfg->output_pins[i].link.buffer_lines); -+ dev_dbg(dev, "\t.link.foreign_key = %d\n", -+ cfg->output_pins[i].link.foreign_key); -+ dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n", -+ cfg->output_pins[i].link.granularity_pointer_update); -+ dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n", -+ cfg->output_pins[i].link.msg_link_streaming_mode); -+ dev_dbg(dev, "\t.link.pbk_id = %d\n", -+ cfg->output_pins[i].link.pbk_id); -+ dev_dbg(dev, "\t.link.pbk_slot_id = %d\n", -+ cfg->output_pins[i].link.pbk_slot_id); -+ dev_dbg(dev, "\t.link.dest = %d\n", -+ cfg->output_pins[i].link.dest); -+ dev_dbg(dev, "\t.link.use_sw_managed = %d\n", -+ cfg->output_pins[i].link.use_sw_managed); -+ dev_dbg(dev, "\t.link.is_snoop = %d\n", -+ cfg->output_pins[i].link.is_snoop); -+ -+ dev_dbg(dev, "\t.crop.line_top = %d\n", -+ cfg->output_pins[i].crop.line_top); -+ dev_dbg(dev, "\t.crop.line_bottom = %d\n", -+ cfg->output_pins[i].crop.line_bottom); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.crop.column_left = %d\n", -+ cfg->output_pins[i].crop.column_left); -+ dev_dbg(dev, "\t.crop.colunm_right = %d\n", -+ cfg->output_pins[i].crop.column_right); -+#endif -+ -+ dev_dbg(dev, "\t.dpcm_enable = %d\n", -+ cfg->output_pins[i].dpcm.enable); -+ dev_dbg(dev, "\t.dpcm.type = %d\n", -+ cfg->output_pins[i].dpcm.type); -+ dev_dbg(dev, "\t.dpcm.predictor = %d\n", -+ cfg->output_pins[i].dpcm.predictor); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.upipe_enable = %d\n", -+ cfg->output_pins[i].upipe_enable); -+ dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); -+ dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); -+ dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); -+#endif -+ } -+ dev_dbg(dev, "---------------------------\n"); -+} -+ -+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -+ struct ipu7_insys_buffset *buf, -+ unsigned int outputs) -+{ -+ unsigned int i; -+ -+ dev_dbg(dev, "--------------------------\n"); -+ dev_dbg(dev, "IPU_ISYS_BUFF_SET\n"); -+ dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map); -+ dev_dbg(dev, ".frame_id = %d\n", buf->frame_id); -+ dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame); -+ -+ for (i = 0; i < outputs; i++) { -+ dev_dbg(dev, ".output_pin[%d]:\n", i); -+#ifndef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.user_token = %llx\n", -+ buf->output_pins[i].user_token); -+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); -+#else -+ dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", -+ buf->output_pins[i].pin_payload.user_token); -+ dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", -+ buf->output_pins[i].pin_payload.addr); -+ dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", -+ buf->output_pins[i].upipe_capture_cfg); -+#endif -+ } -+ dev_dbg(dev, "---------------------------\n"); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -new file mode 100644 -index 000000000000..1235adc9694e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-fw-isys.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_ISYS_H -+#define IPU7_FW_ISYS_H -+ -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+struct device; -+struct ipu7_insys_buffset; -+struct ipu7_insys_stream_cfg; -+struct ipu7_isys; -+ -+/* From here on type defines not coming from the ISYSAPI interface */ -+ -+int ipu7_fw_isys_init(struct ipu7_isys *isys); -+void ipu7_fw_isys_release(struct ipu7_isys *isys); -+int ipu7_fw_isys_open(struct ipu7_isys *isys); -+int ipu7_fw_isys_close(struct ipu7_isys *isys); -+ -+void ipu7_fw_isys_dump_stream_cfg(struct device *dev, -+ struct ipu7_insys_stream_cfg *cfg); -+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, -+ struct ipu7_insys_buffset *buf, -+ unsigned int outputs); -+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, u16 send_type); -+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, -+ const unsigned int stream_handle, -+ void *cpu_mapped_buf, -+ dma_addr_t dma_mapped_buf, -+ size_t size, u16 send_type); -+struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); -+void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys); -+#endif -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -new file mode 100644 -index 000000000000..b8c5db7ae300 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.c -@@ -0,0 +1,1034 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-isys-csi-phy.h" -+ -+#define PORT_A 0U -+#define PORT_B 1U -+#define PORT_C 2U -+#define PORT_D 3U -+ -+#define N_DATA_IDS 8U -+static DECLARE_BITMAP(data_ids, N_DATA_IDS); -+ -+struct ddlcal_counter_ref_s { -+ u16 min_mbps; -+ u16 max_mbps; -+ -+ u16 ddlcal_counter_ref; -+}; -+ -+struct ddlcal_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 oa_lanex_hsrx_cdphy_sel_fast; -+ u16 ddlcal_max_phase; -+ u16 phase_bound; -+ u16 ddlcal_dll_fbk; -+ u16 ddlcal_ddl_coarse_bank; -+ u16 fjump_deskew; -+ u16 min_eye_opening_deskew; -+}; -+ -+struct i_thssettle_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 i_thssettle; -+}; -+ -+ /* lane2 for 4l3t, lane1 for 2l2t */ -+struct oa_lane_clk_div_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 oa_lane_hsrx_hs_clk_div; -+}; -+ -+struct cdr_fbk_cap_prog_params { -+ u16 min_mbps; -+ u16 max_mbps; -+ u16 val; -+}; -+ -+static const struct ddlcal_counter_ref_s table0[] = { -+ { 1500, 1999, 118 }, -+ { 2000, 2499, 157 }, -+ { 2500, 3499, 196 }, -+ { 3500, 4499, 274 }, -+ { 4500, 4500, 352 }, -+ { } -+}; -+ -+static const struct ddlcal_params table1[] = { -+ { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 }, -+ { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 }, -+ { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 }, -+ { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 }, -+ { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 }, -+ { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 }, -+ { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 }, -+ { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 }, -+ { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 }, -+ { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 }, -+ { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 }, -+ { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 }, -+ { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 }, -+ { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 }, -+ { } -+}; -+ -+static const struct i_thssettle_params table2[] = { -+ { 80, 124, 24 }, -+ { 125, 249, 20 }, -+ { 250, 499, 16 }, -+ { 500, 749, 14 }, -+ { 750, 1499, 13 }, -+ { 1500, 4500, 12 }, -+ { } -+}; -+ -+static const struct oa_lane_clk_div_params table6[] = { -+ { 80, 159, 0x1 }, -+ { 160, 319, 0x2 }, -+ { 320, 639, 0x3 }, -+ { 640, 1279, 0x4 }, -+ { 1280, 2560, 0x5 }, -+ { 2561, 4500, 0x6 }, -+ { } -+}; -+ -+static const struct cdr_fbk_cap_prog_params table7[] = { -+ { 80, 919, 0 }, -+ { 920, 1029, 1 }, -+ { 1030, 1169, 2 }, -+ { 1170, 1349, 3 }, -+ { 1350, 1589, 4 }, -+ { 1590, 1949, 5 }, -+ { 1950, 2499, 6 }, -+ { } -+}; -+ -+static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -+ -+ dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x", -+ base + addr - isys_base, data); -+ writew(data, base + addr); -+} -+ -+static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id); -+ u16 data; -+ -+ data = readw(base + addr); -+ dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x", -+ base + addr - isys_base, data); -+ -+ return data; -+} -+ -+static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -+ struct device *dev = &isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, data); -+ writel(data, base + addr); -+ dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, readl(base + addr)); -+} -+ -+static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ u32 gpreg = isys->pdata->ipdata->csi2.gpreg; -+ void __iomem *base = isys_base + gpreg + 0x1000 * id; -+ struct device *dev = &isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, data); -+ writel(data, base + addr); -+ dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x", -+ base + addr - isys_base, readl(base + addr)); -+} -+ -+static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id); -+ u32 data; -+ -+ data = readl(base + addr); -+ dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x", -+ base + addr - isys_base, data); -+ -+ return data; -+} -+ -+static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -+ u16 val, u8 lo, u8 hi) -+{ -+ u32 temp, mask; -+ -+ WARN_ON(lo > hi); -+ WARN_ON(hi > 15); -+ -+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -+ temp = dwc_phy_read(isys, id, addr); -+ temp &= ~mask; -+ temp |= (val << lo) & mask; -+ dwc_phy_write(isys, id, addr, temp); -+} -+ -+static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr, -+ u32 val, u8 hi, u8 lo) -+{ -+ u32 temp, mask; -+ -+ WARN_ON(lo > hi); -+ -+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi)); -+ temp = dwc_csi_read(isys, id, addr); -+ temp &= ~mask; -+ temp |= (val << lo) & mask; -+ dwc_csi_write(isys, id, addr, temp); -+} -+ -+static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 id, lanes, phy_mode; -+ u32 val; -+ -+ id = csi2->port; -+ lanes = csi2->nlanes; -+ phy_mode = csi2->phy_mode; -+ dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u", -+ id, lanes, phy_mode); -+ -+ val = dwc_csi_read(isys, id, VERSION); -+ dev_dbg(dev, "csi-%d controller version = 0x%x", id, val); -+ -+ /* num of active data lanes */ -+ dwc_csi_write(isys, id, N_LANES, lanes - 1); -+ dwc_csi_write(isys, id, CDPHY_MODE, phy_mode); -+ dwc_csi_write(isys, id, VC_EXTENSION, 0); -+ -+ /* only mask PHY_FATAL and PKT_FATAL interrupts */ -+ dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff); -+ dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3); -+ dwc_csi_write(isys, id, INT_MSK_PHY, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_LINE, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0); -+ dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0); -+} -+ -+static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id) -+{ -+ dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0); -+ dwc_csi_write(isys, id, DPHY_RSTZ, 0); -+ dwc_csi_write(isys, id, CSI2_RESETN, 0); -+ gpreg_write(isys, id, PHY_RESET, 0); -+ gpreg_write(isys, id, PHY_SHUTDOWN, 0); -+} -+ -+/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */ -+static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ u32 reg, n; -+ u8 lo, hi; -+ int ret; -+ -+ dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n", -+ id, vc, dt); -+ -+ dwc_csi_write(isys, id, VC_EXTENSION, 0x0); -+ n = find_first_zero_bit(data_ids, N_DATA_IDS); -+ if (n == N_DATA_IDS) -+ return -ENOSPC; -+ -+ ret = test_and_set_bit(n, data_ids); -+ if (ret) -+ return -EBUSY; -+ -+ reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2; -+ lo = (n % 4) * 8; -+ hi = lo + 4; -+ dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo); -+ -+ reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2; -+ lo = (n % 4) * 8; -+ hi = lo + 5; -+ dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo); -+ -+ return 0; -+} -+ -+static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id) -+{ -+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct v4l2_mbus_frame_desc desc; -+ struct v4l2_subdev *ext_sd; -+ struct media_pad *pad; -+ unsigned int i; -+ int ret; -+ -+ pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -+ if (IS_ERR(pad)) { -+ dev_warn(dev, "can't get remote source pad of %s (%ld)\n", -+ csi2->asd.sd.name, PTR_ERR(pad)); -+ return PTR_ERR(pad); -+ } -+ -+ ext_sd = media_entity_to_v4l2_subdev(pad->entity); -+ if (WARN(!ext_sd, "Failed to get subdev for entity %s\n", -+ pad->entity->name)) -+ return -ENODEV; -+ -+ ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc); -+ if (ret) -+ return ret; -+ -+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -+ dev_warn(dev, "Unsupported frame descriptor type\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ desc_entry = &desc.entry[i]; -+ if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) { -+ ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc, -+ desc_entry->bus.csi2.dt); -+ if (ret) -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+#define CDPHY_TIMEOUT 5000000U -+static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id) -+{ -+ void __iomem *isys_base = isys->pdata->base; -+ u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg; -+ void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ u32 phy_ready; -+ u32 reg, rext; -+ int ret; -+ -+ dev_dbg(dev, "waiting phy ready...\n"); -+ ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready, -+ phy_ready & BIT(0) && phy_ready != ~0U, -+ 100, CDPHY_TIMEOUT); -+ dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY)); -+ dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id, -+ dwc_csi_read(isys, id, PHY_RX)); -+ dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id, -+ dwc_csi_read(isys, id, PHY_STOPSTATE)); -+ dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id, -+ dwc_csi_read(isys, id, PHY_CAL)); -+ for (i = 0; i < 4U; i++) { -+ reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U); -+ dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n", -+ id, i, dwc_phy_read(isys, id, reg)); -+ } -+ dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5)); -+ dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0)); -+ dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB)); -+ dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB)); -+ dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id, -+ dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT)); -+ -+ if (!ret) { -+ if (id) { -+ dev_dbg(dev, "ignore phy %u rext\n", id); -+ return 0; -+ } -+ -+ rext = dwc_phy_read(isys, id, -+ CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU; -+ dev_dbg(dev, "phy %u rext value = %u\n", id, rext); -+ isys->phy_rext_cal = (rext ? rext : 5); -+ -+ return 0; -+ } -+ -+ dev_err(dev, "wait phy ready timeout!\n"); -+ -+ return ret; -+} -+ -+static int lookup_table1(u64 mbps) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(table1); i++) { -+ if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps) -+ return i; -+ } -+ -+ return -ENXIO; -+} -+ -+static const u16 deskew_fine_mem[] = { -+ 0x0404, 0x040c, 0x0414, 0x041c, -+ 0x0423, 0x0429, 0x0430, 0x043a, -+ 0x0445, 0x044a, 0x0450, 0x045a, -+ 0x0465, 0x0469, 0x0472, 0x047a, -+ 0x0485, 0x0489, 0x0490, 0x049a, -+ 0x04a4, 0x04ac, 0x04b4, 0x04bc, -+ 0x04c4, 0x04cc, 0x04d4, 0x04dc, -+ 0x04e4, 0x04ec, 0x04f4, 0x04fc, -+ 0x0504, 0x050c, 0x0514, 0x051c, -+ 0x0523, 0x0529, 0x0530, 0x053a, -+ 0x0545, 0x054a, 0x0550, 0x055a, -+ 0x0565, 0x0569, 0x0572, 0x057a, -+ 0x0585, 0x0589, 0x0590, 0x059a, -+ 0x05a4, 0x05ac, 0x05b4, 0x05bc, -+ 0x05c4, 0x05cc, 0x05d4, 0x05dc, -+ 0x05e4, 0x05ec, 0x05f4, 0x05fc, -+ 0x0604, 0x060c, 0x0614, 0x061c, -+ 0x0623, 0x0629, 0x0632, 0x063a, -+ 0x0645, 0x064a, 0x0650, 0x065a, -+ 0x0665, 0x0669, 0x0672, 0x067a, -+ 0x0685, 0x0689, 0x0690, 0x069a, -+ 0x06a4, 0x06ac, 0x06b4, 0x06bc, -+ 0x06c4, 0x06cc, 0x06d4, 0x06dc, -+ 0x06e4, 0x06ec, 0x06f4, 0x06fc, -+ 0x0704, 0x070c, 0x0714, 0x071c, -+ 0x0723, 0x072a, 0x0730, 0x073a, -+ 0x0745, 0x074a, 0x0750, 0x075a, -+ 0x0765, 0x0769, 0x0772, 0x077a, -+ 0x0785, 0x0789, 0x0790, 0x079a, -+ 0x07a4, 0x07ac, 0x07b4, 0x07bc, -+ 0x07c4, 0x07cc, 0x07d4, 0x07dc, -+ 0x07e4, 0x07ec, 0x07f4, 0x07fc, -+}; -+ -+static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -+ bool aggregation, u64 mbps) -+{ -+ u16 hsrxval0 = 0; -+ u16 hsrxval1 = 0; -+ u16 hsrxval2 = 0; -+ int index; -+ u16 reg; -+ u16 val; -+ u32 i; -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9); -+ if (mbps > 1500) -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -+ 40, 0, 7); -+ else -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, -+ 104, 0, 7); -+ -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6); -+ -+ for (i = 0; i < ARRAY_SIZE(table0); i++) { -+ if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) { -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3, -+ table0[i].ddlcal_counter_ref, -+ 0, 9); -+ break; -+ } -+ } -+ -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, -+ table1[index].phase_bound, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -+ table1[index].ddlcal_dll_fbk, 4, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5, -+ table1[index].ddlcal_ddl_coarse_bank, 0, 3); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8; -+ val = table1[index].oa_lanex_hsrx_cdphy_sel_fast; -+ for (i = 0; i < lanes + 1; i++) -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -+ 12, 12); -+ } -+ -+ reg = CORE_DIG_DLANE_0_RW_LP_0; -+ for (i = 0; i < lanes; i++) -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, -+ 0, 0, 0); -+ if (!is_ipu7(isys->adev->isp->hw_ver) || -+ id == PORT_B || id == PORT_C) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -+ 1, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -+ 0, 0, 0); -+ } else { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, -+ 0, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, -+ 1, 0, 0); -+ } -+ -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -+ 0, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -+ 0, 0, 0); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12; -+ val = (mbps > 1500) ? 0 : 1; -+ for (i = 0; i < lanes + 1; i++) { -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3); -+ } -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13; -+ val = (mbps > 1500) ? 0 : 1; -+ for (i = 0; i < lanes + 1; i++) { -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3); -+ } -+ -+ if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C) -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9; -+ else -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9; -+ -+ for (i = 0; i < ARRAY_SIZE(table6); i++) { -+ if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) { -+ dwc_phy_write_mask(isys, id, reg, -+ table6[i].oa_lane_hsrx_hs_clk_div, -+ 5, 7); -+ break; -+ } -+ } -+ -+ if (aggregation) { -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1, -+ 1, 1); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15; -+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -+ -+ val = (id == PORT_A) ? 3 : 0; -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15; -+ dwc_phy_write_mask(isys, id, reg, val, 3, 4); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15; -+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7); -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_0; -+ for (i = 0; i < ARRAY_SIZE(table2); i++) { -+ if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) { -+ u8 j; -+ -+ for (j = 0; j < lanes; j++) -+ dwc_phy_write_mask(isys, id, reg + (j * 0x400), -+ table2[i].i_thssettle, -+ 8, 15); -+ break; -+ } -+ } -+ -+ /* deskew */ -+ for (i = 0; i < lanes; i++) { -+ reg = CORE_DIG_DLANE_0_RW_CFG_1; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), -+ ((mbps > 1500) ? 0x1 : 0x2), 2, 3); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_2; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), -+ ((mbps > 2500) ? 0 : 1), 15, 15); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12); -+ -+ reg = CORE_DIG_DLANE_0_RW_LP_0; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_LP_2; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_1; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_3; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2); -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ val = table1[index].fjump_deskew; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -+ 3, 8); -+ } -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_4; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_5; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_6; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7); -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ val = table1[index].min_eye_opening_deskew; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, -+ 8, 15); -+ } -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_7; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15); -+ -+ reg = CORE_DIG_DLANE_0_RW_HS_RX_9; -+ index = lookup_table1(mbps); -+ if (index >= 0) { -+ val = table1[index].ddlcal_max_phase; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), -+ val, 0, 7); -+ } -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15); -+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0); -+ -+ for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++) -+ dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM, -+ deskew_fine_mem[i], 0, 15); -+ -+ if (mbps > 1500) { -+ hsrxval0 = 4; -+ hsrxval2 = 3; -+ } -+ -+ if (mbps > 2500) -+ hsrxval1 = 2; -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -+ hsrxval0, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -+ hsrxval0, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -+ hsrxval0, 0, 2); -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -+ hsrxval0, 0, 2); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -+ hsrxval0, 0, 2); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9, -+ hsrxval1, 3, 4); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9, -+ hsrxval1, 3, 4); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9, -+ hsrxval1, 3, 4); -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9, -+ hsrxval1, 3, 4); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9, -+ hsrxval1, 3, 4); -+ } -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15, -+ hsrxval2, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15, -+ hsrxval2, 0, 2); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15, -+ hsrxval2, 0, 2); -+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15, -+ hsrxval2, 0, 2); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15, -+ hsrxval2, 0, 2); -+ } -+ -+ /* force and override rext */ -+ if (isys->phy_rext_cal && id) { -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8, -+ isys->phy_rext_cal, 0, 3); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -+ 1, 11, 11); -+ } -+} -+ -+static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -+ bool aggregation, u64 mbps) -+{ -+ u8 trios = 2; -+ u16 coarse_target; -+ u16 deass_thresh; -+ u16 delay_thresh; -+ u16 reset_thresh; -+ u16 cap_prog = 6U; -+ u16 reg; -+ u16 val; -+ u32 i; -+ u64 r64; -+ u32 r; -+ -+ if (is_ipu7p5(isys->adev->isp->hw_ver)) -+ val = 0x15; -+ else -+ val = 0x155; -+ -+ if (is_ipu7(isys->adev->isp->hw_ver)) -+ trios = 3; -+ -+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7); -+ -+ reg = CORE_DIG_CLANE_0_RW_LP_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11); -+ -+ val = (mbps > 900U) ? 1U : 0U; -+ for (i = 0; i < trios; i++) { -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0); -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_1; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_5; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_6; -+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15); -+ } -+ -+ /* -+ * Below 900Msps, always use the same value. -+ * The formula is suitable for data rate 80-3500Msps. -+ * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5 -+ */ -+ if (mbps >= 80U) -+ coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1; -+ else -+ coarse_target = 56; -+ -+ for (i = 0; i < trios; i++) { -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400; -+ dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15); -+ } -+ -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0); -+ -+ if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) { -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2, -+ 1, 0, 0); -+ dwc_phy_write_mask(isys, id, -+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2, -+ 0, 0, 0); -+ } -+ -+ for (i = 0; i < trios; i++) { -+ reg = CORE_DIG_RW_TRIO0_0 + i * 0x400; -+ dwc_phy_write_mask(isys, id, reg, 1, 6, 8); -+ dwc_phy_write_mask(isys, id, reg, 1, 3, 5); -+ dwc_phy_write_mask(isys, id, reg, 2, 0, 2); -+ } -+ -+ deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1; -+ if (r64 != 0) -+ deass_thresh++; -+ -+ reg = CORE_DIG_RW_TRIO0_2; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, -+ deass_thresh, 0, 7); -+ -+ delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u; -+ -+ if (delay_thresh < 1) -+ delay_thresh = 1; -+ -+ reg = CORE_DIG_RW_TRIO0_1; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, -+ delay_thresh, 0, 15); -+ -+ reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r); -+ if (!r) -+ reset_thresh--; -+ -+ if (reset_thresh < 1) -+ reset_thresh = 1; -+ -+ reg = CORE_DIG_RW_TRIO0_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, -+ reset_thresh, 9, 11); -+ -+ reg = CORE_DIG_CLANE_0_RW_LP_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); -+ -+ reg = CORE_DIG_CLANE_0_RW_LP_2; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0); -+ -+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0; -+ for (i = 0; i < trios; i++) -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6); -+ -+ for (i = 0; i < ARRAY_SIZE(table7); i++) { -+ if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) { -+ cap_prog = table7[i].val; -+ break; -+ } -+ } -+ -+ for (i = 0; i < (lanes + 1); i++) { -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; -+ dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); -+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); -+ -+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; -+ dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); -+ } -+} -+ -+static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, -+ bool aggregation) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 phy_mode; -+ s64 link_freq; -+ u64 mbps; -+ -+ if (aggregation) -+ link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]); -+ else -+ link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]); -+ -+ if (link_freq < 0) { -+ dev_err(dev, "get link freq failed (%lld)\n", link_freq); -+ return link_freq; -+ } -+ -+ mbps = div_u64(link_freq, 500000); -+ dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n", -+ id, lanes, aggregation, mbps); -+ -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7); -+ dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2, -+ 1, 12, 13); -+ dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0, -+ 63, 2, 7); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1, -+ 563, 0, 11); -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7); -+ /* bypass the RCAL state (bit6) */ -+ if (aggregation && id != PORT_A) -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45, -+ 0, 7); -+ -+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7); -+ dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8); -+ dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6); -+ dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1); -+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1); -+ dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -+ 0, 10, 10); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -+ 1, 10, 10); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1, -+ 0, 15, 15); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3, -+ 3, 8, 9); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0, -+ 0, 15, 15); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6, -+ 7, 12, 14); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, -+ 0, 8, 10); -+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, -+ 0, 8, 8); -+ -+ if (aggregation) -+ phy_mode = isys->csi2[0].phy_mode; -+ else -+ phy_mode = isys->csi2[id].phy_mode; -+ -+ if (phy_mode == PHY_MODE_DPHY) { -+ ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps); -+ } else if (phy_mode == PHY_MODE_CPHY) { -+ ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps); -+ } else { -+ dev_err(dev, "unsupported phy mode %d!\n", -+ isys->csi2[id].phy_mode); -+ } -+ -+ return 0; -+} -+ -+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ u32 lanes = csi2->nlanes; -+ bool aggregation = false; -+ u32 id = csi2->port; -+ int ret; -+ -+ /* lanes remapping for aggregation (port AB) mode */ -+ if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) { -+ aggregation = true; -+ lanes = 2; -+ } -+ -+ ipu7_isys_csi_phy_reset(isys, id); -+ gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1); -+ gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -+ gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U); -+ gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf); -+ gpreg_write(isys, id, PHY_MODE, csi2->phy_mode); -+ -+ /* config PORT_B if aggregation mode */ -+ if (aggregation) { -+ ipu7_isys_csi_phy_reset(isys, PORT_B); -+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0); -+ gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3); -+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2); -+ gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf); -+ gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode); -+ } -+ -+ ipu7_isys_csi_ctrl_cfg(csi2); -+ ipu7_isys_csi_ctrl_dids_config(csi2, id); -+ -+ ret = ipu7_isys_phy_config(isys, id, lanes, aggregation); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, id, PHY_RESET, 1); -+ gpreg_write(isys, id, PHY_SHUTDOWN, 1); -+ dwc_csi_write(isys, id, DPHY_RSTZ, 1); -+ dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1); -+ dwc_csi_write(isys, id, CSI2_RESETN, 1); -+ -+ ret = ipu7_isys_phy_ready(isys, id); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0); -+ gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0); -+ -+ /* config PORT_B if aggregation mode */ -+ if (aggregation) { -+ ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, PORT_B, PHY_RESET, 1); -+ gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1); -+ dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1); -+ dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1); -+ dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1); -+ ret = ipu7_isys_phy_ready(isys, PORT_B); -+ if (ret < 0) -+ return ret; -+ -+ gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0); -+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0); -+ } -+ -+ return 0; -+} -+ -+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ -+ ipu7_isys_csi_phy_reset(isys, csi2->port); -+ if (!is_ipu7(isys->adev->isp->hw_ver) && -+ csi2->nlanes > 2U && csi2->port == PORT_A) -+ ipu7_isys_csi_phy_reset(isys, PORT_B); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -new file mode 100644 -index 000000000000..dfdcb61540c4 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi-phy.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_CSI_PHY_H -+#define IPU7_ISYS_CSI_PHY_H -+ -+struct ipu7_isys; -+ -+#define PHY_MODE_DPHY 0U -+#define PHY_MODE_CPHY 1U -+ -+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2); -+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -new file mode 100644 -index 000000000000..aad52c44a005 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2-regs.h -@@ -0,0 +1,1197 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2020 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_CSI2_REG_H -+#define IPU7_ISYS_CSI2_REG_H -+ -+/* IS main regs base */ -+#define IS_MAIN_BASE 0x240000 -+#define IS_MAIN_S2B_BASE (IS_MAIN_BASE + 0x22000) -+#define IS_MAIN_B2O_BASE (IS_MAIN_BASE + 0x26000) -+#define IS_MAIN_ISD_M0_BASE (IS_MAIN_BASE + 0x2b000) -+#define IS_MAIN_ISD_M1_BASE (IS_MAIN_BASE + 0x2b100) -+#define IS_MAIN_ISD_INT_BASE (IS_MAIN_BASE + 0x2b200) -+#define IS_MAIN_GDA_BASE (IS_MAIN_BASE + 0x32000) -+#define IS_MAIN_GPREGS_MAIN_BASE (IS_MAIN_BASE + 0x32500) -+#define IS_MAIN_IRQ_CTRL_BASE (IS_MAIN_BASE + 0x32700) -+#define IS_MAIN_PWM_CTRL_BASE (IS_MAIN_BASE + 0x32b00) -+ -+#define S2B_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_S2B_BASE + 0x1c) -+#define S2B_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_S2B_BASE + 0x20) -+#define S2B_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_S2B_BASE + 0x24) -+#define S2B_IID_IRQ_CTL_STATUS(iid) (IS_MAIN_S2B_BASE + 0x94 + \ -+ 0x100 * (iid)) -+ -+#define B2O_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_B2O_BASE + 0x30) -+#define B2O_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_B2O_BASE + 0x34) -+#define B2O_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_B2O_BASE + 0x38) -+#define B2O_IID_IRQ_CTL_STATUS(oid) (IS_MAIN_B2O_BASE + 0x3dc + \ -+ 0x200 * (oid)) -+ -+#define ISD_M0_IRQ_CTL_STATUS (IS_MAIN_ISD_M0_BASE + 0x1c) -+#define ISD_M0_IRQ_CTL_CLEAR (IS_MAIN_ISD_M0_BASE + 0x20) -+#define ISD_M0_IRQ_CTL_ENABLE (IS_MAIN_ISD_M0_BASE + 0x24) -+ -+#define ISD_M1_IRQ_CTL_STATUS (IS_MAIN_ISD_M1_BASE + 0x1c) -+#define ISD_M1_IRQ_CTL_CLEAR (IS_MAIN_ISD_M1_BASE + 0x20) -+#define ISD_M1_IRQ_CTL_ENABLE (IS_MAIN_ISD_M1_BASE + 0x24) -+ -+#define ISD_INT_IRQ_CTL_STATUS (IS_MAIN_ISD_INT_BASE + 0x1c) -+#define ISD_INT_IRQ_CTL_CLEAR (IS_MAIN_ISD_INT_BASE + 0x20) -+#define ISD_INT_IRQ_CTL_ENABLE (IS_MAIN_ISD_INT_BASE + 0x24) -+ -+#define GDA_IRQ_CTL_STATUS (IS_MAIN_GDA_BASE + 0x1c) -+#define GDA_IRQ_CTL_CLEAR (IS_MAIN_GDA_BASE + 0x20) -+#define GDA_IRQ_CTL_ENABLE (IS_MAIN_GDA_BASE + 0x24) -+ -+#define IS_MAIN_IRQ_CTL_EDGE IS_MAIN_IRQ_CTRL_BASE -+#define IS_MAIN_IRQ_CTL_MASK (IS_MAIN_IRQ_CTRL_BASE + 0x4) -+#define IS_MAIN_IRQ_CTL_STATUS (IS_MAIN_IRQ_CTRL_BASE + 0x8) -+#define IS_MAIN_IRQ_CTL_CLEAR (IS_MAIN_IRQ_CTRL_BASE + 0xc) -+#define IS_MAIN_IRQ_CTL_ENABLE (IS_MAIN_IRQ_CTRL_BASE + 0x10) -+#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IS_MAIN_IRQ_CTRL_BASE + 0x14) -+ -+/* IS IO regs base */ -+#define IS_PHY_NUM 4U -+#define IS_IO_BASE 0x280000 -+ -+/* dwc csi cdphy registers */ -+#define IS_IO_CDPHY_BASE(i) (IS_IO_BASE + 0x10000 * (i)) -+#define PPI_STARTUP_RW_COMMON_DPHY_0 0x1800 -+#define PPI_STARTUP_RW_COMMON_DPHY_1 0x1802 -+#define PPI_STARTUP_RW_COMMON_DPHY_2 0x1804 -+#define PPI_STARTUP_RW_COMMON_DPHY_3 0x1806 -+#define PPI_STARTUP_RW_COMMON_DPHY_4 0x1808 -+#define PPI_STARTUP_RW_COMMON_DPHY_5 0x180a -+#define PPI_STARTUP_RW_COMMON_DPHY_6 0x180c -+#define PPI_STARTUP_RW_COMMON_DPHY_7 0x180e -+#define PPI_STARTUP_RW_COMMON_DPHY_8 0x1810 -+#define PPI_STARTUP_RW_COMMON_DPHY_9 0x1812 -+#define PPI_STARTUP_RW_COMMON_DPHY_A 0x1814 -+#define PPI_STARTUP_RW_COMMON_DPHY_10 0x1820 -+#define PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822 -+#define PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824 -+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848 -+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a -+#define PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c -+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e -+#define PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850 -+#define PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852 -+#define PPI_CALIBCTRL_R_ADC_DEBUG 0x1854 -+#define PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00 -+#define PPI_RW_LPDCOCAL_TIMEBASE 0x1c02 -+#define PPI_RW_LPDCOCAL_NREF 0x1c04 -+#define PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06 -+#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08 -+#define PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a -+#define PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c -+#define PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e -+#define PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16 -+#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18 -+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a -+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c -+#define PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e -+#define PPI_RW_LB_TIMEBASE_CONFIG 0x1c20 -+#define PPI_RW_LB_STARTCMU_CONFIG 0x1c22 -+#define PPI_R_LBPULSE_COUNTER_RB 0x1c24 -+#define PPI_R_LB_START_CMU_RB 0x1c26 -+#define PPI_RW_LB_DPHY_BURST_START 0x1c28 -+#define PPI_RW_LB_CPHY_BURST_START 0x1c2a -+#define PPI_RW_DDLCAL_CFG_0 0x1c40 -+#define PPI_RW_DDLCAL_CFG_1 0x1c42 -+#define PPI_RW_DDLCAL_CFG_2 0x1c44 -+#define PPI_RW_DDLCAL_CFG_3 0x1c46 -+#define PPI_RW_DDLCAL_CFG_4 0x1c48 -+#define PPI_RW_DDLCAL_CFG_5 0x1c4a -+#define PPI_RW_DDLCAL_CFG_6 0x1c4c -+#define PPI_RW_DDLCAL_CFG_7 0x1c4e -+#define PPI_R_DDLCAL_DEBUG_0 0x1c50 -+#define PPI_R_DDLCAL_DEBUG_1 0x1c52 -+#define PPI_RW_PARITY_TEST 0x1c60 -+#define PPI_RW_STARTUP_OVR_0 0x1c62 -+#define PPI_RW_STARTUP_STATE_OVR_1 0x1c64 -+#define PPI_RW_DTB_SELECTOR 0x1c66 -+#define PPI_RW_DPHY_CLK_SPARE 0x1c6a -+#define PPI_RW_COMMON_CFG 0x1c6c -+#define PPI_RW_TERMCAL_CFG_0 0x1c80 -+#define PPI_R_TERMCAL_DEBUG_0 0x1c82 -+#define PPI_RW_TERMCAL_CTRL_0 0x1c84 -+#define PPI_RW_OFFSETCAL_CFG_0 0x1ca0 -+#define PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2 -+#define PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4 -+#define PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6 -+#define PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8 -+#define PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa -+#define PPI_RW_HSDCOCAL_CFG_O 0x1d00 -+#define PPI_RW_HSDCOCAL_CFG_1 0x1d02 -+#define PPI_RW_HSDCOCAL_CFG_2 0x1d04 -+#define PPI_RW_HSDCOCAL_CFG_3 0x1d06 -+#define PPI_RW_HSDCOCAL_CFG_4 0x1d08 -+#define PPI_RW_HSDCOCAL_CFG_5 0x1d0a -+#define PPI_RW_HSDCOCAL_CFG_6 0x1d0c -+#define PPI_RW_HSDCOCAL_CFG_7 0x1d0e -+#define PPI_RW_HSDCOCAL_CFG_8 0x1d10 -+#define PPI_R_HSDCOCAL_DEBUG_RB 0x1d12 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086 -+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8 -+#define CORE_DIG_RW_TRIO0_0 0x2100 -+#define CORE_DIG_RW_TRIO0_1 0x2102 -+#define CORE_DIG_RW_TRIO0_2 0x2104 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486 -+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8 -+#define CORE_DIG_RW_TRIO1_0 0x2500 -+#define CORE_DIG_RW_TRIO1_1 0x2502 -+#define CORE_DIG_RW_TRIO1_2 0x2504 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886 -+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4 -+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6 -+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8 -+#define CORE_DIG_RW_TRIO2_0 0x2900 -+#define CORE_DIG_RW_TRIO2_1 0x2902 -+#define CORE_DIG_RW_TRIO2_2 0x2904 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86 -+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068 -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c -+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086 -+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400 -+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402 -+#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812 -+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826 -+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854 -+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856 -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858 -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e -+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860 -+#define CORE_DIG_RW_COMMON_0 0x3880 -+#define CORE_DIG_RW_COMMON_1 0x3882 -+#define CORE_DIG_RW_COMMON_2 0x3884 -+#define CORE_DIG_RW_COMMON_3 0x3886 -+#define CORE_DIG_RW_COMMON_4 0x3888 -+#define CORE_DIG_RW_COMMON_5 0x388a -+#define CORE_DIG_RW_COMMON_6 0x388c -+#define CORE_DIG_RW_COMMON_7 0x388e -+#define CORE_DIG_RW_COMMON_8 0x3890 -+#define CORE_DIG_RW_COMMON_9 0x3892 -+#define CORE_DIG_RW_COMMON_10 0x3894 -+#define CORE_DIG_RW_COMMON_11 0x3896 -+#define CORE_DIG_RW_COMMON_12 0x3898 -+#define CORE_DIG_RW_COMMON_13 0x389a -+#define CORE_DIG_RW_COMMON_14 0x389c -+#define CORE_DIG_RW_COMMON_15 0x389e -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0 -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2 -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4 -+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6 -+#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0 -+#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2 -+#define PPI_RW_DPHY_LANE0_LBERT_0 0x4000 -+#define PPI_RW_DPHY_LANE0_LBERT_1 0x4002 -+#define PPI_R_DPHY_LANE0_LBERT_0 0x4004 -+#define PPI_R_DPHY_LANE0_LBERT_1 0x4006 -+#define PPI_RW_DPHY_LANE0_SPARE 0x4008 -+#define PPI_RW_DPHY_LANE1_LBERT_0 0x4400 -+#define PPI_RW_DPHY_LANE1_LBERT_1 0x4402 -+#define PPI_R_DPHY_LANE1_LBERT_0 0x4404 -+#define PPI_R_DPHY_LANE1_LBERT_1 0x4406 -+#define PPI_RW_DPHY_LANE1_SPARE 0x4408 -+#define PPI_RW_DPHY_LANE2_LBERT_0 0x4800 -+#define PPI_RW_DPHY_LANE2_LBERT_1 0x4802 -+#define PPI_R_DPHY_LANE2_LBERT_0 0x4804 -+#define PPI_R_DPHY_LANE2_LBERT_1 0x4806 -+#define PPI_RW_DPHY_LANE2_SPARE 0x4808 -+#define PPI_RW_DPHY_LANE3_LBERT_0 0x4c00 -+#define PPI_RW_DPHY_LANE3_LBERT_1 0x4c02 -+#define PPI_R_DPHY_LANE3_LBERT_0 0x4c04 -+#define PPI_R_DPHY_LANE3_LBERT_1 0x4c06 -+#define PPI_RW_DPHY_LANE3_SPARE 0x4c08 -+#define CORE_DIG_DLANE_0_RW_CFG_0 0x6000 -+#define CORE_DIG_DLANE_0_RW_CFG_1 0x6002 -+#define CORE_DIG_DLANE_0_RW_CFG_2 0x6004 -+#define CORE_DIG_DLANE_0_RW_LP_0 0x6080 -+#define CORE_DIG_DLANE_0_RW_LP_1 0x6082 -+#define CORE_DIG_DLANE_0_RW_LP_2 0x6084 -+#define CORE_DIG_DLANE_0_R_LP_0 0x60a0 -+#define CORE_DIG_DLANE_0_R_LP_1 0x60a2 -+#define CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0 -+#define CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100 -+#define CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102 -+#define CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104 -+#define CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106 -+#define CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108 -+#define CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a -+#define CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c -+#define CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e -+#define CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110 -+#define CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112 -+#define CORE_DIG_DLANE_0_R_HS_RX_0 0x6120 -+#define CORE_DIG_DLANE_0_R_HS_RX_1 0x6122 -+#define CORE_DIG_DLANE_0_R_HS_RX_2 0x6124 -+#define CORE_DIG_DLANE_0_R_HS_RX_3 0x6126 -+#define CORE_DIG_DLANE_0_R_HS_RX_4 0x6128 -+#define CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200 -+#define CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202 -+#define CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204 -+#define CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206 -+#define CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208 -+#define CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a -+#define CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c -+#define CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e -+#define CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210 -+#define CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212 -+#define CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214 -+#define CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216 -+#define CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218 -+#define CORE_DIG_DLANE_1_RW_CFG_0 0x6400 -+#define CORE_DIG_DLANE_1_RW_CFG_1 0x6402 -+#define CORE_DIG_DLANE_1_RW_CFG_2 0x6404 -+#define CORE_DIG_DLANE_1_RW_LP_0 0x6480 -+#define CORE_DIG_DLANE_1_RW_LP_1 0x6482 -+#define CORE_DIG_DLANE_1_RW_LP_2 0x6484 -+#define CORE_DIG_DLANE_1_R_LP_0 0x64a0 -+#define CORE_DIG_DLANE_1_R_LP_1 0x64a2 -+#define CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0 -+#define CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500 -+#define CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502 -+#define CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504 -+#define CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506 -+#define CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508 -+#define CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a -+#define CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c -+#define CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e -+#define CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510 -+#define CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512 -+#define CORE_DIG_DLANE_1_R_HS_RX_0 0x6520 -+#define CORE_DIG_DLANE_1_R_HS_RX_1 0x6522 -+#define CORE_DIG_DLANE_1_R_HS_RX_2 0x6524 -+#define CORE_DIG_DLANE_1_R_HS_RX_3 0x6526 -+#define CORE_DIG_DLANE_1_R_HS_RX_4 0x6528 -+#define CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600 -+#define CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602 -+#define CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604 -+#define CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606 -+#define CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608 -+#define CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a -+#define CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c -+#define CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e -+#define CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610 -+#define CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612 -+#define CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614 -+#define CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616 -+#define CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618 -+#define CORE_DIG_DLANE_2_RW_CFG_0 0x6800 -+#define CORE_DIG_DLANE_2_RW_CFG_1 0x6802 -+#define CORE_DIG_DLANE_2_RW_CFG_2 0x6804 -+#define CORE_DIG_DLANE_2_RW_LP_0 0x6880 -+#define CORE_DIG_DLANE_2_RW_LP_1 0x6882 -+#define CORE_DIG_DLANE_2_RW_LP_2 0x6884 -+#define CORE_DIG_DLANE_2_R_LP_0 0x68a0 -+#define CORE_DIG_DLANE_2_R_LP_1 0x68a2 -+#define CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0 -+#define CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900 -+#define CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902 -+#define CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904 -+#define CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906 -+#define CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908 -+#define CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a -+#define CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c -+#define CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e -+#define CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910 -+#define CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912 -+#define CORE_DIG_DLANE_2_R_HS_RX_0 0x6920 -+#define CORE_DIG_DLANE_2_R_HS_RX_1 0x6922 -+#define CORE_DIG_DLANE_2_R_HS_RX_2 0x6924 -+#define CORE_DIG_DLANE_2_R_HS_RX_3 0x6926 -+#define CORE_DIG_DLANE_2_R_HS_RX_4 0x6928 -+#define CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00 -+#define CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02 -+#define CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04 -+#define CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06 -+#define CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08 -+#define CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a -+#define CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c -+#define CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e -+#define CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10 -+#define CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12 -+#define CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14 -+#define CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16 -+#define CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18 -+#define CORE_DIG_DLANE_3_RW_CFG_0 0x6c00 -+#define CORE_DIG_DLANE_3_RW_CFG_1 0x6c02 -+#define CORE_DIG_DLANE_3_RW_CFG_2 0x6c04 -+#define CORE_DIG_DLANE_3_RW_LP_0 0x6c80 -+#define CORE_DIG_DLANE_3_RW_LP_1 0x6c82 -+#define CORE_DIG_DLANE_3_RW_LP_2 0x6c84 -+#define CORE_DIG_DLANE_3_R_LP_0 0x6ca0 -+#define CORE_DIG_DLANE_3_R_LP_1 0x6ca2 -+#define CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0 -+#define CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00 -+#define CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02 -+#define CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04 -+#define CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06 -+#define CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08 -+#define CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a -+#define CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c -+#define CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e -+#define CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10 -+#define CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12 -+#define CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20 -+#define CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22 -+#define CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24 -+#define CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26 -+#define CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28 -+#define CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00 -+#define CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02 -+#define CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04 -+#define CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06 -+#define CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08 -+#define CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a -+#define CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c -+#define CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e -+#define CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10 -+#define CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12 -+#define CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14 -+#define CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16 -+#define CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18 -+#define CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000 -+#define CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002 -+#define CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004 -+#define CORE_DIG_DLANE_CLK_RW_LP_0 0x7080 -+#define CORE_DIG_DLANE_CLK_RW_LP_1 0x7082 -+#define CORE_DIG_DLANE_CLK_RW_LP_2 0x7084 -+#define CORE_DIG_DLANE_CLK_R_LP_0 0x70a0 -+#define CORE_DIG_DLANE_CLK_R_LP_1 0x70a2 -+#define CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110 -+#define CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126 -+#define CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216 -+#define CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218 -+#define PPI_RW_CPHY_TRIO0_LBERT_0 0x8000 -+#define PPI_RW_CPHY_TRIO0_LBERT_1 0x8002 -+#define PPI_R_CPHY_TRIO0_LBERT_0 0x8004 -+#define PPI_R_CPHY_TRIO0_LBERT_1 0x8006 -+#define PPI_RW_CPHY_TRIO0_SPARE 0x8008 -+#define PPI_RW_CPHY_TRIO1_LBERT_0 0x8400 -+#define PPI_RW_CPHY_TRIO1_LBERT_1 0x8402 -+#define PPI_R_CPHY_TRIO1_LBERT_0 0x8404 -+#define PPI_R_CPHY_TRIO1_LBERT_1 0x8406 -+#define PPI_RW_CPHY_TRIO1_SPARE 0x8408 -+#define PPI_RW_CPHY_TRIO2_LBERT_0 0x8800 -+#define PPI_RW_CPHY_TRIO2_LBERT_1 0x8802 -+#define PPI_R_CPHY_TRIO2_LBERT_0 0x8804 -+#define PPI_R_CPHY_TRIO2_LBERT_1 0x8806 -+#define PPI_RW_CPHY_TRIO2_SPARE 0x8808 -+#define CORE_DIG_CLANE_0_RW_CFG_0 0xa000 -+#define CORE_DIG_CLANE_0_RW_CFG_2 0xa004 -+#define CORE_DIG_CLANE_0_RW_LP_0 0xa080 -+#define CORE_DIG_CLANE_0_RW_LP_1 0xa082 -+#define CORE_DIG_CLANE_0_RW_LP_2 0xa084 -+#define CORE_DIG_CLANE_0_R_LP_0 0xa0a0 -+#define CORE_DIG_CLANE_0_R_LP_1 0xa0a2 -+#define CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100 -+#define CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102 -+#define CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104 -+#define CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106 -+#define CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108 -+#define CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a -+#define CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c -+#define CORE_DIG_CLANE_0_R_RX_0 0xa120 -+#define CORE_DIG_CLANE_0_R_RX_1 0xa122 -+#define CORE_DIG_CLANE_0_R_TX_0 0xa124 -+#define CORE_DIG_CLANE_0_R_RX_2 0xa126 -+#define CORE_DIG_CLANE_0_R_RX_3 0xa128 -+#define CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200 -+#define CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202 -+#define CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204 -+#define CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206 -+#define CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208 -+#define CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a -+#define CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c -+#define CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e -+#define CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210 -+#define CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212 -+#define CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214 -+#define CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216 -+#define CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218 -+#define CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a -+#define CORE_DIG_CLANE_1_RW_CFG_0 0xa400 -+#define CORE_DIG_CLANE_1_RW_CFG_2 0xa404 -+#define CORE_DIG_CLANE_1_RW_LP_0 0xa480 -+#define CORE_DIG_CLANE_1_RW_LP_1 0xa482 -+#define CORE_DIG_CLANE_1_RW_LP_2 0xa484 -+#define CORE_DIG_CLANE_1_R_LP_0 0xa4a0 -+#define CORE_DIG_CLANE_1_R_LP_1 0xa4a2 -+#define CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500 -+#define CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502 -+#define CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504 -+#define CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506 -+#define CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508 -+#define CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a -+#define CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c -+#define CORE_DIG_CLANE_1_R_RX_0 0xa520 -+#define CORE_DIG_CLANE_1_R_RX_1 0xa522 -+#define CORE_DIG_CLANE_1_R_TX_0 0xa524 -+#define CORE_DIG_CLANE_1_R_RX_2 0xa526 -+#define CORE_DIG_CLANE_1_R_RX_3 0xa528 -+#define CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600 -+#define CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602 -+#define CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604 -+#define CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606 -+#define CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608 -+#define CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a -+#define CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c -+#define CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e -+#define CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610 -+#define CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612 -+#define CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614 -+#define CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616 -+#define CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618 -+#define CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a -+#define CORE_DIG_CLANE_2_RW_CFG_0 0xa800 -+#define CORE_DIG_CLANE_2_RW_CFG_2 0xa804 -+#define CORE_DIG_CLANE_2_RW_LP_0 0xa880 -+#define CORE_DIG_CLANE_2_RW_LP_1 0xa882 -+#define CORE_DIG_CLANE_2_RW_LP_2 0xa884 -+#define CORE_DIG_CLANE_2_R_LP_0 0xa8a0 -+#define CORE_DIG_CLANE_2_R_LP_1 0xa8a2 -+#define CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900 -+#define CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902 -+#define CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904 -+#define CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906 -+#define CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908 -+#define CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a -+#define CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c -+#define CORE_DIG_CLANE_2_R_RX_0 0xa920 -+#define CORE_DIG_CLANE_2_R_RX_1 0xa922 -+#define CORE_DIG_CLANE_2_R_TX_0 0xa924 -+#define CORE_DIG_CLANE_2_R_RX_2 0xa926 -+#define CORE_DIG_CLANE_2_R_RX_3 0xa928 -+#define CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00 -+#define CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02 -+#define CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04 -+#define CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06 -+#define CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08 -+#define CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a -+#define CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c -+#define CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e -+#define CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10 -+#define CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12 -+#define CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14 -+#define CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16 -+#define CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18 -+#define CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a -+ -+/* dwc csi host controller registers */ -+#define IS_IO_CSI2_HOST_BASE(i) (IS_IO_BASE + 0x40000 + \ -+ 0x2000 * (i)) -+#define VERSION 0x0 -+#define N_LANES 0x4 -+#define CSI2_RESETN 0x8 -+#define INT_ST_MAIN 0xc -+#define DATA_IDS_1 0x10 -+#define DATA_IDS_2 0x14 -+#define CDPHY_MODE 0x1c -+#define DATA_IDS_VC_1 0x30 -+#define DATA_IDS_VC_2 0x34 -+#define PHY_SHUTDOWNZ 0x40 -+#define DPHY_RSTZ 0x44 -+#define PHY_RX 0x48 -+#define PHY_STOPSTATE 0x4c -+#define PHY_TEST_CTRL0 0x50 -+#define PHY_TEST_CTRL1 0x54 -+#define PPI_PG_PATTERN_VRES 0x60 -+#define PPI_PG_PATTERN_HRES 0x64 -+#define PPI_PG_CONFIG 0x68 -+#define PPI_PG_ENABLE 0x6c -+#define PPI_PG_STATUS 0x70 -+#define VC_EXTENSION 0xc8 -+#define PHY_CAL 0xcc -+#define INT_ST_PHY_FATAL 0xe0 -+#define INT_MSK_PHY_FATAL 0xe4 -+#define INT_FORCE_PHY_FATAL 0xe8 -+#define INT_ST_PKT_FATAL 0xf0 -+#define INT_MSK_PKT_FATAL 0xf4 -+#define INT_FORCE_PKT_FATAL 0xf8 -+#define INT_ST_PHY 0x110 -+#define INT_MSK_PHY 0x114 -+#define INT_FORCE_PHY 0x118 -+#define INT_ST_LINE 0x130 -+#define INT_MSK_LINE 0x134 -+#define INT_FORCE_LINE 0x138 -+#define INT_ST_BNDRY_FRAME_FATAL 0x280 -+#define INT_MSK_BNDRY_FRAME_FATAL 0x284 -+#define INT_FORCE_BNDRY_FRAME_FATAL 0x288 -+#define INT_ST_SEQ_FRAME_FATAL 0x290 -+#define INT_MSK_SEQ_FRAME_FATAL 0x294 -+#define INT_FORCE_SEQ_FRAME_FATAL 0x298 -+#define INT_ST_CRC_FRAME_FATAL 0x2a0 -+#define INT_MSK_CRC_FRAME_FATAL 0x2a4 -+#define INT_FORCE_CRC_FRAME_FATAL 0x2a8 -+#define INT_ST_PLD_CRC_FATAL 0x2b0 -+#define INT_MSK_PLD_CRC_FATAL 0x2b4 -+#define INT_FORCE_PLD_CRC_FATAL 0x2b8 -+#define INT_ST_DATA_ID 0x2c0 -+#define INT_MSK_DATA_ID 0x2c4 -+#define INT_FORCE_DATA_ID 0x2c8 -+#define INT_ST_ECC_CORRECTED 0x2d0 -+#define INT_MSK_ECC_CORRECTED 0x2d4 -+#define INT_FORCE_ECC_CORRECTED 0x2d8 -+#define SCRAMBLING 0x300 -+#define SCRAMBLING_SEED1 0x304 -+#define SCRAMBLING_SEED2 0x308 -+#define SCRAMBLING_SEED3 0x30c -+#define SCRAMBLING_SEED4 0x310 -+#define SCRAMBLING 0x300 -+ -+#define IS_IO_CSI2_ADPL_PORT_BASE(i) (IS_IO_BASE + 0x40800 + \ -+ 0x2000 * (i)) -+#define CSI2_ADPL_INPUT_MODE 0x0 -+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4 -+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8 -+#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc -+#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4 -+#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8 -+#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac -+#define CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc -+#define CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0 -+#define CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4 -+#define CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8 -+#define CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc -+#define CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0 -+ -+/* software control the legacy csi irq */ -+#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40c00 + \ -+ 0x2000 * (i)) -+#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \ -+ 0x2000 * (i)) -+#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IS_IO_BASE + 0x49000) -+#define IS_IO_CSI2_IRQ_CTRL_BASE (IS_IO_BASE + 0x4e100) -+ -+#define IRQ_CTL_EDGE 0x0 -+#define IRQ_CTL_MASK 0x4 -+#define IRQ_CTL_STATUS 0x8 -+#define IRQ_CTL_CLEAR 0xc -+#define IRQ_CTL_ENABLE 0x10 -+/* FE irq for PTL */ -+#define IRQ1_CTL_MASK 0x14 -+#define IRQ1_CTL_STATUS 0x18 -+#define IRQ1_CTL_CLEAR 0x1c -+#define IRQ1_CTL_ENABLE 0x20 -+ -+/* software to set the clock gate to use the port or mgc */ -+#define IS_IO_GPREGS_BASE (IS_IO_BASE + 0x49200) -+#define SRST_PORT_ARB 0x0 -+#define SRST_MGC 0x4 -+#define SRST_WIDTH_CONV 0x8 -+#define SRST_CSI_IRQ 0xc -+#define SRST_CSI_LEGACY_IRQ 0x10 -+#define CLK_EN_TXCLKESC 0x14 -+#define CLK_DIV_FACTOR_IS_CLK 0x18 -+#define CLK_DIV_FACTOR_APB_CLK 0x1c -+#define CSI_PORT_CLK_GATE 0x20 -+#define CSI_PORTAB_AGGREGATION 0x24 -+#define MGC_CLK_GATE 0x2c -+#define CG_CTRL_BITS 0x3c -+#define SPARE_RW 0xf8 -+#define SPARE_RO 0xfc -+ -+#define IS_IO_CSI2_MPF_PORT_BASE(i) (IS_IO_BASE + 0x53000 + \ -+ 0x1000 * (i)) -+#define MPF_16_IRQ_CNTRL_STATUS 0x238 -+#define MPF_16_IRQ_CNTRL_CLEAR 0x23c -+#define MPF_16_IRQ_CNTRL_ENABLE 0x240 -+ -+/* software config the phy */ -+#define IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x53400) -+#define IPU8_IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x40e00) -+#define CSI_ADAPT_LAYER_SRST 0x0 -+#define MPF_SRST_RST 0x4 -+#define CSI_ERR_IRQ_CTRL_SRST 0x8 -+#define CSI_SYNC_RC_SRST 0xc -+#define CSI_CG_CTRL_BITS 0x10 -+#define SOC_CSI2HOST_SELECT 0x14 -+#define PHY_RESET 0x18 -+#define PHY_SHUTDOWN 0x1c -+#define PHY_MODE 0x20 -+#define PHY_READY 0x24 -+#define PHY_CLK_LANE_FORCE_CONTROL 0x28 -+#define PHY_CLK_LANE_CONTROL 0x2c -+#define PHY_CLK_LANE_STATUS 0x30 -+#define PHY_LANE_RX_ESC_REQ 0x34 -+#define PHY_LANE_RX_ESC_DATA 0x38 -+#define PHY_LANE_TURNDISABLE 0x3c -+#define PHY_LANE_DIRECTION 0x40 -+#define PHY_LANE_FORCE_CONTROL 0x44 -+#define PHY_LANE_CONTROL_EN 0x48 -+#define PHY_LANE_CONTROL_DATAWIDTH 0x4c -+#define PHY_LANE_STATUS 0x50 -+#define PHY_LANE_ERR 0x54 -+#define PHY_LANE_RXALP 0x58 -+#define PHY_LANE_RXALP_NIBBLE 0x5c -+#define PHY_PARITY_ERROR 0x60 -+#define PHY_DEBUG_REGS_CLK_GATE_EN 0x64 -+#define SPARE_RW 0xf8 -+#define SPARE_RO 0xfc -+ -+/* software not touch */ -+#define PORT_ARB_BASE (IS_IO_BASE + 0x4e000) -+#define PORT_ARB_IRQ_CTL_STATUS 0x4 -+#define PORT_ARB_IRQ_CTL_CLEAR 0x8 -+#define PORT_ARB_IRQ_CTL_ENABLE 0xc -+ -+#define MGC_PPC 4U -+#define MGC_DTYPE_RAW(i) (((i) - 8) / 2) -+#define IS_IO_MGC_BASE (IS_IO_BASE + 0x48000) -+#define MGC_KICK 0x0 -+#define MGC_ASYNC_STOP 0x4 -+#define MGC_PORT_OFFSET 0x100 -+#define MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4) -+#define MGC_MG_PORT(i) (IS_IO_MGC_BASE + \ -+ (i) * MGC_PORT_OFFSET) -+/* per mgc instance */ -+#define MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28 -+#define MGC_MG_MODE 0x2c -+#define MGC_MG_INIT_COUNTER 0x30 -+#define MGC_MG_MIPI_VC 0x34 -+#define MGC_MG_MIPI_DTYPES 0x38 -+#define MGC_MG_MULTI_DTYPES_MODE 0x3c -+#define MGC_MG_NOF_FRAMES 0x40 -+#define MGC_MG_FRAME_DIM 0x44 -+#define MGC_MG_HBLANK 0x48 -+#define MGC_MG_VBLANK 0x4c -+#define MGC_MG_TPG_MODE 0x50 -+#define MGC_MG_TPG_R0 0x54 -+#define MGC_MG_TPG_G0 0x58 -+#define MGC_MG_TPG_B0 0x5c -+#define MGC_MG_TPG_R1 0x60 -+#define MGC_MG_TPG_G1 0x64 -+#define MGC_MG_TPG_B1 0x68 -+#define MGC_MG_TPG_FACTORS 0x6c -+#define MGC_MG_TPG_MASKS 0x70 -+#define MGC_MG_TPG_XY_MASK 0x74 -+#define MGC_MG_TPG_TILE_DIM 0x78 -+#define MGC_MG_PRBS_LFSR_INIT_0 0x7c -+#define MGC_MG_PRBS_LFSR_INIT_1 0x80 -+#define MGC_MG_SYNC_STOP_POINT 0x84 -+#define MGC_MG_SYNC_STOP_POINT_LOC 0x88 -+#define MGC_MG_ERR_INJECT 0x8c -+#define MGC_MG_ERR_LOCATION 0x90 -+#define MGC_MG_DTO_SPEED_CTRL_EN 0x94 -+#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98 -+#define MGC_MG_HOR_LOC_STTS 0x9c -+#define MGC_MG_VER_LOC_STTS 0xa0 -+#define MGC_MG_FRAME_NUM_STTS 0xa4 -+#define MGC_MG_BUSY_STTS 0xa8 -+#define MGC_MG_STOPPED_STTS 0xac -+/* tile width and height in pixels for Chess board and Color palette */ -+#define MGC_TPG_TILE_WIDTH 64U -+#define MGC_TPG_TILE_HEIGHT 64U -+ -+#define IPU_CSI_PORT_A_ADDR_OFFSET 0x0 -+#define IPU_CSI_PORT_B_ADDR_OFFSET 0x0 -+#define IPU_CSI_PORT_C_ADDR_OFFSET 0x0 -+#define IPU_CSI_PORT_D_ADDR_OFFSET 0x0 -+ -+/* -+ * 0 - CSI RX Port 0 interrupt; -+ * 1 - MPF Port 0 interrupt; -+ * 2 - CSI RX Port 1 interrupt; -+ * 3 - MPF Port 1 interrupt; -+ * 4 - CSI RX Port 2 interrupt; -+ * 5 - MPF Port 2 interrupt; -+ * 6 - CSI RX Port 3 interrupt; -+ * 7 - MPF Port 3 interrupt; -+ * 8 - Port ARB FIFO 0 overflow; -+ * 9 - Port ARB FIFO 1 overflow; -+ * 10 - Port ARB FIFO 2 overflow; -+ * 11 - Port ARB FIFO 3 overflow; -+ * 12 - isys_cfgnoc_err_probe_intl; -+ * 13-15 - reserved -+ */ -+#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff -+ -+/* Adapter layer irq */ -+#define IPU7_CSI_ADPL_IRQ_MASK 0xffff -+ -+/* sw irq from legacy irq control -+ * legacy irq status -+ * IPU7 -+ * 0 - CSI Port 0 error interrupt -+ * 1 - CSI Port 0 sync interrupt -+ * 2 - CSI Port 1 error interrupt -+ * 3 - CSI Port 1 sync interrupt -+ * 4 - CSI Port 2 error interrupt -+ * 5 - CSI Port 2 sync interrupt -+ * 6 - CSI Port 3 error interrupt -+ * 7 - CSI Port 3 sync interrupt -+ * IPU7P5 -+ * 0 - CSI Port 0 error interrupt -+ * 1 - CSI Port 0 fs interrupt -+ * 2 - CSI Port 0 fe interrupt -+ * 3 - CSI Port 1 error interrupt -+ * 4 - CSI Port 1 fs interrupt -+ * 5 - CSI Port 1 fe interrupt -+ * 6 - CSI Port 2 error interrupt -+ * 7 - CSI Port 2 fs interrupt -+ * 8 - CSI Port 2 fe interrupt -+ */ -+#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff -+ -+/* legacy error status per port -+ * 0 - Error handler FIFO full; -+ * 1 - Reserved Short Packet encoding detected; -+ * 2 - Reserved Long Packet encoding detected; -+ * 3 - Received packet is too short (fewer data words than specified in header); -+ * 4 - Received packet is too long (more data words than specified in header); -+ * 5 - Short packet discarded due to errors; -+ * 6 - Long packet discarded due to errors; -+ * 7 - CSI Combo Rx interrupt; -+ * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0; -+ */ -+#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff -+ -+/* -+ * 0 - VC0 frame start received -+ * 1 - VC0 frame end received -+ * 2 - VC1 frame start received -+ * 3 - VC1 frame end received -+ * 4 - VC2 frame start received -+ * 5 - VC2 frame end received -+ * 6 - VC3 frame start received -+ * 7 - VC3 frame end received -+ * 8 - VC4 frame start received -+ * 9 - VC4 frame end received -+ * 10 - VC5 frame start received -+ * 11 - VC5 frame end received -+ * 12 - VC6 frame start received -+ * 13 - VC6 frame end received -+ * 14 - VC7 frame start received -+ * 15 - VC7 frame end received -+ * 16 - VC8 frame start received -+ * 17 - VC8 frame end received -+ * 18 - VC9 frame start received -+ * 19 - VC9 frame end received -+ * 20 - VC10 frame start received -+ * 21 - VC10 frame end received -+ * 22 - VC11 frame start received -+ * 23 - VC11 frame end received -+ * 24 - VC12 frame start received -+ * 25 - VC12 frame end received -+ * 26 - VC13 frame start received -+ * 27 - VC13 frame end received -+ * 28 - VC14 frame start received -+ * 29 - VC14 frame end received -+ * 30 - VC15 frame start received -+ * 31 - VC15 frame end received -+ */ -+ -+#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0 -+#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0 -+ -+#define CSI_RX_NUM_ERRORS_IN_IRQ 12U -+#define CSI_RX_NUM_SYNC_IN_IRQ 32U -+ -+enum CSI_FE_MODE_TYPE { -+ CSI_FE_DPHY_MODE = 0, -+ CSI_FE_CPHY_MODE = 1, -+}; -+ -+enum CSI_FE_INPUT_MODE { -+ CSI_SENSOR_INPUT = 0, -+ CSI_MIPIGEN_INPUT = 1, -+}; -+ -+enum MGC_CSI_ADPL_TYPE { -+ MGC_MAPPED_2_LANES = 0, -+ MGC_MAPPED_4_LANES = 1, -+}; -+ -+enum CSI2HOST_SELECTION { -+ CSI2HOST_SEL_SOC = 0, -+ CSI2HOST_SEL_CSI2HOST = 1, -+}; -+ -+#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port)) -+#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port)) -+ -+/* ---------------------------------------------------------------- */ -+#define CSI_REG_BASE 0x220000 -+#define CSI_REG_BASE_PORT(id) ((id) * 0x1000) -+ -+/* CSI Port General Purpose Registers */ -+#define CSI_REG_PORT_GPREG_SRST 0x0 -+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4 -+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8 -+ -+#define CSI_RX_NUM_IRQ 32U -+ -+#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555 -+#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa -+#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff -+#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff -+ -+#endif /* IPU7_ISYS_CSI2_REG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -new file mode 100644 -index 000000000000..0fe4299222e8 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.c -@@ -0,0 +1,544 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-isys-csi-phy.h" -+ -+static const u32 csi2_supported_codes[] = { -+ MEDIA_BUS_FMT_Y10_1X10, -+ MEDIA_BUS_FMT_RGB565_1X16, -+ MEDIA_BUS_FMT_RGB888_1X24, -+ MEDIA_BUS_FMT_UYVY8_1X16, -+ MEDIA_BUS_FMT_YUYV8_1X16, -+ MEDIA_BUS_FMT_YUYV10_1X20, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ 0, -+}; -+ -+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2) -+{ -+ struct media_pad *src_pad; -+ -+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity); -+ if (IS_ERR(src_pad)) { -+ dev_err(&csi2->isys->adev->auxdev.dev, -+ "can't get source pad of %s (%ld)\n", -+ csi2->asd.sd.name, PTR_ERR(src_pad)); -+ return PTR_ERR(src_pad); -+ } -+ -+ return v4l2_get_link_freq(src_pad, 0, 0); -+} -+ -+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n", -+ sub->type, sub->id); -+ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+} -+ -+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = { -+ .subscribe_event = csi2_subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_device *isp = csi2->isys->adev->isp; -+ unsigned int offset, mask; -+ -+ /* enable CSI2 legacy error irq */ -+ offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(mask, csi2->base + offset + IRQ_CTL_MASK); -+ writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ /* enable CSI2 legacy sync irq */ -+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(mask, csi2->base + offset + IRQ_CTL_MASK); -+ writel(mask, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK; -+ if (!is_ipu7(isp->hw_ver)) { -+ writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -+ writel(mask, csi2->base + offset + IRQ1_CTL_MASK); -+ writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE); -+ } -+} -+ -+static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_device *isp = csi2->isys->adev->isp; -+ unsigned int offset, mask; -+ -+ /* disable CSI2 legacy error irq */ -+ offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(0, csi2->base + offset + IRQ_CTL_MASK); -+ writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ /* disable CSI2 legacy sync irq */ -+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK; -+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ writel(0, csi2->base + offset + IRQ_CTL_MASK); -+ writel(0, csi2->base + offset + IRQ_CTL_ENABLE); -+ -+ if (!is_ipu7(isp->hw_ver)) { -+ writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR); -+ writel(0, csi2->base + offset + IRQ1_CTL_MASK); -+ writel(0, csi2->base + offset + IRQ1_CTL_ENABLE); -+ } -+} -+ -+static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ void __iomem *isys_base = isys->pdata->base; -+ -+ ipu7_isys_csi_phy_powerdown(csi2); -+ -+ writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK); -+ csi2_irq_disable(csi2); -+} -+ -+static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_isys *isys = csi2->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *isys_base = isys->pdata->base; -+ unsigned int port, nlanes, offset; -+ int ret; -+ -+ port = csi2->port; -+ nlanes = csi2->nlanes; -+ -+ offset = IS_IO_GPREGS_BASE; -+ writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK); -+ dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n", -+ port, readl(isys_base + offset + CSI_PORT_CLK_GATE), -+ readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK)); -+ if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) { -+ dev_dbg(dev, "CSI port %u in aggregation mode\n", port); -+ writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION); -+ } -+ -+ /* input is coming from CSI receiver (sensor) */ -+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -+ writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE); -+ writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN); -+ -+ ret = ipu7_isys_csi_phy_powerup(csi2); -+ if (ret) { -+ dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret); -+ return ret; -+ } -+ -+ csi2_irq_enable(csi2); -+ -+ return 0; -+} -+ -+static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct device *dev = &asd->isys->adev->auxdev.dev; -+ struct v4l2_mbus_framefmt *sink_ffmt; -+ struct v4l2_mbus_framefmt *src_ffmt; -+ struct v4l2_rect *crop; -+ -+ if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) -+ return -EINVAL; -+ -+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -+ sel->pad, -+ sel->stream); -+ if (!sink_ffmt) -+ return -EINVAL; -+ -+ src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); -+ if (!src_ffmt) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -+ if (!crop) -+ return -EINVAL; -+ -+ /* Only vertical cropping is supported */ -+ sel->r.left = 0; -+ sel->r.width = sink_ffmt->width; -+ /* Non-bayer formats can't be single line cropped */ -+ if (!ipu7_isys_is_bayer_format(sink_ffmt->code)) -+ sel->r.top &= ~1U; -+ sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT, -+ sink_ffmt->height - sel->r.top); -+ *crop = sel->r; -+ -+ /* update source pad format */ -+ src_ffmt->width = sel->r.width; -+ src_ffmt->height = sel->r.height; -+ if (ipu7_isys_is_bayer_format(sink_ffmt->code)) -+ src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code, -+ sel->r.left, -+ sel->r.top); -+ dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n", -+ sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height, -+ src_ffmt->code); -+ -+ return 0; -+} -+ -+static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_selection *sel) -+{ -+ struct v4l2_mbus_framefmt *sink_ffmt; -+ struct v4l2_rect *crop; -+ int ret = 0; -+ -+ if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK) -+ return -EINVAL; -+ -+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state, -+ sel->pad, -+ sel->stream); -+ if (!sink_ffmt) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); -+ if (!crop) -+ return -EINVAL; -+ -+ switch (sel->target) { -+ case V4L2_SEL_TGT_CROP_DEFAULT: -+ case V4L2_SEL_TGT_CROP_BOUNDS: -+ sel->r.left = 0; -+ sel->r.top = 0; -+ sel->r.width = sink_ffmt->width; -+ sel->r.height = sink_ffmt->height; -+ break; -+ case V4L2_SEL_TGT_CROP: -+ sel->r = *crop; -+ break; -+ default: -+ ret = -EINVAL; -+ } -+ -+ return ret; -+} -+ -+/* -+ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set -+ * of streams. -+ */ -+#define CSI2_SUBDEV_MAX_STREAM_ID 63 -+ -+static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -+ struct v4l2_subdev *r_sd; -+ struct media_pad *rp; -+ u32 sink_pad, sink_stream; -+ int ret, i; -+ -+ if (!csi2->stream_count) { -+ dev_dbg(&csi2->isys->adev->auxdev.dev, -+ "stream on CSI2-%u with %u lanes\n", csi2->port, -+ csi2->nlanes); -+ ret = ipu7_isys_csi2_enable_stream(csi2); -+ if (ret) -+ return ret; -+ } -+ -+ for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -+ if (streams_mask & BIT_ULL(i)) -+ break; -+ } -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -+ &sink_pad, &sink_stream); -+ if (ret) -+ return ret; -+ -+ rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -+ r_sd = media_entity_to_v4l2_subdev(rp->entity); -+ -+ ret = v4l2_subdev_enable_streams(r_sd, rp->index, -+ BIT_ULL(sink_stream)); -+ if (!ret) { -+ csi2->stream_count++; -+ return 0; -+ } -+ -+ if (!csi2->stream_count) -+ ipu7_isys_csi2_disable_stream(csi2); -+ -+ return ret; -+} -+ -+static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ u32 pad, u64 streams_mask) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd); -+ struct v4l2_subdev *r_sd; -+ struct media_pad *rp; -+ u32 sink_pad, sink_stream; -+ int ret, i; -+ -+ for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) { -+ if (streams_mask & BIT_ULL(i)) -+ break; -+ } -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i, -+ &sink_pad, &sink_stream); -+ if (ret) -+ return ret; -+ -+ rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]); -+ r_sd = media_entity_to_v4l2_subdev(rp->entity); -+ -+ v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream)); -+ -+ if (--csi2->stream_count) -+ return 0; -+ -+ dev_dbg(&csi2->isys->adev->auxdev.dev, -+ "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes); -+ -+ ipu7_isys_csi2_disable_stream(csi2); -+ -+ return 0; -+} -+ -+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = { -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu7_isys_subdev_set_fmt, -+ .get_selection = ipu7_isys_csi2_get_sel, -+ .set_selection = ipu7_isys_csi2_set_sel, -+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -+ .enable_streams = ipu7_isys_csi2_enable_streams, -+ .disable_streams = ipu7_isys_csi2_disable_streams, -+ .set_routing = ipu7_isys_subdev_set_routing, -+}; -+ -+static const struct v4l2_subdev_ops csi2_sd_ops = { -+ .core = &csi2_sd_core_ops, -+ .pad = &csi2_sd_pad_ops, -+}; -+ -+static const struct media_entity_operations csi2_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+ .has_pad_interdep = v4l2_subdev_has_pad_interdep, -+}; -+ -+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2) -+{ -+ if (!csi2->isys) -+ return; -+ -+ v4l2_device_unregister_subdev(&csi2->asd.sd); -+ v4l2_subdev_cleanup(&csi2->asd.sd); -+ ipu7_isys_subdev_cleanup(&csi2->asd); -+ csi2->isys = NULL; -+} -+ -+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, -+ struct ipu7_isys *isys, -+ void __iomem *base, unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ csi2->isys = isys; -+ csi2->base = base; -+ csi2->port = index; -+ -+ if (!is_ipu7(isys->adev->isp->hw_ver)) -+ csi2->legacy_irq_mask = 0x7U << (index * 3U); -+ else -+ csi2->legacy_irq_mask = 0x3U << (index * 2U); -+ -+ dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index, -+ csi2->legacy_irq_mask); -+ -+ csi2->asd.sd.entity.ops = &csi2_entity_ops; -+ csi2->asd.isys = isys; -+ -+ ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0, -+ IPU7_NR_OF_CSI2_SINK_PADS, -+ IPU7_NR_OF_CSI2_SRC_PADS); -+ if (ret) -+ return ret; -+ -+ csi2->asd.source = (int)index; -+ csi2->asd.supported_codes = csi2_supported_codes; -+ snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name), -+ IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index); -+ v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd); -+ -+ ret = v4l2_subdev_init_finalize(&csi2->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret); -+ goto isys_subdev_cleanup; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret); -+ goto v4l2_subdev_cleanup; -+ } -+ -+ return 0; -+ -+v4l2_subdev_cleanup: -+ v4l2_subdev_cleanup(&csi2->asd.sd); -+isys_subdev_cleanup: -+ ipu7_isys_subdev_cleanup(&csi2->asd); -+ -+ return ret; -+} -+ -+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct video_device *vdev = csi2->asd.sd.devnode; -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.id = stream->vc; -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n", -+ csi2->port, ev.u.frame_sync.frame_sequence, stream->vc); -+} -+ -+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n", -+ csi2->port, frame_sequence); -+} -+ -+int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -+ struct ipu7_isys_csi2 *csi2, -+ struct media_entity *source_entity, -+ struct v4l2_mbus_frame_desc_entry *entry, -+ int *nr_queues) -+{ -+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL; -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct v4l2_mbus_frame_desc desc; -+ struct v4l2_subdev *source; -+ struct media_pad *pad; -+ unsigned int i; -+ int ret; -+ -+ source = media_entity_to_v4l2_subdev(source_entity); -+ if (!source) -+ return -EPIPE; -+ -+ pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]); -+ if (!pad) -+ return -EPIPE; -+ -+ ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc); -+ if (ret) -+ return ret; -+ -+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) { -+ dev_err(dev, "Unsupported frame descriptor type\n"); -+ return -EINVAL; -+ } -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ if (source_stream == desc.entry[i].stream) { -+ desc_entry = &desc.entry[i]; -+ break; -+ } -+ } -+ -+ if (!desc_entry) { -+ dev_err(dev, "Failed to find stream %u from remote subdev\n", -+ source_stream); -+ return -EINVAL; -+ } -+ -+ if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) { -+ dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc); -+ return -EINVAL; -+ } -+ -+ *entry = *desc_entry; -+ -+ for (i = 0; i < desc.num_entries; i++) { -+ if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc) -+ (*nr_queues)++; -+ } -+ -+ return 0; -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -new file mode 100644 -index 000000000000..6c23b80f92a2 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-csi2.h -@@ -0,0 +1,64 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_CSI2_H -+#define IPU7_ISYS_CSI2_H -+ -+#include -+#include -+ -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-video.h" -+ -+struct ipu7_isys; -+struct ipu7_isys_csi2_pdata; -+struct ipu7_isys_stream; -+ -+#define IPU7_NR_OF_CSI2_VC 16U -+#define INVALID_VC_ID -1 -+#define IPU7_NR_OF_CSI2_SINK_PADS 1U -+#define IPU7_CSI2_PAD_SINK 0U -+#define IPU7_NR_OF_CSI2_SRC_PADS 8U -+#define IPU7_CSI2_PAD_SRC 1U -+#define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \ -+ IPU7_NR_OF_CSI2_SRC_PADS) -+ -+/* -+ * struct ipu7_isys_csi2 -+ * -+ * @nlanes: number of lanes in the receiver -+ */ -+struct ipu7_isys_csi2 { -+ struct ipu7_isys_subdev asd; -+ struct ipu7_isys_csi2_pdata *pdata; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS]; -+ -+ void __iomem *base; -+ u32 receiver_errors; -+ u32 legacy_irq_mask; -+ unsigned int nlanes; -+ unsigned int port; -+ unsigned int phy_mode; -+ unsigned int stream_count; -+}; -+ -+#define ipu7_isys_subdev_to_csi2(__sd) \ -+ container_of(__sd, struct ipu7_isys_csi2, asd) -+ -+#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd) -+ -+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2); -+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys, -+ void __iomem *base, unsigned int index); -+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2); -+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream); -+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream); -+int ipu7_isys_csi2_get_remote_desc(u32 source_stream, -+ struct ipu7_isys_csi2 *csi2, -+ struct media_entity *source_entity, -+ struct v4l2_mbus_frame_desc_entry *entry, -+ int *nr_queues); -+#endif /* IPU7_ISYS_CSI2_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -new file mode 100644 -index 000000000000..9cee0fb4440c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -@@ -0,0 +1,1193 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-platform-regs.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include "ipu7-cpd.h" -+#endif -+ -+#define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) -+ -+static int ipu7_isys_buf_init(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ int ret; -+ -+ ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); -+ if (ret) -+ return ret; -+ -+ ivb->dma_addr = sg_dma_address(sg->sgl); -+ -+ return 0; -+} -+ -+static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ -+ ivb->dma_addr = 0; -+ ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0); -+} -+ -+static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, -+ unsigned int *num_planes, unsigned int sizes[], -+ struct device *alloc_devs[]) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ u32 size = av->pix_fmt.sizeimage; -+ -+ /* num_planes == 0: we're being called through VIDIOC_REQBUFS */ -+ if (!*num_planes) { -+ sizes[0] = size; -+ } else if (sizes[0] < size) { -+ dev_dbg(dev, "%s: queue setup: size %u < %u\n", -+ av->vdev.name, sizes[0], size); -+ return -EINVAL; -+ } -+ -+ *num_planes = 1; -+ -+ return 0; -+} -+ -+static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ u32 bytesperline = av->pix_fmt.bytesperline; -+ u32 height = av->pix_fmt.height; -+ -+ dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n", -+ av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0)); -+ -+ if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0)) -+ return -EINVAL; -+ -+ dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n", -+ av->vdev.name, bytesperline, height); -+ vb2_set_plane_payload(vb, 0, bytesperline * height); -+ -+ return 0; -+} -+ -+/* -+ * Queue a buffer list back to incoming or active queues. The buffers -+ * are removed from the buffer list. -+ */ -+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -+ unsigned long op_flags, -+ enum vb2_buffer_state state) -+{ -+ struct ipu7_isys_buffer *ib, *ib_safe; -+ unsigned long flags; -+ bool first = true; -+ -+ if (!bl) -+ return; -+ -+ WARN_ON_ONCE(!bl->nbufs); -+ WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE && -+ op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING); -+ -+ list_for_each_entry_safe(ib, ib_safe, &bl->head, head) { -+ struct ipu7_isys_video *av; -+ -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ struct ipu7_isys_queue *aq = -+ vb2_queue_to_isys_queue(vb->vb2_queue); -+ -+ av = ipu7_isys_queue_to_video(aq); -+ spin_lock_irqsave(&aq->lock, flags); -+ list_del(&ib->head); -+ if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE) -+ list_add(&ib->head, &aq->active); -+ else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING) -+ list_add_tail(&ib->head, &aq->incoming); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE) -+ vb2_buffer_done(vb, state); -+ -+ if (first) { -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "queue buf list %p flags %lx, s %d, %d bufs\n", -+ bl, op_flags, state, bl->nbufs); -+ first = false; -+ } -+ -+ bl->nbufs--; -+ } -+ -+ WARN_ON(bl->nbufs); -+} -+ -+/* -+ * flush_firmware_streamon_fail() - Flush in cases where requests may -+ * have been queued to firmware and the *firmware streamon fails for a -+ * reason or another. -+ */ -+static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_queue *aq; -+ unsigned long flags; -+ -+ lockdep_assert_held(&stream->mutex); -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer *ib, *ib_safe; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ list_for_each_entry_safe(ib, ib_safe, &aq->active, head) { -+ struct vb2_buffer *vb = -+ ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ if (av->streaming) { -+ dev_dbg(dev, -+ "%s: queue buffer %u back to incoming\n", -+ av->vdev.name, vb->index); -+ /* Queue already streaming, return to driver. */ -+ list_add(&ib->head, &aq->incoming); -+ continue; -+ } -+ /* Queue not yet streaming, return to user. */ -+ dev_dbg(dev, "%s: return %u back to videobuf2\n", -+ av->vdev.name, vb->index); -+ vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib), -+ VB2_BUF_STATE_QUEUED); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ } -+} -+ -+/* -+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers -+ * that contains one entry from each video buffer queue. If a buffer can't be -+ * obtained from every queue, the buffers are returned back to the queue. -+ */ -+static int buffer_list_get(struct ipu7_isys_stream *stream, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING; -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu7_isys_queue *aq; -+ unsigned long flags; -+ -+ bl->nbufs = 0; -+ INIT_LIST_HEAD(&bl->head); -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu7_isys_buffer *ib; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ if (list_empty(&aq->incoming)) { -+ spin_unlock_irqrestore(&aq->lock, flags); -+ if (!list_empty(&bl->head)) -+ ipu7_isys_buffer_list_queue(bl, buf_flag, 0); -+ return -ENODATA; -+ } -+ -+ ib = list_last_entry(&aq->incoming, -+ struct ipu7_isys_buffer, head); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ -+ if (av->skipframe) { -+ atomic_set(&ib->skipframe_flag, 1); -+ av->skipframe--; -+ } else { -+ atomic_set(&ib->skipframe_flag, 0); -+ } -+#endif -+ dev_dbg(dev, "buffer: %s: buffer %u\n", -+ ipu7_isys_queue_to_video(aq)->vdev.name, -+ ipu7_isys_buffer_to_vb2_buffer(ib)->index); -+ list_del(&ib->head); -+ list_add(&ib->head, &bl->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ bl->nbufs++; -+ } -+ -+ dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs); -+ -+ return 0; -+} -+ -+static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, -+ struct ipu7_insys_buffset *set) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ -+#ifndef IPU8_INSYS_NEW_ABI -+ set->output_pins[aq->fw_output].addr = ivb->dma_addr; -+ set->output_pins[aq->fw_output].user_token = (uintptr_t)set; -+#else -+ set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; -+ set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; -+ set->output_pins[aq->fw_output].upipe_capture_cfg = 0; -+#endif -+} -+ -+/* -+ * Convert a buffer list to a isys fw ABI framebuffer set. The -+ * buffer list is not modified. -+ */ -+#define IPU_ISYS_FRAME_NUM_THRESHOLD (30) -+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -+ struct ipu7_isys_stream *stream, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ struct ipu7_isys_buffer *ib; -+ u32 buf_id; -+ -+ WARN_ON(!bl->nbufs); -+ -+ set->skip_frame = 0; -+ set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP | -+ IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ; -+ -+ buf_id = atomic_fetch_inc(&stream->buf_id); -+ set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER; -+ -+ list_for_each_entry(ib, &bl->head, head) { -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ ipu7_isys_buf_to_fw_frame_buf_pin(vb, set); -+ } -+} -+ -+/* Start streaming for real. The buffer list must be available. */ -+static int ipu7_isys_stream_start(struct ipu7_isys_video *av, -+ struct ipu7_isys_buffer_list *bl, bool error) -+{ -+ struct ipu7_isys_stream *stream = av->stream; -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer_list __bl; -+ int ret; -+ -+ mutex_lock(&stream->isys->stream_mutex); -+ -+ ret = ipu7_isys_video_set_streaming(av, 1, bl); -+ mutex_unlock(&stream->isys->stream_mutex); -+ if (ret) -+ goto out_requeue; -+ -+ stream->streaming = 1; -+ -+ bl = &__bl; -+ -+ do { -+ struct ipu7_insys_buffset *buf = NULL; -+ struct isys_fw_msgs *msg; -+ enum ipu7_insys_send_type send_type = -+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE; -+ -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) -+ break; -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) -+ return -ENOMEM; -+ -+ buf = &msg->fw_msg.frame; -+ -+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -+ -+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, -+ stream->nr_output_pins); -+ -+ ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, -+ 0); -+ -+ ret = ipu7_fw_isys_complex_cmd(stream->isys, -+ stream->stream_handle, buf, -+ msg->dma_addr, sizeof(*buf), -+ send_type); -+ } while (!WARN_ON(ret)); -+ -+ return 0; -+ -+out_requeue: -+ if (bl && bl->nbufs) -+ ipu7_isys_buffer_list_queue(bl, -+ IPU_ISYS_BUFFER_LIST_FL_INCOMING | -+ (error ? -+ IPU_ISYS_BUFFER_LIST_FL_SET_STATE : -+ 0), error ? VB2_BUF_STATE_ERROR : -+ VB2_BUF_STATE_QUEUED); -+ flush_firmware_streamon_fail(stream); -+ -+ return ret; -+} -+ -+static void buf_queue(struct vb2_buffer *vb) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_video_buffer *ivb = -+ vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ struct media_pipeline *media_pipe = -+ media_entity_pipeline(&av->vdev.entity); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_buffer *ib = &ivb->ib; -+ struct ipu7_insys_buffset *buf = NULL; -+ struct ipu7_isys_buffer_list bl; -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ dma_addr_t dma; -+ int ret; -+ -+ dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name); -+ -+ dma = ivb->dma_addr; -+ dev_dbg(dev, "iova: iova %pad\n", &dma); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ list_add(&ib->head, &aq->incoming); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ if (!media_pipe || !vb->vb2_queue->start_streaming_called) { -+ dev_dbg(dev, "media pipeline is not ready for %s\n", -+ av->vdev.name); -+ return; -+ } -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&av->isys->reset_mutex); -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ dev_dbg(dev, "in reset, adding to incoming\n"); -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ /* ip may be cleared in ipu reset */ -+ stream = av->stream; -+#endif -+ mutex_lock(&stream->mutex); -+ -+ if (stream->nr_streaming != stream->nr_queues) { -+ dev_dbg(dev, "not streaming yet, adding to incoming\n"); -+ goto out; -+ } -+ -+ /* -+ * We just put one buffer to the incoming list of this queue -+ * (above). Let's see whether all queues in the pipeline would -+ * have a buffer. -+ */ -+ ret = buffer_list_get(stream, &bl); -+ if (ret < 0) { -+ dev_dbg(dev, "No buffers available\n"); -+ goto out; -+ } -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ buf = &msg->fw_msg.frame; -+ -+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl); -+ -+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins); -+ -+ if (!stream->streaming) { -+ ret = ipu7_isys_stream_start(av, &bl, true); -+ if (ret) -+ dev_err(dev, "stream start failed.\n"); -+ goto out; -+ } -+ -+ /* -+ * We must queue the buffers in the buffer list to the -+ * appropriate video buffer queues BEFORE passing them to the -+ * firmware since we could get a buffer event back before we -+ * have queued them ourselves to the active queue. -+ */ -+ ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -+ -+ ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle, -+ buf, msg->dma_addr, sizeof(*buf), -+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE); -+ if (ret < 0) -+ dev_err(dev, "send stream capture failed\n"); -+ -+out: -+ mutex_unlock(&stream->mutex); -+} -+ -+static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) -+{ -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *remote_pad = -+ media_pad_remote_pad_first(av->vdev.entity.pads); -+ struct v4l2_mbus_framefmt format; -+ struct v4l2_subdev *sd; -+ u32 r_stream, code; -+ int ret; -+ -+ if (!remote_pad) -+ return -ENOTCONN; -+ -+ sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index); -+ -+ ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream, -+ &format); -+ if (ret) { -+ dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n", -+ sd->entity.name, remote_pad->index, r_stream); -+ return ret; -+ } -+ -+ if (format.width != av->pix_fmt.width || -+ format.height != av->pix_fmt.height) { -+ dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n", -+ av->pix_fmt.width, av->pix_fmt.height, format.width, -+ format.height); -+ return -EINVAL; -+ } -+ -+ code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -+ if (format.code != code) { -+ dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n", -+ code, format.code); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void return_buffers(struct ipu7_isys_queue *aq, -+ enum vb2_buffer_state state) -+{ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ bool need_reset = false; -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+#endif -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ /* -+ * Something went wrong (FW crash / HW hang / not all buffers -+ * returned from isys) if there are still buffers queued in active -+ * queue. We have to clean up places a bit. -+ */ -+ while (!list_empty(&aq->active)) { -+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, state); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ need_reset = true; -+#endif -+ } -+ -+ while (!list_empty(&aq->incoming)) { -+ ib = list_last_entry(&aq->incoming, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, state); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ -+ spin_unlock_irqrestore(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ -+ if (need_reset) { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = true; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+#endif -+} -+ -+static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) -+{ -+ video_device_pipeline_stop(&av->vdev); -+ ipu7_isys_put_stream(av->stream); -+ av->stream = NULL; -+} -+ -+static int start_streaming(struct vb2_queue *q, unsigned int count) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ struct ipu7_isys_buffer_list __bl, *bl = NULL; -+ struct ipu7_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues, ret; -+ -+ dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n", -+ av->vdev.name, av->pix_fmt.width, av->pix_fmt.height, -+ pfmt->css_pixelformat); -+ -+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_dbg(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu7_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_dbg(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu7_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) { -+ mutex_unlock(&stream->mutex); -+ goto out_pipeline_stop; -+ } -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) { -+ dev_warn(dev, "no buffer available, DRIVER BUG?\n"); -+ goto out; -+ } -+ -+ ret = ipu7_isys_fw_open(av->isys); -+ if (ret) -+ goto out_stream_start; -+ -+ ipu7_isys_setup_hw(av->isys); -+ -+ ret = ipu7_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_isys_fw_close; -+ -+out: -+ mutex_unlock(&stream->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->start_streaming = 1; -+#endif -+ -+ return 0; -+ -+out_isys_fw_close: -+ ipu7_isys_fw_close(av->isys); -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ mutex_unlock(&stream->mutex); -+ -+out_pipeline_stop: -+ ipu7_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static void reset_stop_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ mutex_lock(&stream->mutex); -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ while (!list_empty(&aq->active)) { -+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+ -+static int reset_start_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer_list __bl, *bl = NULL; -+ struct ipu7_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues; -+ int ret; -+ -+ dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); -+ -+ av->skipframe = 1; -+ -+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_dbg(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu7_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_dbg(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu7_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) { -+ mutex_unlock(&stream->mutex); -+ goto out_pipeline_stop; -+ } -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ ret = buffer_list_get(stream, bl); -+ /* -+ * In reset start streaming and no buffer available, -+ * it is considered that gstreamer has been closed, -+ * and reset start is no needed, not driver bug. -+ */ -+ if (ret) { -+ dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ goto out_stream_start; -+ } -+ -+ ret = ipu7_isys_fw_open(av->isys); -+ if (ret) -+ goto out_stream_start; -+ -+ ipu7_isys_setup_hw(av->isys); -+ -+ ret = ipu7_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_isys_fw_close; -+ -+out: -+ mutex_unlock(&stream->mutex); -+ av->start_streaming = 1; -+ return 0; -+ -+out_isys_fw_close: -+ ipu7_isys_fw_close(av->isys); -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ mutex_unlock(&stream->mutex); -+ -+out_pipeline_stop: -+ ipu7_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ av->start_streaming = 0; -+ dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); -+ return ret; -+} -+ -+static int ipu_isys_reset(struct ipu7_isys_video *self_av, -+ struct ipu7_isys_stream *self_stream) -+{ -+ struct ipu7_isys *isys = self_av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ struct ipu7_isys_video *av = NULL; -+ struct ipu7_isys_stream *stream = NULL; -+ struct device *dev = &adev->auxdev.dev; -+ int i, j; -+ int has_streaming = 0; -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&isys->reset_mutex); -+ return 0; -+ } -+ isys->state |= RESET_STATE_IN_RESET; -+ dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); -+ -+ while (isys->state & RESET_STATE_IN_STOP_STREAMING) { -+ dev_dbg(dev, "isys reset: %s: wait for stop\n", -+ self_av->vdev.name); -+ mutex_unlock(&isys->reset_mutex); -+ usleep_range(10000, 11000); -+ mutex_lock(&isys->reset_mutex); -+ } -+ -+ mutex_unlock(&isys->reset_mutex); -+ -+ dev_dbg(dev, "reset stop streams\n"); -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ av = &isys->csi2[i].av[j]; -+ if (av == self_av) -+ continue; -+ -+ stream = av->stream; -+ if (!stream || stream == self_stream) -+ continue; -+ -+ if (!stream->streaming && !stream->nr_streaming) -+ continue; -+ -+ av->reset = true; -+ has_streaming = true; -+ reset_stop_streaming(av); -+ } -+ } -+ -+ if (!has_streaming) -+ goto end_of_reset; -+ -+ ipu7_cleanup_fw_msg_bufs(isys); -+ -+ dev_dbg(dev, "reset start streams\n"); -+ -+ for (j = 0; j < csi2_pdata->nports; j++) { -+ for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { -+ av = &isys->csi2[j].av[i]; -+ if (!av->reset) -+ continue; -+ -+ av->reset = false; -+ reset_start_streaming(av); -+ } -+ } -+ -+end_of_reset: -+ mutex_lock(&isys->reset_mutex); -+ isys->state &= ~RESET_STATE_IN_RESET; -+ mutex_unlock(&isys->reset_mutex); -+ dev_dbg(dev, "reset done\n"); -+ -+ return 0; -+} -+ -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ bool need_reset; -+ -+ dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); -+ -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(dev, "stop: %s: wait for rest or stop, isys->state = %d\n", -+ av->vdev.name, av->isys->state); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ -+ if (!av->start_streaming) { -+ mutex_unlock(&av->isys->reset_mutex); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ return; -+ } -+ -+ av->isys->state |= RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ stream = av->stream; -+ if (!stream) { -+ dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } -+ -+ mutex_lock(&stream->mutex); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(dev, "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu7_isys_fw_close(av->isys); -+ -+ av->start_streaming = 0; -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ need_reset = av->isys->need_reset; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ if (need_reset) { -+ if (!stream->nr_streaming) { -+ ipu_isys_reset(av, stream); -+ } else { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = false; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+ } -+ -+ dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); -+} -+#else -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ -+ mutex_lock(&stream->mutex); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(&av->isys->adev->auxdev.dev, -+ "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+#endif -+ -+static unsigned int -+get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) -+{ -+ struct ipu7_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ -+ /* -+ * The timestamp is invalid as no TSC in some FPGA platform, -+ * so get the sequence from pipeline directly in this case. -+ */ -+ if (time == 0) -+ return atomic_read(&stream->sequence) - 1; -+ -+ for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -+ if (time == stream->seq[i].timestamp) { -+ dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n", -+ stream->seq[i].sequence, time); -+ return stream->seq[i].sequence; -+ } -+ -+ dev_dbg(dev, "SOF: looking for %llu\n", time); -+ for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++) -+ dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n", -+ stream->seq[i].sequence, stream->seq[i].timestamp); -+ dev_dbg(dev, "SOF sequence number not found\n"); -+ -+ return atomic_read(&stream->sequence) - 1; -+} -+ -+static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time) -+{ -+ struct ipu7_bus_device *adev = av->isys->adev; -+ struct ipu7_device *isp = adev->isp; -+ u64 delta, tsc_now; -+ -+ ipu_buttress_tsc_read(isp, &tsc_now); -+ if (!tsc_now) -+ return 0; -+ -+ delta = tsc_now - time; -+ -+ return ipu_buttress_tsc_ticks_to_ns(delta, isp); -+} -+ -+static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib, -+ u64 time) -+{ -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ u64 ns; -+ u32 sequence; -+ -+ ns = ktime_get_ns() - get_sof_ns_delta(av, time); -+ sequence = get_sof_sequence_by_timestamp(stream, time); -+ -+ vbuf->vb2_buf.timestamp = ns; -+ vbuf->sequence = sequence; -+ -+ dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n", -+ av->vdev.name, ktime_get_ns(), sequence); -+ dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index, -+ vbuf->vb2_buf.timestamp); -+} -+ -+static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) -+{ -+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ if (atomic_read(&ib->str2mmio_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ /* -+ * Operation on buffer is ended with error and will be reported -+ * to the userspace when it is de-queued -+ */ -+ atomic_set(&ib->str2mmio_flag, 0); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ } else if (atomic_read(&ib->skipframe_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ atomic_set(&ib->skipframe_flag, 0); -+#endif -+ } else { -+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE); -+ } -+} -+ -+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -+ struct ipu7_insys_resp *info) -+{ -+ struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq; -+ u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]); -+ struct ipu7_isys *isys = stream->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ bool first = true; -+ struct vb2_v4l2_buffer *buf; -+ -+ dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n", -+ ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr, -+ info->frame_id); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ if (list_empty(&aq->active)) { -+ spin_unlock_irqrestore(&aq->lock, flags); -+ dev_err(dev, "active queue empty\n"); -+ return; -+ } -+ -+ list_for_each_entry_reverse(ib, &aq->active, head) { -+ struct ipu7_isys_video_buffer *ivb; -+ struct vb2_v4l2_buffer *vvb; -+ dma_addr_t addr; -+ -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ vvb = to_vb2_v4l2_buffer(vb); -+ ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb); -+ addr = ivb->dma_addr; -+ -+ if (info->pin.addr != addr) { -+ if (first) -+ dev_err(dev, "Unexpected buffer address %pad\n", -+ &addr); -+ -+ first = false; -+ continue; -+ } -+ -+ dev_dbg(dev, "buffer: found buffer %pad\n", &addr); -+ -+ buf = to_vb2_v4l2_buffer(vb); -+ buf->field = V4L2_FIELD_NONE; -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu7_isys_buf_calc_sequence_time(ib, time); -+ -+ ipu7_isys_queue_buf_done(ib); -+ -+ return; -+ } -+ -+ dev_err(dev, "Failed to find a matching video buffer\n"); -+ -+ spin_unlock_irqrestore(&aq->lock, flags); -+} -+ -+static const struct vb2_ops ipu7_isys_queue_ops = { -+ .queue_setup = ipu7_isys_queue_setup, -+ .buf_init = ipu7_isys_buf_init, -+ .buf_prepare = ipu7_isys_buf_prepare, -+ .buf_cleanup = ipu7_isys_buf_cleanup, -+ .start_streaming = start_streaming, -+ .stop_streaming = stop_streaming, -+ .buf_queue = buf_queue, -+}; -+ -+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq) -+{ -+ struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys; -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_bus_device *adev = isys->adev; -+ int ret; -+ -+ if (!aq->vbq.io_modes) -+ aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF; -+ -+ aq->vbq.drv_priv = isys; -+ aq->vbq.ops = &ipu7_isys_queue_ops; -+ aq->vbq.lock = &av->mutex; -+ aq->vbq.mem_ops = &vb2_dma_sg_memops; -+ aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ aq->vbq.min_queued_buffers = 1; -+ aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; -+ -+ ret = vb2_queue_init(&aq->vbq); -+ if (ret) -+ return ret; -+ -+ aq->dev = &adev->auxdev.dev; -+ aq->vbq.dev = &adev->isp->pdev->dev; -+ spin_lock_init(&aq->lock); -+ INIT_LIST_HEAD(&aq->active); -+ INIT_LIST_HEAD(&aq->incoming); -+ -+ return 0; -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -new file mode 100644 -index 000000000000..5a909c3a78d2 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.h -@@ -0,0 +1,75 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_QUEUE_H -+#define IPU7_ISYS_QUEUE_H -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+struct device; -+struct ipu7_isys_stream; -+struct ipu7_insys_resp; -+struct ipu7_insys_buffset; -+ -+struct ipu7_isys_queue { -+ struct vb2_queue vbq; -+ struct list_head node; -+ struct device *dev; -+ spinlock_t lock; -+ struct list_head active; -+ struct list_head incoming; -+ unsigned int fw_output; -+}; -+ -+struct ipu7_isys_buffer { -+ struct list_head head; -+ atomic_t str2mmio_flag; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ atomic_t skipframe_flag; -+#endif -+}; -+ -+struct ipu7_isys_video_buffer { -+ struct vb2_v4l2_buffer vb_v4l2; -+ struct ipu7_isys_buffer ib; -+ dma_addr_t dma_addr; -+}; -+ -+#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0) -+#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1) -+#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2) -+ -+struct ipu7_isys_buffer_list { -+ struct list_head head; -+ unsigned int nbufs; -+}; -+ -+#define vb2_queue_to_isys_queue(__vb2) \ -+ container_of(__vb2, struct ipu7_isys_queue, vbq) -+ -+#define ipu7_isys_to_isys_video_buffer(__ib) \ -+ container_of(__ib, struct ipu7_isys_video_buffer, ib) -+ -+#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb) \ -+ container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2) -+ -+#define ipu7_isys_buffer_to_vb2_buffer(__ib) \ -+ (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf) -+ -+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl, -+ unsigned long op_flags, -+ enum vb2_buffer_state state); -+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set, -+ struct ipu7_isys_stream *stream, -+ struct ipu7_isys_buffer_list *bl); -+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream, -+ struct ipu7_insys_resp *info); -+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq); -+#endif /* IPU7_ISYS_QUEUE_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -new file mode 100644 -index 000000000000..98b6ef6a2f21 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.c -@@ -0,0 +1,348 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "ipu7-bus.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-subdev.h" -+ -+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ return MIPI_CSI2_DT_RGB565; -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return MIPI_CSI2_DT_RGB888; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return MIPI_CSI2_DT_YUV422_10B; -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return MIPI_CSI2_DT_YUV422_8B; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return MIPI_CSI2_DT_RAW12; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return MIPI_CSI2_DT_RAW10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return MIPI_CSI2_DT_RAW8; -+ default: -+ WARN_ON(1); -+ return 0xff; -+ } -+} -+ -+bool ipu7_isys_is_bayer_format(u32 code) -+{ -+ switch (ipu7_isys_mbus_code_to_mipi(code)) { -+ case MIPI_CSI2_DT_RAW8: -+ case MIPI_CSI2_DT_RAW10: -+ case MIPI_CSI2_DT_RAW12: -+ case MIPI_CSI2_DT_RAW14: -+ case MIPI_CSI2_DT_RAW16: -+ case MIPI_CSI2_DT_RAW20: -+ case MIPI_CSI2_DT_RAW24: -+ case MIPI_CSI2_DT_RAW28: -+ return true; -+ default: -+ return false; -+ } -+} -+ -+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y) -+{ -+ static const u32 code_map[] = { -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ }; -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(code_map); i++) -+ if (code_map[i] == code) -+ break; -+ -+ if (WARN_ON(i == ARRAY_SIZE(code_map))) -+ return code; -+ -+ return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))]; -+} -+ -+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ u32 code = asd->supported_codes[0]; -+ struct v4l2_mbus_framefmt *fmt; -+ u32 other_pad, other_stream; -+ struct v4l2_rect *crop; -+ unsigned int i; -+ int ret; -+ -+ /* No transcoding, source and sink formats must match. */ -+ if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) && -+ sd->entity.num_pads > 1) -+ return v4l2_subdev_get_fmt(sd, state, format); -+ -+ format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH, -+ IPU_ISYS_MAX_WIDTH); -+ format->format.height = clamp(format->format.height, -+ IPU_ISYS_MIN_HEIGHT, -+ IPU_ISYS_MAX_HEIGHT); -+ -+ for (i = 0; asd->supported_codes[i]; i++) { -+ if (asd->supported_codes[i] == format->format.code) { -+ code = asd->supported_codes[i]; -+ break; -+ } -+ } -+ format->format.code = code; -+ format->format.field = V4L2_FIELD_NONE; -+ -+ /* Store the format and propagate it to the source pad. */ -+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream); -+ if (!fmt) -+ return -EINVAL; -+ -+ *fmt = format->format; -+ -+ if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK)) -+ return 0; -+ -+ /* propagate format to following source pad */ -+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, -+ format->stream); -+ if (!fmt) -+ return -EINVAL; -+ -+ *fmt = format->format; -+ -+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, -+ format->pad, -+ format->stream, -+ &other_pad, -+ &other_stream); -+ if (ret) -+ return -EINVAL; -+ -+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); -+ /* reset crop */ -+ crop->left = 0; -+ crop->top = 0; -+ crop->width = fmt->width; -+ crop->height = fmt->height; -+ -+ return 0; -+} -+ -+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum *code) -+{ -+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd); -+ const u32 *supported_codes = asd->supported_codes; -+ u32 index; -+ -+ for (index = 0; supported_codes[index]; index++) { -+ if (index == code->index) { -+ code->code = supported_codes[index]; -+ return 0; -+ } -+ } -+ -+ return -EINVAL; -+} -+ -+static int subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_krouting *routing) -+{ -+ static const struct v4l2_mbus_framefmt fmt = { -+ .width = 4096, -+ .height = 3072, -+ .code = MEDIA_BUS_FMT_SGRBG10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ int ret; -+ -+ ret = v4l2_subdev_routing_validate(sd, routing, -+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); -+ if (ret) -+ return ret; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt); -+} -+ -+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_mbus_framefmt *format) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_mbus_framefmt *fmt; -+ -+ if (!sd || !format) -+ return -EINVAL; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ fmt = v4l2_subdev_state_get_format(state, pad, stream); -+ if (fmt) -+ *format = *fmt; -+ v4l2_subdev_unlock_state(state); -+ -+ return fmt ? 0 : -EINVAL; -+} -+ -+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev_route *routes; -+ u32 source_stream = 0; -+ unsigned int i; -+ -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ if (!state) -+ return 0; -+ -+ routes = state->routing.routes; -+ for (i = 0; i < state->routing.num_routes; i++) { -+ if (routes[i].source_pad == pad) { -+ source_stream = routes[i].source_stream; -+ break; -+ } -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ return source_stream; -+} -+ -+static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route route = { -+ .sink_pad = 0, -+ .sink_stream = 0, -+ .source_pad = 1, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ }; -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = &route, -+ }; -+ -+ return subdev_set_routing(sd, state, &routing); -+} -+ -+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing) -+{ -+ return subdev_set_routing(sd, state, routing); -+} -+ -+static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = { -+ .init_state = ipu7_isys_subdev_init_state, -+}; -+ -+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -+ const struct v4l2_subdev_ops *ops, -+ unsigned int nr_ctrls, -+ unsigned int num_sink_pads, -+ unsigned int num_source_pads) -+{ -+ unsigned int num_pads = num_sink_pads + num_source_pads; -+ unsigned int i; -+ int ret; -+ -+ v4l2_subdev_init(&asd->sd, ops); -+ -+ asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | -+ V4L2_SUBDEV_FL_HAS_EVENTS | -+ V4L2_SUBDEV_FL_STREAMS; -+ asd->sd.owner = THIS_MODULE; -+ asd->sd.dev = &asd->isys->adev->auxdev.dev; -+ asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; -+ asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops; -+ -+ asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads, -+ sizeof(*asd->pad), GFP_KERNEL); -+ if (!asd->pad) -+ return -ENOMEM; -+ -+ for (i = 0; i < num_sink_pads; i++) -+ asd->pad[i].flags = MEDIA_PAD_FL_SINK | -+ MEDIA_PAD_FL_MUST_CONNECT; -+ -+ for (i = num_sink_pads; i < num_pads; i++) -+ asd->pad[i].flags = MEDIA_PAD_FL_SOURCE; -+ -+ ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad); -+ if (ret) { -+ pr_err("isys subdev init failed %d.\n", ret); -+ return ret; -+ } -+ -+ if (asd->ctrl_init) { -+ ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls); -+ if (ret) -+ goto out_media_entity_cleanup; -+ -+ asd->ctrl_init(&asd->sd); -+ if (asd->ctrl_handler.error) { -+ ret = asd->ctrl_handler.error; -+ goto out_v4l2_ctrl_handler_free; -+ } -+ -+ asd->sd.ctrl_handler = &asd->ctrl_handler; -+ } -+ -+ asd->source = -1; -+ -+ return 0; -+ -+out_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(&asd->ctrl_handler); -+ -+out_media_entity_cleanup: -+ media_entity_cleanup(&asd->sd.entity); -+ -+ return ret; -+} -+ -+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd) -+{ -+ media_entity_cleanup(&asd->sd.entity); -+ v4l2_ctrl_handler_free(&asd->ctrl_handler); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -new file mode 100644 -index 000000000000..aeefbb515807 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-subdev.h -@@ -0,0 +1,56 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_SUBDEV_H -+#define IPU7_ISYS_SUBDEV_H -+ -+#include -+ -+#include -+#include -+#include -+ -+struct ipu7_isys; -+ -+struct ipu7_isys_subdev { -+ struct v4l2_subdev sd; -+ struct ipu7_isys *isys; -+ u32 const *supported_codes; -+ struct media_pad *pad; -+ struct v4l2_ctrl_handler ctrl_handler; -+ void (*ctrl_init)(struct v4l2_subdev *sd); -+ int source; /* SSI stream source; -1 if unset */ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ bool is_tpg; -+#endif -+}; -+ -+#define to_ipu7_isys_subdev(__sd) \ -+ container_of(__sd, struct ipu7_isys_subdev, sd) -+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code); -+bool ipu7_isys_is_bayer_format(u32 code); -+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y); -+ -+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_format *format); -+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ struct v4l2_subdev_mbus_code_enum -+ *code); -+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad); -+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream, -+ struct v4l2_mbus_framefmt *format); -+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state, -+ enum v4l2_subdev_format_whence which, -+ struct v4l2_subdev_krouting *routing); -+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd, -+ const struct v4l2_subdev_ops *ops, -+ unsigned int nr_ctrls, -+ unsigned int num_sink_pads, -+ unsigned int num_source_pads); -+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd); -+#endif /* IPU7_ISYS_SUBDEV_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -new file mode 100644 -index 000000000000..35b6298e4f8c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.c -@@ -0,0 +1,693 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-tpg.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-platform-regs.h" -+ -+static const u32 tpg_supported_codes[] = { -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ 0, -+}; -+ -+#define IPU_ISYS_FREQ 533000000UL -+ -+static u32 isys_mbus_code_to_bpp(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return 24; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return 20; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return 16; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return 12; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return 10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return 8; -+ default: -+ WARN_ON(1); -+ return 0; -+ } -+} -+ -+static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { -+ .s_stream = tpg_set_stream, -+}; -+ -+static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, -+ struct -+ ipu7_isys_subdev, -+ ctrl_handler), -+ struct ipu7_isys_tpg, asd); -+ switch (ctrl->id) { -+ case V4L2_CID_HBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_HBLANK); -+ break; -+ case V4L2_CID_VBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_VBLANK); -+ break; -+ case V4L2_CID_TEST_PATTERN: -+ writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); -+ break; -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { -+ .s_ctrl = ipu7_isys_tpg_s_ctrl, -+}; -+ -+static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) -+{ -+ return MGC_PPC * IPU_ISYS_FREQ / bpp; -+} -+ -+static const char *const tpg_mode_items[] = { -+ "Ramp", -+ "Checkerboard", -+ "Monochrome per frame", -+ "Color palette", -+}; -+ -+static struct v4l2_ctrl_config tpg_mode = { -+ .ops = &ipu7_isys_tpg_ctrl_ops, -+ .id = V4L2_CID_TEST_PATTERN, -+ .name = "Test Pattern", -+ .type = V4L2_CTRL_TYPE_MENU, -+ .min = TPG_MODE_RAMP, -+ .max = ARRAY_SIZE(tpg_mode_items) - 1, -+ .def = TPG_MODE_COLOR_PALETTE, -+ .menu_skip_mask = 0x2, -+ .qmenu = tpg_mode_items, -+}; -+ -+static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ int hblank; -+ u64 default_pixel_rate; -+ -+ hblank = 1024; -+ -+ tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_HBLANK, 8, 65535, 1, hblank); -+ -+ tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_VBLANK, 8, 65535, 1, 1024); -+ -+ default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); -+ tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_PIXEL_RATE, -+ default_pixel_rate, -+ default_pixel_rate, -+ 1, default_pixel_rate); -+ if (tpg->pixel_rate) { -+ tpg->pixel_rate->cur.val = default_pixel_rate; -+ tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ } -+ -+ v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); -+} -+ -+static int tpg_sd_init_cfg(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route routes[] = { -+ { -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ } -+ }; -+ -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = routes, -+ }; -+ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_SBGGR10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); -+} -+ -+static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { -+ .init_state = tpg_sd_init_cfg, -+}; -+ -+static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu7_isys_subdev_set_fmt, -+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -+}; -+ -+static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+}; -+ -+/* V4L2 subdev core operations */ -+static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { -+ .subscribe_event = subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static const struct v4l2_subdev_ops tpg_sd_ops = { -+ .core = &tpg_sd_core_ops, -+ .video = &tpg_sd_video_ops, -+ .pad = &tpg_sd_pad_ops, -+}; -+ -+static struct media_entity_operations tpg_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ struct video_device *vdev = tpg->asd.sd.devnode; -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "sof_event::tpg-%i sequence: %i\n", tpg->index, -+ ev.u.frame_sync.frame_sequence); -+} -+ -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "eof_event::tpg-%i sequence: %i\n", -+ tpg->index, frame_sequence); -+} -+ -+#define DEFAULT_VC_ID 0 -+static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) -+{ -+ return false; -+} -+ -+static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, -+ void __iomem *mg_base) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "---------MGC REG DUMP START----------"); -+ -+ dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", -+ MGC_MG_CSI_ADAPT_LAYER_TYPE, -+ readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); -+ dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); -+ dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); -+ dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); -+ dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MULTI_DTYPES_MODE, -+ readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); -+ dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", -+ MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); -+ dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", -+ MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); -+ dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", -+ MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); -+ dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", -+ MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); -+ dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); -+ dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", -+ readl(mg_base + MGC_MG_TPG_R0), -+ readl(mg_base + MGC_MG_TPG_G0), -+ readl(mg_base + MGC_MG_TPG_B0)); -+ dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", -+ readl(mg_base + MGC_MG_TPG_R1), -+ readl(mg_base + MGC_MG_TPG_G1), -+ readl(mg_base + MGC_MG_TPG_B1)); -+ dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); -+ dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", -+ MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); -+ dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", -+ MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_EN, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_INCR_VAL, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); -+ dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", -+ MGC_MG_FRAME_NUM_STTS, -+ readl(mg_base + MGC_MG_FRAME_NUM_STTS)); -+ -+ dev_dbg(dev, "---------MGC REG DUMP END----------"); -+} -+ -+#define TPG_STOP_TIMEOUT 500000 -+static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ unsigned int port; -+ u32 status; -+ void __iomem *reg; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ -+ port = 1 << tpg->index; -+ -+ dev_dbg(dev, "MG%d generated %u frames", tpg->index, -+ readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); -+ writel(port, mgc_base + MGC_ASYNC_STOP); -+ -+ dev_dbg(dev, "wait for MG%d stop", tpg->index); -+ -+ reg = mg_base + MGC_MG_STOPPED_STTS; -+ ret = readl_poll_timeout(reg, status, status & 0x1, 200, -+ TPG_STOP_TIMEOUT); -+ if (ret < 0) { -+ dev_err(dev, "mgc stop timeout"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "MG%d STOPPED", tpg->index); -+ -+ return 0; -+} -+ -+#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) -+#define TPG_FRAME_RATE 30 -+#define TPG_BLANK_RATIO (4 / 3) -+static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, -+ u32 *hblank_cycles, u32 *vblank_cycles) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 width, height; -+ u32 code; -+ u32 bpp; -+ u32 bits_per_line; -+ u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; -+ u64 vblank_us, hblank_us; -+ u32 ref_clk; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ u32 dto_incr_val = 0x100; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return; -+ -+ dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); -+ bits_per_line = width * bpp * TPG_BLANK_RATIO; -+ -+ cycles = div_u64(bits_per_line, 64); -+ dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, -+ bits_per_line, cycles); -+ -+ do { -+ dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, -+ dto_incr_val); -+ rate = div_u64(1 << 16, dto_incr_val); -+ ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); -+ dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, -+ ns_per_cycle); -+ -+ line_time_ns = cycles * ns_per_cycle; -+ frame_time_us = line_time_ns * height / 1000; -+ dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", -+ tpg->index, line_time_ns, frame_time_us); -+ -+ if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) -+ break; -+ -+ /* dto incr val step 0x100 */ -+ dto_incr_val += 0x100; -+ } while (dto_incr_val < (1 << 16)); -+ -+ if (dto_incr_val >= (1 << 16)) { -+ dev_warn(dev, "No DTO_INCR_VAL found\n"); -+ hblank_us = 10; /* 10us */ -+ vblank_us = 10000; /* 10ms */ -+ dto_incr_val = 0x1000; -+ } else { -+ hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; -+ vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; -+ } -+ -+ dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", -+ hblank_us, vblank_us, dto_incr_val); -+ -+ ref_clk = tpg->isys->adev->isp->buttress.ref_clk; -+ -+ *dto = dto_incr_val; -+ *hblank_cycles = hblank_us * ref_clk / 10; -+ *vblank_cycles = vblank_us * ref_clk / 10; -+ dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", -+ *hblank_cycles, *vblank_cycles); -+} -+ -+static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 port_map; -+ u32 csi_port; -+ u32 code, bpp; -+ u32 width, height; -+ u32 dto, hblank, vblank; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return ret; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", -+ tpg->index, code, width, height); -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return -EINVAL; -+ -+ csi_port = tpg->index; -+ if (csi_port >= 4) -+ dev_err(dev, "invalid tpg index %u\n", tpg->index); -+ -+ dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", -+ DEFAULT_VC_ID, csi_port); -+ -+ /* config port map -+ * TODO: add VC support and TPG with multiple -+ * source pads. Currently, for simplicity, only map 1 mg to 1 csi port -+ */ -+ port_map = 1 << tpg->index; -+ writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); -+ -+ /* configure adapt layer type */ -+ writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); -+ -+ /* configure MGC mode -+ * 0 - Disable MGC -+ * 1 - Enable PRBS -+ * 2 - Enable TPG -+ * 3 - Reserved [Write phase: SW/FW debug] -+ */ -+ writel(2, mg_base + MGC_MG_MODE); -+ -+ /* config mg init counter */ -+ writel(0, mg_base + MGC_MG_INIT_COUNTER); -+ -+ /* -+ * configure virtual channel -+ * TODO: VC support if need -+ * currently each MGC just uses 1 virtual channel -+ */ -+ writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); -+ -+ /* -+ * configure data type and multi dtypes mode -+ * TODO: it needs to add the metedata flow. -+ */ -+ if (is_metadata_enabled(tpg)) { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } else { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } -+ -+ /* -+ * configure frame information -+ */ -+ writel(0, mg_base + MGC_MG_NOF_FRAMES); -+ writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); -+ -+ tpg_get_timing(tpg, &dto, &hblank, &vblank); -+ writel(hblank, mg_base + MGC_MG_HBLANK); -+ writel(vblank, mg_base + MGC_MG_VBLANK); -+ -+ /* -+ * configure tpg mode, colors, mask, tile dimension -+ * Mode was set by user configuration -+ * 0 - Ramp mode -+ * 1 - Checkerboard -+ * 2 - Monochrome per frame -+ * 3 - Color palette -+ */ -+ writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); -+ -+ /* red and green for checkerboard, n/a for other modes */ -+ writel(58, mg_base + MGC_MG_TPG_R0); -+ writel(122, mg_base + MGC_MG_TPG_G0); -+ writel(46, mg_base + MGC_MG_TPG_B0); -+ writel(123, mg_base + MGC_MG_TPG_R1); -+ writel(85, mg_base + MGC_MG_TPG_G1); -+ writel(67, mg_base + MGC_MG_TPG_B1); -+ -+ writel(0x0, mg_base + MGC_MG_TPG_FACTORS); -+ -+ /* hor_mask [15:0] ver_mask [31:16] */ -+ writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); -+ /* xy_mask [11:0] */ -+ writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); -+ writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), -+ mg_base + MGC_MG_TPG_TILE_DIM); -+ -+ writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); -+ writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); -+ -+ /* disable err_injection */ -+ writel(0, mg_base + MGC_MG_ERR_INJECT); -+ writel(0, mg_base + MGC_MG_ERR_LOCATION); -+ -+ ipu7_mipigen_regdump(tpg, mg_base); -+ -+ dev_dbg(dev, "starting MG%d streaming...\n", csi_port); -+ -+ /* kick and start */ -+ writel(port_map, mgc_base + MGC_KICK); -+ -+ return 0; -+} -+ -+static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct ipu7_isys_csi2 *csi2; -+ u32 offset; -+ struct ipu7_isys *isys = tpg->isys; -+ -+ csi2 = &isys->csi2[tpg->index]; -+ offset = IS_IO_GPREGS_BASE; -+ -+ /* MGC is in use by SW or not */ -+ if (enable) -+ writel(1, csi2->base + offset + MGC_CLK_GATE); -+ else -+ writel(0, csi2->base + offset + MGC_CLK_GATE); -+} -+ -+static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ struct ipu7_isys *isys = tpg->isys; -+ struct ipu7_isys_csi2 *csi2; -+ u32 port, offset, val; -+ void __iomem *isys_base = isys->pdata->base; -+ -+ port = tpg->index; -+ csi2 = &isys->csi2[port]; -+ -+ offset = IS_IO_GPREGS_BASE; -+ val = readl(isys_base + offset + CSI_PORT_CLK_GATE); -+ dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); -+ -+ if (!enable) { -+ writel(~(1 << port) & val, -+ isys_base + offset + CSI_PORT_CLK_GATE); -+ return; -+ } -+ -+ /* set csi port is using by SW */ -+ writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); -+ /* input is coming from MGC */ -+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -+ writel(CSI_MIPIGEN_INPUT, -+ csi2->base + offset + CSI2_ADPL_INPUT_MODE); -+} -+ -+/* TODO: add the processing of vc */ -+int tpg_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ struct ipu7_isys_stream *stream = tpg->av->stream; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ -+ if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { -+ dev_err(dev, "invalid MGC index %d\n", tpg->index); -+ return -EINVAL; -+ } -+ -+ if (!enable) { -+ /* Stop MGC */ -+ stream->asd->is_tpg = false; -+ stream->asd = NULL; -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ ret = tpg_stop_stream(tpg); -+ ipu7_isys_ungate_mgc(tpg, enable); -+ -+ return ret; -+ } -+ -+ stream->asd = &tpg->asd; -+ /* ungate the MGC clock to program */ -+ ipu7_isys_ungate_mgc(tpg, enable); -+ /* Start MGC */ -+ ret = tpg_start_stream(tpg); -+ v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ -+ return ret; -+} -+ -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) -+{ -+ v4l2_device_unregister_subdev(&tpg->asd.sd); -+ ipu7_isys_subdev_cleanup(&tpg->asd); -+} -+ -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ tpg->isys = isys; -+ tpg->base = base; -+ tpg->sel = sel; -+ tpg->index = index; -+ -+ tpg->asd.sd.entity.ops = &tpg_entity_ops; -+ tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; -+ tpg->asd.isys = isys; -+ -+ ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, -+ NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); -+ if (ret) -+ return ret; -+ -+ tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; -+ tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -+ -+ tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; -+ tpg->asd.supported_codes = tpg_supported_codes; -+ tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; -+ -+ snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), -+ IPU_ISYS_ENTITY_PREFIX " TPG %u", index); -+ v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); -+ -+ ret = v4l2_subdev_init_finalize(&tpg->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to finalize subdev (%d)\n", ret); -+ goto fail; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); -+ if (ret) { -+ dev_info(dev, "can't register v4l2 subdev\n"); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ ipu7_isys_tpg_cleanup(tpg); -+ -+ return ret; -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -new file mode 100644 -index 000000000000..e2542a6472e3 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-tpg.h -@@ -0,0 +1,70 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_TPG_H -+#define IPU7_ISYS_TPG_H -+ -+#include -+#include -+#include -+ -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-queue.h" -+ -+struct ipu7_isys_tpg_pdata; -+struct ipu7_isys; -+ -+#define TPG_PAD_SOURCE 0 -+#define NR_OF_TPG_PADS 1 -+#define NR_OF_TPG_SOURCE_PADS 1 -+#define NR_OF_TPG_SINK_PADS 0 -+#define NR_OF_TPG_STREAMS 1 -+ -+enum isys_tpg_mode { -+ TPG_MODE_RAMP = 0, -+ TPG_MODE_CHECKERBOARD = 1, -+ TPG_MODE_MONO = 2, -+ TPG_MODE_COLOR_PALETTE = 3, -+}; -+ -+/* -+ * struct ipu7_isys_tpg -+ * -+ * @nlanes: number of lanes in the receiver -+ */ -+struct ipu7_isys_tpg { -+ struct ipu7_isys_subdev asd; -+ struct ipu7_isys_tpg_pdata *pdata; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_video *av; -+ -+ /* MG base not MGC */ -+ void __iomem *base; -+ void __iomem *sel; -+ unsigned int index; -+ -+ struct v4l2_ctrl *hblank; -+ struct v4l2_ctrl *vblank; -+ struct v4l2_ctrl *pixel_rate; -+}; -+ -+#define ipu7_isys_subdev_to_tpg(__sd) \ -+ container_of(__sd, struct ipu7_isys_tpg, asd) -+ -+#define to_ipu7_isys_tpg(sd) \ -+ container_of(to_ipu7_isys_subdev(sd), \ -+ struct ipu7_isys_tpg, asd) -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, -+ struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index); -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); -+int tpg_set_stream(struct v4l2_subdev *sd, int enable); -+ -+#endif /* IPU7_ISYS_TPG_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.c b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -new file mode 100644 -index 000000000000..12fdf556c656 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-video.c -@@ -0,0 +1,1311 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-platform-regs.h" -+ -+const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { -+ {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW16}, -+ {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8, -+ IPU_INSYS_FRAME_FORMAT_RAW8}, -+ {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12, -+ IPU_INSYS_FRAME_FORMAT_RAW12}, -+ {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10, -+ IPU_INSYS_FRAME_FORMAT_RAW10}, -+ {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16, -+ IPU_INSYS_FRAME_FORMAT_UYVY}, -+ {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16, -+ IPU_INSYS_FRAME_FORMAT_YUYV}, -+ {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16, -+ IPU_INSYS_FRAME_FORMAT_RGB565}, -+ {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24, -+ IPU_INSYS_FRAME_FORMAT_RGBA888}, -+}; -+ -+static int video_open(struct file *file) -+{ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = video_drvdata(file); -+ struct ipu7_isys *isys = av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -+ return -EIO; -+ } -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif -+ return v4l2_fh_open(file); -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static int video_release(struct file *file) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: enter\n", av->vdev.name); -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: wait for reset\n", av->vdev.name); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ return vb2_fop_release(file); -+} -+ -+#endif -+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -+ const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i]; -+ -+ if (pfmt->pixelformat == pixelformat) -+ return pfmt; -+ } -+ -+ return &ipu7_isys_pfmts[0]; -+} -+ -+static int ipu7_isys_vidioc_querycap(struct file *file, void *fh, -+ struct v4l2_capability *cap) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver)); -+ strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card)); -+ -+ return 0; -+} -+ -+static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh, -+ struct v4l2_fmtdesc *f) -+{ -+ unsigned int i, num_found; -+ -+ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) -+ continue; -+ -+ if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code) -+ continue; -+ -+ if (num_found < f->index) { -+ num_found++; -+ continue; -+ } -+ -+ f->flags = 0; -+ f->pixelformat = ipu7_isys_pfmts[i].pixelformat; -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh, -+ struct v4l2_frmsizeenum *fsize) -+{ -+ unsigned int i; -+ -+ if (fsize->index > 0) -+ return -EINVAL; -+ -+ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) { -+ if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat) -+ continue; -+ -+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; -+ fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH; -+ fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH; -+ fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT; -+ fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT; -+ fsize->stepwise.step_width = 2; -+ fsize->stepwise.step_height = 2; -+ -+ return 0; -+ } -+ -+ return -EINVAL; -+} -+ -+static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ f->fmt.pix = av->pix_fmt; -+ -+ return 0; -+} -+ -+static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type, -+ u32 *format, u32 *width, u32 *height, -+ u32 *bytesperline, u32 *sizeimage) -+{ -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(*format); -+ -+ *format = pfmt->pixelformat; -+ *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH); -+ *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT); -+ -+ if (pfmt->bpp != pfmt->bpp_packed) -+ *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE); -+ else -+ *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE); -+ -+ *bytesperline = ALIGN(*bytesperline, 64U); -+ -+ /* -+ * (height + 1) * bytesperline due to a hardware issue: the DMA unit -+ * is a power of two, and a line should be transferred as few units -+ * as possible. The result is that up to line length more data than -+ * the image size may be transferred to memory after the image. -+ * Another limitation is the GDA allocation unit size. For low -+ * resolution it gives a bigger number. Use larger one to avoid -+ * memory corruption. -+ */ -+ *sizeimage = *bytesperline * *height + -+ max(*bytesperline, -+ av->isys->pdata->ipdata->isys_dma_overshoot); -+} -+ -+static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av, -+ struct v4l2_format *f) -+{ -+ ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat, -+ &f->fmt.pix.width, &f->fmt.pix.height, -+ &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage); -+ -+ f->fmt.pix.field = V4L2_FIELD_NONE; -+ f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW; -+ f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; -+ f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT; -+ f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT; -+} -+ -+static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ if (vb2_is_busy(&av->aq.vbq)) -+ return -EBUSY; -+ -+ __ipu_isys_vidioc_try_fmt_vid_cap(av, f); -+ -+ return 0; -+} -+ -+static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh, -+ struct v4l2_format *f) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f); -+ av->pix_fmt = f->fmt.pix; -+ -+ return 0; -+} -+ -+static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv, -+ struct v4l2_requestbuffers *p) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ int ret; -+ -+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type); -+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type); -+ -+ ret = vb2_queue_change_type(&av->aq.vbq, p->type); -+ if (ret) -+ return ret; -+ -+ return vb2_ioctl_reqbufs(file, priv, p); -+} -+ -+static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv, -+ struct v4l2_create_buffers *p) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ int ret; -+ -+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type); -+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type); -+ -+ ret = vb2_queue_change_type(&av->aq.vbq, p->format.type); -+ if (ret) -+ return ret; -+ -+ return vb2_ioctl_create_bufs(file, priv, p); -+} -+ -+static int link_validate(struct media_link *link) -+{ -+ struct ipu7_isys_video *av = -+ container_of(link->sink, struct ipu7_isys_video, pad); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct v4l2_subdev_state *s_state; -+ struct v4l2_mbus_framefmt *s_fmt; -+ struct v4l2_subdev *s_sd; -+ struct media_pad *s_pad; -+ u32 s_stream, code; -+ int ret = -EPIPE; -+ -+ if (!link->source->entity) -+ return ret; -+ -+ s_sd = media_entity_to_v4l2_subdev(link->source->entity); -+ s_state = v4l2_subdev_get_unlocked_active_state(s_sd); -+ if (!s_state) -+ return ret; -+ -+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n", -+ link->source->entity->name, link->source->index, -+ link->sink->entity->name); -+ -+ s_pad = media_pad_remote_pad_first(&av->pad); -+ s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index); -+ -+ v4l2_subdev_lock_state(s_state); -+ -+ s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream); -+ if (!s_fmt) { -+ dev_err(dev, "failed to get source pad format\n"); -+ goto unlock; -+ } -+ -+ code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code; -+ -+ if (s_fmt->width != av->pix_fmt.width || -+ s_fmt->height != av->pix_fmt.height || s_fmt->code != code) { -+ dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n", -+ s_fmt->width, s_fmt->height, s_fmt->code, -+ av->pix_fmt.width, av->pix_fmt.height, code); -+ goto unlock; -+ } -+ -+ v4l2_subdev_unlock_state(s_state); -+ -+ return 0; -+unlock: -+ v4l2_subdev_unlock_state(s_state); -+ -+ return ret; -+} -+ -+static void get_stream_opened(struct ipu7_isys_video *av) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&av->isys->streams_lock, flags); -+ av->isys->stream_opened++; -+ spin_unlock_irqrestore(&av->isys->streams_lock, flags); -+} -+ -+static void put_stream_opened(struct ipu7_isys_video *av) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&av->isys->streams_lock, flags); -+ av->isys->stream_opened--; -+ spin_unlock_irqrestore(&av->isys->streams_lock, flags); -+} -+ -+static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, -+ struct ipu7_insys_stream_cfg *cfg) -+{ -+ struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad); -+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity); -+ struct ipu7_isys_stream *stream = av->stream; -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ struct ipu7_insys_output_pin *output_pin; -+ struct ipu7_insys_input_pin *input_pin; -+ int input_pins = cfg->nof_input_pins++; -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct ipu7_isys *isys = av->isys; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct v4l2_mbus_framefmt fmt; -+ int output_pins; -+ u32 src_stream; -+ int ret; -+ -+ src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index); -+ ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream, -+ &fmt); -+ if (ret < 0) { -+ dev_err(dev, "can't get stream format (%d)\n", ret); -+ return ret; -+ } -+ -+ input_pin = &cfg->input_pins[input_pins]; -+ input_pin->input_res.width = fmt.width; -+ input_pin->input_res.height = fmt.height; -+ input_pin->dt = av->dt; -+ input_pin->disable_mipi_unpacking = 0; -+ pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE) -+ input_pin->disable_mipi_unpacking = 1; -+ input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE; -+ input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME; -+ /* if enable polling isys interrupt, the follow values maybe set */ -+ input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | -+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED; -+ -+ output_pins = cfg->nof_output_pins++; -+ aq->fw_output = output_pins; -+ stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready; -+ stream->output_pins[output_pins].aq = aq; -+ -+ output_pin = &cfg->output_pins[output_pins]; -+ /* output pin msg link */ -+ output_pin->link.buffer_lines = 0; -+ output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE; -+ output_pin->link.granularity_pointer_update = 0; -+ output_pin->link.msg_link_streaming_mode = -+ IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF; -+ -+ output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE; -+ output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE; -+ output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM; -+ output_pin->link.use_sw_managed = 1; -+ /* TODO: set the snoop bit for metadata capture */ -+ output_pin->link.is_snoop = 0; -+ -+ /* output pin crop */ -+ output_pin->crop.line_top = 0; -+ output_pin->crop.line_bottom = 0; -+#ifdef IPU8_INSYS_NEW_ABI -+ output_pin->crop.column_left = 0; -+ output_pin->crop.column_right = 0; -+#endif -+ -+ /* output de-compression */ -+ output_pin->dpcm.enable = 0; -+ -+#ifdef IPU8_INSYS_NEW_ABI -+ /* upipe_cfg */ -+ output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; -+ output_pin->upipe_pin_cfg.plane_offset_1 = 0; -+ output_pin->upipe_pin_cfg.plane_offset_2 = 0; -+ output_pin->upipe_pin_cfg.single_uob_fifo = 0; -+ output_pin->upipe_pin_cfg.shared_uob_fifo = 0; -+ output_pin->upipe_enable = 0; -+ output_pin->binning_factor = 0; -+ /* stupid setting, even unused, SW still need to set a valid value */ -+ output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; -+#endif -+ -+ /* frame format type */ -+ pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ output_pin->ft = (u16)pfmt->css_pixelformat; -+ -+ /* stride in bytes */ -+ output_pin->stride = av->pix_fmt.bytesperline; -+ output_pin->send_irq = 1; -+ output_pin->early_ack_en = 0; -+ -+ /* input pin id */ -+ output_pin->input_pin_id = input_pins; -+ -+ return 0; -+} -+ -+/* Create stream and start it using the CSS FW ABI. */ -+static int start_stream_firmware(struct ipu7_isys_video *av, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_insys_stream_cfg *stream_cfg; -+ struct ipu7_insys_buffset *buf = NULL; -+ struct isys_fw_msgs *msg = NULL; -+ struct ipu7_isys_queue *aq; -+ int ret, retout, tout; -+ u16 send_type; -+ -+ if (WARN_ON(!bl)) -+ return -EIO; -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) -+ return -ENOMEM; -+ -+ stream_cfg = &msg->fw_msg.stream; -+ stream_cfg->port_id = stream->stream_source; -+ stream_cfg->vc = stream->vc; -+ stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP | -+ IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ; -+ -+ list_for_each_entry(aq, &stream->queues, node) { -+ struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq); -+ -+ ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg); -+ if (ret < 0) { -+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -+ return ret; -+ } -+ } -+ -+ ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg); -+ -+ stream->nr_output_pins = stream_cfg->nof_output_pins; -+ -+ reinit_completion(&stream->stream_open_completion); -+ -+ ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, -+ stream_cfg, msg->dma_addr, -+ sizeof(*stream_cfg), -+ IPU_INSYS_SEND_TYPE_STREAM_OPEN); -+ if (ret < 0) { -+ dev_err(dev, "can't open stream (%d)\n", ret); -+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -+ return ret; -+ } -+ -+ get_stream_opened(av); -+ -+ tout = wait_for_completion_timeout(&stream->stream_open_completion, -+ FW_CALL_TIMEOUT_JIFFIES); -+ -+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg); -+ -+ if (!tout) { -+ dev_err(dev, "stream open time out\n"); -+ ret = -ETIMEDOUT; -+ goto out_put_stream_opened; -+ } -+ if (stream->error) { -+ dev_err(dev, "stream open error: %d\n", stream->error); -+ ret = -EIO; -+ goto out_put_stream_opened; -+ } -+ dev_dbg(dev, "start stream: open complete\n"); -+ -+ msg = ipu7_get_fw_msg_buf(stream); -+ if (!msg) { -+ ret = -ENOMEM; -+ goto out_put_stream_opened; -+ } -+ buf = &msg->fw_msg.frame; -+ -+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl); -+ ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0); -+ -+ reinit_completion(&stream->stream_start_completion); -+ -+ send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE; -+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, -+ stream_cfg->nof_output_pins); -+ ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf, -+ msg->dma_addr, sizeof(*buf), -+ send_type); -+ if (ret < 0) { -+ dev_err(dev, "can't start streaming (%d)\n", ret); -+ goto out_stream_close; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_start_completion, -+ FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) { -+ dev_err(dev, "stream start time out\n"); -+ ret = -ETIMEDOUT; -+ goto out_stream_close; -+ } -+ if (stream->error) { -+ dev_err(dev, "stream start error: %d\n", stream->error); -+ ret = -EIO; -+ goto out_stream_close; -+ } -+ dev_dbg(dev, "start stream: complete\n"); -+ -+ return 0; -+ -+out_stream_close: -+ reinit_completion(&stream->stream_close_completion); -+ -+ retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -+ if (retout < 0) { -+ dev_dbg(dev, "can't close stream (%d)\n", retout); -+ goto out_put_stream_opened; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_close_completion, -+ FW_CALL_TIMEOUT_JIFFIES); -+ if (!tout) -+ dev_err(dev, "stream close time out with error %d\n", -+ stream->error); -+ else -+ dev_dbg(dev, "stream close complete\n"); -+ -+out_put_stream_opened: -+ put_stream_opened(av); -+ -+ return ret; -+} -+ -+static void stop_streaming_firmware(struct ipu7_isys_video *av) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret, tout; -+ -+ reinit_completion(&stream->stream_stop_completion); -+ -+ ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU_INSYS_SEND_TYPE_STREAM_FLUSH); -+ if (ret < 0) { -+ dev_err(dev, "can't stop stream (%d)\n", ret); -+ return; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_stop_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else -+ FW_CALL_TIMEOUT_JIFFIES); -+#endif -+ if (!tout) -+ dev_warn(dev, "stream stop time out\n"); -+ else if (stream->error) -+ dev_warn(dev, "stream stop error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "stop stream: complete\n"); -+} -+ -+static void close_streaming_firmware(struct ipu7_isys_video *av) -+{ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret, tout; -+ -+ reinit_completion(&stream->stream_close_completion); -+ -+ ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle, -+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE); -+ if (ret < 0) { -+ dev_err(dev, "can't close stream (%d)\n", ret); -+ return; -+ } -+ -+ tout = wait_for_completion_timeout(&stream->stream_close_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else -+ FW_CALL_TIMEOUT_JIFFIES); -+#endif -+ if (!tout) -+ dev_warn(dev, "stream close time out\n"); -+ else if (stream->error) -+ dev_warn(dev, "stream close error: %d\n", stream->error); -+ else -+ dev_dbg(dev, "close stream: complete\n"); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ stream->last_sequence = atomic_read(&stream->sequence); -+ dev_dbg(dev, "ip->last_sequence = %d\n", -+ stream->last_sequence); -+ -+#endif -+ put_stream_opened(av); -+} -+ -+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -+ struct media_entity *source_entity, -+ int nr_queues) -+{ -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_csi2 *csi2; -+ -+ if (WARN_ON(stream->nr_streaming)) -+ return -EINVAL; -+ -+ stream->nr_queues = nr_queues; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ atomic_set(&stream->sequence, stream->last_sequence); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "atomic_set : stream->last_sequence = %d\n", -+ stream->last_sequence); -+ } else { -+ atomic_set(&stream->sequence, 0); -+ } -+#else -+ atomic_set(&stream->sequence, 0); -+#endif -+ atomic_set(&stream->buf_id, 0); -+ -+ stream->seq_index = 0; -+ memset(stream->seq, 0, sizeof(stream->seq)); -+ -+ if (WARN_ON(!list_empty(&stream->queues))) -+ return -EINVAL; -+ -+ stream->stream_source = stream->asd->source; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (!stream->asd->is_tpg) { -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ csi2->receiver_errors = 0; -+ } -+#else -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ csi2->receiver_errors = 0; -+#endif -+ stream->source_entity = source_entity; -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "prepare stream: external entity %s\n", -+ stream->source_entity->name); -+ -+ return 0; -+} -+ -+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream) -+{ -+ unsigned long flags; -+ struct device *dev; -+ unsigned int i; -+ -+ if (!stream) { -+ pr_err("ipu7-isys: no available stream\n"); -+ return; -+ } -+ -+ dev = &stream->isys->adev->auxdev.dev; -+ -+ spin_lock_irqsave(&stream->isys->streams_lock, flags); -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (&stream->isys->streams[i] == stream) { -+ if (stream->isys->streams_ref_count[i] > 0) -+ stream->isys->streams_ref_count[i]--; -+ else -+ dev_warn(dev, "invalid stream %d\n", i); -+ -+ break; -+ } -+ } -+ spin_unlock_irqrestore(&stream->isys->streams_lock, flags); -+} -+ -+static struct ipu7_isys_stream * -+ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd) -+{ -+ struct ipu7_isys_stream *stream = NULL; -+ struct ipu7_isys *isys = av->isys; -+ unsigned long flags; -+ unsigned int i; -+ u8 vc = av->vc; -+ -+ if (!isys) -+ return NULL; -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (isys->streams_ref_count[i] && isys->streams[i].vc == vc && -+ isys->streams[i].asd == asd) { -+ isys->streams_ref_count[i]++; -+ stream = &isys->streams[i]; -+ break; -+ } -+ } -+ -+ if (!stream) { -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (!isys->streams_ref_count[i]) { -+ isys->streams_ref_count[i]++; -+ stream = &isys->streams[i]; -+ stream->vc = vc; -+ stream->asd = asd; -+ break; -+ } -+ } -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle) -+{ -+ unsigned long flags; -+ struct ipu7_isys_stream *stream = NULL; -+ -+ if (!isys) -+ return NULL; -+ -+ if (stream_handle >= IPU_ISYS_MAX_STREAMS) { -+ dev_err(&isys->adev->auxdev.dev, -+ "stream_handle %d is invalid\n", stream_handle); -+ return NULL; -+ } -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ if (isys->streams_ref_count[stream_handle] > 0) { -+ isys->streams_ref_count[stream_handle]++; -+ stream = &isys->streams[stream_handle]; -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc) -+{ -+ struct ipu7_isys_stream *stream = NULL; -+ unsigned long flags; -+ unsigned int i; -+ -+ if (!isys) -+ return NULL; -+ -+ if (source < 0) { -+ dev_err(&isys->adev->auxdev.dev, -+ "query stream with invalid port number\n"); -+ return NULL; -+ } -+ -+ spin_lock_irqsave(&isys->streams_lock, flags); -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ if (!isys->streams_ref_count[i]) -+ continue; -+ -+ if (isys->streams[i].stream_source == source && -+ isys->streams[i].vc == vc) { -+ stream = &isys->streams[i]; -+ isys->streams_ref_count[i]++; -+ break; -+ } -+ } -+ spin_unlock_irqrestore(&isys->streams_lock, flags); -+ -+ return stream; -+} -+ -+static u32 get_remote_pad_stream(struct media_pad *r_pad) -+{ -+ struct v4l2_subdev_state *state; -+ struct v4l2_subdev *sd; -+ u32 stream_id = 0; -+ unsigned int i; -+ -+ sd = media_entity_to_v4l2_subdev(r_pad->entity); -+ state = v4l2_subdev_lock_and_get_active_state(sd); -+ if (!state) -+ return 0; -+ -+ for (i = 0; i < state->stream_configs.num_configs; i++) { -+ struct v4l2_subdev_stream_config *cfg = -+ &state->stream_configs.configs[i]; -+ if (cfg->pad == r_pad->index) { -+ stream_id = cfg->stream; -+ break; -+ } -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ return stream_id; -+} -+ -+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -+ struct ipu7_isys_buffer_list *bl) -+{ -+ struct ipu7_isys_stream *stream = av->stream; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *r_pad; -+ struct v4l2_subdev *sd; -+ u32 r_stream; -+ int ret = 0; -+ -+ dev_dbg(dev, "set stream: %d\n", state); -+ -+ if (WARN(!stream->source_entity, "No source entity for stream\n")) -+ return -ENODEV; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (stream->asd->is_tpg) { -+ sd = &stream->asd->sd; -+ r_pad = media_pad_remote_pad_first(&av->pad); -+ r_stream = -+ ipu7_isys_get_src_stream_by_src_pad(sd, r_pad->index); -+ -+ if (!state) { -+ stop_streaming_firmware(av); -+ dev_dbg(dev, "disable streams 0x%lx of %s\n", -+ BIT(r_stream), sd->name); -+ ret = v4l2_subdev_disable_streams(sd, r_pad->index, -+ BIT(r_stream)); -+ if (ret) -+ dev_err(dev, "disable streams of %s failed\n", -+ sd->name); -+ -+ close_streaming_firmware(av); -+ } else { -+ ret = start_stream_firmware(av, bl); -+ if (ret) { -+ dev_err(dev, "start FW stream failed\n"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "set stream: source %d, handle %d\n", -+ stream->stream_source, stream->stream_handle); -+ -+ dev_dbg(dev, "enable streams 0x%lx of %s\n", -+ BIT(r_stream), sd->name); -+ /* start sub-device which connects with video */ -+ ret = v4l2_subdev_enable_streams(sd, r_pad->index, -+ BIT(r_stream)); -+ if (ret) { -+ dev_err(dev, "enable streams of %s failed\n", -+ sd->name); -+ goto out_media_entity_stop_streaming_firmware; -+ } -+ } -+ av->streaming = state; -+ -+ return 0; -+ } -+ -+#endif -+ sd = &stream->asd->sd; -+ r_pad = media_pad_remote_pad_first(&av->pad); -+ r_stream = get_remote_pad_stream(r_pad); -+ if (!state) { -+ stop_streaming_firmware(av); -+ -+ /* stop sub-device which connects with video */ -+ dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n", -+ sd->name, r_pad->index, BIT_ULL(r_stream)); -+ ret = v4l2_subdev_disable_streams(sd, r_pad->index, -+ BIT_ULL(r_stream)); -+ if (ret) { -+ dev_err(dev, "disable streams %s failed with %d\n", -+ sd->name, ret); -+ return ret; -+ } -+ -+ close_streaming_firmware(av); -+ } else { -+ ret = start_stream_firmware(av, bl); -+ if (ret) { -+ dev_err(dev, "start stream of firmware failed\n"); -+ return ret; -+ } -+ -+ /* start sub-device which connects with video */ -+ dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n", -+ sd->name, r_pad->index, BIT_ULL(r_stream)); -+ ret = v4l2_subdev_enable_streams(sd, r_pad->index, -+ BIT_ULL(r_stream)); -+ if (ret) { -+ dev_err(dev, "enable streams %s failed with %d\n", -+ sd->name, ret); -+ goto out_media_entity_stop_streaming_firmware; -+ } -+ } -+ -+ av->streaming = state; -+ -+ return 0; -+ -+out_media_entity_stop_streaming_firmware: -+ stop_streaming_firmware(av); -+ -+ return ret; -+} -+ -+static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = { -+ .vidioc_querycap = ipu7_isys_vidioc_querycap, -+ .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt, -+ .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes, -+ .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap, -+ .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap, -+ .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap, -+ .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs, -+ .vidioc_create_bufs = ipu7_isys_vidioc_create_bufs, -+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, -+ .vidioc_querybuf = vb2_ioctl_querybuf, -+ .vidioc_qbuf = vb2_ioctl_qbuf, -+ .vidioc_dqbuf = vb2_ioctl_dqbuf, -+ .vidioc_streamon = vb2_ioctl_streamon, -+ .vidioc_streamoff = vb2_ioctl_streamoff, -+ .vidioc_expbuf = vb2_ioctl_expbuf, -+}; -+ -+static const struct media_entity_operations entity_ops = { -+ .link_validate = link_validate, -+}; -+ -+static const struct v4l2_file_operations isys_fops = { -+ .owner = THIS_MODULE, -+ .poll = vb2_fop_poll, -+ .unlocked_ioctl = video_ioctl2, -+ .mmap = vb2_fop_mmap, -+ .open = video_open, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ .release = video_release, -+#else -+ .release = vb2_fop_release, -+#endif -+}; -+ -+int ipu7_isys_fw_open(struct ipu7_isys *isys) -+{ -+ struct ipu7_bus_device *adev = isys->adev; -+ int ret; -+ -+ ret = pm_runtime_resume_and_get(&adev->auxdev.dev); -+ if (ret < 0) -+ return ret; -+ -+ mutex_lock(&isys->mutex); -+ -+ if (isys->ref_count++) -+ goto unlock; -+ -+ /* -+ * Buffers could have been left to wrong queue at last closure. -+ * Move them now back to empty buffer queue. -+ */ -+ ipu7_cleanup_fw_msg_bufs(isys); -+ -+ ret = ipu7_fw_isys_open(isys); -+ if (ret < 0) -+ goto out; -+ -+unlock: -+ mutex_unlock(&isys->mutex); -+ -+ return 0; -+out: -+ isys->ref_count--; -+ mutex_unlock(&isys->mutex); -+ pm_runtime_put(&adev->auxdev.dev); -+ -+ return ret; -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+void ipu7_isys_fw_close(struct ipu7_isys *isys) -+{ -+ int ret = 0; -+ -+ mutex_lock(&isys->mutex); -+ isys->ref_count--; -+ if (!isys->ref_count) { -+ /* need reset when fw close is abnormal */ -+ ret = ipu7_fw_isys_close(isys); -+ if (ret) { -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = true; -+ mutex_unlock(&isys->reset_mutex); -+ } -+ } -+ -+ mutex_unlock(&isys->mutex); -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put_sync(&isys->adev->auxdev.dev); -+ } else { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put(&isys->adev->auxdev.dev); -+ } -+} -+#else -+void ipu7_isys_fw_close(struct ipu7_isys *isys) -+{ -+ mutex_lock(&isys->mutex); -+ -+ isys->ref_count--; -+ -+ if (!isys->ref_count) -+ ipu7_fw_isys_close(isys); -+ -+ mutex_unlock(&isys->mutex); -+ pm_runtime_put(&isys->adev->auxdev.dev); -+} -+#endif -+ -+int ipu7_isys_setup_video(struct ipu7_isys_video *av, -+ struct media_entity **source_entity, int *nr_queues) -+{ -+ const struct ipu7_isys_pixelformat *pfmt = -+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct media_pad *source_pad, *remote_pad; -+ struct v4l2_mbus_frame_desc_entry entry; -+ struct v4l2_subdev_route *route = NULL; -+ struct v4l2_subdev_route *r; -+ struct v4l2_subdev_state *state; -+ struct ipu7_isys_subdev *asd; -+ struct v4l2_subdev *remote_sd; -+ struct media_pipeline *pipeline; -+ int ret = -EINVAL; -+ -+ *nr_queues = 0; -+ -+ remote_pad = media_pad_remote_pad_unique(&av->pad); -+ if (IS_ERR(remote_pad)) { -+ dev_dbg(dev, "failed to get remote pad\n"); -+ return PTR_ERR(remote_pad); -+ } -+ -+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); -+ asd = to_ipu7_isys_subdev(remote_sd); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ -+ if (strncmp(remote_pad->entity->name, "Intel IPU7 TPG", -+ strlen("Intel IPU7 TPG")) == 0) { -+ dev_dbg(dev, "Find TPG:%s stream\n", remote_sd->name); -+ -+ av->vc = 0; -+ av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -+ ret = video_device_pipeline_alloc_start(&av->vdev); -+ if (ret < 0) { -+ dev_dbg(dev, "media pipeline start failed\n"); -+ return ret; -+ } -+ -+ *source_entity = remote_pad->entity; -+ av->stream = ipu7_isys_get_stream(av, asd); -+ if (!av->stream) { -+ video_device_pipeline_stop(&av->vdev); -+ dev_err(dev, "no available stream for firmware\n"); -+ return -EINVAL; -+ } -+ -+ av->stream->asd->is_tpg = true; -+ *nr_queues = 1; -+ -+ return 0; -+ } -+#endif -+ -+ source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]); -+ if (!source_pad) { -+ dev_dbg(dev, "No external source entity\n"); -+ return -ENODEV; -+ } -+ -+ *source_entity = source_pad->entity; -+ -+ state = v4l2_subdev_lock_and_get_active_state(remote_sd); -+ for_each_active_route(&state->routing, r) { -+ if (r->source_pad == remote_pad->index) -+ route = r; -+ } -+ -+ if (!route) { -+ v4l2_subdev_unlock_state(state); -+ dev_dbg(dev, "Failed to find route\n"); -+ return -ENODEV; -+ } -+ -+ v4l2_subdev_unlock_state(state); -+ -+ ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream, -+ to_ipu7_isys_csi2(asd), -+ *source_entity, &entry, -+ nr_queues); -+ if (ret == -ENOIOCTLCMD) { -+ av->vc = 0; -+ av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code); -+ if (av->dt == 0xff) -+ return -EINVAL; -+ *nr_queues = 1; -+ } else if (*nr_queues && !ret) { -+ dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n", -+ entry.stream, entry.length, entry.bus.csi2.vc, -+ entry.bus.csi2.dt); -+ -+ av->vc = entry.bus.csi2.vc; -+ av->dt = entry.bus.csi2.dt; -+ } else { -+ dev_err(dev, "failed to get remote frame desc\n"); -+ return ret; -+ } -+ -+ pipeline = media_entity_pipeline(&av->vdev.entity); -+ if (!pipeline) -+ ret = video_device_pipeline_alloc_start(&av->vdev); -+ else -+ ret = video_device_pipeline_start(&av->vdev, pipeline); -+ if (ret < 0) { -+ dev_dbg(dev, "media pipeline start failed\n"); -+ return ret; -+ } -+ -+ av->stream = ipu7_isys_get_stream(av, asd); -+ if (!av->stream) { -+ video_device_pipeline_stop(&av->vdev); -+ dev_err(dev, "no available stream for firmware\n"); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Do everything that's needed to initialise things related to video -+ * buffer queue, video node, and the related media entity. The caller -+ * is expected to assign isys field and set the name of the video -+ * device. -+ */ -+int ipu7_isys_video_init(struct ipu7_isys_video *av) -+{ -+ struct v4l2_format format = { -+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, -+ .fmt.pix = { -+ .width = 1920, -+ .height = 1080, -+ }, -+ }; -+ int ret; -+ -+ mutex_init(&av->mutex); -+ av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC | -+ V4L2_CAP_VIDEO_CAPTURE; -+ av->vdev.vfl_dir = VFL_DIR_RX; -+ -+ ret = ipu7_isys_queue_init(&av->aq); -+ if (ret) -+ goto out_mutex_destroy; -+ -+ av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; -+ ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad); -+ if (ret) -+ goto out_vb2_queue_cleanup; -+ -+ av->vdev.entity.ops = &entity_ops; -+ av->vdev.release = video_device_release_empty; -+ av->vdev.fops = &isys_fops; -+ av->vdev.v4l2_dev = &av->isys->v4l2_dev; -+ av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev; -+ av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops; -+ av->vdev.queue = &av->aq.vbq; -+ av->vdev.lock = &av->mutex; -+ -+ __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); -+ av->pix_fmt = format.fmt.pix; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->reset = false; -+ av->skipframe = 0; -+ av->start_streaming = 0; -+#endif -+ -+ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); -+ video_set_drvdata(&av->vdev, av); -+ -+ ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1); -+ if (ret) -+ goto out_media_entity_cleanup; -+ -+ return ret; -+ -+out_media_entity_cleanup: -+ vb2_video_unregister_device(&av->vdev); -+ media_entity_cleanup(&av->vdev.entity); -+ -+out_vb2_queue_cleanup: -+ vb2_queue_release(&av->aq.vbq); -+ -+out_mutex_destroy: -+ mutex_destroy(&av->mutex); -+ -+ return ret; -+} -+ -+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av) -+{ -+ vb2_video_unregister_device(&av->vdev); -+ media_entity_cleanup(&av->vdev.entity); -+ mutex_destroy(&av->mutex); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-video.h b/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -new file mode 100644 -index 000000000000..e6d1da2b7b47 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-video.h -@@ -0,0 +1,125 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_VIDEO_H -+#define IPU7_ISYS_VIDEO_H -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#include "ipu7-isys-queue.h" -+ -+#define IPU_INSYS_OUTPUT_PINS 11U -+#define IPU_ISYS_MAX_PARALLEL_SOF 2U -+ -+struct file; -+struct ipu7_isys; -+struct ipu7_isys_csi2; -+struct ipu7_insys_stream_cfg; -+struct ipu7_isys_subdev; -+ -+struct ipu7_isys_pixelformat { -+ u32 pixelformat; -+ u32 bpp; -+ u32 bpp_packed; -+ u32 code; -+ u32 css_pixelformat; -+}; -+ -+struct sequence_info { -+ unsigned int sequence; -+ u64 timestamp; -+}; -+ -+struct output_pin_data { -+ void (*pin_ready)(struct ipu7_isys_stream *stream, -+ struct ipu7_insys_resp *info); -+ struct ipu7_isys_queue *aq; -+}; -+ -+/* -+ * Align with firmware stream. Each stream represents a CSI virtual channel. -+ * May map to multiple video devices -+ */ -+struct ipu7_isys_stream { -+ struct mutex mutex; -+ struct media_entity *source_entity; -+ atomic_t sequence; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ int last_sequence; -+#endif -+ atomic_t buf_id; -+ unsigned int seq_index; -+ struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; -+ int stream_source; -+ int stream_handle; -+ unsigned int nr_output_pins; -+ struct ipu7_isys_subdev *asd; -+ -+ int nr_queues; /* Number of capture queues */ -+ int nr_streaming; -+ int streaming; -+ struct list_head queues; -+ struct completion stream_open_completion; -+ struct completion stream_close_completion; -+ struct completion stream_start_completion; -+ struct completion stream_stop_completion; -+ struct ipu7_isys *isys; -+ -+ struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS]; -+ int error; -+ u8 vc; -+}; -+ -+struct ipu7_isys_video { -+ struct ipu7_isys_queue aq; -+ /* Serialise access to other fields in the struct. */ -+ struct mutex mutex; -+ struct media_pad pad; -+ struct video_device vdev; -+ struct v4l2_pix_format pix_fmt; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_csi2 *csi2; -+ struct ipu7_isys_stream *stream; -+ unsigned int streaming; -+ u8 vc; -+ u8 dt; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ unsigned int reset; -+ unsigned int skipframe; -+ unsigned int start_streaming; -+#endif -+}; -+ -+#define ipu7_isys_queue_to_video(__aq) \ -+ container_of(__aq, struct ipu7_isys_video, aq) -+ -+extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[]; -+ -+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat); -+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, -+ struct media_entity *source_entity, -+ int nr_queues); -+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state, -+ struct ipu7_isys_buffer_list *bl); -+int ipu7_isys_fw_open(struct ipu7_isys *isys); -+void ipu7_isys_fw_close(struct ipu7_isys *isys); -+int ipu7_isys_setup_video(struct ipu7_isys_video *av, -+ struct media_entity **source_entity, int *nr_queues); -+int ipu7_isys_video_init(struct ipu7_isys_video *av); -+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av); -+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream); -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, -+ u8 stream_handle); -+struct ipu7_isys_stream * -+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc); -+#endif /* IPU7_ISYS_VIDEO_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c -new file mode 100644 -index 000000000000..e5ba50142a1a ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys.c -@@ -0,0 +1,1624 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-isys.h" -+#include "ipu7-mmu.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-csi2.h" -+#include "ipu7-isys-csi-phy.h" -+#include "ipu7-isys-csi2-regs.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+#include "ipu7-isys-tpg.h" -+#endif -+#include "ipu7-isys-video.h" -+#include "ipu7-platform-regs.h" -+ -+#define ISYS_PM_QOS_VALUE 300 -+ -+static int -+isys_complete_ext_device_registration(struct ipu7_isys *isys, -+ struct v4l2_subdev *sd, -+ struct ipu7_isys_csi2_config *csi2) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ unsigned int i; -+ int ret; -+ -+ v4l2_set_subdev_hostdata(sd, csi2); -+ -+ for (i = 0; i < sd->entity.num_pads; i++) { -+ if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE) -+ break; -+ } -+ -+ if (i == sd->entity.num_pads) { -+ dev_warn(dev, "no source pad in external entity\n"); -+ ret = -ENOENT; -+ goto skip_unregister_subdev; -+ } -+ -+ ret = media_create_pad_link(&sd->entity, i, -+ &isys->csi2[csi2->port].asd.sd.entity, -+ 0, MEDIA_LNK_FL_ENABLED | -+ MEDIA_LNK_FL_IMMUTABLE); -+ if (ret) { -+ dev_warn(dev, "can't create link\n"); -+ goto skip_unregister_subdev; -+ } -+ -+ isys->csi2[csi2->port].nlanes = csi2->nlanes; -+ if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) -+ isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; -+ else -+ isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; -+ -+ return 0; -+ -+skip_unregister_subdev: -+ v4l2_device_unregister_subdev(sd); -+ return ret; -+} -+ -+struct isys_i2c_test { -+ u8 bus_nr; -+ u16 addr; -+ struct i2c_client *client; -+}; -+ -+static int isys_i2c_test(struct device *dev, void *priv) -+{ -+ struct i2c_client *client = i2c_verify_client(dev); -+ struct isys_i2c_test *test = priv; -+ -+ if (!client) -+ return 0; -+ -+ if (i2c_adapter_id(client->adapter) != test->bus_nr || -+ client->addr != test->addr) -+ return 0; -+ -+ test->client = client; -+ -+ return 0; -+} -+ -+static -+struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct i2c_board_info *info = &sd_info->i2c.board_info; -+ struct isys_i2c_test test = { -+ .bus_nr = i2c_adapter_id(adapter), -+ .addr = info->addr, -+ }; -+ int ret; -+ -+ ret = i2c_for_each_dev(&test, isys_i2c_test); -+ if (ret || !test.client) -+ return NULL; -+ return test.client; -+} -+ -+static int isys_register_ext_subdev(struct ipu7_isys *isys, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct i2c_adapter *adapter; -+ struct v4l2_subdev *sd; -+ struct i2c_client *client; -+ int ret; -+ int bus; -+ -+ bus = sd_info->i2c.i2c_adapter_id; -+ adapter = i2c_get_adapter(bus); -+ if (!adapter) { -+ dev_warn(dev, "can't find adapter\n"); -+ return -ENOENT; -+ } -+ -+ dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -+ sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -+ bus); -+ -+ if (sd_info->csi2) { -+ dev_info(dev, "sensor device on CSI port: %d\n", -+ sd_info->csi2->port); -+ if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -+ !isys->csi2[sd_info->csi2->port].isys) { -+ dev_warn(dev, "invalid csi2 port %u\n", -+ sd_info->csi2->port); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ } else { -+ dev_info(dev, "No camera subdevice\n"); -+ } -+ -+ client = isys_find_i2c_subdev(adapter, sd_info); -+ if (client) { -+ dev_warn(dev, "Device exists\n"); -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+ /* TODO: remove i2c_unregister_device() */ -+ i2c_unregister_device(client); -+#else -+ ret = 0; -+ goto skip_put_adapter; -+#endif -+ } -+ -+ sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -+ &sd_info->i2c.board_info, NULL); -+ if (!sd) { -+ dev_warn(dev, "can't create new i2c subdev\n"); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ -+ if (!sd_info->csi2) -+ return 0; -+ -+ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -+ -+skip_put_adapter: -+ i2c_put_adapter(adapter); -+ -+ return ret; -+} -+ -+static void isys_register_ext_subdevs(struct ipu7_isys *isys) -+{ -+ struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -+ struct ipu7_isys_subdev_info **sd_info; -+ -+ if (!spdata) { -+ dev_info(&isys->adev->auxdev.dev, -+ "no subdevice info provided\n"); -+ return; -+ } -+ for (sd_info = spdata->subdevs; *sd_info; sd_info++) -+ isys_register_ext_subdev(isys, *sd_info); -+} -+ -+static void isys_stream_init(struct ipu7_isys *isys) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) { -+ mutex_init(&isys->streams[i].mutex); -+ init_completion(&isys->streams[i].stream_open_completion); -+ init_completion(&isys->streams[i].stream_close_completion); -+ init_completion(&isys->streams[i].stream_start_completion); -+ init_completion(&isys->streams[i].stream_stop_completion); -+ INIT_LIST_HEAD(&isys->streams[i].queues); -+ isys->streams[i].isys = isys; -+ isys->streams[i].stream_handle = i; -+ isys->streams[i].vc = INVALID_VC_ID; -+ } -+} -+ -+static int isys_fw_log_init(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_log *fw_log; -+ void *log_buf; -+ -+ if (isys->fw_log) -+ return 0; -+ -+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -+ if (!fw_log) -+ return -ENOMEM; -+ -+ mutex_init(&fw_log->mutex); -+ -+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!log_buf) -+ return -ENOMEM; -+ -+ fw_log->head = log_buf; -+ fw_log->addr = log_buf; -+ fw_log->count = 0; -+ fw_log->size = 0; -+ -+ isys->fw_log = fw_log; -+ -+ return 0; -+} -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+/* The .bound() notifier callback when a match is found */ -+static int isys_notifier_bound(struct v4l2_async_notifier *notifier, -+ struct v4l2_subdev *sd, -+ struct v4l2_async_connection *asc) -+{ -+ struct ipu7_isys *isys = container_of(notifier, -+ struct ipu7_isys, notifier); -+ struct sensor_async_sd *s_asd = -+ container_of(asc, struct sensor_async_sd, asc); -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ ret = ipu_bridge_instantiate_vcm(sd->dev); -+ if (ret) { -+ dev_err(dev, "instantiate vcm failed\n"); -+ return ret; -+ } -+ -+ dev_info(dev, "bind %s nlanes is %d port is %d\n", -+ sd->name, s_asd->csi2.nlanes, s_asd->csi2.port); -+ isys_complete_ext_device_registration(isys, sd, &s_asd->csi2); -+ -+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+} -+ -+static int isys_notifier_complete(struct v4l2_async_notifier *notifier) -+{ -+ struct ipu7_isys *isys = container_of(notifier, -+ struct ipu7_isys, notifier); -+ -+ dev_info(&isys->adev->auxdev.dev, -+ "All sensor registration completed.\n"); -+ -+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+} -+ -+static const struct v4l2_async_notifier_operations isys_async_ops = { -+ .bound = isys_notifier_bound, -+ .complete = isys_notifier_complete, -+}; -+ -+static int isys_notifier_init(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2 = -+ &isys->pdata->ipdata->csi2; -+ struct ipu7_device *isp = isys->adev->isp; -+ struct device *dev = &isp->pdev->dev; -+ unsigned int i; -+ int ret; -+ -+ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev); -+ -+ for (i = 0; i < csi2->nports; i++) { -+ struct v4l2_fwnode_endpoint vep = { -+ .bus_type = V4L2_MBUS_UNKNOWN -+ }; -+ struct sensor_async_sd *s_asd; -+ struct fwnode_handle *ep; -+ -+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!ep) -+ continue; -+ -+ ret = v4l2_fwnode_endpoint_parse(ep, &vep); -+ if (ret) -+ goto err_parse; -+ -+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY && -+ vep.bus_type != V4L2_MBUS_CSI2_CPHY) { -+ ret = -EINVAL; -+ dev_err(dev, "unsupported bus type %d!\n", -+ vep.bus_type); -+ goto err_parse; -+ } -+ -+ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep, -+ struct -+ sensor_async_sd); -+ if (IS_ERR(s_asd)) { -+ ret = PTR_ERR(s_asd); -+ goto err_parse; -+ } -+ -+ s_asd->csi2.port = vep.base.port; -+ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes; -+ s_asd->csi2.bus_type = vep.bus_type; -+ -+ fwnode_handle_put(ep); -+ -+ continue; -+ -+err_parse: -+ fwnode_handle_put(ep); -+ return ret; -+ } -+ -+ if (list_empty(&isys->notifier.waiting_list)) { -+ /* isys probe could continue with async subdevs missing */ -+ dev_warn(dev, "no subdev found in graph\n"); -+ return 0; -+ } -+ -+ isys->notifier.ops = &isys_async_ops; -+ ret = v4l2_async_nf_register(&isys->notifier); -+ if (ret) { -+ dev_err(dev, "failed to register async notifier(%d)\n", ret); -+ v4l2_async_nf_cleanup(&isys->notifier); -+ } -+ -+ return ret; -+} -+ -+static void isys_notifier_cleanup(struct ipu7_isys *isys) -+{ -+ v4l2_async_nf_unregister(&isys->notifier); -+ v4l2_async_nf_cleanup(&isys->notifier); -+} -+#endif -+ -+static void isys_unregister_video_devices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i, j; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) -+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); -+} -+ -+static int isys_register_video_devices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i, j; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -+ -+ snprintf(av->vdev.name, sizeof(av->vdev.name), -+ IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u", -+ i * IPU7_NR_OF_CSI2_SRC_PADS + j); -+ av->isys = isys; -+ av->aq.vbq.buf_struct_size = -+ sizeof(struct ipu7_isys_video_buffer); -+ -+ ret = ipu7_isys_video_init(av); -+ if (ret) -+ goto fail; -+ } -+ } -+ -+ return 0; -+ -+fail: -+ i = i + 1U; -+ while (i--) { -+ while (j--) -+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]); -+ j = IPU7_NR_OF_CSI2_SRC_PADS; -+ } -+ -+ return ret; -+} -+ -+static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2 = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i; -+ -+ for (i = 0; i < csi2->nports; i++) -+ ipu7_isys_csi2_cleanup(&isys->csi2[i]); -+} -+ -+static int isys_csi2_register_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ ret = ipu7_isys_csi2_init(&isys->csi2[i], isys, -+ isys->pdata->base + -+ csi2_pdata->offsets[i], i); -+ if (ret) -+ goto fail; -+ } -+ -+ isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK; -+ -+ return 0; -+ -+fail: -+ while (i--) -+ ipu7_isys_csi2_cleanup(&isys->csi2[i]); -+ -+ return ret; -+} -+ -+static int isys_csi2_create_media_links(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct media_entity *sd; -+ unsigned int i, j; -+ int ret; -+ -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ sd = &isys->csi2[i].asd.sd.entity; -+ -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ struct ipu7_isys_video *av = &isys->csi2[i].av[j]; -+ -+ ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j, -+ &av->vdev.entity, 0, 0); -+ if (ret) { -+ dev_err(dev, "CSI2 can't create link\n"); -+ return ret; -+ } -+ -+ av->csi2 = &isys->csi2[i]; -+ } -+ } -+ -+ return 0; -+} -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+static void isys_tpg_unregister_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -+ &isys->pdata->ipdata->tpg; -+ unsigned int i; -+ -+ if (!isys->tpg) -+ return; -+ -+ for (i = 0; i < tpg_pdata->ntpgs; i++) -+ ipu7_isys_tpg_cleanup(&isys->tpg[i]); -+ -+ kfree(isys->tpg); -+ isys->tpg = NULL; -+} -+ -+static int isys_tpg_register_subdevices(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -+ &isys->pdata->ipdata->tpg; -+ unsigned int i; -+ int ret; -+ -+ isys->tpg = kcalloc(tpg_pdata->ntpgs, sizeof(*isys->tpg), GFP_KERNEL); -+ if (!isys->tpg) -+ return -ENOMEM; -+ -+ for (i = 0; i < tpg_pdata->ntpgs; i++) { -+ ret = ipu7_isys_tpg_init(&isys->tpg[i], isys, -+ isys->pdata->base + -+ tpg_pdata->offsets[i], -+ tpg_pdata->sels ? -+ (isys->pdata->base + -+ tpg_pdata->sels[i]) : NULL, i); -+ if (ret) -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ while (i--) -+ ipu7_isys_tpg_cleanup(&isys->tpg[i]); -+ -+ kfree(isys->tpg); -+ isys->tpg = NULL; -+ -+ return ret; -+} -+ -+static int isys_tpg_create_media_links(struct ipu7_isys *isys) -+{ -+ const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -+ &isys->pdata->ipdata->tpg; -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct ipu7_isys_tpg *tpg; -+ struct media_entity *sd; -+ unsigned int i; -+ int ret; -+ -+ for (i = 0; i < tpg_pdata->ntpgs; i++) { -+ tpg = &isys->tpg[i]; -+ sd = &tpg->asd.sd.entity; -+ tpg->av = &isys->csi2[tpg->index].av[0]; -+ -+ ret = media_create_pad_link(sd, TPG_PAD_SOURCE, -+ &tpg->av->vdev.entity, -+ TPG_PAD_SOURCE, 0); -+ if (ret) { -+ dev_err(dev, "TPG can't create link\n"); -+ return ret; -+ } -+ } -+ -+ return 0; -+} -+ -+#endif -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+static int isys_register_devices(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_video_unregister_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ -+ if (!isys->pdata->spdata) { -+ ret = isys_notifier_init(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } else { -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } -+ -+ return 0; -+ -+out_csi2_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_video_unregister_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+#else -+static int isys_register_devices(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_video_unregister_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ ret = isys_tpg_register_subdevices(isys); -+ if (!ret) -+ ret = isys_tpg_create_media_links(isys); -+ -+ if (ret) -+ goto out_tpg_unregister_subdevices; -+ -+#endif -+ -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ if (ret) -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ goto out_tpg_unregister_subdevices; -+#else -+ goto out_csi2_unregister_subdevices; -+#endif -+ -+ return 0; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+out_tpg_unregister_subdevices: -+ isys_tpg_unregister_subdevices(isys); -+#endif -+out_csi2_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_video_unregister_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+#endif -+ -+static void isys_unregister_devices(struct ipu7_isys *isys) -+{ -+ isys_unregister_video_devices(isys); -+ isys_csi2_unregister_subdevices(isys); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ isys_tpg_unregister_subdevices(isys); -+#endif -+ v4l2_device_unregister(&isys->v4l2_dev); -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+} -+ -+static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable) -+{ -+ u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -+ void __iomem *base = isys->pdata->base; -+ u32 mask = isys->isr_csi2_mask; -+ -+ if (!enable) { -+ writel(mask, base + offset + IRQ_CTL_CLEAR); -+ writel(0, base + offset + IRQ_CTL_ENABLE); -+ return; -+ } -+ -+ writel(mask, base + offset + IRQ_CTL_EDGE); -+ writel(mask, base + offset + IRQ_CTL_CLEAR); -+ writel(mask, base + offset + IRQ_CTL_MASK); -+ writel(mask, base + offset + IRQ_CTL_ENABLE); -+} -+ -+static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable) -+{ -+ void __iomem *base = isys->pdata->base; -+ u32 mask = IS_UC_TO_SW_IRQ_MASK; -+ u32 offset = IS_UC_CTRL_BASE; -+ -+ if (!enable) { -+ writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE); -+ return; -+ } -+ -+ writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR); -+ writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N); -+ writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE); -+} -+ -+void ipu7_isys_setup_hw(struct ipu7_isys *isys) -+{ -+ u32 offset; -+ void __iomem *base = isys->pdata->base; -+ -+ /* soft reset */ -+ offset = IS_IO_GPREGS_BASE; -+ -+ writel(0x0, base + offset + CLK_EN_TXCLKESC); -+ /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */ -+ writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK); -+ /* correct the initial printf configuration */ -+ writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -+ -+ enable_to_sw_irq(isys, 1); -+ enable_csi2_legacy_irq(isys, 1); -+} -+ -+static void isys_cleanup_hw(struct ipu7_isys *isys) -+{ -+ enable_csi2_legacy_irq(isys, 0); -+ enable_to_sw_irq(isys, 0); -+} -+ -+static int isys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ struct ipu7_device *isp = adev->isp; -+ unsigned long flags; -+ int ret; -+ -+ if (!isys) -+ return 0; -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE); -+ -+ ret = ipu_buttress_start_tsc_sync(isp); -+ if (ret) -+ return ret; -+ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->power = 1; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ -+ return 0; -+} -+ -+static int isys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ -+ if (!isys) -+ return 0; -+ -+ isys_cleanup_hw(isys); -+ -+ spin_lock_irqsave(&isys->power_lock, flags); -+ isys->power = 0; -+ spin_unlock_irqrestore(&isys->power_lock, flags); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = false; -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif -+ cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+static int isys_suspend(struct device *dev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(dev); -+ -+ /* If stream is open, refuse to suspend */ -+ if (isys->stream_opened) -+ return -EBUSY; -+ -+ return 0; -+} -+ -+static int isys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static const struct dev_pm_ops isys_pm_ops = { -+ .runtime_suspend = isys_runtime_pm_suspend, -+ .runtime_resume = isys_runtime_pm_resume, -+ .suspend = isys_suspend, -+ .resume = isys_resume, -+}; -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct isys_fw_msgs *fwmsg, *safe; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ if (!isys->pdata->spdata) -+ isys_notifier_cleanup(isys); -+ -+ isys_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#else -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct isys_fw_msgs *fwmsg, *safe; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ isys_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#endif -+ -+#ifdef CONFIG_DEBUG_FS -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_isys *isys = file->private_data; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 log_size; -+ int ret = 0; -+ void *buf; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_info(dev, "copy %d bytes fw log to user...\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations isys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, isys, &isys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ isys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ -+static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) -+{ -+ struct ipu7_bus_device *adev = isys->adev; -+ struct isys_fw_msgs *addr; -+ dma_addr_t dma_addr; -+ unsigned long flags; -+ unsigned int i; -+ -+ for (i = 0; i < amount; i++) { -+ addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs), -+ &dma_addr, GFP_KERNEL, 0); -+ if (!addr) -+ break; -+ addr->dma_addr = dma_addr; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ list_add(&addr->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ } -+ -+ if (i == amount) -+ return 0; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ while (!list_empty(&isys->framebuflist)) { -+ addr = list_first_entry(&isys->framebuflist, -+ struct isys_fw_msgs, head); -+ list_del(&addr->head); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ addr, addr->dma_addr, 0); -+ spin_lock_irqsave(&isys->listlock, flags); -+ } -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ -+ return -ENOMEM; -+} -+ -+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream) -+{ -+ struct device *dev = &stream->isys->adev->auxdev.dev; -+ struct ipu7_isys *isys = stream->isys; -+ struct isys_fw_msgs *msg; -+ unsigned long flags; -+ int ret; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ if (list_empty(&isys->framebuflist)) { -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dev_dbg(dev, "Frame buffer list empty\n"); -+ -+ ret = alloc_fw_msg_bufs(isys, 5); -+ if (ret < 0) -+ return NULL; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ if (list_empty(&isys->framebuflist)) { -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ dev_err(dev, "Frame list empty\n"); -+ return NULL; -+ } -+ } -+ msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head); -+ list_move(&msg->head, &isys->framebuflist_fw); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+ memset(&msg->fw_msg, 0, sizeof(msg->fw_msg)); -+ -+ return msg; -+} -+ -+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys) -+{ -+ struct isys_fw_msgs *fwmsg, *fwmsg0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head) -+ list_move(&fwmsg->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+} -+ -+void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data) -+{ -+ struct isys_fw_msgs *msg; -+ void *ptr = (void *)data; -+ unsigned long flags; -+ -+ if (WARN_ON_ONCE(!ptr)) -+ return; -+ -+ spin_lock_irqsave(&isys->listlock, flags); -+ msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy); -+ list_move(&msg->head, &isys->framebuflist); -+ spin_unlock_irqrestore(&isys->listlock, flags); -+} -+ -+static int isys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ struct ipu7_device *isp = adev->isp; -+ struct ipu7_isys *isys; -+ int ret = 0; -+ -+ if (!isp->ipu7_bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL); -+ if (!isys) -+ return -ENOMEM; -+ -+ ret = pm_runtime_resume_and_get(&auxdev->dev); -+ if (ret < 0) -+ return ret; -+ -+ adev->auxdrv_data = -+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver); -+ isys->adev = adev; -+ isys->pdata = adev->pdata; -+ -+ INIT_LIST_HEAD(&isys->requests); -+ csi2_pdata = &isys->pdata->ipdata->csi2; -+ -+ isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports, -+ sizeof(*isys->csi2), GFP_KERNEL); -+ if (!isys->csi2) { -+ ret = -ENOMEM; -+ goto out_runtime_put; -+ } -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ goto out_runtime_put; -+ -+ spin_lock_init(&isys->streams_lock); -+ spin_lock_init(&isys->power_lock); -+ isys->power = 0; -+ -+ mutex_init(&isys->mutex); -+ mutex_init(&isys->stream_mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_init(&isys->reset_mutex); -+ isys->state = 0; -+#endif -+ -+ spin_lock_init(&isys->listlock); -+ INIT_LIST_HEAD(&isys->framebuflist); -+ INIT_LIST_HEAD(&isys->framebuflist_fw); -+ -+ dev_set_drvdata(&auxdev->dev, isys); -+ -+ isys->icache_prefetch = 0; -+ isys->phy_rext_cal = 0; -+ -+ isys_stream_init(isys); -+ -+#ifdef CONFIG_DEBUG_FS -+ /* Debug fs failure is not fatal. */ -+ ipu7_isys_init_debugfs(isys); -+#endif -+ -+ cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); -+ ret = alloc_fw_msg_bufs(isys, 40); -+ if (ret < 0) -+ goto out_cleanup_isys; -+ -+ ret = ipu7_fw_isys_init(isys); -+ if (ret) -+ goto out_cleanup_isys; -+ -+ ret = isys_register_devices(isys); -+ if (ret) -+ goto out_cleanup_fw; -+ -+ ret = isys_fw_log_init(isys); -+ if (ret) -+ goto out_cleanup; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ pm_runtime_put(&auxdev->dev); -+ -+ return 0; -+ -+out_cleanup: -+ isys_unregister_devices(isys); -+out_cleanup_fw: -+ ipu7_fw_isys_release(isys); -+out_cleanup_isys: -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ mutex_destroy(&isys->mutex); -+ mutex_destroy(&isys->stream_mutex); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+out_runtime_put: -+ pm_runtime_put(&auxdev->dev); -+ -+ return ret; -+} -+ -+struct ipu7_csi2_error { -+ const char *error_string; -+ bool is_info_only; -+}; -+ -+/* -+ * Strings corresponding to CSI-2 receiver errors are here. -+ * Corresponding macros are defined in the header file. -+ */ -+static const struct ipu7_csi2_error dphy_rx_errors[] = { -+ { "Error handler FIFO full", false }, -+ { "Reserved Short Packet encoding detected", true }, -+ { "Reserved Long Packet encoding detected", true }, -+ { "Received packet is too short", false}, -+ { "Received packet is too long", false}, -+ { "Short packet discarded due to errors", false }, -+ { "Long packet discarded due to errors", false }, -+ { "CSI Combo Rx interrupt", false }, -+ { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false }, -+ { "Received NULL packet", true }, -+ { "Received blanking packet", true }, -+ { "Tie to 0", true }, -+ { } -+}; -+ -+static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2) -+{ -+ u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port); -+ u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS); -+ u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK; -+ -+ if (!status) -+ return; -+ -+ dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n", -+ csi2->port, status); -+ -+ writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR); -+ csi2->receiver_errors |= status & mask; -+} -+ -+static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2) -+{ -+ struct ipu7_csi2_error const *errors; -+ unsigned int i; -+ u32 status; -+ -+ /* Register errors once more in case of error interrupts are disabled */ -+ ipu7_isys_register_errors(csi2); -+ status = csi2->receiver_errors; -+ csi2->receiver_errors = 0; -+ errors = dphy_rx_errors; -+ -+ for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) { -+ if (status & BIT(i)) -+ dev_err_ratelimited(&csi2->isys->adev->auxdev.dev, -+ "csi2-%i error: %s\n", -+ csi2->port, -+ errors[i].error_string); -+ } -+} -+ -+struct resp_to_msg { -+ enum ipu7_insys_resp_type type; -+ const char *msg; -+}; -+ -+static const struct resp_to_msg is_fw_msg[] = { -+ {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE, -+ "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, -+ "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"}, -+ {IPU_INSYS_RESP_TYPE_PIN_DATA_READY, -+ "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"}, -+ {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"}, -+ {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE, -+ "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"}, -+ {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, -+ "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"}, -+ {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"}, -+}; -+ -+int isys_isr_one(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ struct ipu7_isys_stream *stream = NULL; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_isys_csi2 *csi2 = NULL; -+ struct ia_gofo_msg_err err_info; -+ struct ipu7_insys_resp *resp; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ struct ipu7_isys_tpg *tpg = NULL; -+#endif -+ u64 ts; -+ -+ if (!isys->adev->syscom) -+ return 1; -+ -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_isys_get_log(isys); -+#endif -+ -+ resp = ipu7_fw_isys_get_resp(isys); -+ if (!resp) -+ return 1; -+ if (resp->type >= N_IPU_INSYS_RESP_TYPE) { -+ dev_err(dev, "Unknown response type %u stream %u\n", -+ resp->type, resp->stream_id); -+ ipu7_fw_isys_put_resp(isys); -+ return 1; -+ } -+ -+ err_info = resp->error_info; -+ ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0]; -+ if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE && -+ err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) { -+ /* receive a sp w/o command, firmware drop it */ -+ dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n", -+ resp->type, is_fw_msg[resp->type].msg, -+ resp->stream_id); -+ dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n", -+ resp->pin_id, resp->buf_id, resp->frame_id); -+ dev_dbg(dev, "\terror group %u code %u details [%u %u]\n", -+ err_info.err_group, err_info.err_code, -+ err_info.err_detail[0], err_info.err_detail[1]); -+ } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) { -+ dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -+ resp->pin_id, resp->buf_id, resp->frame_id); -+ dev_err(dev, "\terror group %u code %u details [%u %u]\n", -+ err_info.err_group, err_info.err_code, -+ err_info.err_detail[0], err_info.err_detail[1]); -+ } else { -+ dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n", -+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id, -+ resp->pin_id, resp->buf_id, resp->frame_id); -+ dev_dbg(dev, "\tts %llu\n", ts); -+ } -+ -+ if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) { -+ dev_err(dev, "bad stream handle %u\n", -+ resp->stream_id); -+ goto leave; -+ } -+ -+ stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id); -+ if (!stream) { -+ dev_err(dev, "stream of stream_handle %u is unused\n", -+ resp->stream_id); -+ goto leave; -+ } -+ -+ stream->error = err_info.err_code; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (stream->asd) { -+ if (stream->asd->is_tpg) -+ tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ else -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+ } -+#else -+ if (stream->asd) -+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd); -+#endif -+ -+ switch (resp->type) { -+ case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE: -+ complete(&stream->stream_open_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK: -+ complete(&stream->stream_close_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK: -+ complete(&stream->stream_start_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK: -+ complete(&stream->stream_stop_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK: -+ complete(&stream->stream_stop_completion); -+ break; -+ case IPU_INSYS_RESP_TYPE_PIN_DATA_READY: -+ /* -+ * firmware only release the capture msg until software -+ * get pin_data_ready event -+ */ -+ ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id); -+ if (resp->pin_id < IPU_INSYS_OUTPUT_PINS && -+ stream->output_pins[resp->pin_id].pin_ready) -+ stream->output_pins[resp->pin_id].pin_ready(stream, -+ resp); -+ else -+ dev_err(dev, "No handler for pin %u ready\n", -+ resp->pin_id); -+ if (csi2) -+ ipu7_isys_csi2_error(csi2); -+ -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK: -+ break; -+ case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE: -+ case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE: -+ break; -+ case IPU_INSYS_RESP_TYPE_FRAME_SOF: -+ if (csi2) -+ ipu7_isys_csi2_sof_event_by_stream(stream); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (tpg) -+ ipu7_isys_tpg_sof_event_by_stream(stream); -+ -+#endif -+ stream->seq[stream->seq_index].sequence = -+ atomic_read(&stream->sequence) - 1U; -+ stream->seq[stream->seq_index].timestamp = ts; -+ dev_dbg(dev, -+ "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n", -+ resp->stream_id, resp->frame_id, -+ stream->seq[stream->seq_index].sequence, ts); -+ stream->seq_index = (stream->seq_index + 1U) -+ % IPU_ISYS_MAX_PARALLEL_SOF; -+ break; -+ case IPU_INSYS_RESP_TYPE_FRAME_EOF: -+ if (csi2) -+ ipu7_isys_csi2_eof_event_by_stream(stream); -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ if (tpg) -+ ipu7_isys_tpg_eof_event_by_stream(stream); -+ -+#endif -+ dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n", -+ resp->stream_id, -+ stream->seq[stream->seq_index].sequence, ts); -+ break; -+ default: -+ dev_err(dev, "Unknown response type %u stream %u\n", -+ resp->type, resp->stream_id); -+ break; -+ } -+ -+ ipu7_isys_put_stream(stream); -+leave: -+ ipu7_fw_isys_put_resp(isys); -+ -+ return 0; -+} -+ -+static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2) -+{ -+ struct device *dev = &csi2->isys->adev->auxdev.dev; -+ struct ipu7_device *isp = csi2->isys->adev->isp; -+ struct ipu7_isys_stream *s; -+ u32 sync, offset; -+ u32 fe = 0; -+ u8 vc; -+ -+ ipu7_isys_register_errors(csi2); -+ -+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port); -+ sync = readl(csi2->base + offset + IRQ_CTL_STATUS); -+ writel(sync, csi2->base + offset + IRQ_CTL_CLEAR); -+ dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync); -+ -+ if (!is_ipu7(isp->hw_ver)) { -+ fe = readl(csi2->base + offset + IRQ1_CTL_STATUS); -+ writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR); -+ dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe); -+ } -+ -+ for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) { -+ s = ipu7_isys_query_stream_by_source(csi2->isys, -+ csi2->asd.source, vc); -+ if (!s) -+ continue; -+ -+ if (!is_ipu7(isp->hw_ver)) { -+ if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc)) -+ ipu7_isys_csi2_sof_event_by_stream(s); -+ -+ if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc)) -+ ipu7_isys_csi2_eof_event_by_stream(s); -+ } else { -+ if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2))) -+ ipu7_isys_csi2_sof_event_by_stream(s); -+ -+ if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2))) -+ ipu7_isys_csi2_eof_event_by_stream(s); -+ } -+ } -+} -+ -+static irqreturn_t isys_isr(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev); -+ u32 status_csi, status_sw, csi_offset, sw_offset; -+ struct device *dev = &isys->adev->auxdev.dev; -+ void __iomem *base = isys->pdata->base; -+ -+ spin_lock(&isys->power_lock); -+ if (!isys->power) { -+ spin_unlock(&isys->power_lock); -+ return IRQ_NONE; -+ } -+ -+ csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE; -+ sw_offset = IS_BASE; -+ -+ status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -+ status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -+ if (!status_csi && !status_sw) { -+ spin_unlock(&isys->power_lock); -+ return IRQ_NONE; -+ } -+ -+ if (status_csi) -+ dev_dbg(dev, "status csi 0x%08x\n", status_csi); -+ if (status_sw) -+ dev_dbg(dev, "status to_sw 0x%08x\n", status_sw); -+ -+ do { -+ writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR); -+ writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR); -+ -+ if (isys->isr_csi2_mask & status_csi) { -+ unsigned int i; -+ -+ for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) { -+ /* irq from not enabled port */ -+ if (!isys->csi2[i].base) -+ continue; -+ if (status_csi & isys->csi2[i].legacy_irq_mask) -+ ipu7_isys_csi2_isr(&isys->csi2[i]); -+ } -+ } -+ -+ if (!isys_isr_one(adev)) -+ status_sw = TO_SW_IRQ_FW; -+ else -+ status_sw = 0; -+ -+ status_csi = readl(base + csi_offset + IRQ_CTL_STATUS); -+ status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS); -+ } while ((status_csi & isys->isr_csi2_mask) || -+ (status_sw & TO_SW_IRQ_FW)); -+ -+ writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N); -+ -+ spin_unlock(&isys->power_lock); -+ -+ return IRQ_HANDLED; -+} -+ -+static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = { -+ .isr = isys_isr, -+ .isr_threaded = NULL, -+ .wake_isr_thread = false, -+}; -+ -+static const struct auxiliary_device_id ipu7_isys_id_table[] = { -+ { -+ .name = "intel_ipu7.isys", -+ .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data, -+ }, -+ { } -+}; -+MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table); -+ -+static struct auxiliary_driver isys_driver = { -+ .name = IPU_ISYS_NAME, -+ .probe = isys_probe, -+ .remove = isys_remove, -+ .id_table = ipu7_isys_id_table, -+ .driver = { -+ .pm = &isys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(isys_driver); -+ -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Tianshu Qiu "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 input system driver"); -+MODULE_IMPORT_NS("INTEL_IPU7"); -+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.h b/drivers/media/pci/intel/ipu7/ipu7-isys.h -new file mode 100644 -index 000000000000..c9ca2fbb4d1e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys.h -@@ -0,0 +1,187 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_H -+#define IPU7_ISYS_H -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_msg_abi.h" -+#include "abi/ipu7_fw_isys_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-isys-csi2.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+#include "ipu7-isys-tpg.h" -+#endif -+#include "ipu7-isys-video.h" -+ -+#ifdef CONFIG_DEBUG_FS -+struct dentry; -+ -+#endif -+#define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" -+ -+/* FW support max 16 streams */ -+#define IPU_ISYS_MAX_STREAMS 16U -+ -+/* -+ * Current message queue configuration. These must be big enough -+ * so that they never gets full. Queues are located in system memory -+ */ -+#define IPU_ISYS_SIZE_RECV_QUEUE 40U -+#define IPU_ISYS_SIZE_LOG_QUEUE 256U -+#define IPU_ISYS_SIZE_SEND_QUEUE 40U -+#define IPU_ISYS_NUM_RECV_QUEUE 1U -+ -+#define IPU_ISYS_MIN_WIDTH 2U -+#define IPU_ISYS_MIN_HEIGHT 2U -+#define IPU_ISYS_MAX_WIDTH 8160U -+#define IPU_ISYS_MAX_HEIGHT 8190U -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define RESET_STATE_IN_RESET 1U -+#define RESET_STATE_IN_STOP_STREAMING 2U -+ -+#endif -+#define FW_CALL_TIMEOUT_JIFFIES \ -+ msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) -+#endif -+ -+struct isys_fw_log { -+ struct mutex mutex; /* protect whole struct */ -+ void *head; -+ void *addr; -+ u32 count; /* running counter of log */ -+ u32 size; /* actual size of log content, in bits */ -+}; -+ -+/* -+ * struct ipu7_isys -+ * -+ * @media_dev: Media device -+ * @v4l2_dev: V4L2 device -+ * @adev: ISYS bus device -+ * @power: Is ISYS powered on or not? -+ * @isr_bits: Which bits does the ISR handle? -+ * @power_lock: Serialise access to power (power state in general) -+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers -+ * @streams_lock: serialise access to streams -+ * @streams: streams per firmware stream ID -+ * @syscom: fw communication layer context -+ #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ * @need_reset: Isys requires d0i0->i3 transition -+ #endif -+ * @ref_count: total number of callers fw open -+ * @mutex: serialise access isys video open/release related operations -+ * @stream_mutex: serialise stream start and stop, queueing requests -+ * @pdata: platform data pointer -+ * @csi2: CSI-2 receivers -+ #ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ * @tpg: test pattern generators -+ #endif -+ */ -+struct ipu7_isys { -+ struct media_device media_dev; -+ struct v4l2_device v4l2_dev; -+ struct ipu7_bus_device *adev; -+ -+ int power; -+ spinlock_t power_lock; /* Serialise access to power */ -+ u32 isr_csi2_mask; -+ u32 csi2_rx_ctrl_cached; -+ spinlock_t streams_lock; -+ struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS]; -+ int streams_ref_count[IPU_ISYS_MAX_STREAMS]; -+ u32 phy_rext_cal; -+ bool icache_prefetch; -+ bool csi2_cse_ipc_not_supported; -+ unsigned int ref_count; -+ unsigned int stream_opened; -+ -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif -+ struct mutex mutex; /* Serialise isys video open/release related */ -+ struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ -+ -+ struct ipu7_isys_pdata *pdata; -+ -+ struct ipu7_isys_csi2 *csi2; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ struct ipu7_isys_tpg *tpg; -+#endif -+ struct isys_fw_log *fw_log; -+ -+ struct list_head requests; -+ struct pm_qos_request pm_qos; -+ spinlock_t listlock; /* Protect framebuflist */ -+ struct list_head framebuflist; -+ struct list_head framebuflist_fw; -+ struct v4l2_async_notifier notifier; -+ -+ struct ipu7_insys_config *subsys_config; -+ dma_addr_t subsys_config_dma_addr; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct mutex reset_mutex; -+ bool need_reset; -+ int state; -+#endif -+}; -+ -+struct isys_fw_msgs { -+ union { -+ u64 dummy; -+ struct ipu7_insys_buffset frame; -+ struct ipu7_insys_stream_cfg stream; -+ } fw_msg; -+ struct list_head head; -+ dma_addr_t dma_addr; -+}; -+ -+struct ipu7_isys_csi2_config { -+ unsigned int nlanes; -+ unsigned int port; -+ enum v4l2_mbus_type bus_type; -+}; -+ -+struct ipu7_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu7_isys_subdev_info { -+ struct ipu7_isys_csi2_config *csi2; -+ struct ipu7_isys_subdev_i2c_info i2c; -+}; -+ -+struct ipu7_isys_subdev_pdata { -+ struct ipu7_isys_subdev_info **subdevs; -+}; -+ -+struct sensor_async_sd { -+ struct v4l2_async_connection asc; -+ struct ipu7_isys_csi2_config csi2; -+}; -+ -+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream); -+void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data); -+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys); -+int isys_isr_one(struct ipu7_bus_device *adev); -+void ipu7_isys_setup_hw(struct ipu7_isys *isys); -+#endif /* IPU7_ISYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.c b/drivers/media/pci/intel/ipu7/ipu7-mmu.c -new file mode 100644 -index 000000000000..e6989e3e59a1 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-mmu.c -@@ -0,0 +1,854 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-dma.h" -+#include "ipu7-mmu.h" -+#include "ipu7-platform-regs.h" -+ -+#define ISP_PAGE_SHIFT 12 -+#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT) -+#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1U)) -+ -+#define ISP_L1PT_SHIFT 22 -+#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1)) -+ -+#define ISP_L2PT_SHIFT 12 -+#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK)))) -+ -+#define ISP_L1PT_PTES 1024U -+#define ISP_L2PT_PTES 1024U -+ -+#define ISP_PADDR_SHIFT 12 -+ -+#define REG_L1_PHYS 0x0004 /* 27-bit pfn */ -+#define REG_INFO 0x0008 -+ -+#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT) -+ -+#define MMU_TLB_INVALIDATE_TIMEOUT 2000 -+ -+static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu) -+{ -+ unsigned int i; -+ u32 irq_cause; -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ irq_cause = readl(mmu->mmu_hw[i].base + MMU_REG_IRQ_CAUSE); -+ pr_info("mmu %s irq_cause = 0x%x", mmu->mmu_hw[i].name, -+ irq_cause); -+ writel(0x1ffff, mmu->mmu_hw[i].base + MMU_REG_IRQ_CLEAR); -+ } -+} -+ -+static void tlb_invalidate(struct ipu7_mmu *mmu) -+{ -+ unsigned long flags; -+ unsigned int i; -+ int ret; -+ u32 val; -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ if (!mmu->ready) { -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+ return; -+ } -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ writel(0xffffffffU, mmu->mmu_hw[i].base + -+ MMU_REG_INVALIDATE_0); -+ -+ /* Need check with HW, use l1streams or l2streams */ -+ if (mmu->mmu_hw[i].nr_l2streams > 32) -+ writel(0xffffffffU, mmu->mmu_hw[i].base + -+ MMU_REG_INVALIDATE_1); -+ -+ /* -+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles) -+ * When the actual MMIO write reaches the IPU TLB Invalidate -+ * register, wmb() will force the TLB invalidate out if the CPU -+ * attempts to update the IOMMU page table (or sooner). -+ */ -+ wmb(); -+ -+ /* wait invalidation done */ -+ ret = readl_poll_timeout_atomic(mmu->mmu_hw[i].base + -+ MMU_REG_INVALIDATION_STATUS, -+ val, !(val & 0x1U), 500, -+ MMU_TLB_INVALIDATE_TIMEOUT); -+ if (ret) -+ dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i); -+ } -+ -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+} -+ -+static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr) -+{ -+ dma_addr_t dma; -+ -+ dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL); -+ if (dma_mapping_error(mmu_info->dev, dma)) -+ return 0; -+ -+ return dma; -+} -+ -+static int get_dummy_page(struct ipu7_mmu_info *mmu_info) -+{ -+ void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ -+ if (!pt) -+ return -ENOMEM; -+ -+ dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt); -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map dummy page\n"); -+ goto err_free_page; -+ } -+ -+ mmu_info->dummy_page = pt; -+ mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT; -+ -+ return 0; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return -ENOMEM; -+} -+ -+static void free_dummy_page(struct ipu7_mmu_info *mmu_info) -+{ -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->dummy_page_pteval), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_page); -+} -+ -+static int alloc_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ unsigned int i; -+ -+ if (!pt) -+ return -ENOMEM; -+ -+ dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt); -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l2pt page\n"); -+ goto err_free_page; -+ } -+ -+ for (i = 0; i < ISP_L2PT_PTES; i++) -+ pt[i] = mmu_info->dummy_page_pteval; -+ -+ mmu_info->dummy_l2_pt = pt; -+ mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT; -+ -+ return 0; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return -ENOMEM; -+} -+ -+static void free_dummy_l2_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_l2_pt); -+} -+ -+static u32 *alloc_l1_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ dma_addr_t dma; -+ unsigned int i; -+ -+ if (!pt) -+ return NULL; -+ -+ dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt); -+ -+ for (i = 0; i < ISP_L1PT_PTES; i++) -+ pt[i] = mmu_info->dummy_l2_pteval; -+ -+ dma = map_single(mmu_info, pt); -+ if (!dma) { -+ dev_err(mmu_info->dev, "Failed to map l1pt page\n"); -+ goto err_free_page; -+ } -+ -+ mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT; -+ dev_dbg(mmu_info->dev, "l1 pt %p mapped at %pad\n", pt, &dma); -+ -+ return pt; -+ -+err_free_page: -+ free_page((unsigned long)pt); -+ return NULL; -+} -+ -+static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info) -+{ -+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32); -+ unsigned int i; -+ -+ if (!pt) -+ return NULL; -+ -+ dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt); -+ -+ for (i = 0; i < ISP_L1PT_PTES; i++) -+ pt[i] = mmu_info->dummy_page_pteval; -+ -+ return pt; -+} -+ -+static void l2_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t dummy, size_t size) -+{ -+ unsigned int l2_entries; -+ unsigned int l2_idx; -+ unsigned long flags; -+ u32 l1_idx; -+ u32 *l2_pt; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ for (l1_idx = iova >> ISP_L1PT_SHIFT; -+ size > 0U && l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ dev_dbg(mmu_info->dev, -+ "unmapping l2 pgtable (l1 index %u (iova 0x%8.8lx))\n", -+ l1_idx, iova); -+ -+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) { -+ dev_err(mmu_info->dev, -+ "unmap not mapped iova 0x%8.8lx l1 index %u\n", -+ iova, l1_idx); -+ continue; -+ } -+ l2_pt = mmu_info->l2_pts[l1_idx]; -+ -+ l2_entries = 0; -+ for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -+ size > 0U && l2_idx < ISP_L2PT_PTES; l2_idx++) { -+ phys_addr_t pteval = TBL_PHYS_ADDR(l2_pt[l2_idx]); -+ -+ dev_dbg(mmu_info->dev, -+ "unmap l2 index %u with pteval 0x%p\n", -+ l2_idx, &pteval); -+ l2_pt[l2_idx] = mmu_info->dummy_page_pteval; -+ -+ iova += ISP_PAGE_SIZE; -+ size -= ISP_PAGE_SIZE; -+ -+ l2_entries++; -+ } -+ -+ WARN_ON_ONCE(!l2_entries); -+ clflush_cache_range(&l2_pt[l2_idx - l2_entries], -+ sizeof(l2_pt[0]) * l2_entries); -+ } -+ -+ WARN_ON_ONCE(size); -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+} -+ -+static int l2_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ struct device *dev = mmu_info->dev; -+ unsigned int l2_entries; -+ u32 *l2_pt, *l2_virt; -+ unsigned int l2_idx; -+ unsigned long flags; -+ size_t mapped = 0; -+ dma_addr_t dma; -+ u32 l1_entry; -+ u32 l1_idx; -+ int err = 0; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ -+ paddr = ALIGN(paddr, ISP_PAGE_SIZE); -+ for (l1_idx = iova >> ISP_L1PT_SHIFT; -+ size && l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ dev_dbg(dev, -+ "mapping l2 page table for l1 index %u (iova %8.8x)\n", -+ l1_idx, (u32)iova); -+ -+ l1_entry = mmu_info->l1_pt[l1_idx]; -+ if (l1_entry == mmu_info->dummy_l2_pteval) { -+ l2_virt = mmu_info->l2_pts[l1_idx]; -+ if (likely(!l2_virt)) { -+ l2_virt = alloc_l2_pt(mmu_info); -+ if (!l2_virt) { -+ err = -ENOMEM; -+ goto error; -+ } -+ } -+ -+ dma = map_single(mmu_info, l2_virt); -+ if (!dma) { -+ dev_err(dev, "Failed to map l2pt page\n"); -+ free_page((unsigned long)l2_virt); -+ err = -EINVAL; -+ goto error; -+ } -+ -+ l1_entry = dma >> ISP_PADDR_SHIFT; -+ -+ dev_dbg(dev, "page for l1_idx %u %p allocated\n", -+ l1_idx, l2_virt); -+ mmu_info->l1_pt[l1_idx] = l1_entry; -+ mmu_info->l2_pts[l1_idx] = l2_virt; -+ -+ clflush_cache_range(&mmu_info->l1_pt[l1_idx], -+ sizeof(mmu_info->l1_pt[l1_idx])); -+ } -+ -+ l2_pt = mmu_info->l2_pts[l1_idx]; -+ l2_entries = 0; -+ -+ for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT; -+ size && l2_idx < ISP_L2PT_PTES; l2_idx++) { -+ l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT; -+ -+ dev_dbg(dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx, -+ l2_pt[l2_idx]); -+ -+ iova += ISP_PAGE_SIZE; -+ paddr += ISP_PAGE_SIZE; -+ mapped += ISP_PAGE_SIZE; -+ size -= ISP_PAGE_SIZE; -+ -+ l2_entries++; -+ } -+ -+ WARN_ON_ONCE(!l2_entries); -+ clflush_cache_range(&l2_pt[l2_idx - l2_entries], -+ sizeof(l2_pt[0]) * l2_entries); -+ } -+ -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ return 0; -+ -+error: -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ /* unroll mapping in case something went wrong */ -+ if (mapped) -+ l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped); -+ -+ return err; -+} -+ -+static int __ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ u32 iova_start = round_down(iova, ISP_PAGE_SIZE); -+ u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE); -+ -+ dev_dbg(mmu_info->dev, -+ "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr %pap\n", -+ iova_start, iova_end, size, &paddr); -+ -+ return l2_map(mmu_info, iova_start, paddr, size); -+} -+ -+static void __ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, -+ unsigned long iova, size_t size) -+{ -+ l2_unmap(mmu_info, iova, 0, size); -+} -+ -+static int allocate_trash_buffer(struct ipu7_mmu *mmu) -+{ -+ unsigned int n_pages = PFN_UP(IPU_MMUV2_TRASH_RANGE); -+ unsigned long iova_addr; -+ struct iova *iova; -+ unsigned int i; -+ dma_addr_t dma; -+ int ret; -+ -+ /* Allocate 8MB in iova range */ -+ iova = alloc_iova(&mmu->dmap->iovad, n_pages, -+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0); -+ if (!iova) { -+ dev_err(mmu->dev, "cannot allocate iova range for trash\n"); -+ return -ENOMEM; -+ } -+ -+ dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) { -+ dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n"); -+ ret = -ENOMEM; -+ goto out_free_iova; -+ } -+ -+ mmu->pci_trash_page = dma; -+ -+ /* -+ * Map the 8MB iova address range to the same physical trash page -+ * mmu->trash_page which is already reserved at the probe -+ */ -+ iova_addr = iova->pfn_lo; -+ for (i = 0; i < n_pages; i++) { -+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr), -+ mmu->pci_trash_page, PAGE_SIZE); -+ if (ret) { -+ dev_err(mmu->dev, -+ "mapping trash buffer range failed\n"); -+ goto out_unmap; -+ } -+ -+ iova_addr++; -+ } -+ -+ mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo); -+ dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n", -+ mmu->mmid, (unsigned int)mmu->iova_trash_page); -+ return 0; -+ -+out_unmap: -+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+out_free_iova: -+ __free_iova(&mmu->dmap->iovad, iova); -+ return ret; -+} -+ -+static void __mmu_at_init(struct ipu7_mmu *mmu) -+{ -+ struct ipu7_mmu_info *mmu_info; -+ unsigned int i; -+ -+ mmu_info = mmu->dmap->mmu_info; -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -+ unsigned int j; -+ -+ /* Write page table address per MMU */ -+ writel((phys_addr_t)mmu_info->l1_pt_dma, -+ mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR); -+ dev_dbg(mmu->dev, "mmu %s base was set as %x\n", mmu_hw->name, -+ readl(mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR)); -+ -+ /* Set info bits and axi_refill per MMU */ -+ writel(mmu_hw->info_bits, -+ mmu_hw->base + MMU_REG_USER_INFO_BITS); -+ writel(mmu_hw->refill, mmu_hw->base + MMU_REG_AXI_REFILL_IF_ID); -+ writel(mmu_hw->collapse_en_bitmap, -+ mmu_hw->base + MMU_REG_COLLAPSE_ENABLE_BITMAP); -+ -+ dev_dbg(mmu->dev, "mmu %s info_bits was set as %x\n", -+ mmu_hw->name, -+ readl(mmu_hw->base + MMU_REG_USER_INFO_BITS)); -+ -+ if (mmu_hw->at_sp_arb_cfg) -+ writel(mmu_hw->at_sp_arb_cfg, -+ mmu_hw->base + MMU_REG_AT_SP_ARB_CFG); -+ -+ /* default irq configuration */ -+ writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_MASK); -+ writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_ENABLE); -+ -+ /* Configure MMU TLB stream configuration for L1/L2 */ -+ for (j = 0; j < mmu_hw->nr_l1streams; j++) { -+ writel(mmu_hw->l1_block_sz[j], mmu_hw->base + -+ mmu_hw->l1_block + 4U * j); -+ } -+ -+ for (j = 0; j < mmu_hw->nr_l2streams; j++) { -+ writel(mmu_hw->l2_block_sz[j], mmu_hw->base + -+ mmu_hw->l2_block + 4U * j); -+ } -+ -+ for (j = 0; j < mmu_hw->uao_p_num; j++) { -+ if (!mmu_hw->uao_p2tlb[j]) -+ continue; -+ writel(mmu_hw->uao_p2tlb[j], mmu_hw->uao_base + 4U * j); -+ } -+ } -+} -+ -+static void __mmu_zlx_init(struct ipu7_mmu *mmu) -+{ -+ unsigned int i; -+ -+ dev_dbg(mmu->dev, "mmu zlx init\n"); -+ -+ for (i = 0; i < mmu->nr_mmus; i++) { -+ struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i]; -+ unsigned int j; -+ -+ dev_dbg(mmu->dev, "mmu %s zlx init\n", mmu_hw->name); -+ for (j = 0; j < IPU_ZLX_POOL_NUM; j++) { -+ if (!mmu_hw->zlx_axi_pool[j]) -+ continue; -+ writel(mmu_hw->zlx_axi_pool[j], -+ mmu_hw->zlx_base + ZLX_REG_AXI_POOL + j * 0x4U); -+ } -+ -+ for (j = 0; j < mmu_hw->zlx_nr; j++) { -+ if (!mmu_hw->zlx_conf[j]) -+ continue; -+ -+ writel(mmu_hw->zlx_conf[j], -+ mmu_hw->zlx_base + ZLX_REG_CONF + j * 0x8U); -+ } -+ -+ for (j = 0; j < mmu_hw->zlx_nr; j++) { -+ if (!mmu_hw->zlx_en[j]) -+ continue; -+ -+ writel(mmu_hw->zlx_en[j], -+ mmu_hw->zlx_base + ZLX_REG_EN + j * 0x8U); -+ } -+ } -+} -+ -+int ipu7_mmu_hw_init(struct ipu7_mmu *mmu) -+{ -+ unsigned long flags; -+ -+ dev_dbg(mmu->dev, "IPU mmu hardware init\n"); -+ -+ /* Initialise the each MMU and ZLX */ -+ __mmu_at_init(mmu); -+ __mmu_zlx_init(mmu); -+ -+ if (!mmu->trash_page) { -+ int ret; -+ -+ mmu->trash_page = alloc_page(GFP_KERNEL); -+ if (!mmu->trash_page) { -+ dev_err(mmu->dev, "insufficient memory for trash buffer\n"); -+ return -ENOMEM; -+ } -+ -+ ret = allocate_trash_buffer(mmu); -+ if (ret) { -+ __free_page(mmu->trash_page); -+ mmu->trash_page = NULL; -+ dev_err(mmu->dev, "trash buffer allocation failed\n"); -+ return ret; -+ } -+ } -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ mmu->ready = true; -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+ -+ return 0; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_init, "INTEL_IPU7"); -+ -+static struct ipu7_mmu_info *ipu7_mmu_alloc(struct ipu7_device *isp) -+{ -+ struct ipu7_mmu_info *mmu_info; -+ int ret; -+ -+ mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL); -+ if (!mmu_info) -+ return NULL; -+ -+ if (isp->secure_mode) { -+ mmu_info->aperture_start = IPU_FW_CODE_REGION_END; -+ mmu_info->aperture_end = -+ (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS); -+ } else { -+ mmu_info->aperture_start = IPU_FW_CODE_REGION_START; -+ mmu_info->aperture_end = -+ (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS_NON_SECURE); -+ } -+ -+ mmu_info->pgsize_bitmap = SZ_4K; -+ mmu_info->dev = &isp->pdev->dev; -+ -+ ret = get_dummy_page(mmu_info); -+ if (ret) -+ goto err_free_info; -+ -+ ret = alloc_dummy_l2_pt(mmu_info); -+ if (ret) -+ goto err_free_dummy_page; -+ -+ mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts)); -+ if (!mmu_info->l2_pts) -+ goto err_free_dummy_l2_pt; -+ -+ /* -+ * We always map the L1 page table (a single page as well as -+ * the L2 page tables). -+ */ -+ mmu_info->l1_pt = alloc_l1_pt(mmu_info); -+ if (!mmu_info->l1_pt) -+ goto err_free_l2_pts; -+ -+ spin_lock_init(&mmu_info->lock); -+ -+ dev_dbg(mmu_info->dev, "domain initialised\n"); -+ -+ return mmu_info; -+ -+err_free_l2_pts: -+ vfree(mmu_info->l2_pts); -+err_free_dummy_l2_pt: -+ free_dummy_l2_pt(mmu_info); -+err_free_dummy_page: -+ free_dummy_page(mmu_info); -+err_free_info: -+ kfree(mmu_info); -+ -+ return NULL; -+} -+ -+void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&mmu->ready_lock, flags); -+ mmu->ready = false; -+ spin_unlock_irqrestore(&mmu->ready_lock, flags); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_cleanup, "INTEL_IPU7"); -+ -+static struct ipu7_dma_mapping *alloc_dma_mapping(struct ipu7_device *isp) -+{ -+ struct ipu7_dma_mapping *dmap; -+ unsigned long base_pfn; -+ -+ dmap = kzalloc(sizeof(*dmap), GFP_KERNEL); -+ if (!dmap) -+ return NULL; -+ -+ dmap->mmu_info = ipu7_mmu_alloc(isp); -+ if (!dmap->mmu_info) { -+ kfree(dmap); -+ return NULL; -+ } -+ -+ /* 0~64M is forbidden for uctile controller */ -+ base_pfn = max_t(unsigned long, 1, -+ PFN_DOWN(dmap->mmu_info->aperture_start)); -+ init_iova_domain(&dmap->iovad, SZ_4K, base_pfn); -+ dmap->mmu_info->dmap = dmap; -+ -+ dev_dbg(&isp->pdev->dev, "alloc mapping\n"); -+ -+ iova_cache_get(); -+ -+ return dmap; -+} -+ -+phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -+ dma_addr_t iova) -+{ -+ phys_addr_t phy_addr; -+ unsigned long flags; -+ u32 *l2_pt; -+ -+ spin_lock_irqsave(&mmu_info->lock, flags); -+ l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT]; -+ phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT]; -+ phy_addr <<= ISP_PAGE_SHIFT; -+ spin_unlock_irqrestore(&mmu_info->lock, flags); -+ -+ return phy_addr; -+} -+ -+void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ size_t size) -+{ -+ unsigned int min_pagesz; -+ -+ dev_dbg(mmu_info->dev, "unmapping iova 0x%lx size 0x%zx\n", iova, size); -+ -+ /* find out the minimum page size supported */ -+ min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -+ -+ /* -+ * The virtual address and the size of the mapping must be -+ * aligned (at least) to the size of the smallest page supported -+ * by the hardware -+ */ -+ if (!IS_ALIGNED(iova | size, min_pagesz)) { -+ dev_err(mmu_info->dev, -+ "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n", -+ iova, size, min_pagesz); -+ return; -+ } -+ -+ __ipu7_mmu_unmap(mmu_info, iova, size); -+} -+ -+int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size) -+{ -+ unsigned int min_pagesz; -+ -+ if (mmu_info->pgsize_bitmap == 0UL) -+ return -ENODEV; -+ -+ /* find out the minimum page size supported */ -+ min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap); -+ -+ /* -+ * both the virtual address and the physical one, as well as -+ * the size of the mapping, must be aligned (at least) to the -+ * size of the smallest page supported by the hardware -+ */ -+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) { -+ dev_err(mmu_info->dev, -+ "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n", -+ iova, &paddr, size, min_pagesz); -+ return -EINVAL; -+ } -+ -+ dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n", -+ iova, &paddr, size); -+ -+ return __ipu7_mmu_map(mmu_info, iova, paddr, size); -+} -+ -+static void ipu7_mmu_destroy(struct ipu7_mmu *mmu) -+{ -+ struct ipu7_dma_mapping *dmap = mmu->dmap; -+ struct ipu7_mmu_info *mmu_info = dmap->mmu_info; -+ struct iova *iova; -+ u32 l1_idx; -+ -+ if (mmu->iova_trash_page) { -+ iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page)); -+ if (iova) { -+ /* unmap and free the trash buffer iova */ -+ ipu7_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo), -+ PFN_PHYS(iova_size(iova))); -+ __free_iova(&dmap->iovad, iova); -+ } else { -+ dev_err(mmu->dev, "trash buffer iova not found.\n"); -+ } -+ -+ mmu->iova_trash_page = 0; -+ dma_unmap_page(mmu_info->dev, mmu->pci_trash_page, -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ mmu->pci_trash_page = 0; -+ __free_page(mmu->trash_page); -+ } -+ -+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) { -+ if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) { -+ dma_unmap_single(mmu_info->dev, -+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->l2_pts[l1_idx]); -+ } -+ } -+ -+ vfree(mmu_info->l2_pts); -+ free_dummy_page(mmu_info); -+ dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma), -+ PAGE_SIZE, DMA_BIDIRECTIONAL); -+ free_page((unsigned long)mmu_info->dummy_l2_pt); -+ free_page((unsigned long)mmu_info->l1_pt); -+ kfree(mmu_info); -+} -+ -+struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -+ void __iomem *base, int mmid, -+ const struct ipu7_hw_variants *hw) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(to_pci_dev(dev)); -+ struct ipu7_mmu_pdata *pdata; -+ struct ipu7_mmu *mmu; -+ unsigned int i; -+ -+ if (hw->nr_mmus > IPU_MMU_MAX_NUM) -+ return ERR_PTR(-EINVAL); -+ -+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ for (i = 0; i < hw->nr_mmus; i++) { -+ struct ipu7_mmu_hw *pdata_mmu = &pdata->mmu_hw[i]; -+ const struct ipu7_mmu_hw *src_mmu = &hw->mmu_hw[i]; -+ -+ if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS || -+ src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS) -+ return ERR_PTR(-EINVAL); -+ -+ *pdata_mmu = *src_mmu; -+ pdata_mmu->base = base + src_mmu->offset; -+ pdata_mmu->zlx_base = base + src_mmu->zlx_offset; -+ pdata_mmu->uao_base = base + src_mmu->uao_offset; -+ } -+ -+ mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL); -+ if (!mmu) -+ return ERR_PTR(-ENOMEM); -+ -+ mmu->mmid = mmid; -+ mmu->mmu_hw = pdata->mmu_hw; -+ mmu->nr_mmus = hw->nr_mmus; -+ mmu->tlb_invalidate = tlb_invalidate; -+ mmu->ready = false; -+ INIT_LIST_HEAD(&mmu->vma_list); -+ spin_lock_init(&mmu->ready_lock); -+ -+ mmu->dmap = alloc_dma_mapping(isp); -+ if (!mmu->dmap) { -+ dev_err(dev, "can't alloc dma mapping\n"); -+ return ERR_PTR(-ENOMEM); -+ } -+ -+ return mmu; -+} -+ -+void ipu7_mmu_cleanup(struct ipu7_mmu *mmu) -+{ -+ struct ipu7_dma_mapping *dmap = mmu->dmap; -+ -+ ipu7_mmu_destroy(mmu); -+ mmu->dmap = NULL; -+ iova_cache_put(); -+ put_iova_domain(&dmap->iovad); -+ kfree(dmap); -+} -diff --git a/drivers/media/pci/intel/ipu7/ipu7-mmu.h b/drivers/media/pci/intel/ipu7/ipu7-mmu.h -new file mode 100644 -index 000000000000..d85bb8ffc711 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-mmu.h -@@ -0,0 +1,414 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_MMU_H -+#define IPU7_MMU_H -+ -+#include -+#include -+#include -+#include -+ -+struct device; -+struct page; -+struct ipu7_hw_variants; -+struct ipu7_mmu; -+struct ipu7_mmu_info; -+ -+#define ISYS_MMID 0x1 -+#define PSYS_MMID 0x0 -+ -+/* IPU7 for LNL */ -+/* IS MMU Cmd RD */ -+#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000 -+#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3 -+#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Cmd WR */ -+#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000 -+#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3 -+#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Data WR Snoop */ -+#define IPU7_IS_MMU_M0_OFFSET 0x276000 -+#define IPU7_IS_MMU_M0_STREAM_NUM 8 -+#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74 -+ -+/* IS MMU Data WR ISOC */ -+#define IPU7_IS_MMU_M1_OFFSET 0x277000 -+#define IPU7_IS_MMU_M1_STREAM_NUM 16 -+#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54 -+#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -+ -+/* PS MMU FW RD */ -+#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000 -+#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20 -+#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4 -+ -+/* PS MMU FW WR */ -+#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000 -+#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10 -+#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -+ -+/* PS MMU FW Data RD VC0 */ -+#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000 -+#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40 -+#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4 -+ -+/* PS MMU FW Data WR VC0 */ -+#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000 -+#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40 -+#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4 -+ -+/* IS UAO UC RD */ -+#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000 -+#define IPU7_IS_UAO_UC_RD_PLANENUM 4 -+ -+/* IS UAO UC WR */ -+#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000 -+#define IPU7_IS_UAO_UC_WR_PLANENUM 4 -+ -+/* IS UAO M0 WR */ -+#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000 -+#define IPU7_IS_UAO_M0_WR_PLANENUM 8 -+ -+/* IS UAO M1 WR */ -+#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000 -+#define IPU7_IS_UAO_M1_WR_PLANENUM 16 -+ -+/* PS UAO FW RD */ -+#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000 -+#define IPU7_PS_UAO_FW_RD_PLANENUM 20 -+ -+/* PS UAO FW WR */ -+#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000 -+#define IPU7_PS_UAO_FW_WR_PLANENUM 16 -+ -+/* PS UAO SRT RD */ -+#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000 -+#define IPU7_PS_UAO_SRT_RD_PLANENUM 40 -+ -+/* PS UAO SRT WR */ -+#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000 -+#define IPU7_PS_UAO_SRT_WR_PLANENUM 40 -+ -+#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000 -+#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000 -+#define IPU7_IS_ZLX_M0_OFFSET 0x27a000 -+#define IPU7_IS_ZLX_M1_OFFSET 0x27b000 -+#define IPU7_IS_ZLX_UC_RD_NUM 4 -+#define IPU7_IS_ZLX_UC_WR_NUM 4 -+#define IPU7_IS_ZLX_M0_NUM 8 -+#define IPU7_IS_ZLX_M1_NUM 16 -+ -+#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000 -+#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000 -+#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000 -+#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000 -+#define IPU7_PS_ZLX_DATA_RD_NUM 32 -+#define IPU7_PS_ZLX_DATA_WR_NUM 32 -+#define IPU7_PS_ZLX_FW_RD_NUM 16 -+#define IPU7_PS_ZLX_FW_WR_NUM 10 -+ -+/* IPU7P5 for PTL */ -+/* IS MMU Cmd RD */ -+#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000 -+#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3 -+#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Cmd WR */ -+#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000 -+#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3 -+#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Data WR Snoop */ -+#define IPU7P5_IS_MMU_M0_OFFSET 0x276000 -+#define IPU7P5_IS_MMU_M0_STREAM_NUM 16 -+#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -+ -+/* IS MMU Data WR ISOC */ -+#define IPU7P5_IS_MMU_M1_OFFSET 0x277000 -+#define IPU7P5_IS_MMU_M1_STREAM_NUM 16 -+#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -+ -+/* PS MMU FW RD */ -+#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000 -+#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16 -+#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94 -+ -+/* PS MMU FW WR */ -+#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000 -+#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10 -+#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c -+ -+/* PS MMU FW Data RD VC0 */ -+#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000 -+#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22 -+#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac -+ -+/* PS MMU FW Data WR VC0 */ -+#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000 -+#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32 -+#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 -+#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4 -+ -+/* IS UAO UC RD */ -+#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000 -+#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4 -+ -+/* IS UAO UC WR */ -+#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000 -+#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4 -+ -+/* IS UAO M0 WR */ -+#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000 -+#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16 -+ -+/* IS UAO M1 WR */ -+#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000 -+#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16 -+ -+/* PS UAO FW RD */ -+#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000 -+#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16 -+ -+/* PS UAO FW WR */ -+#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000 -+#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10 -+ -+/* PS UAO SRT RD */ -+#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000 -+#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22 -+ -+/* PS UAO SRT WR */ -+#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000 -+#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32 -+ -+#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000 -+#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000 -+#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000 -+#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000 -+#define IPU7P5_IS_ZLX_UC_RD_NUM 4 -+#define IPU7P5_IS_ZLX_UC_WR_NUM 4 -+#define IPU7P5_IS_ZLX_M0_NUM 16 -+#define IPU7P5_IS_ZLX_M1_NUM 16 -+ -+#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000 -+#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000 -+#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000 -+#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000 -+#define IPU7P5_PS_ZLX_DATA_RD_NUM 22 -+#define IPU7P5_PS_ZLX_DATA_WR_NUM 32 -+#define IPU7P5_PS_ZLX_FW_RD_NUM 16 -+#define IPU7P5_PS_ZLX_FW_WR_NUM 10 -+ -+/* IS MMU Cmd RD */ -+#define IPU8_IS_MMU_FW_RD_OFFSET 0x270000 -+#define IPU8_IS_MMU_FW_RD_STREAM_NUM 3 -+#define IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Cmd WR */ -+#define IPU8_IS_MMU_FW_WR_OFFSET 0x271000 -+#define IPU8_IS_MMU_FW_WR_STREAM_NUM 3 -+#define IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60 -+ -+/* IS MMU Data WR Snoop */ -+#define IPU8_IS_MMU_M0_OFFSET 0x272000 -+#define IPU8_IS_MMU_M0_STREAM_NUM 16 -+#define IPU8_IS_MMU_M0_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_M0_L2_BLOCKNR_REG 0x94 -+ -+/* IS MMU Data WR ISOC */ -+#define IPU8_IS_MMU_M1_OFFSET 0x273000 -+#define IPU8_IS_MMU_M1_STREAM_NUM 16 -+#define IPU8_IS_MMU_M1_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_M1_L2_BLOCKNR_REG 0x94 -+ -+/* IS MMU UPIPE ISOC */ -+#define IPU8_IS_MMU_UPIPE_OFFSET 0x274000 -+#define IPU8_IS_MMU_UPIPE_STREAM_NUM 6 -+#define IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG 0x54 -+#define IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG 0x6c -+ -+/* PS MMU FW RD */ -+#define IPU8_PS_MMU_FW_RD_OFFSET 0x148000 -+#define IPU8_PS_MMU_FW_RD_STREAM_NUM 12 -+#define IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x84 -+ -+/* PS MMU FW WR */ -+#define IPU8_PS_MMU_FW_WR_OFFSET 0x149000 -+#define IPU8_PS_MMU_FW_WR_STREAM_NUM 8 -+#define IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x74 -+ -+/* PS MMU FW Data RD VC0 */ -+#define IPU8_PS_MMU_SRT_RD_OFFSET 0x14a000 -+#define IPU8_PS_MMU_SRT_RD_STREAM_NUM 26 -+#define IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xbc -+ -+/* PS MMU FW Data WR VC0 */ -+#define IPU8_PS_MMU_SRT_WR_OFFSET 0x14b000 -+#define IPU8_PS_MMU_SRT_WR_STREAM_NUM 26 -+#define IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54 -+#define IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xbc -+ -+/* IS UAO UC RD */ -+#define IPU8_IS_UAO_UC_RD_OFFSET 0x27a000 -+#define IPU8_IS_UAO_UC_RD_PLANENUM 4 -+ -+/* IS UAO UC WR */ -+#define IPU8_IS_UAO_UC_WR_OFFSET 0x27b000 -+#define IPU8_IS_UAO_UC_WR_PLANENUM 4 -+ -+/* IS UAO M0 WR */ -+#define IPU8_IS_UAO_M0_WR_OFFSET 0x27c000 -+#define IPU8_IS_UAO_M0_WR_PLANENUM 16 -+ -+/* IS UAO M1 WR */ -+#define IPU8_IS_UAO_M1_WR_OFFSET 0x27d000 -+#define IPU8_IS_UAO_M1_WR_PLANENUM 16 -+ -+/* IS UAO UPIPE */ -+#define IPU8_IS_UAO_UPIPE_OFFSET 0x27e000 -+#define IPU8_IS_UAO_UPIPE_PLANENUM 6 -+ -+/* PS UAO FW RD */ -+#define IPU8_PS_UAO_FW_RD_OFFSET 0x156000 -+#define IPU8_PS_UAO_FW_RD_PLANENUM 12 -+ -+/* PS UAO FW WR */ -+#define IPU8_PS_UAO_FW_WR_OFFSET 0x157000 -+#define IPU8_PS_UAO_FW_WR_PLANENUM 8 -+ -+/* PS UAO SRT RD */ -+#define IPU8_PS_UAO_SRT_RD_OFFSET 0x154000 -+#define IPU8_PS_UAO_SRT_RD_PLANENUM 26 -+ -+/* PS UAO SRT WR */ -+#define IPU8_PS_UAO_SRT_WR_OFFSET 0x155000 -+#define IPU8_PS_UAO_SRT_WR_PLANENUM 26 -+ -+#define IPU8_IS_ZLX_UC_RD_OFFSET 0x275000 -+#define IPU8_IS_ZLX_UC_WR_OFFSET 0x276000 -+#define IPU8_IS_ZLX_M0_OFFSET 0x277000 -+#define IPU8_IS_ZLX_M1_OFFSET 0x278000 -+#define IPU8_IS_ZLX_UPIPE_OFFSET 0x279000 -+#define IPU8_IS_ZLX_UC_RD_NUM 4 -+#define IPU8_IS_ZLX_UC_WR_NUM 4 -+#define IPU8_IS_ZLX_M0_NUM 16 -+#define IPU8_IS_ZLX_M1_NUM 16 -+#define IPU8_IS_ZLX_UPIPE_NUM 6 -+ -+#define IPU8_PS_ZLX_DATA_RD_OFFSET 0x14e000 -+#define IPU8_PS_ZLX_DATA_WR_OFFSET 0x14f000 -+#define IPU8_PS_ZLX_FW_RD_OFFSET 0x150000 -+#define IPU8_PS_ZLX_FW_WR_OFFSET 0x151000 -+#define IPU8_PS_ZLX_DATA_RD_NUM 26 -+#define IPU8_PS_ZLX_DATA_WR_NUM 26 -+#define IPU8_PS_ZLX_FW_RD_NUM 12 -+#define IPU8_PS_ZLX_FW_WR_NUM 8 -+ -+#define MMU_REG_INVALIDATE_0 0x00 -+#define MMU_REG_INVALIDATE_1 0x04 -+#define MMU_REG_PAGE_TABLE_BASE_ADDR 0x08 -+#define MMU_REG_USER_INFO_BITS 0x0c -+#define MMU_REG_AXI_REFILL_IF_ID 0x10 -+#define MMU_REG_PW_EN_BITMAP 0x14 -+#define MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18 -+#define MMU_REG_GENERAL_REG 0x1c -+#define MMU_REG_AT_SP_ARB_CFG 0x20 -+#define MMU_REG_INVALIDATION_STATUS 0x24 -+#define MMU_REG_IRQ_LEVEL_NO_PULSE 0x28 -+#define MMU_REG_IRQ_MASK 0x2c -+#define MMU_REG_IRQ_ENABLE 0x30 -+#define MMU_REG_IRQ_EDGE 0x34 -+#define MMU_REG_IRQ_CLEAR 0x38 -+#define MMU_REG_IRQ_CAUSE 0x3c -+#define MMU_REG_CG_CTRL_BITS 0x40 -+#define MMU_REG_RD_FIFOS_STATUS 0x44 -+#define MMU_REG_WR_FIFOS_STATUS 0x48 -+#define MMU_REG_COMMON_FIFOS_STATUS 0x4c -+#define MMU_REG_FSM_STATUS 0x50 -+ -+#define ZLX_REG_AXI_POOL 0x0 -+#define ZLX_REG_EN 0x20 -+#define ZLX_REG_CONF 0x24 -+#define ZLX_REG_CG_CTRL 0x900 -+#define ZLX_REG_FORCE_BYPASS 0x904 -+ -+struct ipu7_mmu_info { -+ struct device *dev; -+ -+ u32 *l1_pt; -+ u32 l1_pt_dma; -+ u32 **l2_pts; -+ -+ u32 *dummy_l2_pt; -+ u32 dummy_l2_pteval; -+ void *dummy_page; -+ u32 dummy_page_pteval; -+ -+ dma_addr_t aperture_start; -+ dma_addr_t aperture_end; -+ unsigned long pgsize_bitmap; -+ -+ spinlock_t lock; /* Serialize access to users */ -+ struct ipu7_dma_mapping *dmap; -+}; -+ -+struct ipu7_mmu { -+ struct list_head node; -+ -+ struct ipu7_mmu_hw *mmu_hw; -+ unsigned int nr_mmus; -+ unsigned int mmid; -+ -+ phys_addr_t pgtbl; -+ struct device *dev; -+ -+ struct ipu7_dma_mapping *dmap; -+ struct list_head vma_list; -+ -+ struct page *trash_page; -+ dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */ -+ dma_addr_t iova_trash_page; /* IOVA for IPU child nodes to use */ -+ -+ bool ready; -+ spinlock_t ready_lock; /* Serialize access to bool ready */ -+ -+ void (*tlb_invalidate)(struct ipu7_mmu *mmu); -+}; -+ -+struct ipu7_mmu *ipu7_mmu_init(struct device *dev, -+ void __iomem *base, int mmid, -+ const struct ipu7_hw_variants *hw); -+void ipu7_mmu_cleanup(struct ipu7_mmu *mmu); -+int ipu7_mmu_hw_init(struct ipu7_mmu *mmu); -+void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu); -+int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ phys_addr_t paddr, size_t size); -+void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova, -+ size_t size); -+phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info, -+ dma_addr_t iova); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h b/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -new file mode 100644 -index 000000000000..eeadc886a8cf ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-platform-regs.h -@@ -0,0 +1,82 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2018 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_PLATFORM_REGS_H -+#define IPU7_PLATFORM_REGS_H -+ -+#define IS_BASE 0x230000 -+#define IS_UC_CTRL_BASE (IS_BASE + 0x0) -+ -+#define PS_BASE 0x130000 -+#define PS_UC_CTRL_BASE (PS_BASE + 0x0) -+ -+/* -+ * bit 0: IRQ from FW, -+ * bit 1, 2 and 3: IRQ from HW -+ */ -+#define TO_SW_IRQ_MASK 0xf -+#define TO_SW_IRQ_FW BIT(0) -+ -+#define FW_CODE_BASE 0x0 -+#define FW_DATA_BASE 0x4 -+#define PRINTF_EN_THROUGH_TRACE 0x3004 -+#define PRINTF_EN_DIRECTLY_TO_DDR 0x3008 -+#define PRINTF_DDR_BASE_ADDR 0x300c -+#define PRINTF_DDR_SIZE 0x3010 -+#define PRINTF_DDR_NEXT_ADDR 0x3014 -+#define PRINTF_STATUS 0x3018 -+#define PRINTF_AXI_CNTL 0x301c -+#define PRINTF_MSG_LENGTH 0x3020 -+#define TO_SW_IRQ_CNTL_EDGE 0x4000 -+#define TO_SW_IRQ_CNTL_MASK_N 0x4004 -+#define TO_SW_IRQ_CNTL_STATUS 0x4008 -+#define TO_SW_IRQ_CNTL_CLEAR 0x400c -+#define TO_SW_IRQ_CNTL_ENABLE 0x4010 -+#define TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x4014 -+#define ERR_IRQ_CNTL_EDGE 0x4018 -+#define ERR_IRQ_CNTL_MASK_N 0x401c -+#define ERR_IRQ_CNTL_STATUS 0x4020 -+#define ERR_IRQ_CNTL_CLEAR 0x4024 -+#define ERR_IRQ_CNTL_ENABLE 0x4028 -+#define ERR_IRQ_CNTL_LEVEL_NOT_PULSE 0x402c -+#define LOCAL_DMEM_BASE_ADDR 0x1300000 -+ -+/* -+ * IS_UC_TO_SW irqs -+ * bit 0: IRQ from local FW -+ * bit 1~3: IRQ from HW -+ */ -+#define IS_UC_TO_SW_IRQ_MASK 0xf -+ -+#define IPU_ISYS_SPC_OFFSET 0x210000 -+#define IPU7_PSYS_SPC_OFFSET 0x118000 -+#define IPU_ISYS_DMEM_OFFSET 0x200000 -+#define IPU_PSYS_DMEM_OFFSET 0x100000 -+ -+#define IPU7_ISYS_CSI_PORT_NUM 4 -+ -+/* IRQ-related registers in PSYS */ -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_EDGE 0x134000 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK 0x134004 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS 0x134008 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR 0x13400c -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE 0x134010 -+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x134014 -+#define IRQ_FROM_LOCAL_FW BIT(0) -+ -+/* -+ * psys subdomains power request regs -+ */ -+enum ipu7_device_buttress_psys_domain_pos { -+ IPU_PSYS_SUBDOMAIN_LB = 0, -+ IPU_PSYS_SUBDOMAIN_BB = 1, -+}; -+ -+#define IPU7_PSYS_DOMAIN_POWER_MASK (BIT(IPU_PSYS_SUBDOMAIN_LB) | \ -+ BIT(IPU_PSYS_SUBDOMAIN_BB)) -+#define IPU8_PSYS_DOMAIN_POWER_MASK BIT(IPU_PSYS_SUBDOMAIN_LB) -+#define IPU_PSYS_DOMAIN_POWER_IN_PROGRESS BIT(31) -+ -+#endif /* IPU7_PLATFORM_REGS_H */ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.c b/drivers/media/pci/intel/ipu7/ipu7-syscom.c -new file mode 100644 -index 000000000000..1a1c8cb02d83 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-syscom.c -@@ -0,0 +1,79 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+ -+#include "abi/ipu7_fw_syscom_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-syscom.h" -+ -+static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx, -+ u32 q) -+{ -+ return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s)); -+} -+ -+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q) -+{ -+ struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -+ void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -+ u32 write_index = readl(queue_indices + -+ offsetof(struct syscom_queue_indices_s, -+ write_index)); -+ u32 read_index = readl(queue_indices + -+ offsetof(struct syscom_queue_indices_s, -+ read_index)); -+ void *token = NULL; -+ -+ if (q < ctx->num_output_queues) { -+ /* Output queue */ -+ bool empty = (write_index == read_index); -+ -+ if (!empty) -+ token = queue_params->token_array_mem + -+ read_index * -+ queue_params->token_size_in_bytes; -+ } else { -+ /* Input queue */ -+ bool full = (read_index == ((write_index + 1U) % -+ (u32)queue_params->max_capacity)); -+ -+ if (!full) -+ token = queue_params->token_array_mem + -+ write_index * queue_params->token_size_in_bytes; -+ } -+ return token; -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7"); -+ -+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q) -+{ -+ struct syscom_queue_config *queue_params = &ctx->queue_configs[q]; -+ void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q); -+ u32 offset, index; -+ -+ if (q < ctx->num_output_queues) -+ /* Output queue */ -+ offset = offsetof(struct syscom_queue_indices_s, read_index); -+ -+ else -+ /* Input queue */ -+ offset = offsetof(struct syscom_queue_indices_s, write_index); -+ -+ index = readl(queue_indices + offset); -+ writel((index + 1U) % queue_params->max_capacity, -+ queue_indices + offset); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7"); -+ -+struct syscom_queue_params_config * -+ipu7_syscom_get_queue_config(struct syscom_config_s *config) -+{ -+ return (struct syscom_queue_params_config *)(&config[1]); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7-syscom.h b/drivers/media/pci/intel/ipu7/ipu7-syscom.h -new file mode 100644 -index 000000000000..e1fbe3b7914e ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7-syscom.h -@@ -0,0 +1,35 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_SYSCOM_H -+#define IPU7_SYSCOM_H -+ -+#include -+ -+struct syscom_config_s; -+struct syscom_queue_params_config; -+ -+struct syscom_queue_config { -+ void *token_array_mem; -+ u32 queue_size; -+ u16 token_size_in_bytes; -+ u16 max_capacity; -+}; -+ -+struct ipu7_syscom_context { -+ u16 num_input_queues; -+ u16 num_output_queues; -+ struct syscom_queue_config *queue_configs; -+ void __iomem *queue_indices; -+ dma_addr_t queue_mem_dma_addr; -+ void *queue_mem; -+ u32 queue_mem_size; -+}; -+ -+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q); -+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q); -+struct syscom_queue_params_config * -+ipu7_syscom_get_queue_config(struct syscom_config_s *config); -+#endif -diff --git a/drivers/media/pci/intel/ipu7/ipu7.c b/drivers/media/pci/intel/ipu7/ipu7.c -new file mode 100644 -index 000000000000..a76863eea356 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7.c -@@ -0,0 +1,2864 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+#include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "abi/ipu7_fw_common_abi.h" -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-mmu.h" -+#include "ipu7-platform-regs.h" -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+#include -+ -+#endif -+#define IPU_PCI_BAR 0 -+#define IPU_PCI_PBBAR 4 -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+static const unsigned int ipu7_tpg_offsets[] = { -+ MGC_MG_PORT(0), -+ MGC_MG_PORT(1), -+ MGC_MG_PORT(2), -+}; -+#endif -+ -+static const unsigned int ipu7_csi_offsets[] = { -+ IPU_CSI_PORT_A_ADDR_OFFSET, -+ IPU_CSI_PORT_B_ADDR_OFFSET, -+ IPU_CSI_PORT_C_ADDR_OFFSET, -+ IPU_CSI_PORT_D_ADDR_OFFSET, -+}; -+ -+static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = { -+ .csi2 = { -+ .gpreg = IS_IO_CSI2_GPREGS_BASE, -+ }, -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7P5_IS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "IS_FW_RD", -+ .offset = IPU7P5_IS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET, -+ .info_bits = 0x20005101, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0 -+ }, -+ .zlx_conf = { -+ 0x0, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004c, -+ 0x0000004d, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_FW_WR", -+ .offset = IPU7P5_IS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET, -+ .info_bits = 0x20005001, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x00010101, -+ 0x00010101, -+ 0x0, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004a, -+ 0x0000004b, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_ISOC", -+ .offset = IPU7P5_IS_MMU_M0_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET, -+ .info_bits = 0x20004e01, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_M0_NUM, -+ .zlx_axi_pool = { -+ 0x00000f10, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x00000041, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_SNOOP", -+ .offset = IPU7P5_IS_MMU_M1_OFFSET, -+ .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET, -+ .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET, -+ .info_bits = 0x20004f01, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -+ .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU7P5_IS_ZLX_M1_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ }, -+ .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ }, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7P5_PS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "PS_FW_RD", -+ .offset = IPU7P5_PS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET, -+ .info_bits = 0x20004001, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000f, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001a, -+ 0x0000001a, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0, 1, 1, 0, 0, -+ 0, 1, 1, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00000000, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00010101, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00010101, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002e, -+ 0x00000035, -+ 0x00000036, -+ 0x00000031, -+ 0x00000037, -+ 0x00000038, -+ 0x00000039, -+ 0x00000032, -+ 0x00000033, -+ 0x0000003a, -+ 0x0000003b, -+ 0x0000003c, -+ 0x00000034, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_FW_WR", -+ .offset = IPU7P5_PS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET, -+ .info_bits = 0x20003e01, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000010, -+ 0x00000010, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00000000, -+ 0x00010101, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002e, -+ 0x0000002f, -+ 0x00000030, -+ 0x00000031, -+ 0x00000032, -+ 0x00000033, -+ 0x00000034, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_DATA_RD", -+ .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET, -+ .info_bits = 0x20003f01, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000b, -+ 0x0000000d, -+ 0x0000000f, -+ 0x00000013, -+ 0x00000017, -+ 0x00000019, -+ 0x0000001b, -+ 0x0000001d, -+ 0x0000001f, -+ 0x0000002b, -+ 0x00000033, -+ 0x0000003f, -+ 0x00000047, -+ 0x00000049, -+ 0x0000004b, -+ 0x0000004c, -+ 0x0000004d, -+ 0x0000004e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00030303, -+ 0x00010101, -+ 0x00010101, -+ 0x00030202, -+ 0x00010101, -+ 0x00010101, -+ 0x00030303, -+ 0x00030303, -+ 0x00010101, -+ 0x00030800, -+ 0x00030500, -+ 0x00020101, -+ 0x00042000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00020400, -+ 0x00010101, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000001c, -+ 0x0000001d, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ 0x00000022, -+ 0x00000023, -+ 0x00000024, -+ 0x00000025, -+ 0x00000026, -+ 0x00000027, -+ 0x00000028, -+ 0x00000029, -+ 0x0000002a, -+ 0x0000002b, -+ 0x0000002c, -+ 0x0000002d, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "PS_DATA_WR", -+ .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET, -+ .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET, -+ .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET, -+ .info_bits = 0x20003d01, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -+ .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000006, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000028, -+ 0x0000002a, -+ 0x00000036, -+ 0x0000003e, -+ 0x00000040, -+ 0x00000042, -+ 0x0000004e, -+ 0x00000056, -+ 0x0000005c, -+ 0x00000068, -+ 0x00000070, -+ 0x00000076, -+ 0x00000077, -+ 0x00000078, -+ 0x00000079, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000006, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000028, -+ 0x0000002a, -+ 0x00000036, -+ 0x0000003e, -+ 0x00000040, -+ 0x00000042, -+ 0x0000004e, -+ 0x00000056, -+ 0x0000005c, -+ 0x00000068, -+ 0x00000070, -+ 0x00000076, -+ 0x00000077, -+ 0x00000078, -+ 0x00000079, -+ }, -+ .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f50, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 0, 0, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00010102, -+ 0x00030103, -+ 0x00030103, -+ 0x00010101, -+ 0x00010101, -+ 0x00030101, -+ 0x00010101, -+ 0x38010101, -+ 0x00000000, -+ 0x00000000, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x00030303, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00010101, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000000, -+ 0x00000001, -+ 0x00000002, -+ 0x00000003, -+ 0x00000004, -+ 0x00000005, -+ 0x00000006, -+ 0x00000007, -+ 0x00000008, -+ 0x00000009, -+ 0x0000000a, -+ 0x0000000b, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000015, -+ 0x00000016, -+ 0x00000017, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001b, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ }, -+ }, -+ .dmem_offset = IPU_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static struct ipu_isys_internal_pdata ipu7_isys_ipdata = { -+ .csi2 = { -+ .gpreg = IS_IO_CSI2_GPREGS_BASE, -+ }, -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7_IS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "IS_FW_RD", -+ .offset = IPU7_IS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET, -+ .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET, -+ .info_bits = 0x20006701, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 0, 0, 0 -+ }, -+ .zlx_conf = { -+ 0x0, 0x0, 0x0, 0x0, -+ }, -+ .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000061, -+ 0x00000064, -+ 0x00000065, -+ }, -+ }, -+ { -+ .name = "IS_FW_WR", -+ .offset = IPU7_IS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET, -+ .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET, -+ .info_bits = 0x20006801, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000061, -+ 0x00000062, -+ 0x00000063, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_ISOC", -+ .offset = IPU7_IS_MMU_M0_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_M0_OFFSET, -+ .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET, -+ .info_bits = 0x20006601, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_M0_NUM, -+ .zlx_axi_pool = { -+ 0x00000f10, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004a, -+ 0x0000004b, -+ 0x0000004c, -+ 0x0000004d, -+ 0x0000004e, -+ 0x0000004f, -+ 0x00000050, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_SNOOP", -+ .offset = IPU7_IS_MMU_M1_OFFSET, -+ .zlx_offset = IPU7_IS_ZLX_M1_OFFSET, -+ .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET, -+ .info_bits = 0x20006901, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG, -+ .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM, -+ .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x3, 0x6, 0x9, 0xc, -+ 0xe, 0x10, 0x12, 0x14, 0x16, -+ 0x18, 0x1a, 0x1c, 0x1e, 0x20, -+ 0x22, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, -+ }, -+ .zlx_nr = IPU7_IS_ZLX_M1_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010103, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000051, -+ 0x00000052, -+ 0x00000053, -+ 0x00000054, -+ 0x00000055, -+ 0x00000056, -+ 0x00000057, -+ 0x00000058, -+ 0x00000059, -+ 0x0000005a, -+ 0x0000005b, -+ 0x0000005c, -+ 0x0000005d, -+ 0x0000005e, -+ 0x0000005f, -+ 0x00000060, -+ }, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu_psys_internal_pdata ipu7_psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU7_PS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "PS_FW_RD", -+ .offset = IPU7_PS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET, -+ .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET, -+ .info_bits = 0x20004801, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0, 0x8, 0xa, 0xc, 0xd, -+ 0xf, 0x11, 0x12, 0x13, 0x14, -+ 0x16, 0x18, 0x19, 0x1a, 0x1a, -+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, 0x20, 0x22, 0x24, 0x26, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ }, -+ .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000036, -+ 0x0000003d, -+ 0x0000003e, -+ 0x00000039, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x0000003a, -+ 0x0000003b, -+ 0x00000042, -+ 0x00000043, -+ 0x00000044, -+ 0x0000003c, -+ }, -+ }, -+ { -+ .name = "PS_FW_WR", -+ .offset = IPU7_PS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET, -+ .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET, -+ .info_bits = 0x20004601, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0, 0x8, 0xa, 0xc, 0xd, -+ 0xe, 0xf, 0x10, 0x10, 0x10, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, 0, 0, 0, 0, -+ 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x00010101, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000036, -+ 0x00000037, -+ 0x00000038, -+ 0x00000039, -+ 0x0000003a, -+ 0x0000003b, -+ 0x0000003c, -+ }, -+ }, -+ { -+ .name = "PS_DATA_RD", -+ .offset = IPU7_PS_MMU_SRT_RD_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET, -+ .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET, -+ .info_bits = 0x20004701, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x4, 0x6, 0x8, 0xb, -+ 0xd, 0xf, 0x11, 0x13, 0x15, -+ 0x17, 0x23, 0x2b, 0x37, 0x3f, -+ 0x41, 0x43, 0x44, 0x45, 0x46, -+ 0x47, 0x48, 0x49, 0x4a, 0x4b, -+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, -+ 0x51, 0x52, 0x53, 0x55, 0x57, -+ 0x59, 0x5b, 0x5d, 0x5f, 0x61, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, 0x20, 0x22, 0x24, 0x26, -+ 0x28, 0x2a, 0x2c, 0x2e, 0x30, -+ 0x32, 0x34, 0x36, 0x38, 0x3a, -+ 0x3c, 0x3e, 0x40, 0x42, 0x44, -+ 0x46, 0x48, 0x4a, 0x4c, 0x4e, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00030303, -+ 0x00010101, -+ 0x00010101, -+ 0x00030202, -+ 0x00010101, -+ 0x00010101, -+ 0x00010101, -+ 0x00030800, -+ 0x00030500, -+ 0x00020101, -+ 0x00042000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00020400, -+ 0x00010101, -+ }, -+ .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000022, -+ 0x00000023, -+ 0x00000024, -+ 0x00000025, -+ 0x00000026, -+ 0x00000027, -+ 0x00000028, -+ 0x00000029, -+ 0x0000002a, -+ 0x0000002b, -+ 0x0000002c, -+ 0x0000002d, -+ 0x0000002e, -+ 0x0000002f, -+ 0x00000030, -+ 0x00000031, -+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ 0x00000032, -+ 0x00000033, -+ 0x00000034, -+ 0x00000035, -+ }, -+ }, -+ { -+ .name = "PS_DATA_WR", -+ .offset = IPU7_PS_MMU_SRT_WR_OFFSET, -+ .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET, -+ .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET, -+ .info_bits = 0x20004501, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x0, -+ .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -+ .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x2, 0x6, 0xa, 0xc, -+ 0xe, 0x10, 0x12, 0x14, 0x16, -+ 0x18, 0x1a, 0x1c, 0x1e, 0x20, -+ 0x22, 0x24, 0x26, 0x32, 0x3a, -+ 0x3c, 0x3e, 0x4a, 0x52, 0x58, -+ 0x64, 0x6c, 0x72, 0x7e, 0x86, -+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90, -+ 0x91, 0x92, 0x94, 0x96, 0x98, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, 0x6, 0x8, -+ 0xa, 0xc, 0xe, 0x10, 0x12, -+ 0x14, 0x16, 0x18, 0x1a, 0x1c, -+ 0x1e, 0x20, 0x22, 0x24, 0x26, -+ 0x28, 0x2a, 0x2c, 0x2e, 0x30, -+ 0x32, 0x34, 0x36, 0x38, 0x3a, -+ 0x3c, 0x3e, 0x40, 0x42, 0x44, -+ 0x46, 0x48, 0x4a, 0x4c, 0x4e, -+ }, -+ .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f50, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 0, 0, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x00010102, -+ 0x00030103, -+ 0x00030103, -+ 0x00010101, -+ 0x00010101, -+ 0x00030101, -+ 0x00010101, -+ 0x38010101, -+ 0x0, -+ 0x0, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x38010101, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00010101, -+ 0x00010101, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x00042000, -+ 0x00031000, -+ 0x00031000, -+ 0x0, -+ 0x0, -+ }, -+ .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000000, -+ 0x00000001, -+ 0x00000002, -+ 0x00000003, -+ 0x00000004, -+ 0x00000005, -+ 0x00000006, -+ 0x00000007, -+ 0x00000008, -+ 0x00000009, -+ 0x0000000a, -+ 0x0000000b, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000015, -+ 0x00000016, -+ 0x00000017, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001b, -+ 0x0000001c, -+ 0x0000001d, -+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ }, -+ }, -+ }, -+ .dmem_offset = IPU_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static struct ipu_isys_internal_pdata ipu8_isys_ipdata = { -+ .csi2 = { -+ .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE, -+ }, -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU8_IS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "IS_FW_RD", -+ .offset = IPU8_IS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_UC_RD_OFFSET, -+ .uao_offset = IPU8_IS_UAO_UC_RD_OFFSET, -+ .info_bits = 0x20005101, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_UC_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0 -+ }, -+ .zlx_conf = { -+ 0, 2, 0, 0 -+ }, -+ .uao_p_num = IPU8_IS_UAO_UC_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004c, -+ 0x0000004d, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_FW_WR", -+ .offset = IPU8_IS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_UC_WR_OFFSET, -+ .uao_offset = IPU8_IS_UAO_UC_WR_OFFSET, -+ .info_bits = 0x20005001, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x0, 0x8, 0xa, -+ }, -+ .l2_block_sz = { -+ 0x0, 0x2, 0x4, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_UC_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x2, -+ 0x2, -+ 0x0, -+ }, -+ .uao_p_num = IPU8_IS_UAO_UC_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000049, -+ 0x0000004a, -+ 0x0000004b, -+ 0x00000000, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_ISOC", -+ .offset = IPU8_IS_MMU_M0_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_M0_OFFSET, -+ .uao_offset = IPU8_IS_UAO_M0_WR_OFFSET, -+ .info_bits = 0x20004e01, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_M0_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_M0_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_M0_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_M0_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_M0_NUM, -+ .zlx_axi_pool = { -+ 0x00000f10, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ }, -+ .uao_p_num = IPU8_IS_UAO_M0_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ 0x0000003b, -+ 0x0000003c, -+ 0x0000003d, -+ 0x0000003e, -+ }, -+ }, -+ { -+ .name = "IS_DATA_WR_SNOOP", -+ .offset = IPU8_IS_MMU_M1_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_M1_OFFSET, -+ .uao_offset = IPU8_IS_UAO_M1_WR_OFFSET, -+ .info_bits = 0x20004f01, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_M1_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_M1_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_M1_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_M1_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_M1_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ }, -+ .uao_p_num = IPU8_IS_UAO_M1_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ 0x0000003f, -+ 0x00000040, -+ 0x00000041, -+ 0x00000042, -+ }, -+ }, -+ { -+ .name = "IS_UPIPE", -+ .offset = IPU8_IS_MMU_UPIPE_OFFSET, -+ .zlx_offset = IPU8_IS_ZLX_UPIPE_OFFSET, -+ .uao_offset = IPU8_IS_UAO_UPIPE_OFFSET, -+ .info_bits = 0x20005201, -+ .refill = 0x00002928, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG, -+ .l2_block = IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -+ .nr_l2streams = IPU8_IS_MMU_UPIPE_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ }, -+ .zlx_nr = IPU8_IS_ZLX_UPIPE_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ 0x3, -+ }, -+ .uao_p_num = IPU8_IS_UAO_UPIPE_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000043, -+ 0x00000044, -+ 0x00000045, -+ 0x00000046, -+ 0x00000047, -+ 0x00000048, -+ }, -+ }, -+ }, -+ .cdc_fifos = 3, -+ .cdc_fifo_threshold = {6, 8, 2}, -+ .dmem_offset = IPU_ISYS_DMEM_OFFSET, -+ .spc_offset = IPU_ISYS_SPC_OFFSET, -+ }, -+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN, -+}; -+ -+static struct ipu_psys_internal_pdata ipu8_psys_ipdata = { -+ .hw_variant = { -+ .offset = IPU_UNIFIED_OFFSET, -+ .nr_mmus = IPU8_PS_MMU_NUM, -+ .mmu_hw = { -+ { -+ .name = "PS_FW_RD", -+ .offset = IPU8_PS_MMU_FW_RD_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_FW_RD_OFFSET, -+ .uao_offset = IPU8_PS_UAO_FW_RD_OFFSET, -+ .info_bits = 0x20003a01, -+ .refill = 0x00002726, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_FW_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x00000018, -+ 0x00000018, -+ 0x00000018, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_FW_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 0, 1, 0, 0, 1, 1, 0, 0, -+ 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, -+ 0x2, -+ 0x0, -+ 0x0, -+ 0x2, -+ 0x2, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_FW_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002d, -+ 0x00000032, -+ 0x00000033, -+ 0x00000030, -+ 0x00000034, -+ 0x00000035, -+ 0x00000036, -+ 0x00000031, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_FW_WR", -+ .offset = IPU8_PS_MMU_FW_WR_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_FW_WR_OFFSET, -+ .uao_offset = IPU8_PS_UAO_FW_WR_OFFSET, -+ .info_bits = 0x20003901, -+ .refill = 0x00002524, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_FW_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000010, -+ 0x00000010, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_FW_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f20, -+ }, -+ .zlx_en = { -+ 0, 1, 1, 0, 0, 0, 0, 0, -+ }, -+ .zlx_conf = { -+ 0x0, 0x2, 0x2, 0x0, -+ 0x0, 0x0, 0x0, 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_FW_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x0000002d, -+ 0x0000002e, -+ 0x0000002f, -+ 0x00000030, -+ 0x00000031, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_DATA_RD", -+ .offset = IPU8_PS_MMU_SRT_RD_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_DATA_RD_OFFSET, -+ .uao_offset = IPU8_PS_UAO_SRT_RD_OFFSET, -+ .info_bits = 0x20003801, -+ .refill = 0x00002322, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000014, -+ 0x00000018, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002c, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ 0x00000036, -+ 0x0000003a, -+ 0x0000003c, -+ 0x0000003c, -+ 0x0000003c, -+ 0x0000003c, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002c, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_DATA_RD_NUM, -+ .zlx_axi_pool = { -+ 0x00000f30, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 0, 0, -+ 0, 0, -+ }, -+ .zlx_conf = { -+ 0x6, 0x3, 0x3, 0x6, -+ 0x2, 0x2, 0x6, 0x6, -+ 0x6, 0x3, 0x6, 0x3, -+ 0x3, 0x2, 0x2, 0x2, -+ 0x2, 0x2, 0x2, 0x6, -+ 0x6, 0x3, 0x0, 0x0, -+ 0x0, 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_SRT_RD_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000017, -+ 0x00000018, -+ 0x00000019, -+ 0x0000001a, -+ 0x0000001b, -+ 0x0000001c, -+ 0x0000001d, -+ 0x0000001e, -+ 0x0000001f, -+ 0x00000020, -+ 0x00000021, -+ 0x00000022, -+ 0x00000023, -+ 0x00000024, -+ 0x00000025, -+ 0x00000026, -+ 0x00000027, -+ 0x00000028, -+ 0x00000029, -+ 0x0000002a, -+ 0x0000002b, -+ 0x0000002c, -+ 0x0, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ }, -+ { -+ .name = "PS_DATA_WR", -+ .offset = IPU8_PS_MMU_SRT_WR_OFFSET, -+ .zlx_offset = IPU8_PS_ZLX_DATA_WR_OFFSET, -+ .uao_offset = IPU8_PS_UAO_SRT_WR_OFFSET, -+ .info_bits = 0x20003701, -+ .refill = 0x00002120, -+ .collapse_en_bitmap = 0x1, -+ .at_sp_arb_cfg = 0x1, -+ .l1_block = IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG, -+ .l2_block = IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG, -+ .nr_l1streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -+ .nr_l2streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM, -+ .l1_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000022, -+ 0x00000024, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ 0x00000036, -+ 0x00000038, -+ 0x0000003a, -+ 0x0000003a, -+ 0x0000003a, -+ }, -+ .l2_block_sz = { -+ 0x00000000, -+ 0x00000002, -+ 0x00000004, -+ 0x00000006, -+ 0x00000008, -+ 0x0000000a, -+ 0x0000000c, -+ 0x0000000e, -+ 0x00000010, -+ 0x00000012, -+ 0x00000014, -+ 0x00000016, -+ 0x00000018, -+ 0x0000001a, -+ 0x0000001c, -+ 0x0000001e, -+ 0x00000020, -+ 0x00000022, -+ 0x00000024, -+ 0x00000026, -+ 0x00000028, -+ 0x0000002a, -+ 0x0000002c, -+ 0x0000002e, -+ 0x00000030, -+ 0x00000032, -+ }, -+ .zlx_nr = IPU8_PS_ZLX_DATA_WR_NUM, -+ .zlx_axi_pool = { -+ 0x00000f50, -+ }, -+ .zlx_en = { -+ 1, 1, 1, 0, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 1, -+ 1, 1, 1, 1, 1, 1, 1, 0, -+ 0, 0, -+ }, -+ .zlx_conf = { -+ 0x3, -+ 0x6, -+ 0x38000002, -+ 0x38000000, -+ 0x3, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x38000002, -+ 0x6, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x3, -+ 0x6, -+ 0x3, -+ 0x3, -+ 0x0, -+ 0x0, -+ 0x0, -+ }, -+ .uao_p_num = IPU8_PS_UAO_SRT_WR_PLANENUM, -+ .uao_p2tlb = { -+ 0x00000000, -+ 0x00000001, -+ 0x00000002, -+ 0x00000003, -+ 0x00000004, -+ 0x00000005, -+ 0x00000006, -+ 0x00000007, -+ 0x00000008, -+ 0x00000009, -+ 0x0000000a, -+ 0x0000000b, -+ 0x0000000c, -+ 0x0000000d, -+ 0x0000000e, -+ 0x0000000f, -+ 0x00000010, -+ 0x00000011, -+ 0x00000012, -+ 0x00000013, -+ 0x00000014, -+ 0x00000015, -+ 0x00000016, -+ 0x00000000, -+ 0x00000000, -+ 0x00000000, -+ }, -+ }, -+ }, -+ .dmem_offset = IPU_PSYS_DMEM_OFFSET, -+ }, -+}; -+ -+static const struct ipu_buttress_ctrl ipu7_isys_buttress_ctrl = { -+ .subsys_id = IPU_IS, -+ .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+ .ovrd_clk = BUTTRESS_OVERRIDE_IS_CLK, -+ .own_clk_ack = BUTTRESS_OWN_ACK_IS_CLK, -+}; -+ -+static const struct ipu_buttress_ctrl ipu7_psys_buttress_ctrl = { -+ .subsys_id = IPU_PS, -+ .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+ .ovrd_clk = BUTTRESS_OVERRIDE_PS_CLK, -+ .own_clk_ack = BUTTRESS_OWN_ACK_PS_CLK, -+}; -+ -+static const struct ipu_buttress_ctrl ipu8_isys_buttress_ctrl = { -+ .subsys_id = IPU_IS, -+ .ratio = IPU8_IS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+}; -+ -+static const struct ipu_buttress_ctrl ipu8_psys_buttress_ctrl = { -+ .subsys_id = IPU_PS, -+ .ratio = IPU8_PS_FREQ_CTL_DEFAULT_RATIO, -+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT, -+ .cdyn = IPU_FREQ_CTL_CDYN, -+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT, -+ .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ, -+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT, -+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK, -+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE, -+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE, -+ .own_clk_ack = BUTTRESS_OWN_ACK_PS_PLL, -+}; -+ -+void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -+ struct ipu_psys_internal_pdata *psys_ipdata) -+{ -+ isys_ipdata->csi2.nports = ARRAY_SIZE(ipu7_csi_offsets); -+ isys_ipdata->csi2.offsets = ipu7_csi_offsets; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ isys_ipdata->tpg.ntpgs = ARRAY_SIZE(ipu7_tpg_offsets); -+ isys_ipdata->tpg.offsets = ipu7_tpg_offsets; -+ isys_ipdata->tpg.sels = NULL; -+#endif -+ isys_ipdata->num_parallel_streams = IPU7_ISYS_NUM_STREAMS; -+ psys_ipdata->hw_variant.spc_offset = IPU7_PSYS_SPC_OFFSET; -+} -+ -+static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) -+{ -+ struct fwnode_handle *endpoint; -+ -+ if (IS_ERR_OR_NULL(fwnode)) -+ return -EINVAL; -+ -+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL); -+ if (endpoint) { -+ fwnode_handle_put(endpoint); -+ return 0; -+ } -+ -+ return ipu7_isys_check_fwnode_graph(fwnode->secondary); -+} -+ -+static struct ipu7_bus_device * -+ipu7_isys_init(struct pci_dev *pdev, struct device *parent, -+ const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ struct ipu7_isys_subdev_pdata *spdata, -+ const struct ipu_isys_internal_pdata *ipdata, -+ unsigned int nr) -+{ -+ struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -+ struct ipu7_bus_device *isys_adev; -+ struct device *dev = &pdev->dev; -+ struct ipu7_isys_pdata *pdata; -+ int ret; -+ -+ ret = ipu7_isys_check_fwnode_graph(fwnode); -+ if (ret) { -+ if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) { -+ dev_err(dev, -+ "fwnode graph has no endpoints connection\n"); -+ return ERR_PTR(-EINVAL); -+ } -+ -+ ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb); -+ if (ret) { -+ dev_err_probe(dev, ret, "IPU bridge init failed\n"); -+ return ERR_PTR(ret); -+ } -+ } -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ pdata->base = base; -+ pdata->ipdata = ipdata; -+ pdata->spdata = spdata; -+ -+ isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -+ IPU_ISYS_NAME); -+ if (IS_ERR(isys_adev)) { -+ dev_err_probe(dev, PTR_ERR(isys_adev), -+ "ipu7_bus_initialize_device isys failed\n"); -+ kfree(pdata); -+ return ERR_CAST(isys_adev); -+ } -+ -+ isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID, -+ &ipdata->hw_variant); -+ if (IS_ERR(isys_adev->mmu)) { -+ dev_err_probe(dev, PTR_ERR(isys_adev->mmu), -+ "ipu7_mmu_init(isys_adev->mmu) failed\n"); -+ put_device(&isys_adev->auxdev.dev); -+ kfree(pdata); -+ return ERR_CAST(isys_adev->mmu); -+ } -+ -+ isys_adev->mmu->dev = &isys_adev->auxdev.dev; -+ isys_adev->subsys = IPU_IS; -+ -+ ret = ipu7_bus_add_device(isys_adev); -+ if (ret) { -+ kfree(pdata); -+ return ERR_PTR(ret); -+ } -+ -+ return isys_adev; -+} -+ -+static struct ipu7_bus_device * -+ipu7_psys_init(struct pci_dev *pdev, struct device *parent, -+ const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ const struct ipu_psys_internal_pdata *ipdata, unsigned int nr) -+{ -+ struct ipu7_bus_device *psys_adev; -+ struct ipu7_psys_pdata *pdata; -+ int ret; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return ERR_PTR(-ENOMEM); -+ -+ pdata->base = base; -+ pdata->ipdata = ipdata; -+ -+ psys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, -+ IPU_PSYS_NAME); -+ if (IS_ERR(psys_adev)) { -+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev), -+ "ipu7_bus_initialize_device psys failed\n"); -+ kfree(pdata); -+ return ERR_CAST(psys_adev); -+ } -+ -+ psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID, -+ &ipdata->hw_variant); -+ if (IS_ERR(psys_adev->mmu)) { -+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu), -+ "ipu7_mmu_init(psys_adev->mmu) failed\n"); -+ put_device(&psys_adev->auxdev.dev); -+ kfree(pdata); -+ return ERR_CAST(psys_adev->mmu); -+ } -+ -+ psys_adev->mmu->dev = &psys_adev->auxdev.dev; -+ psys_adev->subsys = IPU_PS; -+ -+ ret = ipu7_bus_add_device(psys_adev); -+ if (ret) { -+ kfree(pdata); -+ return ERR_PTR(ret); -+ } -+ -+ return psys_adev; -+} -+ -+static struct ia_gofo_msg_log_info_ts fw_error_log[IPU_SUBSYS_NUM]; -+void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) -+{ -+ void __iomem *reg = adev->isp->base + ((adev->subsys == IPU_IS) ? -+ BUTTRESS_REG_FW_GP24 : -+ BUTTRESS_REG_FW_GP8); -+ -+ memcpy_fromio(&fw_error_log[adev->subsys], reg, -+ sizeof(fw_error_log[adev->subsys])); -+} -+EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); -+ -+#ifdef CONFIG_DEBUG_FS -+static struct debugfs_blob_wrapper isys_fw_error; -+static struct debugfs_blob_wrapper psys_fw_error; -+ -+static int ipu7_init_debugfs(struct ipu7_device *isp) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -+ if (!dir) -+ return -ENOMEM; -+ -+ isys_fw_error.data = &fw_error_log[IPU_IS]; -+ isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); -+ file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); -+ if (!file) -+ goto err; -+ psys_fw_error.data = &fw_error_log[IPU_PS]; -+ psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); -+ file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); -+ if (!file) -+ goto err; -+ -+ isp->ipu7_dir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+ -+static void ipu7_remove_debugfs(struct ipu7_device *isp) -+{ -+ /* -+ * Since isys and psys debugfs dir will be created under ipu root dir, -+ * mark its dentry to NULL to avoid duplicate removal. -+ */ -+ debugfs_remove_recursive(isp->ipu7_dir); -+ isp->ipu7_dir = NULL; -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+static void ipu7_pci_config_setup(struct pci_dev *dev) -+{ -+ u16 pci_command; -+ -+ pci_read_config_word(dev, PCI_COMMAND, &pci_command); -+ pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; -+ pci_write_config_word(dev, PCI_COMMAND, pci_command); -+} -+ -+static int ipu7_map_fw_code_region(struct ipu7_bus_device *sys, -+ void *data, size_t size) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct sg_table *sgt = &sys->fw_sgt; -+ struct ipu7_device *isp = adev->isp; -+ struct pci_dev *pdev = isp->pdev; -+ unsigned long n_pages, i; -+ unsigned long attr = 0; -+ struct page **pages; -+ int ret; -+ -+ n_pages = PFN_UP(size); -+ -+ pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL); -+ if (!pages) -+ return -ENOMEM; -+ -+ for (i = 0; i < n_pages; i++) { -+ struct page *p = vmalloc_to_page(data); -+ -+ if (!p) { -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ pages[i] = p; -+ data += PAGE_SIZE; -+ } -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size, -+ GFP_KERNEL); -+ if (ret) { -+ ret = -ENOMEM; -+ goto out; -+ } -+ -+ if (!isp->secure_mode) -+ attr |= DMA_ATTR_RESERVE_REGION; -+ -+ ret = dma_map_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+ if (ret < 0) { -+ dev_err(dev, "map fw code[%lu pages %u nents] failed\n", -+ n_pages, sgt->nents); -+ ret = -ENOMEM; -+ sg_free_table(sgt); -+ goto out; -+ } -+ -+ ret = ipu7_dma_map_sgtable(sys, sgt, DMA_BIDIRECTIONAL, attr); -+ if (ret) { -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+ sg_free_table(sgt); -+ goto out; -+ } -+ -+ ipu7_dma_sync_sgtable(sys, sgt); -+ -+ dev_dbg(dev, "fw code region mapped at 0x%pad entries %d\n", -+ &sgt->sgl->dma_address, sgt->nents); -+ -+out: -+ kfree(pages); -+ -+ return ret; -+} -+ -+static void ipu7_unmap_fw_code_region(struct ipu7_bus_device *sys) -+{ -+ struct pci_dev *pdev = sys->isp->pdev; -+ struct sg_table *sgt = &sys->fw_sgt; -+ -+ ipu7_dma_unmap_sgtable(sys, sgt, DMA_BIDIRECTIONAL, 0); -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+ sg_free_table(sgt); -+} -+ -+static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, -+ const char *sys_name) -+{ -+ struct device *dev = &sys->auxdev.dev; -+ struct ipu7_device *isp = sys->isp; -+ int ret; -+ -+ /* Copy FW binaries to specific location. */ -+ ret = ipu7_cpd_copy_binary(isp->cpd_fw->data, sys_name, -+ isp->fw_code_region, &sys->fw_entry); -+ if (ret) { -+ dev_err(dev, "%s binary not found.\n", sys_name); -+ return ret; -+ } -+ -+ ret = pm_runtime_get_sync(dev); -+ if (ret < 0) { -+ dev_err(dev, "Failed to get runtime PM\n"); -+ return ret; -+ } -+ -+ ret = ipu7_mmu_hw_init(sys->mmu); -+ if (ret) { -+ dev_err(dev, "Failed to set mmu hw\n"); -+ pm_runtime_put(dev); -+ return ret; -+ } -+ -+ /* Map code region. */ -+ ret = ipu7_map_fw_code_region(sys, isp->fw_code_region, -+ IPU_FW_CODE_REGION_SIZE); -+ if (ret) -+ dev_err(dev, "Failed to map fw code region for %s.\n", -+ sys_name); -+ -+ ipu7_mmu_hw_cleanup(sys->mmu); -+ pm_runtime_put(dev); -+ -+ return ret; -+} -+ -+static int ipu7_init_fw_code_region(struct ipu7_device *isp) -+{ -+ int ret; -+ -+ /* -+ * Allocate and map memory for FW execution. -+ * Not required in secure mode, in which FW runs in IMR. -+ */ -+ isp->fw_code_region = vmalloc(IPU_FW_CODE_REGION_SIZE); -+ if (!isp->fw_code_region) -+ return -ENOMEM; -+ -+ ret = ipu7_init_fw_code_region_by_sys(isp->isys, "isys"); -+ if (ret) -+ goto fail_init; -+ -+ ret = ipu7_init_fw_code_region_by_sys(isp->psys, "psys"); -+ if (ret) -+ goto fail_init; -+ -+ return 0; -+ -+fail_init: -+ vfree(isp->fw_code_region); -+ -+ return ret; -+} -+ -+static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) -+{ -+ struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL; -+ struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); -+ const struct ipu_buttress_ctrl *isys_buttress_ctrl; -+ const struct ipu_buttress_ctrl *psys_buttress_ctrl; -+ struct ipu_isys_internal_pdata *isys_ipdata; -+ struct ipu_psys_internal_pdata *psys_ipdata; -+ unsigned int dma_mask = IPU_DMA_MASK; -+ struct device *dev = &pdev->dev; -+ void __iomem *isys_base = NULL; -+ void __iomem *psys_base = NULL; -+ phys_addr_t phys, pb_phys; -+ struct ipu7_device *isp; -+ u32 is_es; -+ int ret; -+ -+ if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es)) -+ is_es = 0; -+ -+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); -+ if (!isp) -+ return -ENOMEM; -+ -+ isp->pdev = pdev; -+ INIT_LIST_HEAD(&isp->devices); -+ -+ ret = pcim_enable_device(pdev); -+ if (ret) -+ return dev_err_probe(dev, ret, "Enable PCI device failed\n"); -+ -+ dev_info(dev, "Device 0x%x (rev: 0x%x)\n", -+ pdev->device, pdev->revision); -+ -+ phys = pci_resource_start(pdev, IPU_PCI_BAR); -+ pb_phys = pci_resource_start(pdev, IPU_PCI_PBBAR); -+ dev_info(dev, "IPU7 PCI BAR0 base %pap BAR2 base %pap\n", -+ &phys, &pb_phys); -+ -+ isp->base = pcim_iomap_region(pdev, IPU_PCI_BAR, IPU_NAME); -+ if (IS_ERR(isp->base)) -+ return dev_err_probe(dev, PTR_ERR(isp->base), -+ "Failed to I/O memory remapping bar %u\n", -+ IPU_PCI_BAR); -+ -+ isp->pb_base = pcim_iomap_region(pdev, IPU_PCI_PBBAR, IPU_NAME); -+ if (IS_ERR(isp->pb_base)) -+ return dev_err_probe(dev, PTR_ERR(isp->pb_base), -+ "Failed to I/O memory remapping bar %u\n", -+ IPU_PCI_PBBAR); -+ -+ dev_info(dev, "IPU7 PCI BAR0 mapped at %p\n BAR2 mapped at %p\n", -+ isp->base, isp->pb_base); -+ -+ pci_set_drvdata(pdev, isp); -+ pci_set_master(pdev); -+ -+ switch (id->device) { -+ case IPU7_PCI_ID: -+ isp->hw_ver = IPU_VER_7; -+ isp->cpd_fw_name = IPU7_FIRMWARE_NAME; -+ isys_ipdata = &ipu7_isys_ipdata; -+ psys_ipdata = &ipu7_psys_ipdata; -+ isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -+ psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -+ break; -+ case IPU7P5_PCI_ID: -+ isp->hw_ver = IPU_VER_7P5; -+ isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME; -+ isys_ipdata = &ipu7p5_isys_ipdata; -+ psys_ipdata = &ipu7p5_psys_ipdata; -+ isys_buttress_ctrl = &ipu7_isys_buttress_ctrl; -+ psys_buttress_ctrl = &ipu7_psys_buttress_ctrl; -+ break; -+ case IPU8_PCI_ID: -+ isp->hw_ver = IPU_VER_8; -+ isp->cpd_fw_name = IPU8_FIRMWARE_NAME; -+ isys_ipdata = &ipu8_isys_ipdata; -+ psys_ipdata = &ipu8_psys_ipdata; -+ isys_buttress_ctrl = &ipu8_isys_buttress_ctrl; -+ psys_buttress_ctrl = &ipu8_psys_buttress_ctrl; -+ break; -+ default: -+ WARN(1, "Unsupported IPU device"); -+ return -ENODEV; -+ } -+ -+ ipu_internal_pdata_init(isys_ipdata, psys_ipdata); -+ -+ isys_base = isp->base + isys_ipdata->hw_variant.offset; -+ psys_base = isp->base + psys_ipdata->hw_variant.offset; -+ -+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_mask)); -+ if (ret) -+ return dev_err_probe(dev, ret, "Failed to set DMA mask\n"); -+ -+ dma_set_max_seg_size(dev, UINT_MAX); -+ -+ ipu7_pci_config_setup(pdev); -+ -+ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); -+ if (ret < 0) -+ return dev_err_probe(dev, ret, "Failed to alloc irq vector\n"); -+ -+ ret = ipu_buttress_init(isp); -+ if (ret) -+ goto pci_irq_free; -+ -+ dev_info(dev, "firmware cpd file: %s\n", isp->cpd_fw_name); -+ -+ ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev); -+ if (ret) { -+ dev_err_probe(dev, ret, -+ "Requesting signed firmware %s failed\n", -+ isp->cpd_fw_name); -+ goto buttress_exit; -+ } -+ -+ ret = ipu7_cpd_validate_cpd_file(isp, isp->cpd_fw->data, -+ isp->cpd_fw->size); -+ if (ret) { -+ dev_err_probe(dev, ret, "Failed to validate cpd\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ isys_ctrl = devm_kmemdup(dev, isys_buttress_ctrl, -+ sizeof(*isys_buttress_ctrl), GFP_KERNEL); -+ if (!isys_ctrl) { -+ ret = -ENOMEM; -+ goto out_ipu_bus_del_devices; -+ } -+ -+#if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -+ ipu_get_acpi_devices(&dev->platform_data); -+#endif -+ isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -+ dev->platform_data, -+ isys_ipdata, 0); -+ if (IS_ERR(isp->isys)) { -+ ret = PTR_ERR(isp->isys); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ psys_ctrl = devm_kmemdup(dev, psys_buttress_ctrl, -+ sizeof(*psys_buttress_ctrl), GFP_KERNEL); -+ if (!psys_ctrl) { -+ ret = -ENOMEM; -+ goto out_ipu_bus_del_devices; -+ } -+ -+ isp->psys = ipu7_psys_init(pdev, &isp->isys->auxdev.dev, -+ psys_ctrl, psys_base, -+ psys_ipdata, 0); -+ if (IS_ERR(isp->psys)) { -+ ret = PTR_ERR(isp->psys); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = devm_request_threaded_irq(dev, pdev->irq, -+ ipu_buttress_isr, -+ ipu_buttress_isr_threaded, -+ IRQF_SHARED, IPU_NAME, isp); -+ if (ret) -+ goto out_ipu_bus_del_devices; -+ -+ if (!isp->secure_mode) { -+ ret = ipu7_init_fw_code_region(isp); -+ if (ret) -+ goto out_ipu_bus_del_devices; -+ } else { -+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ dev_err(&isp->psys->auxdev.dev, -+ "Failed to get runtime PM\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = ipu7_mmu_hw_init(isp->psys->mmu); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "Failed to init MMU hardware\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = ipu7_map_fw_code_region(isp->psys, -+ (void *)isp->cpd_fw->data, -+ isp->cpd_fw->size); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "failed to map fw image\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ret = ipu_buttress_authenticate(isp); -+ if (ret) { -+ dev_err_probe(&isp->pdev->dev, ret, -+ "FW authentication failed\n"); -+ goto out_ipu_bus_del_devices; -+ } -+ -+ ipu7_mmu_hw_cleanup(isp->psys->mmu); -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ } -+ -+#ifdef CONFIG_DEBUG_FS -+ ret = ipu7_init_debugfs(isp); -+ if (ret) { -+ dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); -+ goto out_ipu_bus_del_devices; -+ } -+#endif -+ pm_runtime_put_noidle(dev); -+ pm_runtime_allow(dev); -+ -+ isp->ipu7_bus_ready_to_probe = true; -+ -+ return 0; -+ -+out_ipu_bus_del_devices: -+ if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->isys); -+ if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->psys); -+#ifdef CONFIG_DEBUG_FS -+ if (!IS_ERR_OR_NULL(isp->fw_code_region)) -+ vfree(isp->fw_code_region); -+#endif -+ if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) -+ ipu7_mmu_cleanup(isp->psys->mmu); -+ if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -+ ipu7_mmu_cleanup(isp->isys->mmu); -+ if (!IS_ERR_OR_NULL(isp->psys)) -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ ipu7_bus_del_devices(pdev); -+ release_firmware(isp->cpd_fw); -+buttress_exit: -+ ipu_buttress_exit(isp); -+pci_irq_free: -+ pci_free_irq_vectors(pdev); -+ -+ return ret; -+} -+ -+static void ipu7_pci_remove(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ ipu7_remove_debugfs(isp); -+#endif -+ if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->isys); -+ if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -+ ipu7_unmap_fw_code_region(isp->psys); -+ -+ if (!IS_ERR_OR_NULL(isp->fw_code_region)) -+ vfree(isp->fw_code_region); -+ -+ ipu7_mmu_cleanup(isp->isys->mmu); -+ ipu7_mmu_cleanup(isp->psys->mmu); -+ -+ ipu7_bus_del_devices(pdev); -+ -+ pm_runtime_forbid(&pdev->dev); -+ pm_runtime_get_noresume(&pdev->dev); -+ -+ ipu_buttress_exit(isp); -+ -+ release_firmware(isp->cpd_fw); -+ -+} -+ -+static void ipu7_pci_reset_prepare(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ -+ dev_warn(&pdev->dev, "FLR prepare\n"); -+ pm_runtime_forbid(&isp->pdev->dev); -+} -+ -+static void ipu7_pci_reset_done(struct pci_dev *pdev) -+{ -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ -+ ipu_buttress_restore(isp); -+ if (isp->secure_mode) -+ ipu_buttress_reset_authentication(isp); -+ -+ isp->ipc_reinit = true; -+ pm_runtime_allow(&isp->pdev->dev); -+ -+ dev_warn(&pdev->dev, "FLR completed\n"); -+} -+ -+/* -+ * PCI base driver code requires driver to provide these to enable -+ * PCI device level PM state transitions (D0<->D3) -+ */ -+static int ipu7_suspend(struct device *dev) -+{ -+ return 0; -+} -+ -+static int ipu7_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu_buttress *b = &isp->buttress; -+ int ret; -+ -+ isp->secure_mode = ipu_buttress_get_secure_mode(isp); -+ dev_info(dev, "IPU7 in %s mode\n", -+ isp->secure_mode ? "secure" : "non-secure"); -+ -+ ipu_buttress_restore(isp); -+ -+ ret = ipu_buttress_ipc_reset(isp, &b->cse); -+ if (ret) -+ dev_err(dev, "IPC reset protocol failed!\n"); -+ -+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); -+ if (ret < 0) { -+ dev_err(dev, "Failed to get runtime PM\n"); -+ return 0; -+ } -+ -+ ret = ipu_buttress_authenticate(isp); -+ if (ret) -+ dev_err(dev, "FW authentication failed(%d)\n", ret); -+ -+ pm_runtime_put(&isp->psys->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu7_runtime_resume(struct device *dev) -+{ -+ struct pci_dev *pdev = to_pci_dev(dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ int ret; -+ -+ ipu_buttress_restore(isp); -+ -+ if (isp->ipc_reinit) { -+ struct ipu_buttress *b = &isp->buttress; -+ -+ isp->ipc_reinit = false; -+ ret = ipu_buttress_ipc_reset(isp, &b->cse); -+ if (ret) -+ dev_err(dev, "IPC reset protocol failed!\n"); -+ } -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops ipu7_pm_ops = { -+ SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume) -+ RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL) -+}; -+ -+static const struct pci_device_id ipu7_pci_tbl[] = { -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, -+ {0,} -+}; -+MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); -+ -+static const struct pci_error_handlers pci_err_handlers = { -+ .reset_prepare = ipu7_pci_reset_prepare, -+ .reset_done = ipu7_pci_reset_done, -+}; -+ -+static struct pci_driver ipu7_pci_driver = { -+ .name = IPU_NAME, -+ .id_table = ipu7_pci_tbl, -+ .probe = ipu7_pci_probe, -+ .remove = ipu7_pci_remove, -+ .driver = { -+ .pm = &ipu7_pm_ops, -+ }, -+ .err_handler = &pci_err_handlers, -+}; -+ -+module_pci_driver(ipu7_pci_driver); -+ -+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE"); -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Tianshu Qiu "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_AUTHOR("Intel"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 pci driver"); -diff --git a/drivers/media/pci/intel/ipu7/ipu7.h b/drivers/media/pci/intel/ipu7/ipu7.h -new file mode 100644 -index 000000000000..454d702ef3d4 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/ipu7.h -@@ -0,0 +1,259 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_H -+#define IPU7_H -+ -+#include -+#include -+#include -+ -+#include "ipu7-buttress.h" -+ -+struct ipu7_bus_device; -+struct pci_dev; -+struct firmware; -+ -+#define IPU_NAME "intel-ipu7" -+#define IPU_MEDIA_DEV_MODEL_NAME "ipu7" -+ -+#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin" -+#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin" -+#define IPU8_FIRMWARE_NAME "intel/ipu/ipu8_fw.bin" -+ -+#define IPU7_ISYS_NUM_STREAMS 12 -+ -+#define IPU7_PCI_ID 0x645d -+#define IPU7P5_PCI_ID 0xb05d -+#define IPU8_PCI_ID 0xd719 -+ -+#define FW_LOG_BUF_SIZE (2 * 1024 * 1024) -+ -+enum ipu_version { -+ IPU_VER_INVALID = 0, -+ IPU_VER_7 = 1, -+ IPU_VER_7P5 = 2, -+ IPU_VER_8 = 3, -+}; -+ -+static inline bool is_ipu7p5(u8 hw_ver) -+{ -+ return hw_ver == IPU_VER_7P5; -+} -+ -+static inline bool is_ipu7(u8 hw_ver) -+{ -+ return hw_ver == IPU_VER_7; -+} -+ -+static inline bool is_ipu8(u8 hw_ver) -+{ -+ return hw_ver == IPU_VER_8; -+} -+ -+#define IPU_UNIFIED_OFFSET 0 -+ -+/* -+ * ISYS DMA can overshoot. For higher resolutions over allocation is one line -+ * but it must be at minimum 1024 bytes. Value could be different in -+ * different versions / generations thus provide it via platform data. -+ */ -+#define IPU_ISYS_OVERALLOC_MIN 1024 -+ -+#define IPU_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */ -+#define IPU_FW_CODE_REGION_START 0x4000000 /* 64MB */ -+#define IPU_FW_CODE_REGION_END (IPU_FW_CODE_REGION_START + \ -+ IPU_FW_CODE_REGION_SIZE) /* 80MB */ -+ -+struct ipu7_device { -+ struct pci_dev *pdev; -+ struct list_head devices; -+ struct ipu7_bus_device *isys; -+ struct ipu7_bus_device *psys; -+ struct ipu_buttress buttress; -+ -+ const struct firmware *cpd_fw; -+ const char *cpd_fw_name; -+ /* Only for non-secure mode. */ -+ void *fw_code_region; -+ -+ void __iomem *base; -+ void __iomem *pb_base; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *ipu7_dir; -+#endif -+ u8 hw_ver; -+ bool ipc_reinit; -+ bool secure_mode; -+ bool ipu7_bus_ready_to_probe; -+}; -+ -+#define IPU_DMA_MASK 39 -+#define IPU_LIB_CALL_TIMEOUT_MS 2000 -+#define IPU_PSYS_CMD_TIMEOUT_MS 2000 -+#define IPU_PSYS_OPEN_CLOSE_TIMEOUT_US 50 -+#define IPU_PSYS_OPEN_CLOSE_RETRY (10000 / IPU_PSYS_OPEN_CLOSE_TIMEOUT_US) -+ -+#define IPU_ISYS_NAME "isys" -+#define IPU_PSYS_NAME "psys" -+ -+#define IPU_MMU_ADDR_BITS 32 -+/* FW is accessible within the first 2 GiB only in non-secure mode. */ -+#define IPU_MMU_ADDR_BITS_NON_SECURE 31 -+ -+#define IPU7_IS_MMU_NUM 4U -+#define IPU7_PS_MMU_NUM 4U -+#define IPU7P5_IS_MMU_NUM 4U -+#define IPU7P5_PS_MMU_NUM 4U -+#define IPU8_IS_MMU_NUM 5U -+#define IPU8_PS_MMU_NUM 4U -+#define IPU_MMU_MAX_NUM 5U /* max(IS, PS) */ -+#define IPU_MMU_MAX_TLB_L1_STREAMS 40U -+#define IPU_MMU_MAX_TLB_L2_STREAMS 40U -+#define IPU_ZLX_MAX_NUM 32U -+#define IPU_ZLX_POOL_NUM 8U -+#define IPU_UAO_PLANE_MAX_NUM 64U -+ -+/* -+ * To maximize the IOSF utlization, IPU need to send requests in bursts. -+ * At the DMA interface with the buttress, there are CDC FIFOs with burst -+ * collection capability. CDC FIFO burst collectors have a configurable -+ * threshold and is configured based on the outcome of performance measurements. -+ * -+ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2 -+ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2 -+ * -+ * Threshold values are pre-defined and are arrived at after performance -+ * evaluations on a type of IPU -+ */ -+#define IPU_MAX_VC_IOSF_PORTS 4 -+ -+/* -+ * IPU must configure correct arbitration mechanism related to the IOSF VC -+ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on -+ * stall and 1 means stall until the request is completed. -+ */ -+#define IPU_BTRS_ARB_MODE_TYPE_REARB 0 -+#define IPU_BTRS_ARB_MODE_TYPE_STALL 1 -+ -+/* Currently chosen arbitration mechanism for VC0 */ -+#define IPU_BTRS_ARB_STALL_MODE_VC0 IPU_BTRS_ARB_MODE_TYPE_REARB -+ -+/* Currently chosen arbitration mechanism for VC1 */ -+#define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB -+ -+struct ipu7_isys_subdev_pdata; -+ -+/* One L2 entry maps 1024 L1 entries and one L1 entry per page */ -+#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) -+/* Max L2 blocks per stream */ -+#define IPU_MMUV2_MAX_L2_BLOCKS 2 -+/* Max L1 blocks per stream */ -+#define IPU_MMUV2_MAX_L1_BLOCKS 16 -+#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \ -+ IPU_MMUV2_MAX_L2_BLOCKS) -+/* Entries per L1 block */ -+#define MMUV2_ENTRIES_PER_L1_BLOCK 16 -+#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE) -+#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE -+ -+struct ipu7_mmu_hw { -+ char name[32]; -+ -+ void __iomem *base; -+ void __iomem *zlx_base; -+ void __iomem *uao_base; -+ -+ u32 offset; -+ u32 zlx_offset; -+ u32 uao_offset; -+ -+ u32 info_bits; -+ u32 refill; -+ u32 collapse_en_bitmap; -+ u32 at_sp_arb_cfg; -+ -+ u32 l1_block; -+ u32 l2_block; -+ -+ u8 nr_l1streams; -+ u8 nr_l2streams; -+ u32 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS]; -+ u32 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS]; -+ -+ u8 zlx_nr; -+ u32 zlx_axi_pool[IPU_ZLX_POOL_NUM]; -+ u32 zlx_en[IPU_ZLX_MAX_NUM]; -+ u32 zlx_conf[IPU_ZLX_MAX_NUM]; -+ -+ u32 uao_p_num; -+ u32 uao_p2tlb[IPU_UAO_PLANE_MAX_NUM]; -+}; -+ -+struct ipu7_mmu_pdata { -+ u32 nr_mmus; -+ struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -+ int mmid; -+}; -+ -+struct ipu7_isys_csi2_pdata { -+ void __iomem *base; -+}; -+ -+struct ipu7_isys_internal_csi2_pdata { -+ u32 nports; -+ u32 const *offsets; -+ u32 gpreg; -+}; -+ -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+struct ipu7_isys_internal_tpg_pdata { -+ u32 ntpgs; -+ u32 const *offsets; -+ u32 *sels; -+}; -+#endif -+ -+struct ipu7_hw_variants { -+ unsigned long offset; -+ u32 nr_mmus; -+ struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM]; -+ u8 cdc_fifos; -+ u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS]; -+ u32 dmem_offset; -+ u32 spc_offset; /* SPC offset from psys base */ -+}; -+ -+struct ipu_isys_internal_pdata { -+ struct ipu7_isys_internal_csi2_pdata csi2; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_MGC -+ struct ipu7_isys_internal_tpg_pdata tpg; -+#endif -+ struct ipu7_hw_variants hw_variant; -+ u32 num_parallel_streams; -+ u32 isys_dma_overshoot; -+}; -+ -+struct ipu7_isys_pdata { -+ void __iomem *base; -+ const struct ipu_isys_internal_pdata *ipdata; -+ struct ipu7_isys_subdev_pdata *spdata; -+}; -+ -+struct ipu_psys_internal_pdata { -+ struct ipu7_hw_variants hw_variant; -+}; -+ -+struct ipu7_psys_pdata { -+ void __iomem *base; -+ const struct ipu_psys_internal_pdata *ipdata; -+}; -+ -+int request_cpd_fw(const struct firmware **firmware_p, const char *name, -+ struct device *device); -+void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata, -+ struct ipu_psys_internal_pdata *psys_ipdata); -+void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev); -+#endif /* IPU7_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/Makefile b/drivers/media/pci/intel/ipu7/psys/Makefile -new file mode 100644 -index 000000000000..33eb383a14bc ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/Makefile -@@ -0,0 +1,18 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2017 - 2024 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+intel-ipu7-psys-objs += ipu-psys.o \ -+ ipu7-psys.o \ -+ ipu7-fw-psys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o -+ -+ccflags-y += -I$(src)/ -+ccflags-y += -I$(src)/../ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -new file mode 100644 -index 000000000000..582e59c89d7b ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu-psys.c -@@ -0,0 +1,1545 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2013 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "ipu7.h" -+#include "ipu7-mmu.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+#include "ipu7-boot.h" -+ -+#define IPU_PSYS_NUM_DEVICES 4U -+ -+static int psys_runtime_pm_resume(struct device *dev); -+static int psys_runtime_pm_suspend(struct device *dev); -+ -+#define IPU_FW_CALL_TIMEOUT_JIFFIES \ -+ msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) -+ -+static dev_t ipu7_psys_dev_t; -+static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); -+static DEFINE_MUTEX(ipu7_psys_mutex); -+ -+static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ struct vm_area_struct *vma; -+ unsigned long start, end; -+ int npages, array_size; -+ struct page **pages; -+ struct sg_table *sgt; -+ int ret = -ENOMEM; -+ int nr = 0; -+ u32 flags; -+ -+ start = (unsigned long)attach->userptr; -+ end = PAGE_ALIGN(start + attach->len); -+ npages = PHYS_PFN(end - (start & PAGE_MASK)); -+ array_size = npages * sizeof(struct page *); -+ -+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -+ if (!sgt) -+ return -ENOMEM; -+ -+ WARN_ON_ONCE(attach->npages); -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ goto free_sgt; -+ -+ mmap_read_lock(current->mm); -+ vma = vma_lookup(current->mm, start); -+ if (unlikely(!vma)) { -+ ret = -EFAULT; -+ goto error_up_read; -+ } -+ mmap_read_unlock(current->mm); -+ -+ flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; -+ nr = pin_user_pages_fast(start & PAGE_MASK, npages, -+ flags, pages); -+ if (nr < npages) -+ goto error; -+ -+ attach->pages = pages; -+ attach->npages = npages; -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, npages, -+ start & ~PAGE_MASK, attach->len, -+ GFP_KERNEL); -+ if (ret < 0) -+ goto error; -+ -+ attach->sgt = sgt; -+ -+ return 0; -+ -+error_up_read: -+ mmap_read_unlock(current->mm); -+error: -+ if (nr) -+ unpin_user_pages(pages, nr); -+ -+ kvfree(pages); -+free_sgt: -+ kfree(sgt); -+ -+ pr_err("failed to get userpages:%d\n", ret); -+ -+ return ret; -+} -+ -+static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ if (!attach || !attach->userptr || !attach->sgt) -+ return; -+ -+ unpin_user_pages(attach->pages, attach->npages); -+ -+ kvfree(attach->pages); -+ -+ sg_free_table(attach->sgt); -+ kfree(attach->sgt); -+ attach->sgt = NULL; -+} -+ -+static int ipu7_dma_buf_attach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_psys_kbuffer *kbuf = dbuf->priv; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ int ret; -+ -+ ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); -+ if (!ipu7_attach) -+ return -ENOMEM; -+ -+ ipu7_attach->len = kbuf->len; -+ ipu7_attach->userptr = kbuf->userptr; -+ -+ attach->priv = ipu7_attach; -+ -+ ret = ipu7_psys_get_userpages(ipu7_attach); -+ if (ret) { -+ kfree(ipu7_attach); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_detach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ -+ ipu7_psys_put_userpages(ipu7_attach); -+ kfree(ipu7_attach); -+ attach->priv = NULL; -+} -+ -+static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, -+ enum dma_data_direction dir) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ unsigned long attrs; -+ int ret; -+ -+ attrs = DMA_ATTR_SKIP_CPU_SYNC; -+ ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, -+ attrs); -+ if (ret) { -+ dev_err(attach->dev, "pci buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, -+ DMA_BIDIRECTIONAL); -+ -+ ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); -+ if (ret) { -+ dev_err(attach->dev, "ipu7 buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); -+ -+ return ipu7_attach->sgt; -+} -+ -+static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, -+ struct sg_table *sgt, -+ enum dma_data_direction dir) -+{ -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ -+ ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+} -+ -+static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) -+{ -+ return -ENOTTY; -+} -+ -+static void ipu7_dma_buf_release(struct dma_buf *buf) -+{ -+ struct ipu7_psys_kbuffer *kbuf = buf->priv; -+ -+ if (!kbuf) -+ return; -+ -+ if (kbuf->db_attach) -+ ipu7_psys_put_userpages(kbuf->db_attach->priv); -+ -+ kfree(kbuf); -+} -+ -+static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, -+ enum dma_data_direction dir) -+{ -+ return -ENOTTY; -+} -+ -+static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (list_empty(&dmabuf->attachments)) -+ return -EINVAL; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) -+ return -EINVAL; -+ -+ map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); -+ map->is_iomem = false; -+ if (!map->vaddr) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (WARN_ON(list_empty(&dmabuf->attachments))) -+ return; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || -+ !ipu7_attach->npages)) -+ return; -+ -+ vm_unmap_ram(map->vaddr, ipu7_attach->npages); -+} -+ -+static struct dma_buf_ops ipu7_dma_buf_ops = { -+ .attach = ipu7_dma_buf_attach, -+ .detach = ipu7_dma_buf_detach, -+ .map_dma_buf = ipu7_dma_buf_map, -+ .unmap_dma_buf = ipu7_dma_buf_unmap, -+ .release = ipu7_dma_buf_release, -+ .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, -+ .mmap = ipu7_dma_buf_mmap, -+ .vmap = ipu7_dma_buf_vmap, -+ .vunmap = ipu7_dma_buf_vunmap, -+}; -+ -+static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) -+{ -+ u8 graph_id = 0; -+ -+ for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { -+ if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) -+ break; -+ } -+ -+ if (graph_id == IPU_PSYS_NUM_STREAMS) -+ return -EBUSY; -+ -+ fh->psys->graph_id[graph_id] = graph_id; -+ return graph_id; -+} -+ -+static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) -+{ -+ fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; -+} -+ -+static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, -+ struct ipu7_bus_device *adev) -+{ -+ if (tq->msg_task) -+ ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), -+ tq->msg_task, tq->task_dma_addr, 0); -+ -+ list_del(&tq->list); -+ kfree(tq); -+} -+ -+static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct ipu_psys_task_ack *ack; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ -+ mutex_destroy(&ip->event_mutex); -+ mutex_destroy(&ip->task_mutex); -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { -+ list_del(&ack->list); -+ kfree(ack); -+ } -+} -+ -+static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ u8 i; -+ -+ INIT_LIST_HEAD(&ip->event_list); -+ INIT_LIST_HEAD(&ip->ack_list); -+ -+ INIT_LIST_HEAD(&ip->tq_running_list); -+ INIT_LIST_HEAD(&ip->tq_list); -+ -+ for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ if (!event) -+ goto event_cleanup; -+ -+ list_add(&event->list, &ip->event_list); -+ } -+ -+ for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { -+ tq = kzalloc(sizeof(*tq), GFP_KERNEL); -+ if (!tq) -+ goto tq_cleanup; -+ -+ list_add(&tq->list, &ip->tq_list); -+ -+ tq->msg_task = -+ ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), -+ &tq->task_dma_addr, GFP_KERNEL, 0); -+ -+ if (!tq->msg_task) { -+ dev_err(dev, "Failed to allocate msg task.\n"); -+ goto tq_cleanup; -+ } -+ } -+ -+ init_completion(&ip->graph_open); -+ init_completion(&ip->graph_close); -+ -+ return 0; -+ -+tq_cleanup: -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+event_cleanup: -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ return -ENOMEM; -+} -+ -+static int ipu7_psys_open(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_psys_fh *fh; -+ struct ipu7_psys_stream *ip; -+ int ret; -+ -+ fh = kzalloc(sizeof(*fh), GFP_KERNEL); -+ if (!fh) -+ return -ENOMEM; -+ -+ ip = kzalloc(sizeof(*ip), GFP_KERNEL); -+ if (!ip) { -+ ret = -ENOMEM; -+ goto alloc_failed; -+ } -+ -+ ret = ipu7_psys_stream_init(ip, psys->adev); -+ if (ret) -+ goto stream_init_failed; -+ -+ fh->ip = ip; -+ ip->fh = fh; -+ -+ fh->psys = psys; -+ -+ file->private_data = fh; -+ -+ mutex_init(&fh->mutex); -+ INIT_LIST_HEAD(&fh->bufmap); -+ init_waitqueue_head(&fh->wait); -+ -+ mutex_init(&ip->task_mutex); -+ mutex_init(&ip->event_mutex); -+ -+ mutex_lock(&psys->mutex); -+ -+ ret = ipu7_psys_get_graph_id(fh); -+ if (ret < 0) -+ goto open_failed; -+ -+ fh->ip->graph_id = ret; -+ -+ ret = pm_runtime_get_sync(dev); -+ if (ret < 0) { -+ dev_err(dev, "Runtime PM failed (%d)\n", ret); -+ goto rpm_put; -+ } -+ -+ list_add_tail(&fh->list, &psys->fhs); -+ -+ mutex_unlock(&psys->mutex); -+ -+ return 0; -+ -+rpm_put: -+ pm_runtime_put(dev); -+ ipu7_psys_put_graph_id(fh); -+ -+open_failed: -+ ipu7_psys_stream_deinit(ip, psys->adev); -+ -+ mutex_destroy(&fh->mutex); -+ -+ mutex_unlock(&psys->mutex); -+ -+stream_init_failed: -+ kfree(ip); -+ -+alloc_failed: -+ kfree(fh); -+ -+ return ret; -+} -+ -+static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ if (!kbuf) -+ return; -+ -+ kbuf->valid = false; -+ if (kbuf->kaddr) { -+ struct iosys_map dmap; -+ -+ iosys_map_set_vaddr(&dmap, kbuf->kaddr); -+ dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); -+ } -+ -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ -+ if (kbuf->sgt) -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ if (kbuf->db_attach) -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ dma_buf_put(kbuf->dbuf); -+ -+ kbuf->db_attach = NULL; -+ kbuf->dbuf = NULL; -+ kbuf->sgt = NULL; -+} -+ -+static int ipu7_psys_release(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct ipu7_psys_kbuffer *kbuf, *kbuf0; -+ struct dma_buf_attachment *dba; -+ -+ mutex_lock(&fh->mutex); -+ /* clean up buffers */ -+ if (!list_empty(&fh->bufmap)) { -+ list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { -+ list_del(&kbuf->list); -+ dba = kbuf->db_attach; -+ -+ /* Unmap and release buffers */ -+ if (kbuf->dbuf && dba) { -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ } else { -+ if (dba) -+ ipu7_psys_put_userpages(dba->priv); -+ kfree(kbuf); -+ } -+ } -+ } -+ mutex_unlock(&fh->mutex); -+ -+ ipu7_psys_stream_deinit(fh->ip, psys->adev); -+ -+ mutex_lock(&psys->mutex); -+ list_del(&fh->list); -+ -+ ipu7_psys_put_graph_id(fh); -+ kfree(fh->ip); -+ -+ mutex_unlock(&psys->mutex); -+ mutex_destroy(&fh->mutex); -+ kfree(fh); -+ -+ pm_runtime_put_sync(&psys->adev->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, -+ struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -+ struct dma_buf *dbuf; -+ int ret; -+ -+ if (!buf->base.userptr) { -+ dev_err(dev, "Buffer allocation not supported\n"); -+ return -EINVAL; -+ } -+ -+ if (!PAGE_ALIGNED(buf->base.userptr)) { -+ dev_err(dev, "Not page-aligned userptr is not supported\n"); -+ return -EINVAL; -+ } -+ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) -+ return -ENOMEM; -+ -+ kbuf->len = buf->len; -+ kbuf->userptr = buf->base.userptr; -+ kbuf->flags = buf->flags; -+ -+ exp_info.ops = &ipu7_dma_buf_ops; -+ exp_info.size = kbuf->len; -+ exp_info.flags = O_RDWR; -+ exp_info.priv = kbuf; -+ -+ dbuf = dma_buf_export(&exp_info); -+ if (IS_ERR(dbuf)) { -+ kfree(kbuf); -+ return PTR_ERR(dbuf); -+ } -+ -+ ret = dma_buf_fd(dbuf, 0); -+ if (ret < 0) { -+ dma_buf_put(dbuf); -+ return ret; -+ } -+ -+ kbuf->fd = ret; -+ buf->base.fd = ret; -+ buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; -+ buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; -+ kbuf->flags = buf->flags; -+ -+ mutex_lock(&fh->mutex); -+ list_add(&kbuf->list, &fh->bufmap); -+ mutex_unlock(&fh->mutex); -+ -+ dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", -+ buf->base.userptr, buf->len, buf->base.fd); -+ -+ return 0; -+} -+ -+static int -+ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) -+{ -+ return 0; -+} -+ -+static struct ipu7_psys_kbuffer * -+ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) -+{ -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ list_for_each_entry(kbuf, &fh->bufmap, list) { -+ if (kbuf->fd == fd) -+ return kbuf; -+ } -+ -+ return NULL; -+} -+ -+static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ -+ if (!kbuf || fd != kbuf->fd) { -+ dev_err(dev, "invalid kbuffer\n"); -+ return -EINVAL; -+ } -+ -+ /* From now on it is not safe to use this kbuffer */ -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ -+ list_del(&kbuf->list); -+ -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+ dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); -+ -+ return 0; -+} -+ -+static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct device *dev = &psys->adev->isp->pdev->dev; -+ struct dma_buf *dbuf; -+ struct iosys_map dmap; -+ int ret; -+ -+ dbuf = dma_buf_get(fd); -+ if (IS_ERR(dbuf)) -+ return -EINVAL; -+ -+ if (!kbuf) { -+ /* This fd isn't generated by ipu7_psys_getbuf, it -+ * is a new fd. Create a new kbuf item for this fd, and -+ * add this kbuf to bufmap list. -+ */ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ -+ /* fd valid and found, need remap */ -+ if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { -+ dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", -+ fd, kbuf); -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ if (ret) -+ goto mapbuf_fail; -+ -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ /* changed external dmabuf */ -+ if (!kbuf) { -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ } -+ -+ if (kbuf->sgt) { -+ dev_dbg(dev, "fd %d has been mapped!\n", fd); -+ dma_buf_put(dbuf); -+ goto mapbuf_end; -+ } -+ -+ kbuf->dbuf = dbuf; -+ -+ if (kbuf->len == 0) -+ kbuf->len = kbuf->dbuf->size; -+ -+ kbuf->fd = fd; -+ -+ kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); -+ if (IS_ERR(kbuf->db_attach)) { -+ ret = PTR_ERR(kbuf->db_attach); -+ dev_err(dev, "dma buf attach failed\n"); -+ goto attach_fail; -+ } -+ -+ kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, -+ DMA_BIDIRECTIONAL); -+ if (IS_ERR_OR_NULL(kbuf->sgt)) { -+ ret = -EINVAL; -+ kbuf->sgt = NULL; -+ dev_err(dev, "dma buf map attachment failed\n"); -+ goto kbuf_map_fail; -+ } -+ -+ if (!kbuf->userptr) { -+ ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ if (ret) { -+ dev_dbg(dev, "ipu7 buf map failed\n"); -+ goto kbuf_map_fail; -+ } -+ } -+ -+ kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); -+ -+ /* no need vmap for imported dmabufs */ -+ if (!kbuf->userptr) -+ goto mapbuf_end; -+ -+ dmap.is_iomem = false; -+ ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); -+ if (ret) { -+ dev_err(dev, "dma buf vmap failed\n"); -+ goto kbuf_map_fail; -+ } -+ kbuf->kaddr = dmap.vaddr; -+ -+mapbuf_end: -+ dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", -+ __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, -+ kbuf->len); -+ kbuf->valid = true; -+ -+ return 0; -+ -+kbuf_map_fail: -+ if (!IS_ERR_OR_NULL(kbuf->sgt)) { -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ } -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ -+attach_fail: -+ list_del(&kbuf->list); -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+mapbuf_fail: -+ dma_buf_put(dbuf); -+ -+ dev_err(dev, "%s failed for fd %d\n", __func__, fd); -+ return ret; -+} -+ -+static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ long ret; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ long ret; -+ -+ dev_dbg(dev, "IOC_UNMAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ if (!kbuf) { -+ dev_err(dev, -+ "buffer with fd %d not found\n", fd); -+ mutex_unlock(&fh->mutex); -+ return -EINVAL; -+ } -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { -+ dev_err(&psys->dev, "nodes is wrong\n"); -+ return -EINVAL; -+ } -+ -+ if (copy_from_user(fh->ip->nodes, graph->nodes, -+ graph->num_nodes * sizeof(*graph->nodes))) { -+ dev_err(&psys->dev, "Failed to copy nodes\n"); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_open); -+ -+ ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to open graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_open, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Open graph %d timeout\n", -+ fh->ip->graph_id); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Failed to set graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ graph->graph_id = fh->ip->graph_id; -+ -+ return 0; -+} -+ -+static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_close); -+ -+ ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to close graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_close, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Close graph %d timeout\n", -+ fh->ip->graph_id); -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Failed to close graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct ipu_psys_task_queue * -+ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_request *task) -+{ -+ struct device *dev = &ip->fh->psys->dev; -+ struct ipu7_psys_kbuffer *kbuf = NULL; -+ struct ipu_psys_task_queue *tq; -+ int fd, prevfd = -1; -+ u32 i; -+ -+ if (task->term_buf_count > MAX_GRAPH_TERMINALS) { -+ dev_err(dev, "num_teminal_buffer is too large\n"); -+ return NULL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (list_empty(&ip->tq_list)) { -+ dev_err(dev, "No available take queue for stream %p\n", ip); -+ goto unlock; -+ } -+ -+ tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, -+ list); -+ -+ if (copy_from_user(tq->task_buffers, -+ task->task_buffers, -+ task->term_buf_count * -+ sizeof(*task->task_buffers))) { -+ dev_err(dev, "failed to copy task buffers\n"); -+ goto unlock; -+ } -+ -+ for (i = 0; i < task->term_buf_count; i++) { -+ fd = tq->task_buffers[i].term_buf.base.fd; -+ kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); -+ if (!kbuf) { -+ dev_err(dev, "fd %d not found\n", fd); -+ goto unlock; -+ } -+ tq->ipu7_addr[i] = kbuf->dma_addr -+ + tq->task_buffers[i].term_buf.data_offset; -+ -+ if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & -+ IPU_BUFFER_FLAG_NO_FLUSH)) -+ continue; -+ -+ prevfd = fd; -+ -+ if (kbuf->kaddr) -+ clflush_cache_range(kbuf->kaddr, kbuf->len); -+ } -+ -+ dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_running_list); -+ -+ mutex_unlock(&ip->task_mutex); -+ return tq; -+ -+unlock: -+ mutex_unlock(&ip->task_mutex); -+ return NULL; -+} -+ -+static long ipu_psys_task_request(struct ipu_psys_task_request *task, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct ipu_psys_task_queue *tq; -+ int ret = 0; -+ -+ if (task->term_buf_count == 0 || !task->task_buffers) { -+ dev_err(&psys->dev, "task_buffer is NULL\n"); -+ return -EINVAL; -+ } -+ -+ tq = ipu7_psys_get_task_queue(fh->ip, task); -+ if (!tq) { -+ dev_err(&psys->dev, "Failed to get task queue\n"); -+ return -EINVAL; -+ } -+ -+ ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to request task %d\n", -+ fh->ip->graph_id); -+ mutex_lock(&fh->ip->task_mutex); -+ list_move_tail(&tq->list, &fh->ip->tq_list); -+ mutex_unlock(&fh->ip->task_mutex); -+ return ret; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; -+ -+ return 0; -+} -+ -+static unsigned int ipu7_psys_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_stream *ip = fh->ip; -+ unsigned int res = 0; -+ -+ dev_dbg(dev, "ipu psys poll\n"); -+ -+ poll_wait(file, &fh->wait, wait); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->ack_list)) -+ res = POLLIN; -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(dev, "ipu psys poll res %u\n", res); -+ -+ return res; -+} -+ -+static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ union { -+ struct ipu_psys_graph_info graph; -+ struct ipu_psys_task_request task; -+ struct ipu_psys_buffer buf; -+ struct ipu_psys_event ev; -+ } karg; -+ struct ipu7_psys_fh *fh = file->private_data; -+ long err = 0; -+ void __user *up = (void __user *)arg; -+ bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && -+ cmd != IPU_IOC_GRAPH_CLOSE); -+ -+ if (copy) { -+ if (_IOC_SIZE(cmd) > sizeof(karg)) -+ return -ENOTTY; -+ -+ if (_IOC_DIR(cmd) & _IOC_WRITE) { -+ err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); -+ if (err) -+ return -EFAULT; -+ } -+ } -+ -+ switch (cmd) { -+ case IPU_IOC_MAPBUF: -+ err = ipu7_psys_mapbuf(arg, fh); -+ break; -+ case IPU_IOC_UNMAPBUF: -+ err = ipu7_psys_unmapbuf(arg, fh); -+ break; -+ case IPU_IOC_GETBUF: -+ err = ipu7_psys_getbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_PUTBUF: -+ err = ipu7_psys_putbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_GRAPH_OPEN: -+ err = ipu_psys_graph_open(&karg.graph, fh); -+ break; -+ case IPU_IOC_GRAPH_CLOSE: -+ err = ipu_psys_graph_close(arg, fh); -+ break; -+ case IPU_IOC_TASK_REQUEST: -+ err = ipu_psys_task_request(&karg.task, fh); -+ break; -+ case IPU_IOC_DQEVENT: -+ err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); -+ break; -+ default: -+ err = -ENOTTY; -+ break; -+ } -+ -+ if (err) -+ return err; -+ -+ if (copy && _IOC_DIR(cmd) & _IOC_READ) -+ if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static const struct file_operations ipu7_psys_fops = { -+ .open = ipu7_psys_open, -+ .release = ipu7_psys_release, -+ .unlocked_ioctl = ipu7_psys_ioctl, -+ .poll = ipu7_psys_poll, -+ .owner = THIS_MODULE, -+}; -+ -+static void ipu7_psys_dev_release(struct device *dev) -+{ -+} -+ -+static int psys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ int ret; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ if (!ipu_buttress_auth_done(adev->isp)) { -+ dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); -+ return 0; -+ } -+ -+ ipu7_psys_setup_hw(psys); -+ -+ ipu7_psys_subdomains_power(psys, 1); -+ -+ ret = ipu7_boot_start_fw(psys->adev); -+ if (ret) { -+ dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); -+ return ret; -+ } -+ -+ ret = ipu7_fw_psys_open(psys); -+ if (ret) { -+ dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); -+ return ret; -+ } -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ psys->ready = 1; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return 0; -+} -+ -+static int psys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (!psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ psys->ready = 0; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ipu7_fw_psys_close(psys); -+ -+ ipu7_boot_stop_fw(psys->adev); -+ -+ ipu7_psys_subdomains_power(psys, 0); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+/* The following PM callbacks are needed to enable runtime PM in IPU PCI -+ * device resume, otherwise, runtime PM can't work in PCI resume from -+ * S3 state. -+ */ -+static int psys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static int psys_suspend(struct device *dev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(dev); -+ unsigned long flags; -+ int ret = 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) -+ ret = -EBUSY; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops psys_pm_ops = { -+ .runtime_suspend = psys_runtime_pm_suspend, -+ .runtime_resume = psys_runtime_pm_resume, -+ .suspend = psys_suspend, -+ .resume = psys_resume, -+}; -+ -+#ifdef CONFIG_DEBUG_FS -+static int psys_fw_log_init(struct ipu7_psys *psys) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log; -+ void *log_buf; -+ -+ if (psys->fw_log) -+ return 0; -+ -+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -+ if (!fw_log) -+ return -ENOMEM; -+ -+ mutex_init(&fw_log->mutex); -+ -+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!log_buf) -+ return -ENOMEM; -+ -+ fw_log->head = log_buf; -+ fw_log->addr = log_buf; -+ fw_log->count = 0; -+ fw_log->size = 0; -+ -+ psys->fw_log = fw_log; -+ -+ return 0; -+} -+ -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_psys *psys = file->private_data; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ struct device *dev = &psys->adev->auxdev.dev; -+ u32 log_size; -+ void *buf; -+ int ret = 0; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations psys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, psys, &psys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ psys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ -+static const struct bus_type ipu7_psys_bus = { -+ .name = "intel-ipu7-psys", -+}; -+ -+static int ipu7_psys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ struct device *dev = &auxdev->dev; -+ struct ipu7_psys *psys; -+ unsigned int minor; -+ unsigned int i; -+ int ret; -+ -+ if (!adev->isp->ipu7_bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, -+ IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); -+ if (ret) { -+ dev_err(dev, "can't alloc psys chrdev region (%d)\n", -+ ret); -+ return ret; -+ } -+ -+ ret = pm_runtime_resume_and_get(&auxdev->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ goto out_unregister_chr_region; -+ -+ mutex_lock(&ipu7_psys_mutex); -+ -+ minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); -+ if (minor == IPU_PSYS_NUM_DEVICES) { -+ dev_err(dev, "too many devices\n"); -+ goto out_unlock; -+ } -+ -+ psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); -+ if (!psys) { -+ ret = -ENOMEM; -+ goto out_unlock; -+ } -+ -+ for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) -+ psys->graph_id[i] = INVALID_STREAM_ID; -+ -+ adev->auxdrv_data = -+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(dev->driver); -+ -+ psys->adev = adev; -+ psys->pdata = adev->pdata; -+ -+ cdev_init(&psys->cdev, &ipu7_psys_fops); -+ psys->cdev.owner = ipu7_psys_fops.owner; -+ -+ ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); -+ if (ret) { -+ dev_err(dev, "cdev_add failed (%d)\n", ret); -+ goto out_unlock; -+ } -+ -+ set_bit(minor, ipu7_psys_devices); -+ -+ spin_lock_init(&psys->ready_lock); -+ -+ psys->ready = 0; -+ psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; -+ -+ mutex_init(&psys->mutex); -+ INIT_LIST_HEAD(&psys->fhs); -+ -+ ret = ipu7_fw_psys_init(psys); -+ if (ret) { -+ dev_err(dev, "FW init failed(%d)\n", ret); -+ goto out_mutex_destroy; -+ } -+ -+ psys->dev.bus = &ipu7_psys_bus; -+ psys->dev.parent = dev; -+ psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); -+ psys->dev.release = ipu7_psys_dev_release; -+ dev_set_name(&psys->dev, "ipu7-psys%d", minor); -+ ret = device_register(&psys->dev); -+ if (ret < 0) { -+ dev_err(&psys->dev, "psys device_register failed\n"); -+ goto out_fw_release; -+ } -+ -+ dev_set_drvdata(dev, psys); -+#ifdef CONFIG_DEBUG_FS -+ psys_fw_log_init(psys); -+ ipu7_psys_init_debugfs(psys); -+#endif -+ dev_info(dev, "IPU psys probe done.\n"); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ pm_runtime_put(&auxdev->dev); -+ -+ return 0; -+ -+out_fw_release: -+ ipu7_fw_psys_release(psys); -+out_mutex_destroy: -+ mutex_destroy(&psys->mutex); -+ cdev_del(&psys->cdev); -+out_unlock: -+ /* Safe to call even if the init is not called */ -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+out_unregister_chr_region: -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ pm_runtime_put(&auxdev->dev); -+ -+ return ret; -+} -+ -+static void ipu7_psys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); -+ struct device *dev = &auxdev->dev; -+#ifdef CONFIG_DEBUG_FS -+ struct ipu7_device *isp = psys->adev->isp; -+ -+ if (isp->ipu7_dir) -+ debugfs_remove_recursive(psys->debugfsdir); -+#endif -+ -+ mutex_lock(&ipu7_psys_mutex); -+ ipu7_fw_psys_release(psys); -+ device_unregister(&psys->dev); -+ clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); -+ cdev_del(&psys->cdev); -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ mutex_destroy(&psys->mutex); -+ -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ -+ dev_info(dev, "removed\n"); -+} -+ -+static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ struct device *dev = &psys->adev->auxdev.dev; -+ void __iomem *base = psys->pdata->base; -+ u32 status, state; -+ int r; -+ -+ mutex_lock(&psys->mutex); -+ r = pm_runtime_get_if_in_use(dev); -+ if (!r || WARN_ON_ONCE(r < 0)) { -+ mutex_unlock(&psys->mutex); -+ return IRQ_NONE; -+ } -+ -+ state = ipu7_boot_get_boot_state(adev); -+ if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { -+ dev_warn(&psys->dev, "error state %u\n", state); -+ } else { -+ status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); -+ writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ -+ if (status & IRQ_FROM_LOCAL_FW) -+ ipu7_psys_handle_events(psys); -+ } -+ -+ pm_runtime_put(dev); -+ mutex_unlock(&psys->mutex); -+ -+ return IRQ_HANDLED; -+} -+ -+static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { -+ .isr_threaded = psys_isr_threaded, -+ .wake_isr_thread = true, -+}; -+ -+static const struct auxiliary_device_id ipu7_psys_id_table[] = { -+ { -+ .name = "intel_ipu7.psys", -+ .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, -+ }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); -+ -+static struct auxiliary_driver ipu7_psys_driver = { -+ .name = IPU_PSYS_NAME, -+ .probe = ipu7_psys_probe, -+ .remove = ipu7_psys_remove, -+ .id_table = ipu7_psys_id_table, -+ .driver = { -+ .pm = &psys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(ipu7_psys_driver); -+ -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_AUTHOR("Tianshu Qiu "); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 processing system driver"); -+MODULE_IMPORT_NS("INTEL_IPU7"); -+MODULE_IMPORT_NS("DMA_BUF"); -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -new file mode 100644 -index 000000000000..15ba548ecd83 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.c -@@ -0,0 +1,603 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2016 - 2024 Intel Corporation -+ -+#include -+#include -+ -+#include -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+#include "abi/ipu7_fw_psys_config_abi.h" -+ -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-syscom.h" -+#include "ipu7-psys.h" -+ -+#define TLV_TYPE(type) ((u32)(type) & 0x3FU) -+#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) -+ -+/* -+ * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) -+ -+/* -+ * Special node resource ID to identify a generic external node. -+ * Required when there is a link to/from IPU and that node. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_syscom_context *syscom; -+ struct ipu7_psys_config *psys_config; -+ struct syscom_queue_config *queue_configs; -+ dma_addr_t psys_config_dma_addr; -+ u32 freq; -+ int i, num_queues, ret; -+ -+ /* Allocate and init syscom context. */ -+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -+ GFP_KERNEL); -+ if (!syscom) -+ return -ENOMEM; -+ -+ adev->syscom = syscom; -+ syscom->num_input_queues = FWPS_MSG_ABI_MAX_INPUT_QUEUES; -+ syscom->num_output_queues = FWPS_MSG_ABI_MAX_OUTPUT_QUEUES; -+ num_queues = syscom->num_input_queues + syscom->num_output_queues; -+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -+ GFP_KERNEL); -+ if (!queue_configs) { -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ syscom->queue_configs = queue_configs; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = -+ IPU_PSYS_ACK_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = -+ IPU_PSYS_LOG_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = -+ IPU_PSYS_CMD_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = -+ FWPS_MSG_HOST2FW_MAX_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = -+ 0; -+ -+ for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { -+ queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; -+ queue_configs[i].token_size_in_bytes = -+ sizeof(struct ia_gofo_msg_indirect); -+ } -+ -+ /* Allocate PSYS subsys config. */ -+ psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), -+ &psys_config_dma_addr, GFP_KERNEL, 0); -+ if (!psys_config) { -+ dev_err(dev, "Failed to allocate psys subsys config.\n"); -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ psys->subsys_config = psys_config; -+ psys->subsys_config_dma_addr = psys_config_dma_addr; -+ memset(psys_config, 0, sizeof(struct ipu7_psys_config)); -+ ret = ipu_buttress_get_psys_freq(adev->isp, &freq); -+ if (ret) { -+ dev_err(dev, "Failed to get PSYS frequency.\n"); -+ ipu7_fw_psys_release(psys); -+ return ret; -+ } -+ -+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -+ freq, psys_config_dma_addr, 1U); -+ if (ret) -+ ipu7_fw_psys_release(psys); -+ return ret; -+} -+ -+void ipu7_fw_psys_release(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ -+ ipu7_boot_release_boot_config(adev); -+ if (psys->subsys_config) { -+ ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), -+ psys->subsys_config, -+ psys->subsys_config_dma_addr, 0); -+ psys->subsys_config = NULL; -+ psys->subsys_config_dma_addr = 0; -+ } -+} -+ -+static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ int ret; -+ -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ return ret; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ if (ack_header->header.tlv_header.tlv_type == type) -+ return 0; -+ -+ return -EAGAIN; -+} -+ -+static int ipu7_fw_dev_open(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_open *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys open\n"); -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | -+ IPU_MSG_DEVICE_OPEN_SEND_IRQ); -+ token->enable_power_gating = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_open(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_open(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to open PSYS dev.\n"); -+ return ret; -+ } -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev open done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN; -+ return 0; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev open timeout!\n"); -+ -+ return ret; -+} -+ -+static int ipu7_fw_dev_close(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | -+ IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+void ipu7_fw_psys_close(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_close(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to close PSYS dev.\n"); -+ return; -+ } -+ -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev close done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; -+ return; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev close timeout!\n"); -+} -+ -+static void -+ipu7_fw_psys_build_node_profile(const struct node_profile *profile, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_cb_profile *cb_profile = -+ (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*cb_profile); -+ -+ memcpy(cb_profile->profile_base.teb, profile->teb, -+ sizeof(cb_profile->profile_base.teb)); -+ -+ memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); -+ memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); -+ memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); -+ -+ cb_profile->profile_base.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); -+ cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+} -+ -+/* skip term, return false */ -+static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_term); -+ -+ if (!term->term_id && !term->buf_size) -+ return false; -+ -+ memset(msg_term, 0, sizeof(*msg_term)); -+ msg_term->term_id = term->term_id; -+ /* Disable progress message on connect terminals */ -+ msg_term->event_req_bm = 0U; -+ msg_term->payload_size = term->buf_size; -+ -+ msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); -+ msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+ return true; -+} -+ -+/* When skip processing node, just return false */ -+static bool ipu7_fw_psys_build_node(const struct graph_node *node, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_node); -+ bool ret = false; -+ u8 i = 0; -+ u8 max_terms = 0; -+ -+ memset(msg_node, 0, sizeof(*msg_node)); -+ /** -+ * Pass node info to FW, do not check for external IP and ISYS -+ * As FW expects a external node -+ */ -+ if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && -+ node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] == 0U) -+ return false; -+ } -+ -+ /** -+ * Sanity check for dummy node, TEB should set to required one -+ */ -+ if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || -+ node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || -+ node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) -+ return false; -+ } -+ -+ msg_node->node_rsrc_id = node->node_rsrc_id; -+ msg_node->node_ctx_id = node->node_ctx_id; -+ msg_node->num_frags = 1; -+ -+ *buf_ptr_ptr += buf_size; -+ -+ msg_node->profiles_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr -+ - (uintptr_t)&msg_node->profiles_list); -+ for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { -+ ipu7_fw_psys_build_node_profile(&node->profiles[i], -+ buf_ptr_ptr); -+ msg_node->profiles_list.num_elems++; -+ } -+ -+ msg_node->terms_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_node->terms_list); -+ max_terms = ARRAY_SIZE(node->terminals); -+ for (i = 0; i < max_terms && i < node->num_terms; i++) { -+ ret = ipu7_fw_psys_build_node_term(&node->terminals[i], -+ buf_ptr_ptr); -+ if (ret) -+ msg_node->terms_list.num_elems++; -+ } -+ -+ buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; -+ msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); -+ msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ return true; -+} -+ -+static bool ipu7_fw_psys_build_link(const struct graph_link *link, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; -+ -+ if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && -+ !link->ep_src.term_id && !link->ep_dst.term_id) -+ return false; -+ -+ msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; -+ msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; -+ -+ msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; -+ msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; -+ -+ msg_link->foreign_key = link->foreign_key; -+ msg_link->streaming_mode = link->streaming_mode; -+ msg_link->pbk_id = link->pbk_id; -+ msg_link->pbk_slot_id = link->pbk_slot_id; -+ msg_link->delayed_link = link->delayed_link; -+ -+ *buf_ptr_ptr += sizeof(*msg_link); -+ -+ msg_link->link_options.num_elems = 0; -+ msg_link->link_options.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_link->link_options); -+ msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); -+ msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); -+ -+ return true; -+} -+ -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *buf_ptr; -+ struct ipu7_msg_graph_open *graph_open; -+ u32 buf_size = 0; -+ bool ret = false; -+ u8 i = 0; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); -+ buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!buf_ptr) -+ return -ENODATA; -+ -+ graph_open = (struct ipu7_msg_graph_open *)buf_ptr; -+ -+ memset(graph_open, 0, sizeof(*graph_open)); -+ graph_open->graph_id = ip->graph_id; -+ graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP -+ | IPU_MSG_GRAPH_OPEN_SEND_IRQ); -+ -+ buf_ptr += sizeof(*graph_open); -+ graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->nodes); -+ for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { -+ ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); -+ if (ret) -+ graph_open->nodes.num_elems++; -+ } -+ -+ graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->links); -+ for (i = 0; i < ARRAY_SIZE(graph->links); i++) { -+ ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); -+ if (ret) -+ graph_open->links.num_elems++; -+ } -+ -+ buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); -+ graph_open->header.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); -+ graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ graph_open->header.user_token = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_graph_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->graph_id = graph_id; -+ token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP -+ | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_task *msg = tq->msg_task; -+ struct ia_gofo_msg_indirect *ind; -+ u32 node_q_id = ip->q_id[task->node_ctx_id]; -+ u32 teb_hi, teb_lo; -+ u64 teb; -+ u8 i, term_id; -+ u8 num_terms; -+ -+ ind = ipu7_syscom_get_token(ctx, node_q_id); -+ if (!ind) -+ return -ENODATA; -+ -+ memset(msg, 0, sizeof(*msg)); -+ msg->graph_id = task->graph_id; -+ msg->node_ctx_id = task->node_ctx_id; -+ msg->profile_idx = 0U; /* Only one profile on HKR */ -+ msg->frame_id = task->frame_id; -+ msg->frag_id = 0U; /* No frag, set to 0 */ -+ /* -+ * Each task has a flag indicating if ack needed, it may be used to -+ * reduce interrupts if multiple CBs supported. -+ */ -+ msg->req_done_msg = 1; -+ msg->req_done_irq = 1; -+ -+ memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, -+ sizeof(task->payload_reuse_bm)); -+ -+ teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; -+ teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; -+ teb = (teb_lo | (((u64)teb_hi) << 32)); -+ -+ num_terms = ip->nodes[msg->node_ctx_id].num_terms; -+ for (i = 0U; i < num_terms; i++) { -+ term_id = tq->task_buffers[i].term_id; -+ if ((1U << term_id) & teb) -+ msg->term_buffers[term_id] = tq->ipu7_addr[i]; -+ } -+ -+ msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); -+ msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); -+ msg->header.user_token = (uintptr_t)tq; -+ -+ ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); -+ ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); -+ ind->header.msg_options.num_elems = 0; -+ ind->header.msg_options.head_offset = 0; -+ ind->ref_header = msg->header.tlv_header; -+ ind->ref_msg_ptr = tq->task_dma_addr; -+ -+ ipu7_syscom_put_token(ctx, node_q_id); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *token; -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ return 0; -+} -+ -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys) -+{ -+ void *token; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ }; -+ -+ return 0; -+} -+ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -new file mode 100644 -index 000000000000..c43f235386d1 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-fw-psys.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2016 - 2024 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_PSYS_H -+#define IPU7_FW_PSYS_H -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+ -+#include "ipu7-syscom.h" -+ -+#include -+ -+#define IPU_PSYS_MAX_GRAPH_NUMS (8U) -+#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS -+ -+struct ipu7_msg_to_str { -+ const enum ipu7_msg_type type; -+ const char *msg; -+}; -+ -+struct ipu7_psys; -+struct ipu7_psys_stream; -+struct ipu_psys_task_queue; -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys); -+void ipu7_fw_psys_release(struct ipu7_psys *psys); -+int ipu7_fw_psys_open(struct ipu7_psys *psys); -+void ipu7_fw_psys_close(struct ipu7_psys *psys); -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip); -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys); -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys); -+#endif /* IPU7_FW_PSYS_H */ -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -new file mode 100644 -index 000000000000..1ce52ae75a4c ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.c -@@ -0,0 +1,398 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2020 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-fw-psys.h" -+ -+#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+static const struct ipu7_msg_to_str ps_fw_msg[] = { -+ {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, -+ {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, -+ {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, -+ {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, -+ {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, -+ {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, -+ {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, -+ {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, -+ {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, -+}; -+ -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_device *isp = psys->adev->isp; -+ void __iomem *base = isp->base; -+ u32 mask; -+ u32 val; -+ u32 req; -+ int ret; -+ -+ /* power domain req */ -+ mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : -+ IPU7_PSYS_DOMAIN_POWER_MASK; -+ req = on ? mask : 0; -+ val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); -+ -+ dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+ if ((val & mask) == req) { -+ dev_warn(dev, -+ "psys sub-domains power already in request state.\n"); -+ return; -+ } -+ writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); -+ ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, -+ val, -+ !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && -+ ((val & mask) == req), -+ 100, DOMAIN_POWE_TIMEOUT_US); -+ if (ret) -+ dev_err(dev, -+ "Psys sub-domains power %s timeout! status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+} -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys) -+{ -+ void __iomem *base = psys->pdata->base; -+ u32 val = IRQ_FROM_LOCAL_FW; -+ -+ /* Enable TO_SW IRQ from FW */ -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ -+ /* correct the initial printf configuration */ -+ writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -+} -+ -+static struct ipu7_psys_stream* -+ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) -+{ -+ struct ipu7_psys_fh *fh; -+ -+ list_for_each_entry(fh, &psys->fhs, list) { -+ if (fh->ip->graph_id == graph_id) -+ return fh->ip; -+ } -+ -+ return NULL; -+} -+ -+static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, -+ const void *buffer) -+{ -+ const struct ipu7_msg_graph_open_ack *ack_msg = -+ (const struct ipu7_msg_graph_open_ack *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ struct ipu7_psys_stream *ip; -+ struct device *dev = &psys->dev; -+ const struct ia_gofo_tlv_header *msg_tlv_item; -+ u16 num_items; -+ u16 head_offset; -+ u32 i; -+ -+ dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto open_graph_exit; -+ } -+ -+ num_items = ack_header->header.msg_options.num_elems; -+ if (!num_items) { -+ dev_err(dev, "%s, num_items is 0\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ head_offset = ack_header->header.msg_options.head_offset; -+ msg_tlv_item = (const struct ia_gofo_tlv_header *) -+ ((u8 *)&ack_header->header.msg_options + head_offset); -+ -+ if (!msg_tlv_item) { -+ dev_err(dev, "%s: failed to get tlv item\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ for (i = 0U; i < num_items; i++) { -+ u32 option_type = msg_tlv_item->tlv_type; -+ u32 option_bytes = msg_tlv_item->tlv_len32 * -+ TLV_ITEM_ALIGNMENT; -+ struct ipu7_msg_graph_open_ack_task_q_info *msg_option = -+ (void *)msg_tlv_item; -+ -+ switch (option_type) { -+ case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: -+ /* -+ * Should do check that: -+ * - Each managed node has a queue ID -+ * - Queue ID's are sane -+ */ -+ dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", -+ msg_option->node_ctx_id, msg_option->q_id); -+ ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; -+ break; -+ default: -+ /* -+ * Only one option supported -+ */ -+ dev_err(dev, "not supported %u\n", option_type); -+ break; -+ } -+ -+ msg_tlv_item = (struct ia_gofo_tlv_header *) -+ (((u8 *)msg_tlv_item) + option_bytes); -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; -+ -+open_graph_exit: -+ complete(&ip->graph_open); -+} -+ -+static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, -+ void *buffer, u32 error) -+{ -+ const struct ipu7_msg_task_done *ack_msg = -+ (const struct ipu7_msg_task_done *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ const struct ia_gofo_msg_header *msg_header = &ack_header->header; -+ struct ipu_psys_task_queue *tq; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ struct ipu_psys_task_ack *event; -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ tq = (void *)(uintptr_t)msg_header->user_token; -+ if (!tq) { -+ dev_err(dev, "%s: task_token is NULL\n", __func__); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { -+ dev_err(dev, "%s: graph %d node %d error %d\n", __func__, -+ ack_msg->graph_id, ack_msg->node_ctx_id, -+ tq->task_state); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ return -ENOENT; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_DONE; -+ dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->event_list)) { -+ event = list_first_entry(&ip->event_list, -+ struct ipu_psys_task_ack, list); -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_move_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_dbg(dev, "event queue is full, add new one\n"); -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ -+ if (event) { -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_add_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_err(dev, "failed to alloc event buf\n"); -+ } -+ } -+ mutex_unlock(&ip->event_mutex); -+ -+ wake_up_interruptible(&ip->fh->wait); -+ -+ return 0; -+} -+ -+static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, -+ void *buffer) -+{ -+ struct ipu7_msg_graph_close_ack *ack_msg = -+ (struct ipu7_msg_graph_close_ack *)buffer; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ -+ dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto graph_close_exit; -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ -+graph_close_exit: -+ complete(&ip->graph_close); -+} -+ -+void ipu7_psys_handle_events(struct ipu7_psys *psys) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ struct device *dev = &psys->dev; -+ u32 error = 0; -+ int ret = 0; -+ -+ do { -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_psys_get_log(psys); -+#endif -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ break; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, -+ ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); -+ -+ if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { -+ dev_err(dev, "group %d, code %d, detail: %d, %d\n", -+ ack_header->err.err_group, -+ ack_header->err.err_code, -+ ack_header->err.err_detail[0], -+ ack_header->err.err_detail[1]); -+ error = (ack_header->err.err_group == -+ IPU_MSG_ERR_GROUP_TASK) ? -+ IPU_PSYS_EVT_ERROR_FRAME : -+ IPU_PSYS_EVT_ERROR_INTERNAL; -+ } -+ -+ switch (ack_header->header.tlv_header.tlv_type) { -+ case IPU_MSG_TYPE_GRAPH_OPEN_ACK: -+ ipu7_psys_handle_graph_open_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_TASK_DONE: -+ ipu7_psys_handle_task_done(psys, buffer, error); -+ break; -+ case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: -+ ipu7_psys_handle_graph_close_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_GENERAL_ERR: -+ /* already printed the log above for general error */ -+ break; -+ default: -+ dev_err(&psys->dev, "Unknown type %d\n", -+ ack_header->header.tlv_header.tlv_type); -+ } -+ } while (1); -+} -+ -+static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, -+ struct ipu_psys_event *event) -+{ -+ struct ipu_psys_task_ack *ack; -+ -+ mutex_lock(&ip->event_mutex); -+ /* Check if there is already an event in the list */ -+ if (list_empty(&ip->ack_list)) { -+ mutex_unlock(&ip->event_mutex); -+ return -EAGAIN; -+ } -+ -+ ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); -+ -+ event->graph_id = ack->graph_id; -+ event->node_ctx_id = ack->node_ctx_id; -+ event->frame_id = ack->frame_id; -+ event->error = ack->err_code; -+ -+ list_move_tail(&ack->list, &ip->event_list); -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", -+ event->graph_id, event->node_ctx_id, event->frame_id); -+ -+ return 0; -+} -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); -+ -+ if (!(f_flags & O_NONBLOCK)) { -+ ret = wait_event_interruptible(fh->wait, -+ !ipu7_psys_get_event(fh->ip, -+ event)); -+ if (ret == -ERESTARTSYS) -+ return ret; -+ } else { -+ ret = ipu7_psys_get_event(fh->ip, event); -+ } -+ -+ return ret; -+} -diff --git a/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -new file mode 100644 -index 000000000000..a72ce4a7c5b9 ---- /dev/null -+++ b/drivers/media/pci/intel/ipu7/psys/ipu7-psys.h -@@ -0,0 +1,184 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2013 - 2024 Intel Corporation */ -+ -+#ifndef IPU7_PSYS_H -+#define IPU7_PSYS_H -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-fw-psys.h" -+ -+#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq -+ -+#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 -+#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 -+#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 -+#define IPU_PSYS_LOG_QUEUE_SIZE 256 -+#define IPU_PSYS_OUT_MSG_SIZE 256 -+ -+/** -+ * Each event from FW will be first queued into a -+ * event queue, define the queue depth here -+ */ -+#define TASK_EVENT_QUEUE_SIZE 3U -+/** -+ * Each task queue from user will be first queued into -+ * a task queue, define the queue depth here -+ */ -+#define TASK_REQUEST_QUEUE_SIZE 8U -+ -+#define INVALID_STREAM_ID 0xFF -+/** -+ * Task request queues per stream -+ * -+ * Each task will first assigned a task queue buffer here, -+ * all the nodes will share the same task queue, maximum -+ * queue will be full there. -+ */ -+struct ipu_psys_task_queue { -+ struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; -+ dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; -+ -+ struct ipu7_msg_task *msg_task; -+ dma_addr_t task_dma_addr; -+ -+ struct list_head list; -+ -+ /* task state of each task input, represent ipu7_msg_task_state */ -+ enum ipu7_msg_task_state task_state; -+}; -+ -+struct psys_fw_log { -+ struct mutex mutex; /* protect whole struct */ -+ void *head; -+ void *addr; -+ u32 count; /* running counter of log */ -+ u32 size; /* actual size of log content, in bits */ -+}; -+ -+/** -+ * Task quest event context -+ * -+ * Each task request should get its event ack from FW and save -+ * to this structure and for user dequeue purpose. -+ */ -+struct ipu_psys_task_ack { -+ u8 graph_id; /* graph id of the task request */ -+ u8 node_ctx_id; /* logical node id */ -+ u8 frame_id; /* frame id of the original task request */ -+ -+ struct list_head list; -+ -+ u32 err_code; /* error indication to user */ -+}; -+ -+/** -+ * stream here is equal to pipe, each stream has -+ * its dedicated graph_id, and task request queue. -+ * -+ * For multiple stream supported design. -+ */ -+struct ipu7_psys_stream { -+ struct ipu7_psys_fh *fh; -+ -+ u8 graph_id; /* graph_id on this stream */ -+ -+ /* Handle events from FW */ -+ struct mutex event_mutex; -+ struct list_head event_list; /* Reserved event list */ -+ struct list_head ack_list; /* Received ack from FW */ -+ -+ /* Serialize task queue */ -+ struct mutex task_mutex; -+ struct list_head tq_list; /* Reserved task queue list */ -+ struct list_head tq_running_list; /* Running task sent to FW */ -+ -+ u8 num_nodes; /* Number of enabled nodes */ -+ struct graph_node nodes[MAX_GRAPH_NODES]; -+ u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ -+ -+ struct completion graph_open; -+ struct completion graph_close; -+ -+ /* Graph state, represent enum ipu7_msg_graph_state */ -+ enum ipu7_msg_graph_state graph_state; -+}; -+ -+struct task_struct; -+struct ipu7_psys { -+ struct cdev cdev; -+ struct device dev; -+ -+ struct mutex mutex; /* Psys various */ -+ int ready; /* psys fw status */ -+ spinlock_t ready_lock; /* protect psys firmware state */ -+ -+ struct list_head fhs; -+ -+ struct ipu7_psys_pdata *pdata; -+ struct ipu7_bus_device *adev; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif -+ -+ unsigned long timeout; -+ -+ struct psys_fw_log *fw_log; -+ -+ /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ -+ u8 graph_id[IPU_PSYS_NUM_STREAMS]; -+ -+ /* Device state, represent enum ipu7_msg_dev_state */ -+ enum ipu7_msg_dev_state dev_state; -+ -+ struct ipu7_psys_config *subsys_config; -+ dma_addr_t subsys_config_dma_addr; -+}; -+ -+struct ipu7_psys_fh { -+ struct ipu7_psys *psys; -+ struct mutex mutex; /* Protects bufmap & kcmds fields */ -+ struct list_head list; -+ struct list_head bufmap; -+ wait_queue_head_t wait; -+ -+ struct ipu7_psys_stream *ip; -+}; -+ -+struct ipu7_dma_buf_attach { -+ struct device *dev; -+ u64 len; -+ uintptr_t *userptr; -+ struct sg_table *sgt; -+ struct page **pages; -+ size_t npages; -+}; -+ -+struct ipu7_psys_kbuffer { -+ u64 len; -+ uintptr_t *userptr; -+ u32 flags; -+ int fd; -+ void *kaddr; -+ struct list_head list; -+ dma_addr_t dma_addr; -+ struct sg_table *sgt; -+ struct dma_buf_attachment *db_attach; -+ struct dma_buf *dbuf; -+ bool valid; /* True when buffer is usable */ -+}; -+ -+#define inode_to_ipu_psys(inode) \ -+ container_of((inode)->i_cdev, struct ipu7_psys, cdev) -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys); -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); -+void ipu7_psys_handle_events(struct ipu7_psys *psys); -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags); -+ -+#endif /* IPU7_PSYS_H */ -diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig -index 724e80a9086d..f8affa636c5c 100644 ---- a/drivers/media/platform/intel/Kconfig -+++ b/drivers/media/platform/intel/Kconfig -@@ -1,14 +1,19 @@ --# SPDX-License-Identifier: GPL-2.0-only -+config INTEL_IPU7_FPGA_PDATA -+ bool "Enable built in platform data for ipu7 fpga" -+ depends on VIDEO_INTEL_IPU7 -+ help -+ Pre-ACPI system platform data is compiled inside kernel. - --comment "Intel media platform drivers" -+ This platform data is only used for PSS stage software -+ development and mainly used for pixter or sensor enablement -+ without ACPI support. - --config VIDEO_PXA27x -- tristate "PXA27x Quick Capture Interface driver" -- depends on V4L_PLATFORM_DRIVERS -- depends on VIDEO_DEV -- depends on PXA27x || COMPILE_TEST -- select VIDEOBUF2_DMA_SG -- select SG_SPLIT -- select V4L2_FWNODE -+config INTEL_IPU7_ACPI -+ tristate "Enable IPU ACPI driver" -+ default VIDEO_INTEL_IPU7 -+ depends on I2C -+ depends on ACPI - help -- This is a v4l2 driver for the PXA27x Quick Capture Interface -+ Driver to read ACPI data from BIOS -+ -+ This driver is used to read ACPI data from BIOS -diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile -index 7e8889cbd2df..d0c41b731794 100644 ---- a/drivers/media/platform/intel/Makefile -+++ b/drivers/media/platform/intel/Makefile -@@ -1,2 +1,19 @@ --# SPDX-License-Identifier: GPL-2.0-only --obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2010 - 2022 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+ccflags-y += -I$(src)/../../pci/intel/ipu7/ -+ -+ifneq ($(filter y m, $(CONFIG_INTEL_IPU7_ACPI)),) -+obj-$(CONFIG_INTEL_IPU7_ACPI) += ipu-acpi.o \ -+ ipu-acpi-pdata.o \ -+ ipu-acpi-common.o -+else -+obj-y += ipu7-fpga-pdata.o -+endif -diff --git a/drivers/media/platform/intel/ipu-acpi-common.c b/drivers/media/platform/intel/ipu-acpi-common.c -new file mode 100644 -index 000000000000..ee7bd6c93f9a ---- /dev/null -+++ b/drivers/media/platform/intel/ipu-acpi-common.c -@@ -0,0 +1,441 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016-2024 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+ -+static int get_integer_dsdt_data(struct device *dev, const u8 *dsdt, -+ int func, u64 *out) -+{ -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ union acpi_object *obj; -+ -+ obj = acpi_evaluate_dsm(dev_handle, (void *)dsdt, 0, func, NULL); -+ if (!obj) { -+ pr_err("IPU ACPI: Could not find corresponding DSM\n"); -+ return -ENODEV; -+ } -+ -+ if (obj->type != ACPI_TYPE_INTEGER) { -+ ACPI_FREE(obj); -+ return -ENODEV; -+ } -+ *out = obj->integer.value; -+ ACPI_FREE(obj); -+ return 0; -+} -+ -+static int read_acpi_block(struct device *dev, char *id, void *data, u32 size) -+{ -+ union acpi_object *obj; -+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ int status; -+ u32 buffer_length; -+ -+ status = acpi_evaluate_object(dev_handle, id, NULL, &buffer); -+ if (!ACPI_SUCCESS(status)) { -+ pr_err("IPU ACPI: Could not find ACPI block with err %d", status); -+ return -ENODEV; -+ } -+ -+ obj = (union acpi_object *)buffer.pointer; -+ if (!obj || obj->type != ACPI_TYPE_BUFFER) { -+ pr_err("IPU ACPI: Could not read ACPI buffer\n"); -+ status = -ENODEV; -+ goto err; -+ } -+ -+ if (obj->buffer.length > size) { -+ pr_err("IPU ACPI: Given buffer is too small\n"); -+ status = -ENODEV; -+ goto err; -+ } -+ -+ memcpy(data, obj->buffer.pointer, min(size, obj->buffer.length)); -+ buffer_length = obj->buffer.length; -+ kfree(buffer.pointer); -+ -+ return buffer_length; -+err: -+ kfree(buffer.pointer); -+ return status; -+} -+ -+static int ipu_acpi_get_gpio_data(struct device *dev, struct ipu_gpio_info *gpio, int size, -+ u64 *gpio_num) -+{ -+ const u8 dsdt_cam_gpio[] = { -+ 0x40, 0x46, 0x23, 0x79, 0x10, 0x9e, 0xea, 0x4f, -+ 0xa5, 0xc1, 0xB5, 0xaa, 0x8b, 0x19, 0x75, 0x6f }; -+ -+ int i = 0, j = 0, retries = 0, loop = 0; -+ u64 num_gpio; -+ int rval; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_gpio, 1, &num_gpio); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get number of GPIO pins\n"); -+ return rval; -+ } -+ -+ pr_info("IPU APCI: Num of gpio found = %lld", num_gpio); -+ -+ if (num_gpio == 0) { -+ *gpio_num = num_gpio; -+ return rval; -+ } -+ -+ if (num_gpio > size) { -+ pr_err("IPU ACPI: Incorrect number of GPIO pins\n"); -+ return rval; -+ } -+ -+ /* repeat until all gpio pin is saved */ -+ while (i < num_gpio && loop <= LOOP_SIZE) { -+ u64 data; -+ struct gpio_desc *desc = NULL; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_gpio, i + 2, &data); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get GPIO data\n"); -+ return -ENODEV; -+ } -+ -+ gpio[i].func = (data & 0xff); -+ gpio[i].valid = FALSE; -+ -+ desc = gpiod_get_index(dev, NULL, i + retries, GPIOD_ASIS); -+ -+ if (!IS_ERR(desc)) { -+ unsigned short pin = desc_to_gpio(desc); -+ bool save = TRUE; -+ -+ /* always save first GPIO pin */ -+ if (i == 0) -+ save = TRUE; -+ -+ /* check subsequent GPIO pin for replicate */ -+ else { -+ for (j = 0; j <= i; j++) { -+ /* retry if same as previous pin */ -+ if (gpio[j].pin == pin) { -+ retries++; -+ save = FALSE; -+ gpiod_put(desc); -+ break; -+ } -+ } -+ } -+ -+ /* save into array */ -+ if (save == TRUE) { -+ pr_info("IPU ACPI: DSM: Pin number = %d. Func = %x.", pin, gpio[i].func); -+ gpio[i].pin = pin; -+ gpio[i].valid = TRUE; -+ gpiod_put(desc); -+ i++; -+ retries = 0; -+ } -+ } -+ loop++; -+ } -+ *gpio_num = num_gpio; -+ -+ return rval; -+} -+ -+// Callback to parse I2C resources from _CRS -+static acpi_status parse_i2c_resource(struct acpi_resource *res, void *data) -+{ -+ char **controller_path = data; -+ struct acpi_resource_i2c_serialbus *i2c; -+ -+ if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) -+ return AE_OK; -+ -+ i2c = &res->data.i2c_serial_bus; -+ if (i2c->type != ACPI_RESOURCE_SERIAL_TYPE_I2C) -+ return AE_OK; -+ -+ *controller_path = kstrdup(i2c->resource_source.string_ptr, GFP_KERNEL); -+ return AE_CTRL_TERMINATE; -+} -+ -+// Get I2C bdf via _CRS -+static int ipu_get_i2c_bdf_crs(struct device *dev, struct ipu_i2c_info *info) -+{ -+ acpi_handle handle = ACPI_HANDLE(dev); -+ acpi_status status; -+ struct acpi_device *controller_adev; -+ struct pci_dev *pci_dev; -+ char *controller_path = NULL; -+ -+ if (!handle) { -+ dev_err(dev, "No ACPI handle\n"); -+ return -EINVAL; -+ } -+ -+ // Parse _CRS for I2C resource -+ status = acpi_walk_resources(handle, "_CRS", parse_i2c_resource, &controller_path); -+ if (ACPI_FAILURE(status) || !controller_path) { -+ dev_err(dev, "Failed to parse _CRS: %s\n", acpi_format_exception(status)); -+ return -EIO; -+ } -+ -+ // Get the I2C controller's ACPI device -+ status = acpi_get_handle(NULL, controller_path, &handle); -+ if (ACPI_FAILURE(status)) { -+ dev_err(dev, "Invalid controller path: %s\n", controller_path); -+ kfree(controller_path); -+ controller_path = NULL; -+ return -ENODEV; -+ } -+ -+ controller_adev = acpi_fetch_acpi_dev(handle); -+ if (!controller_adev) { -+ dev_err(dev, "No ACPI device for controller: %s\n", controller_path); -+ kfree(controller_path); -+ return -ENODEV; -+ } -+ -+ // Map to PCI device -+ pci_dev = acpi_get_pci_dev(handle); -+ if (!pci_dev) { -+ dev_err(dev, "No PCI device for controller: %s\n", controller_path); -+ kfree(controller_path); -+ return -ENODEV; -+ } -+ -+ // Extract bdf using pci_domain_nr() -+ snprintf(info->bdf, sizeof(info->bdf), "%04x:%02x:%02x.%x", -+ pci_domain_nr(pci_dev->bus), pci_dev->bus->number, -+ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn)); -+ pci_dev_put(pci_dev); -+ -+ return 0; -+} -+ -+static int ipu_acpi_get_i2c_info(struct device *dev, struct ipu_i2c_info *i2c, int size, u64 *num) -+{ -+ const u8 dsdt_cam_i2c[] = { -+ 0x49, 0x75, 0x25, 0x26, 0x71, 0x92, 0xA4, 0x4C, -+ 0xBB, 0x43, 0xC4, 0x89, 0x9D, 0x5A, 0x48, 0x81}; -+ -+ u64 num_i2c; -+ int i; -+ int rval; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, 1, &num_i2c); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get number of I2C\n"); -+ return -ENODEV; -+ } -+ -+ for (i = 0; i < num_i2c && i < size; i++) { -+ u64 data; -+ -+ rval = get_integer_dsdt_data(dev, dsdt_cam_i2c, i + 2, -+ &data); -+ -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get I2C data\n"); -+ return -ENODEV; -+ } -+ -+ i2c[i].bus = ((data >> 24) & 0xff); -+ i2c[i].addr = (data >> 8) & 0xff; -+ rval = ipu_get_i2c_bdf_crs(dev, i2c + i); -+ if (rval < 0) { -+ pr_err("IPU ACPI: Failed to get i2c bdf\n"); -+ return -ENODEV; -+ } -+ pr_info("IPU ACPI: DSM: i2c bus %d addr %x bdf: %s\n", -+ i2c[i].bus, i2c[i].addr, i2c[i].bdf); -+ } -+ -+ *num = num_i2c; -+ -+ return 0; -+} -+ -+static int match_depend(struct device *dev, const void *data) -+{ -+ return (dev && dev->fwnode == data) ? 1 : 0; -+} -+ -+int ipu_acpi_get_control_logic_data(struct device *dev, -+ struct control_logic_data **ctl_data) -+{ -+ /* CLDB data */ -+ struct control_logic_data_packed ctl_logic_data; -+ int ret; -+ -+ ret = read_acpi_block(dev, "CLDB", &ctl_logic_data, -+ sizeof(ctl_logic_data)); -+ -+ if (ret < 0) { -+ pr_err("IPU ACPI: Fail to read from CLDB"); -+ return ret; -+ } -+ -+ (*ctl_data)->type = ctl_logic_data.controllogictype; -+ (*ctl_data)->id = ctl_logic_data.controllogicid; -+ (*ctl_data)->sku = ctl_logic_data.sensorcardsku; -+ -+ pr_info("IPU ACPI: CLDB: version %d clid %d cltype %d sku %d", -+ ctl_logic_data.version, -+ ctl_logic_data.controllogicid, -+ ctl_logic_data.controllogictype, -+ ctl_logic_data.sensorcardsku); -+ -+ /* GPIO data */ -+ ret = ipu_acpi_get_gpio_data(dev, (*ctl_data)->gpio, ARRAY_SIZE((*ctl_data)->gpio), -+ &((*ctl_data)->gpio_num)); -+ -+ if (ret < 0) { -+ dev_err(dev, "Failed to get GPIO data"); -+ return ret; -+ } -+ return 0; -+} -+ -+int ipu_acpi_get_dep_data(struct device *dev, -+ struct control_logic_data *ctl_data) -+{ -+ struct acpi_handle *dev_handle = ACPI_HANDLE(dev); -+ struct acpi_handle_list dep_devices; -+ acpi_status status; -+ int i; -+ int rval; -+ -+ ctl_data->completed = false; -+ -+ if (!acpi_has_method(dev_handle, "_DEP")) { -+ pr_err("IPU ACPI: %s does not have _DEP method", dev_name(dev)); -+ return 0; -+ } -+ -+ if (!acpi_evaluate_reference(dev_handle, "_DEP", NULL, &dep_devices)) { -+ pr_err("IPU ACPI: %s failed to evaluate _DEP", dev_name(dev)); -+ return -ENODEV; -+ } -+ -+ for (i = 0; i < dep_devices.count; i++) { -+ struct acpi_device *device; -+ struct acpi_device_info *info; -+ struct device *p_dev; -+ int match; -+ -+ status = acpi_get_object_info(dep_devices.handles[i], &info); -+ if (ACPI_FAILURE(status)) { -+ pr_err("IPU ACPI: %s error reading _DEP device info", dev_name(dev)); -+ continue; -+ } -+ -+ match = info->valid & ACPI_VALID_HID && -+ !strcmp(info->hardware_id.string, "INT3472"); -+ -+ kfree(info); -+ -+ if (!match) -+ continue; -+ -+ /* Process device IN3472 created by acpi */ -+ device = acpi_fetch_acpi_dev(dep_devices.handles[i]); -+ if (!device) { -+ pr_err("IPU ACPI: Failed to get ACPI device"); -+ return -ENODEV; -+ } -+ -+ pr_info("IPU ACPI: Dependent ACPI device found: %s\n", -+ dev_name(&device->dev)); -+ -+ p_dev = bus_find_device(&platform_bus_type, NULL, -+ &device->fwnode, match_depend); -+ -+ if (p_dev) { -+ pr_info("IPU ACPI: Dependent platform device found %s\n", -+ dev_name(p_dev)); -+ -+ /* obtain Control Logic Data from BIOS */ -+ rval = ipu_acpi_get_control_logic_data(p_dev, &ctl_data); -+ -+ if (rval) { -+ pr_err("IPU ACPI: Error getting Control Logic Data"); -+ return rval; -+ } -+ -+ ctl_data->completed = true; -+ } else -+ pr_err("IPU ACPI: Dependent platform device not found for %s\n", dev_name(dev)); -+ } -+ -+ if (!ctl_data->completed) { -+ ctl_data->type = CL_EMPTY; -+ pr_err("IPU APCI: No control logic data available"); -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_acpi_get_dep_data); -+ -+int ipu_acpi_get_cam_data(struct device *dev, -+ struct sensor_bios_data *sensor) -+{ -+ /* SSDB */ -+ struct sensor_bios_data_packed sensor_data; -+ int ret; -+ -+ ret = read_acpi_block(dev, "SSDB", &sensor_data, -+ sizeof(sensor_data)); -+ -+ if (ret < 0) { -+ pr_err("IPU ACPI: Fail to read from SSDB with err %d", ret); -+ return ret; -+ } -+ -+ /* Xshutdown is not part of the ssdb data */ -+ sensor->link = sensor_data.link; -+ sensor->lanes = sensor_data.lanes; -+ sensor->pprval = sensor_data.pprval; -+ sensor->pprunit = sensor_data.pprunit; -+ sensor->bus_type = sensor_data.phyconfig; -+ sensor->degree = sensor_data.degree; -+ -+ pr_info("IPU ACPI: SSDB: name %s. link %d. lanes %d. pprval %d. pprunit %x. degree %d", -+ dev_name(dev), sensor->link, sensor->lanes, sensor->pprval, sensor->pprunit, -+ sensor->degree); -+ -+ /* I2C */ -+ ret = ipu_acpi_get_i2c_info(dev, sensor->i2c, ARRAY_SIZE(sensor->i2c), &sensor->i2c_num); -+ -+ if (ret < 0) { -+ pr_err("IPU ACPI: Failed to get I2C info"); -+ return ret; -+ } -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_acpi_get_cam_data); -+ -+MODULE_AUTHOR("Samu Onkalo "); -+MODULE_AUTHOR("Khai Wen Ng "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU ACPI support"); -diff --git a/drivers/media/platform/intel/ipu-acpi-pdata.c b/drivers/media/platform/intel/ipu-acpi-pdata.c -new file mode 100644 -index 000000000000..0a84dccf980c ---- /dev/null -+++ b/drivers/media/platform/intel/ipu-acpi-pdata.c -@@ -0,0 +1,594 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#include -+#include -+ -+#define MIN_SENSOR_I2C 1 -+#define MIN_SERDES_I2C 3 -+#define SUFFIX_BASE 97 -+ -+struct ipu7_isys_subdev_pdata acpi_subdev_pdata = { -+ .subdevs = (struct ipu7_isys_subdev_info *[]) { -+ NULL, -+ } -+}; -+ -+struct serdes_local serdes_info; -+ -+/* -+ * The dev_id was hard code in platform data, as i2c bus number -+ * may change dynamiclly, we need to update this bus id -+ * accordingly. -+ * -+ * @adapter_id: hardware i2c adapter id, this was fixed in platform data -+ * return: i2c bus id registered in system -+ */ -+static int get_i2c_bus_id(int adapter_id, char *adapter_bdf, int bdf_len) -+{ -+ struct i2c_adapter *adapter; -+ char name[32]; -+ int i = 0; -+ -+ if (adapter_bdf) { -+ while ((adapter = i2c_get_adapter(i)) != NULL) { -+ struct device *parent = adapter->dev.parent; -+ struct device *pp = parent->parent; -+ -+ if (pp && !strncmp(adapter_bdf, dev_name(pp), bdf_len)) -+ return i; -+ i++; -+ } -+ } -+ -+ i = 0; -+ snprintf(name, sizeof(name), "i2c_designware.%d", adapter_id); -+ while ((adapter = i2c_get_adapter(i)) != NULL) { -+ struct device *parent = adapter->dev.parent; -+ -+ if (parent && !strncmp(name, dev_name(parent), sizeof(name))) -+ return i; -+ i++; -+ } -+ -+ /* Not found, should never happen! */ -+ WARN_ON_ONCE(1); -+ return -1; -+} -+ -+/* -+ * update i2c bus here to avoid deadlock between i2c_for_each_dev -+ * and i2c_get_adapter -+ */ -+static void update_i2c_bus_id(void) -+{ -+ struct ipu7_isys_subdev_info **subdevs = acpi_subdev_pdata.subdevs; -+ for (int i = 0; subdevs[i] != NULL; i++) { -+ subdevs[i]->i2c.i2c_adapter_id = -+ get_i2c_bus_id(subdevs[i]->i2c.i2c_adapter_id, -+ subdevs[i]->i2c.i2c_adapter_bdf, -+ sizeof(subdevs[i]->i2c.i2c_adapter_bdf)); -+ } -+} -+ -+struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void) -+{ -+ struct ipu7_isys_subdev_pdata *ptr; -+ -+ update_i2c_bus_id(); -+ ptr = &acpi_subdev_pdata; -+ return ptr; -+} -+EXPORT_SYMBOL(get_acpi_subdev_pdata); -+ -+static void print_serdes_sdinfo(struct serdes_subdev_info *sdinfo) -+{ -+ int i; -+ struct serdes_module_pdata *sd_mpdata = sdinfo->board_info.platform_data; -+ -+ if (!sd_mpdata) { -+ pr_err("Empty serdes module pdata"); -+ return; -+ } -+ -+ pr_debug("\t\trx_port \t\t= %d", sdinfo->rx_port); -+ pr_debug("\t\tphy_i2c_addr \t\t= 0x%x", sdinfo->phy_i2c_addr); -+ pr_debug("\t\tser_alias \t\t= 0x%x", sdinfo->ser_alias); -+ pr_debug("\t\tser_phys_addr \t\t= 0x%x", sdinfo->ser_phys_addr); -+ pr_debug("\t\tsuffix \t\t\t= %s", sdinfo->suffix); -+ pr_debug("\t\tboard_info.type \t= %s", sdinfo->board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sdinfo->board_info.addr); -+ -+ pr_debug("serdes board_info.platform_data"); -+ pr_debug("\t\tlanes \t\t\t= %d", sd_mpdata->lanes); -+ pr_debug("\t\tmodule_name \t\t= %s", sd_mpdata->module_name); -+ pr_debug("\t\tfsin \t\t\t= %d", sd_mpdata->fsin); -+ -+ if (serdes_info.gpio_powerup_seq > 0) -+ for (i = 0; i < serdes_info.gpio_powerup_seq; i++) -+ pr_debug("\t\t gpio_powerup_seq[%d] \t= %d", i, -+ (int)sd_mpdata->gpio_powerup_seq[i]); -+} -+ -+static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) -+{ -+ struct serdes_platform_data *sd_pdata = sd->i2c.board_info.platform_data; -+ int i; -+ struct serdes_subdev_info *sd_sdinfo; -+ struct serdes_module_pdata *sd_mpdata; -+ -+ if (!sd_pdata) { -+ pr_err("Empty serdes subdev pdata"); -+ return; -+ } -+ -+ pr_debug("IPU ACPI: %s", __func__); -+ pr_debug("sd_csi2"); -+ pr_debug("\t\tnlanes \t\t\t= %d", sd->csi2->nlanes); -+ pr_debug("\t\tport \t\t\t= %d", sd->csi2->port); -+ pr_debug("\t\ttype \t\t\t= %d", sd->csi2->bus_type); -+ -+ pr_debug("sd->i2c"); -+ pr_debug("\t\ti2c_adapter_bdf \t= %s", sd->i2c.i2c_adapter_bdf); -+ pr_debug("\t\tboard_info.type \t= %s", sd->i2c.board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sd->i2c.board_info.addr); -+ -+ pr_debug("sd->i2c.board_info.platform_data"); -+ pr_debug("\t\treset_gpio \t\t= %d", sd_pdata->reset_gpio); -+ pr_debug("\t\tFPD_gpio \t\t= %d", sd_pdata->FPD_gpio); -+ pr_debug("\t\tsuffix \t\t\t= %c", sd_pdata->suffix); -+ -+ pr_debug("\t\tlink_freq_mbps \t\t= %d", sd_pdata->link_freq_mbps); -+ pr_debug("\t\tdeser_nlanes \t\t= %d", sd_pdata->deser_nlanes); -+ pr_debug("\t\tser_nlanes \t\t= %d", sd_pdata->ser_nlanes); -+ -+ for (i = 0; i < serdes_info.rx_port; i++) { -+ sd_sdinfo = &sd_pdata->subdev_info[i]; -+ sd_mpdata = sd_sdinfo->board_info.platform_data; -+ -+ if (!sd_mpdata) -+ continue; -+ -+ pr_debug("serdes subdev_info[%d]", i); -+ print_serdes_sdinfo(sd_sdinfo); -+ } -+ -+} -+ -+static void print_subdev(struct ipu7_isys_subdev_info *sd) -+{ -+ struct sensor_platform_data *spdata = sd->i2c.board_info.platform_data; -+ int i; -+ -+ if (!spdata) { -+ pr_err("IPU ACPI: Empty sensor subdev"); -+ return; -+ } -+ -+ pr_debug("IPU ACPI: %s", __func__); -+ pr_debug("sd->csi2"); -+ pr_debug("\t\tnlanes \t\t\t= %d", sd->csi2->nlanes); -+ pr_debug("\t\tport \t\t\t= %d", sd->csi2->port); -+ pr_debug("\t\ttype \t\t\t= %d", sd->csi2->bus_type); -+ -+ pr_debug("sd->i2c"); -+ pr_debug("\t\ti2c_adapter_bdf \t= %s", sd->i2c.i2c_adapter_bdf); -+ pr_debug("\t\tboard_info.type \t= %s", sd->i2c.board_info.type); -+ pr_debug("\t\tboard_info.addr \t= 0x%x", sd->i2c.board_info.addr); -+ -+ pr_debug("sd->i2c.platform_data"); -+ pr_debug("\t\tport \t\t\t= %d", spdata->port); -+ pr_debug("\t\tlanes \t\t\t= %d", spdata->lanes); -+ pr_debug("\t\ti2c_slave_address \t= 0x%x", spdata->i2c_slave_address); -+ pr_debug("\t\tirq_pin \t\t= %d", spdata->irq_pin); -+ pr_debug("\t\tirq_pin_name \t\t= %s", spdata->irq_pin_name); -+ pr_debug("\t\tsuffix \t\t\t= %c", spdata->suffix); -+ pr_debug("\t\treset_pin \t\t= %d", spdata->reset_pin); -+ pr_debug("\t\tdetect_pin \t\t= %d", spdata->detect_pin); -+ -+ for (i = 0; i < IPU7_SPDATA_GPIO_NUM; i++) -+ pr_debug("\t\tgpios[%d] \t\t= %d", i, spdata->gpios[i]); -+} -+ -+static void set_common_gpio(struct control_logic_data *ctl_data, -+ struct sensor_platform_data **pdata) -+{ -+ int i; -+ -+ /* TODO: consider remove specific naming such as irq_pin, and use gpios[] */ -+ (*pdata)->irq_pin = -1; -+ (*pdata)->reset_pin = -1; -+ (*pdata)->detect_pin = -1; -+ -+ (*pdata)->gpios[0] = -1; -+ (*pdata)->gpios[1] = 0; -+ (*pdata)->gpios[2] = 0; -+ (*pdata)->gpios[3] = 0; -+ -+ /* all sensors should have RESET GPIO */ -+ if (ctl_data->completed && ctl_data->gpio_num > 0) -+ for (i = 0; i < ctl_data->gpio_num; i++) -+ if (ctl_data->gpio[i].func != GPIO_RESET) -+ dev_err(ctl_data->dev, -+ "IPU ACPI: Invalid GPIO func: %d\n", -+ ctl_data->gpio[i].func); -+} -+ -+static int set_csi2(struct ipu7_isys_subdev_info **sensor_sd, -+ unsigned int lanes, unsigned int port, -+ unsigned int bus_type) -+{ -+ struct ipu7_isys_csi2_config *csi2_config; -+ -+ csi2_config = kzalloc(sizeof(*csi2_config), GFP_KERNEL); -+ if (!csi2_config) -+ return -ENOMEM; -+ -+ csi2_config->nlanes = lanes; -+ csi2_config->port = port; -+ if (bus_type == PHY_MODE_DPHY) -+ csi2_config->bus_type = V4L2_MBUS_CSI2_DPHY; -+ else if (bus_type == PHY_MODE_CPHY) -+ csi2_config->bus_type = V4L2_MBUS_CSI2_CPHY; -+ else -+ csi2_config->bus_type = V4L2_MBUS_UNKNOWN; -+ -+ (*sensor_sd)->csi2 = csi2_config; -+ -+ return 0; -+} -+ -+static void set_i2c(struct ipu7_isys_subdev_info **sensor_sd, -+ struct device *dev, -+ const char *sensor_name, -+ unsigned int addr, -+ const char *i2c_adapter_bdf) -+{ -+ dev_info(dev, "IPU ACPI: kernel I2C BDF: %s", i2c_adapter_bdf); -+ (*sensor_sd)->i2c.board_info.addr = addr; -+ strscpy((*sensor_sd)->i2c.board_info.type, sensor_name, I2C_NAME_SIZE); -+ strscpy((*sensor_sd)->i2c.i2c_adapter_bdf, i2c_adapter_bdf, -+ sizeof((*sensor_sd)->i2c.i2c_adapter_bdf)); -+} -+ -+static void set_serdes_sd_pdata(struct serdes_module_pdata **module_pdata, -+ const char *sensor_name, const char *hid_name, -+ unsigned int lanes) -+{ -+ /* general */ -+ (*module_pdata)->lanes = lanes; -+ strscpy((*module_pdata)->module_name, sensor_name, I2C_NAME_SIZE); -+} -+ -+#define PORT_NR 8 -+ -+static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, -+ struct device *dev, -+ struct serdes_platform_data **pdata, -+ const char *sensor_name, -+ const char *hid_name, -+ unsigned int lanes, -+ unsigned int addr, -+ unsigned int subdev_num) -+{ -+ int i; -+ struct serdes_module_pdata *module_pdata[PORT_NR]; -+ struct serdes_subdev_info *serdes_sdinfo; -+ size_t subdev_size = subdev_num * sizeof(*serdes_sdinfo); -+ unsigned int port = (*pdata)->suffix - SUFFIX_BASE; -+ -+ serdes_sdinfo = kzalloc(subdev_size, GFP_KERNEL); -+ if (!serdes_sdinfo) -+ return -ENOMEM; -+ -+ for (i = 0; i < subdev_num; i++) { -+ module_pdata[i] = kzalloc(sizeof(*module_pdata[i]), GFP_KERNEL); -+ if (!module_pdata[i]) { -+ kfree(serdes_sdinfo); -+ return -ENOMEM; -+ } -+ -+ set_serdes_sd_pdata(&module_pdata[i], sensor_name, hid_name, lanes); -+ -+ /* board info */ -+ strscpy(serdes_sdinfo[i].board_info.type, sensor_name, I2C_NAME_SIZE); -+ serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ serdes_sdinfo[i].board_info.platform_data = module_pdata[i]; -+ -+ /* serdes_subdev_info */ -+ serdes_sdinfo[i].rx_port = i; -+ serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; -+ -+ serdes_sdinfo[i].phy_i2c_addr = serdes_info.phy_i2c_addr; -+ snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", -+ SUFFIX_BASE + i, port); -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ serdes_sdinfo[i].ser_phys_addr = 0x40; -+ serdes_sdinfo[i].sensor_dt = 0x1e; -+#endif -+ } -+ -+ (*pdata)->subdev_info = serdes_sdinfo; -+ (*pdata)->subdev_num = subdev_num; -+ -+ return 0; -+} -+ -+static int set_pdata(struct ipu7_isys_subdev_info **sensor_sd, -+ struct device *dev, -+ const char *sensor_name, -+ const char *hid_name, -+ struct control_logic_data *ctl_data, -+ unsigned int port, -+ unsigned int lanes, -+ unsigned int addr, -+ unsigned int subdev_num, -+ unsigned int deser_lanes, -+ bool is_dummy, -+ enum connection_type connect, -+ int link_freq, -+ int des_port) -+{ -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *pdata; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pr_debug("IPU ACPI: %s - Direct connection", __func__); -+ /* use ascii */ -+ /* port for start from 0 */ -+ if (port >= 0) { -+ pdata->suffix = port + SUFFIX_BASE; -+ pr_info("IPU ACPI: create %s on port %d", -+ sensor_name, port); -+ } else -+ dev_err(dev, "INVALID MIPI PORT"); -+ -+ pdata->port = port; -+ pdata->lanes = lanes; -+ pdata->i2c_slave_address = addr; -+ -+ /* gpio */ -+ set_common_gpio(ctl_data, &pdata); -+ -+ (*sensor_sd)->i2c.board_info.platform_data = pdata; -+ } else if (connect == TYPE_SERDES) { -+ struct serdes_platform_data *pdata; -+ -+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); -+ if (!pdata) -+ return -ENOMEM; -+ -+ pr_debug("IPU ACPI: %s - Serdes connection", __func__); -+ /* use ascii */ -+ if (port >= 0) { -+ pdata->suffix = port + SUFFIX_BASE; -+ pr_info("IPU ACPI: create %s on mipi port %d", -+ sensor_name, port); -+ } else -+ pr_err("IPU ACPI: Invalid MIPI Port : %d", port); -+ -+ pdata->link_freq_mbps = link_freq; -+ pdata->bus_type = (*sensor_sd)->csi2->bus_type; -+ pdata->deser_nlanes = deser_lanes; -+ pdata->ser_nlanes = lanes; -+ pdata->des_port = des_port; -+ strscpy(pdata->ser_name, (*sensor_sd)->i2c.board_info.type, I2C_NAME_SIZE); -+ set_serdes_subdev(sensor_sd, dev, &pdata, sensor_name, hid_name, lanes, addr, subdev_num); -+ -+ (*sensor_sd)->i2c.board_info.platform_data = pdata; -+ pdata->deser_board_info = &(*sensor_sd)->i2c.board_info; -+ } -+ -+ return 0; -+} -+ -+static void set_serdes_info(struct device *dev, const char *sensor_name, -+ const char *serdes_name, -+ struct sensor_bios_data *cam_data, -+ int sensor_physical_addr) -+{ -+ int i; -+ -+ serdes_info.deser_num = 0; -+ /* pprunit as num of sensor connected to deserializer */ -+ serdes_info.rx_port = cam_data->pprunit; -+ -+ /* i2c devices */ -+ serdes_info.i2c_num = cam_data->i2c_num; -+ -+ i = 1; -+ /* serializer mapped addr */ -+ serdes_info.ser_map_addr = cam_data->i2c[i++].addr; -+ /* sensor mapped addr */ -+ serdes_info.sensor_map_addr = cam_data->i2c[i++].addr; -+ -+ serdes_info.gpio_powerup_seq = 0; -+ -+ serdes_info.phy_i2c_addr = sensor_physical_addr; -+} -+ -+static int populate_sensor_pdata(struct device *dev, -+ struct ipu7_isys_subdev_info **sensor_sd, -+ struct sensor_bios_data *cam_data, -+ struct control_logic_data *ctl_data, -+ enum connection_type connect, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq) -+{ -+ struct ipu7_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -+ int i = 0; -+ int ret; -+ -+ if (connect == TYPE_DIRECT) { -+ /* sensor csi2 info */ -+ ret = set_csi2(sensor_sd, cam_data->lanes, cam_data->link, cam_data->bus_type); -+ if (ret) -+ return ret; -+ -+ /* sensor i2c info */ -+ if (cam_data->i2c_num == MIN_SENSOR_I2C) { -+ pr_debug("IPU ACPI: num of I2C device for Direct connection: %lld is Correct.", -+ cam_data->i2c_num); -+ set_i2c(sensor_sd, dev, sensor_name, cam_data->i2c[0].addr, cam_data->i2c[0].bdf); -+ } else { -+ pr_err("IPU ACPI: num of I2C device for Direct connection : %lld is Incorrect", -+ cam_data->i2c_num); -+ return -1; -+ } -+ /* Others use DISCRETE Control Logic */ -+ if (ctl_data->type != CL_DISCRETE) { -+ dev_err(dev, "IPU ACPI: Control Logic Type\n"); -+ dev_err(dev, "for %s: %d is Incorrect\n", -+ sensor_name, ctl_data->type); -+ return -EINVAL; -+ } -+ } else if (connect == TYPE_SERDES) { -+ /* serdes csi2 info. pprval as deserializer lane */ -+ ret = set_csi2(sensor_sd, cam_data->pprval, cam_data->link, cam_data->bus_type); -+ if (ret) -+ return ret; -+ -+ /* Use DISCRETE Control Logic or No Control Logic for serdes */ -+ if (ctl_data->type != CL_DISCRETE && ctl_data->type != CL_EMPTY) { -+ pr_err("IPU ACPI: Control Logic Type for serdes: %d is Incorrect", -+ ctl_data->type); -+ return -1; -+ } -+ -+ /* serdes i2c info */ -+ if (cam_data->i2c_num >= MIN_SERDES_I2C) { -+ pr_debug("IPU ACPI: num of I2C device for Serdes connection: %lld is Correct", -+ cam_data->i2c_num); -+ set_i2c(sensor_sd, dev, serdes_name, cam_data->i2c[0].addr, cam_data->i2c[0].bdf); -+ } else { -+ pr_err("IPU ACPI: num of I2C device for Serdes connection: %lld is Incorrect", -+ cam_data->i2c_num); -+ return -1; -+ } -+ -+ /* local serdes info */ -+ set_serdes_info(dev, sensor_name, serdes_name, cam_data, sensor_physical_addr); -+ } -+ -+ /* Use last I2C device */ -+ ret = set_pdata(sensor_sd, dev, sensor_name, hid_name, ctl_data, cam_data->link, -+ cam_data->lanes, cam_data->i2c[cam_data->i2c_num - 1].addr, -+ cam_data->pprunit, cam_data->pprval, false, connect, link_freq, cam_data->degree); -+ if (ret) -+ return ret; -+ -+ /* update local ipu7_isys_subdev_pdata */ -+ while (i <= MAX_ACPI_SENSOR_NUM) { -+ if (!ptr_acpi_subdev_pdata->subdevs[i]) { -+ ptr_acpi_subdev_pdata->subdevs[i] = *sensor_sd; -+ ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -+ break; -+ } -+ i++; -+ } -+ -+ /* print new subdev */ -+ if (connect == TYPE_DIRECT) { -+ pr_debug("New sensor subdev\n"); -+ print_subdev(*sensor_sd); -+ } else { -+ pr_debug("New serdes subdev\n"); -+ print_serdes_subdev(*sensor_sd); -+ } -+ -+ /* update total num of sensor connected */ -+ if (connect == TYPE_SERDES) -+ serdes_info.deser_num++; -+ -+ return 0; -+} -+ -+int get_sensor_pdata(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, size_t size, -+ enum connection_type connect, const char *sensor_name, -+ const char *serdes_name, const char *hid_name, -+ int sensor_physical_addr, int link_freq) -+{ -+ struct sensor_bios_data *cam_data; -+ struct control_logic_data *ctl_data; -+ struct ipu7_isys_subdev_info *sensor_sd; -+ int rval; -+ -+ cam_data = kzalloc(sizeof(*cam_data), GFP_KERNEL); -+ if (!cam_data) -+ return -ENOMEM; -+ cam_data->dev = dev; -+ -+ ctl_data = kzalloc(sizeof(*ctl_data), GFP_KERNEL); -+ if (!ctl_data) { -+ kfree(cam_data); -+ return -ENOMEM; -+ } -+ ctl_data->dev = dev; -+ -+ sensor_sd = kzalloc(sizeof(*sensor_sd), GFP_KERNEL); -+ if (!sensor_sd) { -+ kfree(cam_data); -+ kfree(ctl_data); -+ return -ENOMEM; -+ } -+ -+ /* camera info */ -+ rval = ipu_acpi_get_cam_data(dev, cam_data); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ /* control logic info */ -+ rval = ipu_acpi_get_dep_data(dev, ctl_data); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ /* populate pdata */ -+ rval = populate_sensor_pdata(dev, &sensor_sd, cam_data, ctl_data, -+ connect, sensor_name, serdes_name, hid_name, -+ sensor_physical_addr, link_freq); -+ if (rval) { -+ kfree(sensor_sd); -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+ } -+ -+ dev->platform_data = sensor_sd; -+ -+ kfree(cam_data); -+ kfree(ctl_data); -+ return rval; -+} -+EXPORT_SYMBOL(get_sensor_pdata); -+ -+MODULE_AUTHOR("Khai Wen, Ng "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU ACPI support"); -diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c -new file mode 100644 -index 000000000000..deed3bba52bb ---- /dev/null -+++ b/drivers/media/platform/intel/ipu-acpi.c -@@ -0,0 +1,224 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+#include "ipu7.h" -+#include "ipu7-isys.h" -+ -+static LIST_HEAD(devices); -+ -+static struct ipu_camera_module_data *add_device_to_list( -+ struct list_head *devices) -+{ -+ struct ipu_camera_module_data *cam_device; -+ -+ cam_device = kzalloc(sizeof(*cam_device), GFP_KERNEL); -+ if (!cam_device) -+ return NULL; -+ -+ list_add(&cam_device->list, devices); -+ return cam_device; -+} -+ -+static const struct ipu_acpi_devices supported_devices[] = { -+/* -+ * { "ACPI ID", sensor_name, get_sensor_pdata, NULL, 0, TYPE, serdes_name, -+ * sensor_physical_addr, link_freq(mbps) }, // Custom HID -+ */ -+ -+#if IS_ENABLED(CONFIG_VIDEO_MAX9X) -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ { "INTC031M", ISX031_NAME, get_sensor_pdata, NULL, 0, TYPE_SERDES, "max9x", -+ ISX031_I2C_ADDRESS, 1600 }, // D3 ISX031 HID -+#endif -+#endif -+}; -+ -+static int get_table_index(const char *acpi_name) -+{ -+ unsigned int i; -+ -+ for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { -+ if (!strncmp(supported_devices[i].hid_name, acpi_name, -+ strlen(supported_devices[i].hid_name))) -+ return i; -+ } -+ -+ return -ENODEV; -+} -+ -+/* List of ACPI devices what we can handle */ -+/* Must match with HID in BIOS option. Add new sensor if required */ -+static const struct acpi_device_id ipu_acpi_match[] = { -+/* -+ * { "AR0234A", 0 }, // Custom HID -+ */ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+ { "INTC1031", 0 }, // ISX031 HID -+ { "INTC031M", 0 }, // D3CMC68N-115-084 ISX031 HID -+#endif -+ {}, -+}; -+ -+static int ipu_acpi_get_pdata(struct device *dev, int index) -+{ -+ struct ipu_camera_module_data *camdata; -+ int rval; -+ -+ if (index < 0) { -+ pr_err("Device is not in supported devices list\n"); -+ return -ENODEV; -+ } -+ -+ camdata = add_device_to_list(&devices); -+ if (!camdata) -+ return -ENOMEM; -+ -+ pr_info("IPU ACPI: Getting BIOS data for %s (%s)", -+ supported_devices[index].real_driver, dev_name(dev)); -+ -+ rval = supported_devices[index].get_platform_data( -+ dev, camdata, -+ supported_devices[index].priv_data, -+ supported_devices[index].priv_size, -+ supported_devices[index].connect, -+ supported_devices[index].real_driver, -+ supported_devices[index].serdes_name, -+ supported_devices[index].hid_name, -+ supported_devices[index].sensor_physical_addr, -+ supported_devices[index].link_freq); -+ -+ if (rval) -+ return -EPROBE_DEFER; -+ -+ return 0; -+} -+ -+/* -+ * different acpi devices may have same HID, so acpi_dev_get_first_match_dev -+ * will always match device to simple fwnode. -+ */ -+static int ipu_acpi_test(struct device *dev, void *priv) -+{ -+ struct acpi_device *adev = NULL; -+ int rval; -+ int acpi_idx = get_table_index(dev_name(dev)); -+ -+ if (acpi_idx < 0) -+ return 0; -+ else -+ dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); -+ -+ const char *target_hid = supported_devices[acpi_idx].hid_name; -+ -+ if (!ACPI_COMPANION(dev)) { -+ while ((adev = acpi_dev_get_next_match_dev(adev, target_hid, -+ NULL, -1))) { -+ if (adev->flags.reserved == 0) { -+ adev->flags.reserved = 1; -+ break; -+ } -+ acpi_dev_put(adev); -+ } -+ -+ if (!adev) { -+ dev_dbg(dev, "No ACPI device found for %s\n", target_hid); -+ return 0; -+ } else { -+ set_primary_fwnode(dev, &adev->fwnode); -+ dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); -+ } -+ } -+ -+ if (ACPI_COMPANION(dev) != adev) { -+ dev_err(dev, "Failed to set ACPI companion for %s\n", -+ dev_name(dev)); -+ acpi_dev_put(adev); -+ return 0; -+ } -+ -+ acpi_dev_put(adev); -+ -+ rval = ipu_acpi_get_pdata(dev, acpi_idx); -+ if (rval) { -+ pr_err("IPU6 ACPI: Failed to process ACPI data"); -+ return rval; -+ } -+ -+ return 0; /* Continue iteration */ -+} -+ -+/* Try to get all IPU related devices mentioned in BIOS and all related information -+ * return a new generated existing pdata -+ */ -+ -+int ipu_get_acpi_devices(void **spdata) -+{ -+ struct ipu_i2c_helper helper = {0}; -+ int rval; -+ struct ipu7_isys_subdev_pdata *ptr = NULL; -+ -+ rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); -+ if (rval < 0) -+ return rval; -+ -+ ptr = get_acpi_subdev_pdata(); -+ if (ptr && *ptr->subdevs) -+ *spdata = ptr; -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_get_acpi_devices); -+ -+static int __init ipu_acpi_init(void) -+{ -+ return 0; -+} -+ -+static void __exit ipu_acpi_exit(void) -+{ -+} -+ -+module_init(ipu_acpi_init); -+module_exit(ipu_acpi_exit); -+ -+MODULE_AUTHOR("Samu Onkalo "); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("IPU ACPI support"); -diff --git a/drivers/media/platform/intel/ipu7-fpga-pdata.c b/drivers/media/platform/intel/ipu7-fpga-pdata.c -new file mode 100644 -index 000000000000..9f60a38d1536 ---- /dev/null -+++ b/drivers/media/platform/intel/ipu7-fpga-pdata.c -@@ -0,0 +1,60 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2020 Intel Corporation -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-isys.h" -+ -+#define OV13B10_LANES 4 -+#define OV13B10_2LANES 2 -+#define OV13B10_I2C_ADDRESS 0x10 -+ -+static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_0 = { -+ .nlanes = OV13B10_LANES, -+ .port = 0, -+}; -+ -+static struct ipu7_isys_subdev_info ov13b10_sd_0 = { -+ .csi2 = &ov13b10_csi2_cfg_0, -+ .i2c = { -+ .board_info = { -+ I2C_BOARD_INFO("ov13b10", OV13B10_I2C_ADDRESS), -+ }, -+ .i2c_adapter_id = 0, -+ } -+}; -+ -+static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_1 = { -+ .nlanes = OV13B10_2LANES, -+ .port = 2, -+}; -+ -+static struct ipu7_isys_subdev_info ov13b10_sd_1 = { -+ .csi2 = &ov13b10_csi2_cfg_1, -+ .i2c = { -+ .board_info = { -+ I2C_BOARD_INFO("ov13b10", OV13B10_I2C_ADDRESS), -+ }, -+ .i2c_adapter_id = 1, -+ } -+}; -+ -+static struct ipu7_isys_subdev_pdata pdata = { -+ .subdevs = (struct ipu7_isys_subdev_info *[]) { -+ &ov13b10_sd_0, -+ &ov13b10_sd_1, -+ NULL, -+ }, -+}; -+ -+static void ipu7_quirk(struct pci_dev *pci_dev) -+{ -+ dev_info(&pci_dev->dev, "%s() attach the platform data", __func__); -+ pci_dev->dev.platform_data = &pdata; -+} -+ -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID, ipu7_quirk); -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID, ipu7_quirk); -+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID, ipu7_quirk); -diff --git a/include/media/i2c/isx031.h b/include/media/i2c/isx031.h -new file mode 100644 -index 000000000000..4542f150695f ---- /dev/null -+++ b/include/media/i2c/isx031.h -@@ -0,0 +1,24 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2014 - 2025 Intel Corporation */ -+ -+#ifndef __ISX031_H -+#define __ISX031_H -+ -+#include -+ -+#define ISX031_NAME "isx031" -+ -+#define ISX031_I2C_ADDRESS 0x1a -+ -+struct isx031_platform_data { -+ unsigned int port; -+ unsigned int lanes; -+ uint32_t i2c_slave_address; -+ int irq_pin; -+ unsigned int irq_pin_flags; -+ char irq_pin_name[16]; -+ char suffix[5]; -+ int gpios[4]; -+}; -+ -+#endif /* __ISX031_H */ -diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h -new file mode 100644 -index 000000000000..e2074419dd88 ---- /dev/null -+++ b/include/media/ipu-acpi-pdata.h -@@ -0,0 +1,120 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2023-2025 Intel Corporation */ -+#include -+#include -+#include -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+#define CL_EMPTY 0 -+#define CL_DISCRETE 1 -+#define SERDES_MAX_GPIO_POWERUP_SEQ 4 -+#define LOOP_SIZE 10 -+ -+/* CPHY is supported since ipu7*/ -+#define PHY_MODE_DPHY 0 -+#define PHY_MODE_CPHY 1 -+ -+int get_sensor_pdata(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, size_t size, -+ enum connection_type connect, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq); -+ -+struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void); -+ -+struct sensor_platform_data { -+ unsigned int port; -+ unsigned int lanes; -+ uint32_t i2c_slave_address; -+ int irq_pin; -+ unsigned int irq_pin_flags; -+ char irq_pin_name[IPU7_SPDATA_IRQ_PIN_NAME_LEN]; -+ int reset_pin; -+ int detect_pin; -+ char suffix; -+ int gpios[IPU7_SPDATA_GPIO_NUM]; -+}; -+ -+struct serdes_platform_data { -+ unsigned int subdev_num; -+ struct serdes_subdev_info *subdev_info; -+ unsigned int reset_gpio; -+ unsigned int FPD_gpio; -+ char suffix; -+ unsigned int link_freq_mbps; -+ enum v4l2_mbus_type bus_type; -+ unsigned int deser_nlanes; -+ unsigned int ser_nlanes; -+ unsigned int des_port; -+ char ser_name[I2C_NAME_SIZE]; -+ struct i2c_board_info *deser_board_info; -+}; -+ -+struct serdes_subdev_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ unsigned short rx_port; -+ unsigned short phy_i2c_addr; -+ unsigned short ser_alias; -+ char suffix[5]; /* suffix for subdevs */ -+ unsigned short ser_phys_addr; -+ unsigned int sensor_dt; -+}; -+ -+struct serdes_module_pdata { -+ unsigned short i2c_addr; -+ unsigned short i2c_adapter; -+ unsigned int lanes; -+ int xshutdown; -+ int fsin; -+ int reset; -+ char gpio_powerup_seq[SERDES_MAX_GPIO_POWERUP_SEQ]; -+ unsigned int module_flags; -+ char module_name[I2C_NAME_SIZE]; -+ char suffix; -+}; -+ -+struct serdes_local { -+ /* num of camera sensor connected to current mipi port */ -+ unsigned int rx_port; -+ -+ /* num of i2c addr for current ACPI device */ -+ unsigned int i2c_num; -+ -+ /* current sensor_addr */ -+ unsigned short sensor_addr; -+ -+ /* physical i2c addr */ -+ unsigned short phy_i2c_addr; -+ -+ /* last mapped addr */ -+ unsigned short sensor_map_addr; -+ -+ /* current serializer_addr */ -+ unsigned short ser_addr; -+ -+ /* last mapped addr */ -+ unsigned short ser_map_addr; -+ -+ /* 2nd group of mapped addr for 2x sensors */ -+ unsigned short sensor_map_addr_2; -+ unsigned short ser_map_addr_2; -+ -+ /* current gpio_powerup_seq */ -+ unsigned int gpio_powerup_seq; -+ -+ /* current module flag */ -+ unsigned int module_flags; -+ -+ /* counter for total camera sensor connected */ -+ unsigned int sensor_num; -+ -+ /* counter for total deser connected */ -+ unsigned int deser_num; -+}; -diff --git a/include/media/ipu-acpi.h b/include/media/ipu-acpi.h -new file mode 100644 -index 000000000000..bc63240d7c24 ---- /dev/null -+++ b/include/media/ipu-acpi.h -@@ -0,0 +1,186 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#ifndef MEDIA_INTEL_IPU_ACPI_H -+#define MEDIA_INTEL_IPU_ACPI_H -+ -+#include "ipu7-isys.h" -+ -+#define MAX_ACPI_SENSOR_NUM 4 -+#define MAX_ACPI_I2C_NUM 12 -+#define MAX_ACPI_GPIO_NUM 12 -+ -+#define GPIO_RESET 0x0 -+ -+#define IPU7_SPDATA_GPIO_NUM 4 -+#define IPU7_SPDATA_IRQ_PIN_NAME_LEN 16 -+ -+enum connection_type { -+ TYPE_DIRECT, -+ TYPE_SERDES -+}; -+ -+/* Data representation as it is in ACPI SSDB buffer */ -+struct sensor_bios_data_packed { -+ u8 version; -+ u8 sku; -+ u8 guid_csi2[16]; -+ u8 devfunction; -+ u8 bus; -+ u32 dphylinkenfuses; -+ u32 clockdiv; -+ u8 link; -+ u8 lanes; -+ u32 csiparams[10]; -+ u32 maxlanespeed; -+ u8 sensorcalibfileidx; -+ u8 sensorcalibfileidxInMBZ[3]; -+ u8 romtype; -+ u8 vcmtype; -+ u8 platforminfo; -+ u8 platformsubinfo; -+ u8 flash; -+ u8 privacyled; -+ u8 degree; -+ u8 mipilinkdefined; -+ u32 mclkspeed; -+ u8 controllogicid; -+ u8 mipidataformat; -+ u8 siliconversion; -+ u8 customerid; -+ u8 mclkport; -+ u8 pmicpos; -+ u8 voltagerail; -+ u8 pprval; -+ u8 pprunit; -+ u8 flashid; -+ u8 phyconfig; -+ u8 reserved2[7]; -+} __attribute__((__packed__)); -+ -+struct ipu_i2c_info { -+ unsigned short bus; -+ unsigned short addr; -+ char bdf[32]; -+}; -+ -+/* Fields needed by ipu driver */ -+/* Each I2C client can have 12 device */ -+struct sensor_bios_data { -+ struct device *dev; -+ u8 link; -+ u8 lanes; -+ u8 vcmtype; -+ u8 flash; -+ u8 degree; -+ u8 mclkport; -+ u32 mclkspeed; -+ u16 xshutdown; -+ u8 controllogicid; -+ u8 pprval; -+ u8 pprunit; -+ struct ipu_i2c_info i2c[MAX_ACPI_I2C_NUM]; -+ u64 i2c_num; -+ u8 bus_type; -+}; -+ -+struct control_logic_data_packed { -+ u8 version; -+ u8 controllogictype; -+ u8 controllogicid; -+ u8 sensorcardsku; -+ u8 inputclk; -+ u8 platformid; -+ u8 subplatformid; -+ u8 customerid; -+ u8 wled1_maxflashcurrent; -+ u8 wled1_maxtorchcurrent; -+ u8 wled2_maxflashcurrent; -+ u8 wled2_maxtorchcurrent; -+ u8 wled1_type; -+ u8 wled2_type; -+ u8 pch_clk_src; -+ u8 reserved2[17]; -+} __attribute__((__packed__)); -+ -+struct ipu_gpio_info { -+ unsigned short init_state; -+ unsigned short pin; -+ unsigned short func; -+ bool valid; -+}; -+ -+/* Each I2C client linked to 1 set of CTL Logic */ -+struct control_logic_data { -+ struct device *dev; -+ u8 id; -+ u8 type; -+ u8 sku; -+ u64 gpio_num; -+ struct ipu_gpio_info gpio[MAX_ACPI_GPIO_NUM]; -+ bool completed; -+}; -+ -+int ipu_get_acpi_devices(void **spdata); -+ -+struct ipu7_isys_subdev_pdata *get_built_in_pdata(void); -+ -+int ipu_acpi_get_cam_data(struct device *dev, -+ struct sensor_bios_data *sensor); -+ -+int ipu_acpi_get_dep_data(struct device *dev, -+ struct control_logic_data *ctl_data); -+ -+int ipu_acpi_get_control_logic_data(struct device *dev, -+ struct control_logic_data **ctl_data); -+ -+struct ipu_i2c_helper { -+ int (*fn)(struct device *dev, void *priv, -+ struct ipu7_isys_csi2_config *csi2, -+ bool reprobe); -+ void *driver_data; -+}; -+ -+struct ipu_camera_module_data { -+ struct list_head list; -+ struct ipu7_isys_subdev_info sd; -+ struct ipu7_isys_csi2_config csi2; -+ unsigned int ext_clk; -+ void *pdata; /* Ptr to generated platform data*/ -+ void *priv; /* Private for specific subdevice */ -+}; -+ -+struct ipu_acpi_devices { -+ const char *hid_name; -+ const char *real_driver; -+ int (*get_platform_data)(struct device *dev, -+ struct ipu_camera_module_data *data, -+ void *priv, -+ size_t size, -+ enum connection_type type, -+ const char *sensor_name, -+ const char *serdes_name, -+ const char *hid_name, -+ int sensor_physical_addr, -+ int link_freq); -+ void *priv_data; -+ size_t priv_size; -+ enum connection_type connect; -+ const char *serdes_name; -+ int sensor_physical_addr; -+ int link_freq; /* in mbps */ -+}; -+ -+#endif -diff --git a/include/uapi/linux/ipu7-psys.h b/include/uapi/linux/ipu7-psys.h -new file mode 100644 -index 000000000000..f7b841faa215 ---- /dev/null -+++ b/include/uapi/linux/ipu7-psys.h -@@ -0,0 +1,246 @@ -+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -+/* Copyright (C) 2013 - 2023 Intel Corporation */ -+ -+#ifndef _UAPI_IPU_PSYS_H -+#define _UAPI_IPU_PSYS_H -+ -+#ifdef __KERNEL__ -+#include -+#else -+#include -+#endif -+ -+struct ipu_psys_capability { -+ uint32_t version; -+ uint8_t driver[20]; -+ uint8_t dev_model[32]; -+ uint32_t reserved[17]; -+} __attribute__ ((packed)); -+ -+/** -+ * PSYS event error to user -+ */ -+enum ipu_psys_event_error { -+ IPU_PSYS_EVT_ERROR_NONE = 0U, -+ IPU_PSYS_EVT_ERROR_INTERNAL = 1U, -+ IPU_PSYS_EVT_ERROR_FRAME = 2U, -+ IPU_PSYS_EVT_ERROR_FORCE_CLOSED = 3U, -+ IPU_PSYS_EVT_ERROR_MAX -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_event - event back from driver to user for requested tasks -+ * @graph_id: unique id per graph -+ * @node_ctx_id: unique logical context id per cb -+ * @frame_id: unique id per frame, originally assigned by user -+ * @error: error code of ipu_psys_event_error type -+ */ -+struct ipu_psys_event { -+ uint8_t graph_id; -+ uint8_t node_ctx_id; -+ uint8_t frame_id; -+ uint32_t error; -+ int32_t reserved[2]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_buffer - for input/output terminals -+ * @len: total allocated size @ base address -+ * @userptr: user pointer -+ * @fd: DMA-BUF handle -+ * @data_offset:offset to valid data -+ * @bytes_used: amount of valid data including offset -+ * @flags: flags -+ */ -+struct ipu_psys_buffer { -+ uint64_t len; -+ union { -+ int fd; -+ void __user *userptr; -+ uint64_t reserved; -+ } base; -+ uint32_t data_offset; -+ uint32_t bytes_used; -+ uint32_t flags; -+ uint32_t reserved[2]; -+} __attribute__ ((packed)); -+ -+/**< Max number of logical node */ -+#define MAX_GRAPH_NODES 5U -+/**< Max number of profile */ -+#define MAX_GRAPH_NODE_PROFILES 1U -+#define MAX_GRAPH_LINKS 10U -+#define MAX_GRAPH_TERMINALS 32U -+ -+/** -+ * Settings per node on the bitmap -+ * @teb: Terminal Enable bitmap -+ * @deb: Device Enable bitmap -+ * @rbm: Routing bitmap -+ * @reb: Routing Enable bitmap -+ */ -+struct node_profile { -+ uint32_t teb[2]; -+ uint32_t deb[4]; -+ uint32_t rbm[4]; -+ uint32_t reb[4]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct node_ternimal - terminal description on the node -+ * -+ * Terminal is the logical connection entity that is in the node, -+ * it can be different types, one node could have multiple terminal. -+ * -+ * @term_id: id of the terminal -+ * @buf_size: payload(PAC or SDI) size of the certain terminal -+ */ -+struct node_ternimal { -+ uint8_t term_id; -+ uint32_t buf_size; -+} __attribute__ ((packed)); -+ -+/** -+ * struct graph_node - Description of graph that will be used for device -+ * and graph open purpose -+ * -+ * Node is the logical entity of a graph, one graph could have multiple -+ * nodes and it could have connection between each node with terminal. -+ * -+ * @node_rsrc_id: Physical node id -+ * @node_ctx_id: Logical node id, unique per graph -+ * @num_terms: Number of enabled terms in the node -+ * @profiles: bitmap settings on the node -+ * @terminals: terminal info on the node -+ * @num_frags: Number of fragments -+ */ -+struct graph_node { -+ uint8_t node_rsrc_id; -+ uint8_t node_ctx_id; -+ uint8_t num_terms; -+ struct node_profile profiles[MAX_GRAPH_NODE_PROFILES]; -+ struct node_ternimal terminals[MAX_GRAPH_TERMINALS]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct graph_link_ep - link endpoint description -+ * -+ * Link endpoint is used to describe the connection between different nodes. -+ * -+ * @node_ctx_id: Node ID as described in the list of nodes in the fgraph -+ * @term_id: Term ID as described in the list of terms in the fgraph -+ */ -+struct graph_link_ep { -+ uint8_t node_ctx_id; -+ uint8_t term_id; -+} __attribute__ ((packed)); -+ -+/** -+ * All local links (links between nodes within a subsystem) require this -+ * value to be set. -+ */ -+#define IPU_PSYS_FOREIGN_KEY_NONE UINT16_MAX -+/** None value of TOP pbk id if not used */ -+#define IPU_PSYS_LINK_PBK_ID_NONE UINT8_MAX -+/** None value of TOP pbk slot id if not used */ -+#define IPU_PSYS_LINK_PBK_SLOT_ID_NONE UINT8_MAX -+/** Static Offline */ -+#define IPU_PSYS_LINK_STREAMING_MODE_SOFF 0U -+ -+/** -+ * struct graph_link - graph link to connect between cbs -+ * -+ * The sink and source links are defined with terminal information. -+ * -+ * @ep_src: Source side of the link -+ * @ep_dst: Destination side of the link -+ * @foreign_key: MUST set to IPU_PSYS_FOREIGN_KEY_NONE -+ * @streaming_mode: Value should be set from IPU_PSYS_LINK_STREAMING_MODE_X -+ * @pbk_id: TOP PBK id that used to connected to external IP -+ * @pbk_slot_id: TOP PBK slot id that used to connected to external IP -+ * @delayed_link: A delay link between producer N and consumer N+1 frame -+ */ -+struct graph_link { -+ struct graph_link_ep ep_src; -+ struct graph_link_ep ep_dst; -+ uint16_t foreign_key; -+ uint8_t streaming_mode; -+ uint8_t pbk_id; -+ uint8_t pbk_slot_id; -+ uint8_t delayed_link; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_graph_info -+ * -+ * Topology that describes an IPU internal connection includes CB and terminal -+ * information. -+ * -+ * @graph_id: id of graph, set initial to 0xFF by user, returned by driver -+ * @num_nodes: number of nodes in graph -+ * @nodes: node entity -+ * @links: link entity -+ */ -+struct ipu_psys_graph_info { -+ uint8_t graph_id; -+ uint8_t num_nodes; -+ struct graph_node __user *nodes; -+ struct graph_link links[MAX_GRAPH_LINKS]; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_term_buffers -+ * -+ * Descprion of each terminal payload buffer -+ * -+ * @term_id: terminal id -+ * @term_buf: terminal buffer -+ */ -+struct ipu_psys_term_buffers { -+ uint8_t term_id; -+ struct ipu_psys_buffer term_buf; -+} __attribute__ ((packed)); -+ -+/** -+ * struct ipu_psys_task_request -+ * -+ * Task request is for user to send a request associated with terminal -+ * payload and expect IPU to process, each task request would expect -+ * an event, @see ipu_psys_event -+ * -+ * @graph_id: graph id returned from graph open -+ * @node_ctx_id: unique logical context id per cb -+ * @frame_id: frame id -+ * @payload_reuse_bm: Any terminal marked here must be enabled -+ * @term_buf_count: the number of terminal buffers -+ * @task_buffers: terminal buffers on the task request -+ * @num_frags: the number of fragments -+ * @frag_buffers: the buffer information of fragments -+ */ -+struct ipu_psys_task_request { -+ uint8_t graph_id; -+ uint8_t node_ctx_id; -+ uint8_t frame_id; -+ uint32_t payload_reuse_bm[2]; -+ uint8_t term_buf_count; -+ struct ipu_psys_term_buffers __user *task_buffers; -+} __attribute__ ((packed)); -+ -+#define IPU_BUFFER_FLAG_INPUT (1 << 0) -+#define IPU_BUFFER_FLAG_OUTPUT (1 << 1) -+#define IPU_BUFFER_FLAG_MAPPED (1 << 2) -+#define IPU_BUFFER_FLAG_NO_FLUSH (1 << 3) -+#define IPU_BUFFER_FLAG_DMA_HANDLE (1 << 4) -+#define IPU_BUFFER_FLAG_USERPTR (1 << 5) -+ -+#define IPU_IOC_QUERYCAP _IOR('A', 1, struct ipu_psys_capability) -+#define IPU_IOC_MAPBUF _IOWR('A', 2, int) -+#define IPU_IOC_UNMAPBUF _IOWR('A', 3, int) -+#define IPU_IOC_GETBUF _IOWR('A', 4, struct ipu_psys_buffer) -+#define IPU_IOC_PUTBUF _IOWR('A', 5, struct ipu_psys_buffer) -+#define IPU_IOC_DQEVENT _IOWR('A', 6, struct ipu_psys_event) -+#define IPU_IOC_GRAPH_OPEN _IOWR('A', 7, struct ipu_psys_graph_info) -+#define IPU_IOC_TASK_REQUEST _IOWR('A', 8, struct ipu_psys_task_request) -+#define IPU_IOC_GRAPH_CLOSE _IOWR('A', 9, int) -+ -+#endif /* _UAPI_IPU_PSYS_H */ --- -2.43.0 - diff --git a/SPECS/kernel/0001-mei-bus-fix-device-leak.security b/SPECS/kernel/0001-mei-bus-fix-device-leak.security new file mode 100644 index 000000000..9059d93d7 --- /dev/null +++ b/SPECS/kernel/0001-mei-bus-fix-device-leak.security @@ -0,0 +1,52 @@ +From d2f493bb12ab187899e8e2437e19b601ff34e529 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Sun, 11 May 2025 14:27:37 +0300 +Subject: [PATCH 1/8] mei: bus: fix device leak + +The bus rescan function creates bus devices for all clients. +The fixup routine is executed on all devices, unneeded +devices are removed and fully initialized once set +is_added flag to 1. + +If link to firmware is reset right after all devices are +initialized, but before fixup is executed, the rescan tries +to remove devices. +The is_added flag is not set the mei_cl_bus_dev_destroy +returns prematurely. +Allow to clean up device when is_added flag is unset to +account for above scenario. + +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/bus.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c +index 2c810ab12e620..abded7457894d 100644 +--- a/drivers/misc/mei/bus.c ++++ b/drivers/misc/mei/bus.c +@@ -1452,17 +1452,14 @@ static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) + */ + static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) + { +- + WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); + +- if (!cldev->is_added) +- return; +- +- device_del(&cldev->dev); ++ if (cldev->is_added) { ++ device_del(&cldev->dev); ++ cldev->is_added = 0; ++ } + + list_del_init(&cldev->bus_list); +- +- cldev->is_added = 0; + put_device(&cldev->dev); + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0001-mei-gsc-add-dependency-on-Xe-driver.rt b/SPECS/kernel/0001-mei-gsc-add-dependency-on-Xe-driver.rt deleted file mode 100644 index 533678538..000000000 --- a/SPECS/kernel/0001-mei-gsc-add-dependency-on-Xe-driver.rt +++ /dev/null @@ -1,33 +0,0 @@ -From 381ca3ef54389d673887c371b0bd7bf277106ba6 Mon Sep 17 00:00:00 2001 -From: Junxiao Chang -Date: Sun, 9 Nov 2025 17:35:33 +0200 -Subject: [PATCH 1/2] mei: gsc: add dependency on Xe driver - -INTEL_MEI_GSC depends on either i915 or Xe -and can be present when either of above is present. - -Cc: -Fixes: 87a4c85d3a3e ("drm/xe/gsc: add gsc device support") -Tested-by: Baoli Zhang -Signed-off-by: Junxiao Chang -Signed-off-by: Alexander Usyskin ---- - drivers/misc/mei/Kconfig | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig -index 7575fee96cc6..59025f0a0f57 100644 ---- a/drivers/misc/mei/Kconfig -+++ b/drivers/misc/mei/Kconfig -@@ -49,7 +49,7 @@ config INTEL_MEI_TXE - config INTEL_MEI_GSC - tristate "Intel MEI GSC embedded device" - depends on INTEL_MEI_ME -- depends on DRM_I915 -+ depends on DRM_I915 || DRM_XE - help - Intel auxiliary driver for GSC devices embedded in Intel graphics devices. - --- -2.43.0 - diff --git a/SPECS/kernel/0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet b/SPECS/kernel/0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet new file mode 100644 index 000000000..035553b4d --- /dev/null +++ b/SPECS/kernel/0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet @@ -0,0 +1,181 @@ +From 8bc3d99d56e4782d8249523be7ea84dc6c27b4c2 Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Wed, 9 Jun 2021 15:12:19 +0800 +Subject: [PATCH 01/18] net: pcs: xpcs: enable xpcs reset skipping + +Some platforms such as the Intel AlderLake configure the xPCS in the +BIOS and a xPCS Soft Reset is not required during driver init. + +This changes the xpcs_create function to take in an additional argument +to check if the platform request to skip xpcs reset during device +initialization. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/dsa/microchip/ksz9477.c | 2 +- + drivers/net/dsa/sja1105/sja1105_mdio.c | 2 +- + drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 4 +++- + drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c | 2 +- + drivers/net/pcs/pcs-xpcs.c | 14 +++++++------- + include/linux/pcs/pcs-xpcs.h | 4 ++-- + include/linux/stmmac.h | 1 + + 7 files changed, 16 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c +index 5df8f153d511b..451e173408564 100644 +--- a/drivers/net/dsa/microchip/ksz9477.c ++++ b/drivers/net/dsa/microchip/ksz9477.c +@@ -336,7 +336,7 @@ int ksz9477_pcs_create(struct ksz_device *dev) + if (ret) + return ret; + +- pcs = xpcs_create_pcs_mdiodev(bus, 0); ++ pcs = xpcs_create_pcs_mdiodev(bus, 0, false); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + p->pcs = pcs; +diff --git a/drivers/net/dsa/sja1105/sja1105_mdio.c b/drivers/net/dsa/sja1105/sja1105_mdio.c +index 8d535c033cef7..d751b2c6f6f9d 100644 +--- a/drivers/net/dsa/sja1105/sja1105_mdio.c ++++ b/drivers/net/dsa/sja1105/sja1105_mdio.c +@@ -409,7 +409,7 @@ static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv) + priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX) + continue; + +- pcs = xpcs_create_pcs_mdiodev(bus, port); ++ pcs = xpcs_create_pcs_mdiodev(bus, port, false); + if (IS_ERR(pcs)) { + rc = PTR_ERR(pcs); + goto out_pcs_free; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +index f408737f6fc73..117ce5da99792 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +@@ -429,8 +429,10 @@ int stmmac_pcs_setup(struct net_device *ndev) + struct fwnode_handle *devnode, *pcsnode; + struct dw_xpcs *xpcs = NULL; + int addr, ret; ++ bool skip_reset; + + devnode = priv->plat->port_node; ++ skip_reset = priv->plat->skip_reset; + + if (priv->plat->pcs_init) { + ret = priv->plat->pcs_init(priv); +@@ -442,7 +444,7 @@ int stmmac_pcs_setup(struct net_device *ndev) + } else if (priv->plat->mdio_bus_data && + priv->plat->mdio_bus_data->pcs_mask) { + addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1; +- xpcs = xpcs_create_mdiodev(priv->mii, addr); ++ xpcs = xpcs_create_mdiodev(priv->mii, addr, skip_reset); + ret = PTR_ERR_OR_ZERO(xpcs); + } else { + return 0; +diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +index 03f1b9bc604d5..d7e95106a45a9 100644 +--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c ++++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c +@@ -151,7 +151,7 @@ static int txgbe_mdio_pcs_init(struct txgbe *txgbe) + if (ret) + return ret; + +- pcs = xpcs_create_pcs_mdiodev(mii_bus, 0); ++ pcs = xpcs_create_pcs_mdiodev(mii_bus, 0, false); + if (IS_ERR(pcs)) + return PTR_ERR(pcs); + +diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c +index 3d1bd5aac0937..532d8da588b58 100644 +--- a/drivers/net/pcs/pcs-xpcs.c ++++ b/drivers/net/pcs/pcs-xpcs.c +@@ -1476,7 +1476,7 @@ static int xpcs_init_id(struct dw_xpcs *xpcs) + return xpcs_identify(xpcs); + } + +-static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) ++static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev, bool skip_reset) + { + struct dw_xpcs *xpcs; + int ret; +@@ -1495,7 +1495,7 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) + + xpcs_get_interfaces(xpcs, xpcs->pcs.supported_interfaces); + +- if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID) ++ if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID || skip_reset) + xpcs->pcs.poll = false; + else + xpcs->need_reset = true; +@@ -1520,7 +1520,7 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev) + * the PCS device couldn't be found on the bus and other negative errno related + * to the data allocation and MDIO-bus communications. + */ +-struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) ++struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, bool skip_reset) + { + struct mdio_device *mdiodev; + struct dw_xpcs *xpcs; +@@ -1529,7 +1529,7 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) + if (IS_ERR(mdiodev)) + return ERR_CAST(mdiodev); + +- xpcs = xpcs_create(mdiodev); ++ xpcs = xpcs_create(mdiodev, skip_reset); + + /* xpcs_create() has taken a refcount on the mdiodev if it was + * successful. If xpcs_create() fails, this will free the mdio +@@ -1543,11 +1543,11 @@ struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr) + } + EXPORT_SYMBOL_GPL(xpcs_create_mdiodev); + +-struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr) ++struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr, bool skip_reset) + { + struct dw_xpcs *xpcs; + +- xpcs = xpcs_create_mdiodev(bus, addr); ++ xpcs = xpcs_create_mdiodev(bus, addr, skip_reset); + if (IS_ERR(xpcs)) + return ERR_CAST(xpcs); + +@@ -1577,7 +1577,7 @@ struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode) + if (!mdiodev) + return ERR_PTR(-EPROBE_DEFER); + +- xpcs = xpcs_create(mdiodev); ++ xpcs = xpcs_create(mdiodev, false); + + /* xpcs_create() has taken a refcount on the mdiodev if it was + * successful. If xpcs_create() fails, this will free the mdio +diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h +index e40f554ff717a..f521032124927 100644 +--- a/include/linux/pcs/pcs-xpcs.h ++++ b/include/linux/pcs/pcs-xpcs.h +@@ -51,11 +51,11 @@ struct dw_xpcs; + struct phylink_pcs *xpcs_to_phylink_pcs(struct dw_xpcs *xpcs); + int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface); + void xpcs_config_eee_mult_fact(struct dw_xpcs *xpcs, u8 mult_fact); +-struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr); ++struct dw_xpcs *xpcs_create_mdiodev(struct mii_bus *bus, int addr, bool skip_reset); + struct dw_xpcs *xpcs_create_fwnode(struct fwnode_handle *fwnode); + void xpcs_destroy(struct dw_xpcs *xpcs); + +-struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr); ++struct phylink_pcs *xpcs_create_pcs_mdiodev(struct mii_bus *bus, int addr, bool skip_reset); + void xpcs_destroy_pcs(struct phylink_pcs *pcs); + + #endif /* __LINUX_PCS_XPCS_H */ +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index fa1318bac06c4..d7d6998fa5bd0 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -299,5 +299,6 @@ struct plat_stmmacenet_data { + int msi_tx_base_vec; + const struct dwmac4_addrs *dwmac4_addrs; + unsigned int flags; ++ bool skip_reset; + }; + #endif +-- +2.43.0 + diff --git a/SPECS/kernel/0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf b/SPECS/kernel/0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf new file mode 100644 index 000000000..03329f3ba --- /dev/null +++ b/SPECS/kernel/0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf @@ -0,0 +1,81 @@ +From ae02bb21a81488358e7fddc6e5ac9f201be722c7 Mon Sep 17 00:00:00 2001 +From: Zhang Rui +Date: Thu, 23 Oct 2025 15:37:53 -0700 +Subject: [PATCH 01/13] perf/x86/intel/cstate: Add Pantherlake support + +Like Lunarlake, Pantherlake supports CC1/CC6/CC7 and PC2/PC6/PC10. + +Signed-off-by: Zhang Rui +Signed-off-by: Peter Zijlstra (Intel) +Reviewed-by: Kan Liang +Reviewed-by: Dapeng Mi +Link: https://patch.msgid.link/20251023223754.1743928-4-zide.chen@intel.com +--- + arch/x86/events/intel/cstate.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c +index 6f5286a99e0c3..369b0d204ff07 100644 +--- a/arch/x86/events/intel/cstate.c ++++ b/arch/x86/events/intel/cstate.c +@@ -41,7 +41,7 @@ + * MSR_CORE_C1_RES: CORE C1 Residency Counter + * perf code: 0x00 + * Available model: SLM,AMT,GLM,CNL,ICX,TNT,ADL,RPL +- * MTL,SRF,GRR,ARL,LNL ++ * MTL,SRF,GRR,ARL,LNL,PTL + * Scope: Core (each processor core has a MSR) + * MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter + * perf code: 0x01 +@@ -53,18 +53,19 @@ + * Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW, + * SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX, + * TGL,TNT,RKL,ADL,RPL,SPR,MTL,SRF, +- * GRR,ARL,LNL ++ * GRR,ARL,LNL,PTL + * Scope: Core + * MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter + * perf code: 0x03 + * Available model: SNB,IVB,HSW,BDW,SKL,CNL,KBL,CML, +- * ICL,TGL,RKL,ADL,RPL,MTL,ARL,LNL ++ * ICL,TGL,RKL,ADL,RPL,MTL,ARL,LNL, ++ * PTL + * Scope: Core + * MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter. + * perf code: 0x00 + * Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL, + * KBL,CML,ICL,ICX,TGL,TNT,RKL,ADL, +- * RPL,SPR,MTL,ARL,LNL,SRF ++ * RPL,SPR,MTL,ARL,LNL,SRF,PTL + * Scope: Package (physical package) + * MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter. + * perf code: 0x01 +@@ -77,7 +78,7 @@ + * Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW, + * SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX, + * TGL,TNT,RKL,ADL,RPL,SPR,MTL,SRF, +- * ARL,LNL ++ * ARL,LNL,PTL + * Scope: Package (physical package) + * MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter. + * perf code: 0x03 +@@ -96,7 +97,7 @@ + * MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter. + * perf code: 0x06 + * Available model: HSW ULT,KBL,GLM,CNL,CML,ICL,TGL, +- * TNT,RKL,ADL,RPL,MTL,ARL,LNL ++ * TNT,RKL,ADL,RPL,MTL,ARL,LNL,PTL + * Scope: Package (physical package) + * MSR_MODULE_C6_RES_MS: Module C6 Residency Counter. + * perf code: 0x00 +@@ -651,6 +652,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = { + X86_MATCH_VFM(INTEL_ARROWLAKE_H, &adl_cstates), + X86_MATCH_VFM(INTEL_ARROWLAKE_U, &adl_cstates), + X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_cstates), ++ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &lnl_cstates), + { }, + }; + MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match); +-- +2.43.0 + diff --git a/SPECS/kernel/0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf b/SPECS/kernel/0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf deleted file mode 100644 index 960534c90..000000000 --- a/SPECS/kernel/0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf +++ /dev/null @@ -1,119 +0,0 @@ -From 71aa7fcebba9ce3bf185178c914873c382112b04 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Thu, 13 Feb 2025 13:24:53 -0800 -Subject: [PATCH 001/100] perf/x86/msr: Make SMI and PPERF on by default - -The MSRs, SMI_COUNT and PPERF, are model-specific MSRs. A very long -CPU ID list is maintained to indicate the supported platforms. With more -and more platforms being introduced, new CPU IDs have to be kept adding. -Also, the old kernel has to be updated to apply the new CPU ID. - -The MSRs have been introduced for a long time. There is no plan to -change them in the near future. Furthermore, the current code utilizes -rdmsr_safe() to check the availability of MSRs before using it. - -Make them on by default. It should be good enough to only rely on the -rdmsr_safe() to check their availability for both existing and future -platforms. - -Signed-off-by: Kan Liang ---- - arch/x86/events/msr.c | 80 ++----------------------------------------- - 1 file changed, 3 insertions(+), 77 deletions(-) - -diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c -index 7f5007a4752a..c6bb7a0f6251 100644 ---- a/arch/x86/events/msr.c -+++ b/arch/x86/events/msr.c -@@ -41,85 +41,11 @@ static bool test_therm_status(int idx, void *data) - - static bool test_intel(int idx, void *data) - { -- if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || -- boot_cpu_data.x86 != 6) -+ if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) - return false; - -- switch (boot_cpu_data.x86_vfm) { -- case INTEL_NEHALEM: -- case INTEL_NEHALEM_G: -- case INTEL_NEHALEM_EP: -- case INTEL_NEHALEM_EX: -- -- case INTEL_WESTMERE: -- case INTEL_WESTMERE_EP: -- case INTEL_WESTMERE_EX: -- -- case INTEL_SANDYBRIDGE: -- case INTEL_SANDYBRIDGE_X: -- -- case INTEL_IVYBRIDGE: -- case INTEL_IVYBRIDGE_X: -- -- case INTEL_HASWELL: -- case INTEL_HASWELL_X: -- case INTEL_HASWELL_L: -- case INTEL_HASWELL_G: -- -- case INTEL_BROADWELL: -- case INTEL_BROADWELL_D: -- case INTEL_BROADWELL_G: -- case INTEL_BROADWELL_X: -- case INTEL_SAPPHIRERAPIDS_X: -- case INTEL_EMERALDRAPIDS_X: -- case INTEL_GRANITERAPIDS_X: -- case INTEL_GRANITERAPIDS_D: -- -- case INTEL_ATOM_SILVERMONT: -- case INTEL_ATOM_SILVERMONT_D: -- case INTEL_ATOM_AIRMONT: -- -- case INTEL_ATOM_GOLDMONT: -- case INTEL_ATOM_GOLDMONT_D: -- case INTEL_ATOM_GOLDMONT_PLUS: -- case INTEL_ATOM_TREMONT_D: -- case INTEL_ATOM_TREMONT: -- case INTEL_ATOM_TREMONT_L: -- -- case INTEL_XEON_PHI_KNL: -- case INTEL_XEON_PHI_KNM: -- if (idx == PERF_MSR_SMI) -- return true; -- break; -- -- case INTEL_SKYLAKE_L: -- case INTEL_SKYLAKE: -- case INTEL_SKYLAKE_X: -- case INTEL_KABYLAKE_L: -- case INTEL_KABYLAKE: -- case INTEL_COMETLAKE_L: -- case INTEL_COMETLAKE: -- case INTEL_ICELAKE_L: -- case INTEL_ICELAKE: -- case INTEL_ICELAKE_X: -- case INTEL_ICELAKE_D: -- case INTEL_TIGERLAKE_L: -- case INTEL_TIGERLAKE: -- case INTEL_ROCKETLAKE: -- case INTEL_ALDERLAKE: -- case INTEL_ALDERLAKE_L: -- case INTEL_ATOM_GRACEMONT: -- case INTEL_RAPTORLAKE: -- case INTEL_RAPTORLAKE_P: -- case INTEL_RAPTORLAKE_S: -- case INTEL_METEORLAKE: -- case INTEL_METEORLAKE_L: -- if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF) -- return true; -- break; -- } -- -- return false; -+ /* Rely on perf_msr_probe() to check the availability */ -+ return true; - } - - PMU_EVENT_ATTR_STRING(tsc, attr_tsc, "event=0x00" ); --- -2.43.0 - diff --git a/SPECS/kernel/0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core b/SPECS/kernel/0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core deleted file mode 100644 index fa243f522..000000000 --- a/SPECS/kernel/0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core +++ /dev/null @@ -1,609 +0,0 @@ -From 884a526208947014c57524dad63cb6f3ff413760 Mon Sep 17 00:00:00 2001 -From: Xi Pardee -Date: Tue, 26 Aug 2025 11:39:43 -0700 -Subject: [PATCH 1/2] platform/x86/intel/pmc: Add Wildcat Lake support to - intel_pmc_core -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add Wildcat Lake support to intel_pmc_core driver. - -Signed-off-by: Xi Pardee -Link: https://lore.kernel.org/r/20250826183946.802684-2-xi.pardee@linux.intel.com -[ij: added #include for BIT()] -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - drivers/platform/x86/intel/pmc/Makefile | 2 +- - drivers/platform/x86/intel/pmc/core.c | 1 + - drivers/platform/x86/intel/pmc/core.h | 8 + - drivers/platform/x86/intel/pmc/ptl.c | 6 +- - drivers/platform/x86/intel/pmc/wcl.c | 486 ++++++++++++++++++++++++ - 5 files changed, 499 insertions(+), 4 deletions(-) - create mode 100644 drivers/platform/x86/intel/pmc/wcl.c - -diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile -index 5f68c8503a56..bb960c8721d7 100644 ---- a/drivers/platform/x86/intel/pmc/Makefile -+++ b/drivers/platform/x86/intel/pmc/Makefile -@@ -4,7 +4,7 @@ - # - - intel_pmc_core-y := core.o spt.o cnp.o icl.o \ -- tgl.o adl.o mtl.o arl.o lnl.o ptl.o -+ tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o - obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o - intel_pmc_core_pltdrv-y := pltdrv.o - obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o -diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c -index d040290e80ff..21ab0f0764d1 100644 ---- a/drivers/platform/x86/intel/pmc/core.c -+++ b/drivers/platform/x86/intel/pmc/core.c -@@ -1632,6 +1632,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { - X86_MATCH_VFM(INTEL_ARROWLAKE_U, &arl_h_pmc_dev), - X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev), - X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev), -+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev), - {} - }; - -diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h -index 4a94a4ee031e..9d54eef6c921 100644 ---- a/drivers/platform/x86/intel/pmc/core.h -+++ b/drivers/platform/x86/intel/pmc/core.h -@@ -298,6 +298,10 @@ enum ppfear_regs { - #define PTL_PMC_LTR_CUR_PLT 0x1C2C - #define PTL_PCD_PMC_MMIO_REG_LEN 0x31A8 - -+/* Wildcat Lake */ -+#define WCL_PMC_LTR_RESERVED 0x1B64 -+#define WCL_PCD_PMC_MMIO_REG_LEN 0x3178 -+ - /* SSRAM PMC Device ID */ - /* LNL */ - #define PMC_DEVID_LNL_SOCM 0xa87f -@@ -505,6 +509,9 @@ extern const struct pmc_bit_map mtl_socm_vnn_misc_status_map[]; - extern const struct pmc_bit_map mtl_socm_signal_status_map[]; - extern const struct pmc_reg_map mtl_socm_reg_map; - extern const struct pmc_reg_map mtl_ioep_reg_map; -+extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[]; -+extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[]; -+extern const struct pmc_bit_map ptl_pcdp_signal_status_map[]; - - void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); - int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); -@@ -528,6 +535,7 @@ extern struct pmc_dev_info arl_pmc_dev; - extern struct pmc_dev_info arl_h_pmc_dev; - extern struct pmc_dev_info lnl_pmc_dev; - extern struct pmc_dev_info ptl_pmc_dev; -+extern struct pmc_dev_info wcl_pmc_dev; - - void cnl_suspend(struct pmc_dev *pmcdev); - int cnl_resume(struct pmc_dev *pmcdev); -diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c -index 394515af60d6..1bbec9856867 100644 ---- a/drivers/platform/x86/intel/pmc/ptl.c -+++ b/drivers/platform/x86/intel/pmc/ptl.c -@@ -162,7 +162,7 @@ static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = { - {} - }; - --static const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = { -+const struct pmc_bit_map ptl_pcdp_clocksource_status_map[] = { - {"AON2_OFF_STS", BIT(0), 1}, - {"AON3_OFF_STS", BIT(1), 0}, - {"AON4_OFF_STS", BIT(2), 1}, -@@ -382,7 +382,7 @@ static const struct pmc_bit_map ptl_pcdp_vnn_req_status_2_map[] = { - {} - }; - --static const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = { -+const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[] = { - {"DTS0_VNN_REQ_STS", BIT(7), 0}, - {"GPIOCOM5_VNN_REQ_STS", BIT(11), 1}, - {} -@@ -421,7 +421,7 @@ static const struct pmc_bit_map ptl_pcdp_vnn_misc_status_map[] = { - {} - }; - --static const struct pmc_bit_map ptl_pcdp_signal_status_map[] = { -+const struct pmc_bit_map ptl_pcdp_signal_status_map[] = { - {"LSX_Wake0_STS", BIT(0), 0}, - {"LSX_Wake1_STS", BIT(1), 0}, - {"LSX_Wake2_STS", BIT(2), 0}, -diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c -new file mode 100644 -index 000000000000..85e90a639e65 ---- /dev/null -+++ b/drivers/platform/x86/intel/pmc/wcl.c -@@ -0,0 +1,486 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * This file contains platform specific structure definitions -+ * and init function used by Wildcat Lake PCH. -+ * -+ * Copyright (c) 2025, Intel Corporation. -+ */ -+ -+#include -+#include -+ -+#include "core.h" -+ -+static const struct pmc_bit_map wcl_pcdn_pfear_map[] = { -+ {"PMC_0", BIT(0)}, -+ {"FUSE_OSSE", BIT(1)}, -+ {"ESPISPI", BIT(2)}, -+ {"XHCI", BIT(3)}, -+ {"SPA", BIT(4)}, -+ {"RSVD", BIT(5)}, -+ {"MPFPW2", BIT(6)}, -+ {"GBE", BIT(7)}, -+ -+ {"SBR16B21", BIT(0)}, -+ {"SBR16B5", BIT(1)}, -+ {"SBR8B1", BIT(2)}, -+ {"SBR8B0", BIT(3)}, -+ {"P2SB0", BIT(4)}, -+ {"D2D_DISP_1", BIT(5)}, -+ {"LPSS", BIT(6)}, -+ {"LPC", BIT(7)}, -+ -+ {"SMB", BIT(0)}, -+ {"ISH", BIT(1)}, -+ {"DBG_SBR16B", BIT(2)}, -+ {"NPK_0", BIT(3)}, -+ {"D2D_NOC_1", BIT(4)}, -+ {"FIA_P", BIT(5)}, -+ {"FUSE", BIT(6)}, -+ {"DBG_PSF", BIT(7)}, -+ -+ {"DISP_PGA1", BIT(0)}, -+ {"XDCI", BIT(1)}, -+ {"EXI", BIT(2)}, -+ {"CSE", BIT(3)}, -+ {"KVMCC", BIT(4)}, -+ {"PMT", BIT(5)}, -+ {"CLINK", BIT(6)}, -+ {"PTIO", BIT(7)}, -+ -+ {"USBR0", BIT(0)}, -+ {"SBR16B22", BIT(1)}, -+ {"SMT1", BIT(2)}, -+ {"MPFPW1", BIT(3)}, -+ {"SMS2", BIT(4)}, -+ {"SMS1", BIT(5)}, -+ {"CSMERTC", BIT(6)}, -+ {"CSMEPSF", BIT(7)}, -+ -+ {"D2D_NOC_0", BIT(0)}, -+ {"ESE", BIT(1)}, -+ {"FIACPCB_P", BIT(2)}, -+ {"RSVD", BIT(3)}, -+ {"SBR8B2", BIT(4)}, -+ {"OSSE_SMT1", BIT(5)}, -+ {"D2D_DISP", BIT(6)}, -+ {"P2SB1", BIT(7)}, -+ -+ {"U3FPW1", BIT(0)}, -+ {"SBR16B3", BIT(1)}, -+ {"PSF4", BIT(2)}, -+ {"CNVI", BIT(3)}, -+ {"UFSX2", BIT(4)}, -+ {"ENDBG", BIT(5)}, -+ {"DBC", BIT(6)}, -+ {"SBRG", BIT(7)}, -+ -+ {"RSVD", BIT(0)}, -+ {"NPK1", BIT(1)}, -+ {"SBR16B7", BIT(2)}, -+ {"SBR16B4", BIT(3)}, -+ {"FIA_XG", BIT(4)}, -+ {"PSF6", BIT(5)}, -+ {"UFSPW1", BIT(6)}, -+ {"FIA_U", BIT(7)}, -+ -+ {"PSF8", BIT(0)}, -+ {"PSF0", BIT(1)}, -+ {"RSVD", BIT(2)}, -+ {"FIACPCB_U", BIT(3)}, -+ {"TAM", BIT(4)}, -+ {"SBR16B0", BIT(5)}, -+ {"TBTLSX", BIT(6)}, -+ {"THC0", BIT(7)}, -+ -+ {"THC1", BIT(0)}, -+ {"PMC_1", BIT(1)}, -+ {"FIACPCB_XG", BIT(2)}, -+ {"TCSS", BIT(3)}, -+ {"DISP_PGA", BIT(4)}, -+ {"SBR16B20", BIT(5)}, -+ {"SBR8B20", BIT(6)}, -+ {"DBG_SBR", BIT(7)}, -+ -+ {"SPC", BIT(0)}, -+ {"ACE_0", BIT(1)}, -+ {"ACE_1", BIT(2)}, -+ {"ACE_2", BIT(3)}, -+ {"ACE_3", BIT(4)}, -+ {"ACE_4", BIT(5)}, -+ {"ACE_5", BIT(6)}, -+ {"ACE_6", BIT(7)}, -+ -+ {"ACE_7", BIT(0)}, -+ {"ACE_8", BIT(1)}, -+ {"ACE_9", BIT(2)}, -+ {"ACE_10", BIT(3)}, -+ {"SBR16B2", BIT(4)}, -+ {"SBR8B4", BIT(5)}, -+ {"OSSE", BIT(6)}, -+ {"SBR16B1", BIT(7)}, -+ {} -+}; -+ -+static const struct pmc_bit_map *ext_wcl_pcdn_pfear_map[] = { -+ wcl_pcdn_pfear_map, -+ NULL -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_ltr_show_map[] = { -+ {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, -+ {"RSVD", WCL_PMC_LTR_RESERVED}, -+ {"SATA", CNP_PMC_LTR_SATA}, -+ {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, -+ {"XHCI", CNP_PMC_LTR_XHCI}, -+ {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, -+ {"ME", CNP_PMC_LTR_ME}, -+ {"SATA1", CNP_PMC_LTR_EVA}, -+ {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, -+ {"HD_AUDIO", CNP_PMC_LTR_AZ}, -+ {"CNV", CNP_PMC_LTR_CNV}, -+ {"LPSS", CNP_PMC_LTR_LPSS}, -+ {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, -+ {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, -+ {"SATA2", PTL_PMC_LTR_SATA2}, -+ {"ESPI", CNP_PMC_LTR_ESPI}, -+ {"SCC", CNP_PMC_LTR_SCC}, -+ {"ISH", CNP_PMC_LTR_ISH}, -+ {"UFSX2", CNP_PMC_LTR_UFSX2}, -+ {"EMMC", CNP_PMC_LTR_EMMC}, -+ {"WIGIG", ICL_PMC_LTR_WIGIG}, -+ {"THC0", TGL_PMC_LTR_THC0}, -+ {"THC1", TGL_PMC_LTR_THC1}, -+ {"SOUTHPORT_G", MTL_PMC_LTR_SPG}, -+ {"ESE", MTL_PMC_LTR_ESE}, -+ {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, -+ {"DMI3", ARL_PMC_LTR_DMI3}, -+ {"OSSE", LNL_PMC_LTR_OSSE}, -+ -+ /* Below two cannot be used for LTR_IGNORE */ -+ {"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT}, -+ {"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_power_gating_status_0_map[] = { -+ {"PMC_PGD0_PG_STS", BIT(0), 0}, -+ {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0}, -+ {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, -+ {"XHCI_PGD0_PG_STS", BIT(3), 1}, -+ {"SPA_PGD0_PG_STS", BIT(4), 1}, -+ {"RSVD_5", BIT(5), 0}, -+ {"MPFPW2_PGD0_PG_STS", BIT(6), 0}, -+ {"GBE_PGD0_PG_STS", BIT(7), 1}, -+ {"SBR16B21_PGD0_PG_STS", BIT(8), 0}, -+ {"SBR16B5_PGD0_PG_STS", BIT(9), 0}, -+ {"SBR8B1_PGD0_PG_STS", BIT(10), 0}, -+ {"SBR8B0_PGD0_PG_STS", BIT(11), 0}, -+ {"P2SB0_PG_STS", BIT(12), 1}, -+ {"D2D_DISP_PGD1_PG_STS", BIT(13), 0}, -+ {"LPSS_PGD0_PG_STS", BIT(14), 1}, -+ {"LPC_PGD0_PG_STS", BIT(15), 0}, -+ {"SMB_PGD0_PG_STS", BIT(16), 0}, -+ {"ISH_PGD0_PG_STS", BIT(17), 0}, -+ {"DBG_SBR16B_PGD0_PG_STS", BIT(18), 0}, -+ {"NPK_PGD0_PG_STS", BIT(19), 0}, -+ {"D2D_NOC_PGD1_PG_STS", BIT(20), 0}, -+ {"FIA_P_PGD0_PG_STS", BIT(21), 0}, -+ {"FUSE_PGD0_PG_STS", BIT(22), 0}, -+ {"DBG_PSF_PGD0_PG_STS", BIT(23), 0}, -+ {"DISP_PGA1_PGD0_PG_STS", BIT(24), 0}, -+ {"XDCI_PGD0_PG_STS", BIT(25), 1}, -+ {"EXI_PGD0_PG_STS", BIT(26), 0}, -+ {"CSE_PGD0_PG_STS", BIT(27), 1}, -+ {"KVMCC_PGD0_PG_STS", BIT(28), 1}, -+ {"PMT_PGD0_PG_STS", BIT(29), 1}, -+ {"CLINK_PGD0_PG_STS", BIT(30), 1}, -+ {"PTIO_PGD0_PG_STS", BIT(31), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_power_gating_status_1_map[] = { -+ {"USBR0_PGD0_PG_STS", BIT(0), 1}, -+ {"SBR16B22_PGD0_PG_STS", BIT(1), 0}, -+ {"SMT1_PGD0_PG_STS", BIT(2), 1}, -+ {"MPFPW1_PGD0_PG_STS", BIT(3), 0}, -+ {"SMS2_PGD0_PG_STS", BIT(4), 1}, -+ {"SMS1_PGD0_PG_STS", BIT(5), 1}, -+ {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, -+ {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, -+ {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, -+ {"ESE_PGD0_PG_STS", BIT(9), 1}, -+ {"FIACPCB_P_PGD0_PG_STS", BIT(10), 0}, -+ {"SBR8B2_PGD0_PG_STS", BIT(12), 0}, -+ {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1}, -+ {"D2D_DISP_PGD0_PG_STS", BIT(14), 0}, -+ {"P2SB1_PGD0_PG_STS", BIT(15), 1}, -+ {"U3FPW1_PGD0_PG_STS", BIT(16), 0}, -+ {"SBR16B3_PGD0_PG_STS", BIT(17), 0}, -+ {"PSF4_PGD0_PG_STS", BIT(18), 0}, -+ {"CNVI_PGD0_PG_STS", BIT(19), 0}, -+ {"UFSX2_PGD0_PG_STS", BIT(20), 1}, -+ {"ENDBG_PGD0_PG_STS", BIT(21), 0}, -+ {"DBC_PGD0_PG_STS", BIT(22), 0}, -+ {"SBRG_PGD0_PG_STS", BIT(23), 0}, -+ {"NPK_PGD1_PG_STS", BIT(25), 0}, -+ {"SBR16B7_PGD0_PG_STS", BIT(26), 0}, -+ {"SBR16B4_PGD0_PG_STS", BIT(27), 0}, -+ {"FIA_XG_PSF_PGD0_PG_STS", BIT(28), 0}, -+ {"PSF6_PGD0_PG_STS", BIT(29), 0}, -+ {"UFSPW1_PGD0_PG_STS", BIT(30), 0}, -+ {"FIA_U_PGD0_PG_STS", BIT(31), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_power_gating_status_2_map[] = { -+ {"PSF8_PGD0_PG_STS", BIT(0), 0}, -+ {"PSF0_PGD0_PG_STS", BIT(1), 0}, -+ {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, -+ {"TAM_PGD0_PG_STS", BIT(4), 1}, -+ {"SBR16B0_PGD0_PG_STS", BIT(5), 0}, -+ {"TBTLSX_PGD0_PG_STS", BIT(6), 1}, -+ {"THC0_PGD0_PG_STS", BIT(7), 1}, -+ {"THC1_PGD0_PG_STS", BIT(8), 1}, -+ {"PMC_PGD1_PG_STS", BIT(9), 0}, -+ {"FIACPCB_XG_PGD0_PG_STS", BIT(10), 0}, -+ {"TCSS_PGD0_PG_STS", BIT(11), 0}, -+ {"DISP_PGA_PGD0_PG_STS", BIT(12), 0}, -+ {"SBR8B4_PGD0_PG_STS", BIT(13), 0}, -+ {"SBR8B20_PGD0_PG_STS", BIT(14), 0}, -+ {"DBG_PGD0_PG_STS", BIT(15), 0}, -+ {"SPC_PGD0_PG_STS", BIT(16), 1}, -+ {"ACE_PGD0_PG_STS", BIT(17), 0}, -+ {"ACE_PGD1_PG_STS", BIT(18), 0}, -+ {"ACE_PGD2_PG_STS", BIT(19), 0}, -+ {"ACE_PGD3_PG_STS", BIT(20), 0}, -+ {"ACE_PGD4_PG_STS", BIT(21), 0}, -+ {"ACE_PGD5_PG_STS", BIT(22), 0}, -+ {"ACE_PGD6_PG_STS", BIT(23), 0}, -+ {"ACE_PGD7_PG_STS", BIT(24), 0}, -+ {"ACE_PGD8_PG_STS", BIT(25), 0}, -+ {"ACE_PGD9_PG_STS", BIT(26), 0}, -+ {"ACE_PGD10_PG_STS", BIT(27), 0}, -+ {"SBR16B2_PG_PGD0_PG_STS", BIT(28), 0}, -+ {"SBR16B20_PGD0_PG_STS", BIT(29), 0}, -+ {"OSSE_PGD0_PG_STS", BIT(30), 1}, -+ {"SBR16B1_PGD0_PG_STS", BIT(31), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_0_map[] = { -+ {"LPSS_D3_STS", BIT(3), 1}, -+ {"XDCI_D3_STS", BIT(4), 1}, -+ {"XHCI_D3_STS", BIT(5), 1}, -+ {"SPA_D3_STS", BIT(12), 0}, -+ {"SPC_D3_STS", BIT(14), 0}, -+ {"OSSE_D3_STS", BIT(15), 0}, -+ {"ESPISPI_D3_STS", BIT(18), 0}, -+ {"PSTH_D3_STS", BIT(21), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_1_map[] = { -+ {"OSSE_SMT1_D3_STS", BIT(16), 0}, -+ {"GBE_D3_STS", BIT(19), 0}, -+ {"ITSS_D3_STS", BIT(23), 0}, -+ {"CNVI_D3_STS", BIT(27), 0}, -+ {"UFSX2_D3_STS", BIT(28), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_2_map[] = { -+ {"CSMERTC_D3_STS", BIT(1), 0}, -+ {"ESE_D3_STS", BIT(2), 0}, -+ {"CSE_D3_STS", BIT(4), 0}, -+ {"KVMCC_D3_STS", BIT(5), 0}, -+ {"USBR0_D3_STS", BIT(6), 0}, -+ {"ISH_D3_STS", BIT(7), 0}, -+ {"SMT1_D3_STS", BIT(8), 0}, -+ {"SMT2_D3_STS", BIT(9), 0}, -+ {"SMT3_D3_STS", BIT(10), 0}, -+ {"CLINK_D3_STS", BIT(14), 0}, -+ {"PTIO_D3_STS", BIT(16), 0}, -+ {"PMT_D3_STS", BIT(17), 0}, -+ {"SMS1_D3_STS", BIT(18), 0}, -+ {"SMS2_D3_STS", BIT(19), 0}, -+ {"OSSE_SMT2_D3_STS", BIT(22), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_d3_status_3_map[] = { -+ {"THC0_D3_STS", BIT(14), 1}, -+ {"THC1_D3_STS", BIT(15), 1}, -+ {"OSSE_SMT3_D3_STS", BIT(16), 0}, -+ {"ACE_D3_STS", BIT(23), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_req_status_0_map[] = { -+ {"LPSS_VNN_REQ_STS", BIT(3), 1}, -+ {"OSSE_VNN_REQ_STS", BIT(15), 1}, -+ {"ESPISPI_VNN_REQ_STS", BIT(18), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_req_status_1_map[] = { -+ {"NPK_VNN_REQ_STS", BIT(4), 1}, -+ {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, -+ {"EXI_VNN_REQ_STS", BIT(9), 1}, -+ {"OSSE_SMT1_VNN_REQ_STS", BIT(16), 1}, -+ {"P2D_VNN_REQ_STS", BIT(18), 1}, -+ {"GBE_VNN_REQ_STS", BIT(19), 1}, -+ {"SMB_VNN_REQ_STS", BIT(25), 1}, -+ {"LPC_VNN_REQ_STS", BIT(26), 0}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_req_status_2_map[] = { -+ {"CSMERTC_VNN_REQ_STS", BIT(1), 1}, -+ {"ESE_VNN_REQ_STS", BIT(2), 1}, -+ {"CSE_VNN_REQ_STS", BIT(4), 1}, -+ {"ISH_VNN_REQ_STS", BIT(7), 1}, -+ {"SMT1_VNN_REQ_STS", BIT(8), 1}, -+ {"CLINK_VNN_REQ_STS", BIT(14), 1}, -+ {"SMS1_VNN_REQ_STS", BIT(18), 1}, -+ {"SMS2_VNN_REQ_STS", BIT(19), 1}, -+ {"GPIOCOM4_VNN_REQ_STS", BIT(20), 1}, -+ {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1}, -+ {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1}, -+ {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1}, -+ {"DISP_SHIM_VNN_REQ_STS", BIT(31), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_vnn_misc_status_map[] = { -+ {"CPU_C10_REQ_STS", BIT(0), 0}, -+ {"TS_OFF_REQ_STS", BIT(1), 0}, -+ {"PNDE_MET_REQ_STS", BIT(2), 1}, -+ {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, -+ {"VNN_SOC_REQ_STS", BIT(6), 1}, -+ {"ISH_VNNAON_REQ_STS", BIT(7), 0}, -+ {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1}, -+ {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1}, -+ {"PLT_GREATER_REQ_STS", BIT(11), 1}, -+ {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, -+ {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, -+ {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, -+ {"EA_REQ_STS", BIT(15), 0}, -+ {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0}, -+ {"BRK_EV_EN_REQ_STS", BIT(17), 0}, -+ {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, -+ {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, -+ {"ARC_IDLE_REQ_STS", BIT(21), 0}, -+ {"FIA_DEEP_PM_REQ_STS", BIT(23), 0}, -+ {"XDCI_ATTACHED_REQ_STS", BIT(24), 1}, -+ {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, -+ {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1}, -+ {"PRE_WAKE0_REQ_STS", BIT(27), 1}, -+ {"PRE_WAKE1_REQ_STS", BIT(28), 1}, -+ {"PRE_WAKE2_REQ_STS", BIT(29), 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map wcl_pcdn_rsc_status_map[] = { -+ {"Memory", 0, 1}, -+ {"PSF0", 0, 1}, -+ {"PSF6", 0, 1}, -+ {"PSF8", 0, 1}, -+ {"SAF_CFI_LINK", 0, 1}, -+ {"SB", 0, 1}, -+ {} -+}; -+ -+static const struct pmc_bit_map *wcl_pcdn_lpm_maps[] = { -+ ptl_pcdp_clocksource_status_map, -+ wcl_pcdn_power_gating_status_0_map, -+ wcl_pcdn_power_gating_status_1_map, -+ wcl_pcdn_power_gating_status_2_map, -+ wcl_pcdn_d3_status_0_map, -+ wcl_pcdn_d3_status_1_map, -+ wcl_pcdn_d3_status_2_map, -+ wcl_pcdn_d3_status_3_map, -+ wcl_pcdn_vnn_req_status_0_map, -+ wcl_pcdn_vnn_req_status_1_map, -+ wcl_pcdn_vnn_req_status_2_map, -+ ptl_pcdp_vnn_req_status_3_map, -+ wcl_pcdn_vnn_misc_status_map, -+ ptl_pcdp_signal_status_map, -+ NULL -+}; -+ -+static const struct pmc_bit_map *wcl_pcdn_blk_maps[] = { -+ wcl_pcdn_power_gating_status_0_map, -+ wcl_pcdn_power_gating_status_1_map, -+ wcl_pcdn_power_gating_status_2_map, -+ wcl_pcdn_rsc_status_map, -+ wcl_pcdn_vnn_req_status_0_map, -+ wcl_pcdn_vnn_req_status_1_map, -+ wcl_pcdn_vnn_req_status_2_map, -+ ptl_pcdp_vnn_req_status_3_map, -+ wcl_pcdn_d3_status_0_map, -+ wcl_pcdn_d3_status_1_map, -+ wcl_pcdn_d3_status_2_map, -+ wcl_pcdn_d3_status_3_map, -+ ptl_pcdp_clocksource_status_map, -+ wcl_pcdn_vnn_misc_status_map, -+ ptl_pcdp_signal_status_map, -+ NULL -+}; -+ -+static const struct pmc_reg_map wcl_pcdn_reg_map = { -+ .pfear_sts = ext_wcl_pcdn_pfear_map, -+ .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, -+ .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, -+ .ltr_show_sts = wcl_pcdn_ltr_show_map, -+ .msr_sts = msr_map, -+ .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, -+ .regmap_length = WCL_PCD_PMC_MMIO_REG_LEN, -+ .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, -+ .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, -+ .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, -+ .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, -+ .lpm_num_maps = PTL_LPM_NUM_MAPS, -+ .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, -+ .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, -+ .etr3_offset = ETR3_OFFSET, -+ .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, -+ .lpm_priority_offset = MTL_LPM_PRI_OFFSET, -+ .lpm_en_offset = MTL_LPM_EN_OFFSET, -+ .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, -+ .lpm_sts = wcl_pcdn_lpm_maps, -+ .lpm_status_offset = MTL_LPM_STATUS_OFFSET, -+ .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, -+ .s0ix_blocker_maps = wcl_pcdn_blk_maps, -+ .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, -+}; -+ -+#define WCL_NPU_PCI_DEV 0xfd3e -+ -+/* -+ * Set power state of select devices that do not have drivers to D3 -+ * so that they do not block Package C entry. -+ */ -+static void wcl_d3_fixup(void) -+{ -+ pmc_core_set_device_d3(WCL_NPU_PCI_DEV); -+} -+ -+static int wcl_resume(struct pmc_dev *pmcdev) -+{ -+ wcl_d3_fixup(); -+ return cnl_resume(pmcdev); -+} -+ -+static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) -+{ -+ wcl_d3_fixup(); -+ return generic_core_init(pmcdev, pmc_dev_info); -+} -+ -+struct pmc_dev_info wcl_pmc_dev = { -+ .map = &wcl_pcdn_reg_map, -+ .suspend = cnl_suspend, -+ .resume = wcl_resume, -+ .init = wcl_core_init, -+}; --- -2.43.0 - diff --git a/SPECS/kernel/0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt b/SPECS/kernel/0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt new file mode 100644 index 000000000..4d32a0766 --- /dev/null +++ b/SPECS/kernel/0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt @@ -0,0 +1,158 @@ +From 16ba458229f01daba619c55e6dc608f11f8f9298 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:30 -0700 +Subject: [PATCH 1/6] platform/x86:intel/pmc: Add support for multiple DMU + GUIDs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Enable support for multiple DMU GUIDs to accommodate Arrow +Lake H/U platforms. Arrow Lake U/H may have several GUIDs +pointing to a single telemetry region providing die C6 value +Add support to search for available GUIDs. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-3-xi.pardee@linux.intel.com +[ij: add include & reverse logic in a loop] +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/arl.c | 6 ++++-- + drivers/platform/x86/intel/pmc/core.c | 22 ++++++++++++++++++---- + drivers/platform/x86/intel/pmc/core.h | 6 +++--- + drivers/platform/x86/intel/pmc/mtl.c | 3 ++- + 4 files changed, 27 insertions(+), 10 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c +index 17ad87b392abe..cc05a168c3721 100644 +--- a/drivers/platform/x86/intel/pmc/arl.c ++++ b/drivers/platform/x86/intel/pmc/arl.c +@@ -720,9 +720,10 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ + return generic_core_init(pmcdev, pmc_dev_info); + } + ++static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info arl_pmc_dev = { + .pci_func = 0, +- .dmu_guid = ARL_PMT_DMU_GUID, ++ .dmu_guids = ARL_PMT_DMU_GUIDS, + .regmap_list = arl_pmc_info_list, + .map = &arl_socs_reg_map, + .sub_req_show = &pmc_core_substate_req_regs_fops, +@@ -732,9 +733,10 @@ struct pmc_dev_info arl_pmc_dev = { + .sub_req = pmc_core_pmt_get_lpm_req, + }; + ++static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info arl_h_pmc_dev = { + .pci_func = 2, +- .dmu_guid = ARL_PMT_DMU_GUID, ++ .dmu_guids = ARL_H_PMT_DMU_GUIDS, + .regmap_list = arl_pmc_info_list, + .map = &mtl_socm_reg_map, + .sub_req_show = &pmc_core_substate_req_regs_fops, +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index ac3d19ae8c56d..ca126a253f9d0 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -20,6 +20,7 @@ enum header_type { + #include + #include + #include ++#include + #include + #include + #include +@@ -1281,7 +1282,20 @@ int get_primary_reg_base(struct pmc *pmc) + return 0; + } + +-void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) ++static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, u32 *guids) ++{ ++ struct telem_endpoint *ep; ++ unsigned int i; ++ ++ for (i = 0; guids[i]; i++) { ++ ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); ++ if (!IS_ERR(ep)) ++ return ep; ++ } ++ return ERR_PTR(-ENODEV); ++} ++ ++void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) + { + struct telem_endpoint *ep; + struct pci_dev *pcidev; +@@ -1292,7 +1306,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid) + return; + } + +- ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); ++ ep = pmc_core_register_endpoint(pcidev, guids); + pci_dev_put(pcidev); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, +@@ -1689,8 +1703,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) + } + + pmc_core_get_low_power_modes(pmcdev); +- if (pmc_dev_info->dmu_guid) +- pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guid); ++ if (pmc_dev_info->dmu_guids) ++ pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids); + + if (ssram) { + ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info); +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index d6818bd34768e..83d6e2e833785 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -481,7 +481,7 @@ enum pmc_index { + /** + * struct pmc_dev_info - Structure to keep PMC device info + * @pci_func: Function number of the primary PMC +- * @dmu_guid: Die Management Unit GUID ++ * @dmu_guids: List of Die Management Unit GUID + * @regmap_list: Pointer to a list of pmc_info structure that could be + * available for the platform. When set, this field implies + * SSRAM support. +@@ -495,7 +495,7 @@ enum pmc_index { + */ + struct pmc_dev_info { + u8 pci_func; +- u32 dmu_guid; ++ u32 *dmu_guids; + struct pmc_info *regmap_list; + const struct pmc_reg_map *map; + const struct file_operations *sub_req_show; +@@ -532,7 +532,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); + int pmc_core_resume_common(struct pmc_dev *pmcdev); + int get_primary_reg_base(struct pmc *pmc); + void pmc_core_get_low_power_modes(struct pmc_dev *pmcdev); +-void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 guid); ++void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids); + void pmc_core_set_device_d3(unsigned int device); + + int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); +diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c +index 0b87e10f864e6..19470ca311cf7 100644 +--- a/drivers/platform/x86/intel/pmc/mtl.c ++++ b/drivers/platform/x86/intel/pmc/mtl.c +@@ -992,9 +992,10 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in + return generic_core_init(pmcdev, pmc_dev_info); + } + ++static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info mtl_pmc_dev = { + .pci_func = 2, +- .dmu_guid = MTL_PMT_DMU_GUID, ++ .dmu_guids = MTL_PMT_DMU_GUIDS, + .regmap_list = mtl_pmc_info_list, + .map = &mtl_socm_reg_map, + .sub_req_show = &pmc_core_substate_req_regs_fops, +-- +2.43.0 + diff --git a/SPECS/kernel/0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency b/SPECS/kernel/0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency deleted file mode 100644 index e72db8be8..000000000 --- a/SPECS/kernel/0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency +++ /dev/null @@ -1,38 +0,0 @@ -From a8b215172a0115f51ecbc6e4a536889d5aa65054 Mon Sep 17 00:00:00 2001 -From: Kuppuswamy Sathyanarayanan -Date: Wed, 22 Oct 2025 14:17:33 -0700 -Subject: [PATCH] platform/x86: intel-uncore-freq: Add additional client - processors -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add Intel uncore frequency driver support for Pantherlake, Wildcatlake -and Novalake processors. - -Signed-off-by: Kuppuswamy Sathyanarayanan -Link: https://patch.msgid.link/20251022211733.3565526-1-sathyanarayanan.kuppuswamy@linux.intel.com -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - .../platform/x86/intel/uncore-frequency/uncore-frequency.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c -index 2a6897035150..0dfc552b2802 100644 ---- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c -+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c -@@ -256,6 +256,10 @@ static const struct x86_cpu_id intel_uncore_cpu_ids[] = { - X86_MATCH_VFM(INTEL_ARROWLAKE, NULL), - X86_MATCH_VFM(INTEL_ARROWLAKE_H, NULL), - X86_MATCH_VFM(INTEL_LUNARLAKE_M, NULL), -+ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL), -+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL), -+ X86_MATCH_VFM(INTEL_NOVALAKE, NULL), -+ X86_MATCH_VFM(INTEL_NOVALAKE_L, NULL), - {} - }; - MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids); --- -2.43.0 - diff --git a/SPECS/kernel/0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt b/SPECS/kernel/0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt new file mode 100644 index 000000000..f2997a500 --- /dev/null +++ b/SPECS/kernel/0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt @@ -0,0 +1,41 @@ +From bb452b663c49326fb098a232162a79a15b4b4869 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Wed, 5 Nov 2025 13:50:14 -0800 +Subject: [PATCH] platform/x86/intel/vsec: Add support for Wildcat Lake +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add Wildcat Lake PMT telemetry support. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251105215020.1984036-1-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/vsec.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c +index f66f0ce8559b1..ecfc7703f2019 100644 +--- a/drivers/platform/x86/intel/vsec.c ++++ b/drivers/platform/x86/intel/vsec.c +@@ -765,6 +765,7 @@ static const struct intel_vsec_platform_info lnl_info = { + #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d + #define PCI_DEVICE_ID_INTEL_VSEC_LNL_M 0x647d + #define PCI_DEVICE_ID_INTEL_VSEC_PTL 0xb07d ++#define PCI_DEVICE_ID_INTEL_VSEC_WCL 0xfd7d + static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, +@@ -776,6 +777,7 @@ static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_LNL_M, &lnl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_PTL, &mtl_info) }, ++ { PCI_DEVICE_DATA(INTEL, VSEC_WCL, &mtl_info) }, + { } + }; + MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); +-- +2.43.0 + diff --git a/SPECS/kernel/0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio b/SPECS/kernel/0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio new file mode 100644 index 000000000..6039d60fa --- /dev/null +++ b/SPECS/kernel/0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio @@ -0,0 +1,94 @@ +From adce89d037cdf0dca931ea2ab3edaf9170778a70 Mon Sep 17 00:00:00 2001 +From: "Baoli.Zhang" +Date: Thu, 22 Jan 2026 18:07:15 +0800 +Subject: [PATCH] soundwire: fix bug in sdw_add_element_group_count found by + syzkaller + +In the original implementation, there is illegal memory access when i is +equal with num in the for-loop in sdw_add_element_group_count as below. + +for (i = 0; i <= num; i++) { + if (rate == group->rates[i] && lane == group->lanes[i]) + ... + +To fix it, we check if rate/lane have already been existed first. And then +add them if not. + +There is no further functional changes beside this error. + +Fixes: 9026118f20e2 ("soundwire: Add generic bandwidth allocation algorithm") +Reviewed-by: Bard Liao +Signed-off-by: Baoli.Zhang +--- + .../soundwire/generic_bandwidth_allocation.c | 47 +++++++++---------- + 1 file changed, 22 insertions(+), 25 deletions(-) + +diff --git a/drivers/soundwire/generic_bandwidth_allocation.c b/drivers/soundwire/generic_bandwidth_allocation.c +index c18f0c16f929..e52c536d663c 100644 +--- a/drivers/soundwire/generic_bandwidth_allocation.c ++++ b/drivers/soundwire/generic_bandwidth_allocation.c +@@ -296,39 +296,36 @@ static int sdw_add_element_group_count(struct sdw_group *group, + int num = group->count; + int i; + +- for (i = 0; i <= num; i++) { ++ for (i = 0; i < num; i++) { + if (rate == group->rates[i] && lane == group->lanes[i]) +- break; +- +- if (i != num) +- continue; +- +- if (group->count >= group->max_size) { +- unsigned int *rates; +- unsigned int *lanes; ++ return 0; ++ } + +- group->max_size += 1; +- rates = krealloc(group->rates, +- (sizeof(int) * group->max_size), +- GFP_KERNEL); +- if (!rates) +- return -ENOMEM; ++ if (group->count >= group->max_size) { ++ unsigned int *rates; ++ unsigned int *lanes; + +- group->rates = rates; ++ group->max_size += 1; ++ rates = krealloc(group->rates, ++ (sizeof(int) * group->max_size), ++ GFP_KERNEL); ++ if (!rates) ++ return -ENOMEM; + +- lanes = krealloc(group->lanes, +- (sizeof(int) * group->max_size), +- GFP_KERNEL); +- if (!lanes) +- return -ENOMEM; ++ group->rates = rates; + +- group->lanes = lanes; +- } ++ lanes = krealloc(group->lanes, ++ (sizeof(int) * group->max_size), ++ GFP_KERNEL); ++ if (!lanes) ++ return -ENOMEM; + +- group->rates[group->count] = rate; +- group->lanes[group->count++] = lane; ++ group->lanes = lanes; + } + ++ group->rates[group->count] = rate; ++ group->lanes[group->count++] = lane; ++ + return 0; + } + +-- +2.34.1 + diff --git a/SPECS/kernel/0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal b/SPECS/kernel/0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal deleted file mode 100644 index edd4332b3..000000000 --- a/SPECS/kernel/0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal +++ /dev/null @@ -1,34 +0,0 @@ -From 4ab127a1503cab71b6cbcecb50c96bd5d7802251 Mon Sep 17 00:00:00 2001 -From: Salah Triki -Date: Fri, 25 Jul 2025 06:07:01 +0100 -Subject: [PATCH 01/11] thermal: intel: int340x: Remove redundant - acpi_has_method() call - -acpi_evaluate_object() returns an error if the needed method does not -exist, so remove an unnecessary acpi_has_method() call preceding it. - -Signed-off-by: Salah Triki -Link: https://patch.msgid.link/aIMQ9RFciI8jmmAh@pc -[ rjw: Subject adjustment ] -Signed-off-by: Rafael J. Wysocki ---- - drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c -index cb149bcdd7d5..ce5d53be108b 100644 ---- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c -+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c -@@ -220,9 +220,6 @@ static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **ps - int i, result = 0; - struct psvt *psvts; - -- if (!acpi_has_method(handle, "PSVT")) -- return -ENODEV; -- - status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; --- -2.43.0 - diff --git a/SPECS/kernel/0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi b/SPECS/kernel/0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi deleted file mode 100644 index efbb39062..000000000 --- a/SPECS/kernel/0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi +++ /dev/null @@ -1,31 +0,0 @@ -From 1f0c8463542c88d50607c5724d7729d6a5c06ecf Mon Sep 17 00:00:00 2001 -From: Tao Yu -Date: Mon, 3 Nov 2025 09:13:14 +0000 -Subject: [PATCH] x86/fred: Revert "x86/fred: Enable FRED by default" - -This reverts commit 9bb180134e3af: due to Ubuntu VMs -unable to recover after system wakes up from S3 for -multiple iterations, this commit will be temporarily -reverted to avoid blocking release. - -Signed-off-by: Tao Yu ---- - arch/x86/kernel/cpu/common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c -index 54c123f3c305..135d3b315b63 100644 ---- a/arch/x86/kernel/cpu/common.c -+++ b/arch/x86/kernel/cpu/common.c -@@ -1717,7 +1717,7 @@ static void __init cpu_parse_early_param(void) - - /* Minimize the gap between FRED is available and available but disabled. */ - arglen = cmdline_find_option(boot_command_line, "fred", arg, sizeof(arg)); -- if (arglen == 3 && !strncmp(arg, "off", 3)) -+ if (arglen != 2 || strncmp(arg, "on", 2)) - setup_clear_cpu_cap(X86_FEATURE_FRED); - - arglen = cmdline_find_option(boot_command_line, "clearcpuid", arg, sizeof(arg)); --- -2.43.0 - diff --git a/SPECS/kernel/0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio b/SPECS/kernel/0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio new file mode 100644 index 000000000..4a7d769cd --- /dev/null +++ b/SPECS/kernel/0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio @@ -0,0 +1,75 @@ +From 8baf781b45472d10a67908aecdbcdc02fab5ae45 Mon Sep 17 00:00:00 2001 +From: Balamurugan C +Date: Tue, 18 Nov 2025 13:53:41 +0530 +Subject: [PATCH 2/2] ASoC: SOF: Intel: hda: Only check SSP MCLK mask in case + of IPC3 + +IPC4 is using the NHLT blob itself and sends the SSP blob from it +directly to the firmware, there is no need for the MCLK quirk +based on the SSP blob since the SSP blob is in use. + +At the same time reword the error, info and debug messages for clarity. + +Signed-off-by: Peter Ujfalusi +Signed-off-by: Balamurugan C +--- + sound/soc/sof/intel/hda.c | 36 ++++++++++++++++++++++-------------- + 1 file changed, 22 insertions(+), 14 deletions(-) + +diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c +index 52e86fa600778..8b43a0447d252 100644 +--- a/sound/soc/sof/intel/hda.c ++++ b/sound/soc/sof/intel/hda.c +@@ -1403,7 +1403,6 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) + mach->mach_params.i2s_link_mask) { + const struct sof_intel_dsp_desc *chip = get_chip_info(sdev->pdata); + int ssp_num; +- int mclk_mask; + + if (hweight_long(mach->mach_params.i2s_link_mask) > 1 && + !(mach->tplg_quirk_mask & SND_SOC_ACPI_TPLG_INTEL_SSP_MSB)) +@@ -1428,19 +1427,28 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev) + + sof_pdata->tplg_filename = tplg_filename; + +- mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ssp_num); +- +- if (mclk_mask < 0) { +- dev_err(sdev->dev, "Invalid MCLK configuration\n"); +- return NULL; +- } +- +- dev_dbg(sdev->dev, "MCLK mask %#x found in NHLT\n", mclk_mask); +- +- if (mclk_mask) { +- dev_info(sdev->dev, "Overriding topology with MCLK mask %#x from NHLT\n", mclk_mask); +- sdev->mclk_id_override = true; +- sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; ++ if (sof_pdata->ipc_type == SOF_IPC_TYPE_3) { ++ int mclk_mask = check_nhlt_ssp_mclk_mask(sdev, ++ ssp_num); ++ ++ if (mclk_mask < 0) { ++ dev_err(sdev->dev, ++ "Invalid MCLK configuration for SSP%d\n", ++ ssp_num); ++ return NULL; ++ } ++ ++ if (mclk_mask) { ++ sdev->mclk_id_override = true; ++ sdev->mclk_id_quirk = (mclk_mask & BIT(0)) ? 0 : 1; ++ dev_info(sdev->dev, ++ "SSP%d to use MCLK id %d (mask: %#x)\n", ++ ssp_num, sdev->mclk_id_quirk, mclk_mask); ++ } else { ++ dev_dbg(sdev->dev, ++ "MCLK mask is empty for SSP%d in NHLT\n", ++ ssp_num); ++ } + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security b/SPECS/kernel/0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security new file mode 100644 index 000000000..c579aa530 --- /dev/null +++ b/SPECS/kernel/0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security @@ -0,0 +1,344 @@ +From 7e9b607ccd9a4d4220d9f8054b80311f16f9f642 Mon Sep 17 00:00:00 2001 +From: Adam Pawlicki +Date: Thu, 13 Nov 2025 10:32:40 +0100 +Subject: [PATCH 2/2] Add updated TPR (TXT Protected Regions) support to + kernel. + +This commit applies a TPR-support patch from Tboot Live Image project. +Its intent is to make kernel aware of the TPR memory and to disable +it before proceeding with system boot so that kernel can establish its +own DMA protection. + +Signed-off-by: AdamX Pawlicki +Signed-off-by: Mateusz Mowka +--- + arch/x86/kernel/tboot.c | 109 +++++++++++++++++++++++++++++++++++- + drivers/iommu/intel/dmar.c | 8 +++ + drivers/iommu/intel/iommu.c | 7 ++- + include/acpi/actbl1.h | 86 ++++++++++++++++++++++++++++ + include/linux/tboot.h | 6 ++ + 5 files changed, 214 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c +index 46b8f1f16676e..04929aba6a5cc 100644 +--- a/arch/x86/kernel/tboot.c ++++ b/arch/x86/kernel/tboot.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -453,8 +454,14 @@ struct sha1_hash { + u8 hash[SHA1_SIZE]; + }; + ++struct heap_ext_data_elt { ++ u32 type; ++ u32 size; ++ u8 data[]; ++} __packed; ++ + struct sinit_mle_data { +- u32 version; /* currently 6 */ ++ u32 version; /* currently 9 */ + struct sha1_hash bios_acm_id; + u32 edx_senter_flags; + u64 mseg_valid; +@@ -469,8 +476,18 @@ struct sinit_mle_data { + u32 mdrs_off; + u32 num_vtd_dmars; + u32 vtd_dmars_off; ++ u32 proc_scrtm_status; /* version 8 or later only*/ ++ struct heap_ext_data_elt ext_data_elts[]; + } __packed; + ++#define HEAP_EXTDATA_TYPE_DTPR 14 ++ ++struct acpi_dtpr_serialize_req { ++ u64 sts : 1; ++ u64 ctrl : 1; ++ u64 unused : 62; ++}; ++ + struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tbl) + { + void *heap_base, *heap_ptr, *config; +@@ -514,3 +531,93 @@ struct acpi_table_header *tboot_get_dmar_table(struct acpi_table_header *dmar_tb + + return dmar_tbl; + } ++ ++struct acpi_table_dtpr *tboot_get_dtpr_table(void) ++{ ++ void *heap_base, *heap_ptr, *config; ++ struct sinit_mle_data *sinit_mle; ++ struct heap_ext_data_elt *elt; ++ u64 sinit_mle_size; ++ ++ if (!tboot_enabled()) ++ return NULL; ++ /* ++ * ACPI tables may not be DMA protected by tboot, so use DMAR copy ++ * SINIT saved in SinitMleData in TXT heap (which is DMA protected) ++ */ ++ ++ /* map config space in order to get heap addr */ ++ config = ioremap(TXT_PUB_CONFIG_REGS_BASE, NR_TXT_CONFIG_PAGES * ++ PAGE_SIZE); ++ if (!config) ++ return NULL; ++ ++ /* now map TXT heap */ ++ heap_base = ioremap(*(u64 *)(config + TXTCR_HEAP_BASE), ++ *(u64 *)(config + TXTCR_HEAP_SIZE)); ++ iounmap(config); ++ if (!heap_base) ++ return NULL; ++ ++ /* walk heap to SinitMleData */ ++ /* skip BiosData */ ++ heap_ptr = heap_base + *(u64 *)heap_base; ++ /* skip OsMleData */ ++ heap_ptr += *(u64 *)heap_ptr; ++ /* skip OsSinitData */ ++ heap_ptr += *(u64 *)heap_ptr; ++ /* now points to SinitMleDataSize; set to SinitMleData */ ++ sinit_mle_size = *(u64 *)heap_ptr; ++ heap_ptr += sizeof(u64); ++ ++ sinit_mle = (struct sinit_mle_data *)heap_ptr; ++ if (sinit_mle->version < 9) ++ return NULL; ++ ++ elt = sinit_mle->ext_data_elts; ++ while (elt->type != HEAP_EXTDATA_TYPE_DTPR) { ++ elt = (void *)elt + elt->size; ++ if ((u64)elt > (u64)sinit_mle + sinit_mle_size) ++ return NULL; ++ } ++ return (struct acpi_table_dtpr *)elt->data; ++} ++ ++static bool tboot_tpr_enabled; ++void tboot_parse_dtpr_table(void) ++{ ++ struct acpi_table_dtpr *dtpr; ++ struct acpi_dtpr_instance *tpr_inst; ++ u32 *instance_cnt; ++ u32 i, j; ++ ++ if (!tboot_enabled()) ++ return; ++ dtpr = tboot_get_dtpr_table(); ++ if (dtpr == NULL) ++ return; ++ tboot_tpr_enabled = 1; ++ instance_cnt = (u32 *)(dtpr + 1); ++ tpr_inst = (struct acpi_dtpr_instance *)(instance_cnt + 1); ++ for (i = 0; i < *instance_cnt; ++i) { ++ for (j = 0; j < tpr_inst->tpr_cnt; ++j) { ++ uint64_t *base = ioremap(tpr_inst->tpr_array[j].base, 16); ++ ++ if (base != NULL) { ++ pr_info("TPR instance %d, TPR %d:base %llx limit %llx\n", ++ i, j, readq(base), readq(base + 1)); ++ *base |= (1 << 4); ++ iounmap(base); ++ } ++ } ++ tpr_inst = (struct acpi_dtpr_instance *)((u8 *)tpr_inst ++ + sizeof(*tpr_inst) + j*sizeof(*(tpr_inst->tpr_array))); ++ } ++ if (tboot_tpr_enabled) ++ pr_debug("TPR protection detected, PMR will be disabled\n"); ++} ++ ++bool tboot_is_tpr_enabled(void) ++{ ++ return tboot_tpr_enabled; ++} +diff --git a/drivers/iommu/intel/dmar.c b/drivers/iommu/intel/dmar.c +index ec975c73cfe6c..9c6f93cf1b3f6 100644 +--- a/drivers/iommu/intel/dmar.c ++++ b/drivers/iommu/intel/dmar.c +@@ -635,6 +635,7 @@ static int __init + parse_dmar_table(void) + { + struct acpi_table_dmar *dmar; ++ struct acpi_table_dtpr *dtpr; + int drhd_count = 0; + int ret; + struct dmar_res_callback cb = { +@@ -670,6 +671,13 @@ parse_dmar_table(void) + return -EINVAL; + } + ++ dtpr = tboot_get_dtpr_table(); ++ if (dtpr) { ++ //TPR is enabled ++ //This will also tell not to establish IOMMU PMRs ++ tboot_parse_dtpr_table(); ++ } ++ + pr_info("Host address width %d\n", dmar->width + 1); + ret = dmar_walk_dmar_table(dmar, &cb); + if (ret == 0 && drhd_count == 0) +diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c +index e236c7ec221f4..5847fb589c892 100644 +--- a/drivers/iommu/intel/iommu.c ++++ b/drivers/iommu/intel/iommu.c +@@ -3017,6 +3017,11 @@ static __init int tboot_force_iommu(void) + if (!tboot_enabled()) + return 0; + ++ //If TPR is enabled we don't need to force IOMMU, ++ //TPR set by SINIT ACM will take care of DMA protection ++ if (tboot_is_tpr_enabled()) ++ return 0; ++ + if (no_iommu || dmar_disabled) + pr_warn("Forcing Intel-IOMMU to enabled\n"); + +@@ -3074,7 +3079,7 @@ int __init intel_iommu_init(void) + * calling SENTER, but the kernel is expected to reset/tear + * down the PMRs. + */ +- if (intel_iommu_tboot_noforce) { ++ if (intel_iommu_tboot_noforce || tboot_is_tpr_enabled()) { + for_each_iommu(iommu, drhd) + iommu_disable_protect_mem_regions(iommu); + } +diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h +index 7f35eb0e84586..c77b0890970c6 100644 +--- a/include/acpi/actbl1.h ++++ b/include/acpi/actbl1.h +@@ -47,6 +47,7 @@ + #define ACPI_SIG_HPET "HPET" /* High Precision Event Timer table */ + #define ACPI_SIG_IBFT "IBFT" /* iSCSI Boot Firmware Table */ + #define ACPI_SIG_MSCT "MSCT" /* Maximum System Characteristics Table */ ++#define ACPI_SIG_DTPR "DTPR" /* TXT DMA Protection Ranges reporting table */ + + #define ACPI_SIG_S3PT "S3PT" /* S3 Performance (sub)Table */ + #define ACPI_SIG_PCCS "PCC" /* PCC Shared Memory Region */ +@@ -1973,6 +1974,91 @@ struct acpi_ibft_target { + u16 reverse_chap_secret_offset; + }; + ++/******************************************************************************* ++ * ++ * DTPR - DMA TPR Reporting ++ * Version 1 ++ * ++ * Conforms to "Intel TXT DMA Protection Ranges", ++ * Version xxx, April 2021 ++ * ++ ******************************************************************************/ ++ ++struct acpi_table_dtpr { ++ struct acpi_table_header header; ++ u32 flags; // 36 ++}; ++ ++struct acpi_tpr_array { ++ u64 base; ++}; ++ ++struct acpi_dtpr_instance { ++ u32 flags; ++ u32 tpr_cnt; ++ struct acpi_tpr_array tpr_array[]; ++}; ++ ++/******************************************************************************* ++ * TPRn_BASE ++ * ++ * Specifies the start address of TPRn region. TPR region address and size must ++ * be with 1MB resolution. These bits are compared with the result of the ++ * TPRn_LIMIT[63:20] * applied to the incoming address, to determine if an ++ * access fall within the TPRn defined region. ++ ******************************************************************************/ ++struct acpi_dtprn_base_reg { ++ u64 reserved0 : 3; ++ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) ++ u64 enable : 1; // 0 == range enabled, 1 == range disabled ++ u64 reserved1 : 15; ++ // Minimal TPRn_Base resolution is 1MB. ++ // Applied to the incoming address, to determine if an access ++ // fall within the TPRn defined region. ++ // Width is determined by a bus width which can be obtained ++ // via CPUID function 0x80000008. ++ u64 tpr_base_rw : 44; ++ //u64 unused : 1; ++}; ++ ++/******************************************************************************* ++ * TPRn_LIMIT ++ * ++ * This register defines an isolated region of memory that can be enabled ++ * to prohibit certain system agents from accessing memory. When an agent ++ * sends a request upstream, whether snooped or not, a TPR prevents that ++ * transaction from changing the state of memory. ++ ******************************************************************************/ ++ ++struct acpi_dtprn_limit_reg { ++ u64 reserved0 : 3; ++ u64 rw : 1; // access: 1 == RO, 0 == RW (for TPR must be RW) ++ u64 enable : 1; // 0 == range enabled, 1 == range disabled ++ u64 reserved1 : 15; ++ // Minimal TPRn_Limit resolution is 1MB. ++ // These bits define TPR limit address. ++ // Width is determined by a bus width. ++ u64 tpr_limit_rw : 44; ++ //u64 unused : 1; ++}; ++ ++/******************************************************************************* ++ * SERIALIZE_REQUEST ++ * ++ * This register is used to request serialization of non-coherent DMA ++ * transactions. OS shall issue it before changing of TPR settings ++ * (base / size). ++ ******************************************************************************/ ++ ++struct acpi_tpr_serialize_request { ++ u64 sts : 1; // Status of serialization request (RO) ++ // 0 == register idle, 1 == serialization in progress ++ u64 ctrl : 1; // Control field to initiate serialization (RW) ++ // 0 == normal, 1 == initialize serialization ++ // (self-clear to allow multiple serialization requests) ++ u64 unused : 62; ++}; ++ + /* Reset to default packing */ + + #pragma pack() +diff --git a/include/linux/tboot.h b/include/linux/tboot.h +index d2279160ef39d..95f784b0bde0a 100644 +--- a/include/linux/tboot.h ++++ b/include/linux/tboot.h +@@ -126,6 +126,9 @@ extern void tboot_probe(void); + extern void tboot_shutdown(u32 shutdown_type); + extern struct acpi_table_header *tboot_get_dmar_table( + struct acpi_table_header *dmar_tbl); ++extern struct acpi_table_dtpr *tboot_get_dtpr_table(void); ++extern void tboot_parse_dtpr_table(void); ++extern bool tboot_is_tpr_enabled(void); + + #else + +@@ -135,6 +138,9 @@ extern struct acpi_table_header *tboot_get_dmar_table( + #define tboot_sleep(sleep_state, pm1a_control, pm1b_control) \ + do { } while (0) + #define tboot_get_dmar_table(dmar_tbl) (dmar_tbl) ++#define tboot_get_dtpr_table() 0 ++#define tboot_parse_dtpr_table() do { } while (0) ++#define tboot_is_tpr_enabled() 0 + + #endif /* !CONFIG_INTEL_TXT */ + +-- +2.43.0 + diff --git a/SPECS/kernel/0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac b/SPECS/kernel/0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac new file mode 100644 index 000000000..d797f85e3 --- /dev/null +++ b/SPECS/kernel/0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac @@ -0,0 +1,54 @@ +From 81409764b31a445a2e7981b938bbf5568633277b Mon Sep 17 00:00:00 2001 +From: Lili Li +Date: Tue, 11 Mar 2025 11:00:23 +0800 +Subject: [PATCH 2/3] EDAC/igen6: Add more Intel Panther Lake-H SoCs support + +Add more Intel Panther Lake-H SoC compute die IDs for EDAC support. + +Reviewed-by: Qiuxu Zhuo +Signed-off-by: Lili Li +--- + drivers/edac/igen6_edac.c | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 092397a154d99..3e9d0118e3958 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -276,6 +276,16 @@ static struct work_struct ecclog_work; + #define DID_PTL_H_SKU1 0xb000 + #define DID_PTL_H_SKU2 0xb001 + #define DID_PTL_H_SKU3 0xb002 ++#define DID_PTL_H_SKU4 0xb003 ++#define DID_PTL_H_SKU5 0xb004 ++#define DID_PTL_H_SKU6 0xb005 ++#define DID_PTL_H_SKU7 0xb008 ++#define DID_PTL_H_SKU8 0xb011 ++#define DID_PTL_H_SKU9 0xb014 ++#define DID_PTL_H_SKU10 0xb015 ++#define DID_PTL_H_SKU11 0xb028 ++#define DID_PTL_H_SKU12 0xb029 ++#define DID_PTL_H_SKU13 0xb02a + + /* Compute die IDs for Wildcat Lake with IBECC */ + #define DID_WCL_SKU1 0xfd00 +@@ -640,6 +650,16 @@ static struct pci_device_id igen6_pci_tbl[] = { + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU4), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU5), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU6), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU7), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU8), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU9), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU10), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU11), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), (kernel_ulong_t)&mtl_p_cfg }, ++ { PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), (kernel_ulong_t)&mtl_p_cfg }, + { PCI_VDEVICE(INTEL, DID_WCL_SKU1), (kernel_ulong_t)&wcl_cfg }, + { }, + }; +-- +2.43.0 + diff --git a/SPECS/kernel/0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac b/SPECS/kernel/0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac deleted file mode 100644 index 0d91f65b4..000000000 --- a/SPECS/kernel/0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac +++ /dev/null @@ -1,188 +0,0 @@ -From ae4d7c15450606f3121d104cdeffced1efb93122 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Tue, 22 Jul 2025 11:25:20 +0800 -Subject: [PATCH 02/13] EDAC/{skx_common,skx}: Use configuration data, not - global macros - -Use model-specific configuration data for the number of memory controllers -per socket, channels per memory controller, and DIMMs per channel as -intended, instead of relying on global macros for maximum values. - -No functional changes intended. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_base.c | 33 ++++++++++++++++++++------------- - drivers/edac/skx_common.c | 16 +++++++++------- - drivers/edac/skx_common.h | 1 + - 3 files changed, 30 insertions(+), 20 deletions(-) - -diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c -index 29897b21fb8e..078ddf95cc6e 100644 ---- a/drivers/edac/skx_base.c -+++ b/drivers/edac/skx_base.c -@@ -33,6 +33,15 @@ static unsigned int nvdimm_count; - #define MASK26 0x3FFFFFF /* Mask for 2^26 */ - #define MASK29 0x1FFFFFFF /* Mask for 2^29 */ - -+static struct res_config skx_cfg = { -+ .type = SKX, -+ .decs_did = 0x2016, -+ .busno_cfg_offset = 0xcc, -+ .ddr_imc_num = 2, -+ .ddr_chan_num = 3, -+ .ddr_dimm_num = 2, -+}; -+ - static struct skx_dev *get_skx_dev(struct pci_bus *bus, u8 idx) - { - struct skx_dev *d; -@@ -52,7 +61,7 @@ enum munittype { - - struct munit { - u16 did; -- u16 devfn[SKX_NUM_IMC]; -+ u16 devfn[2]; - u8 busidx; - u8 per_socket; - enum munittype mtype; -@@ -89,11 +98,11 @@ static int get_all_munits(const struct munit *m) - if (!pdev) - break; - ndev++; -- if (m->per_socket == SKX_NUM_IMC) { -- for (i = 0; i < SKX_NUM_IMC; i++) -+ if (m->per_socket == skx_cfg.ddr_imc_num) { -+ for (i = 0; i < skx_cfg.ddr_imc_num; i++) - if (m->devfn[i] == pdev->devfn) - break; -- if (i == SKX_NUM_IMC) -+ if (i == skx_cfg.ddr_imc_num) - goto fail; - } - d = get_skx_dev(pdev->bus, m->busidx); -@@ -157,12 +166,6 @@ static int get_all_munits(const struct munit *m) - return -ENODEV; - } - --static struct res_config skx_cfg = { -- .type = SKX, -- .decs_did = 0x2016, -- .busno_cfg_offset = 0xcc, --}; -- - static const struct x86_cpu_id skx_cpuids[] = { - X86_MATCH_VFM(INTEL_SKYLAKE_X, &skx_cfg), - { } -@@ -186,11 +189,11 @@ static int skx_get_dimm_config(struct mem_ctl_info *mci, struct res_config *cfg) - /* Only the mcmtr on the first channel is effective */ - pci_read_config_dword(imc->chan[0].cdev, 0x87c, &mcmtr); - -- for (i = 0; i < SKX_NUM_CHANNELS; i++) { -+ for (i = 0; i < cfg->ddr_chan_num; i++) { - ndimms = 0; - pci_read_config_dword(imc->chan[i].cdev, 0x8C, &amap); - pci_read_config_dword(imc->chan[i].cdev, 0x400, &mcddrtcfg); -- for (j = 0; j < SKX_NUM_DIMMS; j++) { -+ for (j = 0; j < cfg->ddr_dimm_num; j++) { - dimm = edac_get_dimm(mci, i, j, 0); - pci_read_config_dword(imc->chan[i].cdev, - 0x80 + 4 * j, &mtr); -@@ -620,6 +623,7 @@ static int __init skx_init(void) - return -ENODEV; - - cfg = (struct res_config *)id->driver_data; -+ skx_set_res_cfg(cfg); - - rc = skx_get_hi_lo(0x2034, off, &skx_tolm, &skx_tohm); - if (rc) -@@ -652,10 +656,13 @@ static int __init skx_init(void) - goto fail; - - edac_dbg(2, "src_id = %d\n", src_id); -- for (i = 0; i < SKX_NUM_IMC; i++) { -+ for (i = 0; i < cfg->ddr_imc_num; i++) { - d->imc[i].mc = mc++; - d->imc[i].lmc = i; - d->imc[i].src_id = src_id; -+ d->imc[i].num_channels = cfg->ddr_chan_num; -+ d->imc[i].num_dimms = cfg->ddr_dimm_num; -+ - rc = skx_register_mci(&d->imc[i], d->imc[i].chan[0].cdev, - "Skylake Socket", EDAC_MOD_STR, - skx_get_dimm_config, cfg); -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index 39c733dbc5b9..cc7d36cf7f3b 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -320,10 +320,10 @@ static int get_width(u32 mtr) - */ - int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) - { -+ int ndev = 0, imc_num = cfg->ddr_imc_num + cfg->hbm_imc_num; - struct pci_dev *pdev, *prev; - struct skx_dev *d; - u32 reg; -- int ndev = 0; - - prev = NULL; - for (;;) { -@@ -354,8 +354,10 @@ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) - d->seg = GET_BITFIELD(reg, 16, 23); - } - -- edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x\n", -- d->bus[0], d->bus[1], d->bus[2], d->bus[3]); -+ d->num_imc = imc_num; -+ -+ edac_dbg(2, "busses: 0x%x, 0x%x, 0x%x, 0x%x, imcs %d\n", -+ d->bus[0], d->bus[1], d->bus[2], d->bus[3], imc_num); - list_add_tail(&d->list, &dev_edac_list); - prev = pdev; - -@@ -541,10 +543,10 @@ int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev, - - /* Allocate a new MC control structure */ - layers[0].type = EDAC_MC_LAYER_CHANNEL; -- layers[0].size = NUM_CHANNELS; -+ layers[0].size = imc->num_channels; - layers[0].is_virt_csrow = false; - layers[1].type = EDAC_MC_LAYER_SLOT; -- layers[1].size = NUM_DIMMS; -+ layers[1].size = imc->num_dimms; - layers[1].is_virt_csrow = true; - mci = edac_mc_alloc(imc->mc, ARRAY_SIZE(layers), layers, - sizeof(struct skx_pvt)); -@@ -784,7 +786,7 @@ void skx_remove(void) - - list_for_each_entry_safe(d, tmp, &dev_edac_list, list) { - list_del(&d->list); -- for (i = 0; i < NUM_IMC; i++) { -+ for (i = 0; i < d->num_imc; i++) { - if (d->imc[i].mci) - skx_unregister_mci(&d->imc[i]); - -@@ -794,7 +796,7 @@ void skx_remove(void) - if (d->imc[i].mbase) - iounmap(d->imc[i].mbase); - -- for (j = 0; j < NUM_CHANNELS; j++) { -+ for (j = 0; j < d->imc[i].num_channels; j++) { - if (d->imc[i].chan[j].cdev) - pci_dev_put(d->imc[i].chan[j].cdev); - } -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index ec4966f7ea40..3f6007a97267 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -134,6 +134,7 @@ struct skx_dev { - struct pci_dev *uracu; /* for i10nm CPU */ - struct pci_dev *pcu_cr3; /* for HBM memory detection */ - u32 mcroute; -+ int num_imc; - /* - * Some server BIOS may hide certain memory controllers, and the - * EDAC driver skips those hidden memory controllers. However, the --- -2.43.0 - diff --git a/SPECS/kernel/0002-INT3472-Support-LT6911GXD.ipu b/SPECS/kernel/0002-INT3472-Support-LT6911GXD.ipu deleted file mode 100644 index 1a83fe435..000000000 --- a/SPECS/kernel/0002-INT3472-Support-LT6911GXD.ipu +++ /dev/null @@ -1,26 +0,0 @@ -From 801344b0e847b7915a9f133ddb08b824fee13658 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Wed, 1 Oct 2025 08:02:47 +0800 -Subject: [PATCH 02/11] INT3472: Support LT6911GXD - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/platform/x86/intel/int3472/discrete.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c -index bdfb8a800c54..eb389888a145 100644 ---- a/drivers/platform/x86/intel/int3472/discrete.c -+++ b/drivers/platform/x86/intel/int3472/discrete.c -@@ -436,7 +436,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) - return ret; - } - -- if (cldb.control_logic_type != 1) { -+ if (cldb.control_logic_type != 1 && cldb.control_logic_type != 5) { - dev_err(&pdev->dev, "Unsupported control logic type %u\n", - cldb.control_logic_type); - return -EINVAL; --- -2.43.0 - diff --git a/SPECS/kernel/0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi b/SPECS/kernel/0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi deleted file mode 100644 index a6d1105c8..000000000 --- a/SPECS/kernel/0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi +++ /dev/null @@ -1,218 +0,0 @@ -From 3ffaf7c418929b7b97e564a0cf78d5e74bce5a0a Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 15:51:20 -0700 -Subject: [PATCH 02/44] KVM: VMX: Add support for the secondary VM exit - controls - -Always load the secondary VM exit controls to prepare for FRED enabling. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Changes in v4: -* Fix clearing VM_EXIT_ACTIVATE_SECONDARY_CONTROLS (Chao Gao). -* Check VM exit/entry consistency based on the new macro from Sean - Christopherson. - -Change in v3: -* Do FRED controls consistency checks in the VM exit/entry consistency - check framework (Sean Christopherson). - -Change in v2: -* Always load the secondary VM exit controls (Sean Christopherson). ---- - arch/x86/include/asm/msr-index.h | 1 + - arch/x86/include/asm/vmx.h | 3 +++ - arch/x86/kvm/vmx/capabilities.h | 9 ++++++++- - arch/x86/kvm/vmx/vmcs.h | 1 + - arch/x86/kvm/vmx/vmx.c | 29 +++++++++++++++++++++++++++-- - arch/x86/kvm/vmx/vmx.h | 7 ++++++- - 6 files changed, 46 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 6b22abdc856a..b56d14b74b21 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -1240,6 +1240,7 @@ - #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 - #define MSR_IA32_VMX_VMFUNC 0x00000491 - #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 -+#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 - - /* Resctrl MSRs: */ - /* - Intel: */ -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index b92ff87e3560..5b53b8da4ce0 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -108,6 +108,7 @@ - #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 - #define VM_EXIT_LOAD_CET_STATE 0x10000000 - #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 -+#define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 - - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff - -@@ -263,6 +264,8 @@ enum vmcs_field { - SHARED_EPT_POINTER = 0x0000203C, - PID_POINTER_TABLE = 0x00002042, - PID_POINTER_TABLE_HIGH = 0x00002043, -+ SECONDARY_VM_EXIT_CONTROLS = 0x00002044, -+ SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, - GUEST_PHYSICAL_ADDRESS = 0x00002400, - GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, - VMCS_LINK_POINTER = 0x00002800, -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 79cd9d316ba4..422d2846980c 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -55,8 +55,9 @@ struct vmcs_config { - u32 cpu_based_exec_ctrl; - u32 cpu_based_2nd_exec_ctrl; - u64 cpu_based_3rd_exec_ctrl; -- u32 vmexit_ctrl; - u32 vmentry_ctrl; -+ u32 vmexit_ctrl; -+ u64 vmexit_2nd_ctrl; - u64 misc; - struct nested_vmx_msrs nested; - }; -@@ -147,6 +148,12 @@ static inline bool cpu_has_tertiary_exec_ctrls(void) - CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; - } - -+static inline bool cpu_has_secondary_vmexit_ctrls(void) -+{ -+ return vmcs_config.vmexit_ctrl & -+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; -+} -+ - static inline bool cpu_has_vmx_virtualize_apic_accesses(void) - { - return vmcs_config.cpu_based_2nd_exec_ctrl & -diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h -index b25625314658..ae152a9d1963 100644 ---- a/arch/x86/kvm/vmx/vmcs.h -+++ b/arch/x86/kvm/vmx/vmcs.h -@@ -47,6 +47,7 @@ struct vmcs_host_state { - struct vmcs_controls_shadow { - u32 vm_entry; - u32 vm_exit; -+ u64 secondary_vm_exit; - u32 pin; - u32 exec; - u32 secondary_exec; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index bec76fbd0b09..bad778acf9b5 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2598,8 +2598,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - u32 _cpu_based_exec_control = 0; - u32 _cpu_based_2nd_exec_control = 0; - u64 _cpu_based_3rd_exec_control = 0; -- u32 _vmexit_control = 0; - u32 _vmentry_control = 0; -+ u32 _vmexit_control = 0; -+ u64 _vmexit2_control = 0; - u64 basic_msr; - u64 misc_msr; - -@@ -2620,6 +2621,12 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - { VM_ENTRY_LOAD_CET_STATE, VM_EXIT_LOAD_CET_STATE }, - }; - -+ struct { -+ u32 entry_control; -+ u64 exit_control; -+ } const vmcs_entry_exit2_pairs[] = { -+ }; -+ - memset(vmcs_conf, 0, sizeof(*vmcs_conf)); - - if (adjust_vmx_controls(KVM_REQUIRED_VMX_CPU_BASED_VM_EXEC_CONTROL, -@@ -2706,10 +2713,19 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - &_vmentry_control)) - return -EIO; - -+ if (_vmexit_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) -+ _vmexit2_control = -+ adjust_vmx_controls64(KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS, -+ MSR_IA32_VMX_EXIT_CTLS2); -+ - if (vmx_check_entry_exit_pairs(vmcs_entry_exit_pairs, - _vmentry_control, _vmexit_control)) - return -EIO; - -+ if (vmx_check_entry_exit_pairs(vmcs_entry_exit2_pairs, -+ _vmentry_control, _vmexit2_control)) -+ return -EIO; -+ - /* - * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they - * can't be used due to an errata where VM Exit may incorrectly clear -@@ -2758,8 +2774,9 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control; - vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control; - vmcs_conf->cpu_based_3rd_exec_ctrl = _cpu_based_3rd_exec_control; -- vmcs_conf->vmexit_ctrl = _vmexit_control; - vmcs_conf->vmentry_ctrl = _vmentry_control; -+ vmcs_conf->vmexit_ctrl = _vmexit_control; -+ vmcs_conf->vmexit_2nd_ctrl = _vmexit2_control; - vmcs_conf->misc = misc_msr; - - #if IS_ENABLED(CONFIG_HYPERV) -@@ -4480,6 +4497,11 @@ static u32 vmx_get_initial_vmexit_ctrl(void) - VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL); - } - -+static u64 vmx_secondary_vmexit_ctrl(void) -+{ -+ return vmcs_config.vmexit_2nd_ctrl; -+} -+ - void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) - { - struct vcpu_vmx *vmx = to_vmx(vcpu); -@@ -4822,6 +4844,9 @@ static void init_vmcs(struct vcpu_vmx *vmx) - - vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl()); - -+ if (cpu_has_secondary_vmexit_ctrls()) -+ secondary_vm_exit_controls_set(vmx, vmx_secondary_vmexit_ctrl()); -+ - /* 22.2.1, 20.8.1 */ - vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl()); - -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index ef8fd345f1ae..47395cb38d88 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -512,7 +512,11 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_PT_CONCEAL_PIP | \ - VM_EXIT_CLEAR_IA32_RTIT_CTL | \ - VM_EXIT_LOAD_CET_STATE | \ -- VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL | \ -+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) -+ -+#define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) -+#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ - (PIN_BASED_EXT_INTR_MASK | \ -@@ -625,6 +629,7 @@ static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u## - } - BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) - BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) -+BUILD_CONTROLS_SHADOW(secondary_vm_exit, SECONDARY_VM_EXIT_CONTROLS, 64) - BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL, 32) - BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL, 32) - BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL, 32) --- -2.43.0 - diff --git a/SPECS/kernel/0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi b/SPECS/kernel/0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi new file mode 100644 index 000000000..4b7d28260 --- /dev/null +++ b/SPECS/kernel/0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi @@ -0,0 +1,106 @@ +From 9f046ab7d611686d27760591e399cb83da0d35e5 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Wed, 30 Aug 2023 23:55:54 -0700 +Subject: [PATCH 02/44] KVM: VMX: Initialize VM entry/exit FRED controls in + vmcs_config + +Setup VM entry/exit FRED controls in the global vmcs_config for proper +FRED VMCS fields management: + 1) load guest FRED state upon VM entry. + 2) save guest FRED state during VM exit. + 3) load host FRED state during VM exit. + +Also add FRED control consistency checks to the existing VM entry/exit +consistency check framework. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +Reviewed-by: Chao Gao +--- + +Change in v5: +* Remove the pair VM_ENTRY_LOAD_IA32_FRED/VM_EXIT_ACTIVATE_SECONDARY_CONTROLS, + since the secondary VM exit controls are unconditionally enabled anyway, and + there are features other than FRED needing it (Chao Gao). +* Add TB from Xuelian Guo. + +Change in v4: +* Do VM exit/entry consistency checks using the new macro from Sean + Christopherson. + +Changes in v3: +* Add FRED control consistency checks to the existing VM entry/exit + consistency check framework (Sean Christopherson). +* Just do the unnecessary FRED state load/store on every VM entry/exit + (Sean Christopherson). +--- + arch/x86/include/asm/vmx.h | 4 ++++ + arch/x86/kvm/vmx/vmx.c | 2 ++ + arch/x86/kvm/vmx/vmx.h | 7 +++++-- + 3 files changed, 11 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index 1f60c04d11fbd..dd79d027ea700 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -109,6 +109,9 @@ + #define VM_EXIT_LOAD_CET_STATE 0x10000000 + #define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 + ++#define SECONDARY_VM_EXIT_SAVE_IA32_FRED BIT_ULL(0) ++#define SECONDARY_VM_EXIT_LOAD_IA32_FRED BIT_ULL(1) ++ + #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff + + #define VM_ENTRY_LOAD_DEBUG_CONTROLS 0x00000004 +@@ -122,6 +125,7 @@ + #define VM_ENTRY_PT_CONCEAL_PIP 0x00020000 + #define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 + #define VM_ENTRY_LOAD_CET_STATE 0x00100000 ++#define VM_ENTRY_LOAD_IA32_FRED 0x00800000 + + #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index f27d29c836298..fbeb85c4673e5 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -2622,6 +2622,8 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, + u32 entry_control; + u64 exit_control; + } const vmcs_entry_exit2_pairs[] = { ++ { VM_ENTRY_LOAD_IA32_FRED, ++ SECONDARY_VM_EXIT_SAVE_IA32_FRED | SECONDARY_VM_EXIT_LOAD_IA32_FRED }, + }; + + memset(vmcs_conf, 0, sizeof(*vmcs_conf)); +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index b2724aab48d2b..2cf599211ab30 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -488,7 +488,8 @@ static inline u8 vmx_get_rvi(void) + VM_ENTRY_LOAD_BNDCFGS | \ + VM_ENTRY_PT_CONCEAL_PIP | \ + VM_ENTRY_LOAD_IA32_RTIT_CTL | \ +- VM_ENTRY_LOAD_CET_STATE) ++ VM_ENTRY_LOAD_CET_STATE | \ ++ VM_ENTRY_LOAD_IA32_FRED) + + #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS \ + (VM_EXIT_SAVE_DEBUG_CONTROLS | \ +@@ -515,7 +516,9 @@ static inline u8 vmx_get_rvi(void) + VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) + + #define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) +-#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) ++#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS \ ++ (SECONDARY_VM_EXIT_SAVE_IA32_FRED | \ ++ SECONDARY_VM_EXIT_LOAD_IA32_FRED) + + #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ + (PIN_BASED_EXT_INTR_MASK | \ +-- +2.43.0 + diff --git a/SPECS/kernel/0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet b/SPECS/kernel/0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet deleted file mode 100644 index 6013f9686..000000000 --- a/SPECS/kernel/0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet +++ /dev/null @@ -1,76 +0,0 @@ -From 5e1b71248258e5e6a04bb3311d5f9a54afa79882 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 29 Jul 2025 11:13:48 -0700 -Subject: [PATCH 02/24] KVM: x86: Use double-underscore read/write MSR helpers - as appropriate - -Use the double-underscore helpers for emulating MSR reads and writes in -he no-underscore versions to better capture the relationship between the -two sets of APIs (the double-underscore versions don't honor userspace MSR -filters). - -No functional change intended. - -Signed-off-by: Sean Christopherson -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 29 ++++++++++++++++------------- - 1 file changed, 16 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 00e8501f88c8..9e78753b36a4 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1936,11 +1936,24 @@ static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu, - __kvm_get_msr); - } - -+int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+{ -+ return kvm_get_msr_ignored_check(vcpu, index, data, false); -+} -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_read); -+ -+int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) -+{ -+ return kvm_set_msr_ignored_check(vcpu, index, data, false); -+} -+EXPORT_SYMBOL_GPL(__kvm_emulate_msr_write); -+ - int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_READ)) - return KVM_MSR_RET_FILTERED; -- return kvm_get_msr_ignored_check(vcpu, index, data, false); -+ -+ return __kvm_emulate_msr_read(vcpu, index, data); - } - EXPORT_SYMBOL_GPL(kvm_emulate_msr_read); - -@@ -1948,21 +1961,11 @@ int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) - { - if (!kvm_msr_allowed(vcpu, index, KVM_MSR_FILTER_WRITE)) - return KVM_MSR_RET_FILTERED; -- return kvm_set_msr_ignored_check(vcpu, index, data, false); --} --EXPORT_SYMBOL_GPL(kvm_emulate_msr_write); - --int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) --{ -- return kvm_get_msr_ignored_check(vcpu, index, data, false); -+ return __kvm_emulate_msr_write(vcpu, index, data); - } --EXPORT_SYMBOL_GPL(__kvm_emulate_msr_read); -+EXPORT_SYMBOL_GPL(kvm_emulate_msr_write); - --int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) --{ -- return kvm_set_msr_ignored_check(vcpu, index, data, false); --} --EXPORT_SYMBOL_GPL(__kvm_emulate_msr_write); - - static void complete_userspace_rdmsr(struct kvm_vcpu *vcpu) - { --- -2.43.0 - diff --git a/SPECS/kernel/0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss b/SPECS/kernel/0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss deleted file mode 100644 index 4cb902e48..000000000 --- a/SPECS/kernel/0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss +++ /dev/null @@ -1,55 +0,0 @@ -From e3a43a95d50ac13d0f2bdca89f48f0ccab0a1ff1 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Mon, 21 Nov 2022 14:01:14 +0200 -Subject: [PATCH 02/16] PCI/portdrv: Do not require an interrupt for all AER - capable ports - -Only Root Ports and Event Collectors use MSI for AER. PCIe Switch ports -or endpoints on the other hand only send messages (that get collected by -the former). For this reason do not require PCIe switch ports and -endpoints to use interrupt if they support AER. - -This allows portdrv to attach PCIe switch ports of Intel DG1 and DG2 -discrete graphics cards. These do not declare MSI or legacy interrupts. - -Signed-off-by: Mika Westerberg ---- - drivers/pci/pcie/portdrv.c | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) - -diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c -index d1b68c18444f..cabf6261779b 100644 ---- a/drivers/pci/pcie/portdrv.c -+++ b/drivers/pci/pcie/portdrv.c -@@ -176,7 +176,7 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) - */ - static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - { -- int ret, i; -+ int ret, i, type; - - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) - irqs[i] = -1; -@@ -189,6 +189,19 @@ static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask) - if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) - goto intx_irq; - -+ /* -+ * Only root ports and event collectors use MSI for errors. Endpoints, -+ * switch ports send messages to them but don't use MSI for that (PCIe -+ * 5.0 sec 6.2.3.2). -+ */ -+ type = pci_pcie_type(dev); -+ if ((mask & PCIE_PORT_SERVICE_AER) && -+ type != PCI_EXP_TYPE_ROOT_PORT && type != PCI_EXP_TYPE_RC_EC) -+ mask &= ~PCIE_PORT_SERVICE_AER; -+ -+ if (!mask) -+ return 0; -+ - /* Try to use MSI-X or MSI if supported */ - if (pcie_port_enable_irq_vec(dev, irqs, mask) == 0) - return 0; --- -2.43.0 - diff --git a/SPECS/kernel/0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu b/SPECS/kernel/0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu deleted file mode 100644 index ca0386962..000000000 --- a/SPECS/kernel/0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu +++ /dev/null @@ -1,876 +0,0 @@ -From f753404ff8871c4779b3b9a6b807c8c08b910b3a Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Tue, 4 Nov 2025 14:28:40 +0800 -Subject: [PATCH 2/2] Revert "media: i2c: max9x: fix S3/S4 error for max9x" - -This reverts commit 34b0b6fcd8a20612950a7a8a7d1b252c43313119. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/isx031.c | 70 +++++------- - drivers/media/i2c/max9x/Makefile | 2 +- - drivers/media/i2c/max9x/max9295.c | 39 +++---- - drivers/media/i2c/max9x/max9296.h | 2 +- - drivers/media/i2c/max9x/max96724.c | 7 +- - drivers/media/i2c/max9x/regmap-retry.c | 52 --------- - drivers/media/i2c/max9x/regmap-retry.h | 18 --- - drivers/media/i2c/max9x/serdes.c | 148 +++++++++++++++++-------- - 8 files changed, 150 insertions(+), 188 deletions(-) - delete mode 100644 drivers/media/i2c/max9x/regmap-retry.c - delete mode 100644 drivers/media/i2c/max9x/regmap-retry.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index e613cdb06b83..ddc3e512efa4 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -33,8 +33,6 @@ - #define ISX031_MODE_4LANES_30FPS 0x17 - #define ISX031_MODE_2LANES_30FPS 0x18 - --static DEFINE_MUTEX(isx031_mutex); -- - struct isx031_reg { - enum { - ISX031_REG_LEN_DELAY = 0, -@@ -97,7 +95,7 @@ struct isx031 { - u8 lanes; - - /* To serialize asynchronus callbacks */ -- struct mutex *mutex; -+ struct mutex mutex; - - /* i2c client */ - struct i2c_client *client; -@@ -308,21 +306,6 @@ static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) - return 0; - } - --static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) --{ -- int ret; -- int retry = 100; -- -- while (retry--) { -- ret = isx031_write_reg(isx031, reg, len, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- - static int isx031_write_reg_list(struct isx031 *isx031, - const struct isx031_reg_list *r_list) - { -@@ -335,7 +318,7 @@ static int isx031_write_reg_list(struct isx031 *isx031, - msleep(r_list->regs[i].val); - continue; - } -- ret = isx031_write_reg_retry(isx031, r_list->regs[i].address, -+ ret = isx031_write_reg(isx031, r_list->regs[i].address, - ISX031_REG_LEN_08BIT, - r_list->regs[i].val); - if (ret) { -@@ -370,7 +353,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); - return ret; - } - -@@ -523,12 +506,13 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - if (isx031->streaming == enable) - return 0; - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); -- goto err_unlock; -+ mutex_unlock(&isx031->mutex); -+ return ret; - } - - ret = isx031_start_streaming(isx031); -@@ -544,8 +528,7 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - - isx031->streaming = enable; - --err_unlock: -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return ret; - } -@@ -570,11 +553,11 @@ static int __maybe_unused isx031_suspend(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - if (isx031->streaming) - isx031_stop_streaming(isx031); - -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -585,9 +568,7 @@ static int __maybe_unused isx031_resume(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - const struct isx031_reg_list *reg_list; -- int ret = 0; -- -- mutex_lock(isx031->mutex); -+ int ret; - - if (isx031->reset_gpio != NULL) - isx031_reset(isx031->reset_gpio); -@@ -598,26 +579,27 @@ static int __maybe_unused isx031_resume(struct device *dev) - ret = isx031_write_reg_list(isx031, reg_list); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); -- goto err_unlock; -+ return ret; - } - } else { - dev_err(&client->dev, "isx031 resume failed"); -- goto err_unlock; -+ return ret; - } - -+ mutex_lock(&isx031->mutex); - if (isx031->streaming) { - ret = isx031_start_streaming(isx031); - if (ret) { - isx031->streaming = false; - isx031_stop_streaming(isx031); -- goto err_unlock; -+ mutex_unlock(&isx031->mutex); -+ return ret; - } - } - --err_unlock: -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - -- return ret; -+ return 0; - } - - static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -@@ -656,7 +638,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - if (i >= ARRAY_SIZE(supported_modes)) - mode = &supported_modes[0]; - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -@@ -665,7 +647,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - isx031->cur_mode = mode; - } - -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -676,14 +658,14 @@ static int isx031_get_format(struct v4l2_subdev *sd, - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - isx031_update_pad_format(isx031->cur_mode, &fmt->format); - -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -692,10 +674,10 @@ static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(isx031->mutex); -+ mutex_lock(&isx031->mutex); - isx031_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); -- mutex_unlock(isx031->mutex); -+ mutex_unlock(&isx031->mutex); - - return 0; - } -@@ -728,10 +710,12 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { - static void isx031_remove(struct i2c_client *client) - { - struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct isx031 *isx031 = to_isx031(sd); - - v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); - - } - -@@ -792,7 +776,8 @@ static int isx031_probe(struct i2c_client *client) - if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - -- isx031->mutex = &isx031_mutex; -+ mutex_init(&isx031->mutex); -+ - /* 1920x1536 default */ - isx031->cur_mode = NULL; - isx031->pre_mode = &supported_modes[0]; -@@ -823,6 +808,7 @@ static int isx031_probe(struct i2c_client *client) - probe_error_media_entity_cleanup: - media_entity_cleanup(&isx031->sd.entity); - pm_runtime_disable(&client->dev); -+ mutex_destroy(&isx031->mutex); - - return ret; - } -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -index ec275fe74e94..ab533b790f72 100644 ---- a/drivers/media/i2c/max9x/Makefile -+++ b/drivers/media/i2c/max9x/Makefile -@@ -7,4 +7,4 @@ ccflags-y += -I$(src)/../../pci/intel/ipu6/ - endif - - obj-m += max9x.o --max9x-y += regmap-retry.o serdes.o max9296.o max96724.o max9295.o max96717.o -+max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index a5bacc3684a9..cc1ee2f5ff92 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -30,7 +30,6 @@ - #include - - #include "max9295.h" --#include "regmap-retry.h" - - static const char *const max9295_gpio_chip_names[] = { - "MFP1", -@@ -262,14 +261,14 @@ static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, - struct device *dev = common->dev; - struct regmap *map = common->map; - int data_type_slot, dt; -- int ret = 0; -+ int ret; - - for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { - dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; - dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", - pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); - -- TRY(ret, regmap_update_bits_retry(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), - MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -@@ -511,35 +510,35 @@ static int max9295_enable_rclk(struct max9x_common *common) - dev_info(dev, "Enable RCLK: 27MHz"); - - // Configure pre-defined 27MHz frequency (0b01 = 27MHz) -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_PREDEF_FREQ_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_PREDEF_FREQ_FIELD, 1U)) - ); - - // Enable reference generation -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG0, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG0, - MAX9295_REFGEN_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_REFGEN_EN_FIELD, 1U)) - ); - - // Configure reference generation output on MFP4 -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, - MAX9295_PCLK_GPIO_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) - ); - - // Enable output -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, - MAX9295_PCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) - ); - -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REG3, -+ TRY(ret, regmap_update_bits(map, MAX9295_REG3, - MAX9295_RCLK_SEL_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_SEL_FIELD, 3U)) - ); - -- TRY(ret, regmap_update_bits_retry(map, MAX9295_REG6, -+ TRY(ret, regmap_update_bits(map, MAX9295_REG6, - MAX9295_RCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_RCLK_EN_FIELD, 1U)) - ); -@@ -554,7 +553,7 @@ static int max9295_set_local_control_channel_enabled(struct max9x_common *common - - dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); - -- return regmap_update_bits_retry(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, - MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); - } - -@@ -576,8 +575,8 @@ static int max9295_verify_devid(struct max9x_common *common) - int ret; - - // Fetch and output chip name + revision -- TRY(ret, regmap_read_retry(map, MAX9295_DEV_ID, &dev_id)); -- TRY(ret, regmap_read_retry(map, MAX9295_DEV_REV, &dev_rev)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); - - switch (dev_id) { - case MAX9295A: -@@ -618,10 +617,10 @@ static int max9295_enable(struct max9x_common *common) - TRY(ret, max9295_set_local_control_channel_enabled(common, false)); - - /* Clear the pipe maps */ -- TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_9, 0)); -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); - - /* Clear the csi port selections */ -- TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); - - return 0; - } -@@ -686,8 +685,6 @@ static int max9295_remap_addr(struct max9x_common *common) - TRY_DEV_HERE(ret, regmap_update_bits(common->map, MAX9295_CFGL_IIC_Y, MAX9295_TR3_TX_SRC_ID, - MAX9X_FIELD_PREP(MAX9295_TR3_TX_SRC_ID, common->client->addr)), dev); - -- msleep(20); -- - return 0; - } - -@@ -720,18 +717,18 @@ static int max9295_add_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr || src == 0) { - dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", - MAX9295_I2C_SRC(i2c_id, alias), virt_addr, - MAX9295_I2C_DST(i2c_id, alias), phys_addr); -- TRY(ret, regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), -+ TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) - ); - -- TRY(ret, regmap_write_retry(map, MAX9295_I2C_SRC(i2c_id, alias), -+ TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) - ); - } -@@ -749,10 +746,10 @@ static int max9295_remove_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr) { -- return regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), -+ return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); - } - } -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -index acc6bb03405b..38c344669df4 100644 ---- a/drivers/media/i2c/max9x/max9296.h -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -49,7 +49,7 @@ enum max9296_link_mode { - #define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ - /* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ - --#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 350 -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 - - #define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 - -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index 37a29d2ec046..b214e1176963 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -30,7 +30,6 @@ - #include - - #include "max96724.h" --#include "regmap-retry.h" - - // Params - int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -@@ -338,7 +337,7 @@ static int max96724_set_all_reset(struct max9x_common *common, bool enable) - - dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); - -- return regmap_update_bits_retry(map, MAX96724_RESET_ALL, -+ return regmap_update_bits(map, MAX96724_RESET_ALL, - MAX96724_RESET_ALL_FIELD, - MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); - } -@@ -355,8 +354,8 @@ static int max96724_soft_reset(struct max9x_common *common) - return ret; - - /* Wait for hardware available after soft reset */ -- /* TODO: Optimize sleep time 20 ms */ -- msleep(20); -+ /* TODO: Optimize sleep time 45 ms */ -+ msleep(45); - - ret = max96724_set_all_reset(common, 0); - if (ret) -diff --git a/drivers/media/i2c/max9x/regmap-retry.c b/drivers/media/i2c/max9x/regmap-retry.c -deleted file mode 100644 -index 48e5ec4bcb4f..000000000000 ---- a/drivers/media/i2c/max9x/regmap-retry.c -+++ /dev/null -@@ -1,52 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013--2025 Intel Corporation -- */ -- --#include "regmap-retry.h" -- --int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val) --{ -- int ret = 0; -- int retry = 50; -- -- while (retry--) { -- ret = regmap_read(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- --int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val) --{ -- int ret = 0; -- int retry = 50; -- -- while (retry--) { -- ret = regmap_write(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- --int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -- unsigned int mask, unsigned int val) --{ -- int ret = 0; -- int retry = 50; -- -- while (retry--) { -- ret = regmap_update_bits(map, reg, mask, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -diff --git a/drivers/media/i2c/max9x/regmap-retry.h b/drivers/media/i2c/max9x/regmap-retry.h -deleted file mode 100644 -index 9e9ac63878a0..000000000000 ---- a/drivers/media/i2c/max9x/regmap-retry.h -+++ /dev/null -@@ -1,18 +0,0 @@ --// SPDX-License-Identifier: GPL-2.0-only --/* -- * Copyright (C) 2013--2025 Intel Corporation -- */ -- --#ifndef _REGMAP_RETRY_H --#define _REGMAP_RETRY_H -- --#include -- --int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val); -- --int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val); -- --int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -- unsigned int mask, unsigned int val); -- --#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index fce930a1fdea..633c55504273 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -31,7 +31,6 @@ - #include - - #include "serdes.h" --#include "regmap-retry.h" - - static const s64 max9x_op_sys_clock[] = { - MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -@@ -398,6 +397,22 @@ static void *parse_serdes_pdata(struct device *dev) - return des_pdata; - } - -+static int regmap_read_retry(struct regmap *map, unsigned int reg, -+ unsigned int *val) -+{ -+ int ret = 0; -+ int i = 0; -+ -+ for (i = 0; i < 50; i++) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ - static int max9x_enable_resume(struct max9x_common *common) - { - struct device *dev = common->dev; -@@ -442,7 +457,6 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - unsigned int phys_addr, virt_addr; - struct i2c_client *phys_client; - struct regmap *phys_map, *virt_map; -- struct device *dev = &serial_link->remote.client->dev; - unsigned int val; - const struct regmap_config regmap_config = { - .reg_bits = 16, -@@ -452,23 +466,27 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - if (!serial_link->remote.pdata) - return 0; - -+ ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; -+ - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; - if (phys_addr == virt_addr) - return 0; - -- dev_info(dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); - - phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); - if (IS_ERR_OR_NULL(phys_client)) { -- dev_err(dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_client); - return ret; - } - - phys_map = regmap_init_i2c(phys_client, ®map_config); - if (IS_ERR_OR_NULL(phys_map)) { -- dev_err(dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_map); - goto err_client; - } -@@ -476,36 +494,36 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); - - virt_map = ser_common->map; -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { -- dev_info(dev, "Remap not necessary"); -+ dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_regmap; - } - - ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(dev, "Device not present at 0x%02x", phys_addr); -+ dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_regmap; - } else { -- dev_info(dev, "DEV_ID before: 0x%02x", val); -+ dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { -- dev_err(dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); - goto err_regmap; - } - - msleep(100); - -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(dev, "Device not present after remap to 0x%02x", virt_addr); -+ dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_regmap; - } else { -- dev_info(dev, "DEV_ID after: 0x%02x", val); -+ dev_info(common->dev, "DEV_ID after: 0x%02x", val); - } - - err_regmap: -@@ -513,6 +531,8 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - err_client: - i2c_unregister_device(phys_client); - -+ max9x_des_deisolate_serial_link(common, link_id); -+ - return ret; - } - -@@ -562,31 +582,42 @@ int max9x_common_resume(struct max9x_common *common) - struct max9x_common *des_common = NULL; - struct device *dev = common->dev; - u32 des_link; -+ u32 phys_addr, virt_addr; - int ret = 0; -+ int retry = 50; - -- if (dev->platform_data && common->type == MAX9X_SERIALIZER) { -+ if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -- WARN_ON(pdata->num_serial_links < 1); -- -- des_common = max9x_client_to_common( -- pdata->serial_links[0].des_client); -- if (des_common) { -- des_link = pdata->serial_links[0].des_link_id; -- ret = max9x_des_isolate_serial_link(des_common, des_link); -- if (ret) -- goto err_deisolate; -- ret = max9x_remap_serializers_resume(des_common, des_link); -- if (ret) -- goto err_deisolate; -- } else { -- return ret; -+ -+ virt_addr = common->client->addr; -+ phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -+ -+ if (common->type == MAX9X_SERIALIZER) { -+ WARN_ON(pdata->num_serial_links < 1); -+ -+ des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ return ret; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_reset_serializer; -+ } - } - } - -- ret = max9x_verify_devid(common); -+ while (retry--) { -+ ret = max9x_verify_devid(common); -+ if (ret) -+ msleep(100); -+ else -+ break; -+ } - if (ret) { - dev_err(dev, "can't get devid after resume"); -- goto err_reset_serializer; -+ goto err_deisolate; - } - - ret = max9x_enable_resume(common); -@@ -610,6 +641,11 @@ int max9x_common_resume(struct max9x_common *common) - err_disable: - max9x_disable(common); - -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) { -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ } -+ - err_reset_serializer: - if (common->type == MAX9X_SERIALIZER) { - if (common->common_ops && common->common_ops->remap_reset) { -@@ -619,11 +655,6 @@ int max9x_common_resume(struct max9x_common *common) - } - } - --err_deisolate: -- if (common->type == MAX9X_SERIALIZER && des_common) { -- max9x_des_deisolate_serial_link(des_common, des_link); -- } -- - return ret; - } - -@@ -633,12 +664,26 @@ int max9x_common_suspend(struct max9x_common *common) - - dev_dbg(common->dev, "try to suspend"); - -+ max9x_disable_translations(common); - - for (link_id = 0; link_id < common->num_serial_links; link_id++) - max9x_disable_serial_link(common, link_id); - - max9x_disable(common); - -+ if (common->type == MAX9X_SERIALIZER) { -+ struct device *dev = common->dev; -+ int ret; -+ -+ if (dev->platform_data) { -+ if (common->common_ops && common->common_ops->remap_reset) { -+ ret = common->common_ops->remap_reset(common); -+ if (ret) -+ return ret; -+ } -+ } -+ } -+ - return 0; - } - -@@ -1054,7 +1099,7 @@ int max9x_verify_devid(struct max9x_common *common) - * Fetch and output chip name + revision - * try both virtual address and physical address - */ -- ret = regmap_read_retry(map, MAX9X_DEV_ID, &dev_id); -+ ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); - if (ret) { - dev_warn(dev, "Failed to read chip ID from virtual address"); - if (phys_map) { -@@ -1074,7 +1119,7 @@ int max9x_verify_devid(struct max9x_common *common) - } - common->des = &max9x_chips[chip_type]; - common->type = common->des->serdes_type; -- TRY(ret, regmap_read_retry(map, common->des->rev_reg, &dev_rev)); -+ TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); - dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); - - dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -@@ -1131,14 +1176,14 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - if (IS_ERR_OR_NULL(virt_map)) - goto err_virt_client; - -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { - dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_virt_regmap; - } - -- ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_virt_regmap; -@@ -1146,7 +1191,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { - dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); -@@ -1155,7 +1200,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - usleep_range(1000, 1050); - -- ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_virt_regmap; -@@ -1173,6 +1218,10 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - err_client: - i2c_unregister_device(phys_client); - -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ -+ max9x_des_deisolate_serial_link(common, link_id); -+ - return ret; - } - -@@ -1737,7 +1786,6 @@ static int max9x_registered(struct v4l2_subdev *sd) - if (subdev_pdata) { - struct max9x_pdata *ser_pdata = - subdev_pdata->board_info.platform_data; -- struct v4l2_subdev *subdev = NULL; - - WARN_ON(ser_pdata->num_serial_links < 1); - -@@ -1749,13 +1797,13 @@ static int max9x_registered(struct v4l2_subdev *sd) - * physical i2c at the same time - */ - ret = max9x_des_isolate_serial_link(common, link_id); -+ if (ret) -+ return ret; - -- if (!ret) -- subdev = v4l2_i2c_new_subdev_board( -- sd->v4l2_dev, -- common->muxc->adapter[link_id], -- &subdev_pdata->board_info, -- NULL); -+ struct v4l2_subdev *subdev = -+ v4l2_i2c_new_subdev_board(sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, NULL); - - ret = max9x_des_deisolate_serial_link(common, link_id); - if (ret) -@@ -2585,6 +2633,8 @@ int max9x_setup_translations(struct max9x_common *common) - break; - } - -+ msleep(10); -+ - return err; - } - --- -2.43.0 - diff --git a/SPECS/kernel/0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet b/SPECS/kernel/0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet new file mode 100644 index 000000000..c28246970 --- /dev/null +++ b/SPECS/kernel/0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet @@ -0,0 +1,85 @@ +From 413ce7c231c970971e284e16f9cef2c0e49e04aa Mon Sep 17 00:00:00 2001 +From: Vinicius Costa Gomes +Date: Wed, 19 Mar 2025 11:17:06 +0500 +Subject: [PATCH 2/4] af_packet: Fix wrong timestamps in tcpdump + +Since commit '97dc7cd ("ptp: Support late timestamp +determination")', timestamps may need to be retrieved with input from +the driver (.ndo_get_tstamp()). Support was missing from mmap'ed +AF_PACKET sockets. + +The problem was noticed using the igc driver, that since commit +'069b142 ("igc: Add support for PTP .getcyclesx64()")' has migrated +to use .ndo_get_tstamp() that timestamps were wrong on the receiving +side, when using tcpdump. + +Signed-off-by: Vinicius Costa Gomes +--- + net/packet/af_packet.c | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c +index 173e6edda08f8..6904a5a810a80 100644 +--- a/net/packet/af_packet.c ++++ b/net/packet/af_packet.c +@@ -450,15 +450,20 @@ static int __packet_get_status(const struct packet_sock *po, void *frame) + } + } + +-static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, +- unsigned int flags) ++static __u32 tpacket_get_timestamp(struct net_device *dev, struct sk_buff *skb, ++ struct timespec64 *ts, unsigned int flags, ++ unsigned int sk_tsflags) + { + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + +- if (shhwtstamps && +- (flags & SOF_TIMESTAMPING_RAW_HARDWARE) && +- ktime_to_timespec64_cond(shhwtstamps->hwtstamp, ts)) +- return TP_STATUS_TS_RAW_HARDWARE; ++ bool cycles = sk_tsflags & SOF_TIMESTAMPING_BIND_PHC; ++ ++ if (shhwtstamps && shhwtstamps->hwtstamp && ++ (flags & SOF_TIMESTAMPING_RAW_HARDWARE)) { ++ ktime_t tstamp = netdev_get_tstamp(dev, shhwtstamps, cycles); ++ ++ return ktime_to_timespec64_cond(tstamp, ts) ? TP_STATUS_TS_RAW_HARDWARE : 0; ++ } + + if ((flags & SOF_TIMESTAMPING_SOFTWARE) && + ktime_to_timespec64_cond(skb_tstamp(skb), ts)) +@@ -470,11 +475,16 @@ static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, + static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame, + struct sk_buff *skb) + { ++ struct net_device *dev = skb->dev; ++ unsigned int sk_tsflags; + union tpacket_uhdr h; + struct timespec64 ts; + __u32 ts_status; + +- if (!(ts_status = tpacket_get_timestamp(skb, &ts, READ_ONCE(po->tp_tstamp)))) ++ sk_tsflags = READ_ONCE(po->sk.sk_tsflags); ++ ts_status = tpacket_get_timestamp(dev, skb, &ts, READ_ONCE(po->tp_tstamp), sk_tsflags); ++ ++ if (!(ts_status)) + return 0; + + h.raw = frame; +@@ -2392,9 +2402,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, + /* Always timestamp; prefer an existing software timestamp taken + * closer to the time of capture. + */ +- ts_status = tpacket_get_timestamp(skb, &ts, ++ ts_status = tpacket_get_timestamp(dev, skb, &ts, + READ_ONCE(po->tp_tstamp) | +- SOF_TIMESTAMPING_SOFTWARE); ++ SOF_TIMESTAMPING_SOFTWARE, ++ READ_ONCE(sk->sk_tsflags)); + if (!ts_status) + ktime_get_real_ts64(&ts); + +-- +2.43.0 + diff --git a/SPECS/kernel/0002-bpf-add-btf-register-unregister-API.ethernet b/SPECS/kernel/0002-bpf-add-btf-register-unregister-API.ethernet new file mode 100644 index 000000000..4073d0468 --- /dev/null +++ b/SPECS/kernel/0002-bpf-add-btf-register-unregister-API.ethernet @@ -0,0 +1,153 @@ +From 1f6f920dfedac24ab1ab03977b223d461fbda737 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Mon, 14 Jun 2021 09:03:35 +0800 +Subject: [PATCH 02/14] bpf: add btf register/unregister API + +A device driver can register own BTF format buffers into the kernel. +Will be used in downstream patches by mlx5 XDP driver to advertise the +types and populated XDP meta data. + +Signed-off-by: Saeed Mahameed +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + include/linux/btf.h | 8 ++++ + kernel/bpf/btf.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 98 insertions(+) + +diff --git a/include/linux/btf.h b/include/linux/btf.h +index f06976ffb63f9..96d6d07ea5a91 100644 +--- a/include/linux/btf.h ++++ b/include/linux/btf.h +@@ -604,6 +604,8 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type + + return btf_type_is_struct(t); + } ++struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size); ++void btf_unregister(struct btf *btf); + #else + static inline const struct btf_type *btf_type_by_id(const struct btf *btf, + u32 type_id) +@@ -682,5 +684,11 @@ static inline int btf_check_iter_arg(struct btf *btf, const struct btf_type *fun + { + return -EOPNOTSUPP; + } ++static inline struct btf * ++btf_register(const char *name, void *btf_data, u32 btf_data_size) ++{ ++ return NULL; ++} ++static inline void btf_unregister(struct btf *btf) { } + #endif + #endif +diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c +index 0de8fc8a0e0b3..2abbd84dc7afe 100644 +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -6380,6 +6380,95 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, + + #endif /* CONFIG_DEBUG_INFO_BTF_MODULES */ + ++/* TODO: reuse btf_parse_raw in btf_parse_module */ ++static struct btf *btf_parse_raw(const char *name, const void *data, unsigned int data_size) ++{ ++ struct btf_verifier_env *env = NULL; ++ struct bpf_verifier_log *log; ++ struct btf *btf = NULL; ++ int err; ++ ++ env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN); ++ if (!env) ++ return ERR_PTR(-ENOMEM); ++ ++ log = &env->log; ++ log->level = BPF_LOG_KERNEL; ++ ++ btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN); ++ if (!btf) { ++ err = -ENOMEM; ++ goto errout; ++ } ++ env->btf = btf; ++ btf->kernel_btf = true; ++ snprintf(btf->name, sizeof(btf->name), "%s", name); ++ ++ btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN); ++ if (!btf->data) { ++ err = -ENOMEM; ++ goto errout; ++ } ++ memcpy(btf->data, data, data_size); ++ btf->data_size = data_size; ++ ++ err = btf_parse_hdr(env); ++ if (err) ++ goto errout; ++ ++ btf->nohdr_data = btf->data + btf->hdr.hdr_len; ++ ++ err = btf_parse_str_sec(env); ++ if (err) ++ goto errout; ++ ++ err = btf_check_all_metas(env); ++ if (err) ++ goto errout; ++ ++ btf_verifier_env_free(env); ++ refcount_set(&btf->refcnt, 1); ++ return btf; ++ ++errout: ++ btf_verifier_env_free(env); ++ if (btf) { ++ kvfree(btf->data); ++ kvfree(btf->types); ++ kfree(btf); ++ } ++ return ERR_PTR(err); ++} ++ ++/* TODO: reuse btf_register in btf_module_notify */ ++struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size) ++{ ++ struct btf *btf; ++ int err; ++ ++ btf = btf_parse_raw(name, btf_data, btf_data_size); ++ if (IS_ERR(btf)) ++ return btf; ++ ++ err = btf_alloc_id(btf); ++ if (err) { ++ btf_free(btf); ++ btf = ERR_PTR(err); ++ } ++ return btf; ++} ++EXPORT_SYMBOL(btf_register); ++ ++void btf_unregister(struct btf *btf) ++{ ++ if (IS_ERR(btf)) ++ return; ++ ++ /* btf_put since btf might be held by user */ ++ btf_put(btf); ++} ++EXPORT_SYMBOL(btf_unregister); ++ + struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog) + { + struct bpf_prog *tgt_prog = prog->aux->dst_prog; +@@ -8142,6 +8231,7 @@ u32 btf_obj_id(const struct btf *btf) + { + return btf->id; + } ++EXPORT_SYMBOL(btf_obj_id); + + bool btf_is_kernel(const struct btf *btf) + { +-- +2.43.0 + diff --git a/SPECS/kernel/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt b/SPECS/kernel/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt index a8159f81b..e02ab65e1 100644 --- a/SPECS/kernel/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt +++ b/SPECS/kernel/0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt @@ -1,4 +1,4 @@ -From 85467ee02ac6e068c96101ee3fbe7292a743ca83 Mon Sep 17 00:00:00 2001 +From cfb6ba1919984dd554322c67c8684f9d824cf7d3 Mon Sep 17 00:00:00 2001 From: Mike Galbraith Date: Sat, 27 Feb 2016 09:01:42 +0100 Subject: [PATCH 2/9] drm/i915: Don't disable interrupts on PREEMPT_RT during @@ -38,7 +38,7 @@ Signed-off-by: Sebastian Andrzej Siewior 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c -index a187db6df2d3..dbdc4d9b2a33 100644 +index a187db6df2d36..dbdc4d9b2a33c 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -562,7 +562,8 @@ void intel_pipe_update_start(struct intel_atomic_state *state, @@ -72,10 +72,10 @@ index a187db6df2d3..dbdc4d9b2a33 100644 if (intel_vgpu_active(dev_priv)) goto out; diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c -index 198e69efe9ac..2d86b2c3f863 100644 +index d4d181f9dca5f..58f2f074aff20 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c -@@ -929,13 +929,15 @@ intel_legacy_cursor_update(struct drm_plane *_plane, +@@ -919,13 +919,15 @@ intel_legacy_cursor_update(struct drm_plane *_plane, */ intel_psr_wait_for_idle_locked(crtc_state); @@ -93,7 +93,7 @@ index 198e69efe9ac..2d86b2c3f863 100644 } if (new_plane_state->uapi.visible) { -@@ -945,7 +947,8 @@ intel_legacy_cursor_update(struct drm_plane *_plane, +@@ -935,7 +937,8 @@ intel_legacy_cursor_update(struct drm_plane *_plane, intel_plane_disable_arm(NULL, plane, crtc_state); } @@ -104,10 +104,10 @@ index 198e69efe9ac..2d86b2c3f863 100644 intel_psr_unlock(crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c -index b82e9fc51af0..d0197fd96d6c 100644 +index d0d9e2c7d5476..590a079f926b2 100644 --- a/drivers/gpu/drm/i915/display/intel_vblank.c +++ b/drivers/gpu/drm/i915/display/intel_vblank.c -@@ -753,11 +753,13 @@ int intel_vblank_evade(struct intel_vblank_evade_ctx *evade) +@@ -761,11 +761,13 @@ int intel_vblank_evade(struct intel_vblank_evade_ctx *evade) break; } diff --git a/SPECS/kernel/0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt b/SPECS/kernel/0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt deleted file mode 100644 index 29935b51c..000000000 --- a/SPECS/kernel/0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt +++ /dev/null @@ -1,51 +0,0 @@ -From d993b6ff2332536674c05fbbae6d78d6ae35ec3f Mon Sep 17 00:00:00 2001 -From: Junxiao Chang -Date: Fri, 7 Nov 2025 11:31:52 +0800 -Subject: [PATCH 2/2] drm/me/gsc: mei interrupt top half should be in irq - disabled context - -MEI GSC interrupt comes from i915 or xe driver. It has top half and -bottom half. Top half is called from i915/xe interrupt handler. It -should be in irq disabled context. - -With RT kernel(PREEMPT_RT enabled), by default IRQ handler is in -threaded IRQ. MEI GSC top half might be in threaded IRQ context. -generic_handle_irq_safe API could be called from either IRQ or -process context, it disables local IRQ then calls MEI GSC interrupt -top half. - -This change fixes B580 GPU boot issue with RT enabled. - -Fixes: e02cea83d32d ("drm/xe/gsc: add Battlemage support") -Tested-by: Baoli Zhang -Signed-off-by: Junxiao Chang -Reviewed-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/xe/xe_heci_gsc.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/xe/xe_heci_gsc.c b/drivers/gpu/drm/xe/xe_heci_gsc.c -index 6d7b62724126..bd5afa675679 100644 ---- a/drivers/gpu/drm/xe/xe_heci_gsc.c -+++ b/drivers/gpu/drm/xe/xe_heci_gsc.c -@@ -221,7 +221,7 @@ void xe_heci_gsc_irq_handler(struct xe_device *xe, u32 iir) - if (xe->heci_gsc.irq < 0) - return; - -- ret = generic_handle_irq(xe->heci_gsc.irq); -+ ret = generic_handle_irq_safe(xe->heci_gsc.irq); - if (ret) - drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret); - } -@@ -241,7 +241,7 @@ void xe_heci_csc_irq_handler(struct xe_device *xe, u32 iir) - if (xe->heci_gsc.irq < 0) - return; - -- ret = generic_handle_irq(xe->heci_gsc.irq); -+ ret = generic_handle_irq_safe(xe->heci_gsc.irq); - if (ret) - drm_err_ratelimited(&xe->drm, "error handling GSC irq: %d\n", ret); - } --- -2.43.0 - diff --git a/SPECS/kernel/0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm b/SPECS/kernel/0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm new file mode 100644 index 000000000..6591aec3d --- /dev/null +++ b/SPECS/kernel/0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm @@ -0,0 +1,155 @@ +From 48372bf2a639efaabe83a9b940ec4e315800391c Mon Sep 17 00:00:00 2001 +From: Dongwon Kim +Date: Tue, 28 Jun 2022 11:23:10 -0700 +Subject: [PATCH 2/3] drm/virtio: freeze and restore hooks to support suspend + and resume + +virtio device needs to delete before VM suspend happens +then reinitialize all virtqueues again upon resume + +Signed-off-by: Dongwon Kim +--- + drivers/gpu/drm/virtio/virtgpu_drv.c | 53 +++++++++++++++++++++++++++- + drivers/gpu/drm/virtio/virtgpu_drv.h | 1 + + drivers/gpu/drm/virtio/virtgpu_kms.c | 25 +++++++++---- + 3 files changed, 72 insertions(+), 7 deletions(-) + +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c +index 71c6ccad4b99b..905246316dc9e 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.c ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.c +@@ -163,6 +163,53 @@ static unsigned int features[] = { + VIRTIO_GPU_F_RESOURCE_BLOB, + VIRTIO_GPU_F_CONTEXT_INIT, + }; ++ ++#ifdef CONFIG_PM_SLEEP ++static int virtgpu_freeze(struct virtio_device *vdev) ++{ ++ struct drm_device *dev = vdev->priv; ++ struct virtio_gpu_device *vgdev = dev->dev_private; ++ int error; ++ ++ error = drm_mode_config_helper_suspend(dev); ++ if (error) { ++ DRM_ERROR("suspend error %d\n", error); ++ return error; ++ } ++ ++ flush_work(&vgdev->obj_free_work); ++ flush_work(&vgdev->ctrlq.dequeue_work); ++ flush_work(&vgdev->cursorq.dequeue_work); ++ flush_work(&vgdev->config_changed_work); ++ vdev->config->del_vqs(vdev); ++ ++ return 0; ++} ++ ++static int virtgpu_restore(struct virtio_device *vdev) ++{ ++ struct drm_device *dev = vdev->priv; ++ struct virtio_gpu_device *vgdev = dev->dev_private; ++ int error; ++ ++ error = virtio_gpu_find_vqs(vgdev); ++ if (error) { ++ DRM_ERROR("failed to find virt queues\n"); ++ return error; ++ } ++ ++ virtio_device_ready(vdev); ++ ++ error = drm_mode_config_helper_resume(dev); ++ if (error) { ++ DRM_ERROR("resume error %d\n", error); ++ return error; ++ } ++ ++ return 0; ++} ++#endif ++ + static struct virtio_driver virtio_gpu_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), +@@ -171,7 +218,11 @@ static struct virtio_driver virtio_gpu_driver = { + .probe = virtio_gpu_probe, + .remove = virtio_gpu_remove, + .shutdown = virtio_gpu_shutdown, +- .config_changed = virtio_gpu_config_changed ++ .config_changed = virtio_gpu_config_changed, ++#ifdef CONFIG_PM_SLEEP ++ .freeze = virtgpu_freeze, ++ .restore = virtgpu_restore, ++#endif + }; + + static int __init virtio_gpu_driver_init(void) +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h +index f17660a71a3e7..1279f998c8e0d 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.h ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.h +@@ -300,6 +300,7 @@ void virtio_gpu_deinit(struct drm_device *dev); + void virtio_gpu_release(struct drm_device *dev); + int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); + void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); ++int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev); + + /* virtgpu_gem.c */ + int virtio_gpu_gem_object_open(struct drm_gem_object *obj, +diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c +index 1c15cbf326b78..d16be811a2bdc 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_kms.c ++++ b/drivers/gpu/drm/virtio/virtgpu_kms.c +@@ -114,15 +114,29 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, + vgdev->num_capsets = num_capsets; + } + +-int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) ++int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev) + { + struct virtqueue_info vqs_info[] = { + { "control", virtio_gpu_ctrl_ack }, + { "cursor", virtio_gpu_cursor_ack }, + }; +- struct virtio_gpu_device *vgdev; +- /* this will expand later */ ++ + struct virtqueue *vqs[2]; ++ int ret; ++ ++ ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); ++ if (ret) ++ return ret; ++ ++ vgdev->ctrlq.vq = vqs[0]; ++ vgdev->cursorq.vq = vqs[1]; ++ ++ return 0; ++} ++ ++int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) ++{ ++ struct virtio_gpu_device *vgdev; + u32 num_scanouts, num_capsets; + int ret = 0; + +@@ -206,13 +220,12 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) + DRM_INFO("features: %ccontext_init\n", + vgdev->has_context_init ? '+' : '-'); + +- ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); ++ ret = virtio_gpu_find_vqs(vgdev); + if (ret) { + DRM_ERROR("failed to find virt queues\n"); + goto err_vqs; + } +- vgdev->ctrlq.vq = vqs[0]; +- vgdev->cursorq.vq = vqs[1]; ++ + ret = virtio_gpu_alloc_vbufs(vgdev); + if (ret) { + DRM_ERROR("failed to alloc vbufs\n"); +-- +2.43.0 + diff --git a/SPECS/kernel/0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm b/SPECS/kernel/0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm deleted file mode 100644 index f632331d0..000000000 --- a/SPECS/kernel/0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm +++ /dev/null @@ -1,187 +0,0 @@ -From 0f5cf73f36b21910dc66c741721326225a610494 Mon Sep 17 00:00:00 2001 -From: Dongwon Kim -Date: Thu, 18 Aug 2022 16:19:33 -0700 -Subject: [PATCH 2/2] drm/virtio: save and restore virtio_gpu_objects - -Host KVM/QEMU loses all graphic resourses submitted by guest OS upon -resumption from sleep or hibernation. This will cause invalid resource -errors when the guest OS makes any request to the host regarding those -resources. One way to prevent this problem is to let virtio-gpu driver -resubmit all existing resources upon resumption. A linked-list for -keeping references of all created virtio_gpu_object and its params is -added for this save and restore mechanism. Virtio-gpu objects are added -to the list whenever a new object is created and sent to the host. -All backed-up objects will then be re-sent to the host in .resume -function with 'create resource' virtio gpu command. - -Signed-off-by: Dongwon Kim ---- - drivers/gpu/drm/virtio/virtgpu_drv.c | 6 +++ - drivers/gpu/drm/virtio/virtgpu_drv.h | 10 ++++ - drivers/gpu/drm/virtio/virtgpu_kms.c | 1 + - drivers/gpu/drm/virtio/virtgpu_object.c | 65 +++++++++++++++++++++++++ - 4 files changed, 82 insertions(+) - -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c -index 905246316dc9..8f333ed92836 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.c -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c -@@ -200,6 +200,12 @@ static int virtgpu_restore(struct virtio_device *vdev) - - virtio_device_ready(vdev); - -+ error = virtio_gpu_object_restore_all(vgdev); -+ if (error) { -+ DRM_ERROR("Failed to recover objects\n"); -+ return error; -+ } -+ - error = drm_mode_config_helper_resume(dev); - if (error) { - DRM_ERROR("resume error %d\n", error); -diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h -index 1279f998c8e0..189e2282af19 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_drv.h -+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h -@@ -126,6 +126,12 @@ struct virtio_gpu_object_array { - struct drm_gem_object *objs[] __counted_by(total); - }; - -+struct virtio_gpu_object_restore { -+ struct virtio_gpu_object *bo; -+ struct virtio_gpu_object_params params; -+ struct list_head node; -+}; -+ - struct virtio_gpu_vbuffer; - struct virtio_gpu_device; - -@@ -265,6 +271,7 @@ struct virtio_gpu_device { - struct work_struct obj_free_work; - spinlock_t obj_free_lock; - struct list_head obj_free_list; -+ struct list_head obj_rec; - - struct virtio_gpu_drv_capset *capsets; - uint32_t num_capsets; -@@ -479,6 +486,9 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo); - - int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, - uint32_t *resid); -+ -+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev); -+ - /* virtgpu_prime.c */ - int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, - struct virtio_gpu_object *bo); -diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c -index 46963f050b88..e087aeeef074 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_kms.c -+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c -@@ -163,6 +163,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) - vgdev->fence_drv.context = dma_fence_context_alloc(1); - spin_lock_init(&vgdev->fence_drv.lock); - INIT_LIST_HEAD(&vgdev->fence_drv.fences); -+ INIT_LIST_HEAD(&vgdev->obj_rec); - INIT_LIST_HEAD(&vgdev->cap_cache); - INIT_WORK(&vgdev->config_changed_work, - virtio_gpu_config_changed_work_func); -diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c -index 5517cff8715c..4e2dbd96bc16 100644 ---- a/drivers/gpu/drm/virtio/virtgpu_object.c -+++ b/drivers/gpu/drm/virtio/virtgpu_object.c -@@ -61,6 +61,38 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t - } - } - -+static void virtio_gpu_object_save_restore_list(struct virtio_gpu_device *vgdev, -+ struct virtio_gpu_object *bo, -+ struct virtio_gpu_object_params *params) -+{ -+ struct virtio_gpu_object_restore *new; -+ -+ new = kvmalloc(sizeof(*new), GFP_KERNEL); -+ if (!new) { -+ DRM_ERROR("Fail to allocate virtio_gpu_object_restore"); -+ return; -+ } -+ -+ new->bo = bo; -+ memcpy(&new->params, params, sizeof(*params)); -+ -+ list_add_tail(&new->node, &vgdev->obj_rec); -+} -+ -+static void virtio_gpu_object_del_restore_list(struct virtio_gpu_device *vgdev, -+ struct virtio_gpu_object *bo) -+{ -+ struct virtio_gpu_object_restore *curr, *tmp; -+ -+ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { -+ if (bo == curr->bo) { -+ list_del(&curr->node); -+ kvfree(curr); -+ break; -+ } -+ } -+} -+ - void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) - { - struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; -@@ -84,6 +116,7 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) - drm_gem_object_release(&bo->base.base); - kfree(bo); - } -+ virtio_gpu_object_del_restore_list(vgdev, bo); - } - - static void virtio_gpu_free_object(struct drm_gem_object *obj) -@@ -257,8 +290,11 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, - objs, fence); - virtio_gpu_object_attach(vgdev, bo, ents, nents); - } -+ /* add submitted object to restore list */ -+ virtio_gpu_object_save_restore_list(vgdev, bo, params); - - *bo_ptr = bo; -+ - return 0; - - err_put_objs: -@@ -271,3 +307,32 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, - drm_gem_shmem_free(shmem_obj); - return ret; - } -+ -+int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev) -+{ -+ struct virtio_gpu_object_restore *curr, *tmp; -+ struct virtio_gpu_mem_entry *ents; -+ unsigned int nents; -+ int ret; -+ -+ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { -+ ret = virtio_gpu_object_shmem_init(vgdev, curr->bo, &ents, &nents); -+ if (ret) -+ break; -+ -+ if (curr->params.blob) { -+ virtio_gpu_cmd_resource_create_blob(vgdev, curr->bo, &curr->params, -+ ents, nents); -+ } else if (curr->params.virgl) { -+ virtio_gpu_cmd_resource_create_3d(vgdev, curr->bo, &curr->params, -+ NULL, NULL); -+ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); -+ } else { -+ virtio_gpu_cmd_create_resource(vgdev, curr->bo, &curr->params, -+ NULL, NULL); -+ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); -+ } -+ } -+ -+ return ret; -+} --- -2.43.0 - diff --git a/SPECS/kernel/0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu b/SPECS/kernel/0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu deleted file mode 100644 index d6ce738f8..000000000 --- a/SPECS/kernel/0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu +++ /dev/null @@ -1,76 +0,0 @@ -From f5ccdd6257bea670f612e8bdabe5052d698dd031 Mon Sep 17 00:00:00 2001 -From: Khai Wen Ng -Date: Thu, 11 Sep 2025 08:38:49 +0800 -Subject: [PATCH 2/6] i2c: i2c-core-acpi: clear dependency for MUX or ATR - adapters - -In ACPI table, devices that should residing on parent MUX or ATR -adapters have dependencies on parent devices. Hence, clear the -dependencies if the adapter created is based by parent through -ATR or MUX. ACPI driver can then call register client for the -devices - -Signed-off-by: Khai Wen Ng -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/i2c-core-acpi.c | 32 +++++++++++++++++++++++++++++++- - 1 file changed, 31 insertions(+), 1 deletion(-) - -diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c -index ed90858a27b7..2f2e26a92937 100644 ---- a/drivers/i2c/i2c-core-acpi.c -+++ b/drivers/i2c/i2c-core-acpi.c -@@ -285,11 +285,40 @@ static void i2c_acpi_register_device(struct i2c_adapter *adapter, - if (acpi_quirk_skip_i2c_client_enumeration(adev)) - return; - -+ /* Check if Device is on ATR or MUX adapter */ -+ if (adapter->is_atr || adapter->is_mux) { -+ u32 channel; -+ -+ if (fwnode_property_present(&adev->fwnode, "channel")) { -+ if (fwnode_property_read_u32(&adev->fwnode, "channel", &channel)) { -+ dev_err(&adev->dev, "failed to read channel property\n"); -+ return; -+ } -+ -+ if (adapter->chan_id != channel) { -+ dev_err(&adev->dev, "device is not on current adapter %d\n", -+ adapter->chan_id); -+ goto err; -+ } else { -+ acpi_dev_clear_dependencies(ACPI_COMPANION(adapter->dev.parent)); -+ } -+ -+ if (adev->dep_unmet) { -+ dev_err(&adev->dev, "device has unmet dependencies\n"); -+ return; -+ } -+ } else { -+ dev_err(&adev->dev, "channel property not present\n"); -+ return; -+ } -+ } -+ - adev->power.flags.ignore_parent = true; - acpi_device_set_enumerated(adev); - - if (IS_ERR(i2c_new_client_device(adapter, info))) - adev->power.flags.ignore_parent = false; -+err: - } - - static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, -@@ -323,7 +352,8 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap) - acpi_status status; - - if (!has_acpi_companion(&adap->dev)) -- return; -+ if (!adap->dev.fwnode) -+ return; - - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - I2C_ACPI_MAX_SCAN_DEPTH, --- -2.43.0 - diff --git a/SPECS/kernel/0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c b/SPECS/kernel/0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c deleted file mode 100644 index f084b1dfa..000000000 --- a/SPECS/kernel/0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c +++ /dev/null @@ -1,170 +0,0 @@ -From 5bdce02f216d9519ec9615248ff5363811fbac2c Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:27 +0300 -Subject: [PATCH 02/11] i3c: master: Add helpers for DMA mapping and bounce - buffer handling - -Some I3C controllers such as MIPI I3C HCI may pad the last DWORD (32-bit) -with stale data from the RX FIFO in DMA transfers if the receive length -is not DWORD aligned and when the device DMA is IOMMU mapped. - -In such a case, a properly sized bounce buffer is required in order to -avoid possible data corruption. In a review discussion, proposal was to -have a common helpers in I3C core for DMA mapping and bounce buffer -handling. - -Drivers may use the helper i3c_master_dma_map_single() to map a buffer -for a DMA transfer. It internally allocates a bounce buffer if buffer is -not DMA'able or when the driver requires it for a transfer. - -Helper i3c_master_dma_unmap_single() does the needed cleanups and -data copying from the bounce buffer. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-2-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master.c | 74 ++++++++++++++++++++++++++++++++++++++ - include/linux/i3c/master.h | 26 ++++++++++++++ - 2 files changed, 100 insertions(+) - -diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c -index 2ef898a8fd80..033d06cabca2 100644 ---- a/drivers/i3c/master.c -+++ b/drivers/i3c/master.c -@@ -8,6 +8,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1727,6 +1728,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master) - } - EXPORT_SYMBOL_GPL(i3c_master_do_daa); - -+/** -+ * i3c_master_dma_map_single() - Map buffer for single DMA transfer -+ * @dev: device object of a device doing DMA -+ * @buf: destination/source buffer for DMA -+ * @len: length of transfer -+ * @force_bounce: true, force to use a bounce buffer, -+ * false, function will auto check is a bounce buffer required -+ * @dir: DMA direction -+ * -+ * Map buffer for a DMA transfer and allocate a bounce buffer if required. -+ * -+ * Return: I3C DMA transfer descriptor or NULL in case of error. -+ */ -+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf, -+ size_t len, bool force_bounce, enum dma_data_direction dir) -+{ -+ struct i3c_dma *dma_xfer __free(kfree) = NULL; -+ void *bounce __free(kfree) = NULL; -+ void *dma_buf = buf; -+ -+ dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL); -+ if (!dma_xfer) -+ return NULL; -+ -+ dma_xfer->dev = dev; -+ dma_xfer->buf = buf; -+ dma_xfer->dir = dir; -+ dma_xfer->len = len; -+ dma_xfer->map_len = len; -+ -+ if (is_vmalloc_addr(buf)) -+ force_bounce = true; -+ -+ if (force_bounce) { -+ dma_xfer->map_len = ALIGN(len, cache_line_size()); -+ if (dir == DMA_FROM_DEVICE) -+ bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL); -+ else -+ bounce = kmemdup(buf, dma_xfer->map_len, GFP_KERNEL); -+ if (!bounce) -+ return NULL; -+ dma_buf = bounce; -+ } -+ -+ dma_xfer->addr = dma_map_single(dev, dma_buf, dma_xfer->map_len, dir); -+ if (dma_mapping_error(dev, dma_xfer->addr)) -+ return NULL; -+ -+ dma_xfer->bounce_buf = no_free_ptr(bounce); -+ return no_free_ptr(dma_xfer); -+} -+EXPORT_SYMBOL_GPL(i3c_master_dma_map_single); -+ -+/** -+ * i3c_master_dma_unmap_single() - Unmap buffer after DMA -+ * @dma_xfer: DMA transfer and mapping descriptor -+ * -+ * Unmap buffer and cleanup DMA transfer descriptor. -+ */ -+void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer) -+{ -+ dma_unmap_single(dma_xfer->dev, dma_xfer->addr, -+ dma_xfer->map_len, dma_xfer->dir); -+ if (dma_xfer->bounce_buf) { -+ if (dma_xfer->dir == DMA_FROM_DEVICE) -+ memcpy(dma_xfer->buf, dma_xfer->bounce_buf, -+ dma_xfer->len); -+ kfree(dma_xfer->bounce_buf); -+ } -+ kfree(dma_xfer); -+} -+EXPORT_SYMBOL_GPL(i3c_master_dma_unmap_single); -+ - /** - * i3c_master_set_info() - set master device information - * @master: master used to send frames on the bus -diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h -index 043f5c7ff398..c52a82dd79a6 100644 ---- a/include/linux/i3c/master.h -+++ b/include/linux/i3c/master.h -@@ -558,6 +558,26 @@ struct i3c_master_controller { - #define i3c_bus_for_each_i3cdev(bus, dev) \ - list_for_each_entry(dev, &(bus)->devs.i3c, common.node) - -+/** -+ * struct i3c_dma - DMA transfer and mapping descriptor -+ * @dev: device object of a device doing DMA -+ * @buf: destination/source buffer for DMA -+ * @len: length of transfer -+ * @map_len: length of DMA mapping -+ * @addr: mapped DMA address for a Host Controller Driver -+ * @dir: DMA direction -+ * @bounce_buf: an allocated bounce buffer if transfer needs it or NULL -+ */ -+struct i3c_dma { -+ struct device *dev; -+ void *buf; -+ size_t len; -+ size_t map_len; -+ dma_addr_t addr; -+ enum dma_data_direction dir; -+ void *bounce_buf; -+}; -+ - int i3c_master_do_i2c_xfers(struct i3c_master_controller *master, - const struct i2c_msg *xfers, - int nxfers); -@@ -575,6 +595,12 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master, - int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master, - u8 addr); - int i3c_master_do_daa(struct i3c_master_controller *master); -+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr, -+ size_t len, bool force_bounce, -+ enum dma_data_direction dir); -+void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer); -+DEFINE_FREE(i3c_master_dma_unmap_single, void *, -+ if (_T) i3c_master_dma_unmap_single(_T)) - - int i3c_master_set_info(struct i3c_master_controller *master, - const struct i3c_device_info *info); --- -2.43.0 - diff --git a/SPECS/kernel/0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet b/SPECS/kernel/0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet deleted file mode 100644 index c663b76bf..000000000 --- a/SPECS/kernel/0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet +++ /dev/null @@ -1,248 +0,0 @@ -From 78661fcc4bca178d718f5e1834ca6f6bf26c3a06 Mon Sep 17 00:00:00 2001 -From: Vinicius Costa Gomes -Date: Tue, 4 May 2021 13:47:03 -0700 -Subject: [PATCH 02/19] igc: Add support for DMA timestamp for non-PTP packets - -For PTP traffic, timestamp is retrieved from TXSTMP register. -For all other packets, DMA timestamp field of the Transmit -Descriptor Write-back is used. - -This is required to work around the HW time stamp misses in -TXSTAMP register due to the HW limitation, when heavy traffic -is initiated. - -Signed-off-by: Vinicius Costa Gomes -Signed-off-by: Aravindhan Gunasekaran -Signed-off-by: Muhammad Husaini Zulkifli ---- - drivers/net/ethernet/intel/igc/igc.h | 3 + - drivers/net/ethernet/intel/igc/igc_base.h | 2 +- - drivers/net/ethernet/intel/igc/igc_defines.h | 2 + - drivers/net/ethernet/intel/igc/igc_main.c | 21 +++++- - drivers/net/ethernet/intel/igc/igc_ptp.c | 72 ++++++++++++++++++++ - 5 files changed, 98 insertions(+), 2 deletions(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h -index a427f05814c1..b9f846389e75 100644 ---- a/drivers/net/ethernet/intel/igc/igc.h -+++ b/drivers/net/ethernet/intel/igc/igc.h -@@ -559,6 +559,7 @@ enum igc_tx_flags { - IGC_TX_FLAGS_TSTAMP_3 = 0x400, - - IGC_TX_FLAGS_TSTAMP_TIMER_1 = 0x800, -+ IGC_TX_FLAGS_DMA_TSTAMP = 0x200, - }; - - enum igc_boards { -@@ -780,6 +781,8 @@ int igc_ptp_hwtstamp_get(struct net_device *netdev, - int igc_ptp_hwtstamp_set(struct net_device *netdev, - struct kernel_hwtstamp_config *config, - struct netlink_ext_ack *extack); -+void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, -+ struct sk_buff *skb, u64 tstamp); - void igc_ptp_tx_hang(struct igc_adapter *adapter); - void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); - void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); -diff --git a/drivers/net/ethernet/intel/igc/igc_base.h b/drivers/net/ethernet/intel/igc/igc_base.h -index eaf17cd031c3..b05cdfaa5b94 100644 ---- a/drivers/net/ethernet/intel/igc/igc_base.h -+++ b/drivers/net/ethernet/intel/igc/igc_base.h -@@ -18,7 +18,7 @@ union igc_adv_tx_desc { - __le32 olinfo_status; - } read; - struct { -- __le64 rsvd; /* Reserved */ -+ __le64 dma_tstamp; - __le32 nxtseq_seed; - __le32 status; - } wb; -diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h -index 498ba1522ca4..ea673df406a9 100644 ---- a/drivers/net/ethernet/intel/igc/igc_defines.h -+++ b/drivers/net/ethernet/intel/igc/igc_defines.h -@@ -315,6 +315,7 @@ - #define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */ - #define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */ - #define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */ -+#define IGC_TXD_STAT_TS_STAT 0x00000002 /* DMA Timestamp in packet */ - #define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */ - #define IGC_TXD_CMD_IP 0x02000000 /* IP packet */ - #define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */ -@@ -585,6 +586,7 @@ - /* Transmit Scheduling */ - #define IGC_TQAVCTRL_TRANSMIT_MODE_TSN 0x00000001 - #define IGC_TQAVCTRL_PREEMPT_ENA 0x00000002 -+#define IGC_TQAVCTRL_1588_STAT_EN 0x00000004 - #define IGC_TQAVCTRL_ENHANCED_QAV 0x00000008 - #define IGC_TQAVCTRL_FUTSCDDIS 0x00000080 - #define IGC_TQAVCTRL_MIN_FRAG_MASK 0x0000C000 -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 728d7ca5338b..caf7bed4c8ae 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -1556,6 +1556,14 @@ static bool igc_request_tx_tstamp(struct igc_adapter *adapter, struct sk_buff *s - return false; - } - -+static bool igc_is_ptp_packet(struct sk_buff *skb) -+{ -+ __be16 protocol = vlan_get_protocol(skb); -+ -+ /* FIXME: also handle UDP packets */ -+ return protocol == htons(ETH_P_1588); -+} -+ - static int igc_insert_empty_frame(struct igc_ring *tx_ring) - { - struct igc_tx_buffer *empty_info; -@@ -1659,16 +1667,21 @@ static netdev_tx_t igc_xmit_frame_ring(struct sk_buff *skb, - - if (unlikely(test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags) && - skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { -+ struct igc_adapter *adapter = netdev_priv(tx_ring->netdev); -+ bool is_ptp = igc_is_ptp_packet(skb); - unsigned long flags; - u32 tstamp_flags; - - spin_lock_irqsave(&adapter->ptp_tx_lock, flags); -- if (igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { -+ if (is_ptp && igc_request_tx_tstamp(adapter, skb, &tstamp_flags)) { - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - tx_flags |= IGC_TX_FLAGS_TSTAMP | tstamp_flags; - if (skb->sk && - READ_ONCE(skb->sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC) - tx_flags |= IGC_TX_FLAGS_TSTAMP_TIMER_1; -+ } else if (!is_ptp) { -+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; -+ tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; - } else { - adapter->tx_hwtstamp_skipped++; - } -@@ -3170,6 +3183,12 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - if (tx_buffer->type == IGC_TX_BUFFER_TYPE_XSK && - tx_buffer->xsk_pending_ts) - break; -+ if (eop_desc->wb.status & cpu_to_le32(IGC_TXD_STAT_TS_STAT) && -+ tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { -+ u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); -+ -+ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); -+ } - - /* clear next_to_watch to prevent false hangs */ - tx_buffer->next_to_watch = NULL; -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index b7b46d863bee..61fe47c2a5d3 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -446,6 +446,31 @@ static int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter, - return 0; - } - -+static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, -+ struct skb_shared_hwtstamps *hwtstamps, -+ u64 systim) -+{ -+ struct igc_hw *hw = &adapter->hw; -+ u32 sec, nsec; -+ -+ /* FIXME: use a workqueue to read these values to avoid -+ * reading these registers in the hot path. -+ */ -+ nsec = rd32(IGC_SYSTIML); -+ sec = rd32(IGC_SYSTIMH); -+ -+ switch (adapter->hw.mac.type) { -+ case igc_i225: -+ memset(hwtstamps, 0, sizeof(*hwtstamps)); -+ -+ /* HACK */ -+ hwtstamps->hwtstamp = ktime_set(sec, systim & 0xFFFFFFFF); -+ break; -+ default: -+ break; -+ } -+} -+ - /** - * igc_ptp_rx_pktstamp - Retrieve timestamp from Rx packet buffer - * @adapter: Pointer to adapter the packet buffer belongs to -@@ -579,6 +604,7 @@ static void igc_ptp_clear_tx_tstamp(struct igc_adapter *adapter) - static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) - { - struct igc_hw *hw = &adapter->hw; -+ u32 tqavctrl; - int i; - - /* Clear the flags first to avoid new packets to be enqueued -@@ -593,14 +619,26 @@ static void igc_ptp_disable_tx_timestamp(struct igc_adapter *adapter) - /* Now we can clean the pending TX timestamp requests. */ - igc_ptp_clear_tx_tstamp(adapter); - -+ tqavctrl = rd32(IGC_TQAVCTRL); -+ tqavctrl &= ~IGC_TQAVCTRL_1588_STAT_EN; -+ -+ wr32(IGC_TQAVCTRL, tqavctrl); -+ - wr32(IGC_TSYNCTXCTL, 0); - } - - static void igc_ptp_enable_tx_timestamp(struct igc_adapter *adapter) - { - struct igc_hw *hw = &adapter->hw; -+ u32 tqavctrl; - int i; - -+ /* Enable DMA Fetch timestamping */ -+ tqavctrl = rd32(IGC_TQAVCTRL); -+ tqavctrl |= IGC_TQAVCTRL_1588_STAT_EN; -+ -+ wr32(IGC_TQAVCTRL, tqavctrl); -+ - wr32(IGC_TSYNCTXCTL, IGC_TSYNCTXCTL_ENABLED | IGC_TSYNCTXCTL_TXSYNSIG); - - /* Read TXSTMP registers to discard any timestamp previously stored. */ -@@ -834,6 +872,40 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter) - } - } - -+void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, -+ struct sk_buff *skb, u64 tstamp) -+{ -+ struct skb_shared_hwtstamps shhwtstamps; -+ int adjust = 0; -+ -+ if (!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)) -+ return; -+ -+ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); -+ -+ /* FIXME: Use different latencies for DMA timestamps? */ -+ switch (adapter->link_speed) { -+ case SPEED_10: -+ adjust = IGC_I225_TX_LATENCY_10; -+ break; -+ case SPEED_100: -+ adjust = IGC_I225_TX_LATENCY_100; -+ break; -+ case SPEED_1000: -+ adjust = IGC_I225_TX_LATENCY_1000; -+ break; -+ case SPEED_2500: -+ adjust = IGC_I225_TX_LATENCY_2500; -+ break; -+ } -+ -+ shhwtstamps.hwtstamp = -+ ktime_add_ns(shhwtstamps.hwtstamp, adjust); -+ -+ /* Notify the stack and free the skb after we've unlocked */ -+ skb_tstamp_tx(skb, &shhwtstamps); -+} -+ - /** - * igc_ptp_tx_tstamp_event - * @adapter: board private structure --- -2.43.0 - diff --git a/SPECS/kernel/0002-issei-add-firmware-and-host-clients-implementatio.security b/SPECS/kernel/0002-issei-add-firmware-and-host-clients-implementatio.security index 250db1613..73d617832 100644 --- a/SPECS/kernel/0002-issei-add-firmware-and-host-clients-implementatio.security +++ b/SPECS/kernel/0002-issei-add-firmware-and-host-clients-implementatio.security @@ -1,7 +1,7 @@ -From f6896065a2048df5ca9aa66bc01ee1058022ba10 Mon Sep 17 00:00:00 2001 +From e46a110e08da5c96d76e4bb79db4a74a525eff87 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 31 Oct 2024 09:57:32 +0200 -Subject: [PATCH 2/5] issei: add firmware and host clients implementation, +Subject: [PATCH 2/7] issei: add firmware and host clients implementation, finish character device Add the core implementation for firmware and host client @@ -29,12 +29,12 @@ Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-issei | 78 ++++ drivers/misc/issei/Makefile | 2 + - drivers/misc/issei/cdev.c | 229 ++++++++++ - drivers/misc/issei/fw_client.c | 176 +++++++ - drivers/misc/issei/fw_client.h | 51 +++ - drivers/misc/issei/host_client.c | 482 ++++++++++++++++++++ + drivers/misc/issei/cdev.c | 237 ++++++++++ + drivers/misc/issei/fw_client.c | 162 +++++++ + drivers/misc/issei/fw_client.h | 51 ++ + drivers/misc/issei/host_client.c | 489 ++++++++++++++++++++ drivers/misc/issei/host_client.h | 70 +++ - 7 files changed, 1088 insertions(+) + 7 files changed, 1089 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-issei create mode 100644 drivers/misc/issei/fw_client.c create mode 100644 drivers/misc/issei/fw_client.h @@ -43,7 +43,7 @@ Signed-off-by: Alexander Usyskin diff --git a/Documentation/ABI/testing/sysfs-class-issei b/Documentation/ABI/testing/sysfs-class-issei new file mode 100644 -index 000000000000..61af59cd87e1 +index 0000000000000..61af59cd87e1d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-issei @@ -0,0 +1,78 @@ @@ -126,7 +126,7 @@ index 000000000000..61af59cd87e1 + + The version of the firmware client diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile -index 9e3ef22305ac..4e8f6a435a31 100644 +index 9e3ef22305ac9..4e8f6a435a318 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -5,3 +5,5 @@ ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"' @@ -136,14 +136,15 @@ index 9e3ef22305ac..4e8f6a435a31 100644 +issei-objs += fw_client.o +issei-objs += host_client.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c -index 26ac95e5f818..c7e204374eba 100644 +index c265012062c71..4296af754830b 100644 --- a/drivers/misc/issei/cdev.c +++ b/drivers/misc/issei/cdev.c -@@ -7,8 +7,11 @@ - #include - #include +@@ -7,8 +7,12 @@ + #include + #include #include +#include ++#include +#include #include "issei_dev.h" @@ -151,7 +152,7 @@ index 26ac95e5f818..c7e204374eba 100644 #include "cdev.h" struct class *issei_class; -@@ -19,6 +22,225 @@ static dev_t issei_devt; +@@ -19,6 +23,232 @@ static dev_t issei_devt; static DEFINE_MUTEX(issei_minor_lock); static DEFINE_IDR(issei_idr); @@ -160,11 +161,16 @@ index 26ac95e5f818..c7e204374eba 100644 + struct issei_host_client *cl; + struct issei_device *idev; + -+ idev = container_of(inode->i_cdev, struct issei_device, cdev); ++ idev = idr_find(&issei_idr, iminor(inode)); ++ if (!idev) ++ return -ENODEV; ++ get_device(&idev->dev); + + cl = issei_cl_create(idev, fp); -+ if (IS_ERR(cl)) ++ if (IS_ERR(cl)) { ++ put_device(&idev->dev); + return PTR_ERR(cl); ++ } + fp->private_data = cl; + + return nonseekable_open(inode, fp); @@ -173,8 +179,10 @@ index 26ac95e5f818..c7e204374eba 100644 +static int issei_release(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl = fp->private_data; ++ struct issei_device *idev = cl->idev; + + issei_cl_remove(cl); ++ put_device(&idev->dev); + + return 0; +} @@ -196,15 +204,15 @@ index 26ac95e5f818..c7e204374eba 100644 + + switch (cmd) { + case IOCTL_ISSEI_CONNECT_CLIENT: -+ dev_dbg(idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); ++ dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { -+ dev_dbg(idev->dev, "failed to copy data from userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return -EFAULT; + } + @@ -216,16 +224,16 @@ index 26ac95e5f818..c7e204374eba 100644 + return ret; + + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { -+ dev_dbg(idev->dev, "failed to copy data to userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data to userland\n"); + return -EFAULT; + } + break; + + case IOCTL_ISSEI_DISCONNECT_CLIENT: -+ dev_dbg(idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); ++ dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + @@ -253,20 +261,20 @@ index 26ac95e5f818..c7e204374eba 100644 + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.h2f) { -+ dev_dbg(idev->dev, "Write is too big %zu > %zu\n", ++ dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", + length, idev->dma.length.h2f); + return -EFBIG; + } + + buf = memdup_user(ubuf, length); + if (IS_ERR(buf)) { -+ dev_dbg(idev->dev, "failed to copy data from userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return PTR_ERR(buf); + } + @@ -299,13 +307,13 @@ index 26ac95e5f818..c7e204374eba 100644 + return 0; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.f2h) { -+ dev_dbg(idev->dev, "Read is too big %zu > %zu\n", ++ dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", + length, idev->dma.length.f2h); + return -EFBIG; + } @@ -328,7 +336,7 @@ index 26ac95e5f818..c7e204374eba 100644 + +copy: + if (copy_to_user(ubuf, data, data_size)) { -+ dev_dbg(idev->dev, "failed to copy data to userland\n"); ++ dev_dbg(&idev->dev, "failed to copy data to userland\n"); + ret = -EFAULT; + } else { + *offset = 0; @@ -349,7 +357,7 @@ index 26ac95e5f818..c7e204374eba 100644 + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_DONE) { -+ dev_dbg(idev->dev, "Device is in transition\n"); ++ dev_dbg(&idev->dev, "Device is in transition\n"); + return EPOLLERR; + } + @@ -377,7 +385,7 @@ index 26ac95e5f818..c7e204374eba 100644 static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr, char *buf) { -@@ -37,6 +259,13 @@ ATTRIBUTE_GROUPS(issei); +@@ -37,6 +267,13 @@ ATTRIBUTE_GROUPS(issei); static const struct file_operations issei_fops = { .owner = THIS_MODULE, @@ -393,16 +401,15 @@ index 26ac95e5f818..c7e204374eba 100644 static int issei_minor_get(struct issei_device *idev) diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c new file mode 100644 -index 000000000000..5f9ba46bdfe7 +index 0000000000000..43c42f16b9cbc --- /dev/null +++ b/drivers/misc/issei/fw_client.c -@@ -0,0 +1,176 @@ +@@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include +#include +#include -+#include +#include +#include +#include @@ -470,13 +477,8 @@ index 000000000000..5f9ba46bdfe7 + const uuid_t *uuid, u32 mtu, u32 flags) +{ + struct issei_fw_client *fw_cl; -+ struct device *clsdev; + int ret; + -+ clsdev = class_find_device_by_devt(issei_class, idev->cdev.dev); -+ if (!clsdev) -+ return ERR_PTR(-ENODEV); -+ + guard(mutex)(&idev->fw_client_lock); + + fw_cl = kzalloc(sizeof(*fw_cl), GFP_KERNEL); @@ -506,34 +508,26 @@ index 000000000000..5f9ba46bdfe7 + + list_add_tail(&fw_cl->list, &idev->fw_client_list); + -+ ret = sysfs_create_group(&clsdev->kobj, &fw_cl->attr_grp); ++ ret = sysfs_create_group(&idev->dev.kobj, &fw_cl->attr_grp); + if (ret) -+ dev_err(idev->dev, "Attr group for client %pUb failed %d\n", uuid, ret); ++ dev_err(&idev->dev, "Attr group for client %pUb failed %d\n", uuid, ret); + -+ dev_dbg(idev->dev, "FW client %pUb created\n", uuid); -+ put_device(clsdev); ++ dev_dbg(&idev->dev, "FW client %pUb created\n", uuid); + return fw_cl; + +free: + kfree(fw_cl); +err: -+ put_device(clsdev); + return ERR_PTR(ret); +} + +static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_fw_client *fw_cl) +{ -+ struct device *clsdev; -+ + WARN(fw_cl->cl, "Removing connected client!\n"); + -+ dev_dbg(idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); ++ dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); + -+ clsdev = class_find_device_by_devt(issei_class, idev->cdev.dev); -+ if (clsdev) { -+ sysfs_remove_group(&clsdev->kobj, &fw_cl->attr_grp); -+ put_device(clsdev); -+ } ++ sysfs_remove_group(&idev->dev.kobj, &fw_cl->attr_grp); + kfree(fw_cl->attr_grp.name); + list_del(&fw_cl->list); + kfree(fw_cl); @@ -575,7 +569,7 @@ index 000000000000..5f9ba46bdfe7 +} diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h new file mode 100644 -index 000000000000..208fdb235b1b +index 0000000000000..208fdb235b1b1 --- /dev/null +++ b/drivers/misc/issei/fw_client.h @@ -0,0 +1,51 @@ @@ -632,10 +626,10 @@ index 000000000000..208fdb235b1b +#endif /* _ISSEI_FW_CLIENT_H_ */ diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c new file mode 100644 -index 000000000000..1eaf33df1da3 +index 0000000000000..6b8dbe566f5f3 --- /dev/null +++ b/drivers/misc/issei/host_client.c -@@ -0,0 +1,482 @@ +@@ -0,0 +1,489 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include @@ -650,13 +644,7 @@ index 000000000000..1eaf33df1da3 +#include "host_client.h" +#include "fw_client.h" + -+/** -+ * issei_cl_fw_id - fw client id in safe way -+ * @cl: host client -+ * -+ * Return: fw client id or 0 if client is not connected -+ */ -+static inline u8 issei_cl_fw_id(const struct issei_host_client *cl) ++static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) +{ + return cl->fw_cl ? cl->fw_cl->id : 0; +} @@ -665,17 +653,20 @@ index 000000000000..1eaf33df1da3 + +#define cl_dbg(_dev_, _cl_, format, arg...) { \ + struct issei_host_client *_l_cl_ = _cl_; \ -+ dev_dbg((_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, issei_cl_fw_id(_l_cl_), ##arg); \ ++ dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, \ ++ __issei_cl_fw_id(_l_cl_), ##arg); \ +} + +#define cl_warn(_dev_, _cl_, format, arg...) { \ + struct issei_host_client *_l_cl_ = _cl_; \ -+ dev_warn((_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, issei_cl_fw_id(_l_cl_), ##arg); \ ++ dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, \ ++ __issei_cl_fw_id(_l_cl_), ##arg); \ +} + +#define cl_err(_dev_, _cl_, format, arg...) { \ + struct issei_host_client *_l_cl_ = _cl_; \ -+ dev_err((_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, issei_cl_fw_id(_l_cl_), ##arg); \ ++ dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, (_l_cl_)->id, \ ++ __issei_cl_fw_id(_l_cl_), ##arg); \ +} + +static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf) @@ -758,6 +749,13 @@ index 000000000000..1eaf33df1da3 + __issei_cl_release_rbuf(cl); +} + ++/** ++ * issei_cl_create - create the host client ++ * @idev: issei device ++ * @fp: file pointer to associate with host client ++ * ++ * Return: client pointer on success, ERR_PTR on error ++ */ +struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp) +{ + struct issei_host_client *cl; @@ -766,7 +764,7 @@ index 000000000000..1eaf33df1da3 + guard(mutex)(&idev->host_client_lock); + + if (idev->host_client_count == ISSEI_HOST_CLIENTS_MAX) { -+ dev_err(idev->dev, "Maximum open clients %d is reached.", ISSEI_HOST_CLIENTS_MAX); ++ dev_err(&idev->dev, "Maximum open clients %d is reached.", ISSEI_HOST_CLIENTS_MAX); + return ERR_PTR(-EMFILE); + } + @@ -792,6 +790,10 @@ index 000000000000..1eaf33df1da3 + return cl; +} + ++/** ++ * issei_cl_remove - disconnect and free the host client ++ * @cl: host client ++ */ +void issei_cl_remove(struct issei_host_client *cl) +{ + struct issei_device *idev; @@ -1044,8 +1046,7 @@ index 000000000000..1eaf33df1da3 + +/** + * issei_cl_read - read data from firmware to provided buffer -+ * @idev: issei device -+ * @length: buffer length ++ * @cl: host client + * @buf: buffer to store data + * @buf_size: maximum buffer size passed and actual buffer size on return + * @@ -1120,7 +1121,7 @@ index 000000000000..1eaf33df1da3 +} diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h new file mode 100644 -index 000000000000..9f081bed190d +index 0000000000000..9f081bed190d2 --- /dev/null +++ b/drivers/misc/issei/host_client.h @@ -0,0 +1,70 @@ diff --git a/SPECS/kernel/0002-macintosh-mac_hid-fix-race-condition-in-mac_hid_togg.patch b/SPECS/kernel/0002-macintosh-mac_hid-fix-race-condition-in-mac_hid_togg.patch deleted file mode 100644 index 54abdf824..000000000 --- a/SPECS/kernel/0002-macintosh-mac_hid-fix-race-condition-in-mac_hid_togg.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 6d30ba14d485c6fac4eb518879b8cca0060e9cc0 Mon Sep 17 00:00:00 2001 -From: Long Li -Date: Tue, 19 Aug 2025 17:10:35 +0800 -Subject: [PATCH 02/51] macintosh/mac_hid: fix race condition in - mac_hid_toggle_emumouse - -The following warning appears when running syzkaller, and this issue also -exists in the mainline code. - - ------------[ cut here ]------------ - list_add double add: new=ffffffffa57eee28, prev=ffffffffa57eee28, next=ffffffffa5e63100. - WARNING: CPU: 0 PID: 1491 at lib/list_debug.c:35 __list_add_valid_or_report+0xf7/0x130 - Modules linked in: - CPU: 0 PID: 1491 Comm: syz.1.28 Not tainted 6.6.0+ #3 - Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.0-0-gd239552ce722-prebuilt.qemu.org 04/01/2014 - RIP: 0010:__list_add_valid_or_report+0xf7/0x130 - RSP: 0018:ff1100010dfb7b78 EFLAGS: 00010282 - RAX: 0000000000000000 RBX: ffffffffa57eee18 RCX: ffffffff97fc9817 - RDX: 0000000000040000 RSI: ffa0000002383000 RDI: 0000000000000001 - RBP: ffffffffa57eee28 R08: 0000000000000001 R09: ffe21c0021bf6f2c - R10: 0000000000000001 R11: 6464615f7473696c R12: ffffffffa5e63100 - R13: ffffffffa57eee28 R14: ffffffffa57eee28 R15: ff1100010dfb7d48 - FS: 00007fb14398b640(0000) GS:ff11000119600000(0000) knlGS:0000000000000000 - CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 - CR2: 0000000000000000 CR3: 000000010d096005 CR4: 0000000000773ef0 - DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 - DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 - PKRU: 80000000 - Call Trace: - - input_register_handler+0xb3/0x210 - mac_hid_start_emulation+0x1c5/0x290 - mac_hid_toggle_emumouse+0x20a/0x240 - proc_sys_call_handler+0x4c2/0x6e0 - new_sync_write+0x1b1/0x2d0 - vfs_write+0x709/0x950 - ksys_write+0x12a/0x250 - do_syscall_64+0x5a/0x110 - entry_SYSCALL_64_after_hwframe+0x78/0xe2 - -The WARNING occurs when two processes concurrently write to the mac-hid -emulation sysctl, causing a race condition in mac_hid_toggle_emumouse(). -Both processes read old_val=0, then both try to register the input handler, -leading to a double list_add of the same handler. - - CPU0 CPU1 - ------------------------- ------------------------- - vfs_write() //write 1 vfs_write() //write 1 - proc_sys_write() proc_sys_write() - mac_hid_toggle_emumouse() mac_hid_toggle_emumouse() - old_val = *valp // old_val=0 - old_val = *valp // old_val=0 - mutex_lock_killable() - proc_dointvec() // *valp=1 - mac_hid_start_emulation() - input_register_handler() - mutex_unlock() - mutex_lock_killable() - proc_dointvec() - mac_hid_start_emulation() - input_register_handler() //Trigger Warning - mutex_unlock() - -Fix this by moving the old_val read inside the mutex lock region. - -Fixes: 99b089c3c38a ("Input: Mac button emulation - implement as an input filter") -Signed-off-by: Long Li -Signed-off-by: Madhavan Srinivasan -Link: https://patch.msgid.link/20250819091035.2263329-1-leo.lilong@huaweicloud.com ---- - drivers/macintosh/mac_hid.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c -index 369d72f59b3c..06fd910b3fd1 100644 ---- a/drivers/macintosh/mac_hid.c -+++ b/drivers/macintosh/mac_hid.c -@@ -187,13 +187,14 @@ static int mac_hid_toggle_emumouse(const struct ctl_table *table, int write, - void *buffer, size_t *lenp, loff_t *ppos) - { - int *valp = table->data; -- int old_val = *valp; -+ int old_val; - int rc; - - rc = mutex_lock_killable(&mac_hid_emumouse_mutex); - if (rc) - return rc; - -+ old_val = *valp; - rc = proc_dointvec(table, write, buffer, lenp, ppos); - - if (rc == 0 && write && *valp != old_val) { --- -2.43.0 - diff --git a/SPECS/kernel/0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu b/SPECS/kernel/0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu deleted file mode 100644 index a0e2e2c79..000000000 --- a/SPECS/kernel/0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu +++ /dev/null @@ -1,171 +0,0 @@ -From 9e46b764cbbd8def4373a51b40bf7abd62c736e3 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Fri, 10 Oct 2025 18:13:01 +0800 -Subject: [PATCH 2/2] media: i2c: max9x: uniform serdes driver compilation - -Tracked-On: #JILCNT-701 -Signed-off-by: hepengpx ---- - drivers/media/i2c/isx031.c | 4 ++-- - drivers/media/i2c/max9x/max9295.c | 9 ++++----- - drivers/media/i2c/max9x/max9296.c | 2 +- - drivers/media/i2c/max9x/serdes.c | 22 ++++++---------------- - 4 files changed, 13 insertions(+), 24 deletions(-) - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index e613cdb06b83..2d669e964b08 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -370,7 +370,7 @@ static int isx031_set_driver_mode(struct isx031 *isx031) - if (mode < 0) - return mode; - -- ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg_retry(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); - return ret; - } - -@@ -411,7 +411,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return ret; - } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -- mode); -+ (u32)mode); - if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", - cur_mode, mode); -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index a5bacc3684a9..8fbd4215e110 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -177,7 +177,7 @@ static int max9295_setup_gpio(struct max9x_common *common) - { - struct device *dev = common->dev; - int ret; -- struct max9x_gpio_pdata *gpio_pdata; -+ struct max9x_gpio_pdata *gpio_pdata = NULL; - - if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -@@ -697,15 +697,14 @@ static int max9295_remap_reset(struct max9x_common *common) - struct device *dev = common->dev; - struct max9x_pdata *pdata = dev->platform_data; - u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -- common->client->addr; -+ common->client->addr; - u32 virt_addr = common->client->addr; - - dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, - phys_addr); - -- TRY(ret, regmap_update_bits( -- common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -- FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ TRY(ret, regmap_update_bits(common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); - - return 0; - } -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index 41074d60cc01..a28aa190364d 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -730,7 +730,7 @@ static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned i - link_cfg = MAX9296_LINK_B; - else { - dev_err(dev, "No link was detected"); -- return -1; -+ return -EINVAL; - } - - dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index fce930a1fdea..8c1db87ed8c2 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -133,7 +133,7 @@ static const struct of_device_id max9x_of_match[] = { - MODULE_DEVICE_TABLE(of, max9x_of_match); - - static const struct i2c_device_id max9x_id[] = { -- { "max9x", MAX9296 }, -+ { "max9x", 0 }, - { "max9296", MAX9296 }, - { "max96724", MAX96724 }, - { "max9295", MAX9295 }, -@@ -841,6 +841,7 @@ void max9x_destroy(struct max9x_common *common) - /* unregister devices? */ - - v4l2_async_unregister_subdev(&common->v4l.sd); -+ v4l2_subdev_cleanup(&common->v4l.sd); - media_entity_cleanup(&common->v4l.sd.entity); - - i2c_mux_del_adapters(common->muxc); -@@ -1483,10 +1484,7 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); - -- if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -- return &common->v4l.ffmts[fmt->pad]; -- -- return ERR_PTR(-EINVAL); -+ return &common->v4l.ffmts[fmt->pad]; - } - - static int max9x_get_fmt(struct v4l2_subdev *sd, -@@ -1562,7 +1560,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - for_each_active_route(&state->routing, route) { - if (route->source_pad != pad) - continue; -- if (route->sink_pad >= common->v4l.num_pads) { -+ if (unlikely(route->sink_pad >= common->v4l.num_pads)) { - ret = -EINVAL; - dev_err(common->dev, "Found invalid route sink_pad!"); - goto out_unlock; -@@ -2092,7 +2090,7 @@ int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) - struct device *dev = common->dev; - int ret; - -- if (link_id >= common->num_serial_links) -+ if (unlikely(link_id >= common->num_serial_links)) - return 0; - - serial_link = &common->serial_link[link_id]; -@@ -2413,13 +2411,9 @@ static int max9x_parse_subdev_pdata(struct max9x_common *common, - int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(10000); - -- dev_dbg(common->dev, "try to select %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2433,7 +2427,7 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - usleep_range(1000, 1050); - - if (time_is_before_jiffies(timeout)) { -- dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ dev_warn(common->dev, "select %d TIMEOUT", chan_id); - return -ETIMEDOUT; - } - } while (1); -@@ -2451,12 +2445,8 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - -- dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - --- -2.43.0 - diff --git a/SPECS/kernel/0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security b/SPECS/kernel/0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security new file mode 100644 index 000000000..cbfae4dc6 --- /dev/null +++ b/SPECS/kernel/0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security @@ -0,0 +1,60 @@ +From 2a787d0659de0d74530788eeb3d0f1b5f2997c97 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Wed, 10 Feb 2021 12:14:55 +0200 +Subject: [PATCH 2/8] mei: bus: add api to query capabilities of ME clients + +Add api to query capabilities of ME clients. +Use bit 0 in bitmask to conway VTag support. + +Co-developed-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/bus.c | 17 +++++++++++++++++ + include/linux/mei_cl_bus.h | 3 +++ + 2 files changed, 20 insertions(+) + +diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c +index abded7457894d..58893e860209b 100644 +--- a/drivers/misc/mei/bus.c ++++ b/drivers/misc/mei/bus.c +@@ -640,6 +640,23 @@ bool mei_cldev_enabled(const struct mei_cl_device *cldev) + } + EXPORT_SYMBOL_GPL(mei_cldev_enabled); + ++/** ++ * mei_cldev_get_capabilities - obtain client capabilities ++ * ++ * @cldev: mei client device ++ * ++ * Return: client capabilities bitmap ++ */ ++u32 mei_cldev_get_capabilities(const struct mei_cl_device *cldev) ++{ ++ u32 cap = 0; ++ ++ if (!mei_cl_vt_support_check(cldev->cl)) ++ cap |= MEI_CLDEV_CAPABILITY_VTAG; ++ ++ return cap; ++} ++ + /** + * mei_cl_bus_module_get - acquire module of the underlying + * hw driver. +diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h +index a82755e1fc40a..709c91489b2a3 100644 +--- a/include/linux/mei_cl_bus.h ++++ b/include/linux/mei_cl_bus.h +@@ -115,6 +115,9 @@ int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, + u8 mei_cldev_ver(const struct mei_cl_device *cldev); + size_t mei_cldev_mtu(const struct mei_cl_device *cldev); + ++#define MEI_CLDEV_CAPABILITY_VTAG BIT(0) ++u32 mei_cldev_get_capabilities(const struct mei_cl_device *cldev); ++ + void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev); + void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data); + +-- +2.43.0 + diff --git a/SPECS/kernel/0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet b/SPECS/kernel/0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet new file mode 100644 index 000000000..a44f9313b --- /dev/null +++ b/SPECS/kernel/0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet @@ -0,0 +1,33 @@ +From 048939bd5fb36e11f912ed5fbe412cb35d6a6e3b Mon Sep 17 00:00:00 2001 +From: "Tan, Tee Min" +Date: Mon, 25 Jun 2018 10:34:14 +0800 +Subject: [PATCH 02/18] net: stmmac: Bugfix on stmmac_interrupt() for WOL + +Modify pm_wakeup_event to pm_wakeup_hard_event. + +With the newly introduced pm_wakeup_hard_event function, +WOL only able to functions properly with using this new +function instead of pm_wakeup_event. + +Signed-off-by: Tan, Tee Min +Signed-off-by: Voon Weifeng +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 0dd17179c85d2..8b9dbf6449f1c 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -5988,7 +5988,7 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv) + queues_count = (rx_cnt > tx_cnt) ? rx_cnt : tx_cnt; + + if (priv->irq_wake) +- pm_wakeup_event(priv->device, 0); ++ pm_wakeup_hard_event(priv->device); + + if (priv->dma_cap.estsel) + stmmac_est_irq_status(priv, priv, priv->dev, +-- +2.43.0 + diff --git a/SPECS/kernel/0002-patch-staging-add-ipu7-isys-reset-code.ipu b/SPECS/kernel/0002-patch-staging-add-ipu7-isys-reset-code.ipu deleted file mode 100644 index 3fcaaa1fc..000000000 --- a/SPECS/kernel/0002-patch-staging-add-ipu7-isys-reset-code.ipu +++ /dev/null @@ -1,824 +0,0 @@ -From 5b41ef3bcf3d8c0c5e027f84f97e1122c1f4ce2c Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 11:46:21 +0800 -Subject: [PATCH 02/21] patch: staging add ipu7 isys reset code - -Adding IPU7 isys reset code macro when -CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET is enabled. - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/Kconfig | 10 + - drivers/staging/media/ipu7/ipu7-isys-queue.c | 379 ++++++++++++++++++- - drivers/staging/media/ipu7/ipu7-isys-queue.h | 3 + - drivers/staging/media/ipu7/ipu7-isys-video.c | 101 +++++ - drivers/staging/media/ipu7/ipu7-isys-video.h | 8 + - drivers/staging/media/ipu7/ipu7-isys.c | 16 + - drivers/staging/media/ipu7/ipu7-isys.h | 16 + - 7 files changed, 532 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig -index 7d831ba7501d..c4eee7c3e6d7 100644 ---- a/drivers/staging/media/ipu7/Kconfig -+++ b/drivers/staging/media/ipu7/Kconfig -@@ -17,3 +17,13 @@ config VIDEO_INTEL_IPU7 - - To compile this driver, say Y here! It contains 2 modules - - intel_ipu7 and intel_ipu7_isys. -+ -+config VIDEO_INTEL_IPU7_ISYS_RESET -+ bool "IPU7 ISYS RESET" -+ depends on VIDEO_INTEL_IPU7 -+ default n -+ help -+ This option enables IPU7 ISYS reset feature to support -+ HDMI-MIPI converter hot-plugging. -+ -+ If doubt, say N here. -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c -index 7046c29141f8..1f5fb7d20d81 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c -@@ -11,6 +11,9 @@ - #include - #include - #include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif - - #include - #include -@@ -26,6 +29,9 @@ - #include "ipu7-isys-csi2-regs.h" - #include "ipu7-isys-video.h" - #include "ipu7-platform-regs.h" -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include "ipu7-cpd.h" -+#endif - - #define IPU_MAX_FRAME_COUNTER (U8_MAX + 1) - -@@ -225,6 +231,16 @@ static int buffer_list_get(struct ipu7_isys_stream *stream, - ib = list_last_entry(&aq->incoming, - struct ipu7_isys_buffer, head); - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ -+ if (av->skipframe) { -+ atomic_set(&ib->skipframe_flag, 1); -+ av->skipframe--; -+ } else { -+ atomic_set(&ib->skipframe_flag, 0); -+ } -+#endif - dev_dbg(dev, "buffer: %s: buffer %u\n", - ipu7_isys_queue_to_video(aq)->vdev.name, - ipu7_isys_buffer_to_vb2_buffer(ib)->index); -@@ -379,6 +395,18 @@ static void buf_queue(struct vb2_buffer *vb) - return; - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&av->isys->reset_mutex); -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ dev_dbg(dev, "in reset, adding to incoming\n"); -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ /* ip may be cleared in ipu reset */ -+ stream = av->stream; -+#endif - mutex_lock(&stream->mutex); - - if (stream->nr_streaming != stream->nr_queues) { -@@ -480,6 +508,10 @@ static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq) - static void return_buffers(struct ipu7_isys_queue *aq, - enum vb2_buffer_state state) - { -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ bool need_reset = false; -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+#endif - struct ipu7_isys_buffer *ib; - struct vb2_buffer *vb; - unsigned long flags; -@@ -501,6 +533,9 @@ static void return_buffers(struct ipu7_isys_queue *aq, - vb2_buffer_done(vb, state); - - spin_lock_irqsave(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ need_reset = true; -+#endif - } - - while (!list_empty(&aq->incoming)) { -@@ -516,6 +551,14 @@ static void return_buffers(struct ipu7_isys_queue *aq, - } - - spin_unlock_irqrestore(&aq->lock, flags); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ -+ if (need_reset) { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = true; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+#endif - } - - static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av) -@@ -594,6 +637,9 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) - - out: - mutex_unlock(&stream->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->start_streaming = 1; -+#endif - - return 0; - -@@ -614,17 +660,292 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) - return ret; - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static void reset_stop_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct ipu7_isys_stream *stream = av->stream; -+ struct ipu7_isys_buffer *ib; -+ struct vb2_buffer *vb; -+ unsigned long flags; -+ -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ mutex_lock(&stream->mutex); -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ while (!list_empty(&aq->active)) { -+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer, -+ head); -+ vb = ipu7_isys_buffer_to_vb2_buffer(ib); -+ -+ list_del(&ib->head); -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ -+ spin_lock_irqsave(&aq->lock, flags); -+ } -+ spin_unlock_irqrestore(&aq->lock, flags); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+ -+static int reset_start_streaming(struct ipu7_isys_video *av) -+{ -+ struct ipu7_isys_queue *aq = &av->aq; -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ struct ipu7_isys_buffer_list __bl, *bl = NULL; -+ struct ipu7_isys_stream *stream; -+ struct media_entity *source_entity = NULL; -+ int nr_queues; -+ int ret; -+ -+ dev_dbg(dev, "%s: reset start streaming\n", av->vdev.name); -+ -+ av->skipframe = 1; -+ -+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues); -+ if (ret < 0) { -+ dev_dbg(dev, "failed to setup video\n"); -+ goto out_return_buffers; -+ } -+ -+ ret = ipu7_isys_link_fmt_validate(aq); -+ if (ret) { -+ dev_dbg(dev, -+ "%s: link format validation failed (%d)\n", -+ av->vdev.name, ret); -+ goto out_pipeline_stop; -+ } -+ -+ stream = av->stream; -+ mutex_lock(&stream->mutex); -+ if (!stream->nr_streaming) { -+ ret = ipu7_isys_video_prepare_stream(av, source_entity, -+ nr_queues); -+ if (ret) { -+ mutex_unlock(&stream->mutex); -+ goto out_pipeline_stop; -+ } -+ } -+ -+ stream->nr_streaming++; -+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming, -+ stream->nr_queues); -+ -+ list_add(&aq->node, &stream->queues); -+ -+ if (stream->nr_streaming != stream->nr_queues) -+ goto out; -+ -+ bl = &__bl; -+ int retry = 5; -+ while (retry--) { -+ ret = buffer_list_get(stream, bl); -+ if (ret < 0) { -+ dev_dbg(dev, "wait for incoming buffer, retry %d\n", retry); -+ usleep_range(100000, 110000); -+ continue; -+ } -+ break; -+ } -+ -+ /* -+ * In reset start streaming and no buffer available, -+ * it is considered that gstreamer has been closed, -+ * and reset start is no needed, not driver bug. -+ */ -+ if (ret) { -+ dev_dbg(dev, "reset start: no buffer available, gstreamer colsed\n"); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ -+ goto out_stream_start; -+ } -+ -+ ret = ipu7_isys_fw_open(av->isys); -+ if (ret) -+ goto out_stream_start; -+ -+ ipu7_isys_setup_hw(av->isys); -+ -+ ret = ipu7_isys_stream_start(av, bl, false); -+ if (ret) -+ goto out_isys_fw_close; -+ -+out: -+ mutex_unlock(&stream->mutex); -+ av->start_streaming = 1; -+ return 0; -+ -+out_isys_fw_close: -+ ipu7_isys_fw_close(av->isys); -+ -+out_stream_start: -+ list_del(&aq->node); -+ stream->nr_streaming--; -+ mutex_unlock(&stream->mutex); -+ -+out_pipeline_stop: -+ ipu7_isys_stream_cleanup(av); -+ -+out_return_buffers: -+ return_buffers(aq, VB2_BUF_STATE_QUEUED); -+ av->start_streaming = 0; -+ dev_dbg(dev, "%s: reset start streaming failed!\n", av->vdev.name); -+ return ret; -+} -+ -+static int ipu_isys_reset(struct ipu7_isys_video *self_av, -+ struct ipu7_isys_stream *self_stream) -+{ -+ struct ipu7_isys *isys = self_av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ struct ipu7_isys_video *av = NULL; -+ struct ipu7_isys_stream *stream = NULL; -+ struct device *dev = &adev->auxdev.dev; -+ int ret = 0; -+ int i, j; -+ int has_streaming = 0; -+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata = -+ &isys->pdata->ipdata->csi2; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&isys->reset_mutex); -+ return 0; -+ } -+ isys->state |= RESET_STATE_IN_RESET; -+ dev_dbg(dev, "%s: %s\n", __func__, self_av->vdev.name); -+ -+ while (isys->state & RESET_STATE_IN_STOP_STREAMING) { -+ dev_dbg(dev, "isys reset: %s: wait for stop\n", -+ self_av->vdev.name); -+ mutex_unlock(&isys->reset_mutex); -+ usleep_range(10000, 11000); -+ mutex_lock(&isys->reset_mutex); -+ } -+ -+ mutex_unlock(&isys->reset_mutex); -+ -+ dev_dbg(dev, "reset stop streams\n"); -+ for (i = 0; i < csi2_pdata->nports; i++) { -+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) { -+ av = &isys->csi2[i].av[j]; -+ if (av == self_av) -+ continue; -+ -+ stream = av->stream; -+ if (!stream || stream == self_stream) -+ continue; -+ -+ if (!stream->streaming && !stream->nr_streaming) -+ continue; -+ -+ av->reset = true; -+ has_streaming = true; -+ reset_stop_streaming(av); -+ } -+ } -+ -+ if (!has_streaming) -+ goto end_of_reset; -+ -+ ipu7_cleanup_fw_msg_bufs(isys); -+ -+ dev_dbg(dev, "reset start streams\n"); -+ -+ for (j = 0; j < csi2_pdata->nports; j++) { -+ for (i = 0; i < IPU7_NR_OF_CSI2_SRC_PADS; i++) { -+ av = &isys->csi2[j].av[i]; -+ if (!av->reset) -+ continue; -+ -+ av->reset = false; -+ ret = reset_start_streaming(av); -+ if (ret) -+ break; -+ } -+ if (ret) -+ break; -+ } -+ -+end_of_reset: -+ mutex_lock(&isys->reset_mutex); -+ isys->state &= ~RESET_STATE_IN_RESET; -+ mutex_unlock(&isys->reset_mutex); -+ dev_dbg(dev, "reset done\n"); -+ -+ return 0; -+} -+ - static void stop_streaming(struct vb2_queue *q) - { - struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); - struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); - struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ int times = 5; -+ -+ struct device *dev = &av->isys->adev->auxdev.dev; -+ bool need_reset; -+ -+ dev_dbg(dev, "stop: %s: enter\n", av->vdev.name); -+ -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(dev, "stop: %s: wait for reset or stop, isys->state = %d\n", -+ av->vdev.name, av->isys->state); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ -+ if (!av->start_streaming) { -+ mutex_unlock(&av->isys->reset_mutex); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ return; -+ } -+ -+ av->isys->state |= RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ while (!list_empty(&aq->active) && times--) { -+ usleep_range(30000, 31000); -+ } -+ -+ stream = av->stream; -+ if (!stream) { -+ dev_err(dev, "stop: %s: ip cleard!\n", av->vdev.name); -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ mutex_unlock(&av->isys->reset_mutex); -+ return; -+ } - - mutex_lock(&stream->mutex); - mutex_lock(&av->isys->stream_mutex); - if (stream->nr_streaming == stream->nr_queues && stream->streaming) -- ipu7_isys_video_set_streaming(av, 0, NULL); -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); - mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(dev, "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } - - stream->nr_streaming--; - list_del(&aq->node); -@@ -637,7 +958,58 @@ static void stop_streaming(struct vb2_queue *q) - return_buffers(aq, VB2_BUF_STATE_ERROR); - - ipu7_isys_fw_close(av->isys); -+ -+ av->start_streaming = 0; -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->state &= ~RESET_STATE_IN_STOP_STREAMING; -+ need_reset = av->isys->need_reset; -+ mutex_unlock(&av->isys->reset_mutex); -+ -+ if (need_reset) { -+ if (!stream->nr_streaming) { -+ ipu_isys_reset(av, stream); -+ } else { -+ mutex_lock(&av->isys->reset_mutex); -+ av->isys->need_reset = false; -+ mutex_unlock(&av->isys->reset_mutex); -+ } -+ } -+ -+ dev_dbg(dev, "stop: %s: exit\n", av->vdev.name); - } -+#else -+static void stop_streaming(struct vb2_queue *q) -+{ -+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q); -+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct ipu7_isys_stream *stream = av->stream; -+ int ret = 0; -+ -+ mutex_lock(&stream->mutex); -+ mutex_lock(&av->isys->stream_mutex); -+ if (stream->nr_streaming == stream->nr_queues && stream->streaming) -+ ret = ipu7_isys_video_set_streaming(av, 0, NULL); -+ mutex_unlock(&av->isys->stream_mutex); -+ if (ret) { -+ dev_err(&av->isys->adev->auxdev.dev, -+ "stop: video set streaming failed\n"); -+ mutex_unlock(&stream->mutex); -+ return; -+ } -+ -+ stream->nr_streaming--; -+ list_del(&aq->node); -+ stream->streaming = 0; -+ -+ mutex_unlock(&stream->mutex); -+ -+ ipu7_isys_stream_cleanup(av); -+ -+ return_buffers(aq, VB2_BUF_STATE_ERROR); -+ -+ ipu7_isys_fw_close(av->isys); -+} -+#endif - - static unsigned int - get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time) -@@ -719,6 +1091,11 @@ static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib) - * to the userspace when it is de-queued - */ - atomic_set(&ib->str2mmio_flag, 0); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ } else if (atomic_read(&ib->skipframe_flag)) { -+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); -+ atomic_set(&ib->skipframe_flag, 0); -+#endif - } else { - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); - } -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.h b/drivers/staging/media/ipu7/ipu7-isys-queue.h -index 0cb08a38f756..5a909c3a78d2 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.h -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.h -@@ -31,6 +31,9 @@ struct ipu7_isys_queue { - struct ipu7_isys_buffer { - struct list_head head; - atomic_t str2mmio_flag; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ atomic_t skipframe_flag; -+#endif - }; - - struct ipu7_isys_video_buffer { -diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c -index 173afd405d9b..b3d337fe786b 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-video.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-video.c -@@ -18,6 +18,9 @@ - #include - #include - #include -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#include -+#endif - - #include - #include -@@ -90,9 +93,43 @@ const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = { - - static int video_open(struct file *file) - { -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct ipu7_isys_video *av = video_drvdata(file); -+ struct ipu7_isys *isys = av->isys; -+ struct ipu7_bus_device *adev = isys->adev; -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ dev_warn(&adev->auxdev.dev, "isys power cycle required\n"); -+ return -EIO; -+ } -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif - return v4l2_fh_open(file); - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+static int video_release(struct file *file) -+{ -+ struct ipu7_isys_video *av = video_drvdata(file); -+ -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: enter\n", av->vdev.name); -+ mutex_lock(&av->isys->reset_mutex); -+ while (av->isys->state & RESET_STATE_IN_RESET) { -+ mutex_unlock(&av->isys->reset_mutex); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "release: %s: wait for reset\n", av->vdev.name); -+ usleep_range(10000, 11000); -+ mutex_lock(&av->isys->reset_mutex); -+ } -+ mutex_unlock(&av->isys->reset_mutex); -+ return vb2_fop_release(file); -+} -+ -+#endif - const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat) - { - unsigned int i; -@@ -589,7 +626,11 @@ static void stop_streaming_firmware(struct ipu7_isys_video *av) - } - - tout = wait_for_completion_timeout(&stream->stream_stop_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else - FW_CALL_TIMEOUT_JIFFIES); -+#endif - if (!tout) - dev_warn(dev, "stream stop time out\n"); - else if (stream->error) -@@ -614,7 +655,11 @@ static void close_streaming_firmware(struct ipu7_isys_video *av) - } - - tout = wait_for_completion_timeout(&stream->stream_close_completion, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ FW_CALL_TIMEOUT_JIFFIES_RESET); -+#else - FW_CALL_TIMEOUT_JIFFIES); -+#endif - if (!tout) - dev_warn(dev, "stream close time out\n"); - else if (stream->error) -@@ -622,6 +667,12 @@ static void close_streaming_firmware(struct ipu7_isys_video *av) - else - dev_dbg(dev, "close stream: complete\n"); - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ stream->last_sequence = atomic_read(&stream->sequence); -+ dev_dbg(dev, "ip->last_sequence = %d\n", -+ stream->last_sequence); -+ -+#endif - put_stream_opened(av); - } - -@@ -636,7 +687,18 @@ int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av, - return -EINVAL; - - stream->nr_queues = nr_queues; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ if (av->isys->state & RESET_STATE_IN_RESET) { -+ atomic_set(&stream->sequence, stream->last_sequence); -+ dev_dbg(&av->isys->adev->auxdev.dev, -+ "atomic_set : stream->last_sequence = %d\n", -+ stream->last_sequence); -+ } else { -+ atomic_set(&stream->sequence, 0); -+ } -+#else - atomic_set(&stream->sequence, 0); -+#endif - atomic_set(&stream->buf_id, 0); - - stream->seq_index = 0; -@@ -897,7 +959,11 @@ static const struct v4l2_file_operations isys_fops = { - .unlocked_ioctl = video_ioctl2, - .mmap = vb2_fop_mmap, - .open = video_open, -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ .release = video_release, -+#else - .release = vb2_fop_release, -+#endif - }; - - int ipu7_isys_fw_open(struct ipu7_isys *isys) -@@ -936,6 +1002,35 @@ int ipu7_isys_fw_open(struct ipu7_isys *isys) - return ret; - } - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+void ipu7_isys_fw_close(struct ipu7_isys *isys) -+{ -+ int ret = 0; -+ -+ mutex_lock(&isys->mutex); -+ isys->ref_count--; -+ if (!isys->ref_count) { -+ /* need reset when fw close is abnormal */ -+ ret = ipu7_fw_isys_close(isys); -+ if (ret) { -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = true; -+ mutex_unlock(&isys->reset_mutex); -+ } -+ } -+ -+ mutex_unlock(&isys->mutex); -+ -+ mutex_lock(&isys->reset_mutex); -+ if (isys->need_reset) { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put_sync(&isys->adev->auxdev.dev); -+ } else { -+ mutex_unlock(&isys->reset_mutex); -+ pm_runtime_put(&isys->adev->auxdev.dev); -+ } -+} -+#else - void ipu7_isys_fw_close(struct ipu7_isys *isys) - { - mutex_lock(&isys->mutex); -@@ -948,6 +1043,7 @@ void ipu7_isys_fw_close(struct ipu7_isys *isys) - mutex_unlock(&isys->mutex); - pm_runtime_put(&isys->adev->auxdev.dev); - } -+#endif - - int ipu7_isys_setup_video(struct ipu7_isys_video *av, - struct media_entity **source_entity, int *nr_queues) -@@ -1082,6 +1178,11 @@ int ipu7_isys_video_init(struct ipu7_isys_video *av) - - __ipu_isys_vidioc_try_fmt_vid_cap(av, &format); - av->pix_fmt = format.fmt.pix; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ av->reset = false; -+ av->skipframe = 0; -+ av->start_streaming = 0; -+#endif - - set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags); - video_set_drvdata(&av->vdev, av); -diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.h b/drivers/staging/media/ipu7/ipu7-isys-video.h -index 1ac1787fabef..e6d1da2b7b47 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-video.h -+++ b/drivers/staging/media/ipu7/ipu7-isys-video.h -@@ -53,6 +53,9 @@ struct ipu7_isys_stream { - struct mutex mutex; - struct media_entity *source_entity; - atomic_t sequence; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ int last_sequence; -+#endif - atomic_t buf_id; - unsigned int seq_index; - struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF]; -@@ -89,6 +92,11 @@ struct ipu7_isys_video { - unsigned int streaming; - u8 vc; - u8 dt; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ unsigned int reset; -+ unsigned int skipframe; -+ unsigned int start_streaming; -+#endif - }; - - #define ipu7_isys_queue_to_video(__aq) \ -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index cb2f49f3e0fa..cfb3989fe719 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -540,6 +540,12 @@ static int isys_runtime_pm_suspend(struct device *dev) - isys->power = 0; - spin_unlock_irqrestore(&isys->power_lock, flags); - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_lock(&isys->reset_mutex); -+ isys->need_reset = false; -+ mutex_unlock(&isys->reset_mutex); -+ -+#endif - cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); - - ipu7_mmu_hw_cleanup(adev->mmu); -@@ -594,6 +600,9 @@ static void isys_remove(struct auxiliary_device *auxdev) - - mutex_destroy(&isys->stream_mutex); - mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif - } - - static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) -@@ -738,6 +747,10 @@ static int isys_probe(struct auxiliary_device *auxdev, - - mutex_init(&isys->mutex); - mutex_init(&isys->stream_mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_init(&isys->reset_mutex); -+ isys->state = 0; -+#endif - - spin_lock_init(&isys->listlock); - INIT_LIST_HEAD(&isys->framebuflist); -@@ -767,6 +780,9 @@ static int isys_probe(struct auxiliary_device *auxdev, - if (ret) - goto out_cleanup; - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif - ipu7_mmu_hw_cleanup(adev->mmu); - pm_runtime_put(&auxdev->dev); - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index ef1ab1b42f6c..17d4d5630169 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -44,8 +44,16 @@ - #define IPU_ISYS_MAX_WIDTH 8160U - #define IPU_ISYS_MAX_HEIGHT 8190U - -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define RESET_STATE_IN_RESET 1U -+#define RESET_STATE_IN_STOP_STREAMING 2U -+ -+#endif - #define FW_CALL_TIMEOUT_JIFFIES \ - msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS) -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+#define FW_CALL_TIMEOUT_JIFFIES_RESET msecs_to_jiffies(200) -+#endif - - struct isys_fw_log { - struct mutex mutex; /* protect whole struct */ -@@ -68,6 +76,9 @@ struct isys_fw_log { - * @streams_lock: serialise access to streams - * @streams: streams per firmware stream ID - * @syscom: fw communication layer context -+ #ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ * @need_reset: Isys requires d0i0->i3 transition -+ #endif - * @ref_count: total number of callers fw open - * @mutex: serialise access isys video open/release related operations - * @stream_mutex: serialise stream start and stop, queueing requests -@@ -109,6 +120,11 @@ struct ipu7_isys { - - struct ipu7_insys_config *subsys_config; - dma_addr_t subsys_config_dma_addr; -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ struct mutex reset_mutex; -+ bool need_reset; -+ int state; -+#endif - }; - - struct isys_fw_msgs { --- -2.43.0 - diff --git a/SPECS/kernel/0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf b/SPECS/kernel/0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf deleted file mode 100644 index e0d5f16f8..000000000 --- a/SPECS/kernel/0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf +++ /dev/null @@ -1,223 +0,0 @@ -From 2e3221601dc344723969ff1499a2a86bdbbc62b8 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 12 May 2025 08:37:39 -0700 -Subject: [PATCH 002/100] perf/x86/intel: Add a check for dynamic constraints - -The current event scheduler has a limit. If the counter constraint of an -event is not a subset of any other counter constraint with an equal or -higher weight. The counters may not be fully utilized. - -To workaround it, the commit bc1738f6ee83 ("perf, x86: Fix event -scheduler for constraints with overlapping counters") introduced an -overlap flag, which is hardcoded to the event constraint that may -trigger the limit. It only works for static constraints. - -Many features on and after Intel PMON v6 require dynamic constraints. An -event constraint is decided by both static and dynamic constraints at -runtime. See commit 4dfe3232cc04 ("perf/x86: Add dynamic constraint"). -The dynamic constraints are from CPUID enumeration. It's impossible to -hardcode it in advance. It's not practical to set the overlap flag to all -events. It's harmful to the scheduler. - -For the existing Intel platforms, the dynamic constraints don't trigger -the limit. A real fix is not required. - -However, for virtualization, VMM may give a weird CPUID enumeration to a -guest. It's impossible to indicate what the weird enumeration is. A -check is introduced, which can list the possible breaks if a weird -enumeration is used. - -Check the dynamic constraints enumerated for normal, branch counters -logging, and auto-counter reload. -Check both PEBS and non-PEBS constratins. - -Closes: https://lore.kernel.org/lkml/20250416195610.GC38216@noisy.programming.kicks-ass.net/ -Signed-off-by: Kan Liang ---- - arch/x86/events/intel/core.c | 156 +++++++++++++++++++++++++++++++++-- - 1 file changed, 148 insertions(+), 8 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index c2fb729c270e..60c950e4527a 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5260,6 +5260,151 @@ static void intel_pmu_check_event_constraints(struct event_constraint *event_con - u64 fixed_cntr_mask, - u64 intel_ctrl); - -+enum dyn_constr_type { -+ DYN_CONSTR_NONE, -+ DYN_CONSTR_BR_CNTR, -+ DYN_CONSTR_ACR_CNTR, -+ DYN_CONSTR_ACR_CAUSE, -+ -+ DYN_CONSTR_MAX, -+}; -+ -+static const char * const dyn_constr_type_name[] = { -+ [DYN_CONSTR_NONE] = "a normal event", -+ [DYN_CONSTR_BR_CNTR] = "a branch counter logging event", -+ [DYN_CONSTR_ACR_CNTR] = "an auto-counter reload event", -+ [DYN_CONSTR_ACR_CAUSE] = "an auto-counter reload cause event", -+}; -+ -+static void __intel_pmu_check_dyn_constr(struct event_constraint *constr, -+ enum dyn_constr_type type, u64 mask) -+{ -+ struct event_constraint *c1, *c2; -+ int new_weight, check_weight; -+ u64 new_mask, check_mask; -+ -+ for_each_event_constraint(c1, constr) { -+ new_mask = c1->idxmsk64 & mask; -+ new_weight = hweight64(new_mask); -+ -+ /* ignore topdown perf metrics event */ -+ if (c1->idxmsk64 & INTEL_PMC_MSK_TOPDOWN) -+ continue; -+ -+ if (!new_weight && fls64(c1->idxmsk64) < INTEL_PMC_IDX_FIXED) { -+ pr_info("The event 0x%llx is not supported as %s.\n", -+ c1->code, dyn_constr_type_name[type]); -+ } -+ -+ if (new_weight <= 1) -+ continue; -+ -+ for_each_event_constraint(c2, c1 + 1) { -+ bool check_fail = false; -+ -+ check_mask = c2->idxmsk64 & mask; -+ check_weight = hweight64(check_mask); -+ -+ if (c2->idxmsk64 & INTEL_PMC_MSK_TOPDOWN || -+ !check_weight) -+ continue; -+ -+ /* The same constraints or no overlap */ -+ if (new_mask == check_mask || -+ (new_mask ^ check_mask) == (new_mask | check_mask)) -+ continue; -+ -+ /* -+ * A scheduler issue may be triggered in the following cases. -+ * - Two overlap constraints have the same weight. -+ * E.g., A constraints: 0x3, B constraints: 0x6 -+ * event counter failure case -+ * B PMC[2:1] 1 -+ * A PMC[1:0] 0 -+ * A PMC[1:0] FAIL -+ * - Two overlap constraints have different weight. -+ * The constraint has a low weight, but has high last bit. -+ * E.g., A constraints: 0x7, B constraints: 0xC -+ * event counter failure case -+ * B PMC[3:2] 2 -+ * A PMC[2:0] 0 -+ * A PMC[2:0] 1 -+ * A PMC[2:0] FAIL -+ */ -+ if (new_weight == check_weight) { -+ check_fail = true; -+ } else if (new_weight < check_weight) { -+ if ((new_mask | check_mask) != check_mask && -+ fls64(new_mask) > fls64(check_mask)) -+ check_fail = true; -+ } else { -+ if ((new_mask | check_mask) != new_mask && -+ fls64(new_mask) < fls64(check_mask)) -+ check_fail = true; -+ } -+ -+ if (check_fail) { -+ pr_info("The two events 0x%llx and 0x%llx may not be " -+ "fully scheduled under some circumstances as " -+ "%s.\n", -+ c1->code, c2->code, dyn_constr_type_name[type]); -+ } -+ } -+ } -+} -+ -+static void intel_pmu_check_dyn_constr(struct pmu *pmu, -+ struct event_constraint *constr, -+ u64 cntr_mask) -+{ -+ enum dyn_constr_type i; -+ u64 mask; -+ -+ for (i = DYN_CONSTR_NONE; i < DYN_CONSTR_MAX; i++) { -+ mask = 0; -+ switch (i) { -+ case DYN_CONSTR_NONE: -+ mask = cntr_mask; -+ break; -+ case DYN_CONSTR_BR_CNTR: -+ if (x86_pmu.flags & PMU_FL_BR_CNTR) -+ mask = x86_pmu.lbr_counters; -+ break; -+ case DYN_CONSTR_ACR_CNTR: -+ mask = hybrid(pmu, acr_cntr_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0); -+ break; -+ case DYN_CONSTR_ACR_CAUSE: -+ if (hybrid(pmu, acr_cntr_mask64) == hybrid(pmu, acr_cause_mask64)) -+ continue; -+ mask = hybrid(pmu, acr_cause_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0); -+ break; -+ default: -+ pr_warn("Unsupported dynamic constraint type %d\n", i); -+ } -+ -+ if (mask) -+ __intel_pmu_check_dyn_constr(constr, i, mask); -+ } -+} -+ -+static void intel_pmu_check_event_constraints_all(struct pmu *pmu) -+{ -+ struct event_constraint *event_constraints = hybrid(pmu, event_constraints); -+ struct event_constraint *pebs_constraints = hybrid(pmu, pebs_constraints); -+ u64 cntr_mask = hybrid(pmu, cntr_mask64); -+ u64 fixed_cntr_mask = hybrid(pmu, fixed_cntr_mask64); -+ u64 intel_ctrl = hybrid(pmu, intel_ctrl); -+ -+ intel_pmu_check_event_constraints(event_constraints, cntr_mask, -+ fixed_cntr_mask, intel_ctrl); -+ -+ if (event_constraints) -+ intel_pmu_check_dyn_constr(pmu, event_constraints, cntr_mask); -+ -+ if (pebs_constraints) -+ intel_pmu_check_dyn_constr(pmu, pebs_constraints, cntr_mask); -+} -+ - static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs); - - static inline bool intel_pmu_broken_perf_cap(void) -@@ -5322,10 +5467,7 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) - else - pmu->intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS); - -- intel_pmu_check_event_constraints(pmu->event_constraints, -- pmu->cntr_mask64, -- pmu->fixed_cntr_mask64, -- pmu->intel_ctrl); -+ intel_pmu_check_event_constraints_all(&pmu->pmu); - - intel_pmu_check_extra_regs(pmu->extra_regs); - } -@@ -7737,10 +7879,8 @@ __init int intel_pmu_init(void) - if (x86_pmu.intel_cap.anythread_deprecated) - x86_pmu.format_attrs = intel_arch_formats_attr; - -- intel_pmu_check_event_constraints(x86_pmu.event_constraints, -- x86_pmu.cntr_mask64, -- x86_pmu.fixed_cntr_mask64, -- x86_pmu.intel_ctrl); -+ intel_pmu_check_event_constraints_all(NULL); -+ - /* - * Access LBR MSR may cause #GP under certain circumstances. - * Check all LBR MSR here. --- -2.43.0 - diff --git a/SPECS/kernel/0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf b/SPECS/kernel/0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf new file mode 100644 index 000000000..041f015a4 --- /dev/null +++ b/SPECS/kernel/0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf @@ -0,0 +1,316 @@ +From 369c34bbb00ae12a88ad60817c463254b56bab99 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Mon, 29 Dec 2025 10:58:52 -0800 +Subject: [PATCH 02/13] perf/x86/intel/uncore: Move uncore discovery init + struct to header + +The discovery base MSR or PCI device is platform-specific and must be +defined statically in the per-platform init table and passed to the +discovery code. + +Move the definition of struct intel_uncore_init_fun to uncore.h so it +can be accessed by discovery code, and rename it to reflect that it +now carries more than just init callbacks. + +Shorten intel_uncore_has_discovery_tables[_pci/msr] to +uncore_discovery[_pci/msr] for improved readability and alignment. + +Drop the `intel_` prefix from new names since the code is under the +intel directory and long identifiers make alignment harder. Further +cleanups will continue removing `intel_` prefixes. + +No functional change intended. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 72 ++++++++++-------------- + arch/x86/events/intel/uncore.h | 10 ++++ + arch/x86/events/intel/uncore_discovery.c | 12 ++-- + arch/x86/events/intel/uncore_discovery.h | 2 +- + 4 files changed, 49 insertions(+), 47 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index e228e564b15ea..cd561290be8ce 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1697,133 +1697,123 @@ static int __init uncore_mmio_init(void) + return ret; + } + +-struct intel_uncore_init_fun { +- void (*cpu_init)(void); +- int (*pci_init)(void); +- void (*mmio_init)(void); +- /* Discovery table is required */ +- bool use_discovery; +- /* The units in the discovery table should be ignored. */ +- int *uncore_units_ignore; +-}; +- +-static const struct intel_uncore_init_fun nhm_uncore_init __initconst = { ++static const struct uncore_plat_init nhm_uncore_init __initconst = { + .cpu_init = nhm_uncore_cpu_init, + }; + +-static const struct intel_uncore_init_fun snb_uncore_init __initconst = { ++static const struct uncore_plat_init snb_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = snb_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun ivb_uncore_init __initconst = { ++static const struct uncore_plat_init ivb_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = ivb_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun hsw_uncore_init __initconst = { ++static const struct uncore_plat_init hsw_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = hsw_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun bdw_uncore_init __initconst = { ++static const struct uncore_plat_init bdw_uncore_init __initconst = { + .cpu_init = snb_uncore_cpu_init, + .pci_init = bdw_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun snbep_uncore_init __initconst = { ++static const struct uncore_plat_init snbep_uncore_init __initconst = { + .cpu_init = snbep_uncore_cpu_init, + .pci_init = snbep_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun nhmex_uncore_init __initconst = { ++static const struct uncore_plat_init nhmex_uncore_init __initconst = { + .cpu_init = nhmex_uncore_cpu_init, + }; + +-static const struct intel_uncore_init_fun ivbep_uncore_init __initconst = { ++static const struct uncore_plat_init ivbep_uncore_init __initconst = { + .cpu_init = ivbep_uncore_cpu_init, + .pci_init = ivbep_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun hswep_uncore_init __initconst = { ++static const struct uncore_plat_init hswep_uncore_init __initconst = { + .cpu_init = hswep_uncore_cpu_init, + .pci_init = hswep_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun bdx_uncore_init __initconst = { ++static const struct uncore_plat_init bdx_uncore_init __initconst = { + .cpu_init = bdx_uncore_cpu_init, + .pci_init = bdx_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun knl_uncore_init __initconst = { ++static const struct uncore_plat_init knl_uncore_init __initconst = { + .cpu_init = knl_uncore_cpu_init, + .pci_init = knl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun skl_uncore_init __initconst = { ++static const struct uncore_plat_init skl_uncore_init __initconst = { + .cpu_init = skl_uncore_cpu_init, + .pci_init = skl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun skx_uncore_init __initconst = { ++static const struct uncore_plat_init skx_uncore_init __initconst = { + .cpu_init = skx_uncore_cpu_init, + .pci_init = skx_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun icl_uncore_init __initconst = { ++static const struct uncore_plat_init icl_uncore_init __initconst = { + .cpu_init = icl_uncore_cpu_init, + .pci_init = skl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun tgl_uncore_init __initconst = { ++static const struct uncore_plat_init tgl_uncore_init __initconst = { + .cpu_init = tgl_uncore_cpu_init, + .mmio_init = tgl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun tgl_l_uncore_init __initconst = { ++static const struct uncore_plat_init tgl_l_uncore_init __initconst = { + .cpu_init = tgl_uncore_cpu_init, + .mmio_init = tgl_l_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun rkl_uncore_init __initconst = { ++static const struct uncore_plat_init rkl_uncore_init __initconst = { + .cpu_init = tgl_uncore_cpu_init, + .pci_init = skl_uncore_pci_init, + }; + +-static const struct intel_uncore_init_fun adl_uncore_init __initconst = { ++static const struct uncore_plat_init adl_uncore_init __initconst = { + .cpu_init = adl_uncore_cpu_init, + .mmio_init = adl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun mtl_uncore_init __initconst = { ++static const struct uncore_plat_init mtl_uncore_init __initconst = { + .cpu_init = mtl_uncore_cpu_init, + .mmio_init = adl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun lnl_uncore_init __initconst = { ++static const struct uncore_plat_init lnl_uncore_init __initconst = { + .cpu_init = lnl_uncore_cpu_init, + .mmio_init = lnl_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun ptl_uncore_init __initconst = { ++static const struct uncore_plat_init ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, + .use_discovery = true, + }; + +-static const struct intel_uncore_init_fun icx_uncore_init __initconst = { ++static const struct uncore_plat_init icx_uncore_init __initconst = { + .cpu_init = icx_uncore_cpu_init, + .pci_init = icx_uncore_pci_init, + .mmio_init = icx_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun snr_uncore_init __initconst = { ++static const struct uncore_plat_init snr_uncore_init __initconst = { + .cpu_init = snr_uncore_cpu_init, + .pci_init = snr_uncore_pci_init, + .mmio_init = snr_uncore_mmio_init, + }; + +-static const struct intel_uncore_init_fun spr_uncore_init __initconst = { ++static const struct uncore_plat_init spr_uncore_init __initconst = { + .cpu_init = spr_uncore_cpu_init, + .pci_init = spr_uncore_pci_init, + .mmio_init = spr_uncore_mmio_init, +@@ -1831,7 +1821,7 @@ static const struct intel_uncore_init_fun spr_uncore_init __initconst = { + .uncore_units_ignore = spr_uncore_units_ignore, + }; + +-static const struct intel_uncore_init_fun gnr_uncore_init __initconst = { ++static const struct uncore_plat_init gnr_uncore_init __initconst = { + .cpu_init = gnr_uncore_cpu_init, + .pci_init = gnr_uncore_pci_init, + .mmio_init = gnr_uncore_mmio_init, +@@ -1839,7 +1829,7 @@ static const struct intel_uncore_init_fun gnr_uncore_init __initconst = { + .uncore_units_ignore = gnr_uncore_units_ignore, + }; + +-static const struct intel_uncore_init_fun generic_uncore_init __initconst = { ++static const struct uncore_plat_init generic_uncore_init __initconst = { + .cpu_init = intel_uncore_generic_uncore_cpu_init, + .pci_init = intel_uncore_generic_uncore_pci_init, + .mmio_init = intel_uncore_generic_uncore_mmio_init, +@@ -1910,7 +1900,7 @@ MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match); + static int __init intel_uncore_init(void) + { + const struct x86_cpu_id *id; +- struct intel_uncore_init_fun *uncore_init; ++ struct uncore_plat_init *uncore_init; + int pret = 0, cret = 0, mret = 0, ret; + + if (boot_cpu_has(X86_FEATURE_HYPERVISOR)) +@@ -1921,16 +1911,16 @@ static int __init intel_uncore_init(void) + + id = x86_match_cpu(intel_uncore_match); + if (!id) { +- if (!uncore_no_discover && intel_uncore_has_discovery_tables(NULL)) +- uncore_init = (struct intel_uncore_init_fun *)&generic_uncore_init; ++ if (!uncore_no_discover && uncore_discovery(NULL)) ++ uncore_init = (struct uncore_plat_init *)&generic_uncore_init; + else + return -ENODEV; + } else { +- uncore_init = (struct intel_uncore_init_fun *)id->driver_data; ++ uncore_init = (struct uncore_plat_init *)id->driver_data; + if (uncore_no_discover && uncore_init->use_discovery) + return -ENODEV; + if (uncore_init->use_discovery && +- !intel_uncore_has_discovery_tables(uncore_init->uncore_units_ignore)) ++ !uncore_discovery(uncore_init)) + return -ENODEV; + } + +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index d8815fff75882..568536ef28ee6 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -47,6 +47,16 @@ struct uncore_event_desc; + struct freerunning_counters; + struct intel_uncore_topology; + ++struct uncore_plat_init { ++ void (*cpu_init)(void); ++ int (*pci_init)(void); ++ void (*mmio_init)(void); ++ /* Discovery table is required */ ++ bool use_discovery; ++ /* The units in the discovery table should be ignored. */ ++ int *uncore_units_ignore; ++}; ++ + struct intel_uncore_type { + const char *name; + int num_counters; +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index 7d57ce706feb1..d39f6a0b8cc35 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -350,7 +350,7 @@ static int parse_discovery_table(struct pci_dev *dev, int die, + return __parse_discovery_table(addr, die, parsed, ignore); + } + +-static bool intel_uncore_has_discovery_tables_pci(int *ignore) ++static bool uncore_discovery_pci(int *ignore) + { + u32 device, val, entry_id, bar_offset; + int die, dvsec = 0, ret = true; +@@ -399,7 +399,7 @@ static bool intel_uncore_has_discovery_tables_pci(int *ignore) + return ret; + } + +-static bool intel_uncore_has_discovery_tables_msr(int *ignore) ++static bool uncore_discovery_msr(int *ignore) + { + unsigned long *die_mask; + bool parsed = false; +@@ -432,10 +432,12 @@ static bool intel_uncore_has_discovery_tables_msr(int *ignore) + return parsed; + } + +-bool intel_uncore_has_discovery_tables(int *ignore) ++bool uncore_discovery(struct uncore_plat_init *init) + { +- return intel_uncore_has_discovery_tables_msr(ignore) || +- intel_uncore_has_discovery_tables_pci(ignore); ++ int *ignore = init ? init->uncore_units_ignore : NULL; ++ ++ return uncore_discovery_msr(ignore) || ++ uncore_discovery_pci(ignore); + } + + void intel_uncore_clear_discovery_tables(void) +diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h +index dff75c98e22f4..dfc237a2b6dfc 100644 +--- a/arch/x86/events/intel/uncore_discovery.h ++++ b/arch/x86/events/intel/uncore_discovery.h +@@ -136,7 +136,7 @@ struct intel_uncore_discovery_type { + u16 num_units; /* number of units */ + }; + +-bool intel_uncore_has_discovery_tables(int *ignore); ++bool uncore_discovery(struct uncore_plat_init *init); + void intel_uncore_clear_discovery_tables(void); + void intel_uncore_generic_uncore_cpu_init(void); + int intel_uncore_generic_uncore_pci_init(void); +-- +2.43.0 + diff --git a/SPECS/kernel/0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt b/SPECS/kernel/0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt new file mode 100644 index 000000000..a1f56f4d3 --- /dev/null +++ b/SPECS/kernel/0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt @@ -0,0 +1,49 @@ +From 7f70318e388fac188633c893e76bc36ecd1a6df3 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:31 -0700 +Subject: [PATCH 2/6] platform/x86:intel/pmc: Add DMU GUID to Arrow Lake U/H +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Arrow Lake U/H platforms may have multiple GUIDs pointing to the +same telemetry region. Add the second possible GUID to the GUID +list to support the Arrow Lake U/H platforms with this GUID. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-4-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/arl.c | 2 +- + drivers/platform/x86/intel/pmc/core.h | 1 + + 2 files changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c +index cc05a168c3721..c0698ef35df89 100644 +--- a/drivers/platform/x86/intel/pmc/arl.c ++++ b/drivers/platform/x86/intel/pmc/arl.c +@@ -733,7 +733,7 @@ struct pmc_dev_info arl_pmc_dev = { + .sub_req = pmc_core_pmt_get_lpm_req, + }; + +-static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; ++static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; + struct pmc_dev_info arl_h_pmc_dev = { + .pci_func = 2, + .dmu_guids = ARL_H_PMT_DMU_GUIDS, +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index 83d6e2e833785..d80257b37ca98 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -283,6 +283,7 @@ enum ppfear_regs { + #define MTL_PMT_DMU_DIE_C6_OFFSET 15 + #define MTL_PMT_DMU_GUID 0x1A067102 + #define ARL_PMT_DMU_GUID 0x1A06A102 ++#define ARL_H_PMT_DMU_GUID 0x1A06A101 + + #define LNL_PMC_MMIO_REG_LEN 0x2708 + #define LNL_PMC_LTR_OSSE 0x1B88 +-- +2.43.0 + diff --git a/SPECS/kernel/0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core b/SPECS/kernel/0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core deleted file mode 100644 index 52350d592..000000000 --- a/SPECS/kernel/0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core +++ /dev/null @@ -1,49 +0,0 @@ -From 589b198fa391f75259fc1e706cf47783651c6463 Mon Sep 17 00:00:00 2001 -From: Xi Pardee -Date: Tue, 26 Aug 2025 11:39:42 -0700 -Subject: [PATCH 2/2] platform/x86/intel/pmc: Add Wildcat Lake support to Intel - PMC SSRAM Telemetry -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add Wildcat Lake support to Intel PMC SSRAM Telemetry driver. - -Signed-off-by: Xi Pardee -Link: https://lore.kernel.org/r/20250826183946.802684-1-xi.pardee@linux.intel.com -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - drivers/platform/x86/intel/pmc/core.h | 3 +++ - drivers/platform/x86/intel/pmc/ssram_telemetry.c | 1 + - 2 files changed, 4 insertions(+) - -diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h -index 9d54eef6c921..47101f0dd09c 100644 ---- a/drivers/platform/x86/intel/pmc/core.h -+++ b/drivers/platform/x86/intel/pmc/core.h -@@ -310,6 +310,9 @@ enum ppfear_regs { - #define PMC_DEVID_PTL_PCDH 0xe37f - #define PMC_DEVID_PTL_PCDP 0xe47f - -+/* WCL */ -+#define PMC_DEVID_WCL_PCDN 0x4d7f -+ - /* ARL */ - #define PMC_DEVID_ARL_SOCM 0x777f - #define PMC_DEVID_ARL_SOCS 0xae7f -diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c -index 93579152188e..03fad9331fc0 100644 ---- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c -+++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c -@@ -190,6 +190,7 @@ static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) }, -+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) }, - { } - }; - MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids); --- -2.43.0 - diff --git a/SPECS/kernel/0002-thermal-intel-int340x-Add-support-for-power-slider.thermal b/SPECS/kernel/0002-thermal-intel-int340x-Add-support-for-power-slider.thermal deleted file mode 100644 index f58293d38..000000000 --- a/SPECS/kernel/0002-thermal-intel-int340x-Add-support-for-power-slider.thermal +++ /dev/null @@ -1,339 +0,0 @@ -From 36bdbac0744bddcabf7a59dcab04e3169ea7c078 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:12 -0700 -Subject: [PATCH 02/11] thermal: intel: int340x: Add support for power slider - -Add support for system wide energy performance preference using a SoC -slider interface defined via processor thermal PCI device MMIO space. - -Using Linux platform-profile class API, register a new platform profile. -Provide three platform power profile choices: -"performance", "balanced" and "low-power". - -Profile sysfs is located at: -/sys/class/platform-profile/platform-profile-* -where attribute "name" is presented as "SoC Power Slider". - -At boot by default the slider is set to balanced mode. This profile is -changed by user space based on user preference via power profile daemon -or directly writing to the "profile" sysfs attribute. - -Add a CPU model specific processor thermal device feature -PROC_THERMAL_FEATURE_SOC_POWER_SLIDER. When enabled for a CPU model, -slider interface is registered. - -During system suspend callback save slider register and restore during -resume callback. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-2-srinivas.pandruvada@linux.intel.com -[ rjw: Removal of redundant outer parens from one expression ] -Signed-off-by: Rafael J. Wysocki ---- - drivers/thermal/intel/int340x_thermal/Kconfig | 1 + - .../thermal/intel/int340x_thermal/Makefile | 1 + - .../processor_thermal_device.c | 20 ++ - .../processor_thermal_device.h | 6 + - .../processor_thermal_soc_slider.c | 194 ++++++++++++++++++ - 5 files changed, 222 insertions(+) - create mode 100644 drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c - -diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig -index 4c699f0896b5..4ced7bdcd62c 100644 ---- a/drivers/thermal/intel/int340x_thermal/Kconfig -+++ b/drivers/thermal/intel/int340x_thermal/Kconfig -@@ -12,6 +12,7 @@ config INT340X_THERMAL - select ACPI_THERMAL_LIB - select INTEL_SOC_DTS_IOSF_CORE - select INTEL_TCC -+ select ACPI_PLATFORM_PROFILE - select PROC_THERMAL_MMIO_RAPL if POWERCAP - help - Newer laptops and tablets that use ACPI may have thermal sensors and -diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile -index 184318d1792b..436be34b21a9 100644 ---- a/drivers/thermal/intel/int340x_thermal/Makefile -+++ b/drivers/thermal/intel/int340x_thermal/Makefile -@@ -14,5 +14,6 @@ obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o - obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_req.o - obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_wt_hint.o - obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_power_floor.o -+obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_soc_slider.o - obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o - obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c -index 29fcece48cad..48e7849d4816 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c -@@ -338,10 +338,17 @@ static int tcc_offset_save = -1; - - int proc_thermal_suspend(struct device *dev) - { -+ struct proc_thermal_device *proc_dev; -+ - tcc_offset_save = intel_tcc_get_offset(-1); - if (tcc_offset_save < 0) - dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save); - -+ proc_dev = dev_get_drvdata(dev); -+ -+ if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) -+ proc_thermal_soc_power_slider_suspend(proc_dev); -+ - return 0; - } - EXPORT_SYMBOL_GPL(proc_thermal_suspend); -@@ -357,6 +364,9 @@ int proc_thermal_resume(struct device *dev) - if (tcc_offset_save >= 0) - intel_tcc_set_offset(-1, tcc_offset_save); - -+ if (proc_dev->mmio_feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) -+ proc_thermal_soc_power_slider_resume(proc_dev); -+ - return 0; - } - EXPORT_SYMBOL_GPL(proc_thermal_resume); -@@ -432,8 +442,18 @@ int proc_thermal_mmio_add(struct pci_dev *pdev, - } - } - -+ if (feature_mask & PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) { -+ ret = proc_thermal_soc_power_slider_add(pdev, proc_priv); -+ if (ret) { -+ dev_info(&pdev->dev, "failed to add soc power efficiency slider\n"); -+ goto err_rem_wlt; -+ } -+ } -+ - return 0; - -+err_rem_wlt: -+ proc_thermal_wt_hint_remove(pdev); - err_rem_rfim: - proc_thermal_rfim_remove(pdev); - err_rem_ptc: -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h -index 49398794124a..30760475102f 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h -@@ -69,6 +69,7 @@ struct rapl_mmio_regs { - #define PROC_THERMAL_FEATURE_POWER_FLOOR 0x40 - #define PROC_THERMAL_FEATURE_MSI_SUPPORT 0x80 - #define PROC_THERMAL_FEATURE_PTC 0x100 -+#define PROC_THERMAL_FEATURE_SOC_POWER_SLIDER 0x200 - - #if IS_ENABLED(CONFIG_PROC_THERMAL_MMIO_RAPL) - int proc_thermal_rapl_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); -@@ -127,4 +128,9 @@ int proc_thermal_mmio_add(struct pci_dev *pdev, - void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); - int proc_thermal_ptc_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); - void proc_thermal_ptc_remove(struct pci_dev *pdev); -+ -+int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv); -+void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv); -+void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv); -+ - #endif -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -new file mode 100644 -index 000000000000..57782a63b9b5 ---- /dev/null -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -0,0 +1,194 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Processor Thermal Device Interface for Reading and Writing -+ * SoC Power Slider Values from User Space. -+ * -+ * Operation: -+ * The SOC_EFFICIENCY_SLIDER_0_0_0_MCHBAR register is accessed -+ * using the MMIO (Memory-Mapped I/O) interface with an MMIO offset of 0x5B38. -+ * Although this register is 64 bits wide, only bits 7:0 are used, -+ * and the other bits remain unchanged. -+ * -+ * Bit definitions -+ * -+ * Bits 2:0 (Slider value): -+ * The SoC optimizer slider value indicates the system wide energy performance -+ * hint. The slider has no specific units and ranges from 0 (highest -+ * performance) to 6 (highest energy efficiency). Value of 7 is reserved. -+ * Bits 3 : Reserved -+ * Bits 6:4 (Offset) -+ * Offset allows the SoC to automatically switch slider position in range -+ * [slider value (bits 2:0) + offset] to improve power efficiency based on -+ * internal SoC algorithms. -+ * Bit 7 (Enable): -+ * If this bit is set, the SoC Optimization sliders will be processed by the -+ * SoC firmware. -+ * -+ * Copyright (c) 2025, Intel Corporation. -+ */ -+ -+#include -+#include -+#include -+#include "processor_thermal_device.h" -+ -+#define SOC_POWER_SLIDER_OFFSET 0x5B38 -+ -+enum power_slider_preference { -+ SOC_POWER_SLIDER_PERFORMANCE, -+ SOC_POWER_SLIDER_BALANCE, -+ SOC_POWER_SLIDER_POWERSAVE, -+}; -+ -+#define SOC_SLIDER_VALUE_MINIMUM 0x00 -+#define SOC_SLIDER_VALUE_BALANCE 0x03 -+#define SOC_SLIDER_VALUE_MAXIMUM 0x06 -+ -+#define SLIDER_MASK GENMASK_ULL(2, 0) -+#define SLIDER_ENABLE_BIT 7 -+ -+static u8 slider_values[] = { -+ [SOC_POWER_SLIDER_PERFORMANCE] = SOC_SLIDER_VALUE_MINIMUM, -+ [SOC_POWER_SLIDER_BALANCE] = SOC_SLIDER_VALUE_BALANCE, -+ [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM, -+}; -+ -+/* Convert from platform power profile option to SoC slider value */ -+static int convert_profile_to_power_slider(enum platform_profile_option profile) -+{ -+ switch (profile) { -+ case PLATFORM_PROFILE_LOW_POWER: -+ return slider_values[SOC_POWER_SLIDER_POWERSAVE]; -+ case PLATFORM_PROFILE_BALANCED: -+ return slider_values[SOC_POWER_SLIDER_BALANCE]; -+ case PLATFORM_PROFILE_PERFORMANCE: -+ return slider_values[SOC_POWER_SLIDER_PERFORMANCE]; -+ default: -+ break; -+ } -+ -+ return -EOPNOTSUPP; -+} -+ -+/* Convert to platform power profile option from SoC slider values */ -+static int convert_power_slider_to_profile(u8 slider) -+{ -+ if (slider == slider_values[SOC_POWER_SLIDER_PERFORMANCE]) -+ return PLATFORM_PROFILE_PERFORMANCE; -+ if (slider == slider_values[SOC_POWER_SLIDER_BALANCE]) -+ return PLATFORM_PROFILE_BALANCED; -+ if (slider == slider_values[SOC_POWER_SLIDER_POWERSAVE]) -+ return PLATFORM_PROFILE_LOW_POWER; -+ -+ return -EOPNOTSUPP; -+} -+ -+static inline u64 read_soc_slider(struct proc_thermal_device *proc_priv) -+{ -+ return readq(proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); -+} -+ -+static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 val) -+{ -+ writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); -+} -+ -+static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider) -+{ -+ u64 val; -+ -+ val = read_soc_slider(proc_priv); -+ val &= ~SLIDER_MASK; -+ val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT); -+ write_soc_slider(proc_priv, val); -+} -+ -+/* profile get/set callbacks are called with a profile lock, so no need for local locks */ -+ -+static int power_slider_platform_profile_set(struct device *dev, -+ enum platform_profile_option profile) -+{ -+ struct proc_thermal_device *proc_priv; -+ int slider; -+ -+ proc_priv = dev_get_drvdata(dev); -+ if (!proc_priv) -+ return -EOPNOTSUPP; -+ -+ slider = convert_profile_to_power_slider(profile); -+ if (slider < 0) -+ return slider; -+ -+ set_soc_power_profile(proc_priv, slider); -+ -+ return 0; -+} -+ -+static int power_slider_platform_profile_get(struct device *dev, -+ enum platform_profile_option *profile) -+{ -+ struct proc_thermal_device *proc_priv; -+ int slider, ret; -+ u64 val; -+ -+ proc_priv = dev_get_drvdata(dev); -+ if (!proc_priv) -+ return -EOPNOTSUPP; -+ -+ val = read_soc_slider(proc_priv); -+ slider = FIELD_GET(SLIDER_MASK, val); -+ -+ ret = convert_power_slider_to_profile(slider); -+ if (ret < 0) -+ return ret; -+ -+ *profile = ret; -+ -+ return 0; -+} -+ -+static int power_slider_platform_profile_probe(void *drvdata, unsigned long *choices) -+{ -+ set_bit(PLATFORM_PROFILE_LOW_POWER, choices); -+ set_bit(PLATFORM_PROFILE_BALANCED, choices); -+ set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); -+ -+ return 0; -+} -+ -+static const struct platform_profile_ops power_slider_platform_profile_ops = { -+ .probe = power_slider_platform_profile_probe, -+ .profile_get = power_slider_platform_profile_get, -+ .profile_set = power_slider_platform_profile_set, -+}; -+ -+int proc_thermal_soc_power_slider_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv) -+{ -+ struct device *ppdev; -+ -+ set_soc_power_profile(proc_priv, slider_values[SOC_POWER_SLIDER_BALANCE]); -+ -+ ppdev = devm_platform_profile_register(&pdev->dev, "SoC Power Slider", proc_priv, -+ &power_slider_platform_profile_ops); -+ -+ return PTR_ERR_OR_ZERO(ppdev); -+} -+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_add, "INT340X_THERMAL"); -+ -+static u64 soc_slider_save; -+ -+void proc_thermal_soc_power_slider_suspend(struct proc_thermal_device *proc_priv) -+{ -+ soc_slider_save = read_soc_slider(proc_priv); -+} -+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_suspend, "INT340X_THERMAL"); -+ -+void proc_thermal_soc_power_slider_resume(struct proc_thermal_device *proc_priv) -+{ -+ write_soc_slider(proc_priv, soc_slider_save); -+} -+EXPORT_SYMBOL_NS_GPL(proc_thermal_soc_power_slider_resume, "INT340X_THERMAL"); -+ -+MODULE_IMPORT_NS("INT340X_THERMAL"); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Processor Thermal Power Slider Interface"); --- -2.43.0 - diff --git a/SPECS/kernel/0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt b/SPECS/kernel/0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt deleted file mode 100644 index 905ec964c..000000000 --- a/SPECS/kernel/0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt +++ /dev/null @@ -1,109 +0,0 @@ -From e58ae10aa522b056caa9a802693b14e557be015d Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Fri, 11 Aug 2023 12:59:09 +0300 -Subject: [PATCH 2/4] thunderbolt: Make XDomain lane bonding comply with the - USB4 v2 spec - -The USB4 v2 Inter-Domain spec "unified" the lane bonding flow so that -when the other end (with higher UUID) is not yet set the target link -width accordingly it is expected to reply with ERROR_NOT_READY. -Implement this for Linux. - -Signed-off-by: Mika Westerberg ---- - drivers/thunderbolt/xdomain.c | 65 +++++++++++++++++++++++++---------- - 1 file changed, 47 insertions(+), 18 deletions(-) - -diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c -index b0630e6d9472..88e5bcb6640e 100644 ---- a/drivers/thunderbolt/xdomain.c -+++ b/drivers/thunderbolt/xdomain.c -@@ -535,29 +535,19 @@ static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route, - } - - static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl, -- struct tb_xdomain *xd, u8 sequence) -+ struct tb_xdomain *xd, u8 sequence, -+ u8 slw, u8 sls, u8 tls, u8 tlw) - { - struct tb_xdp_link_state_status_response res; -- struct tb_port *port = tb_xdomain_downstream_port(xd); -- u32 val[2]; -- int ret; - - memset(&res, 0, sizeof(res)); - tb_xdp_fill_header(&res.hdr, xd->route, sequence, - LINK_STATE_STATUS_RESPONSE, sizeof(res)); - -- ret = tb_port_read(port, val, TB_CFG_PORT, -- port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val)); -- if (ret) -- return ret; -- -- res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> -- LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; -- res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> -- LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; -- res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; -- res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> -- LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; -+ res.slw = slw; -+ res.sls = sls; -+ res.tls = tls; -+ res.tlw = tlw; - - return __tb_xdomain_response(ctl, &res, sizeof(res), - TB_CFG_PKG_XDOMAIN_RESP); -@@ -802,8 +792,47 @@ static void tb_xdp_handle_request(struct work_struct *work) - route); - - if (xd) { -- ret = tb_xdp_link_state_status_response(tb, ctl, xd, -- sequence); -+ struct tb_port *port = tb_xdomain_downstream_port(xd); -+ u8 slw, sls, tls, tlw; -+ u32 val[2]; -+ -+ /* -+ * Read the adapter supported and target widths -+ * and speeds. -+ */ -+ ret = tb_port_read(port, val, TB_CFG_PORT, -+ port->cap_phy + LANE_ADP_CS_0, -+ ARRAY_SIZE(val)); -+ if (ret) -+ break; -+ -+ slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> -+ LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; -+ sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >> -+ LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT; -+ tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK; -+ tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >> -+ LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; -+ -+ /* -+ * When we have higher UUID, we are supposed to -+ * return ERROR_NOT_READY if the tlw is not yet -+ * set according to the Inter-Domain spec for -+ * USB4 v2. -+ */ -+ if (xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH && -+ xd->target_link_width && -+ xd->target_link_width != tlw) { -+ tb_dbg(tb, "%llx: target link width not yet set %#x != %#x\n", -+ route, tlw, xd->target_link_width); -+ tb_xdp_error_response(ctl, route, sequence, -+ ERROR_NOT_READY); -+ } else { -+ tb_dbg(tb, "%llx: replying with target link width set to %#x\n", -+ route, tlw); -+ ret = tb_xdp_link_state_status_response(tb, ctl, -+ xd, sequence, slw, sls, tls, tlw); -+ } - } else { - tb_xdp_error_response(ctl, route, sequence, - ERROR_NOT_READY); --- -2.43.0 - diff --git a/SPECS/kernel/0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo b/SPECS/kernel/0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo new file mode 100644 index 000000000..6754a7165 --- /dev/null +++ b/SPECS/kernel/0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo @@ -0,0 +1,40 @@ +From 353fba46fd0866f0bbbca8c86c315f54baa2e3db Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Thu, 14 Aug 2025 23:29:57 -0400 +Subject: [PATCH 02/21] tools/power turbostat: Add Wildcat Lake and Nova Lake + support + +Treat Wildcat Lake and Nova Lake (and Panther Lake) +the same as Lunar Lake, for now. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 1b5ca2f4e92ff..7c24c2f9a0752 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -1210,6 +1210,9 @@ static const struct platform_data turbostat_pdata[] = { + { INTEL_ARROWLAKE, &adl_features }, + { INTEL_LUNARLAKE_M, &lnl_features }, + { INTEL_PANTHERLAKE_L, &lnl_features }, ++ { INTEL_NOVALAKE, &lnl_features }, ++ { INTEL_NOVALAKE_L, &lnl_features }, ++ { INTEL_WILDCATLAKE_L, &lnl_features }, + { INTEL_ATOM_SILVERMONT, &slv_features }, + { INTEL_ATOM_SILVERMONT_D, &slvd_features }, + { INTEL_ATOM_AIRMONT, &amt_features }, +@@ -10126,7 +10129,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.09.09 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.10.18 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel/0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac b/SPECS/kernel/0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac new file mode 100644 index 000000000..b1093cd11 --- /dev/null +++ b/SPECS/kernel/0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac @@ -0,0 +1,163 @@ +From 5b64d5870c761a47c148280951e13ffd4ac6d00b Mon Sep 17 00:00:00 2001 +From: Qiuxu Zhuo +Date: Thu, 13 Nov 2025 13:01:01 +0800 +Subject: [PATCH 3/3] EDAC/igen6: Fix masks of {MCHBAR, TOM, TOUUD} registers + +The masks used to retrieve base addresses from {MCHBAR, TOM, TOUUD} +registers are CPU model-specific. Currently, igen6_edac uses the +same masks for all CPUs, which is incorrect. Add three new fields +to structure res_config to make these masks CPU model-specific and +configure them properly. + +Fixes: 0b7338b27e82 ("EDAC/igen6: Add Intel Tiger Lake SoC support") +Signed-off-by: Qiuxu Zhuo +--- + drivers/edac/igen6_edac.c | 41 +++++++++++++++++++++++++++++++++++---- + 1 file changed, 37 insertions(+), 4 deletions(-) + +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 3e9d0118e3958..5f851c772a321 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -87,7 +87,6 @@ + /* Host MMIO base address */ + #define MCHBAR_OFFSET 0x48 + #define MCHBAR_EN BIT_ULL(0) +-#define MCHBAR_BASE(v) (GET_BITFIELD(v, 16, 38) << 16) + #define MCHBAR_SIZE 0x10000 + + /* Parameters for the channel decode stage */ +@@ -129,6 +128,12 @@ static struct res_config { + bool machine_check; + /* The number of present memory controllers. */ + int num_imc; ++ /* Host MMIO configuration register */ ++ u64 reg_mchbar_mask; ++ /* Top of Memory */ ++ u64 reg_tom_mask; ++ /* Top of Upper Usable DRAM */ ++ u64 reg_touud_mask; + u32 imc_base; + u32 cmf_base; + u32 cmf_size; +@@ -315,7 +320,8 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar) + return -ENODEV; + } + +- *mchbar = MCHBAR_BASE(u.v); ++ *mchbar = u.v & res_cfg->reg_mchbar_mask; ++ edac_dbg(2, "MCHBAR 0x%llx (reg 0x%llx)\n", *mchbar, u.v); + + return 0; + } +@@ -496,6 +502,9 @@ static u64 rpl_p_err_addr(u64 ecclog) + + static struct res_config ehl_cfg = { + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(38, 16), ++ .reg_tom_mask = GENMASK_ULL(38, 20), ++ .reg_touud_mask = GENMASK_ULL(38, 20), + .imc_base = 0x5000, + .ibecc_base = 0xdc00, + .ibecc_available = ehl_ibecc_available, +@@ -506,6 +515,9 @@ static struct res_config ehl_cfg = { + + static struct res_config icl_cfg = { + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(38, 16), ++ .reg_tom_mask = GENMASK_ULL(38, 20), ++ .reg_touud_mask = GENMASK_ULL(38, 20), + .imc_base = 0x5000, + .ibecc_base = 0xd800, + .ibecc_error_log_offset = 0x170, +@@ -517,6 +529,9 @@ static struct res_config icl_cfg = { + static struct res_config tgl_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(38, 17), ++ .reg_tom_mask = GENMASK_ULL(38, 20), ++ .reg_touud_mask = GENMASK_ULL(38, 20), + .imc_base = 0x5000, + .cmf_base = 0x11000, + .cmf_size = 0x800, +@@ -531,6 +546,9 @@ static struct res_config tgl_cfg = { + static struct res_config adl_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, +@@ -542,6 +560,9 @@ static struct res_config adl_cfg = { + static struct res_config adl_n_cfg = { + .machine_check = true, + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, +@@ -553,6 +574,9 @@ static struct res_config adl_n_cfg = { + static struct res_config rpl_p_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x68, +@@ -565,6 +589,9 @@ static struct res_config rpl_p_cfg = { + static struct res_config mtl_ps_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, +@@ -576,6 +603,9 @@ static struct res_config mtl_ps_cfg = { + static struct res_config mtl_p_cfg = { + .machine_check = true, + .num_imc = 2, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, +@@ -587,6 +617,9 @@ static struct res_config mtl_p_cfg = { + static struct res_config wcl_cfg = { + .machine_check = true, + .num_imc = 1, ++ .reg_mchbar_mask = GENMASK_ULL(41, 17), ++ .reg_tom_mask = GENMASK_ULL(41, 20), ++ .reg_touud_mask = GENMASK_ULL(41, 20), + .imc_base = 0xd800, + .ibecc_base = 0xd400, + .ibecc_error_log_offset = 0x170, +@@ -1216,7 +1249,7 @@ static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) + goto fail; + } + +- igen6_tom = u.v & GENMASK_ULL(38, 20); ++ igen6_tom = u.v & res_cfg->reg_tom_mask; + + if (get_mchbar(pdev, mchbar)) + goto fail; +@@ -1227,7 +1260,7 @@ static int igen6_pci_setup(struct pci_dev *pdev, u64 *mchbar) + else if (pci_read_config_dword(pdev, TOUUD_OFFSET + 4, &u.v_hi)) + edac_dbg(2, "Failed to read upper TOUUD\n"); + else +- igen6_touud = u.v & GENMASK_ULL(38, 20); ++ igen6_touud = u.v & res_cfg->reg_touud_mask; + #endif + + return 0; +-- +2.43.0 + diff --git a/SPECS/kernel/0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac b/SPECS/kernel/0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac deleted file mode 100644 index 521c8c95b..000000000 --- a/SPECS/kernel/0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac +++ /dev/null @@ -1,93 +0,0 @@ -From 7fe5d4b4ea29feb773895fd8b007774e2682bba0 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Wed, 23 Jul 2025 12:47:56 +0800 -Subject: [PATCH 03/13] EDAC/skx_common: Move mc_mapping to be a field inside - struct skx_imc - -The mc_mapping and imc fields of struct skx_dev have the same size, -NUM_IMC. Move mc_mapping to be a field inside struct skx_imc to prepare -for making the imc array of memory controller instances a flexible array. - -No functional changes intended. - -Suggested-by: Tony Luck -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 8 ++++---- - drivers/edac/skx_common.h | 20 ++++++++++---------- - 2 files changed, 14 insertions(+), 14 deletions(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index cc7d36cf7f3b..3f6a074d685c 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -131,7 +131,7 @@ static void skx_init_mc_mapping(struct skx_dev *d) - * EDAC driver. - */ - for (int i = 0; i < NUM_IMC; i++) -- d->mc_mapping[i] = i; -+ d->imc[i].mc_mapping = i; - } - - void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc) -@@ -139,16 +139,16 @@ void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc) - edac_dbg(0, "Set the mapping of mc phy idx to logical idx: %02d -> %02d\n", - pmc, lmc); - -- d->mc_mapping[pmc] = lmc; -+ d->imc[pmc].mc_mapping = lmc; - } - EXPORT_SYMBOL_GPL(skx_set_mc_mapping); - - static u8 skx_get_mc_mapping(struct skx_dev *d, u8 pmc) - { - edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n", -- pmc, d->mc_mapping[pmc]); -+ pmc, d->imc[pmc].mc_mapping); - -- return d->mc_mapping[pmc]; -+ return d->imc[pmc].mc_mapping; - } - - static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index 3f6007a97267..95d61d23f89e 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -135,16 +135,6 @@ struct skx_dev { - struct pci_dev *pcu_cr3; /* for HBM memory detection */ - u32 mcroute; - int num_imc; -- /* -- * Some server BIOS may hide certain memory controllers, and the -- * EDAC driver skips those hidden memory controllers. However, the -- * ADXL still decodes memory error address using physical memory -- * controller indices. The mapping table is used to convert the -- * physical indices (reported by ADXL) to the logical indices -- * (used the EDAC driver) of present memory controllers during the -- * error handling process. -- */ -- u8 mc_mapping[NUM_IMC]; - struct skx_imc { - struct mem_ctl_info *mci; - struct pci_dev *mdev; /* for i10nm CPU */ -@@ -156,6 +146,16 @@ struct skx_dev { - u8 mc; /* system wide mc# */ - u8 lmc; /* socket relative mc# */ - u8 src_id; -+ /* -+ * Some server BIOS may hide certain memory controllers, and the -+ * EDAC driver skips those hidden memory controllers. However, the -+ * ADXL still decodes memory error address using physical memory -+ * controller indices. The mapping table is used to convert the -+ * physical indices (reported by ADXL) to the logical indices -+ * (used the EDAC driver) of present memory controllers during the -+ * error handling process. -+ */ -+ u8 mc_mapping; - struct skx_channel { - struct pci_dev *cdev; - struct pci_dev *edev; --- -2.43.0 - diff --git a/SPECS/kernel/0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi b/SPECS/kernel/0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi new file mode 100644 index 000000000..ef41cff6c --- /dev/null +++ b/SPECS/kernel/0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi @@ -0,0 +1,68 @@ +From d9ee7982e5ed07c369987e8944cb49a3eda82f1f Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Fri, 17 Nov 2023 21:34:27 -0800 +Subject: [PATCH 03/44] KVM: VMX: Disable FRED if FRED consistency checks fail + +Do not virtualize FRED if FRED consistency checks fail. + +Either on broken hardware, or when run KVM on top of another hypervisor +before the underlying hypervisor implements nested FRED correctly. + +Suggested-by: Chao Gao +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Reviewed-by: Chao Gao +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v5: +* Drop the cpu_feature_enabled() in cpu_has_vmx_fred() (Sean). +* Add TB from Xuelian Guo. + +Change in v4: +* Call out the reason why not check FRED VM-exit controls in + cpu_has_vmx_fred() (Chao Gao). +--- + arch/x86/kvm/vmx/capabilities.h | 10 ++++++++++ + arch/x86/kvm/vmx/vmx.c | 3 +++ + 2 files changed, 13 insertions(+) + +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index 6bd67c40ca3b8..651507627ef32 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -405,6 +405,16 @@ static inline bool vmx_pebs_supported(void) + return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept; + } + ++static inline bool cpu_has_vmx_fred(void) ++{ ++ /* ++ * setup_vmcs_config() guarantees FRED VM-entry/exit controls ++ * are either all set or none. So, no need to check FRED VM-exit ++ * controls. ++ */ ++ return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED); ++} ++ + static inline bool cpu_has_notify_vmexit(void) + { + return vmcs_config.cpu_based_2nd_exec_ctrl & +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index fbeb85c4673e5..90cbc73b21288 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -8009,6 +8009,9 @@ static __init void vmx_set_cpu_caps(void) + kvm_cpu_cap_check_and_set(X86_FEATURE_DTES64); + } + ++ if (!cpu_has_vmx_fred()) ++ kvm_cpu_cap_clear(X86_FEATURE_FRED); ++ + if (!enable_pmu) + kvm_cpu_cap_clear(X86_FEATURE_PDCM); + kvm_caps.supported_perf_cap = vmx_get_perf_capabilities(); +-- +2.43.0 + diff --git a/SPECS/kernel/0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi b/SPECS/kernel/0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi deleted file mode 100644 index cdde05251..000000000 --- a/SPECS/kernel/0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi +++ /dev/null @@ -1,106 +0,0 @@ -From ef2e33eab5f2cfc9e295f0dca5009d67e7f7a642 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Wed, 30 Aug 2023 23:55:54 -0700 -Subject: [PATCH 03/44] KVM: VMX: Initialize VM entry/exit FRED controls in - vmcs_config - -Setup VM entry/exit FRED controls in the global vmcs_config for proper -FRED VMCS fields management: - 1) load guest FRED state upon VM entry. - 2) save guest FRED state during VM exit. - 3) load host FRED state during VM exit. - -Also add FRED control consistency checks to the existing VM entry/exit -consistency check framework. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo -Reviewed-by: Chao Gao ---- - -Change in v5: -* Remove the pair VM_ENTRY_LOAD_IA32_FRED/VM_EXIT_ACTIVATE_SECONDARY_CONTROLS, - since the secondary VM exit controls are unconditionally enabled anyway, and - there are features other than FRED needing it (Chao Gao). -* Add TB from Xuelian Guo. - -Change in v4: -* Do VM exit/entry consistency checks using the new macro from Sean - Christopherson. - -Changes in v3: -* Add FRED control consistency checks to the existing VM entry/exit - consistency check framework (Sean Christopherson). -* Just do the unnecessary FRED state load/store on every VM entry/exit - (Sean Christopherson). ---- - arch/x86/include/asm/vmx.h | 4 ++++ - arch/x86/kvm/vmx/vmx.c | 2 ++ - arch/x86/kvm/vmx/vmx.h | 7 +++++-- - 3 files changed, 11 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index 5b53b8da4ce0..ab70f52798d4 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -110,6 +110,9 @@ - #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 - #define VM_EXIT_ACTIVATE_SECONDARY_CONTROLS 0x80000000 - -+#define SECONDARY_VM_EXIT_SAVE_IA32_FRED BIT_ULL(0) -+#define SECONDARY_VM_EXIT_LOAD_IA32_FRED BIT_ULL(1) -+ - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff - - #define VM_ENTRY_LOAD_DEBUG_CONTROLS 0x00000004 -@@ -123,6 +126,7 @@ - #define VM_ENTRY_PT_CONCEAL_PIP 0x00020000 - #define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 - #define VM_ENTRY_LOAD_CET_STATE 0x00100000 -+#define VM_ENTRY_LOAD_IA32_FRED 0x00800000 - - #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index bad778acf9b5..aa23ecf18cda 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2625,6 +2625,8 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - u32 entry_control; - u64 exit_control; - } const vmcs_entry_exit2_pairs[] = { -+ { VM_ENTRY_LOAD_IA32_FRED, -+ SECONDARY_VM_EXIT_SAVE_IA32_FRED | SECONDARY_VM_EXIT_LOAD_IA32_FRED }, - }; - - memset(vmcs_conf, 0, sizeof(*vmcs_conf)); -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 47395cb38d88..50656aa3f57a 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -488,7 +488,8 @@ static inline u8 vmx_get_rvi(void) - VM_ENTRY_LOAD_BNDCFGS | \ - VM_ENTRY_PT_CONCEAL_PIP | \ - VM_ENTRY_LOAD_IA32_RTIT_CTL | \ -- VM_ENTRY_LOAD_CET_STATE) -+ VM_ENTRY_LOAD_CET_STATE | \ -+ VM_ENTRY_LOAD_IA32_FRED) - - #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS \ - (VM_EXIT_SAVE_DEBUG_CONTROLS | \ -@@ -516,7 +517,9 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) - - #define KVM_REQUIRED_VMX_SECONDARY_VM_EXIT_CONTROLS (0) --#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS (0) -+#define KVM_OPTIONAL_VMX_SECONDARY_VM_EXIT_CONTROLS \ -+ (SECONDARY_VM_EXIT_SAVE_IA32_FRED | \ -+ SECONDARY_VM_EXIT_LOAD_IA32_FRED) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ - (PIN_BASED_EXT_INTR_MASK | \ --- -2.43.0 - diff --git a/SPECS/kernel/0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet b/SPECS/kernel/0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet deleted file mode 100644 index ca4e96f2b..000000000 --- a/SPECS/kernel/0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet +++ /dev/null @@ -1,96 +0,0 @@ -From 7152d3ef6b9033f8e8801694f93b04a4b161e194 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:33 -0700 -Subject: [PATCH] KVM: x86: Add kvm_msr_{read,write}() helpers - -Wrap __kvm_{get,set}_msr() into two new helpers for KVM usage and use the -helpers to replace existing usage of the raw functions. -kvm_msr_{read,write}() are KVM-internal helpers, i.e. used when KVM needs -to get/set a MSR value for emulating CPU behavior, i.e., host_initiated == -%true in the helpers. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 3 ++- - arch/x86/kvm/cpuid.c | 2 +- - arch/x86/kvm/x86.c | 16 +++++++++++++--- - 3 files changed, 16 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index b3fff43e0510..8ab2e9f5ff60 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -2158,9 +2158,10 @@ void kvm_enable_efer_bits(u64); - bool kvm_valid_efer(struct kvm_vcpu *vcpu, u64 efer); - int kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); - int kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); --int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, bool host_initiated); - int __kvm_emulate_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); - int __kvm_emulate_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); -+int kvm_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data); -+int kvm_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data); - int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu); - int kvm_emulate_rdmsr_imm(struct kvm_vcpu *vcpu, u32 msr, int reg); - int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu); -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index cc16e28bfab2..5e43a4999a4b 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -2003,7 +2003,7 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, - if (function == 7 && index == 0) { - u64 data; - if ((*ebx & (feature_bit(RTM) | feature_bit(HLE))) && -- !__kvm_get_msr(vcpu, MSR_IA32_TSX_CTRL, &data, true) && -+ !kvm_msr_read(vcpu, MSR_IA32_TSX_CTRL, &data) && - (data & TSX_CTRL_CPUID_CLEAR)) - *ebx &= ~(feature_bit(RTM) | feature_bit(HLE)); - } else if (function == 0x80000007) { -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 9bfc44972d26..3f6cd49e172f 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1909,8 +1909,8 @@ static int kvm_set_msr_ignored_check(struct kvm_vcpu *vcpu, - * Returns 0 on success, non-0 otherwise. - * Assumes vcpu_load() was already called. - */ --int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, -- bool host_initiated) -+static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, -+ bool host_initiated) - { - struct msr_data msr; - int ret; -@@ -1936,6 +1936,16 @@ int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, - return ret; - } - -+int kvm_msr_write(struct kvm_vcpu *vcpu, u32 index, u64 data) -+{ -+ return __kvm_set_msr(vcpu, index, data, true); -+} -+ -+int kvm_msr_read(struct kvm_vcpu *vcpu, u32 index, u64 *data) -+{ -+ return __kvm_get_msr(vcpu, index, data, true); -+} -+ - static int kvm_get_msr_ignored_check(struct kvm_vcpu *vcpu, - u32 index, u64 *data, bool host_initiated) - { -@@ -12527,7 +12537,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - MSR_IA32_MISC_ENABLE_BTS_UNAVAIL; - - __kvm_set_xcr(vcpu, 0, XFEATURE_MASK_FP); -- __kvm_set_msr(vcpu, MSR_IA32_XSS, 0, true); -+ kvm_msr_write(vcpu, MSR_IA32_XSS, 0); - } - - /* All GPRs except RDX (handled below) are zeroed on RESET/INIT. */ --- -2.43.0 - diff --git a/SPECS/kernel/0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss b/SPECS/kernel/0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss deleted file mode 100644 index 4e9e86f02..000000000 --- a/SPECS/kernel/0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss +++ /dev/null @@ -1,148 +0,0 @@ -From 67beb7e6e71fcc0220699ead6e277a145fd4c1ed Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Fri, 4 Jan 2019 17:30:12 +0300 -Subject: [PATCH 03/16] PCI: Add sysfs attribute for disabling PCIe link to - downstream component - -PCIe root and downstream ports have link control register that can be -used disable the link from software. This can be useful for instance -when performing "software" hotplug on systems that do not support real -PCIe/ACPI hotplug. - -For example when used with FPGA card we can burn a new FPGA image -without need to reboot the system. - -First we remove the FGPA device from Linux PCI stack: - - # echo 1 > /sys/bus/pci/devices/0000:00:01.1/0000:02:00.0/remove - -Then we disable the link: - - # echo 1 > /sys/bus/pci/devices/0000:00:01.1/link_disable - -By doing this we prevent the kernel from accessing the hardware while we -burn the new FPGA image. Once the new FPGA is burned we can re-enable -the link and rescan the new and possibly different device: - - # echo 0 > /sys/bus/pci/devices/0000:00:01.1/link_disable - # echo 1 > /sys/bus/pci/devices/0000:00:01.1/rescan - -Signed-off-by: Mika Westerberg ---- - Documentation/ABI/testing/sysfs-bus-pci | 8 +++ - drivers/pci/pci-sysfs.c | 65 ++++++++++++++++++++++++- - 2 files changed, 72 insertions(+), 1 deletion(-) - -diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci -index 69f952fffec7..29ece4a2d5f9 100644 ---- a/Documentation/ABI/testing/sysfs-bus-pci -+++ b/Documentation/ABI/testing/sysfs-bus-pci -@@ -394,6 +394,14 @@ Description: - This is similar to /sys/bus/pci/drivers_autoprobe, but - affects only the VFs associated with a specific PF. - -+What: /sys/bus/pci/devices/.../link_disable -+Date: September 2019 -+Contact: Mika Westerberg -+Description: -+ PCIe root and downstream ports have this attribute. Writing -+ 1 causes the link to downstream component be disabled. -+ Re-enabling the link happens by writing 0 instead. -+ - What: /sys/bus/pci/devices/.../p2pmem/size - Date: November 2017 - Contact: Logan Gunthorpe -diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c -index 5eea14c1f7f5..da48d0bbfd39 100644 ---- a/drivers/pci/pci-sysfs.c -+++ b/drivers/pci/pci-sysfs.c -@@ -239,6 +239,56 @@ static ssize_t current_link_width_show(struct device *dev, - } - static DEVICE_ATTR_RO(current_link_width); - -+static ssize_t link_disable_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct pci_dev *pci_dev = to_pci_dev(dev); -+ u16 linkctl; -+ int ret; -+ -+ ret = pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, &linkctl); -+ if (ret) -+ return -EINVAL; -+ -+ return sprintf(buf, "%d\n", !!(linkctl & PCI_EXP_LNKCTL_LD)); -+} -+ -+static ssize_t link_disable_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct pci_dev *pci_dev = to_pci_dev(dev); -+ u16 linkctl; -+ bool disable; -+ int ret; -+ -+ ret = kstrtobool(buf, &disable); -+ if (ret) -+ return ret; -+ -+ ret = pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, &linkctl); -+ if (ret) -+ return -EINVAL; -+ -+ if (disable) { -+ if (linkctl & PCI_EXP_LNKCTL_LD) -+ goto out; -+ linkctl |= PCI_EXP_LNKCTL_LD; -+ } else { -+ if (!(linkctl & PCI_EXP_LNKCTL_LD)) -+ goto out; -+ linkctl &= ~PCI_EXP_LNKCTL_LD; -+ } -+ -+ ret = pcie_capability_write_word(pci_dev, PCI_EXP_LNKCTL, linkctl); -+ if (ret) -+ return ret; -+ -+out: -+ return count; -+} -+static DEVICE_ATTR_RW(link_disable); -+ - static ssize_t secondary_bus_number_show(struct device *dev, - struct device_attribute *attr, - char *buf) -@@ -660,6 +710,7 @@ static struct attribute *pcie_dev_attrs[] = { - &dev_attr_current_link_width.attr, - &dev_attr_max_link_width.attr, - &dev_attr_max_link_speed.attr, -+ &dev_attr_link_disable.attr, - NULL, - }; - -@@ -1749,8 +1800,20 @@ static umode_t pcie_dev_attrs_are_visible(struct kobject *kobj, - struct device *dev = kobj_to_dev(kobj); - struct pci_dev *pdev = to_pci_dev(dev); - -- if (pci_is_pcie(pdev)) -+ if (pci_is_pcie(pdev)) { -+ if (a == &dev_attr_link_disable.attr) { -+ switch (pci_pcie_type(pdev)) { -+ case PCI_EXP_TYPE_ROOT_PORT: -+ case PCI_EXP_TYPE_DOWNSTREAM: -+ break; -+ -+ default: -+ return 0; -+ } -+ } -+ - return a->mode; -+ } - - return 0; - } --- -2.43.0 - diff --git a/SPECS/kernel/0003-bpf-add-btf-register-unregister-API.ethernet b/SPECS/kernel/0003-bpf-add-btf-register-unregister-API.ethernet deleted file mode 100644 index cb414d1b0..000000000 --- a/SPECS/kernel/0003-bpf-add-btf-register-unregister-API.ethernet +++ /dev/null @@ -1,153 +0,0 @@ -From 532613b50ef36ffd757593933f64dde097636f30 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Mon, 14 Jun 2021 09:03:35 +0800 -Subject: [PATCH 03/19] bpf: add btf register/unregister API - -A device driver can register own BTF format buffers into the kernel. -Will be used in downstream patches by mlx5 XDP driver to advertise the -types and populated XDP meta data. - -Signed-off-by: Saeed Mahameed -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - include/linux/btf.h | 8 ++++ - kernel/bpf/btf.c | 90 +++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 98 insertions(+) - -diff --git a/include/linux/btf.h b/include/linux/btf.h -index 9eda6b113f9b..4152af147d87 100644 ---- a/include/linux/btf.h -+++ b/include/linux/btf.h -@@ -604,6 +604,8 @@ static inline bool btf_type_is_struct_ptr(struct btf *btf, const struct btf_type - - return btf_type_is_struct(t); - } -+struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size); -+void btf_unregister(struct btf *btf); - #else - static inline const struct btf_type *btf_type_by_id(const struct btf *btf, - u32 type_id) -@@ -682,5 +684,11 @@ static inline int btf_check_iter_arg(struct btf *btf, const struct btf_type *fun - { - return -EOPNOTSUPP; - } -+static inline struct btf * -+btf_register(const char *name, void *btf_data, u32 btf_data_size) -+{ -+ return NULL; -+} -+static inline void btf_unregister(struct btf *btf) { } - #endif - #endif -diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c -index 64739308902f..1478010a1629 100644 ---- a/kernel/bpf/btf.c -+++ b/kernel/bpf/btf.c -@@ -6391,6 +6391,95 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, - - #endif /* CONFIG_DEBUG_INFO_BTF_MODULES */ - -+/* TODO: reuse btf_parse_raw in btf_parse_module */ -+static struct btf *btf_parse_raw(const char *name, const void *data, unsigned int data_size) -+{ -+ struct btf_verifier_env *env = NULL; -+ struct bpf_verifier_log *log; -+ struct btf *btf = NULL; -+ int err; -+ -+ env = kzalloc(sizeof(*env), GFP_KERNEL | __GFP_NOWARN); -+ if (!env) -+ return ERR_PTR(-ENOMEM); -+ -+ log = &env->log; -+ log->level = BPF_LOG_KERNEL; -+ -+ btf = kzalloc(sizeof(*btf), GFP_KERNEL | __GFP_NOWARN); -+ if (!btf) { -+ err = -ENOMEM; -+ goto errout; -+ } -+ env->btf = btf; -+ btf->kernel_btf = true; -+ snprintf(btf->name, sizeof(btf->name), "%s", name); -+ -+ btf->data = kvmalloc(data_size, GFP_KERNEL | __GFP_NOWARN); -+ if (!btf->data) { -+ err = -ENOMEM; -+ goto errout; -+ } -+ memcpy(btf->data, data, data_size); -+ btf->data_size = data_size; -+ -+ err = btf_parse_hdr(env); -+ if (err) -+ goto errout; -+ -+ btf->nohdr_data = btf->data + btf->hdr.hdr_len; -+ -+ err = btf_parse_str_sec(env); -+ if (err) -+ goto errout; -+ -+ err = btf_check_all_metas(env); -+ if (err) -+ goto errout; -+ -+ btf_verifier_env_free(env); -+ refcount_set(&btf->refcnt, 1); -+ return btf; -+ -+errout: -+ btf_verifier_env_free(env); -+ if (btf) { -+ kvfree(btf->data); -+ kvfree(btf->types); -+ kfree(btf); -+ } -+ return ERR_PTR(err); -+} -+ -+/* TODO: reuse btf_register in btf_module_notify */ -+struct btf *btf_register(const char *name, void *btf_data, u32 btf_data_size) -+{ -+ struct btf *btf; -+ int err; -+ -+ btf = btf_parse_raw(name, btf_data, btf_data_size); -+ if (IS_ERR(btf)) -+ return btf; -+ -+ err = btf_alloc_id(btf); -+ if (err) { -+ btf_free(btf); -+ btf = ERR_PTR(err); -+ } -+ return btf; -+} -+EXPORT_SYMBOL(btf_register); -+ -+void btf_unregister(struct btf *btf) -+{ -+ if (IS_ERR(btf)) -+ return; -+ -+ /* btf_put since btf might be held by user */ -+ btf_put(btf); -+} -+EXPORT_SYMBOL(btf_unregister); -+ - struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog) - { - struct bpf_prog *tgt_prog = prog->aux->dst_prog; -@@ -8153,6 +8242,7 @@ u32 btf_obj_id(const struct btf *btf) - { - return btf->id; - } -+EXPORT_SYMBOL(btf_obj_id); - - bool btf_is_kernel(const struct btf *btf) - { --- -2.43.0 - diff --git a/SPECS/kernel/0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl b/SPECS/kernel/0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl new file mode 100644 index 000000000..e7d8e5d08 --- /dev/null +++ b/SPECS/kernel/0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl @@ -0,0 +1,65 @@ +From cf5cc639f8231faee39df708cef6edbe4802cb56 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Fri, 7 Nov 2025 20:07:28 +0100 +Subject: [PATCH 03/17] cpuidle: Add sanity check for exit latency and target + residency + +Make __cpuidle_driver_init() fail if the exit latency of one of the +driver's idle states is less than its target residency which would +break cpuidle assumptions. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Artem Bityutskiy +Reviewed-by: Christian Loehle +[ rjw: Changelog fix ] +Link: https://patch.msgid.link/12779486.O9o76ZdvQC@rafael.j.wysocki +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/driver.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c +index 9bbfa594c4425..1c295a93d5829 100644 +--- a/drivers/cpuidle/driver.c ++++ b/drivers/cpuidle/driver.c +@@ -152,7 +152,7 @@ static void cpuidle_setup_broadcast_timer(void *arg) + * __cpuidle_driver_init - initialize the driver's internal data + * @drv: a valid pointer to a struct cpuidle_driver + */ +-static void __cpuidle_driver_init(struct cpuidle_driver *drv) ++static int __cpuidle_driver_init(struct cpuidle_driver *drv) + { + int i; + +@@ -193,7 +193,17 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) + s->exit_latency_ns = 0; + else + s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); ++ ++ /* ++ * Ensure that the exit latency of a CPU idle state does not ++ * exceed its target residency which is assumed in cpuidle in ++ * multiple places. ++ */ ++ if (s->exit_latency_ns > s->target_residency_ns) ++ return -EINVAL; + } ++ ++ return 0; + } + + /** +@@ -223,7 +233,9 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) + if (cpuidle_disabled()) + return -ENODEV; + +- __cpuidle_driver_init(drv); ++ ret = __cpuidle_driver_init(drv); ++ if (ret) ++ return ret; + + ret = __cpuidle_set_driver(drv); + if (ret) +-- +2.43.0 + diff --git a/SPECS/kernel/0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt b/SPECS/kernel/0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt new file mode 100644 index 000000000..c21313475 --- /dev/null +++ b/SPECS/kernel/0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt @@ -0,0 +1,83 @@ +From 5507a6988da65bb3566d330c76261fa9e7823ad4 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Thu, 6 Dec 2018 09:52:20 +0100 +Subject: [PATCH 3/9] drm/i915: Disable tracing points on PREEMPT_RT + +Luca Abeni reported this: +| BUG: scheduling while atomic: kworker/u8:2/15203/0x00000003 +| CPU: 1 PID: 15203 Comm: kworker/u8:2 Not tainted 4.19.1-rt3 #10 +| Call Trace: +| rt_spin_lock+0x3f/0x50 +| gen6_read32+0x45/0x1d0 [i915] +| g4x_get_vblank_counter+0x36/0x40 [i915] +| trace_event_raw_event_i915_pipe_update_start+0x7d/0xf0 [i915] + +The tracing events use trace_intel_pipe_update_start() among other events +use functions acquire spinlock_t locks which are transformed into +sleeping locks on PREEMPT_RT. A few trace points use +intel_get_crtc_scanline(), others use ->get_vblank_counter() wich also +might acquire a sleeping locks on PREEMPT_RT. +At the time the arguments are evaluated within trace point, preemption +is disabled and so the locks must not be acquired on PREEMPT_RT. + +Based on this I don't see any other way than disable trace points on +PREMPT_RT. + +Acked-by: Tvrtko Ursulin +Reported-by: Luca Abeni +Cc: Steven Rostedt +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/display/intel_display_trace.h | 4 ++++ + drivers/gpu/drm/i915/i915_trace.h | 4 ++++ + drivers/gpu/drm/i915/intel_uncore_trace.h | 4 ++++ + 3 files changed, 12 insertions(+) + +diff --git a/drivers/gpu/drm/i915/display/intel_display_trace.h b/drivers/gpu/drm/i915/display/intel_display_trace.h +index 27ebc32cb61a5..a519d94700c36 100644 +--- a/drivers/gpu/drm/i915/display/intel_display_trace.h ++++ b/drivers/gpu/drm/i915/display/intel_display_trace.h +@@ -13,6 +13,10 @@ + #if !defined(__INTEL_DISPLAY_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) + #define __INTEL_DISPLAY_TRACE_H__ + ++#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) ++#define NOTRACE ++#endif ++ + #include + #include + #include +diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h +index 7ed41ce9b7085..6b87ef6005c69 100644 +--- a/drivers/gpu/drm/i915/i915_trace.h ++++ b/drivers/gpu/drm/i915/i915_trace.h +@@ -6,6 +6,10 @@ + #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) + #define _I915_TRACE_H_ + ++#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) ++#define NOTRACE ++#endif ++ + #include + #include + #include +diff --git a/drivers/gpu/drm/i915/intel_uncore_trace.h b/drivers/gpu/drm/i915/intel_uncore_trace.h +index f13ff71edf2db..3c67e267fb602 100644 +--- a/drivers/gpu/drm/i915/intel_uncore_trace.h ++++ b/drivers/gpu/drm/i915/intel_uncore_trace.h +@@ -7,6 +7,10 @@ + #if !defined(__INTEL_UNCORE_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) + #define __INTEL_UNCORE_TRACE_H__ + ++#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) ++#define NOTRACE ++#endif ++ + #include "i915_reg_defs.h" + + #include +-- +2.43.0 + diff --git a/SPECS/kernel/0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt b/SPECS/kernel/0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt deleted file mode 100644 index db980bbe3..000000000 --- a/SPECS/kernel/0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt +++ /dev/null @@ -1,44 +0,0 @@ -From 191ddbc03ac12821fda0363a4bd26042839a423c Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Mon, 25 Oct 2021 15:05:18 +0200 -Subject: [PATCH 3/9] drm/i915: Don't check for atomic context on PREEMPT_RT - -The !in_atomic() check in _wait_for_atomic() triggers on PREEMPT_RT -because the uncore::lock is a spinlock_t and does not disable -preemption or interrupts. - -Changing the uncore:lock to a raw_spinlock_t doubles the worst case -latency on an otherwise idle testbox during testing. - -Ignore _WAIT_FOR_ATOMIC_CHECK() on PREEMPT_RT. - -Reviewed-by: Tvrtko Ursulin -Link: https://lore.kernel.org/all/20211006164628.s2mtsdd2jdbfyf7g@linutronix.de/ -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/i915_utils.h | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h -index f7fb40cfdb70..9cb40c2c4b12 100644 ---- a/drivers/gpu/drm/i915/i915_utils.h -+++ b/drivers/gpu/drm/i915/i915_utils.h -@@ -267,8 +267,13 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms) - (Wmax)) - #define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000) - --/* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */ --#if IS_ENABLED(CONFIG_DRM_I915_DEBUG) && IS_ENABLED(CONFIG_PREEMPT_COUNT) -+/* -+ * If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. -+ * On PREEMPT_RT the context isn't becoming atomic because it is used in an -+ * interrupt handler or because a spinlock_t is acquired. This leads to -+ * warnings which don't occur otherwise and therefore the check is disabled. -+ */ -+#if IS_ENABLED(CONFIG_DRM_I915_DEBUG) && IS_ENABLED(CONFIG_PREEMPT_COUNT) && !defined(CONFIG_PREEMPT_RT) - # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) WARN_ON_ONCE((ATOMIC) && !in_atomic()) - #else - # define _WAIT_FOR_ATOMIC_CHECK(ATOMIC) do { } while (0) --- -2.43.0 - diff --git a/SPECS/kernel/0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm b/SPECS/kernel/0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm new file mode 100644 index 000000000..865f1ffa1 --- /dev/null +++ b/SPECS/kernel/0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm @@ -0,0 +1,189 @@ +From c6a6bbf8d3a8e0f06cfe948db0ef9597fb3bf78c Mon Sep 17 00:00:00 2001 +From: Dongwon Kim +Date: Thu, 18 Aug 2022 16:19:33 -0700 +Subject: [PATCH 3/3] drm/virtio: save and restore virtio_gpu_objects + +Host KVM/QEMU loses all graphic resourses submitted by guest OS upon +resumption from sleep or hibernation. This will cause invalid resource +errors when the guest OS makes any request to the host regarding those +resources. One way to prevent this problem is to let virtio-gpu driver +resubmit all existing resources upon resumption. A linked-list for +keeping references of all created virtio_gpu_object and its params is +added for this save and restore mechanism. Virtio-gpu objects are added +to the list whenever a new object is created and sent to the host. +All backed-up objects will then be re-sent to the host in .resume +function with 'create resource' virtio gpu command. + +Signed-off-by: Dongwon Kim +--- + drivers/gpu/drm/virtio/virtgpu_drv.c | 7 +++ + drivers/gpu/drm/virtio/virtgpu_drv.h | 10 ++++ + drivers/gpu/drm/virtio/virtgpu_kms.c | 1 + + drivers/gpu/drm/virtio/virtgpu_object.c | 65 +++++++++++++++++++++++++ + 4 files changed, 83 insertions(+) + +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c +index 905246316dc9e..e0af6aa705c3b 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.c ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.c +@@ -200,6 +200,13 @@ static int virtgpu_restore(struct virtio_device *vdev) + + virtio_device_ready(vdev); + ++ error = virtio_gpu_object_restore_all(vgdev); ++ ++ if (error) { ++ DRM_ERROR("Failed to recover objects\n"); ++ return error; ++ } ++ + error = drm_mode_config_helper_resume(dev); + if (error) { + DRM_ERROR("resume error %d\n", error); +diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h +index 1279f998c8e0d..189e2282af191 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_drv.h ++++ b/drivers/gpu/drm/virtio/virtgpu_drv.h +@@ -126,6 +126,12 @@ struct virtio_gpu_object_array { + struct drm_gem_object *objs[] __counted_by(total); + }; + ++struct virtio_gpu_object_restore { ++ struct virtio_gpu_object *bo; ++ struct virtio_gpu_object_params params; ++ struct list_head node; ++}; ++ + struct virtio_gpu_vbuffer; + struct virtio_gpu_device; + +@@ -265,6 +271,7 @@ struct virtio_gpu_device { + struct work_struct obj_free_work; + spinlock_t obj_free_lock; + struct list_head obj_free_list; ++ struct list_head obj_rec; + + struct virtio_gpu_drv_capset *capsets; + uint32_t num_capsets; +@@ -479,6 +486,9 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo); + + int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, + uint32_t *resid); ++ ++int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev); ++ + /* virtgpu_prime.c */ + int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo); +diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c +index d16be811a2bdc..3aaf9ad387cab 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_kms.c ++++ b/drivers/gpu/drm/virtio/virtgpu_kms.c +@@ -163,6 +163,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) + vgdev->fence_drv.context = dma_fence_context_alloc(1); + spin_lock_init(&vgdev->fence_drv.lock); + INIT_LIST_HEAD(&vgdev->fence_drv.fences); ++ INIT_LIST_HEAD(&vgdev->obj_rec); + INIT_LIST_HEAD(&vgdev->cap_cache); + INIT_WORK(&vgdev->config_changed_work, + virtio_gpu_config_changed_work_func); +diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c +index e6363c887500a..f3d70c417c4b3 100644 +--- a/drivers/gpu/drm/virtio/virtgpu_object.c ++++ b/drivers/gpu/drm/virtio/virtgpu_object.c +@@ -61,6 +61,38 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t + ida_free(&vgdev->resource_ida, id - 1); + } + ++static void virtio_gpu_object_save_restore_list(struct virtio_gpu_device *vgdev, ++ struct virtio_gpu_object *bo, ++ struct virtio_gpu_object_params *params) ++{ ++ struct virtio_gpu_object_restore *new; ++ ++ new = kvmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) { ++ DRM_ERROR("Fail to allocate virtio_gpu_object_restore"); ++ return; ++ } ++ ++ new->bo = bo; ++ memcpy(&new->params, params, sizeof(*params)); ++ ++ list_add_tail(&new->node, &vgdev->obj_rec); ++} ++ ++static void virtio_gpu_object_del_restore_list(struct virtio_gpu_device *vgdev, ++ struct virtio_gpu_object *bo) ++{ ++ struct virtio_gpu_object_restore *curr, *tmp; ++ ++ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { ++ if (bo == curr->bo) { ++ list_del(&curr->node); ++ kvfree(curr); ++ break; ++ } ++ } ++} ++ + void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) + { + struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; +@@ -84,6 +116,7 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) + drm_gem_object_release(&bo->base.base); + kfree(bo); + } ++ virtio_gpu_object_del_restore_list(vgdev, bo); + } + + static void virtio_gpu_free_object(struct drm_gem_object *obj) +@@ -257,8 +290,11 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + objs, fence); + virtio_gpu_object_attach(vgdev, bo, ents, nents); + } ++ /* add submitted object to restore list */ ++ virtio_gpu_object_save_restore_list(vgdev, bo, params); + + *bo_ptr = bo; ++ + return 0; + + err_put_objs: +@@ -271,3 +307,32 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, + drm_gem_shmem_free(shmem_obj); + return ret; + } ++ ++int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev) ++{ ++ struct virtio_gpu_object_restore *curr, *tmp; ++ struct virtio_gpu_mem_entry *ents; ++ unsigned int nents; ++ int ret; ++ ++ list_for_each_entry_safe(curr, tmp, &vgdev->obj_rec, node) { ++ ret = virtio_gpu_object_shmem_init(vgdev, curr->bo, &ents, &nents); ++ if (ret) ++ break; ++ ++ if (curr->params.blob) { ++ virtio_gpu_cmd_resource_create_blob(vgdev, curr->bo, &curr->params, ++ ents, nents); ++ } else if (curr->params.virgl) { ++ virtio_gpu_cmd_resource_create_3d(vgdev, curr->bo, &curr->params, ++ NULL, NULL); ++ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); ++ } else { ++ virtio_gpu_cmd_create_resource(vgdev, curr->bo, &curr->params, ++ NULL, NULL); ++ virtio_gpu_object_attach(vgdev, curr->bo, ents, nents); ++ } ++ } ++ ++ return ret; ++} +\ No newline at end of file +-- +2.43.0 + diff --git a/SPECS/kernel/0003-i2c-atr-Add-fwnode-handling.ipu b/SPECS/kernel/0003-i2c-atr-Add-fwnode-handling.ipu deleted file mode 100644 index 164ed3640..000000000 --- a/SPECS/kernel/0003-i2c-atr-Add-fwnode-handling.ipu +++ /dev/null @@ -1,52 +0,0 @@ -From bbe1d540eb9feae0b2a06aa9d8854048f161ab21 Mon Sep 17 00:00:00 2001 -From: Khai Wen Ng -Date: Thu, 28 Aug 2025 14:46:45 +0800 -Subject: [PATCH 3/6] i2c: atr: Add fwnode handling - -fwnode does not support i2c-atr nodes structure. -points to parent fwnode instead. - -Signed-off-by: Khai Wen Ng -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/i2c-atr.c | 23 +++++++++++++---------- - 1 file changed, 13 insertions(+), 10 deletions(-) - -diff --git a/drivers/i2c/i2c-atr.c b/drivers/i2c/i2c-atr.c -index f998e5f43fc0..e1285a48841a 100644 ---- a/drivers/i2c/i2c-atr.c -+++ b/drivers/i2c/i2c-atr.c -@@ -836,17 +836,20 @@ int i2c_atr_add_adapter(struct i2c_atr *atr, struct i2c_atr_adap_desc *desc) - u32 reg; - - atr_node = device_get_named_child_node(dev, "i2c-atr"); -- -- fwnode_for_each_child_node(atr_node, child) { -- ret = fwnode_property_read_u32(child, "reg", ®); -- if (ret) -- continue; -- if (chan_id == reg) -- break; -+ if (atr_node) { -+ fwnode_for_each_child_node(atr_node, child) { -+ ret = fwnode_property_read_u32(child, "reg", ®); -+ if (ret) -+ continue; -+ if (chan_id == reg) -+ break; -+ } -+ -+ device_set_node(&chan->adap.dev, child); -+ fwnode_handle_put(atr_node); -+ } else if (dev_fwnode(dev)) { -+ device_set_node(&chan->adap.dev, fwnode_handle_get(dev_fwnode(dev))); - } -- -- device_set_node(&chan->adap.dev, child); -- fwnode_handle_put(atr_node); - } - - if (desc->num_aliases > 0) { --- -2.43.0 - diff --git a/SPECS/kernel/0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c b/SPECS/kernel/0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c deleted file mode 100644 index 8bfab0fa8..000000000 --- a/SPECS/kernel/0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c +++ /dev/null @@ -1,179 +0,0 @@ -From aceac75fc246f697442498cf7fa3d6f58be88a76 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:28 +0300 -Subject: [PATCH 03/11] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and - bounce buffering - -So far only I3C private and I2C transfers have required a bounce buffer -for DMA transfers when buffer is not DMA'able. - -It was observed that when the device DMA is IOMMU mapped and the receive -length is not a multiple of DWORDs (32-bit), the last DWORD is padded -with stale data from the RX FIFO, corrupting 1-3 bytes beyond the -expected data. - -A similar issue, though less severe, occurs when an I3C target returns -less data than requested. In this case, the padding does not exceed the -requested number of bytes, assuming the device DMA is not IOMMU mapped. - -Therefore, all I3C private transfer, CCC command payload and I2C -transfer receive buffers must be properly sized for the DMA being IOMMU -mapped. Even if those buffers are already DMA safe, their size may not -be DWORD aligned. - -To prepare for the device DMA being IOMMU mapped and to address the -above issue, use helpers from I3C core for DMA mapping and bounce -buffering for all DMA transfers. - -For now, require bounce buffer only when the buffer is in the -vmalloc() area to avoid unnecessary copying with CCC commands and -DMA-safe I2C transfers. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-3-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 34 -------------------------- - drivers/i3c/master/mipi-i3c-hci/dma.c | 27 +++++++++----------- - drivers/i3c/master/mipi-i3c-hci/hci.h | 3 +-- - 3 files changed, 12 insertions(+), 52 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index 60f1175f1f37..b2977b6ac9f7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -272,34 +272,6 @@ static int i3c_hci_daa(struct i3c_master_controller *m) - return hci->cmd->perform_daa(hci); - } - --static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci, -- struct hci_xfer *xfer) --{ -- if (hci->io != &mipi_i3c_hci_dma || -- xfer->data == NULL || !is_vmalloc_addr(xfer->data)) -- return 0; -- -- if (xfer->rnw) -- xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL); -- else -- xfer->bounce_buf = kmemdup(xfer->data, -- xfer->data_len, GFP_KERNEL); -- -- return xfer->bounce_buf == NULL ? -ENOMEM : 0; --} -- --static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci, -- struct hci_xfer *xfer) --{ -- if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL) -- return; -- -- if (xfer->rnw) -- memcpy(xfer->data, xfer->bounce_buf, xfer->data_len); -- -- kfree(xfer->bounce_buf); --} -- - static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - struct i3c_priv_xfer *i3c_xfers, - int nxfers) -@@ -333,9 +305,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - } - hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]); - xfer[i].cmd_desc[0] |= CMD_0_ROC; -- ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]); -- if (ret) -- goto out; - } - last = i - 1; - xfer[last].cmd_desc[0] |= CMD_0_TOC; -@@ -359,9 +328,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - } - - out: -- for (i = 0; i < nxfers; i++) -- i3c_hci_free_safe_xfer_buf(hci, &xfer[i]); -- - hci_free_xfer(xfer, nxfers); - return ret; - } -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 491dfe70b660..351851859f02 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -349,9 +349,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci, - xfer = xfer_list + i; - if (!xfer->data) - continue; -- dma_unmap_single(&hci->master.dev, -- xfer->data_dma, xfer->data_len, -- xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE); -+ i3c_master_dma_unmap_single(xfer->dma); - } - } - -@@ -362,7 +360,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - struct hci_rh_data *rh; - unsigned int i, ring, enqueue_ptr; - u32 op1_val, op2_val; -- void *buf; - - /* For now we only use ring 0 */ - ring = 0; -@@ -373,6 +370,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - for (i = 0; i < n; i++) { - struct hci_xfer *xfer = xfer_list + i; - u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr; -+ enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE : -+ DMA_TO_DEVICE; - - /* store cmd descriptor */ - *ring_data++ = xfer->cmd_desc[0]; -@@ -391,21 +390,17 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - - /* 2nd and 3rd words of Data Buffer Descriptor Structure */ - if (xfer->data) { -- buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data; -- xfer->data_dma = -- dma_map_single(&hci->master.dev, -- buf, -- xfer->data_len, -- xfer->rnw ? -- DMA_FROM_DEVICE : -- DMA_TO_DEVICE); -- if (dma_mapping_error(&hci->master.dev, -- xfer->data_dma)) { -+ xfer->dma = i3c_master_dma_map_single(&hci->master.dev, -+ xfer->data, -+ xfer->data_len, -+ false, -+ dir); -+ if (!xfer->dma) { - hci_dma_unmap_xfer(hci, xfer_list, i); - return -ENOMEM; - } -- *ring_data++ = lower_32_bits(xfer->data_dma); -- *ring_data++ = upper_32_bits(xfer->data_dma); -+ *ring_data++ = lower_32_bits(xfer->dma->addr); -+ *ring_data++ = upper_32_bits(xfer->dma->addr); - } else { - *ring_data++ = 0; - *ring_data++ = 0; -diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h -index 69ea1d10414b..33bc4906df1f 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/hci.h -+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h -@@ -94,8 +94,7 @@ struct hci_xfer { - }; - struct { - /* DMA specific */ -- dma_addr_t data_dma; -- void *bounce_buf; -+ struct i3c_dma *dma; - int ring_number; - int ring_entry; - }; --- -2.43.0 - diff --git a/SPECS/kernel/0003-issei-implement-main-thread-and-ham-messages.security b/SPECS/kernel/0003-issei-implement-main-thread-and-ham-messages.security index 3f44cc70c..2dd880258 100644 --- a/SPECS/kernel/0003-issei-implement-main-thread-and-ham-messages.security +++ b/SPECS/kernel/0003-issei-implement-main-thread-and-ham-messages.security @@ -1,7 +1,7 @@ -From d11bfa704500611a05aca620fe7e58d982bf1aac Mon Sep 17 00:00:00 2001 +From 851cf5152bb84e8ebdec88a6298f272e7b105875 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 31 Oct 2024 10:00:50 +0200 -Subject: [PATCH 3/5] issei: implement main thread and ham messages +Subject: [PATCH 3/7] issei: implement main thread and ham messages Introduce the main thread and HECI Active Management (HAM) message handling for the ISSEI (Intel Silicon Security Engine @@ -20,17 +20,17 @@ Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- drivers/misc/issei/Makefile | 2 + - drivers/misc/issei/ham.c | 142 +++++++++++++++ + drivers/misc/issei/ham.c | 142 ++++++++++++++++ drivers/misc/issei/ham.h | 20 +++ - drivers/misc/issei/issei_dev.h | 9 + - drivers/misc/issei/main.c | 320 +++++++++++++++++++++++++++++++++ - 5 files changed, 493 insertions(+) + drivers/misc/issei/issei_dev.h | 5 + + drivers/misc/issei/main.c | 287 +++++++++++++++++++++++++++++++++ + 5 files changed, 456 insertions(+) create mode 100644 drivers/misc/issei/ham.c create mode 100644 drivers/misc/issei/ham.h create mode 100644 drivers/misc/issei/main.c diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile -index 4e8f6a435a31..712d62eb9790 100644 +index 4e8f6a435a318..712d62eb97900 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -7,3 +7,5 @@ issei-objs += cdev.o @@ -41,7 +41,7 @@ index 4e8f6a435a31..712d62eb9790 100644 +issei-objs += main.o diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c new file mode 100644 -index 000000000000..d6a81b68e351 +index 0000000000000..0fd589ffa8a87 --- /dev/null +++ b/drivers/misc/issei/ham.c @@ -0,0 +1,142 @@ @@ -94,26 +94,26 @@ index 000000000000..d6a81b68e351 + int ret; + + if (idev->rst_state != ISSEI_RST_STATE_START) { -+ dev_err(idev->dev, "Wrong state %d != %d\n", ++ dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_START); + return -EPROTO; + } + + if (length < sizeof(*res)) { -+ dev_err(idev->dev, "Small start response size %zu < %zu\n", ++ dev_err(&idev->dev, "Small start response size %zu < %zu\n", + length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->heci_capabilities_length) { -+ dev_err(idev->dev, "Wrong start response size %zu != %u\n", ++ dev_err(&idev->dev, "Wrong start response size %zu != %u\n", + length - sizeof(*res), res->heci_capabilities_length); + return -EPROTO; + } + + memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); + idev->fw_protocol_ver = res->supported_version; -+ dev_dbg(idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, ++ dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_protocol_ver, + idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); + @@ -131,18 +131,18 @@ index 000000000000..d6a81b68e351 + size_t i; + + if (idev->rst_state != ISSEI_RST_STATE_CLIENT_ENUM) { -+ dev_err(idev->dev, "Wrong state %d != %d\n", ++ dev_err(&idev->dev, "Wrong state %d != %d\n", + idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); + return -EPROTO; + } + + if (length < sizeof(*res)) { -+ dev_err(idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); ++ dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) != res->client_count * sizeof(struct ham_client_properties)) { -+ dev_err(idev->dev, "Wrong response size %zu < %zu\n", ++ dev_err(&idev->dev, "Wrong response size %zu < %zu\n", + length - sizeof(*res), + res->client_count * sizeof(struct ham_client_properties)); + return -EPROTO; @@ -150,7 +150,7 @@ index 000000000000..d6a81b68e351 + + for (i = 0; i < res->client_count; i++) { + client = &res->clients_props[i]; -+ dev_dbg(idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", ++ dev_dbg(&idev->dev, "client: id = %u ver = %u uuid = %pUb mtu = %u flags = %u", + client->client_number, client->protocol_ver, &client->client_uuid, + client->client_mtu, client->flags); + issei_fw_cl_create(idev, client->client_number, client->protocol_ver, @@ -166,7 +166,7 @@ index 000000000000..d6a81b68e351 + + /* process error */ + if (status != HAMS_SUCCESS) { -+ dev_err(idev->dev, "HAM command %u failed %u", hdr->cmd, status); ++ dev_err(&idev->dev, "HAM command %u failed %u", hdr->cmd, status); + return -EIO; + } + @@ -180,7 +180,7 @@ index 000000000000..d6a81b68e351 + break; + + default: -+ dev_err(idev->dev, "Unexpected command 0x%x", hdr->cmd); ++ dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); + ret = -EPROTO; + break; + } @@ -189,7 +189,7 @@ index 000000000000..d6a81b68e351 +} diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h new file mode 100644 -index 000000000000..33947d455302 +index 0000000000000..33947d4553029 --- /dev/null +++ b/drivers/misc/issei/ham.h @@ -0,0 +1,20 @@ @@ -214,17 +214,13 @@ index 000000000000..33947d455302 + +#endif /* _ISSEI_HAM_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h -index 4e507ce4d10e..4de4e48fb8d4 100644 +index cfac43c14ba24..082ac5b9acc2f 100644 --- a/drivers/misc/issei/issei_dev.h +++ b/drivers/misc/issei/issei_dev.h -@@ -150,4 +150,13 @@ struct issei_device { +@@ -152,4 +152,9 @@ struct issei_device { char hw[] __aligned(sizeof(void *)); }; -+void issei_device_init(struct issei_device *idev, struct device *device, -+ const struct issei_dma_length *dma_length, -+ const struct issei_hw_ops *ops); -+ +int issei_start(struct issei_device *idev); +void issei_stop(struct issei_device *idev); + @@ -233,13 +229,14 @@ index 4e507ce4d10e..4de4e48fb8d4 100644 #endif /* _ISSEI_DEV_H_ */ diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c new file mode 100644 -index 000000000000..84b58730bc23 +index 0000000000000..2e2b5b6b5aa42 --- /dev/null +++ b/drivers/misc/issei/main.c -@@ -0,0 +1,320 @@ +@@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include ++#include +#include +#include +#include @@ -272,12 +269,12 @@ index 000000000000..84b58730bc23 + ret = idev->ops->hw_reset(idev, !idev->power_down); + issei_dmam_clean(idev); + if (ret) { -+ dev_err(idev->dev, "hw_reset failed ret = %d\n", ret); ++ dev_err(&idev->dev, "hw_reset failed ret = %d\n", ret); + return ret; + } + + if (idev->power_down) { -+ dev_dbg(idev->dev, "powering down: end of reset\n"); ++ dev_dbg(&idev->dev, "powering down: end of reset\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + return -ENODEV; + } @@ -293,13 +290,13 @@ index 000000000000..84b58730bc23 + if (ret) + return ret; + -+ dev_dbg(idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, ++ dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data.host_id, + data.status, data.length); + if (issei_is_ham_rsp(data.fw_id, data.host_id)) { + ret = issei_ham_process_ham_rsp(idev, data.status, data.buf, data.length); + kfree(data.buf); + } else { -+ dev_dbg(idev->dev, "Client data\n"); ++ dev_dbg(&idev->dev, "Client data\n"); + ret = issei_cl_read_buf(idev, data.host_id, data.buf, data.length); + if (ret) + kfree(data.buf); @@ -330,22 +327,22 @@ index 000000000000..84b58730bc23 + int ret; + + while (!kthread_should_stop()) { -+ dev_dbg(idev->dev, "process_work in %d\n", idev->rst_state); ++ dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); + if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_HW_READY) { + if (!idev->power_down) -+ dev_dbg(idev->dev, "HW not ready, resetting\n"); ++ dev_dbg(&idev->dev, "HW not ready, resetting\n"); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + if (idev->power_down) + idev->rst_state = ISSEI_RST_STATE_INIT; + atomic_set(&idev->rst_irq, 0); -+ dev_dbg(idev->dev, "reset_step in %d\n", idev->rst_state); ++ dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); + timeout = MAX_SCHEDULE_TIMEOUT; + ret = 0; + switch (idev->rst_state) { + case ISSEI_RST_STATE_DISABLED: + if (idev->power_down) { -+ dev_dbg(idev->dev, "Interrupt in power down?\n"); ++ dev_dbg(&idev->dev, "Interrupt in power down?\n"); + break; + } + idev->rst_state = ISSEI_RST_STATE_INIT; @@ -358,7 +355,7 @@ index 000000000000..84b58730bc23 + if (!idev->power_down) { + idev->reset_count++; + if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { -+ dev_err(idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); ++ dev_err(&idev->dev, "reset: reached maximal consecutive resets: disabling the device\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + break; + } @@ -366,7 +363,7 @@ index 000000000000..84b58730bc23 + + ret = issei_reset(idev); + if (idev->power_down) { -+ dev_dbg(idev->dev, "Powering down\n"); ++ dev_dbg(&idev->dev, "Powering down\n"); + return 0; + } + if (!ret) { @@ -377,7 +374,7 @@ index 000000000000..84b58730bc23 + + case ISSEI_RST_STATE_HW_READY: + if (idev->ops->hw_is_ready(idev)) { -+ dev_dbg(idev->dev, "HW is ready\n"); ++ dev_dbg(&idev->dev, "HW is ready\n"); + idev->ops->hw_reset_release(idev); + idev->ops->host_set_ready(idev); + ret = idev->ops->setup_message_send(idev); @@ -386,7 +383,7 @@ index 000000000000..84b58730bc23 + timeout = msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + } + } else { -+ dev_dbg(idev->dev, "HW is not ready?\n"); ++ dev_dbg(&idev->dev, "HW is not ready?\n"); + timeout = old_timeout; + } + break; @@ -419,7 +416,7 @@ index 000000000000..84b58730bc23 + if (!ret) { + idev->reset_count = 0; + idev->rst_state = ISSEI_RST_STATE_DONE; -+ dev_dbg(idev->dev, "Reset finished successfully\n"); ++ dev_dbg(&idev->dev, "Reset finished successfully\n"); + } else if (ret == -ENODATA) { + ret = 0; + timeout = old_timeout; @@ -432,21 +429,21 @@ index 000000000000..84b58730bc23 + } + + if (ret) { -+ dev_dbg(idev->dev, "Process failed ret = %d\n", ret); ++ dev_dbg(&idev->dev, "Process failed ret = %d\n", ret); + idev->rst_state = ISSEI_RST_STATE_INIT; + continue; + } + old_timeout = wait_event_interruptible_timeout(idev->wait_rst_irq, + atomic_read(&idev->rst_irq), + timeout); -+ dev_dbg(idev->dev, "Out of wait %d %d %ld\n", idev->rst_state, ++ dev_dbg(&idev->dev, "Out of wait %d %d %ld\n", idev->rst_state, + atomic_read(&idev->rst_irq), old_timeout); + + if (idev->rst_state == ISSEI_RST_STATE_DISABLED) + continue; + + if (!atomic_read(&idev->rst_irq)) { -+ dev_dbg(idev->dev, "Timed out at state %d, resetting\n", idev->rst_state); ++ dev_dbg(&idev->dev, "Timed out at state %d, resetting\n", idev->rst_state); + idev->rst_state = ISSEI_RST_STATE_INIT; + } + } @@ -455,40 +452,6 @@ index 000000000000..84b58730bc23 +} + +/** -+ * issei_device_init - initialize issei device structure. -+ * @idev: the device structure -+ * @dev: parent device structure -+ * @dma_length: structure with DMA sizes -+ * @ops: hardware-related operations -+ */ -+void issei_device_init(struct issei_device *idev, struct device *dev, -+ const struct issei_dma_length *dma_length, -+ const struct issei_hw_ops *ops) -+{ -+ idev->dev = dev; -+ idev->power_down = false; -+ init_waitqueue_head(&idev->wait_rst_irq); -+ atomic_set(&idev->rst_irq, 0); -+ init_waitqueue_head(&idev->wait_rst_state); -+ idev->rst_state = ISSEI_RST_STATE_INIT; -+ -+ mutex_init(&idev->host_client_lock); -+ INIT_LIST_HEAD(&idev->host_client_list); -+ idev->host_client_last_id = 0; -+ idev->host_client_count = 0; -+ -+ mutex_init(&idev->fw_client_lock); -+ INIT_LIST_HEAD(&idev->fw_client_list); -+ -+ idev->dma.length = *dma_length; -+ -+ INIT_LIST_HEAD(&idev->write_queue); -+ -+ idev->ops = ops; -+} -+EXPORT_SYMBOL_GPL(issei_device_init); -+ -+/** + * issei_start - configure HW device and start processing thread. + * @idev: the device structure + * @@ -511,9 +474,9 @@ index 000000000000..84b58730bc23 + return ret; + + idev->reset_thread = kthread_run(issei_process_thread, idev, -+ "kisseiprocess/%s", dev_name(idev->dev)); ++ "kisseiprocess/%s", dev_name(&idev->dev)); + if (IS_ERR(idev->reset_thread)) { -+ dev_err(idev->dev, "unable to create process thread. ret = %d\n", ret); ++ dev_err(&idev->dev, "unable to create process thread. ret = %d\n", ret); + return PTR_ERR(idev->reset_thread); + } + diff --git a/SPECS/kernel/0003-media-i2c-add-support-for-lt6911gxd.ipu b/SPECS/kernel/0003-media-i2c-add-support-for-lt6911gxd.ipu deleted file mode 100644 index c6133b98e..000000000 --- a/SPECS/kernel/0003-media-i2c-add-support-for-lt6911gxd.ipu +++ /dev/null @@ -1,47 +0,0 @@ -From 07da798345a021ab91021da8dae73e071acfcc0d Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Wed, 11 Dec 2024 17:11:18 +0800 -Subject: [PATCH 03/11] media: i2c: add support for lt6911gxd - -Signed-off-by: linya14x -Signed-off-by: zouxiaoh ---- - drivers/media/i2c/Kconfig | 12 ++++++++++++ - drivers/media/i2c/Makefile | 1 + - 2 files changed, 13 insertions(+) - -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index 6237fe804a5c..7ec788adeec1 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -267,6 +267,18 @@ config VIDEO_IMX415 - To compile this driver as a module, choose M here: the - module will be called imx415. - -+config VIDEO_LT6911GXD -+ tristate "Lontium LT6911GXD decoder" -+ depends on ACPI || COMPILE_TEST -+ select V4L2_CCI_I2C -+ help -+ This is a Video4Linux2 sensor-level driver for the Lontium -+ LT6911GXD HDMI to MIPI CSI-2 bridge. -+ -+ To compile this driver as a module, choose M here: the -+ module will be called lt6911gxd. -+ -+ - config VIDEO_MAX9271_LIB - tristate - -diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile -index 5873d29433ee..2fbdd4c181d1 100644 ---- a/drivers/media/i2c/Makefile -+++ b/drivers/media/i2c/Makefile -@@ -162,3 +162,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o - obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o - obj-$(CONFIG_VIDEO_WM8739) += wm8739.o - obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -+obj-$(CONFIG_VIDEO_LT6911GXD) += lt6911gxd.o --- -2.43.0 - diff --git a/SPECS/kernel/0003-mei-expose-device-kind-for-ioe-device.security b/SPECS/kernel/0003-mei-expose-device-kind-for-ioe-device.security new file mode 100644 index 000000000..d7a7afe60 --- /dev/null +++ b/SPECS/kernel/0003-mei-expose-device-kind-for-ioe-device.security @@ -0,0 +1,386 @@ +From ecfca416c07938af73aa64b8a1d1f79c57a32659 Mon Sep 17 00:00:00 2001 +From: "Abliyev, Reuven" +Date: Sun, 9 Mar 2025 23:53:00 +0200 +Subject: [PATCH 3/8] mei: expose device kind for ioe device + +Detect IO extender device and set appropriate kind. +Rewrite device kind to store index instead of string internally. + +Signed-off-by: Abliyev, Reuven +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/hw-me-regs.h | 4 ++ + drivers/misc/mei/hw-me.c | 80 ++++++++++++++++++++++++++++----- + drivers/misc/mei/hw-me.h | 4 +- + drivers/misc/mei/main.c | 20 ++++++--- + drivers/misc/mei/mei_dev.h | 17 ++++--- + drivers/misc/mei/platform-vsc.c | 2 +- + 6 files changed, 104 insertions(+), 23 deletions(-) + +diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h +index a4f75dc369292..8bc61ffa123fc 100644 +--- a/drivers/misc/mei/hw-me-regs.h ++++ b/drivers/misc/mei/hw-me-regs.h +@@ -6,6 +6,8 @@ + #ifndef _MEI_HW_MEI_REGS_H_ + #define _MEI_HW_MEI_REGS_H_ + ++#include ++ + /* + * MEI device IDs + */ +@@ -139,6 +141,8 @@ + # define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070 + # define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000 + # define PCI_CFG_HFS_3_FW_SKU_SPS 0x00000060 ++# define PCI_CFG_HFS_3_EXT_SKU_MSK GENMASK(3, 0) /* IOE detection bits */ ++# define PCI_CFG_HFS_3_EXT_SKU_IOE 0x00000001 + #define PCI_CFG_HFS_4 0x64 + #define PCI_CFG_HFS_5 0x68 + # define GSC_CFG_HFS_5_BOOT_TYPE_MSK 0x00000003 +diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c +index d4612c6597844..c92a0e27787bb 100644 +--- a/drivers/misc/mei/hw-me.c ++++ b/drivers/misc/mei/hw-me.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + + #include "mei_dev.h" + #include "hbm.h" +@@ -1573,14 +1574,50 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) + fw_type == PCI_CFG_HFS_3_FW_SKU_SPS; + } + +-#define MEI_CFG_KIND_ITOUCH \ +- .kind = "itouch" ++static enum mei_dev_kind mei_cfg_kind_mei(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_MEI; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_itouch(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_ITOUCH; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_gsc(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_GSC; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_gscfi(const struct pci_dev *pdev) ++{ ++ return MEI_DEV_KIND_GSCFI; ++} ++ ++static enum mei_dev_kind mei_cfg_kind_ioe(const struct pci_dev *pdev) ++{ ++ u32 reg; + +-#define MEI_CFG_TYPE_GSC \ +- .kind = "gsc" ++ pci_bus_read_config_dword(pdev->bus, 0, PCI_CFG_HFS_3, ®); ++ trace_mei_pci_cfg_read(&pdev->dev, "PCI_CFG_HFS_3", PCI_CFG_HFS_3, reg); ++ return FIELD_GET(PCI_CFG_HFS_3_EXT_SKU_MSK, reg) == PCI_CFG_HFS_3_EXT_SKU_IOE ? ++ MEI_DEV_KIND_IOE : MEI_DEV_KIND_MEI; ++} ++ ++#define MEI_CFG_KIND_MEI \ ++ .get_kind = mei_cfg_kind_mei ++ ++#define MEI_CFG_KIND_ITOUCH \ ++ .get_kind = mei_cfg_kind_itouch + +-#define MEI_CFG_TYPE_GSCFI \ +- .kind = "gscfi" ++#define MEI_CFG_KIND_GSC \ ++ .get_kind = mei_cfg_kind_gsc ++ ++#define MEI_CFG_KIND_GSCFI \ ++ .get_kind = mei_cfg_kind_gscfi ++ ++#define MEI_CFG_KIND_IOE \ ++ .get_kind = mei_cfg_kind_ioe + + #define MEI_CFG_FW_SPS_IGN \ + .quirk_probe = mei_me_fw_type_sps_ign +@@ -1619,27 +1656,32 @@ static bool mei_me_fw_type_sps_ign(const struct pci_dev *pdev) + + /* ICH Legacy devices */ + static const struct mei_cfg mei_me_ich_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_ICH_HFS, + }; + + /* ICH devices */ + static const struct mei_cfg mei_me_ich10_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_ICH10_HFS, + }; + + /* PCH6 devices */ + static const struct mei_cfg mei_me_pch6_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH_HFS, + }; + + /* PCH7 devices */ + static const struct mei_cfg mei_me_pch7_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH_HFS, + MEI_CFG_FW_VER_SUPP, + }; + + /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ + static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_FW_NM, +@@ -1647,6 +1689,7 @@ static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { + + /* PCH8 Lynx Point and newer devices */ + static const struct mei_cfg mei_me_pch8_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + }; +@@ -1660,6 +1703,7 @@ static const struct mei_cfg mei_me_pch8_itouch_cfg = { + + /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ + static const struct mei_cfg mei_me_pch8_sps_4_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_FW_SPS_4, +@@ -1667,6 +1711,7 @@ static const struct mei_cfg mei_me_pch8_sps_4_cfg = { + + /* LBG with quirk for SPS (4.0) Firmware exclusion */ + static const struct mei_cfg mei_me_pch12_sps_4_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_FW_SPS_4, +@@ -1674,6 +1719,7 @@ static const struct mei_cfg mei_me_pch12_sps_4_cfg = { + + /* Cannon Lake and newer devices */ + static const struct mei_cfg mei_me_pch12_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, +@@ -1681,6 +1727,7 @@ static const struct mei_cfg mei_me_pch12_cfg = { + + /* Cannon Lake with quirk for SPS 5.0 and newer Firmware exclusion */ + static const struct mei_cfg mei_me_pch12_sps_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, +@@ -1699,14 +1746,26 @@ static const struct mei_cfg mei_me_pch12_itouch_sps_cfg = { + + /* Tiger Lake and newer devices */ + static const struct mei_cfg mei_me_pch15_cfg = { ++ MEI_CFG_KIND_MEI, ++ MEI_CFG_PCH8_HFS, ++ MEI_CFG_FW_VER_SUPP, ++ MEI_CFG_DMA_128, ++ MEI_CFG_TRC, ++}; ++ ++/* NVL-H devices */ ++static const struct mei_cfg mei_me_pch22_cfg = { ++ MEI_CFG_KIND_IOE, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, + MEI_CFG_TRC, + }; + ++ + /* Tiger Lake with quirk for SPS 5.0 and newer Firmware exclusion */ + static const struct mei_cfg mei_me_pch15_sps_cfg = { ++ MEI_CFG_KIND_MEI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + MEI_CFG_DMA_128, +@@ -1716,14 +1775,14 @@ static const struct mei_cfg mei_me_pch15_sps_cfg = { + + /* Graphics System Controller */ + static const struct mei_cfg mei_me_gsc_cfg = { +- MEI_CFG_TYPE_GSC, ++ MEI_CFG_KIND_GSC, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + }; + + /* Graphics System Controller Firmware Interface */ + static const struct mei_cfg mei_me_gscfi_cfg = { +- MEI_CFG_TYPE_GSCFI, ++ MEI_CFG_KIND_GSCFI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, + }; +@@ -1750,6 +1809,7 @@ static const struct mei_cfg *const mei_cfg_list[] = { + [MEI_ME_PCH15_SPS_CFG] = &mei_me_pch15_sps_cfg, + [MEI_ME_GSC_CFG] = &mei_me_gsc_cfg, + [MEI_ME_GSCFI_CFG] = &mei_me_gscfi_cfg, ++ [MEI_ME_PCH22_CFG] = &mei_me_pch22_cfg, + }; + + const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) +@@ -1777,6 +1837,7 @@ struct mei_device *mei_me_dev_init(struct device *parent, + { + struct mei_device *dev; + struct mei_me_hw *hw; ++ struct pci_dev *pdev = to_pci_dev(parent); + int i; + + dev = kzalloc(sizeof(*dev) + sizeof(*hw), GFP_KERNEL); +@@ -1792,8 +1853,7 @@ struct mei_device *mei_me_dev_init(struct device *parent, + hw->cfg = cfg; + + dev->fw_f_fw_ver_supported = cfg->fw_ver_supported; +- +- dev->kind = cfg->kind; ++ dev->kind = cfg->get_kind(pdev); + + return dev; + } +diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h +index 204b92af6c478..520f2b7bd301a 100644 +--- a/drivers/misc/mei/hw-me.h ++++ b/drivers/misc/mei/hw-me.h +@@ -27,7 +27,7 @@ + struct mei_cfg { + const struct mei_fw_status fw_status; + bool (*quirk_probe)(const struct pci_dev *pdev); +- const char *kind; ++ enum mei_dev_kind (*get_kind)(const struct pci_dev *pdev); + size_t dma_size[DMA_DSCR_NUM]; + u32 fw_ver_supported:1; + u32 hw_trc_supported:1; +@@ -110,6 +110,7 @@ static inline bool mei_me_hw_use_polling(const struct mei_me_hw *hw) + * SPS firmware exclusion. + * @MEI_ME_GSC_CFG: Graphics System Controller + * @MEI_ME_GSCFI_CFG: Graphics System Controller Firmware Interface ++ * @MEI_ME_PCH22_CFG: Platform Controller Hub Gen22 and newer + * @MEI_ME_NUM_CFG: Upper Sentinel. + */ + enum mei_cfg_idx { +@@ -130,6 +131,7 @@ enum mei_cfg_idx { + MEI_ME_PCH15_SPS_CFG, + MEI_ME_GSC_CFG, + MEI_ME_GSCFI_CFG, ++ MEI_ME_PCH22_CFG, + MEI_ME_NUM_CFG, + }; + +diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c +index 86a73684a3732..00b0781fc2933 100644 +--- a/drivers/misc/mei/main.c ++++ b/drivers/misc/mei/main.c +@@ -1155,6 +1155,15 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) + } + } + ++static const char *mei_kind_names[] = { ++ "mei", ++ "itouch", ++ "gsc", ++ "gscfi", ++ "ioe", ++ "ivsc", ++}; ++ + /** + * kind_show - display device kind + * +@@ -1168,14 +1177,13 @@ static ssize_t kind_show(struct device *device, + struct device_attribute *attr, char *buf) + { + struct mei_device *dev = dev_get_drvdata(device); +- ssize_t ret; + +- if (dev->kind) +- ret = sprintf(buf, "%s\n", dev->kind); +- else +- ret = sprintf(buf, "%s\n", "mei"); ++ BUILD_BUG_ON(ARRAY_SIZE(mei_kind_names) != MEI_DEV_KIND_MAX); + +- return ret; ++ if (dev->kind < MEI_DEV_KIND_MEI || dev->kind >= MEI_DEV_KIND_MAX) ++ return -EINVAL; ++ ++ return sysfs_emit(buf, "%s\n", mei_kind_names[dev->kind]); + } + static DEVICE_ATTR_RO(kind); + +diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h +index 0bf8d552c3eab..1db718d5c93f6 100644 +--- a/drivers/misc/mei/mei_dev.h ++++ b/drivers/misc/mei/mei_dev.h +@@ -469,6 +469,15 @@ struct mei_dev_timeouts { + unsigned long link_reset_wait; /* link reset wait timeout, in jiffies */ + }; + ++enum mei_dev_kind { ++ MEI_DEV_KIND_MEI, ++ MEI_DEV_KIND_ITOUCH, ++ MEI_DEV_KIND_GSC, ++ MEI_DEV_KIND_GSCFI, ++ MEI_DEV_KIND_IOE, ++ MEI_DEV_KIND_IVSC, ++ MEI_DEV_KIND_MAX, ++}; + /** + * struct mei_device - MEI private device struct + * +@@ -645,7 +654,7 @@ struct mei_device { + struct list_head device_list; + struct mutex cl_bus_lock; + +- const char *kind; ++ enum mei_dev_kind kind; + + #if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +@@ -906,8 +915,7 @@ static inline ssize_t mei_fw_status_str(struct mei_device *dev, + */ + static inline bool kind_is_gsc(struct mei_device *dev) + { +- /* check kind for NULL because it may be not set, like at the fist call to hw_start */ +- return dev->kind && (strcmp(dev->kind, "gsc") == 0); ++ return dev->kind == MEI_DEV_KIND_GSC; + } + + /** +@@ -919,7 +927,6 @@ static inline bool kind_is_gsc(struct mei_device *dev) + */ + static inline bool kind_is_gscfi(struct mei_device *dev) + { +- /* check kind for NULL because it may be not set, like at the fist call to hw_start */ +- return dev->kind && (strcmp(dev->kind, "gscfi") == 0); ++ return dev->kind == MEI_DEV_KIND_GSCFI; + } + #endif +diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c +index 9787b9cee71ca..5100234ba5b69 100644 +--- a/drivers/misc/mei/platform-vsc.c ++++ b/drivers/misc/mei/platform-vsc.c +@@ -350,7 +350,7 @@ static int mei_vsc_probe(struct platform_device *pdev) + mei_device_init(mei_dev, dev, false, &mei_vsc_hw_ops); + + mei_dev->fw_f_fw_ver_supported = 0; +- mei_dev->kind = "ivsc"; ++ mei_dev->kind = MEI_DEV_KIND_IVSC; + + hw = mei_dev_to_vsc_hw(mei_dev); + atomic_set(&hw->write_lock_cnt, 0); +-- +2.43.0 + diff --git a/SPECS/kernel/0003-net-core-XDP-metadata-BTF-netlink-API.ethernet b/SPECS/kernel/0003-net-core-XDP-metadata-BTF-netlink-API.ethernet new file mode 100644 index 000000000..d11d794ab --- /dev/null +++ b/SPECS/kernel/0003-net-core-XDP-metadata-BTF-netlink-API.ethernet @@ -0,0 +1,213 @@ +From 6fec66c6a28573522d164b3bd0e09b96d3364ac5 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Mon, 14 Jun 2021 11:16:14 +0800 +Subject: [PATCH 03/14] net/core: XDP metadata BTF netlink API + +Add new devlink XDP attributes to be used to query or setup XDP metadata +BTF state. + +IFLA_XDP_MD_BTF_ID: type NLA_U32. +IFLA_XDP_MD_BTF_STATE: type = NLA_U8. + +On XDP query driver reports current loaded BTF ID, and its state if +active or not. + +On XDP setup, driver will use these attributes to activate/deactivate +a specific BTF ID. + +Signed-off-by: Saeed Mahameed +Signed-off-by: Jithu Joseph +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + include/linux/netdevice.h | 15 +++++++++- + include/uapi/linux/if_link.h | 2 ++ + net/core/dev.c | 53 ++++++++++++++++++++++++++++++++++++ + net/core/rtnetlink.c | 18 +++++++++++- + 4 files changed, 86 insertions(+), 2 deletions(-) + +diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h +index c6c04cd0a6816..35a5666aa86dd 100644 +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -965,6 +965,10 @@ enum bpf_netdev_command { + */ + XDP_SETUP_PROG, + XDP_SETUP_PROG_HW, ++ /* Setup/query XDP Meta Data BTF */ ++ XDP_SETUP_MD_BTF, ++ XDP_QUERY_MD_BTF, ++ + /* BPF program for offload callbacks, invoked at program load time. */ + BPF_OFFLOAD_MAP_ALLOC, + BPF_OFFLOAD_MAP_FREE, +@@ -988,6 +992,7 @@ struct bpf_xdp_entity { + struct bpf_prog *prog; + struct bpf_xdp_link *link; + }; ++struct btf; + + struct netdev_bpf { + enum bpf_netdev_command command; +@@ -996,7 +1001,11 @@ struct netdev_bpf { + struct { + u32 flags; + struct bpf_prog *prog; +- struct netlink_ext_ack *extack; ++ }; ++ /* XDP_{SETUP/QUERY}_MD_BTF */ ++ struct { ++ u8 btf_enable; ++ u32 btf_id; + }; + /* BPF_OFFLOAD_MAP_ALLOC, BPF_OFFLOAD_MAP_FREE */ + struct { +@@ -1007,6 +1016,7 @@ struct netdev_bpf { + struct xsk_buff_pool *pool; + u16 queue_id; + } xsk; ++ struct netlink_ext_ack *extack; + }; + }; + +@@ -4271,6 +4281,9 @@ u32 dev_xdp_prog_id(struct net_device *dev, enum bpf_xdp_mode mode); + + u32 dev_get_min_mp_channel_count(const struct net_device *dev); + ++int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, ++ u8 enable); ++u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled); + int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); + int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); + int dev_forward_skb_nomtu(struct net_device *dev, struct sk_buff *skb); +diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h +index 3b491d96e52eb..611f1d846001f 100644 +--- a/include/uapi/linux/if_link.h ++++ b/include/uapi/linux/if_link.h +@@ -1912,6 +1912,8 @@ enum { + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, ++ IFLA_XDP_MD_BTF_ID, ++ IFLA_XDP_MD_BTF_STATE, + __IFLA_XDP_MAX, + }; + +diff --git a/net/core/dev.c b/net/core/dev.c +index 5b536860138d1..9e6c1580dac2f 100644 +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -10659,6 +10659,59 @@ u32 dev_get_min_mp_channel_count(const struct net_device *dev) + return 0; + } + ++/** ++ * dev_xdp_query_md_btf - Query meta data btf of a device ++ * @dev: device ++ * @enabled: 1 if enabled, 0 otherwise ++ * ++ * Returns btf id > 0 if valid ++ */ ++u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled) ++{ ++ struct netdev_bpf xdp; ++ bpf_op_t ndo_bpf; ++ ++ ndo_bpf = dev->netdev_ops->ndo_bpf; ++ if (!ndo_bpf) ++ return 0; ++ ++ memset(&xdp, 0, sizeof(xdp)); ++ xdp.command = XDP_QUERY_MD_BTF; ++ ++ if (ndo_bpf(dev, &xdp)) ++ return 0; /* 0 is an invalid btf id */ ++ ++ *enabled = xdp.btf_enable; ++ return xdp.btf_id; ++} ++ ++/** ++ * dev_xdp_setup_md_btf - enable or disable meta data btf for a device ++ * @dev: device ++ * @extack: netlink extended ack ++ * @enable: 1 to enable, 0 to disable ++ * ++ * Returns 0 on success ++ */ ++int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, ++ u8 enable) ++{ ++ struct netdev_bpf xdp; ++ bpf_op_t ndo_bpf; ++ ++ ndo_bpf = dev->netdev_ops->ndo_bpf; ++ if (!ndo_bpf) ++ return -EOPNOTSUPP; ++ ++ memset(&xdp, 0, sizeof(xdp)); ++ ++ xdp.command = XDP_SETUP_MD_BTF; ++ xdp.btf_enable = enable; ++ xdp.extack = extack; ++ ++ return ndo_bpf(dev, &xdp); ++} ++ + /** + * dev_index_reserve() - allocate an ifindex in a namespace + * @net: the applicable net namespace +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index 576d5ec3bb364..b00c602d676fe 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1734,8 +1734,9 @@ static int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev, + + static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + { ++ u32 prog_id, md_btf_id; ++ u8 md_btf_enabled = 0; + struct nlattr *xdp; +- u32 prog_id; + int err; + u8 mode; + +@@ -1768,6 +1769,10 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + goto err_cancel; + } + ++ md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); ++ nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); ++ nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); ++ + nla_nest_end(skb, xdp); + return 0; + +@@ -2302,6 +2307,8 @@ static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { + [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, + [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, + [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, ++ [IFLA_XDP_MD_BTF_ID] = { .type = NLA_U32 }, ++ [IFLA_XDP_MD_BTF_STATE] = { .type = NLA_U8 }, + }; + + static struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla, +@@ -3390,6 +3397,15 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev, + goto errout; + status |= DO_SETLINK_NOTIFY; + } ++ ++ if (xdp[IFLA_XDP_MD_BTF_STATE]) { ++ u8 enable = nla_get_u8(xdp[IFLA_XDP_MD_BTF_STATE]); ++ ++ err = dev_xdp_setup_md_btf(dev, extack, enable); ++ if (err) ++ goto errout; ++ status |= DO_SETLINK_NOTIFY; ++ } + } + + errout: +-- +2.43.0 + diff --git a/SPECS/kernel/0003-net-phy-increase-gpy-loopback-test-delay.ethernet b/SPECS/kernel/0003-net-phy-increase-gpy-loopback-test-delay.ethernet new file mode 100644 index 000000000..11366bfa2 --- /dev/null +++ b/SPECS/kernel/0003-net-phy-increase-gpy-loopback-test-delay.ethernet @@ -0,0 +1,29 @@ +From f5645b8738dd3b92a99c1dfffbc9859c3a509e6f Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Tue, 18 Oct 2022 17:12:48 +0800 +Subject: [PATCH 03/18] net: phy: increase gpy loopback test delay + +Increase the gpy loopback delay to avoid phy getting stuck in an unknown +state. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/phy/mxl-gpy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c +index 2a873f791733a..2ea1d3ba28055 100644 +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -838,7 +838,7 @@ static int gpy_loopback(struct phy_device *phydev, bool enable, int speed) + /* It takes some time for PHY device to switch into + * loopback mode. + */ +- msleep(100); ++ msleep(600); + } else { + priv->lb_dis_to = get_jiffies_64() + HZ * 3; + } +-- +2.43.0 + diff --git a/SPECS/kernel/0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt b/SPECS/kernel/0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt deleted file mode 100644 index 4af4cb4f3..000000000 --- a/SPECS/kernel/0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt +++ /dev/null @@ -1,42 +0,0 @@ -From fc4e455b2a02e268ac0e3d6e579b8295487ea614 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Thu, 24 Apr 2025 13:33:05 +0300 -Subject: [PATCH 3/4] net: thunderbolt: Allow changing MTU of the device - -This adds possibility to affect the MTU of the device. - -Signed-off-by: Mika Westerberg ---- - drivers/net/thunderbolt/main.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c -index dcaa62377808..9e46f713c390 100644 ---- a/drivers/net/thunderbolt/main.c -+++ b/drivers/net/thunderbolt/main.c -@@ -1257,11 +1257,22 @@ static void tbnet_get_stats64(struct net_device *dev, - stats->rx_missed_errors = net->stats.rx_missed_errors; - } - -+static int tbnet_change_mtu(struct net_device *dev, int new_mtu) -+{ -+ /* MTU < 68 is an error and causes problems on some kernels */ -+ if (new_mtu < 68 || new_mtu > (TBNET_MAX_MTU - ETH_HLEN)) -+ return -EINVAL; -+ -+ dev->mtu = new_mtu; -+ return 0; -+} -+ - static const struct net_device_ops tbnet_netdev_ops = { - .ndo_open = tbnet_open, - .ndo_stop = tbnet_stop, - .ndo_start_xmit = tbnet_start_xmit, - .ndo_get_stats64 = tbnet_get_stats64, -+ .ndo_change_mtu = tbnet_change_mtu, - }; - - static void tbnet_generate_mac(struct net_device *dev) --- -2.43.0 - diff --git a/SPECS/kernel/0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu b/SPECS/kernel/0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu deleted file mode 100644 index 0838230b8..000000000 --- a/SPECS/kernel/0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu +++ /dev/null @@ -1,207 +0,0 @@ -From 4e55336cf7c4aab293b4b2e2cc3bceb01924da23 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:09:58 +0800 -Subject: [PATCH 03/21] patch: staging add enbaled IPU8_INSYS_NEW_ABI - -Signed-off-by: linya14x ---- - .../staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 47 +++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-fw-isys.c | 29 ++++++++++++ - drivers/staging/media/ipu7/ipu7-isys-queue.c | 6 +++ - drivers/staging/media/ipu7/ipu7-isys-video.c | 17 +++++++ - 4 files changed, 99 insertions(+) - -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -index c42d0b7a2627..45db85eb13ec 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -@@ -251,6 +251,10 @@ struct ipu7_insys_output_link { - struct ipu7_insys_output_cropping { - u16 line_top; - u16 line_bottom; -+#ifdef IPU8_INSYS_NEW_ABI -+ u16 column_left; -+ u16 column_right; -+#endif - }; - - struct ipu7_insys_output_dpcm { -@@ -260,16 +264,55 @@ struct ipu7_insys_output_dpcm { - u8 pad; - }; - -+#ifdef IPU8_INSYS_NEW_ABI -+enum ipu_insys_cfa_dim { -+ IPU_INSYS_CFA_DIM_2x2 = 0, -+ IPU_INSYS_CFA_DIM_4x4 = 1, -+ N_IPU_INSYS_CFA_DIM -+}; -+ -+#define IPU_INSYS_MAX_BINNING_FACTOR (4U) -+#define IPU_INSYS_UPIPE_MAX_OUTPUTS (2U) -+#define IPU_INSYS_UPIPE_MAX_UOB_FIFO_ALLOC (4U) -+#define IPU_INSYS_UPIPE_STREAM_CFG_BUF_SIZE (32U) -+#define IPU_INSYS_UPIPE_FRAME_CFG_BUF_SIZE (36U) -+ -+struct ipu7_insys_upipe_output_pin { -+ ia_gofo_addr_t opaque_pin_cfg; -+ u16 plane_offset_1; -+ u16 plane_offset_2; -+ u8 single_uob_fifo; -+ u8 shared_uob_fifo; -+ u8 pad[2]; -+}; -+ -+struct ipu7_insys_capture_output_pin_cfg { -+ struct ipu7_insys_capture_output_pin_payload pin_payload; -+ ia_gofo_addr_t upipe_capture_cfg; -+}; -+ -+#endif - struct ipu7_insys_output_pin { - struct ipu7_insys_output_link link; - struct ipu7_insys_output_cropping crop; - struct ipu7_insys_output_dpcm dpcm; -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_upipe_output_pin upipe_pin_cfg; -+#endif - u32 stride; - u16 ft; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 upipe_enable; -+#endif - u8 send_irq; - u8 input_pin_id; - u8 early_ack_en; -+#ifdef IPU8_INSYS_NEW_ABI -+ u8 cfa_dim; -+ u8 binning_factor; -+#else - u8 pad[3]; -+#endif - }; - - struct ipu7_insys_input_pin { -@@ -294,7 +337,11 @@ struct ipu7_insys_stream_cfg { - }; - - struct ipu7_insys_buffset { -+#ifdef IPU8_INSYS_NEW_ABI -+ struct ipu7_insys_capture_output_pin_cfg output_pins[4]; -+#else - struct ipu7_insys_capture_output_pin_payload output_pins[4]; -+#endif - u8 capture_msg_map; - u8 frame_id; - u8 skip_frame; -diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c -index e4b9c364572b..c98326bd9fee 100644 ---- a/drivers/staging/media/ipu7/ipu7-fw-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c -@@ -268,6 +268,12 @@ void ipu7_fw_isys_dump_stream_cfg(struct device *dev, - cfg->output_pins[i].crop.line_top); - dev_dbg(dev, "\t.crop.line_bottom = %d\n", - cfg->output_pins[i].crop.line_bottom); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.crop.column_left = %d\n", -+ cfg->output_pins[i].crop.column_left); -+ dev_dbg(dev, "\t.crop.colunm_right = %d\n", -+ cfg->output_pins[i].crop.column_right); -+#endif - - dev_dbg(dev, "\t.dpcm_enable = %d\n", - cfg->output_pins[i].dpcm.enable); -@@ -275,6 +281,20 @@ void ipu7_fw_isys_dump_stream_cfg(struct device *dev, - cfg->output_pins[i].dpcm.type); - dev_dbg(dev, "\t.dpcm.predictor = %d\n", - cfg->output_pins[i].dpcm.predictor); -+#ifdef IPU8_INSYS_NEW_ABI -+ dev_dbg(dev, "\t.upipe_enable = %d\n", -+ cfg->output_pins[i].upipe_enable); -+ dev_dbg(dev, "\t.upipe_pin_cfg.opaque_pin_cfg = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.opaque_pin_cfg); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_1 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_1); -+ dev_dbg(dev, "\t.upipe_pin_cfg.plane_offset_2 = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.plane_offset_2); -+ dev_dbg(dev, "\t.upipe_pin_cfg.singel_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.single_uob_fifo); -+ dev_dbg(dev, "\t.upipe_pin_cfg.shared_uob_fifo = %d\n", -+ cfg->output_pins[i].upipe_pin_cfg.shared_uob_fifo); -+#endif - } - dev_dbg(dev, "---------------------------\n"); - } -@@ -293,9 +313,18 @@ void ipu7_fw_isys_dump_frame_buff_set(struct device *dev, - - for (i = 0; i < outputs; i++) { - dev_dbg(dev, ".output_pin[%d]:\n", i); -+#ifndef IPU8_INSYS_NEW_ABI - dev_dbg(dev, "\t.user_token = %llx\n", - buf->output_pins[i].user_token); - dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr); -+#else -+ dev_dbg(dev, "\t.pin_payload.user_token = %llx\n", -+ buf->output_pins[i].pin_payload.user_token); -+ dev_dbg(dev, "\t.pin_payload.addr = 0x%x\n", -+ buf->output_pins[i].pin_payload.addr); -+ dev_dbg(dev, "\t.pin_payload.upipe_capture_cfg = 0x%x\n", -+ buf->output_pins[i].upipe_capture_cfg); -+#endif - } - dev_dbg(dev, "---------------------------\n"); - } -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c -index 1f5fb7d20d81..527fdfe2f11c 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c -@@ -264,8 +264,14 @@ static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb, - struct ipu7_isys_video_buffer *ivb = - vb2_buffer_to_ipu7_isys_video_buffer(vvb); - -+#ifndef IPU8_INSYS_NEW_ABI - set->output_pins[aq->fw_output].addr = ivb->dma_addr; - set->output_pins[aq->fw_output].user_token = (uintptr_t)set; -+#else -+ set->output_pins[aq->fw_output].pin_payload.addr = ivb->dma_addr; -+ set->output_pins[aq->fw_output].pin_payload.user_token = (uintptr_t)set; -+ set->output_pins[aq->fw_output].upipe_capture_cfg = 0; -+#endif - } - - /* -diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c -index b3d337fe786b..de3ebf9e3d3a 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-video.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-video.c -@@ -457,10 +457,27 @@ static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av, - /* output pin crop */ - output_pin->crop.line_top = 0; - output_pin->crop.line_bottom = 0; -+#ifdef IPU8_INSYS_NEW_ABI -+ output_pin->crop.column_left = 0; -+ output_pin->crop.column_right = 0; -+#endif - - /* output de-compression */ - output_pin->dpcm.enable = 0; - -+#ifdef IPU8_INSYS_NEW_ABI -+ /* upipe_cfg */ -+ output_pin->upipe_pin_cfg.opaque_pin_cfg = 0; -+ output_pin->upipe_pin_cfg.plane_offset_1 = 0; -+ output_pin->upipe_pin_cfg.plane_offset_2 = 0; -+ output_pin->upipe_pin_cfg.single_uob_fifo = 0; -+ output_pin->upipe_pin_cfg.shared_uob_fifo = 0; -+ output_pin->upipe_enable = 0; -+ output_pin->binning_factor = 0; -+ /* stupid setting, even unused, SW still need to set a valid value */ -+ output_pin->cfa_dim = IPU_INSYS_CFA_DIM_2x2; -+#endif -+ - /* frame format type */ - pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat); - output_pin->ft = (u16)pfmt->css_pixelformat; --- -2.43.0 - diff --git a/SPECS/kernel/0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf b/SPECS/kernel/0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf new file mode 100644 index 000000000..42b964fac --- /dev/null +++ b/SPECS/kernel/0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf @@ -0,0 +1,289 @@ +From dd312063b1c9c1f5f3dddeb6c015bb05504609f9 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Tue, 30 Dec 2025 15:05:54 -0800 +Subject: [PATCH 03/13] perf/x86/intel/uncore: Support per-platform discovery + base devices + +On DMR platforms, IMH discovery tables are enumerated via PCI, while +CBB domains use MSRs, unlike earlier platforms which relied on either +PCI or MSR exclusively. + +DMR also uses different MSRs and PCI devices, requiring support for +multiple, platform-specific discovery bases. + +Introduce struct uncore_discovery_domain to hold the discovery base and +other domain-specific configuration. + +Move uncore_units_ignore into uncore_discovery_domain so a single +structure can be passed to uncore_discovery_[pci/msr]. + +No functional change intended. + +Co-developed-by: Dapeng Mi +Signed-off-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 32 +++++++++---- + arch/x86/events/intel/uncore.h | 15 ++++-- + arch/x86/events/intel/uncore_discovery.c | 58 +++++++++++++++--------- + 3 files changed, 70 insertions(+), 35 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index cd561290be8ce..e3193a37a2150 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1798,7 +1798,7 @@ static const struct uncore_plat_init lnl_uncore_init __initconst = { + static const struct uncore_plat_init ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, +- .use_discovery = true, ++ .domain[0].discovery_base = UNCORE_DISCOVERY_MSR, + }; + + static const struct uncore_plat_init icx_uncore_init __initconst = { +@@ -1817,16 +1817,18 @@ static const struct uncore_plat_init spr_uncore_init __initconst = { + .cpu_init = spr_uncore_cpu_init, + .pci_init = spr_uncore_pci_init, + .mmio_init = spr_uncore_mmio_init, +- .use_discovery = true, +- .uncore_units_ignore = spr_uncore_units_ignore, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = UNCORE_DISCOVERY_TABLE_DEVICE, ++ .domain[0].units_ignore = spr_uncore_units_ignore, + }; + + static const struct uncore_plat_init gnr_uncore_init __initconst = { + .cpu_init = gnr_uncore_cpu_init, + .pci_init = gnr_uncore_pci_init, + .mmio_init = gnr_uncore_mmio_init, +- .use_discovery = true, +- .uncore_units_ignore = gnr_uncore_units_ignore, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = UNCORE_DISCOVERY_TABLE_DEVICE, ++ .domain[0].units_ignore = gnr_uncore_units_ignore, + }; + + static const struct uncore_plat_init generic_uncore_init __initconst = { +@@ -1897,6 +1899,17 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = { + }; + MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match); + ++static bool uncore_use_discovery(struct uncore_plat_init *config) ++{ ++ int i; ++ ++ for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) ++ if (config->domain[i].discovery_base) ++ return true; ++ ++ return false; ++} ++ + static int __init intel_uncore_init(void) + { + const struct x86_cpu_id *id; +@@ -1911,15 +1924,14 @@ static int __init intel_uncore_init(void) + + id = x86_match_cpu(intel_uncore_match); + if (!id) { +- if (!uncore_no_discover && uncore_discovery(NULL)) +- uncore_init = (struct uncore_plat_init *)&generic_uncore_init; +- else ++ uncore_init = (struct uncore_plat_init *)&generic_uncore_init; ++ if (uncore_no_discover || !uncore_discovery(uncore_init)) + return -ENODEV; + } else { + uncore_init = (struct uncore_plat_init *)id->driver_data; +- if (uncore_no_discover && uncore_init->use_discovery) ++ if (uncore_no_discover && uncore_use_discovery(uncore_init)) + return -ENODEV; +- if (uncore_init->use_discovery && ++ if (uncore_use_discovery(uncore_init) && + !uncore_discovery(uncore_init)) + return -ENODEV; + } +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 568536ef28ee6..1574ffc7ee053 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -47,14 +47,21 @@ struct uncore_event_desc; + struct freerunning_counters; + struct intel_uncore_topology; + ++struct uncore_discovery_domain { ++ /* MSR address or PCI device used as the discovery base */ ++ u32 discovery_base; ++ bool base_is_pci; ++ /* The units in the discovery table should be ignored. */ ++ int *units_ignore; ++}; ++ ++#define UNCORE_DISCOVERY_DOMAINS 2 + struct uncore_plat_init { + void (*cpu_init)(void); + int (*pci_init)(void); + void (*mmio_init)(void); +- /* Discovery table is required */ +- bool use_discovery; +- /* The units in the discovery table should be ignored. */ +- int *uncore_units_ignore; ++ ++ struct uncore_discovery_domain domain[UNCORE_DISCOVERY_DOMAINS]; + }; + + struct intel_uncore_type { +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index d39f6a0b8cc35..10957163301b9 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -259,23 +259,25 @@ uncore_insert_box_info(struct uncore_unit_discovery *unit, + } + + static bool +-uncore_ignore_unit(struct uncore_unit_discovery *unit, int *ignore) ++uncore_ignore_unit(struct uncore_unit_discovery *unit, ++ struct uncore_discovery_domain *domain) + { + int i; + +- if (!ignore) ++ if (!domain || !domain->units_ignore) + return false; + +- for (i = 0; ignore[i] != UNCORE_IGNORE_END ; i++) { +- if (unit->box_type == ignore[i]) ++ for (i = 0; domain->units_ignore[i] != UNCORE_IGNORE_END ; i++) { ++ if (unit->box_type == domain->units_ignore[i]) + return true; + } + + return false; + } + +-static int __parse_discovery_table(resource_size_t addr, int die, +- bool *parsed, int *ignore) ++static int __parse_discovery_table(char *type, ++ struct uncore_discovery_domain *domain, ++ resource_size_t addr, int die, bool *parsed) + { + struct uncore_global_discovery global; + struct uncore_unit_discovery unit; +@@ -314,7 +316,7 @@ static int __parse_discovery_table(resource_size_t addr, int die, + if (unit.access_type >= UNCORE_ACCESS_MAX) + continue; + +- if (uncore_ignore_unit(&unit, ignore)) ++ if (uncore_ignore_unit(&unit, domain)) + continue; + + uncore_insert_box_info(&unit, die); +@@ -325,9 +327,9 @@ static int __parse_discovery_table(resource_size_t addr, int die, + return 0; + } + +-static int parse_discovery_table(struct pci_dev *dev, int die, +- u32 bar_offset, bool *parsed, +- int *ignore) ++static int parse_discovery_table(struct uncore_discovery_domain *domain, ++ struct pci_dev *dev, int die, ++ u32 bar_offset, bool *parsed) + { + resource_size_t addr; + u32 val; +@@ -347,17 +349,19 @@ static int parse_discovery_table(struct pci_dev *dev, int die, + } + #endif + +- return __parse_discovery_table(addr, die, parsed, ignore); ++ return __parse_discovery_table("PCI", domain, addr, die, parsed); + } + +-static bool uncore_discovery_pci(int *ignore) ++static bool uncore_discovery_pci(struct uncore_discovery_domain *domain) + { + u32 device, val, entry_id, bar_offset; + int die, dvsec = 0, ret = true; + struct pci_dev *dev = NULL; + bool parsed = false; + +- if (has_generic_discovery_table()) ++ if (domain->discovery_base) ++ device = domain->discovery_base; ++ else if (has_generic_discovery_table()) + device = UNCORE_DISCOVERY_TABLE_DEVICE; + else + device = PCI_ANY_ID; +@@ -386,7 +390,7 @@ static bool uncore_discovery_pci(int *ignore) + if (die < 0) + continue; + +- parse_discovery_table(dev, die, bar_offset, &parsed, ignore); ++ parse_discovery_table(domain, dev, die, bar_offset, &parsed); + } + } + +@@ -399,11 +403,11 @@ static bool uncore_discovery_pci(int *ignore) + return ret; + } + +-static bool uncore_discovery_msr(int *ignore) ++static bool uncore_discovery_msr(struct uncore_discovery_domain *domain) + { + unsigned long *die_mask; + bool parsed = false; +- int cpu, die; ++ int cpu, die, msr; + u64 base; + + die_mask = kcalloc(BITS_TO_LONGS(uncore_max_dies()), +@@ -411,19 +415,22 @@ static bool uncore_discovery_msr(int *ignore) + if (!die_mask) + return false; + ++ msr = domain->discovery_base ? ++ domain->discovery_base : UNCORE_DISCOVERY_MSR; ++ + cpus_read_lock(); + for_each_online_cpu(cpu) { + die = topology_logical_die_id(cpu); + if (__test_and_set_bit(die, die_mask)) + continue; + +- if (rdmsrq_safe_on_cpu(cpu, UNCORE_DISCOVERY_MSR, &base)) ++ if (rdmsrq_safe_on_cpu(cpu, msr, &base)) + continue; + + if (!base) + continue; + +- __parse_discovery_table(base, die, &parsed, ignore); ++ __parse_discovery_table("MSR", domain, base, die, &parsed); + } + + cpus_read_unlock(); +@@ -434,10 +441,19 @@ static bool uncore_discovery_msr(int *ignore) + + bool uncore_discovery(struct uncore_plat_init *init) + { +- int *ignore = init ? init->uncore_units_ignore : NULL; ++ struct uncore_discovery_domain *domain; ++ bool ret = false; ++ int i; + +- return uncore_discovery_msr(ignore) || +- uncore_discovery_pci(ignore); ++ for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) { ++ domain = &init->domain[i]; ++ if (!domain->base_is_pci) ++ ret |= uncore_discovery_msr(domain); ++ else ++ ret |= uncore_discovery_pci(domain); ++ } ++ ++ return ret; + } + + void intel_uncore_clear_discovery_tables(void) +-- +2.43.0 + diff --git a/SPECS/kernel/0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt b/SPECS/kernel/0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt new file mode 100644 index 000000000..34c48796e --- /dev/null +++ b/SPECS/kernel/0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt @@ -0,0 +1,371 @@ +From bd20eea645052e21bab1baadb1565b8dafafdac1 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:32 -0700 +Subject: [PATCH 3/6] platform/x86:intel/pmc: Rename PMC index variable to + pmc_idx +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Rename all PMC index variables to pmc_idx in core.c. This improves +code readability and consistency. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-5-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/core.c | 108 +++++++++++++------------- + 1 file changed, 54 insertions(+), 54 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index ca126a253f9d0..5f58dfa989ad5 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -312,20 +312,20 @@ static inline u8 pmc_core_reg_read_byte(struct pmc *pmc, int offset) + } + + static void pmc_core_display_map(struct seq_file *s, int index, int idx, int ip, +- int pmc_index, u8 pf_reg, const struct pmc_bit_map **pf_map) ++ int pmc_idx, u8 pf_reg, const struct pmc_bit_map **pf_map) + { + seq_printf(s, "PMC%d:PCH IP: %-2d - %-32s\tState: %s\n", +- pmc_index, ip, pf_map[idx][index].name, ++ pmc_idx, ip, pf_map[idx][index].name, + pf_map[idx][index].bit_mask & pf_reg ? "Off" : "On"); + } + + static int pmc_core_ppfear_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + u8 pf_regs[PPFEAR_MAX_NUM_ENTRIES]; + unsigned int index, iter, idx, ip = 0; +@@ -343,7 +343,7 @@ static int pmc_core_ppfear_show(struct seq_file *s, void *unused) + for (idx = 0; maps[idx]; idx++) { + for (index = 0; maps[idx][index].name && + index < pmc->map->ppfear_buckets * 8; ip++, index++) +- pmc_core_display_map(s, index, idx, ip, i, ++ pmc_core_display_map(s, index, idx, ip, pmc_idx, + pf_regs[index / 8], maps); + } + } +@@ -472,7 +472,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) + struct pmc *pmc; + const struct pmc_reg_map *map; + u32 reg; +- unsigned int pmc_index; ++ unsigned int pmc_idx; + int ltr_index; + + ltr_index = value; +@@ -480,8 +480,8 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) + * is based on the contiguous indexes from ltr_show output. + * pmc index and ltr index needs to be calculated from it. + */ +- for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_index++) { +- pmc = pmcdev->pmcs[pmc_index]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs) && ltr_index >= 0; pmc_idx++) { ++ pmc = pmcdev->pmcs[pmc_idx]; + + if (!pmc) + continue; +@@ -498,10 +498,10 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) + ltr_index = ltr_index - (map->ltr_ignore_max + 2) - 1; + } + +- if (pmc_index >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) ++ if (pmc_idx >= ARRAY_SIZE(pmcdev->pmcs) || ltr_index < 0) + return -EINVAL; + +- pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_index, ltr_index); ++ pr_debug("ltr_ignore for pmc%d: ltr_index:%d\n", pmc_idx, ltr_index); + + guard(mutex)(&pmcdev->lock); + +@@ -636,14 +636,14 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) + u64 decoded_snoop_ltr, decoded_non_snoop_ltr, val; + u32 ltr_raw_data, scale; + u16 snoop_ltr, nonsnoop_ltr; +- unsigned int i, index, ltr_index = 0; ++ unsigned int pmc_idx, index, ltr_index = 0; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc; + const struct pmc_bit_map *map; + u32 ltr_ign_reg; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -677,7 +677,7 @@ static int pmc_core_ltr_show(struct seq_file *s, void *unused) + } + + seq_printf(s, "%d\tPMC%d:%-32s\tLTR: RAW: 0x%-16x\tNon-Snoop(ns): %-16llu\tSnoop(ns): %-16llu\tLTR_IGNORE: %d\n", +- ltr_index, i, map[index].name, ltr_raw_data, ++ ltr_index, pmc_idx, map[index].name, ltr_raw_data, + decoded_non_snoop_ltr, + decoded_snoop_ltr, ltr_ign_data); + ltr_index++; +@@ -690,15 +690,15 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_ltr); + static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int pmcidx; ++ unsigned int pmc_idx; + +- for (pmcidx = 0; pmcidx < ARRAY_SIZE(pmcdev->pmcs); pmcidx++) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { + const struct pmc_bit_map **maps; + unsigned int arr_size, r_idx; + u32 offset, counter; + struct pmc *pmc; + +- pmc = pmcdev->pmcs[pmcidx]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + maps = pmc->map->s0ix_blocker_maps; +@@ -712,7 +712,7 @@ static int pmc_core_s0ix_blocker_show(struct seq_file *s, void *unused) + if (!map->blk) + continue; + counter = pmc_core_reg_read(pmc, offset); +- seq_printf(s, "PMC%d:%-30s %-30d\n", pmcidx, ++ seq_printf(s, "PMC%d:%-30s %-30d\n", pmc_idx, + map->name, counter); + offset += map->blk * S0IX_BLK_SIZE; + } +@@ -724,13 +724,13 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_s0ix_blocker); + + static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) + { +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { + struct pmc *pmc; + u32 ltr_ign; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -751,12 +751,12 @@ static void pmc_core_ltr_ignore_all(struct pmc_dev *pmcdev) + + static void pmc_core_ltr_restore_all(struct pmc_dev *pmcdev) + { +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); i++) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); pmc_idx++) { + struct pmc *pmc; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -795,10 +795,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_res); + static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + u32 offset; + +@@ -806,7 +806,7 @@ static int pmc_core_substate_sts_regs_show(struct seq_file *s, void *unused) + continue; + maps = pmc->map->lpm_sts; + offset = pmc->map->lpm_status_offset; +- pmc_core_lpm_display(pmc, NULL, s, offset, i, "STATUS", maps); ++ pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "STATUS", maps); + } + + return 0; +@@ -816,10 +816,10 @@ DEFINE_SHOW_ATTRIBUTE(pmc_core_substate_sts_regs); + static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) + { + struct pmc_dev *pmcdev = s->private; +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + u32 offset; + +@@ -827,7 +827,7 @@ static int pmc_core_substate_l_sts_regs_show(struct seq_file *s, void *unused) + continue; + maps = pmc->map->lpm_sts; + offset = pmc->map->lpm_live_status_offset; +- pmc_core_lpm_display(pmc, NULL, s, offset, i, "LIVE_STATUS", maps); ++ pmc_core_lpm_display(pmc, NULL, s, offset, pmc_idx, "LIVE_STATUS", maps); + } + + return 0; +@@ -920,11 +920,11 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) + u32 sts_offset; + u32 sts_offset_live; + u32 *lpm_req_regs; +- unsigned int mp, pmc_index; ++ unsigned int mp, pmc_idx; + int num_maps; + +- for (pmc_index = 0; pmc_index < ARRAY_SIZE(pmcdev->pmcs); ++pmc_index) { +- struct pmc *pmc = pmcdev->pmcs[pmc_index]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + const struct pmc_bit_map **maps; + + if (!pmc) +@@ -945,7 +945,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) + continue; + + /* Display the header */ +- pmc_core_substate_req_header_show(s, pmc_index, HEADER_STATUS); ++ pmc_core_substate_req_header_show(s, pmc_idx, HEADER_STATUS); + + /* Loop over maps */ + for (mp = 0; mp < num_maps; mp++) { +@@ -983,7 +983,7 @@ static int pmc_core_substate_req_regs_show(struct seq_file *s, void *unused) + } + + /* Display the element name in the first column */ +- seq_printf(s, "pmc%d: %34s |", pmc_index, map[i].name); ++ seq_printf(s, "pmc%d: %34s |", pmc_idx, map[i].name); + + /* Loop over the enabled states and display if required */ + pmc_for_each_mode(mode, pmcdev) { +@@ -1567,7 +1567,7 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + { + struct pci_dev *pcidev __free(pci_dev_put) = NULL; + struct telem_endpoint *ep; +- unsigned int i; ++ unsigned int pmc_idx; + u32 guid; + int ret; + +@@ -1575,10 +1575,10 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + if (!pcidev) + return -ENODEV; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { + struct pmc *pmc; + +- pmc = pmcdev->pmcs[i]; ++ pmc = pmcdev->pmcs[pmc_idx]; + if (!pmc) + continue; + +@@ -1610,7 +1610,7 @@ static const struct pmc_reg_map *pmc_core_find_regmap(struct pmc_info *list, u16 + return NULL; + } + +-static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) ++static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) + + { + struct pmc_ssram_telemetry pmc_ssram_telemetry; +@@ -1618,7 +1618,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + struct pmc *pmc; + int ret; + +- ret = pmc_ssram_telemetry_get_pmc_info(pmc_index, &pmc_ssram_telemetry); ++ ret = pmc_ssram_telemetry_get_pmc_info(pmc_idx, &pmc_ssram_telemetry); + if (ret) + return ret; + +@@ -1626,7 +1626,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + if (!map) + return -ENODEV; + +- pmc = pmcdev->pmcs[pmc_index]; ++ pmc = pmcdev->pmcs[pmc_idx]; + /* Memory for primary PMC has been allocated */ + if (!pmc) { + pmc = devm_kzalloc(&pmcdev->pdev->dev, sizeof(*pmc), GFP_KERNEL); +@@ -1643,7 +1643,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_index) + return -ENOMEM; + } + +- pmcdev->pmcs[pmc_index] = pmc; ++ pmcdev->pmcs[pmc_idx] = pmc; + + return 0; + } +@@ -1715,8 +1715,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) + return 0; + + unmap_regbase: +- for (unsigned int i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (unsigned int pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + + if (pmc && pmc->regbase) + iounmap(pmc->regbase); +@@ -1809,10 +1809,10 @@ static void pmc_core_do_dmi_quirks(struct pmc *pmc) + static void pmc_core_clean_structure(struct platform_device *pdev) + { + struct pmc_dev *pmcdev = platform_get_drvdata(pdev); +- unsigned int i; ++ unsigned int pmc_idx; + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + + if (pmc && pmc->regbase) + iounmap(pmc->regbase); +@@ -1972,7 +1972,7 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) + struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; + const struct pmc_bit_map **maps = pmc->map->lpm_sts; + int offset = pmc->map->lpm_status_offset; +- unsigned int i; ++ unsigned int pmc_idx, i; + + /* Check if the syspend used S0ix */ + if (pm_suspend_via_firmware()) +@@ -2010,13 +2010,13 @@ int pmc_core_resume_common(struct pmc_dev *pmcdev) + if (pmc->map->slps0_dbg_maps) + pmc_core_slps0_display(pmc, dev, NULL); + +- for (i = 0; i < ARRAY_SIZE(pmcdev->pmcs); ++i) { +- struct pmc *pmc = pmcdev->pmcs[i]; ++ for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { ++ struct pmc *pmc = pmcdev->pmcs[pmc_idx]; + + if (!pmc) + continue; + if (pmc->map->lpm_sts) +- pmc_core_lpm_display(pmc, dev, NULL, offset, i, "STATUS", maps); ++ pmc_core_lpm_display(pmc, dev, NULL, offset, pmc_idx, "STATUS", maps); + } + + return 0; +-- +2.43.0 + diff --git a/SPECS/kernel/0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal b/SPECS/kernel/0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal deleted file mode 100644 index 996d8fa2a..000000000 --- a/SPECS/kernel/0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal +++ /dev/null @@ -1,33 +0,0 @@ -From d307ddbc6471709664c7c7e0d75e5a213d901416 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:13 -0700 -Subject: [PATCH 03/11] thermal: intel: int340x: Enable power slider interface - for Panther Lake - -Set the PROC_THERMAL_FEATURE_SOC_POWER_SLIDER feature flag in -proc_thermal_pci_ids[] for Panther Lake to enable power slider interface. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-3-srinivas.pandruvada@linux.intel.com -Signed-off-by: Rafael J. Wysocki ---- - .../intel/int340x_thermal/processor_thermal_device_pci.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c -index d4d7e8e147d2..e2471768d355 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c -@@ -498,7 +498,8 @@ static const struct pci_device_id proc_thermal_pci_ids[] = { - { PCI_DEVICE_DATA(INTEL, PTL_THERMAL, PROC_THERMAL_FEATURE_RAPL | - PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_DVFS | - PROC_THERMAL_FEATURE_MSI_SUPPORT | PROC_THERMAL_FEATURE_WT_HINT | -- PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC) }, -+ PROC_THERMAL_FEATURE_POWER_FLOOR | PROC_THERMAL_FEATURE_PTC | -+ PROC_THERMAL_FEATURE_SOC_POWER_SLIDER) }, - { PCI_DEVICE_DATA(INTEL, WCL_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT | - PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR | - PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_HINT | --- -2.43.0 - diff --git a/SPECS/kernel/0003-tools-power-turbostat-Refactor-added-column-header-p.turbo b/SPECS/kernel/0003-tools-power-turbostat-Refactor-added-column-header-p.turbo new file mode 100644 index 000000000..257a5858c --- /dev/null +++ b/SPECS/kernel/0003-tools-power-turbostat-Refactor-added-column-header-p.turbo @@ -0,0 +1,216 @@ +From 34544cbf2afc6465c4f19762dc10950293ec1a2c Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 17:19:43 -0300 +Subject: [PATCH 03/21] tools/power turbostat: Refactor added column header + printing + +Over time, we built up many copies of nearly identical code... + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 137 +++++++------------------- + 1 file changed, 36 insertions(+), 101 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 7c24c2f9a0752..5d753df8706d0 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2705,6 +2705,24 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + } + } + ++/* ++ * print_name() ++ * Print column header name for 64-bit counter in 16 columns (at least 8-char plus a tab) ++ * Otherwise, allow the name + tab to fit within 8-coumn tab-stop. ++ * In both cases, left justififed, just like other turbostat columns, ++ * to allow the column values to consume the tab. ++ * ++ * Yes, 32-bit counters can overflow 8-columns, but then they are usually 64-bit counters. ++ * 64-bit counters can overflow 16-columns, but rarely do. ++ */ ++static inline int print_name(int width, int *printed, char *delim, char *name) ++{ ++ if (width == 64) ++ return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); ++ else ++ return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); ++} ++ + void print_header(char *delim) + { + struct msr_counter *mp; +@@ -2760,50 +2778,22 @@ void print_header(char *delim) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%sSMI", (printed++ ? delim : "")); + +- for (mp = sys.tp; mp; mp = mp->next) { ++ for (mp = sys.tp; mp; mp = mp->next) ++ outp += print_name(mp->width, &printed, delim, mp->name); + +- if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { +- if (mp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), mp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), mp->name); +- } else { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), mp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), mp->name); +- } +- } +- +- for (pp = sys.perf_tp; pp; pp = pp->next) { +- +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name); +- } else { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name); +- } +- } ++ for (pp = sys.perf_tp; pp; pp = pp->next) ++ outp += print_name(pp->width, &printed, delim, pp->name); + + ppmt = sys.pmt_tp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name); +- else +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name); +- ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name); + break; + } + +@@ -2836,49 +2826,23 @@ void print_header(char *delim) + outp += sprintf(outp, "%sCor_J", (printed++ ? delim : "")); + } + +- for (mp = sys.cp; mp; mp = mp->next) { +- if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { +- if (mp->width == 64) +- outp += sprintf(outp, "%s%18.18s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%10.10s", delim, mp->name); +- } else { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%s", delim, mp->name); +- } +- } +- +- for (pp = sys.perf_cp; pp; pp = pp->next) { ++ for (mp = sys.cp; mp; mp = mp->next) ++ outp += print_name(mp->width, &printed, delim, mp->name); + +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name); +- } else { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name); +- } +- } ++ for (pp = sys.perf_cp; pp; pp = pp->next) ++ outp += print_name(pp->width, &printed, delim, pp->name); + + ppmt = sys.pmt_cp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name); +- else +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); + + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name); + break; + } + +@@ -2966,51 +2930,22 @@ void print_header(char *delim) + if (DO_BIC(BIC_UNCORE_MHZ)) + outp += sprintf(outp, "%sUncMHz", (printed++ ? delim : "")); + +- for (mp = sys.pp; mp; mp = mp->next) { +- if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) { +- if (mp->width == 64) +- outp += sprintf(outp, "%s%18.18s", delim, mp->name); +- else if (mp->width == 32) +- outp += sprintf(outp, "%s%10.10s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%7.7s", delim, mp->name); +- } else { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", delim, mp->name); +- else +- outp += sprintf(outp, "%s%7.7s", delim, mp->name); +- } +- } +- +- for (pp = sys.perf_pp; pp; pp = pp->next) { ++ for (mp = sys.pp; mp; mp = mp->next) ++ outp += print_name(mp->width, &printed, delim, mp->name); + +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 64) +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), pp->name); +- } else { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8s", (printed++ ? delim : ""), pp->name); +- else +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), pp->name); +- } +- } ++ for (pp = sys.perf_pp; pp; pp = pp->next) ++ outp += print_name(pp->width, &printed, delim, pp->name); + + ppmt = sys.pmt_pp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s%10.10s", (printed++ ? delim : ""), ppmt->name); +- else +- outp += sprintf(outp, "%s%18.18s", (printed++ ? delim : ""), ppmt->name); +- ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += sprintf(outp, "%s%s", (printed++ ? delim : ""), ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name); + break; + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss b/SPECS/kernel/0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss deleted file mode 100644 index ac273cb98..000000000 --- a/SPECS/kernel/0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss +++ /dev/null @@ -1,50 +0,0 @@ -From 574db93bef3983313bf866d37c250c1076a22e76 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Tue, 12 Nov 2019 15:41:42 +0300 -Subject: [PATCH 04/16] ACPI / hotplug / PCI: Take runtime PM autosuspend into - account - -PCIe ports (the only ones we do runtime PM) are using runtime PM -autosuspend to keep the port powered on for a while after it becomes -idle. However, ACPI hotplug does not take this into account so if we get -multiple hotplug events in a short period of time we may be powering -ports on and off and then back on unnecessarily. - -For this reason call pm_runtime_put_autosuspend() for them (with the -accompanying pm_runtime_mark_last_busy()). - -Signed-off-by: Mika Westerberg ---- - drivers/pci/hotplug/acpiphp_glue.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c -index 5b1f271c6034..58686705824d 100644 ---- a/drivers/pci/hotplug/acpiphp_glue.c -+++ b/drivers/pci/hotplug/acpiphp_glue.c -@@ -683,7 +683,8 @@ static void trim_stale_devices(struct pci_dev *dev) - list_for_each_entry_safe_reverse(child, tmp, &bus->devices, bus_list) - trim_stale_devices(child); - -- pm_runtime_put(&dev->dev); -+ pm_runtime_mark_last_busy(&dev->dev); -+ pm_runtime_put_autosuspend(&dev->dev); - } - } - -@@ -725,8 +726,10 @@ static void acpiphp_check_bridge(struct acpiphp_bridge *bridge) - } - } - -- if (bridge->pci_dev) -- pm_runtime_put(&bridge->pci_dev->dev); -+ if (bridge->pci_dev) { -+ pm_runtime_mark_last_busy(&bridge->pci_dev->dev); -+ pm_runtime_put_autosuspend(&bridge->pci_dev->dev); -+ } - } - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac b/SPECS/kernel/0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac deleted file mode 100644 index 95e7e27aa..000000000 --- a/SPECS/kernel/0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac +++ /dev/null @@ -1,95 +0,0 @@ -From 0d54237ef5bc3d6d48ebe4a737f963130089726d Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Wed, 23 Jul 2025 13:44:54 +0800 -Subject: [PATCH 04/13] EDAC/skx_common: Swap memory controller index mapping - -The current mapping of memory controller indices is from physical index [1] -to logical index [2], as show below: - - skx_dev->imc[pmc].mc_mapping = lmc - -Since skx_dev->imc[] is an array of present memory controller instances, -mapping memory controller indices from logical index to physical index, -as show below, is more reasonable. This is also a preparatory step for -making skx_dev->imc[] a flexible array. - - skx_dev->imc[lmc].mc_mapping = pmc - -Both mappings are equivalent. No functional changes intended. - -[1] Indices for memory controllers include both those present to the - OS and those disabled by BIOS. - -[2] Indices for memory controllers present to the OS. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 28 ++++++++++++++++++++-------- - 1 file changed, 20 insertions(+), 8 deletions(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index 3f6a074d685c..e03268e9073f 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -130,7 +130,7 @@ static void skx_init_mc_mapping(struct skx_dev *d) - * the logical indices of the memory controllers enumerated by the - * EDAC driver. - */ -- for (int i = 0; i < NUM_IMC; i++) -+ for (int i = 0; i < d->num_imc; i++) - d->imc[i].mc_mapping = i; - } - -@@ -139,22 +139,28 @@ void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc) - edac_dbg(0, "Set the mapping of mc phy idx to logical idx: %02d -> %02d\n", - pmc, lmc); - -- d->imc[pmc].mc_mapping = lmc; -+ d->imc[lmc].mc_mapping = pmc; - } - EXPORT_SYMBOL_GPL(skx_set_mc_mapping); - --static u8 skx_get_mc_mapping(struct skx_dev *d, u8 pmc) -+static int skx_get_mc_mapping(struct skx_dev *d, u8 pmc) - { -- edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n", -- pmc, d->imc[pmc].mc_mapping); -+ for (int lmc = 0; lmc < d->num_imc; lmc++) { -+ if (d->imc[lmc].mc_mapping == pmc) { -+ edac_dbg(0, "Get the mapping of mc phy idx to logical idx: %02d -> %02d\n", -+ pmc, lmc); - -- return d->imc[pmc].mc_mapping; -+ return lmc; -+ } -+ } -+ -+ return -1; - } - - static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) - { -+ int i, lmc, len = 0; - struct skx_dev *d; -- int i, len = 0; - - if (res->addr >= skx_tohm || (res->addr >= skx_tolm && - res->addr < BIT_ULL(32))) { -@@ -218,7 +224,13 @@ static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) - return false; - } - -- res->imc = skx_get_mc_mapping(d, res->imc); -+ lmc = skx_get_mc_mapping(d, res->imc); -+ if (lmc < 0) { -+ skx_printk(KERN_ERR, "No lmc for imc %d\n", res->imc); -+ return false; -+ } -+ -+ res->imc = lmc; - - for (i = 0; i < adxl_component_count; i++) { - if (adxl_values[i] == ~0x0ull) --- -2.43.0 - diff --git a/SPECS/kernel/0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi b/SPECS/kernel/0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi deleted file mode 100644 index ce98f839b..000000000 --- a/SPECS/kernel/0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi +++ /dev/null @@ -1,68 +0,0 @@ -From 165783514c08480544f0318242343073c2feb6b3 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Fri, 17 Nov 2023 21:34:27 -0800 -Subject: [PATCH 04/44] KVM: VMX: Disable FRED if FRED consistency checks fail - -Do not virtualize FRED if FRED consistency checks fail. - -Either on broken hardware, or when run KVM on top of another hypervisor -before the underlying hypervisor implements nested FRED correctly. - -Suggested-by: Chao Gao -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Reviewed-by: Chao Gao -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Drop the cpu_feature_enabled() in cpu_has_vmx_fred() (Sean). -* Add TB from Xuelian Guo. - -Change in v4: -* Call out the reason why not check FRED VM-exit controls in - cpu_has_vmx_fred() (Chao Gao). ---- - arch/x86/kvm/vmx/capabilities.h | 10 ++++++++++ - arch/x86/kvm/vmx/vmx.c | 3 +++ - 2 files changed, 13 insertions(+) - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 422d2846980c..1100291d42ea 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -412,6 +412,16 @@ static inline bool vmx_pebs_supported(void) - !enable_mediated_pmu; - } - -+static inline bool cpu_has_vmx_fred(void) -+{ -+ /* -+ * setup_vmcs_config() guarantees FRED VM-entry/exit controls -+ * are either all set or none. So, no need to check FRED VM-exit -+ * controls. -+ */ -+ return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED); -+} -+ - static inline bool cpu_has_notify_vmexit(void) - { - return vmcs_config.cpu_based_2nd_exec_ctrl & -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index aa23ecf18cda..24c447935e9b 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -8041,6 +8041,9 @@ static __init void vmx_set_cpu_caps(void) - kvm_cpu_cap_check_and_set(X86_FEATURE_DTES64); - } - -+ if (!cpu_has_vmx_fred()) -+ kvm_cpu_cap_clear(X86_FEATURE_FRED); -+ - if (!enable_pmu) - kvm_cpu_cap_clear(X86_FEATURE_PDCM); - kvm_caps.supported_perf_cap = vmx_get_perf_capabilities(); --- -2.43.0 - diff --git a/SPECS/kernel/0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet b/SPECS/kernel/0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet deleted file mode 100644 index 2acc5420f..000000000 --- a/SPECS/kernel/0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet +++ /dev/null @@ -1,89 +0,0 @@ -From 11cb235bf8f729277eb398a45062222e55e95f2f Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 4 Jul 2025 01:49:34 -0700 -Subject: [PATCH 04/24] KVM: x86: Manually clear MPX state only on INIT - -Don't manually clear/zero MPX state on RESET, as the guest FPU state is -zero allocated and KVM only does RESET during vCPU creation, i.e. the -relevant state is guaranteed to be all zeroes. - -Opportunistically move the relevant code into a helper in anticipation of -adding support for CET shadow stacks, which also has state that is zeroed -on INIT. - -Signed-off-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 46 ++++++++++++++++++++++++++++++---------------- - 1 file changed, 30 insertions(+), 16 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 7a3a62adab95..a72f5f986984 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -12395,6 +12395,35 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) - kvfree(vcpu->arch.cpuid_entries); - } - -+static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) -+{ -+ struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate; -+ -+ /* -+ * Guest FPU state is zero allocated and so doesn't need to be manually -+ * cleared on RESET, i.e. during vCPU creation. -+ */ -+ if (!init_event || !fpstate) -+ return; -+ -+ /* -+ * On INIT, only select XSTATE components are zeroed, most components -+ * are unchanged. Currently, the only components that are zeroed and -+ * supported by KVM are MPX related. -+ */ -+ if (!kvm_mpx_supported()) -+ return; -+ -+ /* -+ * All paths that lead to INIT are required to load the guest's FPU -+ * state (because most paths are buried in KVM_RUN). -+ */ -+ kvm_put_guest_fpu(vcpu); -+ fpstate_clear_xstate_component(fpstate, XFEATURE_BNDREGS); -+ fpstate_clear_xstate_component(fpstate, XFEATURE_BNDCSR); -+ kvm_load_guest_fpu(vcpu); -+} -+ - void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - { - struct kvm_cpuid_entry2 *cpuid_0x1; -@@ -12452,22 +12481,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - kvm_async_pf_hash_reset(vcpu); - vcpu->arch.apf.halted = false; - -- if (vcpu->arch.guest_fpu.fpstate && kvm_mpx_supported()) { -- struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate; -- -- /* -- * All paths that lead to INIT are required to load the guest's -- * FPU state (because most paths are buried in KVM_RUN). -- */ -- if (init_event) -- kvm_put_guest_fpu(vcpu); -- -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDREGS); -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDCSR); -- -- if (init_event) -- kvm_load_guest_fpu(vcpu); -- } -+ kvm_xstate_reset(vcpu, init_event); - - if (!init_event) { - vcpu->arch.smbase = 0x30000; --- -2.43.0 - diff --git a/SPECS/kernel/0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl b/SPECS/kernel/0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl new file mode 100644 index 000000000..6115857ef --- /dev/null +++ b/SPECS/kernel/0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl @@ -0,0 +1,97 @@ +From 7e185270dc31e49b386520fae18a574bcd9613c6 Mon Sep 17 00:00:00 2001 +From: Christian Loehle +Date: Mon, 10 Nov 2025 12:08:19 +0000 +Subject: [PATCH 04/17] cpuidle: teo: Use this_cpu_ptr() where possible + +The cpuidle governor callbacks for update, select and reflect +are always running on the actual idle entering/exiting CPU, so +use the more optimized this_cpu_ptr() to access the internal teo +data. + +This brings down the latency-critical teo_reflect() from +static void teo_reflect(struct cpuidle_device *dev, int state) +{ +ffffffc080ffcff0: hint #0x19 +ffffffc080ffcff4: stp x29, x30, [sp, #-48]! + struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); +ffffffc080ffcff8: adrp x2, ffffffc0848c0000 +{ +ffffffc080ffcffc: add x29, sp, #0x0 +ffffffc080ffd000: stp x19, x20, [sp, #16] +ffffffc080ffd004: orr x20, xzr, x0 + struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); +ffffffc080ffd008: add x0, x2, #0xc20 +{ +ffffffc080ffd00c: stp x21, x22, [sp, #32] + struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); +ffffffc080ffd010: adrp x19, ffffffc083eb5000 +ffffffc080ffd014: add x19, x19, #0xbb0 +ffffffc080ffd018: ldr w3, [x20, #4] + + dev->last_state_idx = state; + +to + +static void teo_reflect(struct cpuidle_device *dev, int state) +{ +ffffffc080ffd034: hint #0x19 +ffffffc080ffd038: stp x29, x30, [sp, #-48]! +ffffffc080ffd03c: add x29, sp, #0x0 +ffffffc080ffd040: stp x19, x20, [sp, #16] +ffffffc080ffd044: orr x20, xzr, x0 + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); +ffffffc080ffd048: adrp x19, ffffffc083eb5000 +{ +ffffffc080ffd04c: stp x21, x22, [sp, #32] + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); +ffffffc080ffd050: add x19, x19, #0xbb0 + + dev->last_state_idx = state; + +This saves us: + adrp x2, ffffffc0848c0000 + add x0, x2, #0xc20 + ldr w3, [x20, #4] + +Signed-off-by: Christian Loehle +[ rjw: Subject tweak ] +Link: https://patch.msgid.link/20251110120819.714560-1-christian.loehle@arm.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/governors/teo.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index bfa55c1eab5bc..a3ebc2cda0933 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -155,7 +155,7 @@ static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); + */ + static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + { +- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); ++ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + int i, idx_timer = 0, idx_duration = 0; + s64 target_residency_ns; + u64 measured_ns; +@@ -268,7 +268,7 @@ static int teo_find_shallower_state(struct cpuidle_driver *drv, + static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + bool *stop_tick) + { +- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); ++ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + s64 latency_req = cpuidle_governor_latency_req(dev->cpu); + ktime_t delta_tick = TICK_NSEC / 2; + unsigned int idx_intercept_sum = 0; +@@ -504,7 +504,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + */ + static void teo_reflect(struct cpuidle_device *dev, int state) + { +- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu); ++ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + + dev->last_state_idx = state; + if (dev->poll_time_limit || +-- +2.43.0 + diff --git a/SPECS/kernel/0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt b/SPECS/kernel/0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt deleted file mode 100644 index ebcb5aa3a..000000000 --- a/SPECS/kernel/0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt +++ /dev/null @@ -1,83 +0,0 @@ -From 8b1eeb83d121280a2f8f91c616e80b79a0a6223a Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Thu, 6 Dec 2018 09:52:20 +0100 -Subject: [PATCH 4/9] drm/i915: Disable tracing points on PREEMPT_RT - -Luca Abeni reported this: -| BUG: scheduling while atomic: kworker/u8:2/15203/0x00000003 -| CPU: 1 PID: 15203 Comm: kworker/u8:2 Not tainted 4.19.1-rt3 #10 -| Call Trace: -| rt_spin_lock+0x3f/0x50 -| gen6_read32+0x45/0x1d0 [i915] -| g4x_get_vblank_counter+0x36/0x40 [i915] -| trace_event_raw_event_i915_pipe_update_start+0x7d/0xf0 [i915] - -The tracing events use trace_intel_pipe_update_start() among other events -use functions acquire spinlock_t locks which are transformed into -sleeping locks on PREEMPT_RT. A few trace points use -intel_get_crtc_scanline(), others use ->get_vblank_counter() wich also -might acquire a sleeping locks on PREEMPT_RT. -At the time the arguments are evaluated within trace point, preemption -is disabled and so the locks must not be acquired on PREEMPT_RT. - -Based on this I don't see any other way than disable trace points on -PREMPT_RT. - -Acked-by: Tvrtko Ursulin -Reported-by: Luca Abeni -Cc: Steven Rostedt -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/display/intel_display_trace.h | 4 ++++ - drivers/gpu/drm/i915/i915_trace.h | 4 ++++ - drivers/gpu/drm/i915/intel_uncore_trace.h | 4 ++++ - 3 files changed, 12 insertions(+) - -diff --git a/drivers/gpu/drm/i915/display/intel_display_trace.h b/drivers/gpu/drm/i915/display/intel_display_trace.h -index 27ebc32cb61a..a519d94700c3 100644 ---- a/drivers/gpu/drm/i915/display/intel_display_trace.h -+++ b/drivers/gpu/drm/i915/display/intel_display_trace.h -@@ -13,6 +13,10 @@ - #if !defined(__INTEL_DISPLAY_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) - #define __INTEL_DISPLAY_TRACE_H__ - -+#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) -+#define NOTRACE -+#endif -+ - #include - #include - #include -diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h -index 7ed41ce9b708..6b87ef6005c6 100644 ---- a/drivers/gpu/drm/i915/i915_trace.h -+++ b/drivers/gpu/drm/i915/i915_trace.h -@@ -6,6 +6,10 @@ - #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) - #define _I915_TRACE_H_ - -+#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) -+#define NOTRACE -+#endif -+ - #include - #include - #include -diff --git a/drivers/gpu/drm/i915/intel_uncore_trace.h b/drivers/gpu/drm/i915/intel_uncore_trace.h -index f13ff71edf2d..3c67e267fb60 100644 ---- a/drivers/gpu/drm/i915/intel_uncore_trace.h -+++ b/drivers/gpu/drm/i915/intel_uncore_trace.h -@@ -7,6 +7,10 @@ - #if !defined(__INTEL_UNCORE_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) - #define __INTEL_UNCORE_TRACE_H__ - -+#if defined(CONFIG_PREEMPT_RT) && !defined(NOTRACE) -+#define NOTRACE -+#endif -+ - #include "i915_reg_defs.h" - - #include --- -2.43.0 - diff --git a/SPECS/kernel/0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt b/SPECS/kernel/0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt new file mode 100644 index 000000000..19132ca85 --- /dev/null +++ b/SPECS/kernel/0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt @@ -0,0 +1,94 @@ +From 71e4d60f9596e1bd36d28f8f1b84b1e2ee16bfa6 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Wed, 8 Sep 2021 19:03:41 +0200 +Subject: [PATCH 4/9] drm/i915/gt: Use spin_lock_irq() instead of + local_irq_disable() + spin_lock() + +execlists_dequeue() is invoked from a function which uses +local_irq_disable() to disable interrupts so the spin_lock() behaves +like spin_lock_irq(). +This breaks PREEMPT_RT because local_irq_disable() + spin_lock() is not +the same as spin_lock_irq(). + +execlists_dequeue_irq() and execlists_dequeue() has each one caller +only. If intel_engine_cs::active::lock is acquired and released with the +_irq suffix then it behaves almost as if execlists_dequeue() would be +invoked with disabled interrupts. The difference is the last part of the +function which is then invoked with enabled interrupts. +I can't tell if this makes a difference. From looking at it, it might +work to move the last unlock at the end of the function as I didn't find +anything that would acquire the lock again. + +Reported-by: Clark Williams +Signed-off-by: Sebastian Andrzej Siewior +Reviewed-by: Maarten Lankhorst +--- + .../drm/i915/gt/intel_execlists_submission.c | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +index 7f389cb0bde44..607c9a7b3a0a6 100644 +--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c ++++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +@@ -1298,7 +1298,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + * and context switches) submission. + */ + +- spin_lock(&sched_engine->lock); ++ spin_lock_irq(&sched_engine->lock); + + /* + * If the queue is higher priority than the last +@@ -1398,7 +1398,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + * Even if ELSP[1] is occupied and not worthy + * of timeslices, our queue might be. + */ +- spin_unlock(&sched_engine->lock); ++ spin_unlock_irq(&sched_engine->lock); + return; + } + } +@@ -1424,7 +1424,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + + if (last && !can_merge_rq(last, rq)) { + spin_unlock(&ve->base.sched_engine->lock); +- spin_unlock(&engine->sched_engine->lock); ++ spin_unlock_irq(&engine->sched_engine->lock); + return; /* leave this for another sibling */ + } + +@@ -1586,7 +1586,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + */ + sched_engine->queue_priority_hint = queue_prio(sched_engine); + i915_sched_engine_reset_on_empty(sched_engine); +- spin_unlock(&sched_engine->lock); ++ spin_unlock_irq(&sched_engine->lock); + + /* + * We can skip poking the HW if we ended up with exactly the same set +@@ -1612,13 +1612,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) + } + } + +-static void execlists_dequeue_irq(struct intel_engine_cs *engine) +-{ +- local_irq_disable(); /* Suspend interrupts across request submission */ +- execlists_dequeue(engine); +- local_irq_enable(); /* flush irq_work (e.g. breadcrumb enabling) */ +-} +- + static void clear_ports(struct i915_request **ports, int count) + { + memset_p((void **)ports, NULL, count); +@@ -2473,7 +2466,7 @@ static void execlists_submission_tasklet(struct tasklet_struct *t) + } + + if (!engine->execlists.pending[0]) { +- execlists_dequeue_irq(engine); ++ execlists_dequeue(engine); + start_timeslice(engine); + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c b/SPECS/kernel/0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c deleted file mode 100644 index 56c27efd4..000000000 --- a/SPECS/kernel/0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c +++ /dev/null @@ -1,194 +0,0 @@ -From 25bca0aade9d59b2c2977c68b8d2d0ae91873c30 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:29 +0300 -Subject: [PATCH 04/11] i3c: mipi-i3c-hci: Use physical device pointer with DMA - API - -DMA transfer faults on Intel hardware when the IOMMU is enabled and -driver initialization will fail when attempting to do the first transfer: - - DMAR: DRHD: handling fault status reg 2 - DMAR: [DMA Read NO_PASID] Request device [00:11.0] fault addr 0x676e3000 [fault reason 0x71] SM: Present bit in first-level paging entry is clear - i3c mipi-i3c-hci.0: ring 0: Transfer Aborted - mipi-i3c-hci mipi-i3c-hci.0: probe with driver mipi-i3c-hci failed with error -62 - -Reason for this is that the IOMMU setup is done for the physical devices -only and not for the virtual I3C Controller device object. - -Therefore use the pointer to a physical device object with the DMA API. - -Due to a data corruption observation when the device DMA is IOMMU -mapped, a properly sized receive bounce buffer is required if transfer -length is not a multiple of DWORDs. - -Reported-by: -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-4-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/dma.c | 46 +++++++++++++++++++-------- - 1 file changed, 33 insertions(+), 13 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 351851859f02..09688ada4912 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - - #include "hci.h" - #include "cmd.h" -@@ -138,6 +139,7 @@ struct hci_rh_data { - }; - - struct hci_rings_data { -+ struct device *sysdev; - unsigned int total; - struct hci_rh_data headers[] __counted_by(total); - }; -@@ -165,20 +167,20 @@ static void hci_dma_cleanup(struct i3c_hci *hci) - rh_reg_write(IBI_SETUP, 0); - - if (rh->xfer) -- dma_free_coherent(&hci->master.dev, -+ dma_free_coherent(rings->sysdev, - rh->xfer_struct_sz * rh->xfer_entries, - rh->xfer, rh->xfer_dma); - if (rh->resp) -- dma_free_coherent(&hci->master.dev, -+ dma_free_coherent(rings->sysdev, - rh->resp_struct_sz * rh->xfer_entries, - rh->resp, rh->resp_dma); - kfree(rh->src_xfers); - if (rh->ibi_status) -- dma_free_coherent(&hci->master.dev, -+ dma_free_coherent(rings->sysdev, - rh->ibi_status_sz * rh->ibi_status_entries, - rh->ibi_status, rh->ibi_status_dma); - if (rh->ibi_data_dma) -- dma_unmap_single(&hci->master.dev, rh->ibi_data_dma, -+ dma_unmap_single(rings->sysdev, rh->ibi_data_dma, - rh->ibi_chunk_sz * rh->ibi_chunks_total, - DMA_FROM_DEVICE); - kfree(rh->ibi_data); -@@ -194,11 +196,23 @@ static int hci_dma_init(struct i3c_hci *hci) - { - struct hci_rings_data *rings; - struct hci_rh_data *rh; -+ struct device *sysdev; - u32 regval; - unsigned int i, nr_rings, xfers_sz, resps_sz; - unsigned int ibi_status_ring_sz, ibi_data_ring_sz; - int ret; - -+ /* -+ * Set pointer to a physical device that does DMA and has IOMMU setup -+ * done for it in case of enabled IOMMU and use it with the DMA API. -+ * Here such device is either -+ * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or -+ * grandparent (PCI enumeration). -+ */ -+ sysdev = hci->master.dev.parent; -+ if (sysdev->parent && dev_is_pci(sysdev->parent)) -+ sysdev = sysdev->parent; -+ - regval = rhs_reg_read(CONTROL); - nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval); - dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings); -@@ -213,6 +227,7 @@ static int hci_dma_init(struct i3c_hci *hci) - return -ENOMEM; - hci->io_data = rings; - rings->total = nr_rings; -+ rings->sysdev = sysdev; - - regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total); - rhs_reg_write(CONTROL, regval); -@@ -239,9 +254,9 @@ static int hci_dma_init(struct i3c_hci *hci) - xfers_sz = rh->xfer_struct_sz * rh->xfer_entries; - resps_sz = rh->resp_struct_sz * rh->xfer_entries; - -- rh->xfer = dma_alloc_coherent(&hci->master.dev, xfers_sz, -+ rh->xfer = dma_alloc_coherent(rings->sysdev, xfers_sz, - &rh->xfer_dma, GFP_KERNEL); -- rh->resp = dma_alloc_coherent(&hci->master.dev, resps_sz, -+ rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz, - &rh->resp_dma, GFP_KERNEL); - rh->src_xfers = - kmalloc_array(rh->xfer_entries, sizeof(*rh->src_xfers), -@@ -295,16 +310,16 @@ static int hci_dma_init(struct i3c_hci *hci) - ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total; - - rh->ibi_status = -- dma_alloc_coherent(&hci->master.dev, ibi_status_ring_sz, -+ dma_alloc_coherent(rings->sysdev, ibi_status_ring_sz, - &rh->ibi_status_dma, GFP_KERNEL); - rh->ibi_data = kmalloc(ibi_data_ring_sz, GFP_KERNEL); - ret = -ENOMEM; - if (!rh->ibi_status || !rh->ibi_data) - goto err_out; - rh->ibi_data_dma = -- dma_map_single(&hci->master.dev, rh->ibi_data, -+ dma_map_single(rings->sysdev, rh->ibi_data, - ibi_data_ring_sz, DMA_FROM_DEVICE); -- if (dma_mapping_error(&hci->master.dev, rh->ibi_data_dma)) { -+ if (dma_mapping_error(rings->sysdev, rh->ibi_data_dma)) { - rh->ibi_data_dma = 0; - ret = -ENOMEM; - goto err_out; -@@ -372,6 +387,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr; - enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE : - DMA_TO_DEVICE; -+ bool need_bounce; - - /* store cmd descriptor */ - *ring_data++ = xfer->cmd_desc[0]; -@@ -390,10 +406,13 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, - - /* 2nd and 3rd words of Data Buffer Descriptor Structure */ - if (xfer->data) { -- xfer->dma = i3c_master_dma_map_single(&hci->master.dev, -+ need_bounce = device_iommu_mapped(rings->sysdev) && -+ xfer->rnw && -+ xfer->data_len != ALIGN(xfer->data_len, 4); -+ xfer->dma = i3c_master_dma_map_single(rings->sysdev, - xfer->data, - xfer->data_len, -- false, -+ need_bounce, - dir); - if (!xfer->dma) { - hci_dma_unmap_xfer(hci, xfer_list, i); -@@ -581,6 +600,7 @@ static void hci_dma_recycle_ibi_slot(struct i3c_hci *hci, - - static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - { -+ struct hci_rings_data *rings = hci->io_data; - struct i3c_dev_desc *dev; - struct i3c_hci_dev_data *dev_data; - struct hci_dma_dev_ibi_data *dev_ibi; -@@ -691,7 +711,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - * rh->ibi_chunk_sz; - if (first_part > ibi_size) - first_part = ibi_size; -- dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma, -+ dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma, - first_part, DMA_FROM_DEVICE); - memcpy(slot->data, ring_ibi_data, first_part); - -@@ -700,7 +720,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - /* we wrap back to the start and copy remaining data */ - ring_ibi_data = rh->ibi_data; - ring_ibi_data_dma = rh->ibi_data_dma; -- dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma, -+ dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma, - ibi_size - first_part, DMA_FROM_DEVICE); - memcpy(slot->data + first_part, ring_ibi_data, - ibi_size - first_part); --- -2.43.0 - diff --git a/SPECS/kernel/0004-issei-add-heci-hardware-module.security b/SPECS/kernel/0004-issei-add-heci-hardware-module.security index 846da87b5..076f29c9e 100644 --- a/SPECS/kernel/0004-issei-add-heci-hardware-module.security +++ b/SPECS/kernel/0004-issei-add-heci-hardware-module.security @@ -1,7 +1,7 @@ -From 973f2c7ef086e185271b1e8b12f3a9383a087b26 Mon Sep 17 00:00:00 2001 +From dfc608acd03571d2a2093d92a2c4ed4f372cd908 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 31 Oct 2024 10:08:57 +0200 -Subject: [PATCH 4/5] issei: add heci hardware module +Subject: [PATCH 4/7] issei: add heci hardware module Add support for the ISSEI (Intel Silicon Security Engine Interface) HECI PCI devices. @@ -16,18 +16,18 @@ Signed-off-by: Alexander Usyskin --- drivers/misc/issei/Kconfig | 14 + drivers/misc/issei/Makefile | 4 + - drivers/misc/issei/hw_heci.c | 552 ++++++++++++++++++++++++++++++ + drivers/misc/issei/hw_heci.c | 548 ++++++++++++++++++++++++++++++ drivers/misc/issei/hw_heci.h | 54 +++ drivers/misc/issei/hw_heci_regs.h | 35 ++ - drivers/misc/issei/pci_heci.c | 166 +++++++++ - 6 files changed, 825 insertions(+) + drivers/misc/issei/pci_heci.c | 144 ++++++++ + 6 files changed, 799 insertions(+) create mode 100644 drivers/misc/issei/hw_heci.c create mode 100644 drivers/misc/issei/hw_heci.h create mode 100644 drivers/misc/issei/hw_heci_regs.h create mode 100644 drivers/misc/issei/pci_heci.c diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig -index ffd027e826df..5fbdcb30e20d 100644 +index ffd027e826dfd..5fbdcb30e20d5 100644 --- a/drivers/misc/issei/Kconfig +++ b/drivers/misc/issei/Kconfig @@ -11,3 +11,17 @@ config INTEL_SSEI @@ -49,7 +49,7 @@ index ffd027e826df..5fbdcb30e20d 100644 + +endif diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile -index 712d62eb9790..fb904fda3949 100644 +index 712d62eb97900..fb904fda39499 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -9,3 +9,7 @@ issei-objs += fw_client.o @@ -62,10 +62,10 @@ index 712d62eb9790..fb904fda3949 100644 +issei-heci-objs += hw_heci.o diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c new file mode 100644 -index 000000000000..7e6ca72e873c +index 0000000000000..6efb29f5d759e --- /dev/null +++ b/drivers/misc/issei/hw_heci.c -@@ -0,0 +1,552 @@ +@@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2025 Intel Corporation */ +#include @@ -217,7 +217,7 @@ index 000000000000..7e6ca72e873c + if (filled_slots > buffer_depth) + return -EOVERFLOW; + -+ dev_dbg(idev->dev, "filled_slots = %08x\n", filled_slots); ++ dev_dbg(&idev->dev, "filled_slots = %08x\n", filled_slots); + return filled_slots; +} + @@ -357,7 +357,7 @@ index 000000000000..7e6ca72e873c + * we need to clean H_CSR_RST bit to start a successful reset sequence. + */ + if (reg & H_CSR_RST) { -+ dev_warn(idev->dev, "H_CSR_RST is set = 0x%08X", reg); ++ dev_warn(&idev->dev, "H_CSR_RST is set = 0x%08X", reg); + reg &= ~H_CSR_RST; + heci_hcsr_set(idev, reg); + reg = heci_hcsr_read(idev); @@ -377,10 +377,10 @@ index 000000000000..7e6ca72e873c + reg = heci_hcsr_read(idev); + + if (!(reg & H_CSR_RST)) -+ dev_warn(idev->dev, "H_CSR_RST is not set = 0x%08X", reg); ++ dev_warn(&idev->dev, "H_CSR_RST is not set = 0x%08X", reg); + + if (reg & H_CSR_RDY) -+ dev_warn(idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); ++ dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); + + if (!enable) + issei_heci_hw_reset_release(idev); @@ -435,7 +435,7 @@ index 000000000000..7e6ca72e873c + struct issei_heci_hw *hw = to_heci_hw(idev); + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { -+ dev_err(idev->dev, "Data size %zu not aligned to slot size %lu\n", ++ dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } @@ -465,7 +465,7 @@ index 000000000000..7e6ca72e873c + u32 *reg_buf = data; + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { -+ dev_err(idev->dev, "Data size %zu not aligned to slot size %lu\n", ++ dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } @@ -499,7 +499,7 @@ index 000000000000..7e6ca72e873c + + ret = heci_write_hbuf(idev, &req, sizeof(req)); + if (ret) -+ dev_err(idev->dev, "Shared memory req write failed ret = %d\n", ret); ++ dev_err(&idev->dev, "Shared memory req write failed ret = %d\n", ret); + + return ret; +} @@ -516,23 +516,23 @@ index 000000000000..7e6ca72e873c + int ret; + + if (heci_count_full_read_slots(idev) != sizeof(res) / CB_SLOT_SIZE) { -+ dev_dbg(idev->dev, "Setup response is not fully received\n"); ++ dev_dbg(&idev->dev, "Setup response is not fully received\n"); + return -ENODATA; + } + + ret = heci_read_hbuf(idev, &res, sizeof(res)); + if (ret) { -+ dev_err(idev->dev, "Shared memory res read failed ret = %d\n", ret); ++ dev_err(&idev->dev, "Shared memory res read failed ret = %d\n", ret); + return ret; + } + + if (res.msg_id != HAM_CB_MESSAGE_ID_RES) { -+ dev_err(idev->dev, "Shared memory res header 0x%x != 0x%x\n", ++ dev_err(&idev->dev, "Shared memory res header 0x%x != 0x%x\n", + res.msg_id, HAM_CB_MESSAGE_ID_RES); + return -EPROTO; + } + if (res.status != 0) { -+ dev_err(idev->dev, "Shared memory res status %d != 0\n", res.status); ++ dev_err(&idev->dev, "Shared memory res status %d != 0\n", res.status); + return -EPROTO; + } + @@ -557,6 +557,11 @@ index 000000000000..7e6ca72e873c + .irq_write_generate = issei_heci_irq_write_generate, +}; + ++const struct issei_hw_ops *issei_heci_get_ops(void) ++{ ++ return &hw_heci_ops; ++} ++ +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id) +{ + struct issei_device *idev = dev_id; @@ -575,7 +580,7 @@ index 000000000000..7e6ca72e873c + heci_hcsr_set_hig(idev); + } + -+ dev_dbg(idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); ++ dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); + + atomic_set(&idev->rst_irq, 1); + wake_up_interruptible(&idev->wait_rst_irq); @@ -595,32 +600,23 @@ index 000000000000..7e6ca72e873c +} + +/** -+ * issei_heci_dev_init - allocates and initializes the issei device structure with hw_heci -+ * @parent: device associated with physical device (pci/platform) ++ * issei_heci_dev_init - initializes the issei device structure with hw_heci ++ * @idev: device structure ++ * @mem_addr: memory address on bar + * @cfg: per device generation config -+ * -+ * Return: The issei_device pointer on success, NULL on failure. + */ -+struct issei_device *issei_heci_dev_init(struct device *parent, const struct hw_heci_cfg *cfg) ++void issei_heci_dev_init(struct issei_device *idev, ++ void __iomem *mem_addr, const struct hw_heci_cfg *cfg) +{ -+ struct issei_device *idev; -+ struct issei_heci_hw *hw; -+ -+ idev = devm_kzalloc(parent, sizeof(*idev) + sizeof(*hw), GFP_KERNEL); -+ if (!idev) -+ return NULL; ++ struct issei_heci_hw *hw = to_heci_hw(idev); + -+ hw = to_heci_hw(idev); + spin_lock_init(&hw->access_lock); -+ -+ issei_device_init(idev, parent, &cfg->dma_length, &hw_heci_ops); ++ hw->mem_addr = mem_addr; + hw->cfg = cfg; -+ -+ return idev; +} diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h new file mode 100644 -index 000000000000..7ba160c3db54 +index 0000000000000..bec2ce8937d1d --- /dev/null +++ b/drivers/misc/issei/hw_heci.h @@ -0,0 +1,54 @@ @@ -635,7 +631,6 @@ index 000000000000..7ba160c3db54 +#include "issei_dev.h" + +struct device; -+struct issei_device; + +/* + * hw_heci_cfg - issei heci device configuration @@ -671,16 +666,17 @@ index 000000000000..7ba160c3db54 +#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw) + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx); ++const struct issei_hw_ops *issei_heci_get_ops(void); + -+struct issei_device *issei_heci_dev_init(struct device *parent, const struct hw_heci_cfg *cfg); ++void issei_heci_dev_init(struct issei_device *idev, ++ void __iomem *mem_addr, const struct hw_heci_cfg *cfg); + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id); -+irqreturn_t issei_heci_irq_thread_handler(int irq, void *dev_id); + +#endif /* _ISSEI_HW_HECI_H_ */ diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci_regs.h new file mode 100644 -index 000000000000..5e804bdb2e94 +index 0000000000000..5e804bdb2e944 --- /dev/null +++ b/drivers/misc/issei/hw_heci_regs.h @@ -0,0 +1,35 @@ @@ -721,12 +717,12 @@ index 000000000000..5e804bdb2e94 +#endif /* _ISSEI_HW_HECI_REGS_H_ */ diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c new file mode 100644 -index 000000000000..b1fafd284521 +index 0000000000000..003cda7b5e8a7 --- /dev/null +++ b/drivers/misc/issei/pci_heci.c -@@ -0,0 +1,166 @@ +@@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 -+/* Copyright (C) 2023-2024 Intel Corporation */ ++/* Copyright (C) 2023-2025 Intel Corporation */ +#include +#include + @@ -736,89 +732,74 @@ index 000000000000..b1fafd284521 + +static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ ++ struct device *dev = &pdev->dev; + const struct hw_heci_cfg *cfg; + struct issei_device *idev; + struct issei_heci_hw *hw; ++ char __iomem *registers; + int err; + + cfg = issei_heci_get_cfg(ent->driver_data); + if (!cfg) -+ return -ENODEV; ++ return dev_err_probe(dev, -ENODEV, "no usable configuration.\n"); + + err = pcim_enable_device(pdev); -+ if (err) { -+ dev_err(&pdev->dev, "failed to enable pci device. err = %d\n", err); -+ return err; -+ } ++ if (err) ++ return dev_err_probe(dev, err, "failed to enable pci device.\n"); + -+ err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); -+ if (err) { -+ dev_err(&pdev->dev, "failed to get pci regions. err = %d\n", err); -+ return err; -+ } ++ pci_set_master(pdev); + -+ err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); -+ if (err) { -+ dev_err(&pdev->dev, "no usable DMA configuration, aborting. err = %d\n", err); -+ return err; -+ } ++ registers = pcim_iomap_region(pdev, 0, KBUILD_MODNAME); ++ if (IS_ERR(registers)) ++ return dev_err_probe(dev, IS_ERR(registers), "failed to get pci region.\n"); + -+ pci_set_master(pdev); ++ err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); ++ if (err) ++ return dev_err_probe(dev, err, "no usable DMA configuration.\n"); + -+ idev = issei_heci_dev_init(&pdev->dev, cfg); -+ if (!idev) -+ return -ENOMEM; -+ hw = to_heci_hw(idev); -+ hw->mem_addr = pcim_iomap_table(pdev)[0]; ++ idev = issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_get_ops()); ++ if (IS_ERR(idev)) ++ return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n"); ++ ++ issei_heci_dev_init(idev, registers, cfg); ++ ++ pci_set_drvdata(pdev, idev); + + err = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (err < 0) { -+ dev_err(&pdev->dev, "pci_alloc_irq_vectors failure. err = %d\n", err); -+ return err; ++ dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n"); ++ goto deregister; + } + ++ hw = to_heci_hw(idev); + hw->irq = pci_irq_vector(pdev, 0); + -+ err = devm_request_threaded_irq(&pdev->dev, hw->irq, ++ err = request_threaded_irq(hw->irq, + issei_heci_irq_quick_handler, + NULL, + IRQF_ONESHOT, KBUILD_MODNAME, idev); -+ if (err) { -+ dev_err(&pdev->dev, "request_threaded_irq failure. err = %d, irq = %d\n", -+ err, hw->irq); ++ if (err) + goto release_irq; -+ } + + err = issei_start(idev); -+ if (err) { -+ dev_err(&pdev->dev, "init hw failure. err = %d.\n", err); -+ goto free_irq; -+ } -+ -+ err = issei_register(idev, &pdev->dev); + if (err) { -+ dev_err(&pdev->dev, "register failure. err = %d.\n", err); -+ goto stop; ++ dev_err_probe(dev, err, "init hw failure.\n"); ++ goto free_irq; + } + -+ pci_set_drvdata(pdev, idev); -+ -+ dev_dbg(&pdev->dev, "initialization successful.\n"); -+ + return 0; + -+stop: -+ issei_stop(idev); +free_irq: -+ /* Manually free IRQ otherwise PCI free irq vectors will fail */ -+ devm_free_irq(&pdev->dev, hw->irq, idev); -+release_irq: + idev->ops->irq_disable(idev); ++ free_irq(hw->irq, idev); ++release_irq: + pci_free_irq_vectors(pdev); ++deregister: ++ issei_deregister(idev); + return err; +} + -+static void __issei_heci_deconstruct(struct pci_dev *pdev) ++static void issei_heci_shutdown(struct pci_dev *pdev) +{ + struct issei_device *idev = pci_get_drvdata(pdev); + struct issei_heci_hw *hw = to_heci_hw(idev); @@ -826,22 +807,13 @@ index 000000000000..b1fafd284521 + issei_stop(idev); + + idev->ops->irq_disable(idev); -+ /* Manually free IRQ otherwise PCI free irq vectors will fail */ -+ devm_free_irq(&pdev->dev, hw->irq, idev); ++ free_irq(hw->irq, idev); + pci_free_irq_vectors(pdev); +} + -+ -+static void issei_heci_shutdown(struct pci_dev *pdev) -+{ -+ dev_dbg(&pdev->dev, "shutdown\n"); -+ __issei_heci_deconstruct(pdev); -+} -+ +static void issei_heci_remove(struct pci_dev *pdev) +{ -+ dev_dbg(&pdev->dev, "stop\n"); -+ __issei_heci_deconstruct(pdev); ++ issei_heci_shutdown(pdev); + + issei_deregister(pci_get_drvdata(pdev)); +} @@ -871,6 +843,8 @@ index 000000000000..b1fafd284521 + {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */ + {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panter Lake H */ + {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panter Lake P */ ++ {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */ ++ {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */ + + {0, } +}; diff --git a/SPECS/kernel/0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu b/SPECS/kernel/0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu deleted file mode 100644 index 13c898b95..000000000 --- a/SPECS/kernel/0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu +++ /dev/null @@ -1,26 +0,0 @@ -From cff0a1ac43aa4e4aab73d712a38311789db15a9e Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Sat, 6 Sep 2025 17:14:52 +0800 -Subject: [PATCH 04/11] media: pci: enable lt6911gxd in ipu-bridge - -Signed-off-by: linya14x ---- - drivers/media/pci/intel/ipu-bridge.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c -index 4e579352ab2c..e58b354ad9e6 100644 ---- a/drivers/media/pci/intel/ipu-bridge.c -+++ b/drivers/media/pci/intel/ipu-bridge.c -@@ -92,6 +92,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { - IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), - /* Toshiba T4KA3 */ - IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), -+ /* Lontium lt6911gxd */ -+ IPU_SENSOR_CONFIG("INTC1124", 0), - }; - - static const struct ipu_property_names prop_names = { --- -2.43.0 - diff --git a/SPECS/kernel/0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu b/SPECS/kernel/0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu deleted file mode 100644 index 98784366e..000000000 --- a/SPECS/kernel/0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu +++ /dev/null @@ -1,124 +0,0 @@ -From 6b7a8bf64caba09b3b26b377641e2246ffbff13b Mon Sep 17 00:00:00 2001 -From: "Yew, Chang Ching" -Date: Thu, 27 Nov 2025 02:49:53 +0800 -Subject: [PATCH 4/6] media: v4l2-async: Fix error handling on steps after - finding a match - -Once an async connection is found to be matching with an fwnode, a -sub-device may be registered (in case it wasn't already), its bound -operation is called, ancillary links are created, the async connection is -added to the sub-device's list of connections and removed from the global -waiting connection list. Further on, the sub-device's possible own -notifier is searched for possible additional matches. - -Fix these specific issues: -- If v4l2_async_match_notify() failed before the sub-notifier handling, the - async connection was unbound and its entry removed from the sub-device's - async connection list. The latter part was also done in - v4l2_async_match_notify(). - -- The async connection's sd field was only set after creating ancillary - links in v4l2_async_match_notify(). It was however dereferenced in - v4l2_async_unbind_subdev_one(), which was called on error path of - v4l2_async_match_notify() failure. - -Signed-off-by: Sakari Ailus ---- - drivers/media/v4l2-core/v4l2-async.c | 45 +++++++++++++++++++--------- - 1 file changed, 31 insertions(+), 14 deletions(-) - -diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c -index ee884a8221fb..1c08bba9ecb9 100644 ---- a/drivers/media/v4l2-core/v4l2-async.c -+++ b/drivers/media/v4l2-core/v4l2-async.c -@@ -343,7 +343,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, - struct v4l2_async_connection *asc) - { -- struct v4l2_async_notifier *subdev_notifier; - bool registered = false; - int ret; - -@@ -389,6 +388,25 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, - dev_dbg(notifier_dev(notifier), "v4l2-async: %s bound (ret %d)\n", - dev_name(sd->dev), ret); - -+ return 0; -+ -+err_call_unbind: -+ v4l2_async_nf_call_unbind(notifier, sd, asc); -+ list_del(&asc->asc_subdev_entry); -+ -+err_unregister_subdev: -+ if (registered) -+ v4l2_device_unregister_subdev(sd); -+ -+ return ret; -+} -+ -+static int -+v4l2_async_nf_try_subdev_notifier(struct v4l2_async_notifier *notifier, -+ struct v4l2_subdev *sd) -+{ -+ struct v4l2_async_notifier *subdev_notifier; -+ - /* - * See if the sub-device has a notifier. If not, return here. - */ -@@ -404,16 +422,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, - subdev_notifier->parent = notifier; - - return v4l2_async_nf_try_all_subdevs(subdev_notifier); -- --err_call_unbind: -- v4l2_async_nf_call_unbind(notifier, sd, asc); -- list_del(&asc->asc_subdev_entry); -- --err_unregister_subdev: -- if (registered) -- v4l2_device_unregister_subdev(sd); -- -- return ret; - } - - /* Test all async sub-devices in a notifier for a match. */ -@@ -445,6 +453,10 @@ v4l2_async_nf_try_all_subdevs(struct v4l2_async_notifier *notifier) - if (ret < 0) - return ret; - -+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd); -+ if (ret < 0) -+ return ret; -+ - /* - * v4l2_async_match_notify() may lead to registering a - * new notifier and thus changing the async subdevs -@@ -829,7 +841,11 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) - ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, - asc); - if (ret) -- goto err_unbind; -+ goto err_unlock; -+ -+ ret = v4l2_async_nf_try_subdev_notifier(notifier, sd); -+ if (ret) -+ goto err_unbind_one; - - ret = v4l2_async_nf_try_complete(notifier); - if (ret) -@@ -853,9 +869,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) - if (subdev_notifier) - v4l2_async_nf_unbind_all_subdevs(subdev_notifier); - -- if (asc) -- v4l2_async_unbind_subdev_one(notifier, asc); -+err_unbind_one: -+ v4l2_async_unbind_subdev_one(notifier, asc); - -+err_unlock: - mutex_unlock(&list_lock); - - sd->owner = NULL; --- -2.43.0 - diff --git a/SPECS/kernel/0004-mei-virtio-virtualization-frontend-driver.security b/SPECS/kernel/0004-mei-virtio-virtualization-frontend-driver.security new file mode 100644 index 000000000..48a2cdab7 --- /dev/null +++ b/SPECS/kernel/0004-mei-virtio-virtualization-frontend-driver.security @@ -0,0 +1,935 @@ +From e5479216aa5ef4d1d806c4ec9d7e7b6fded0dff1 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Tue, 18 Aug 2020 14:51:47 +0300 +Subject: [PATCH 4/8] mei: virtio: virtualization frontend driver + +This frontend driver implements MEI hw interface based on virtio +framework to let MEI driver work without changes under virtualization. +It requires a backend service in the ACRN device-model on the service +OS side to make it work. The backend service will emulate mei routing +and assign vtags for each mei vritio device. + +The backend service is available in ACRN device-model at github. +For more information, please refer to https://projectacrn.org + +The ACRN virtio sub device id for MEI is is 0x8602. + +Co-developed-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +Signed-off-by: Wang Yu +Signed-off-by: Liu Shuo +Link: https://lore.kernel.org/r/20200818115147.2567012-14-tomas.winkler@intel.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/mei/Kconfig | 9 + + drivers/misc/mei/Makefile | 3 + + drivers/misc/mei/hw-virtio.c | 863 +++++++++++++++++++++++++++++++++++ + 3 files changed, 875 insertions(+) + create mode 100644 drivers/misc/mei/hw-virtio.c + +diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig +index f4eb307cd35ed..2b1330b35454e 100644 +--- a/drivers/misc/mei/Kconfig ++++ b/drivers/misc/mei/Kconfig +@@ -94,6 +94,15 @@ config INTEL_MEI_LB + authenticated and versioned, and do not require firmware flashing + or system reboot. + ++config INTEL_MEI_VIRTIO ++ tristate "Intel MEI interface emulation with virtio framework" ++ depends on INTEL_MEI && VIRTIO_PCI ++ help ++ This module implements mei hw emulation over virtio transport. ++ The module will be called mei_virtio. ++ Enable this if your virtual machine supports virtual mei ++ device over virtio. ++ + source "drivers/misc/mei/hdcp/Kconfig" + source "drivers/misc/mei/pxp/Kconfig" + source "drivers/misc/mei/gsc_proxy/Kconfig" +diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile +index a203ed766b338..9de79da3d7027 100644 +--- a/drivers/misc/mei/Makefile ++++ b/drivers/misc/mei/Makefile +@@ -25,6 +25,9 @@ obj-$(CONFIG_INTEL_MEI_TXE) += mei-txe.o + mei-txe-objs := pci-txe.o + mei-txe-objs += hw-txe.o + ++obj-$(CONFIG_INTEL_MEI_VIRTIO) += mei-virtio.o ++mei-virtio-objs := hw-virtio.o ++ + mei-$(CONFIG_EVENT_TRACING) += mei-trace.o + CFLAGS_mei-trace.o = -I$(src) + +diff --git a/drivers/misc/mei/hw-virtio.c b/drivers/misc/mei/hw-virtio.c +new file mode 100644 +index 0000000000000..5e3f405d11deb +--- /dev/null ++++ b/drivers/misc/mei/hw-virtio.c +@@ -0,0 +1,863 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Intel Management Engine Interface (Intel MEI) Linux driver ++ * Copyright (c) 2018-2020, Intel Corporation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mei_dev.h" ++#include "hbm.h" ++#include "client.h" ++ ++#define MEI_VIRTIO_RPM_TIMEOUT 500 ++/* ACRN virtio device types */ ++#ifndef VIRTIO_ID_MEI ++#define VIRTIO_ID_MEI 0xFFFE /* virtio mei */ ++#endif ++ ++/** ++ * struct mei_virtio_cfg - settings passed from the virtio backend ++ * @buf_depth: read buffer depth in slots (4bytes) ++ * @hw_ready: hw is ready for operation ++ * @host_reset: synchronize reset with virtio backend ++ * @reserved: reserved for alignment ++ * @fw_status: FW status ++ */ ++struct mei_virtio_cfg { ++ u32 buf_depth; ++ u8 hw_ready; ++ u8 host_reset; ++ u8 reserved[2]; ++ u32 fw_status[MEI_FW_STATUS_MAX]; ++} __packed; ++ ++struct mei_virtio_hw { ++ struct mei_device mdev; ++ char name[32]; ++ ++ struct virtqueue *in; ++ struct virtqueue *out; ++ ++ bool host_ready; ++ struct work_struct intr_handler; ++ ++ u32 *recv_buf; ++ u8 recv_rdy; ++ size_t recv_sz; ++ u32 recv_idx; ++ u32 recv_len; ++ ++ /* send buffer */ ++ atomic_t hbuf_ready; ++ const void *send_hdr; ++ const void *send_buf; ++ ++ struct mei_virtio_cfg cfg; ++}; ++ ++#define to_virtio_hw(_dev) container_of(_dev, struct mei_virtio_hw, mdev) ++ ++/** ++ * mei_virtio_fw_status() - read status register of mei ++ * @dev: mei device ++ * @fw_status: fw status register values ++ * ++ * Return: always 0 ++ */ ++static int mei_virtio_fw_status(struct mei_device *dev, ++ struct mei_fw_status *fw_status) ++{ ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ fw_status->count = MEI_FW_STATUS_MAX; ++ virtio_cread_bytes(vdev, offsetof(struct mei_virtio_cfg, fw_status), ++ fw_status->status, sizeof(fw_status->status)); ++ return 0; ++} ++ ++/** ++ * mei_virtio_pg_state() - translate internal pg state ++ * to the mei power gating state ++ * There is no power management in ACRN mode always return OFF ++ * @dev: mei device ++ * ++ * Return: ++ * * MEI_PG_OFF - if aliveness is on (always) ++ * * MEI_PG_ON - (never) ++ */ ++static inline enum mei_pg_state mei_virtio_pg_state(struct mei_device *dev) ++{ ++ return MEI_PG_OFF; ++} ++ ++/** ++ * mei_virtio_hw_config() - configure hw dependent settings ++ * ++ * @dev: mei device ++ * ++ * Return: always 0 ++ */ ++static int mei_virtio_hw_config(struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_virtio_hbuf_empty_slots() - counts write empty slots. ++ * @dev: the device structure ++ * ++ * Return: always return frontend buf size if buffer is ready, 0 otherwise ++ */ ++static int mei_virtio_hbuf_empty_slots(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ return (atomic_read(&hw->hbuf_ready) == 1) ? hw->cfg.buf_depth : 0; ++} ++ ++/** ++ * mei_virtio_hbuf_is_ready() - checks if write buffer is ready ++ * @dev: the device structure ++ * ++ * Return: true if hbuf is ready ++ */ ++static bool mei_virtio_hbuf_is_ready(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ return atomic_read(&hw->hbuf_ready) == 1; ++} ++ ++/** ++ * mei_virtio_hbuf_depth() - returns depth of FE write buffer. ++ * @dev: the device structure ++ * ++ * Return: size of frontend write buffer in bytes ++ */ ++static u32 mei_virtio_hbuf_depth(const struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ return hw->cfg.buf_depth; ++} ++ ++/** ++ * mei_virtio_intr_clear() - clear and stop interrupts ++ * @dev: the device structure ++ */ ++static void mei_virtio_intr_clear(struct mei_device *dev) ++{ ++ /* ++ * In our virtio solution, there are two types of interrupts, ++ * vq interrupt and config change interrupt. ++ * 1) start/reset rely on virtio config changed interrupt; ++ * 2) send/recv rely on virtio virtqueue interrupts. ++ * They are all virtual interrupts. So, we don't have corresponding ++ * operation to do here. ++ */ ++} ++ ++/** ++ * mei_virtio_intr_enable() - enables mei BE virtqueues callbacks ++ * @dev: the device structure ++ */ ++static void mei_virtio_intr_enable(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ virtqueue_enable_cb(hw->in); ++ virtqueue_enable_cb(hw->out); ++} ++ ++/** ++ * mei_virtio_intr_disable() - disables mei BE virtqueues callbacks ++ * ++ * @dev: the device structure ++ */ ++static void mei_virtio_intr_disable(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ virtqueue_disable_cb(hw->in); ++ virtqueue_disable_cb(hw->out); ++} ++ ++/** ++ * mei_virtio_synchronize_irq() - wait for pending IRQ handlers for all ++ * virtqueue ++ * @dev: the device structure ++ */ ++static void mei_virtio_synchronize_irq(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ /* ++ * Now, all IRQ handlers are converted to workqueue. ++ * Change synchronize irq to flush this work. ++ */ ++ flush_work(&hw->intr_handler); ++} ++ ++static void mei_virtio_free_outbufs(struct mei_virtio_hw *hw) ++{ ++ kfree(hw->send_hdr); ++ kfree(hw->send_buf); ++ hw->send_hdr = NULL; ++ hw->send_buf = NULL; ++} ++ ++/** ++ * mei_virtio_write_message() - writes a message to mei virtio back-end service. ++ * @dev: the device structure ++ * @hdr: mei header of message ++ * @hdr_len: header length ++ * @data: message payload will be written ++ * @data_len: message payload length ++ * ++ * Return: ++ * * 0: on success ++ * * -EIO: if write has failed ++ * * -ENOMEM: on memory allocation failure ++ */ ++static int mei_virtio_write_message(struct mei_device *dev, ++ const void *hdr, size_t hdr_len, ++ const void *data, size_t data_len) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct scatterlist sg[2]; ++ const void *hbuf, *dbuf; ++ int ret; ++ ++ if (WARN_ON(!atomic_add_unless(&hw->hbuf_ready, -1, 0))) ++ return -EIO; ++ ++ hbuf = kmemdup(hdr, hdr_len, GFP_KERNEL); ++ hw->send_hdr = hbuf; ++ ++ dbuf = kmemdup(data, data_len, GFP_KERNEL); ++ hw->send_buf = dbuf; ++ ++ if (!hbuf || !dbuf) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ sg_init_table(sg, 2); ++ sg_set_buf(&sg[0], hbuf, hdr_len); ++ sg_set_buf(&sg[1], dbuf, data_len); ++ ++ ret = virtqueue_add_outbuf(hw->out, sg, 2, hw, GFP_KERNEL); ++ if (ret) { ++ dev_err(&dev->dev, "failed to add outbuf\n"); ++ goto fail; ++ } ++ ++ virtqueue_kick(hw->out); ++ return 0; ++fail: ++ ++ mei_virtio_free_outbufs(hw); ++ ++ return ret; ++} ++ ++/** ++ * mei_virtio_count_full_read_slots() - counts read full slots. ++ * @dev: the device structure ++ * ++ * Return: -EOVERFLOW if overflow, otherwise filled slots count ++ */ ++static int mei_virtio_count_full_read_slots(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ if (hw->recv_idx > hw->recv_len) ++ return -EOVERFLOW; ++ ++ return hw->recv_len - hw->recv_idx; ++} ++ ++/** ++ * mei_virtio_read_hdr() - Reads 32bit dword from mei virtio receive buffer ++ * ++ * @dev: the device structure ++ * ++ * Return: 32bit dword of receive buffer (u32) ++ */ ++static inline u32 mei_virtio_read_hdr(const struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ WARN_ON(hw->cfg.buf_depth < hw->recv_idx + 1); ++ ++ return hw->recv_buf[hw->recv_idx++]; ++} ++ ++static int mei_virtio_read(struct mei_device *dev, unsigned char *buffer, ++ unsigned long len) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ u32 slots = mei_data2slots(len); ++ ++ if (WARN_ON(hw->cfg.buf_depth < hw->recv_idx + slots)) ++ return -EOVERFLOW; ++ ++ /* ++ * Assumption: There is only one MEI message in recv_buf each time. ++ * Backend service need follow this rule too. ++ */ ++ memcpy(buffer, hw->recv_buf + hw->recv_idx, len); ++ hw->recv_idx += slots; ++ ++ return 0; ++} ++ ++static bool mei_virtio_pg_is_enabled(struct mei_device *dev) ++{ ++ return false; ++} ++ ++static bool mei_virtio_pg_in_transition(struct mei_device *dev) ++{ ++ return false; ++} ++ ++static void mei_virtio_add_recv_buf(struct mei_virtio_hw *hw) ++{ ++ struct scatterlist sg; ++ ++ if (hw->recv_rdy) /* not needed */ ++ return; ++ ++ /* refill the recv_buf to IN virtqueue to get next message */ ++ sg_init_one(&sg, hw->recv_buf, mei_slots2data(hw->cfg.buf_depth)); ++ hw->recv_len = 0; ++ hw->recv_idx = 0; ++ hw->recv_rdy = 1; ++ virtqueue_add_inbuf(hw->in, &sg, 1, hw->recv_buf, GFP_KERNEL); ++ virtqueue_kick(hw->in); ++} ++ ++/** ++ * mei_virtio_hw_is_ready() - check whether the BE(hw) has turned ready ++ * @dev: mei device ++ * Return: bool ++ */ ++static bool mei_virtio_hw_is_ready(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ virtio_cread(vdev, struct mei_virtio_cfg, ++ hw_ready, &hw->cfg.hw_ready); ++ ++ dev_dbg(&dev->dev, "hw ready %d\n", hw->cfg.hw_ready); ++ ++ return hw->cfg.hw_ready; ++} ++ ++/** ++ * mei_virtio_hw_reset - resets virtio hw. ++ * ++ * @dev: the device structure ++ * @intr_enable: virtio use data/config callbacks ++ * ++ * Return: 0 on success an error code otherwise ++ */ ++static int mei_virtio_hw_reset(struct mei_device *dev, bool intr_enable) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ dev_dbg(&dev->dev, "hw reset\n"); ++ ++ dev->recvd_hw_ready = false; ++ hw->host_ready = false; ++ atomic_set(&hw->hbuf_ready, 0); ++ hw->recv_len = 0; ++ hw->recv_idx = 0; ++ ++ hw->cfg.host_reset = 1; ++ virtio_cwrite(vdev, struct mei_virtio_cfg, ++ host_reset, &hw->cfg.host_reset); ++ ++ mei_virtio_hw_is_ready(dev); ++ ++ if (intr_enable) ++ mei_virtio_intr_enable(dev); ++ ++ return 0; ++} ++ ++/** ++ * mei_virtio_hw_reset_release() - release device from the reset ++ * @dev: the device structure ++ */ ++static void mei_virtio_hw_reset_release(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ struct virtio_device *vdev = dev_to_virtio(dev->parent); ++ ++ dev_dbg(&dev->dev, "hw reset release\n"); ++ hw->cfg.host_reset = 0; ++ virtio_cwrite(vdev, struct mei_virtio_cfg, ++ host_reset, &hw->cfg.host_reset); ++} ++ ++/** ++ * mei_virtio_hw_ready_wait() - wait until the virtio(hw) has turned ready ++ * or timeout is reached ++ * @dev: mei device ++ * ++ * Return: 0 on success, error otherwise ++ */ ++static int mei_virtio_hw_ready_wait(struct mei_device *dev) ++{ ++ mutex_unlock(&dev->device_lock); ++ wait_event_timeout(dev->wait_hw_ready, ++ dev->recvd_hw_ready, ++ mei_secs_to_jiffies(MEI_HW_READY_TIMEOUT)); ++ mutex_lock(&dev->device_lock); ++ if (!dev->recvd_hw_ready) { ++ dev_err(&dev->dev, "wait hw ready failed\n"); ++ return -ETIMEDOUT; ++ } ++ ++ dev->recvd_hw_ready = false; ++ return 0; ++} ++ ++/** ++ * mei_virtio_hw_start() - hw start routine ++ * @dev: mei device ++ * ++ * Return: 0 on success, error otherwise ++ */ ++static int mei_virtio_hw_start(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ int ret; ++ ++ dev_dbg(&dev->dev, "hw start\n"); ++ mei_virtio_hw_reset_release(dev); ++ ++ ret = mei_virtio_hw_ready_wait(dev); ++ if (ret) ++ return ret; ++ ++ mei_virtio_add_recv_buf(hw); ++ atomic_set(&hw->hbuf_ready, 1); ++ dev_dbg(&dev->dev, "hw is ready\n"); ++ hw->host_ready = true; ++ ++ return 0; ++} ++ ++/** ++ * mei_virtio_host_is_ready() - check whether the FE has turned ready ++ * @dev: mei device ++ * ++ * Return: bool ++ */ ++static bool mei_virtio_host_is_ready(struct mei_device *dev) ++{ ++ struct mei_virtio_hw *hw = to_virtio_hw(dev); ++ ++ dev_dbg(&dev->dev, "host ready %d\n", hw->host_ready); ++ ++ return hw->host_ready; ++} ++ ++/** ++ * mei_virtio_data_in() - The callback of recv virtqueue of virtio mei ++ * @vq: receiving virtqueue ++ */ ++static void mei_virtio_data_in(struct virtqueue *vq) ++{ ++ struct mei_virtio_hw *hw = vq->vdev->priv; ++ ++ /* disable interrupts (enabled again from in the interrupt worker) */ ++ virtqueue_disable_cb(hw->in); ++ ++ schedule_work(&hw->intr_handler); ++} ++ ++/** ++ * mei_virtio_data_out() - The callback of send virtqueue of virtio mei ++ * @vq: transmitting virtqueue ++ */ ++static void mei_virtio_data_out(struct virtqueue *vq) ++{ ++ struct mei_virtio_hw *hw = vq->vdev->priv; ++ ++ schedule_work(&hw->intr_handler); ++} ++ ++static void mei_virtio_intr_handler(struct work_struct *work) ++{ ++ struct mei_virtio_hw *hw = ++ container_of(work, struct mei_virtio_hw, intr_handler); ++ struct mei_device *dev = &hw->mdev; ++ LIST_HEAD(complete_list); ++ s32 slots; ++ int rets = 0; ++ void *data; ++ unsigned int len; ++ ++ mutex_lock(&dev->device_lock); ++ ++ if (dev->dev_state == MEI_DEV_DISABLED) { ++ dev_warn(&dev->dev, "Interrupt in disabled state.\n"); ++ mei_virtio_intr_disable(dev); ++ goto end; ++ } ++ ++ /* check if ME wants a reset */ ++ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { ++ dev_warn(&dev->dev, "BE service not ready: resetting.\n"); ++ schedule_work(&dev->reset_work); ++ goto end; ++ } ++ ++ /* check if we need to start the dev */ ++ if (!mei_host_is_ready(dev)) { ++ if (mei_hw_is_ready(dev)) { ++ dev_dbg(&dev->dev, "we need to start the dev.\n"); ++ dev->recvd_hw_ready = true; ++ wake_up(&dev->wait_hw_ready); ++ } else { ++ dev_warn(&dev->dev, "Spurious Interrupt\n"); ++ } ++ goto end; ++ } ++ ++ /* read */ ++ if (hw->recv_rdy) { ++ data = virtqueue_get_buf(hw->in, &len); ++ if (!data || !len) { ++ dev_dbg(&dev->dev, "No data %d", len); ++ } else { ++ dev_dbg(&dev->dev, "data_in %d\n", len); ++ WARN_ON(data != hw->recv_buf); ++ hw->recv_len = mei_data2slots(len); ++ hw->recv_rdy = 0; ++ } ++ } ++ ++ /* write */ ++ if (!atomic_read(&hw->hbuf_ready)) { ++ if (!virtqueue_get_buf(hw->out, &len)) { ++ dev_warn(&dev->dev, "Failed to getbuf\n"); ++ } else { ++ mei_virtio_free_outbufs(hw); ++ atomic_inc(&hw->hbuf_ready); ++ } ++ } ++ ++ /* check slots available for reading */ ++ slots = mei_count_full_read_slots(dev); ++ while (slots > 0) { ++ dev_dbg(&dev->dev, "slots to read = %08x\n", slots); ++ rets = mei_irq_read_handler(dev, &complete_list, &slots); ++ ++ if (rets && ++ (dev->dev_state != MEI_DEV_RESETTING && ++ dev->dev_state != MEI_DEV_POWER_DOWN)) { ++ dev_err(&dev->dev, "mei_irq_read_handler ret = %d.\n", ++ rets); ++ schedule_work(&dev->reset_work); ++ goto end; ++ } ++ } ++ ++ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); ++ ++ mei_irq_write_handler(dev, &complete_list); ++ ++ dev->hbuf_is_ready = mei_hbuf_is_ready(dev); ++ ++ mei_irq_compl_handler(dev, &complete_list); ++ ++ mei_virtio_add_recv_buf(hw); ++ ++end: ++ if (dev->dev_state != MEI_DEV_DISABLED) { ++ if (!virtqueue_enable_cb(hw->in)) ++ schedule_work(&hw->intr_handler); ++ } ++ ++ mutex_unlock(&dev->device_lock); ++} ++ ++static void mei_virtio_config_changed(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ struct mei_device *dev = &hw->mdev; ++ ++ virtio_cread(vdev, struct mei_virtio_cfg, ++ hw_ready, &hw->cfg.hw_ready); ++ ++ if (dev->dev_state == MEI_DEV_DISABLED) { ++ dev_dbg(&dev->dev, "disabled state don't start\n"); ++ return; ++ } ++ ++ /* Run intr handler once to handle reset notify */ ++ schedule_work(&hw->intr_handler); ++} ++ ++static void mei_virtio_remove_vqs(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ virtqueue_detach_unused_buf(hw->in); ++ hw->recv_len = 0; ++ hw->recv_idx = 0; ++ hw->recv_rdy = 0; ++ ++ virtqueue_detach_unused_buf(hw->out); ++ ++ mei_virtio_free_outbufs(hw); ++ ++ vdev->config->del_vqs(vdev); ++} ++ ++/* ++ * There are two virtqueues, one is for send and another is for recv. ++ */ ++static int mei_virtio_init_vqs(struct mei_virtio_hw *hw, ++ struct virtio_device *vdev) ++{ ++ struct virtqueue *vqs[2]; ++ struct virtqueue_info vqs_info[] = { ++ { "in", mei_virtio_data_in }, ++ { "out", mei_virtio_data_out }, ++ }; ++ int ret; ++ ++ ret = virtio_find_vqs(vdev, 2, vqs, vqs_info, NULL); ++ if (ret) ++ return ret; ++ ++ hw->in = vqs[0]; ++ hw->out = vqs[1]; ++ ++ return 0; ++} ++ ++static const struct mei_hw_ops mei_virtio_ops = { ++ .fw_status = mei_virtio_fw_status, ++ .pg_state = mei_virtio_pg_state, ++ ++ .host_is_ready = mei_virtio_host_is_ready, ++ ++ .hw_is_ready = mei_virtio_hw_is_ready, ++ .hw_reset = mei_virtio_hw_reset, ++ .hw_config = mei_virtio_hw_config, ++ .hw_start = mei_virtio_hw_start, ++ ++ .pg_in_transition = mei_virtio_pg_in_transition, ++ .pg_is_enabled = mei_virtio_pg_is_enabled, ++ ++ .intr_clear = mei_virtio_intr_clear, ++ .intr_enable = mei_virtio_intr_enable, ++ .intr_disable = mei_virtio_intr_disable, ++ .synchronize_irq = mei_virtio_synchronize_irq, ++ ++ .hbuf_free_slots = mei_virtio_hbuf_empty_slots, ++ .hbuf_is_ready = mei_virtio_hbuf_is_ready, ++ .hbuf_depth = mei_virtio_hbuf_depth, ++ ++ .write = mei_virtio_write_message, ++ ++ .rdbuf_full_slots = mei_virtio_count_full_read_slots, ++ .read_hdr = mei_virtio_read_hdr, ++ .read = mei_virtio_read, ++}; ++ ++static int mei_virtio_probe(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw; ++ int ret; ++ ++ hw = devm_kzalloc(&vdev->dev, sizeof(*hw), GFP_KERNEL); ++ if (!hw) ++ return -ENOMEM; ++ ++ vdev->priv = hw; ++ ++ INIT_WORK(&hw->intr_handler, mei_virtio_intr_handler); ++ ++ ret = mei_virtio_init_vqs(hw, vdev); ++ if (ret) ++ goto vqs_failed; ++ ++ virtio_cread(vdev, struct mei_virtio_cfg, ++ buf_depth, &hw->cfg.buf_depth); ++ ++ hw->recv_buf = kzalloc(mei_slots2data(hw->cfg.buf_depth), GFP_KERNEL); ++ if (!hw->recv_buf) { ++ ret = -ENOMEM; ++ goto hbuf_failed; ++ } ++ atomic_set(&hw->hbuf_ready, 0); ++ ++ virtio_device_ready(vdev); ++ ++ mei_device_init(&hw->mdev, &vdev->dev, false, &mei_virtio_ops); ++ ++ pm_runtime_get_noresume(&vdev->dev); ++ pm_runtime_set_active(&vdev->dev); ++ pm_runtime_enable(&vdev->dev); ++ ++ ret = mei_start(&hw->mdev); ++ if (ret) ++ goto mei_start_failed; ++ ++ pm_runtime_set_autosuspend_delay(&vdev->dev, MEI_VIRTIO_RPM_TIMEOUT); ++ pm_runtime_use_autosuspend(&vdev->dev); ++ ++ ret = mei_register(&hw->mdev, &vdev->dev); ++ if (ret) ++ goto mei_failed; ++ ++ pm_runtime_put(&vdev->dev); ++ ++ return 0; ++ ++mei_failed: ++ mei_stop(&hw->mdev); ++mei_start_failed: ++ mei_cancel_work(&hw->mdev); ++ mei_disable_interrupts(&hw->mdev); ++ kfree(hw->recv_buf); ++hbuf_failed: ++ vdev->config->del_vqs(vdev); ++vqs_failed: ++ return ret; ++} ++ ++static int __maybe_unused mei_virtio_pm_runtime_idle(struct device *device) ++{ ++ struct virtio_device *vdev = dev_to_virtio(device); ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ dev_dbg(&vdev->dev, "rpm: mei_virtio : runtime_idle\n"); ++ ++ if (!hw) ++ return -ENODEV; ++ ++ if (mei_write_is_idle(&hw->mdev)) ++ pm_runtime_autosuspend(device); ++ ++ return -EBUSY; ++} ++ ++static int __maybe_unused mei_virtio_pm_runtime_suspend(struct device *device) ++{ ++ return 0; ++} ++ ++static int __maybe_unused mei_virtio_pm_runtime_resume(struct device *device) ++{ ++ return 0; ++} ++ ++static int __maybe_unused mei_virtio_freeze(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ dev_dbg(&vdev->dev, "freeze\n"); ++ ++ if (!hw) ++ return -ENODEV; ++ ++ mei_stop(&hw->mdev); ++ mei_disable_interrupts(&hw->mdev); ++ cancel_work_sync(&hw->intr_handler); ++ vdev->config->reset(vdev); ++ mei_virtio_remove_vqs(vdev); ++ ++ return 0; ++} ++ ++static int __maybe_unused mei_virtio_restore(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ int ret; ++ ++ dev_dbg(&vdev->dev, "restore\n"); ++ ++ if (!hw) ++ return -ENODEV; ++ ++ ret = mei_virtio_init_vqs(hw, vdev); ++ if (ret) ++ return ret; ++ ++ virtio_device_ready(vdev); ++ ++ ret = mei_restart(&hw->mdev); ++ if (ret) ++ return ret; ++ ++ /* Start timer if stopped in suspend */ ++ schedule_delayed_work(&hw->mdev.timer_work, HZ); ++ ++ return 0; ++} ++ ++static const struct dev_pm_ops mei_virtio_pm_ops = { ++ SET_RUNTIME_PM_OPS(mei_virtio_pm_runtime_suspend, ++ mei_virtio_pm_runtime_resume, ++ mei_virtio_pm_runtime_idle) ++}; ++ ++static void mei_virtio_remove(struct virtio_device *vdev) ++{ ++ struct mei_virtio_hw *hw = vdev->priv; ++ ++ mei_stop(&hw->mdev); ++ mei_disable_interrupts(&hw->mdev); ++ cancel_work_sync(&hw->intr_handler); ++ mei_deregister(&hw->mdev); ++ vdev->config->reset(vdev); ++ mei_virtio_remove_vqs(vdev); ++ kfree(hw->recv_buf); ++ pm_runtime_disable(&vdev->dev); ++} ++ ++static struct virtio_device_id id_table[] = { ++ { VIRTIO_ID_MEI, VIRTIO_DEV_ANY_ID }, ++ { } ++}; ++ ++static struct virtio_driver mei_virtio_driver = { ++ .id_table = id_table, ++ .probe = mei_virtio_probe, ++ .remove = mei_virtio_remove, ++ .config_changed = mei_virtio_config_changed, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .pm = &mei_virtio_pm_ops, ++ }, ++#ifdef CONFIG_PM_SLEEP ++ .freeze = mei_virtio_freeze, ++ .restore = mei_virtio_restore, ++#endif ++}; ++ ++module_virtio_driver(mei_virtio_driver); ++MODULE_DEVICE_TABLE(virtio, id_table); ++MODULE_DESCRIPTION("Virtio MEI frontend driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.43.0 + diff --git a/SPECS/kernel/0004-net-core-XDP-metadata-BTF-netlink-API.ethernet b/SPECS/kernel/0004-net-core-XDP-metadata-BTF-netlink-API.ethernet deleted file mode 100644 index ca4a7f691..000000000 --- a/SPECS/kernel/0004-net-core-XDP-metadata-BTF-netlink-API.ethernet +++ /dev/null @@ -1,213 +0,0 @@ -From 66599d365b29a2a9306b4e686289e1c8ccb38e12 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Mon, 14 Jun 2021 11:16:14 +0800 -Subject: [PATCH 04/19] net/core: XDP metadata BTF netlink API - -Add new devlink XDP attributes to be used to query or setup XDP metadata -BTF state. - -IFLA_XDP_MD_BTF_ID: type NLA_U32. -IFLA_XDP_MD_BTF_STATE: type = NLA_U8. - -On XDP query driver reports current loaded BTF ID, and its state if -active or not. - -On XDP setup, driver will use these attributes to activate/deactivate -a specific BTF ID. - -Signed-off-by: Saeed Mahameed -Signed-off-by: Jithu Joseph -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - include/linux/netdevice.h | 15 +++++++++- - include/uapi/linux/if_link.h | 2 ++ - net/core/dev.c | 53 ++++++++++++++++++++++++++++++++++++ - net/core/rtnetlink.c | 18 +++++++++++- - 4 files changed, 86 insertions(+), 2 deletions(-) - -diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h -index f3a3b761abfb..f16a70c4298c 100644 ---- a/include/linux/netdevice.h -+++ b/include/linux/netdevice.h -@@ -965,6 +965,10 @@ enum bpf_netdev_command { - */ - XDP_SETUP_PROG, - XDP_SETUP_PROG_HW, -+ /* Setup/query XDP Meta Data BTF */ -+ XDP_SETUP_MD_BTF, -+ XDP_QUERY_MD_BTF, -+ - /* BPF program for offload callbacks, invoked at program load time. */ - BPF_OFFLOAD_MAP_ALLOC, - BPF_OFFLOAD_MAP_FREE, -@@ -988,6 +992,7 @@ struct bpf_xdp_entity { - struct bpf_prog *prog; - struct bpf_xdp_link *link; - }; -+struct btf; - - struct netdev_bpf { - enum bpf_netdev_command command; -@@ -996,7 +1001,11 @@ struct netdev_bpf { - struct { - u32 flags; - struct bpf_prog *prog; -- struct netlink_ext_ack *extack; -+ }; -+ /* XDP_{SETUP/QUERY}_MD_BTF */ -+ struct { -+ u8 btf_enable; -+ u32 btf_id; - }; - /* BPF_OFFLOAD_MAP_ALLOC, BPF_OFFLOAD_MAP_FREE */ - struct { -@@ -1007,6 +1016,7 @@ struct netdev_bpf { - struct xsk_buff_pool *pool; - u16 queue_id; - } xsk; -+ struct netlink_ext_ack *extack; - }; - }; - -@@ -4244,6 +4254,9 @@ u32 dev_xdp_prog_id(struct net_device *dev, enum bpf_xdp_mode mode); - - u32 dev_get_min_mp_channel_count(const struct net_device *dev); - -+int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, -+ u8 enable); -+u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled); - int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); - int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); - int dev_forward_skb_nomtu(struct net_device *dev, struct sk_buff *skb); -diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h -index 784ace3a519c..d7f118af1147 100644 ---- a/include/uapi/linux/if_link.h -+++ b/include/uapi/linux/if_link.h -@@ -1909,6 +1909,8 @@ enum { - IFLA_XDP_SKB_PROG_ID, - IFLA_XDP_HW_PROG_ID, - IFLA_XDP_EXPECTED_FD, -+ IFLA_XDP_MD_BTF_ID, -+ IFLA_XDP_MD_BTF_STATE, - __IFLA_XDP_MAX, - }; - -diff --git a/net/core/dev.c b/net/core/dev.c -index 8d49b2198d07..e5768993cc72 100644 ---- a/net/core/dev.c -+++ b/net/core/dev.c -@@ -10558,6 +10558,59 @@ u32 dev_get_min_mp_channel_count(const struct net_device *dev) - return 0; - } - -+/** -+ * dev_xdp_query_md_btf - Query meta data btf of a device -+ * @dev: device -+ * @enabled: 1 if enabled, 0 otherwise -+ * -+ * Returns btf id > 0 if valid -+ */ -+u32 dev_xdp_query_md_btf(struct net_device *dev, u8 *enabled) -+{ -+ struct netdev_bpf xdp; -+ bpf_op_t ndo_bpf; -+ -+ ndo_bpf = dev->netdev_ops->ndo_bpf; -+ if (!ndo_bpf) -+ return 0; -+ -+ memset(&xdp, 0, sizeof(xdp)); -+ xdp.command = XDP_QUERY_MD_BTF; -+ -+ if (ndo_bpf(dev, &xdp)) -+ return 0; /* 0 is an invalid btf id */ -+ -+ *enabled = xdp.btf_enable; -+ return xdp.btf_id; -+} -+ -+/** -+ * dev_xdp_setup_md_btf - enable or disable meta data btf for a device -+ * @dev: device -+ * @extack: netlink extended ack -+ * @enable: 1 to enable, 0 to disable -+ * -+ * Returns 0 on success -+ */ -+int dev_xdp_setup_md_btf(struct net_device *dev, struct netlink_ext_ack *extack, -+ u8 enable) -+{ -+ struct netdev_bpf xdp; -+ bpf_op_t ndo_bpf; -+ -+ ndo_bpf = dev->netdev_ops->ndo_bpf; -+ if (!ndo_bpf) -+ return -EOPNOTSUPP; -+ -+ memset(&xdp, 0, sizeof(xdp)); -+ -+ xdp.command = XDP_SETUP_MD_BTF; -+ xdp.btf_enable = enable; -+ xdp.extack = extack; -+ -+ return ndo_bpf(dev, &xdp); -+} -+ - /** - * dev_index_reserve() - allocate an ifindex in a namespace - * @net: the applicable net namespace -diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -index 094b085cff20..aa18bd5b8c63 100644 ---- a/net/core/rtnetlink.c -+++ b/net/core/rtnetlink.c -@@ -1732,8 +1732,9 @@ static int rtnl_xdp_report_one(struct sk_buff *skb, struct net_device *dev, - - static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - { -+ u32 prog_id, md_btf_id; -+ u8 md_btf_enabled = 0; - struct nlattr *xdp; -- u32 prog_id; - int err; - u8 mode; - -@@ -1766,6 +1767,10 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - goto err_cancel; - } - -+ md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); -+ nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); -+ nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); -+ - nla_nest_end(skb, xdp); - return 0; - -@@ -2294,6 +2299,8 @@ static const struct nla_policy ifla_xdp_policy[IFLA_XDP_MAX + 1] = { - [IFLA_XDP_ATTACHED] = { .type = NLA_U8 }, - [IFLA_XDP_FLAGS] = { .type = NLA_U32 }, - [IFLA_XDP_PROG_ID] = { .type = NLA_U32 }, -+ [IFLA_XDP_MD_BTF_ID] = { .type = NLA_U32 }, -+ [IFLA_XDP_MD_BTF_STATE] = { .type = NLA_U8 }, - }; - - static struct rtnl_link_ops *linkinfo_to_kind_ops(const struct nlattr *nla, -@@ -3382,6 +3389,15 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev, - goto errout; - status |= DO_SETLINK_NOTIFY; - } -+ -+ if (xdp[IFLA_XDP_MD_BTF_STATE]) { -+ u8 enable = nla_get_u8(xdp[IFLA_XDP_MD_BTF_STATE]); -+ -+ err = dev_xdp_setup_md_btf(dev, extack, enable); -+ if (err) -+ goto errout; -+ status |= DO_SETLINK_NOTIFY; -+ } - } - - errout: --- -2.43.0 - diff --git a/SPECS/kernel/0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet b/SPECS/kernel/0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet new file mode 100644 index 000000000..922619eee --- /dev/null +++ b/SPECS/kernel/0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet @@ -0,0 +1,63 @@ +From cd577235483d3d7348ebcda3f5c72f8b69083567 Mon Sep 17 00:00:00 2001 +From: Ling Pei Lee +Date: Fri, 10 Sep 2021 23:49:43 +0800 +Subject: [PATCH 04/18] net: stmmac: Resolve poor line rate after switching + from TSO off to TSO on + +Clear mss in TDES and call stmmac_enable_tso() to indicate +a new TSO transmission when it is enabled from TSO off using +ethtool command. + +TSO Driver is disable when 'priv->tso = 0' in this same function. +The reason this is put as part of fix_features rather than set_features is +because the commit f748be531d70("stmmac: support new GMAC4") has +already introduced the following codes in fix_features:- + ++ /* Disable tso if asked by ethtool */ ++ if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) { ++ if (features & NETIF_F_TSO) ++ priv->tso = true; ++ else ++ priv->tso = false; ++ } + +Fixes: f748be531d70 ("stmmac: support new GMAC4") +Signed-off-by: Ling Pei Lee +Signed-off-by: Looi Hong Aun +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 8b9dbf6449f1c..2cd7548edd82f 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -5915,6 +5915,8 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, + netdev_features_t features) + { + struct stmmac_priv *priv = netdev_priv(dev); ++ u32 tx_cnt = priv->plat->tx_queues_to_use; ++ u32 chan; + + if (priv->plat->rx_coe == STMMAC_RX_COE_NONE) + features &= ~NETIF_F_RXCSUM; +@@ -5938,6 +5940,16 @@ static netdev_features_t stmmac_fix_features(struct net_device *dev, + priv->tso = false; + } + ++ for (chan = 0; chan < tx_cnt; chan++) { ++ struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan]; ++ ++ /* TSO and TBS cannot co-exist */ ++ if (tx_q->tbs & STMMAC_TBS_AVAIL) ++ continue; ++ ++ tx_q->mss = 0; ++ stmmac_enable_tso(priv, priv->ioaddr, priv->tso, chan); ++ } + return features; + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu b/SPECS/kernel/0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu deleted file mode 100644 index d5ab0f361..000000000 --- a/SPECS/kernel/0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu +++ /dev/null @@ -1,272 +0,0 @@ -From fa11e4f711b6ccb10163eeee10d01be41041e9c5 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Mon, 20 Oct 2025 12:36:12 +0800 -Subject: [PATCH 04/21] patch: staging add enable CONFIG_DEBUG_FS - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 84 ++++++++++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-isys.h | 7 +++ - drivers/staging/media/ipu7/ipu7.c | 62 ++++++++++++++++++- - drivers/staging/media/ipu7/ipu7.h | 3 + - 4 files changed, 155 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index cfb3989fe719..44ea4eef1976 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -9,6 +9,9 @@ - #include - #include - #include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif - #include - #include - #include -@@ -582,6 +585,10 @@ static void isys_remove(struct auxiliary_device *auxdev) - struct isys_fw_msgs *fwmsg, *safe; - struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); - -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif - for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) - mutex_destroy(&isys->streams[i].mutex); - -@@ -605,6 +612,78 @@ static void isys_remove(struct auxiliary_device *auxdev) - #endif - } - -+#ifdef CONFIG_DEBUG_FS -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_isys *isys = file->private_data; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct device *dev = &isys->adev->auxdev.dev; -+ u32 log_size; -+ int ret = 0; -+ void *buf; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_info(dev, "copy %d bytes fw log to user...\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations isys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_isys_init_debugfs(struct ipu7_isys *isys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("isys", isys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, isys, &isys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ isys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ - static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount) - { - struct ipu7_bus_device *adev = isys->adev; -@@ -763,6 +842,11 @@ static int isys_probe(struct auxiliary_device *auxdev, - - isys_stream_init(isys); - -+#ifdef CONFIG_DEBUG_FS -+ /* Debug fs failure is not fatal. */ -+ ipu7_isys_init_debugfs(isys); -+#endif -+ - cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE); - ret = alloc_fw_msg_bufs(isys, 20); - if (ret < 0) -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index 17d4d5630169..cd7375417452 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -25,6 +25,10 @@ - #include "ipu7-isys-csi2.h" - #include "ipu7-isys-video.h" - -+#ifdef CONFIG_DEBUG_FS -+struct dentry; -+ -+#endif - #define IPU_ISYS_ENTITY_PREFIX "Intel IPU7" - - /* FW support max 16 streams */ -@@ -103,6 +107,9 @@ struct ipu7_isys { - unsigned int ref_count; - unsigned int stream_opened; - -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif - struct mutex mutex; /* Serialise isys video open/release related */ - struct mutex stream_mutex; /* Stream start, stop, queueing reqs */ - -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index ee6b63717ed3..5649d8e1c352 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -7,6 +7,9 @@ - #include - #include - #include -+#ifdef CONFIG_DEBUG_FS -+#include -+#endif - #include - #include - #include -@@ -2248,7 +2251,50 @@ void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev) - } - EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7"); - --static void ipu7_pci_config_setup(struct pci_dev *dev) -+#ifdef CONFIG_DEBUG_FS -+static struct debugfs_blob_wrapper isys_fw_error; -+static struct debugfs_blob_wrapper psys_fw_error; -+ -+static int ipu7_init_debugfs(struct ipu7_device *isp) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir(pci_name(isp->pdev), NULL); -+ if (!dir) -+ return -ENOMEM; -+ -+ isys_fw_error.data = &fw_error_log[IPU_IS]; -+ isys_fw_error.size = sizeof(fw_error_log[IPU_IS]); -+ file = debugfs_create_blob("is_fw_error", 0400, dir, &isys_fw_error); -+ if (!file) -+ goto err; -+ psys_fw_error.data = &fw_error_log[IPU_PS]; -+ psys_fw_error.size = sizeof(fw_error_log[IPU_PS]); -+ file = debugfs_create_blob("ps_fw_error", 0400, dir, &psys_fw_error); -+ if (!file) -+ goto err; -+ -+ isp->ipu7_dir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+ -+static void ipu7_remove_debugfs(struct ipu7_device *isp) -+{ -+ /* -+ * Since isys and psys debugfs dir will be created under ipu root dir, -+ * mark its dentry to NULL to avoid duplicate removal. -+ */ -+ debugfs_remove_recursive(isp->ipu7_dir); -+ isp->ipu7_dir = NULL; -+} -+#endif /* CONFIG_DEBUG_FS */ -+ -+static int ipu7_pci_config_setup(struct pci_dev *dev) - { - u16 pci_command; - -@@ -2604,6 +2650,13 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - pm_runtime_put(&isp->psys->auxdev.dev); - } - -+#ifdef CONFIG_DEBUG_FS -+ ret = ipu7_init_debugfs(isp); -+ if (ret) { -+ dev_err_probe(dev, ret, "Failed to initialize debugfs\n"); -+ goto out_ipu_bus_del_devices; -+ } -+#endif - pm_runtime_put_noidle(dev); - pm_runtime_allow(dev); - -@@ -2616,6 +2669,10 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - ipu7_unmap_fw_code_region(isp->isys); - if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) - ipu7_unmap_fw_code_region(isp->psys); -+#ifdef CONFIG_DEBUG_FS -+ if (!IS_ERR_OR_NULL(isp->fw_code_region)) -+ vfree(isp->fw_code_region); -+#endif - if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu)) - ipu7_mmu_cleanup(isp->psys->mmu); - if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu)) -@@ -2636,6 +2693,9 @@ static void ipu7_pci_remove(struct pci_dev *pdev) - { - struct ipu7_device *isp = pci_get_drvdata(pdev); - -+#ifdef CONFIG_DEBUG_FS -+ ipu7_remove_debugfs(isp); -+#endif - if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents) - ipu7_unmap_fw_code_region(isp->isys); - if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents) -diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h -index ac8ac0689468..3b3ea5fb613f 100644 ---- a/drivers/staging/media/ipu7/ipu7.h -+++ b/drivers/staging/media/ipu7/ipu7.h -@@ -81,6 +81,9 @@ struct ipu7_device { - - void __iomem *base; - void __iomem *pb_base; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *ipu7_dir; -+#endif - u8 hw_ver; - bool ipc_reinit; - bool secure_mode; --- -2.43.0 - diff --git a/SPECS/kernel/0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf b/SPECS/kernel/0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf new file mode 100644 index 000000000..54a69abf9 --- /dev/null +++ b/SPECS/kernel/0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf @@ -0,0 +1,123 @@ +From a7b7cbaf27f9c2a83ab053d7048a83126e06654a Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Tue, 30 Dec 2025 16:17:29 -0800 +Subject: [PATCH 04/13] perf/x86/intel/uncore: Remove + has_generic_discovery_table() + +In the !x86_match_cpu() fallback path, has_generic_discovery_table() +is removed because it does not handle multiple PCI devices. Instead, +use PCI_ANY_ID in generic_uncore_init[] to probe all PCI devices. + +For MSR portals, only probe MSR 0x201e to keep the fallback simple, as +this path is best-effort only. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 3 ++ + arch/x86/events/intel/uncore_discovery.c | 42 +++++------------------- + 2 files changed, 12 insertions(+), 33 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index e3193a37a2150..5135a39d81f6b 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1835,6 +1835,9 @@ static const struct uncore_plat_init generic_uncore_init __initconst = { + .cpu_init = intel_uncore_generic_uncore_cpu_init, + .pci_init = intel_uncore_generic_uncore_pci_init, + .mmio_init = intel_uncore_generic_uncore_mmio_init, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = PCI_ANY_ID, ++ .domain[1].discovery_base = UNCORE_DISCOVERY_MSR, + }; + + static const struct x86_cpu_id intel_uncore_match[] __initconst = { +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index 10957163301b9..24b8da18ee6c8 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -12,24 +12,6 @@ + static struct rb_root discovery_tables = RB_ROOT; + static int num_discovered_types[UNCORE_ACCESS_MAX]; + +-static bool has_generic_discovery_table(void) +-{ +- struct pci_dev *dev; +- int dvsec; +- +- dev = pci_get_device(PCI_VENDOR_ID_INTEL, UNCORE_DISCOVERY_TABLE_DEVICE, NULL); +- if (!dev) +- return false; +- +- /* A discovery table device has the unique capability ID. */ +- dvsec = pci_find_next_ext_capability(dev, 0, UNCORE_EXT_CAP_ID_DISCOVERY); +- pci_dev_put(dev); +- if (dvsec) +- return true; +- +- return false; +-} +- + static int logical_die_id; + + static int get_device_die_id(struct pci_dev *dev) +@@ -359,12 +341,7 @@ static bool uncore_discovery_pci(struct uncore_discovery_domain *domain) + struct pci_dev *dev = NULL; + bool parsed = false; + +- if (domain->discovery_base) +- device = domain->discovery_base; +- else if (has_generic_discovery_table()) +- device = UNCORE_DISCOVERY_TABLE_DEVICE; +- else +- device = PCI_ANY_ID; ++ device = domain->discovery_base; + + /* + * Start a new search and iterates through the list of +@@ -407,7 +384,7 @@ static bool uncore_discovery_msr(struct uncore_discovery_domain *domain) + { + unsigned long *die_mask; + bool parsed = false; +- int cpu, die, msr; ++ int cpu, die; + u64 base; + + die_mask = kcalloc(BITS_TO_LONGS(uncore_max_dies()), +@@ -415,16 +392,13 @@ static bool uncore_discovery_msr(struct uncore_discovery_domain *domain) + if (!die_mask) + return false; + +- msr = domain->discovery_base ? +- domain->discovery_base : UNCORE_DISCOVERY_MSR; +- + cpus_read_lock(); + for_each_online_cpu(cpu) { + die = topology_logical_die_id(cpu); + if (__test_and_set_bit(die, die_mask)) + continue; + +- if (rdmsrq_safe_on_cpu(cpu, msr, &base)) ++ if (rdmsrq_safe_on_cpu(cpu, domain->discovery_base, &base)) + continue; + + if (!base) +@@ -447,10 +421,12 @@ bool uncore_discovery(struct uncore_plat_init *init) + + for (i = 0; i < UNCORE_DISCOVERY_DOMAINS; i++) { + domain = &init->domain[i]; +- if (!domain->base_is_pci) +- ret |= uncore_discovery_msr(domain); +- else +- ret |= uncore_discovery_pci(domain); ++ if (domain->discovery_base) { ++ if (!domain->base_is_pci) ++ ret |= uncore_discovery_msr(domain); ++ else ++ ret |= uncore_discovery_pci(domain); ++ } + } + + return ret; +-- +2.43.0 + diff --git a/SPECS/kernel/0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt b/SPECS/kernel/0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt new file mode 100644 index 000000000..940626dd7 --- /dev/null +++ b/SPECS/kernel/0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt @@ -0,0 +1,224 @@ +From 0a6b452a5a68677c64f98d54872fdf065cacb6dc Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:33 -0700 +Subject: [PATCH 4/6] platform/x86:intel/pmc: Relocate lpm_req_guid to + pmc_reg_map +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Relocate the lpm_req_guid field from pmc_info to pmc_reg_map. The +previous implementation stored lpm_req_guid in pmc_info and relied +on pmc_core_find_guid() to retrieve the correct GUID, which was +unnecessary. Since lpm_req_guid is specific to PMC, pmc_reg_map is +a more appropriate location for this information. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-6-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/arl.c | 6 ++---- + drivers/platform/x86/intel/pmc/core.c | 15 ++------------- + drivers/platform/x86/intel/pmc/core.h | 4 +++- + drivers/platform/x86/intel/pmc/lnl.c | 2 +- + drivers/platform/x86/intel/pmc/mtl.c | 6 +++--- + drivers/platform/x86/intel/pmc/ptl.c | 3 +-- + 6 files changed, 12 insertions(+), 24 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c +index c0698ef35df89..eb23bc68340ab 100644 +--- a/drivers/platform/x86/intel/pmc/arl.c ++++ b/drivers/platform/x86/intel/pmc/arl.c +@@ -281,6 +281,7 @@ static const struct pmc_reg_map arl_socs_reg_map = { + .etr3_offset = ETR3_OFFSET, + .pson_residency_offset = TGL_PSON_RESIDENCY_OFFSET, + .pson_residency_counter_step = TGL_PSON_RES_COUNTER_STEP, ++ .lpm_req_guid = SOCS_LPM_REQ_GUID, + }; + + static const struct pmc_bit_map arl_pchs_ltr_show_map[] = { +@@ -648,26 +649,23 @@ static const struct pmc_reg_map arl_pchs_reg_map = { + .lpm_num_maps = ADL_LPM_NUM_MAPS, + .lpm_reg_index = ARL_LPM_REG_INDEX, + .etr3_offset = ETR3_OFFSET, ++ .lpm_req_guid = PCHS_LPM_REQ_GUID, + }; + + static struct pmc_info arl_pmc_info_list[] = { + { +- .guid = IOEP_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_IOEP, + .map = &mtl_ioep_reg_map, + }, + { +- .guid = SOCS_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_SOCS, + .map = &arl_socs_reg_map, + }, + { +- .guid = PCHS_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_PCHS, + .map = &arl_pchs_reg_map, + }, + { +- .guid = SOCM_LPM_REQ_GUID, + .devid = PMC_DEVID_ARL_SOCM, + .map = &mtl_socm_reg_map, + }, +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index 5f58dfa989ad5..e272c971d73a5 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -1444,15 +1444,6 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info + } + } + +-static u32 pmc_core_find_guid(struct pmc_info *list, const struct pmc_reg_map *map) +-{ +- for (; list->map; ++list) +- if (list->map == map) +- return list->guid; +- +- return 0; +-} +- + /* + * This function retrieves low power mode requirement data from PMC Low + * Power Mode (LPM) table. +@@ -1568,7 +1559,6 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + struct pci_dev *pcidev __free(pci_dev_put) = NULL; + struct telem_endpoint *ep; + unsigned int pmc_idx; +- u32 guid; + int ret; + + pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func)); +@@ -1582,11 +1572,10 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * + if (!pmc) + continue; + +- guid = pmc_core_find_guid(pmcdev->regmap_list, pmc->map); +- if (!guid) ++ if (!pmc->map->lpm_req_guid) + return -ENXIO; + +- ep = pmt_telem_find_and_register_endpoint(pcidev, guid, 0); ++ ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); + if (IS_ERR(ep)) { + dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); + return -EPROBE_DEFER; +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index d80257b37ca98..cccd3bcafe00d 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -356,6 +356,7 @@ struct pmc_bit_map { + * @s0ix_blocker_offset PWRMBASE offset to S0ix blocker counter + * @num_s0ix_blocker: Number of S0ix blockers + * @blocker_req_offset: Telemetry offset to S0ix blocker low power mode substate requirement table ++ * @lpm_req_guid: Telemetry GUID to read low power mode substate requirement table + * + * Each PCH has unique set of register offsets and bit indexes. This structure + * captures them to have a common implementation. +@@ -397,6 +398,8 @@ struct pmc_reg_map { + const u8 *lpm_reg_index; + const u32 pson_residency_offset; + const u32 pson_residency_counter_step; ++ /* GUID for telemetry regions */ ++ const u32 lpm_req_guid; + }; + + /** +@@ -406,7 +409,6 @@ struct pmc_reg_map { + * specific attributes + */ + struct pmc_info { +- u32 guid; + u16 devid; + const struct pmc_reg_map *map; + }; +diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c +index 6fa027e7071f4..1cd81ee54dcf8 100644 +--- a/drivers/platform/x86/intel/pmc/lnl.c ++++ b/drivers/platform/x86/intel/pmc/lnl.c +@@ -533,11 +533,11 @@ static const struct pmc_reg_map lnl_socm_reg_map = { + .s0ix_blocker_maps = lnl_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .lpm_reg_index = LNL_LPM_REG_INDEX, ++ .lpm_req_guid = SOCM_LPM_REQ_GUID, + }; + + static struct pmc_info lnl_pmc_info_list[] = { + { +- .guid = SOCM_LPM_REQ_GUID, + .devid = PMC_DEVID_LNL_SOCM, + .map = &lnl_socm_reg_map, + }, +diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c +index 19470ca311cf7..57508cbf9cd42 100644 +--- a/drivers/platform/x86/intel/pmc/mtl.c ++++ b/drivers/platform/x86/intel/pmc/mtl.c +@@ -473,6 +473,7 @@ const struct pmc_reg_map mtl_socm_reg_map = { + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, ++ .lpm_req_guid = SOCP_LPM_REQ_GUID, + }; + + static const struct pmc_bit_map mtl_ioep_pfear_map[] = { +@@ -797,6 +798,7 @@ const struct pmc_reg_map mtl_ioep_reg_map = { + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, ++ .lpm_req_guid = IOEP_LPM_REQ_GUID, + }; + + static const struct pmc_bit_map mtl_ioem_pfear_map[] = { +@@ -944,21 +946,19 @@ static const struct pmc_reg_map mtl_ioem_reg_map = { + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_reg_index = MTL_LPM_REG_INDEX, ++ .lpm_req_guid = IOEM_LPM_REQ_GUID, + }; + + static struct pmc_info mtl_pmc_info_list[] = { + { +- .guid = SOCP_LPM_REQ_GUID, + .devid = PMC_DEVID_MTL_SOCM, + .map = &mtl_socm_reg_map, + }, + { +- .guid = IOEP_LPM_REQ_GUID, + .devid = PMC_DEVID_MTL_IOEP, + .map = &mtl_ioep_reg_map, + }, + { +- .guid = IOEM_LPM_REQ_GUID, + .devid = PMC_DEVID_MTL_IOEM, + .map = &mtl_ioem_reg_map + }, +diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c +index 1b35b84e06fa2..1f48e2bbc699f 100644 +--- a/drivers/platform/x86/intel/pmc/ptl.c ++++ b/drivers/platform/x86/intel/pmc/ptl.c +@@ -528,16 +528,15 @@ static const struct pmc_reg_map ptl_pcdp_reg_map = { + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = PTL_NUM_S0IX_BLOCKER, + .blocker_req_offset = PTL_BLK_REQ_OFFSET, ++ .lpm_req_guid = PCDP_LPM_REQ_GUID, + }; + + static struct pmc_info ptl_pmc_info_list[] = { + { +- .guid = PCDP_LPM_REQ_GUID, + .devid = PMC_DEVID_PTL_PCDH, + .map = &ptl_pcdp_reg_map, + }, + { +- .guid = PCDP_LPM_REQ_GUID, + .devid = PMC_DEVID_PTL_PCDP, + .map = &ptl_pcdp_reg_map, + }, +-- +2.43.0 + diff --git a/SPECS/kernel/0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet b/SPECS/kernel/0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet new file mode 100644 index 000000000..a9f770343 --- /dev/null +++ b/SPECS/kernel/0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet @@ -0,0 +1,32 @@ +From 0518f6873bfdb48c1767661c518f95346f22a62a Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Fri, 30 Jul 2021 08:33:53 +0800 +Subject: [PATCH 04/14] rtnetlink: Fix unchecked return value of + dev_xdp_query_md_btf() + +This patch is to check the return value of dev_xdp_query_md_btf() +whether it contain a valid btf id or vice versa. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + net/core/rtnetlink.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index b00c602d676fe..b4ac5dedaed0a 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1770,6 +1770,9 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + } + + md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); ++ if (!md_btf_id) ++ goto err_cancel; ++ + nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); + nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); + +-- +2.43.0 + diff --git a/SPECS/kernel/0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal b/SPECS/kernel/0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal deleted file mode 100644 index 6421c83a6..000000000 --- a/SPECS/kernel/0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal +++ /dev/null @@ -1,86 +0,0 @@ -From f7522111511911d62f066a39ac1a66c5d6f7c493 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:14 -0700 -Subject: [PATCH 04/11] thermal: intel: int340x: Add module parameter for - balanced Slider - -By default, the SoC slider value for the "balanced" platform profile is -set to 3. This update introduces a new module parameter, allowing users -to modify this default value. - -The module parameter can be specified during load time to set a custom -slider value for the "balanced" profile. If the module parameter is not -specified at load time and is updated later, the new value will only take -effect after the next write of "balanced" to the sysfs "profile" -attribute. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-4-srinivas.pandruvada@linux.intel.com -[ rjw: Minor adjustments of module param description ] -Signed-off-by: Rafael J. Wysocki ---- - .../processor_thermal_soc_slider.c | 41 +++++++++++++++++++ - 1 file changed, 41 insertions(+) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -index 57782a63b9b5..ffbad6b4326e 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -53,6 +53,43 @@ static u8 slider_values[] = { - [SOC_POWER_SLIDER_POWERSAVE] = SOC_SLIDER_VALUE_MAXIMUM, - }; - -+/* Lock to protect module param updates */ -+static DEFINE_MUTEX(slider_param_lock); -+ -+static int slider_balanced_param = SOC_SLIDER_VALUE_BALANCE; -+ -+static int slider_def_balance_set(const char *arg, const struct kernel_param *kp) -+{ -+ u8 slider_val; -+ int ret; -+ -+ guard(mutex)(&slider_param_lock); -+ -+ ret = kstrtou8(arg, 16, &slider_val); -+ if (!ret) { -+ if (slider_val > SOC_SLIDER_VALUE_MAXIMUM) -+ return -EINVAL; -+ -+ slider_balanced_param = slider_val; -+ } -+ -+ return ret; -+} -+ -+static int slider_def_balance_get(char *buf, const struct kernel_param *kp) -+{ -+ guard(mutex)(&slider_param_lock); -+ return sysfs_emit(buf, "%02x\n", slider_values[SOC_POWER_SLIDER_BALANCE]); -+} -+ -+static const struct kernel_param_ops slider_def_balance_ops = { -+ .set = slider_def_balance_set, -+ .get = slider_def_balance_get, -+}; -+ -+module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644); -+MODULE_PARM_DESC(slider_balance, "Set slider default value for balance"); -+ - /* Convert from platform power profile option to SoC slider value */ - static int convert_profile_to_power_slider(enum platform_profile_option profile) - { -@@ -115,6 +152,10 @@ static int power_slider_platform_profile_set(struct device *dev, - if (!proc_priv) - return -EOPNOTSUPP; - -+ guard(mutex)(&slider_param_lock); -+ -+ slider_values[SOC_POWER_SLIDER_BALANCE] = slider_balanced_param; -+ - slider = convert_profile_to_power_slider(profile); - if (slider < 0) - return slider; --- -2.43.0 - diff --git a/SPECS/kernel/0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt b/SPECS/kernel/0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt deleted file mode 100644 index 9a17548d0..000000000 --- a/SPECS/kernel/0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt +++ /dev/null @@ -1,142 +0,0 @@ -From 7218ac5ef718c410845cfdbf55822b3953aeba36 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Mon, 3 Jun 2024 08:32:00 +0300 -Subject: [PATCH 4/4] thunderbolt: Add Kconfig option to disable PCIe tunneling - -In typical cases PCIe tunneling is needed to make the devices fully -usable for the host system. However, it poses a security issue because -they can also use DMA to access the host memory. We already have two -ways of preventing this, one an IOMMU that is enabled on recent systems -by default and the second is the "authorized" attribute under each -connected device that needs to be written by userspace before a PCIe -tunnel is created. This option adds one more by adding a Kconfig option, -which is enabled by default, that can be used to make kernel binaries -where PCIe tunneling is completely disabled. - -Signed-off-by: Mika Westerberg ---- - drivers/thunderbolt/Kconfig | 18 ++++++++++++++++++ - drivers/thunderbolt/tb.c | 2 +- - drivers/thunderbolt/tb.h | 9 +++++++++ - drivers/thunderbolt/tunnel.c | 8 ++++---- - drivers/thunderbolt/usb4.c | 2 +- - 5 files changed, 33 insertions(+), 6 deletions(-) - -diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig -index 0abdb69ee9f4..8bf4ecf7f76e 100644 ---- a/drivers/thunderbolt/Kconfig -+++ b/drivers/thunderbolt/Kconfig -@@ -18,6 +18,24 @@ menuconfig USB4 - - if USB4 - -+config USB4_PCIE_TUNNELING -+ bool "Allow PCI Express tunneling over USB4 fabric" -+ depends on PCI -+ default y -+ help -+ USB4 and Thunderbolt devices typically include PCIe switch -+ with a number of PCIe endpoints such as USB host controllers, -+ GPUs and network adapters. These are made available to the -+ host system through PCIe tunneling. These can use DMA and -+ therefore have access to the host memory which is typically -+ guarded by an IOMMU. This option allows disabling PCIe -+ tunneling completely. -+ -+ For devices to be usable it is recommended to say Y here. -+ -+ Note this only works with systems that use Software Based -+ Connection Manager (this is most USB4 hosts). -+ - config USB4_DEBUGFS_WRITE - bool "Enable write by debugfs to configuration spaces (DANGEROUS)" - help -diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c -index c14ab1fbeeaf..0514a673471a 100644 ---- a/drivers/thunderbolt/tb.c -+++ b/drivers/thunderbolt/tb.c -@@ -3364,7 +3364,7 @@ struct tb *tb_probe(struct tb_nhi *nhi) - if (!tb) - return NULL; - -- if (tb_acpi_may_tunnel_pcie()) -+ if (tb_may_tunnel_pcie()) - tb->security_level = TB_SECURITY_USER; - else - tb->security_level = TB_SECURITY_NOPCIE; -diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h -index f503bad86413..7d5c673412f4 100644 ---- a/drivers/thunderbolt/tb.h -+++ b/drivers/thunderbolt/tb.h -@@ -1518,6 +1518,15 @@ static inline int tb_acpi_power_on_retimers(struct tb_port *port) { return 0; } - static inline int tb_acpi_power_off_retimers(struct tb_port *port) { return 0; } - #endif - -+static inline bool tb_may_tunnel_pcie(void) -+{ -+#ifdef CONFIG_USB4_PCIE_TUNNELING -+ return tb_acpi_may_tunnel_pcie(); -+#else -+ return false; -+#endif -+} -+ - #ifdef CONFIG_DEBUG_FS - void tb_debugfs_init(void); - void tb_debugfs_exit(void); -diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c -index d52efe3f658c..6b85da2eea60 100644 ---- a/drivers/thunderbolt/tunnel.c -+++ b/drivers/thunderbolt/tunnel.c -@@ -130,7 +130,7 @@ static unsigned int tb_available_credits(const struct tb_port *port, - size_t ndp; - - usb3 = tb_acpi_may_tunnel_usb3() ? sw->max_usb3_credits : 0; -- pcie = tb_acpi_may_tunnel_pcie() ? sw->max_pcie_credits : 0; -+ pcie = tb_may_tunnel_pcie() ? sw->max_pcie_credits : 0; - - if (tb_acpi_is_xdomain_allowed()) { - spare = min_not_zero(sw->max_dma_credits, dma_credits); -@@ -553,7 +553,7 @@ bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up, - if (WARN_ON_ONCE(!port->remote)) - return false; - -- if (!tb_acpi_may_tunnel_pcie()) -+ if (!tb_may_tunnel_pcie()) - return false; - - if (tb_port_get_link_generation(port) < 4) -@@ -1720,7 +1720,7 @@ static unsigned int tb_dma_available_credits(const struct tb_port *port) - int credits; - - credits = tb_available_credits(port, NULL); -- if (tb_acpi_may_tunnel_pcie()) -+ if (tb_may_tunnel_pcie()) - credits -= sw->max_pcie_credits; - credits -= port->dma_credits; - -@@ -2031,7 +2031,7 @@ static int tb_usb3_consumed_bandwidth(struct tb_tunnel *tunnel, - int *consumed_up, int *consumed_down) - { - struct tb_port *port = tb_upstream_port(tunnel->dst_port->sw); -- int pcie_weight = tb_acpi_may_tunnel_pcie() ? TB_PCI_WEIGHT : 0; -+ int pcie_weight = tb_may_tunnel_pcie() ? TB_PCI_WEIGHT : 0; - - /* - * PCIe tunneling, if enabled, affects the USB3 bandwidth so -diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c -index fdae76c8f728..b3d22873e837 100644 ---- a/drivers/thunderbolt/usb4.c -+++ b/drivers/thunderbolt/usb4.c -@@ -276,7 +276,7 @@ int usb4_switch_setup(struct tb_switch *sw) - * Only enable PCIe tunneling if the parent router supports it - * and it is not disabled. - */ -- if (tb_acpi_may_tunnel_pcie() && -+ if (tb_may_tunnel_pcie() && - tb_switch_find_port(parent, TB_TYPE_PCIE_DOWN)) { - val |= ROUTER_CS_5_PTO; - /* --- -2.43.0 - diff --git a/SPECS/kernel/0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo b/SPECS/kernel/0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo new file mode 100644 index 000000000..2324125c7 --- /dev/null +++ b/SPECS/kernel/0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo @@ -0,0 +1,258 @@ +From 19762cd26107878f80efcb5e366a47cee62e1b31 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 19:41:30 -0300 +Subject: [PATCH 04/21] tools/power turbostat: Refactor added-counter value + printing code + +We build up many copies of very similar code... + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 153 ++++++++++---------------- + 1 file changed, 57 insertions(+), 96 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 5d753df8706d0..f9b99940b2478 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2717,10 +2717,24 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + */ + static inline int print_name(int width, int *printed, char *delim, char *name) + { +- if (width == 64) ++ if (width <= 32) ++ return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); ++ else + return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); ++} ++static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) ++{ ++ if (width <= 32) ++ return (sprintf(outp, "%s%08x", (*printed++ ? delim : ""), (unsigned int)value)); + else +- return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); ++ return (sprintf(outp, "%s%016llx", (*printed++ ? delim : ""), value)); ++} ++static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) ++{ ++ if (width <= 32) ++ return (sprintf(outp, "%s%d", (*printed++ ? delim : ""), (unsigned int)value)); ++ else ++ return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + } + + void print_header(char *delim) +@@ -3221,20 +3235,13 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->smi_count); + +- /* Added counters */ ++ /* Added Thread Counters */ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { +- if (mp->format == FORMAT_RAW) { +- if (mp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)t->counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->counter[i]); +- } else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), t->counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->counter[i]); +- } else if (mp->format == FORMAT_PERCENT) { ++ if (mp->format == FORMAT_RAW) ++ outp += print_hex_value(mp->width, &printed, delim, t->counter[i]); ++ else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(mp->width, &printed, delim, t->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) { + if (mp->type == COUNTER_USEC) + outp += + sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +@@ -3244,21 +3251,13 @@ int format_counters(PER_THREAD_PARAMS) + } + } + +- /* Added perf counters */ ++ /* Added perf Thread Counters */ + for (i = 0, pp = sys.perf_tp; pp; ++i, pp = pp->next) { +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)t->perf_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->perf_counter[i]); +- } else if (pp->format == FORMAT_DELTA) { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), t->perf_counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), t->perf_counter[i]); +- } else if (pp->format == FORMAT_PERCENT) { ++ if (pp->format == FORMAT_RAW) ++ outp += print_hex_value(pp->width, &printed, delim, t->perf_counter[i]); ++ else if (pp->format == FORMAT_DELTA) ++ outp += print_decimal_value(pp->width, &printed, delim, t->perf_counter[i]); ++ else if (pp->format == FORMAT_PERCENT) { + if (pp->type == COUNTER_USEC) + outp += + sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +@@ -3269,17 +3268,13 @@ int format_counters(PER_THREAD_PARAMS) + } + } + ++ /* Added PMT Thread Counters */ + for (i = 0, ppmt = sys.pmt_tp; ppmt; i++, ppmt = ppmt->next) { + const unsigned long value_raw = t->pmt_counter[i]; + double value_converted; + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)t->pmt_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), t->pmt_counter[i]); +- ++ outp += print_hex_value(pmt_counter_get_width(ppmt), &printed, delim, t->pmt_counter[i]); + break; + + case PMT_TYPE_XTAL_TIME: +@@ -3319,52 +3314,35 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_CORE_THROT_CNT)) + outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->core_throt_cnt); + ++ /* Added Core Counters */ + for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { +- if (mp->format == FORMAT_RAW) { +- if (mp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)c->counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->counter[i]); +- } else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), c->counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->counter[i]); +- } else if (mp->format == FORMAT_PERCENT) { ++ if (mp->format == FORMAT_RAW) ++ outp += print_hex_value(mp->width, &printed, delim, c->counter[i]); ++ else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(mp->width, &printed, delim, c->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->counter[i] / tsc); + } + } + ++ /* Added perf Core counters */ + for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) { +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)c->perf_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->perf_counter[i]); +- } else if (pp->format == FORMAT_DELTA) { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), c->perf_counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), c->perf_counter[i]); +- } else if (pp->format == FORMAT_PERCENT) { ++ if (pp->format == FORMAT_RAW) ++ outp += print_hex_value(pp->width, &printed, delim, c->perf_counter[i]); ++ else if (pp->format == FORMAT_DELTA) ++ outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); ++ else if (pp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->perf_counter[i] / tsc); + } + } + ++ /* Added PMT Core counters */ + for (i = 0, ppmt = sys.pmt_cp; ppmt; i++, ppmt = ppmt->next) { + const unsigned long value_raw = c->pmt_counter[i]; + double value_converted; + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)c->pmt_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), c->pmt_counter[i]); +- ++ outp += print_hex_value(pmt_counter_get_width(ppmt), &printed, delim, c->pmt_counter[i]); + break; + + case PMT_TYPE_XTAL_TIME: +@@ -3518,37 +3496,24 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_UNCORE_MHZ)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), p->uncore_mhz); + ++ /* Added Package Counters */ + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { +- if (mp->format == FORMAT_RAW) { +- if (mp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), (unsigned int)p->counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->counter[i]); +- } else if (mp->format == FORMAT_DELTA) { +- if ((mp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), p->counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), p->counter[i]); +- } else if (mp->format == FORMAT_PERCENT) { ++ if (mp->format == FORMAT_RAW) ++ outp += print_hex_value(mp->width, &printed, delim, p->counter[i]); ++ else if (mp->format == FORMAT_DELTA) ++ outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->counter[i] / tsc); + } else if (mp->type == COUNTER_K2M) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->counter[i] / 1000); + } + ++ /* Added perf Package Counters */ + for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) { +- if (pp->format == FORMAT_RAW) { +- if (pp->width == 32) +- outp += +- sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)p->perf_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->perf_counter[i]); +- } else if (pp->format == FORMAT_DELTA) { +- if ((pp->type == COUNTER_ITEMS) && sums_need_wide_columns) +- outp += sprintf(outp, "%s%8lld", (printed++ ? delim : ""), p->perf_counter[i]); +- else +- outp += sprintf(outp, "%s%lld", (printed++ ? delim : ""), p->perf_counter[i]); ++ if (pp->format == FORMAT_RAW) ++ outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); ++ else if (pp->format == FORMAT_DELTA) { ++ outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); + } else if (pp->format == FORMAT_PERCENT) { + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->perf_counter[i] / tsc); + } else if (pp->type == COUNTER_K2M) { +@@ -3557,17 +3522,13 @@ int format_counters(PER_THREAD_PARAMS) + } + } + ++ /* Added PMT Package Counters */ + for (i = 0, ppmt = sys.pmt_pp; ppmt; i++, ppmt = ppmt->next) { + const unsigned long value_raw = p->pmt_counter[i]; + double value_converted; + switch (ppmt->type) { + case PMT_TYPE_RAW: +- if (pmt_counter_get_width(ppmt) <= 32) +- outp += sprintf(outp, "%s0x%08x", (printed++ ? delim : ""), +- (unsigned int)p->pmt_counter[i]); +- else +- outp += sprintf(outp, "%s0x%016llx", (printed++ ? delim : ""), p->pmt_counter[i]); +- ++ outp += print_hex_value(pmt_counter_get_width(ppmt), &printed, delim, p->pmt_counter[i]); + break; + + case PMT_TYPE_XTAL_TIME: +-- +2.43.0 + diff --git a/SPECS/kernel/0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi b/SPECS/kernel/0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi new file mode 100644 index 000000000..a2f08b20d --- /dev/null +++ b/SPECS/kernel/0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi @@ -0,0 +1,228 @@ +From 52c71c618c4e3b0ab4515b0f0eaa2cdc1205c36d Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Thu, 28 Aug 2025 13:26:38 -0400 +Subject: [PATCH 04/44] x86/cea: Prefix event stack names with ESTACK_ + +Add the ESTACK_ prefix to event stack names to improve clarity and +readability. Without the prefix, names like DF, NMI, and DB are too +brief and potentially ambiguous. + +This renaming also prepares for converting __this_cpu_ist_top_va from +a macro into a function that accepts an enum exception_stack_ordering +argument, without requiring changes to existing callsites. + +Acked-by: Dave Hansen +Signed-off-by: Xin Li (Intel) +--- + +Changes in v7: +* Move rename code to this patch (Dave Hansen). +* Fix a vertical alignment (Dave Hansen). +--- + arch/x86/coco/sev/noinstr.c | 4 ++-- + arch/x86/coco/sev/vc-handle.c | 2 +- + arch/x86/include/asm/cpu_entry_area.h | 26 +++++++++++++------------- + arch/x86/kernel/cpu/common.c | 10 +++++----- + arch/x86/kernel/dumpstack_64.c | 14 +++++++------- + arch/x86/kernel/fred.c | 6 +++--- + arch/x86/kernel/traps.c | 2 +- + arch/x86/mm/cpu_entry_area.c | 12 ++++++------ + arch/x86/mm/fault.c | 2 +- + 9 files changed, 39 insertions(+), 39 deletions(-) + +diff --git a/arch/x86/coco/sev/noinstr.c b/arch/x86/coco/sev/noinstr.c +index b527eafb63123..c3985c9b232c5 100644 +--- a/arch/x86/coco/sev/noinstr.c ++++ b/arch/x86/coco/sev/noinstr.c +@@ -30,7 +30,7 @@ static __always_inline bool on_vc_stack(struct pt_regs *regs) + if (ip_within_syscall_gap(regs)) + return false; + +- return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC))); ++ return ((sp >= __this_cpu_ist_bottom_va(ESTACK_VC)) && (sp < __this_cpu_ist_top_va(ESTACK_VC))); + } + + /* +@@ -82,7 +82,7 @@ void noinstr __sev_es_ist_exit(void) + /* Read IST entry */ + ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]); + +- if (WARN_ON(ist == __this_cpu_ist_top_va(VC))) ++ if (WARN_ON(ist == __this_cpu_ist_top_va(ESTACK_VC))) + return; + + /* Read back old IST entry and write it to the TSS */ +diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c +index 7fc136a353347..1d3f086ae4c3d 100644 +--- a/arch/x86/coco/sev/vc-handle.c ++++ b/arch/x86/coco/sev/vc-handle.c +@@ -871,7 +871,7 @@ static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt, + + static __always_inline bool is_vc2_stack(unsigned long sp) + { +- return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2)); ++ return (sp >= __this_cpu_ist_bottom_va(ESTACK_VC2) && sp < __this_cpu_ist_top_va(ESTACK_VC2)); + } + + static __always_inline bool vc_from_invalid_context(struct pt_regs *regs) +diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h +index 462fc34f13176..d0f884c281787 100644 +--- a/arch/x86/include/asm/cpu_entry_area.h ++++ b/arch/x86/include/asm/cpu_entry_area.h +@@ -18,19 +18,19 @@ + + /* Macro to enforce the same ordering and stack sizes */ + #define ESTACKS_MEMBERS(guardsize, optional_stack_size) \ +- char DF_stack_guard[guardsize]; \ +- char DF_stack[EXCEPTION_STKSZ]; \ +- char NMI_stack_guard[guardsize]; \ +- char NMI_stack[EXCEPTION_STKSZ]; \ +- char DB_stack_guard[guardsize]; \ +- char DB_stack[EXCEPTION_STKSZ]; \ +- char MCE_stack_guard[guardsize]; \ +- char MCE_stack[EXCEPTION_STKSZ]; \ +- char VC_stack_guard[guardsize]; \ +- char VC_stack[optional_stack_size]; \ +- char VC2_stack_guard[guardsize]; \ +- char VC2_stack[optional_stack_size]; \ +- char IST_top_guard[guardsize]; \ ++ char ESTACK_DF_stack_guard[guardsize]; \ ++ char ESTACK_DF_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_NMI_stack_guard[guardsize]; \ ++ char ESTACK_NMI_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_DB_stack_guard[guardsize]; \ ++ char ESTACK_DB_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_MCE_stack_guard[guardsize]; \ ++ char ESTACK_MCE_stack[EXCEPTION_STKSZ]; \ ++ char ESTACK_VC_stack_guard[guardsize]; \ ++ char ESTACK_VC_stack[optional_stack_size]; \ ++ char ESTACK_VC2_stack_guard[guardsize]; \ ++ char ESTACK_VC2_stack[optional_stack_size]; \ ++ char ESTACK_IST_top_guard[guardsize]; \ + + /* The exception stacks' physical storage. No guard pages required */ + struct exception_stacks { +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +index 02d97834a1d4d..a2a251549fa15 100644 +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -2336,12 +2336,12 @@ static inline void setup_getcpu(int cpu) + static inline void tss_setup_ist(struct tss_struct *tss) + { + /* Set up the per-CPU TSS IST stacks */ +- tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(DF); +- tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(NMI); +- tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(DB); +- tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(MCE); ++ tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(ESTACK_DF); ++ tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(ESTACK_NMI); ++ tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(ESTACK_DB); ++ tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(ESTACK_MCE); + /* Only mapped when SEV-ES is active */ +- tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(VC); ++ tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(ESTACK_VC); + } + #else /* CONFIG_X86_64 */ + static inline void tss_setup_ist(struct tss_struct *tss) { } +diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c +index 6c5defd6569a3..40f51e2781715 100644 +--- a/arch/x86/kernel/dumpstack_64.c ++++ b/arch/x86/kernel/dumpstack_64.c +@@ -73,7 +73,7 @@ struct estack_pages { + PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = { \ + .offs = CEA_ESTACK_OFFS(st), \ + .size = CEA_ESTACK_SIZE(st), \ +- .type = STACK_TYPE_EXCEPTION + ESTACK_ ##st, } ++ .type = STACK_TYPE_EXCEPTION + st, } + + /* + * Array of exception stack page descriptors. If the stack is larger than +@@ -83,12 +83,12 @@ struct estack_pages { + */ + static const + struct estack_pages estack_pages[CEA_ESTACK_PAGES] ____cacheline_aligned = { +- EPAGERANGE(DF), +- EPAGERANGE(NMI), +- EPAGERANGE(DB), +- EPAGERANGE(MCE), +- EPAGERANGE(VC), +- EPAGERANGE(VC2), ++ EPAGERANGE(ESTACK_DF), ++ EPAGERANGE(ESTACK_NMI), ++ EPAGERANGE(ESTACK_DB), ++ EPAGERANGE(ESTACK_MCE), ++ EPAGERANGE(ESTACK_VC), ++ EPAGERANGE(ESTACK_VC2), + }; + + static __always_inline bool in_exception_stack(unsigned long *stack, struct stack_info *info) +diff --git a/arch/x86/kernel/fred.c b/arch/x86/kernel/fred.c +index 816187da3a47c..06d944a3d0511 100644 +--- a/arch/x86/kernel/fred.c ++++ b/arch/x86/kernel/fred.c +@@ -87,7 +87,7 @@ void cpu_init_fred_rsps(void) + FRED_STKLVL(X86_TRAP_DF, FRED_DF_STACK_LEVEL)); + + /* The FRED equivalents to IST stacks... */ +- wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(DB)); +- wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(NMI)); +- wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(DF)); ++ wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); ++ wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); ++ wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); + } +diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c +index 6b22611e69cc8..47b7b7495114f 100644 +--- a/arch/x86/kernel/traps.c ++++ b/arch/x86/kernel/traps.c +@@ -954,7 +954,7 @@ asmlinkage __visible noinstr struct pt_regs *vc_switch_off_ist(struct pt_regs *r + + if (!get_stack_info_noinstr(stack, current, &info) || info.type == STACK_TYPE_ENTRY || + info.type > STACK_TYPE_EXCEPTION_LAST) +- sp = __this_cpu_ist_top_va(VC2); ++ sp = __this_cpu_ist_top_va(ESTACK_VC2); + + sync: + /* +diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c +index 575f863f3c75e..9fa371af8abc7 100644 +--- a/arch/x86/mm/cpu_entry_area.c ++++ b/arch/x86/mm/cpu_entry_area.c +@@ -151,15 +151,15 @@ static void __init percpu_setup_exception_stacks(unsigned int cpu) + * by guard pages so each stack must be mapped separately. DB2 is + * not mapped; it just exists to catch triple nesting of #DB. + */ +- cea_map_stack(DF); +- cea_map_stack(NMI); +- cea_map_stack(DB); +- cea_map_stack(MCE); ++ cea_map_stack(ESTACK_DF); ++ cea_map_stack(ESTACK_NMI); ++ cea_map_stack(ESTACK_DB); ++ cea_map_stack(ESTACK_MCE); + + if (IS_ENABLED(CONFIG_AMD_MEM_ENCRYPT)) { + if (cc_platform_has(CC_ATTR_GUEST_STATE_ENCRYPT)) { +- cea_map_stack(VC); +- cea_map_stack(VC2); ++ cea_map_stack(ESTACK_VC); ++ cea_map_stack(ESTACK_VC2); + } + } + } +diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c +index 998bd807fc7ba..6d59ff7c73e17 100644 +--- a/arch/x86/mm/fault.c ++++ b/arch/x86/mm/fault.c +@@ -671,7 +671,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code, + * and then double-fault, though, because we're likely to + * break the console driver and lose most of the stack dump. + */ +- call_on_stack(__this_cpu_ist_top_va(DF) - sizeof(void*), ++ call_on_stack(__this_cpu_ist_top_va(ESTACK_DF) - sizeof(void *), + handle_stack_overflow, + ASM_CALL_ARG3, + , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info)); +-- +2.43.0 + diff --git a/SPECS/kernel/0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac b/SPECS/kernel/0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac deleted file mode 100644 index 2c16ee695..000000000 --- a/SPECS/kernel/0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac +++ /dev/null @@ -1,59 +0,0 @@ -From 5deabf6f8b12692ad9f18b425c2290e2f0d9c107 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Wed, 23 Jul 2025 20:31:05 +0800 -Subject: [PATCH 05/13] EDAC/skx_common: Make skx_dev->imc[] a flexible array - -The current skx->imc[NUM_IMC] array of memory controller instances is -sized using the macro NUM_IMC. Each time EDAC support is added for a -new CPU, NUM_IMC needs to be updated to ensure it is greater than or -equal to the number of memory controllers for the new CPU. This approach -is inconvenient and results in memory waste for older CPUs with fewer -memory controllers. - -To address this, make skx->imc[] a flexible array and determine its size -from configuration data or at runtime. - -Suggested-by: Tony Luck -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 3 ++- - drivers/edac/skx_common.h | 2 +- - 2 files changed, 3 insertions(+), 2 deletions(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index e03268e9073f..09187043c045 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -17,6 +17,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -343,7 +344,7 @@ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list) - if (!pdev) - break; - ndev++; -- d = kzalloc(sizeof(*d), GFP_KERNEL); -+ d = kzalloc(struct_size(d, imc, imc_num), GFP_KERNEL); - if (!d) { - pci_dev_put(pdev); - return -ENOMEM; -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index 95d61d23f89e..e7038fd45d06 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -172,7 +172,7 @@ struct skx_dev { - u8 colbits; - } dimms[NUM_DIMMS]; - } chan[NUM_CHANNELS]; -- } imc[NUM_IMC]; -+ } imc[]; - }; - - struct skx_pvt { --- -2.43.0 - diff --git a/SPECS/kernel/0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security b/SPECS/kernel/0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security new file mode 100644 index 000000000..5269ebf2d --- /dev/null +++ b/SPECS/kernel/0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security @@ -0,0 +1,180 @@ +From 6ddc16899f378f03affaac0b7a2a864938afe044 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Tue, 1 Mar 2022 16:36:25 +0200 +Subject: [PATCH 5/8] INTEL_DII: mei: avoid reset if fw is down + +If FW is not ready and FW reset flag is off +do not start reset flow as FW will not answer anyway. +Wait till interrupt with FW reset flag on to start reset flow. + +Flow: +FW on the way down unset ME_RDY_HRA and ME_RST_HRA and sends interrupt. +Driver receives interrupt and stops all communication. +(Before the patch here the driver starts reset flow). +FW on the way up set ME_RST_HRA and sends interrupt. +Driver receives interrupt and starts the link reset flow. +FW sets ME_RDY_HRA and unsets ME_RST_HRA and sends interrupt. +Driver continues with link reset flow. + +Limit the flow to GSC for now. + +Co-developed-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/gsc-me.c | 2 +- + drivers/misc/mei/hw-me.c | 16 ++++++++++++++++ + drivers/misc/mei/hw-me.h | 2 ++ + drivers/misc/mei/init.c | 7 +++++-- + drivers/misc/mei/main.c | 1 + + drivers/misc/mei/mei_dev.h | 3 ++- + 6 files changed, 27 insertions(+), 4 deletions(-) + +diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c +index 93cba090ea088..72e1d195dce9a 100644 +--- a/drivers/misc/mei/gsc-me.c ++++ b/drivers/misc/mei/gsc-me.c +@@ -21,7 +21,7 @@ + + #include "mei-trace.h" + +-#define MEI_GSC_RPM_TIMEOUT 500 ++#define MEI_GSC_RPM_TIMEOUT 2000 + + static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) + { +diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c +index c92a0e27787bb..78b3050f2f801 100644 +--- a/drivers/misc/mei/hw-me.c ++++ b/drivers/misc/mei/hw-me.c +@@ -1299,6 +1299,7 @@ EXPORT_SYMBOL_GPL(mei_me_irq_quick_handler); + irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) + { + struct mei_device *dev = (struct mei_device *) dev_id; ++ struct mei_me_hw *hw = to_me_hw(dev); + struct list_head cmpl_list; + s32 slots; + u32 hcsr; +@@ -1313,6 +1314,16 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) + + INIT_LIST_HEAD(&cmpl_list); + ++ /* HW not ready without reset - HW is powering down */ ++ if (hw->cfg->hw_down_supported && !mei_hw_is_ready(dev) && !mei_me_hw_is_resetting(dev)) { ++ dev_notice(&dev->dev, "FW not ready and not resetting\n"); ++ mei_cl_all_disconnect(dev); ++ /* move device to fw down state to allow reset flow on next interrupt */ ++ mei_set_devstate(dev, MEI_DEV_FW_DOWN); ++ pm_runtime_mark_last_busy(dev->parent); ++ goto end; ++ } ++ + /* check if ME wants a reset */ + if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { + if (kind_is_gsc(dev) || kind_is_gscfi(dev)) { +@@ -1654,6 +1665,9 @@ static enum mei_dev_kind mei_cfg_kind_ioe(const struct pci_dev *pdev) + #define MEI_CFG_TRC \ + .hw_trc_supported = 1 + ++#define MEI_CFG_DOWN \ ++ .hw_down_supported = 1 ++ + /* ICH Legacy devices */ + static const struct mei_cfg mei_me_ich_cfg = { + MEI_CFG_KIND_MEI, +@@ -1778,6 +1792,7 @@ static const struct mei_cfg mei_me_gsc_cfg = { + MEI_CFG_KIND_GSC, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, ++ MEI_CFG_DOWN, + }; + + /* Graphics System Controller Firmware Interface */ +@@ -1785,6 +1800,7 @@ static const struct mei_cfg mei_me_gscfi_cfg = { + MEI_CFG_KIND_GSCFI, + MEI_CFG_PCH8_HFS, + MEI_CFG_FW_VER_SUPP, ++ MEI_CFG_DOWN, + }; + + /* +diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h +index 520f2b7bd301a..db5df58b26c88 100644 +--- a/drivers/misc/mei/hw-me.h ++++ b/drivers/misc/mei/hw-me.h +@@ -23,6 +23,7 @@ + * @dma_size: device DMA buffers size + * @fw_ver_supported: is fw version retrievable from FW + * @hw_trc_supported: does the hw support trc register ++ * @hw_down_supported: can go down + */ + struct mei_cfg { + const struct mei_fw_status fw_status; +@@ -31,6 +32,7 @@ struct mei_cfg { + size_t dma_size[DMA_DSCR_NUM]; + u32 fw_ver_supported:1; + u32 hw_trc_supported:1; ++ u32 hw_down_supported:1; + }; + + +diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c +index b789c4d9c709f..c5ed8b29e4d4e 100644 +--- a/drivers/misc/mei/init.c ++++ b/drivers/misc/mei/init.c +@@ -27,6 +27,7 @@ const char *mei_dev_state_str(int state) + MEI_DEV_STATE(POWERING_DOWN); + MEI_DEV_STATE(POWER_DOWN); + MEI_DEV_STATE(POWER_UP); ++ MEI_DEV_STATE(FW_DOWN); + default: + return "unknown"; + } +@@ -105,7 +106,8 @@ int mei_reset(struct mei_device *dev) + if (state != MEI_DEV_INITIALIZING && + state != MEI_DEV_DISABLED && + state != MEI_DEV_POWER_DOWN && +- state != MEI_DEV_POWER_UP) { ++ state != MEI_DEV_POWER_UP && ++ state != MEI_DEV_FW_DOWN) { + char fw_sts_str[MEI_FW_STATUS_STR_SZ]; + + mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ); +@@ -343,7 +345,8 @@ EXPORT_SYMBOL_GPL(mei_stop); + */ + bool mei_write_is_idle(struct mei_device *dev) + { +- bool idle = (dev->dev_state == MEI_DEV_ENABLED && ++ bool idle = ((dev->dev_state == MEI_DEV_ENABLED || ++ dev->dev_state == MEI_DEV_FW_DOWN) && + list_empty(&dev->ctrl_wr_list) && + list_empty(&dev->write_list) && + list_empty(&dev->write_waiting_list)); +diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c +index 00b0781fc2933..c13679ff721d4 100644 +--- a/drivers/misc/mei/main.c ++++ b/drivers/misc/mei/main.c +@@ -1154,6 +1154,7 @@ void mei_set_devstate(struct mei_device *dev, enum mei_dev_state state) + put_device(clsdev); + } + } ++EXPORT_SYMBOL_GPL(mei_set_devstate); + + static const char *mei_kind_names[] = { + "mei", +diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h +index 1db718d5c93f6..ad41d88fafe16 100644 +--- a/drivers/misc/mei/mei_dev.h ++++ b/drivers/misc/mei/mei_dev.h +@@ -65,7 +65,8 @@ enum mei_dev_state { + MEI_DEV_DISABLED, + MEI_DEV_POWERING_DOWN, + MEI_DEV_POWER_DOWN, +- MEI_DEV_POWER_UP ++ MEI_DEV_POWER_UP, ++ MEI_DEV_FW_DOWN, + }; + + /** +-- +2.43.0 + diff --git a/SPECS/kernel/0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet b/SPECS/kernel/0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet deleted file mode 100644 index f06af8ff7..000000000 --- a/SPECS/kernel/0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet +++ /dev/null @@ -1,59 +0,0 @@ -From 296265e9e4a461e10613e4e4b6df6715f171b1e7 Mon Sep 17 00:00:00 2001 -From: Chao Gao -Date: Fri, 4 Jul 2025 01:49:35 -0700 -Subject: [PATCH 05/24] KVM: x86: Zero XSTATE components on INIT by iterating - over supported features - -Tweak the code a bit to facilitate resetting more xstate components in -the future, e.g., CET's xstate-managed MSRs. - -No functional change intended. - -Suggested-by: Sean Christopherson -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a72f5f986984..a6765d1d1741 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -12398,6 +12398,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) - static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) - { - struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate; -+ u64 xfeatures_mask; -+ int i; - - /* - * Guest FPU state is zero allocated and so doesn't need to be manually -@@ -12411,16 +12413,20 @@ static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) - * are unchanged. Currently, the only components that are zeroed and - * supported by KVM are MPX related. - */ -- if (!kvm_mpx_supported()) -+ xfeatures_mask = (kvm_caps.supported_xcr0 | kvm_caps.supported_xss) & -+ (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); -+ if (!xfeatures_mask) - return; - -+ BUILD_BUG_ON(sizeof(xfeatures_mask) * BITS_PER_BYTE <= XFEATURE_MAX); -+ - /* - * All paths that lead to INIT are required to load the guest's FPU - * state (because most paths are buried in KVM_RUN). - */ - kvm_put_guest_fpu(vcpu); -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDREGS); -- fpstate_clear_xstate_component(fpstate, XFEATURE_BNDCSR); -+ for_each_set_bit(i, (unsigned long *)&xfeatures_mask, XFEATURE_MAX) -+ fpstate_clear_xstate_component(fpstate, i); - kvm_load_guest_fpu(vcpu); - } - --- -2.43.0 - diff --git a/SPECS/kernel/0005-comedi-multiq3-sanitize-config-options-in-multiq3_at.patch b/SPECS/kernel/0005-comedi-multiq3-sanitize-config-options-in-multiq3_at.patch deleted file mode 100644 index de00a0262..000000000 --- a/SPECS/kernel/0005-comedi-multiq3-sanitize-config-options-in-multiq3_at.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 84a7d0659251f918ab696c643b2e3381e72b49a6 Mon Sep 17 00:00:00 2001 -From: Nikita Zhandarovich -Date: Thu, 23 Oct 2025 16:22:04 +0300 -Subject: [PATCH 05/16] comedi: multiq3: sanitize config options in - multiq3_attach() - -Syzbot identified an issue [1] in multiq3_attach() that induces a -task timeout due to open() or COMEDI_DEVCONFIG ioctl operations, -specifically, in the case of multiq3 driver. - -This problem arose when syzkaller managed to craft weird configuration -options used to specify the number of channels in encoder subdevice. -If a particularly great number is passed to s->n_chan in -multiq3_attach() via it->options[2], then multiple calls to -multiq3_encoder_reset() at the end of driver-specific attach() method -will be running for minutes, thus blocking tasks and affected devices -as well. - -While this issue is most likely not too dangerous for real-life -devices, it still makes sense to sanitize configuration inputs. Enable -a sensible limit on the number of encoder chips (4 chips max, each -with 2 channels) to stop this behaviour from manifesting. - -[1] Syzbot crash: -INFO: task syz.2.19:6067 blocked for more than 143 seconds. -... -Call Trace: - - context_switch kernel/sched/core.c:5254 [inline] - __schedule+0x17c4/0x4d60 kernel/sched/core.c:6862 - __schedule_loop kernel/sched/core.c:6944 [inline] - schedule+0x165/0x360 kernel/sched/core.c:6959 - schedule_preempt_disabled+0x13/0x30 kernel/sched/core.c:7016 - __mutex_lock_common kernel/locking/mutex.c:676 [inline] - __mutex_lock+0x7e6/0x1350 kernel/locking/mutex.c:760 - comedi_open+0xc0/0x590 drivers/comedi/comedi_fops.c:2868 - chrdev_open+0x4cc/0x5e0 fs/char_dev.c:414 - do_dentry_open+0x953/0x13f0 fs/open.c:965 - vfs_open+0x3b/0x340 fs/open.c:1097 -... - -Reported-by: syzbot+7811bb68a317954a0347@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=7811bb68a317954a0347 -Fixes: 77e01cdbad51 ("Staging: comedi: add multiq3 driver") -Cc: stable -Signed-off-by: Nikita Zhandarovich -Reviewed-by: Ian Abbott -Link: https://patch.msgid.link/20251023132205.395753-1-n.zhandarovich@fintech.ru -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/drivers/multiq3.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/comedi/drivers/multiq3.c b/drivers/comedi/drivers/multiq3.c -index 07ff5383da99..ac369e9a262d 100644 ---- a/drivers/comedi/drivers/multiq3.c -+++ b/drivers/comedi/drivers/multiq3.c -@@ -67,6 +67,11 @@ - #define MULTIQ3_TRSFRCNTR_OL 0x10 /* xfer CNTR to OL (x and y) */ - #define MULTIQ3_EFLAG_RESET 0x06 /* reset E bit of flag reg */ - -+/* -+ * Limit on the number of optional encoder channels -+ */ -+#define MULTIQ3_MAX_ENC_CHANS 8 -+ - static void multiq3_set_ctrl(struct comedi_device *dev, unsigned int bits) - { - /* -@@ -312,6 +317,10 @@ static int multiq3_attach(struct comedi_device *dev, - s->insn_read = multiq3_encoder_insn_read; - s->insn_config = multiq3_encoder_insn_config; - -+ /* sanity check for number of encoder channels */ -+ if (s->n_chan > MULTIQ3_MAX_ENC_CHANS) -+ s->n_chan = MULTIQ3_MAX_ENC_CHANS; -+ - for (i = 0; i < s->n_chan; i++) - multiq3_encoder_reset(dev, i); - --- -2.43.0 - diff --git a/SPECS/kernel/0005-drm-i915-Drop-the-irqs_disabled-check.rt b/SPECS/kernel/0005-drm-i915-Drop-the-irqs_disabled-check.rt new file mode 100644 index 000000000..491bdd1db --- /dev/null +++ b/SPECS/kernel/0005-drm-i915-Drop-the-irqs_disabled-check.rt @@ -0,0 +1,45 @@ +From 19ac9d48ac2ac510b2c8cea7c5bc70e88ee9a261 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Fri, 1 Oct 2021 20:01:03 +0200 +Subject: [PATCH 5/9] drm/i915: Drop the irqs_disabled() check + +The !irqs_disabled() check triggers on PREEMPT_RT even with +i915_sched_engine::lock acquired. The reason is the lock is transformed +into a sleeping lock on PREEMPT_RT and does not disable interrupts. + +There is no need to check for disabled interrupts. The lockdep +annotation below already check if the lock has been acquired by the +caller and will yell if the interrupts are not disabled. + +Remove the !irqs_disabled() check. + +Reported-by: Maarten Lankhorst +Acked-by: Tvrtko Ursulin +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/i915_request.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c +index b9a2b2194c8ff..e24798e4b44ea 100644 +--- a/drivers/gpu/drm/i915/i915_request.c ++++ b/drivers/gpu/drm/i915/i915_request.c +@@ -608,7 +608,6 @@ bool __i915_request_submit(struct i915_request *request) + + RQ_TRACE(request, "\n"); + +- GEM_BUG_ON(!irqs_disabled()); + lockdep_assert_held(&engine->sched_engine->lock); + + /* +@@ -717,7 +716,6 @@ void __i915_request_unsubmit(struct i915_request *request) + */ + RQ_TRACE(request, "\n"); + +- GEM_BUG_ON(!irqs_disabled()); + lockdep_assert_held(&engine->sched_engine->lock); + + /* +-- +2.43.0 + diff --git a/SPECS/kernel/0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt b/SPECS/kernel/0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt deleted file mode 100644 index 0235f1712..000000000 --- a/SPECS/kernel/0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt +++ /dev/null @@ -1,94 +0,0 @@ -From 1d150a832087e70e0612d41bb918bfae6ff74955 Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Wed, 8 Sep 2021 19:03:41 +0200 -Subject: [PATCH 5/9] drm/i915/gt: Use spin_lock_irq() instead of - local_irq_disable() + spin_lock() - -execlists_dequeue() is invoked from a function which uses -local_irq_disable() to disable interrupts so the spin_lock() behaves -like spin_lock_irq(). -This breaks PREEMPT_RT because local_irq_disable() + spin_lock() is not -the same as spin_lock_irq(). - -execlists_dequeue_irq() and execlists_dequeue() has each one caller -only. If intel_engine_cs::active::lock is acquired and released with the -_irq suffix then it behaves almost as if execlists_dequeue() would be -invoked with disabled interrupts. The difference is the last part of the -function which is then invoked with enabled interrupts. -I can't tell if this makes a difference. From looking at it, it might -work to move the last unlock at the end of the function as I didn't find -anything that would acquire the lock again. - -Reported-by: Clark Williams -Signed-off-by: Sebastian Andrzej Siewior -Reviewed-by: Maarten Lankhorst ---- - .../drm/i915/gt/intel_execlists_submission.c | 17 +++++------------ - 1 file changed, 5 insertions(+), 12 deletions(-) - -diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c -index 03baa7fa0a27..799e943d9fac 100644 ---- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c -+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c -@@ -1294,7 +1294,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - * and context switches) submission. - */ - -- spin_lock(&sched_engine->lock); -+ spin_lock_irq(&sched_engine->lock); - - /* - * If the queue is higher priority than the last -@@ -1394,7 +1394,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - * Even if ELSP[1] is occupied and not worthy - * of timeslices, our queue might be. - */ -- spin_unlock(&sched_engine->lock); -+ spin_unlock_irq(&sched_engine->lock); - return; - } - } -@@ -1420,7 +1420,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - - if (last && !can_merge_rq(last, rq)) { - spin_unlock(&ve->base.sched_engine->lock); -- spin_unlock(&engine->sched_engine->lock); -+ spin_unlock_irq(&engine->sched_engine->lock); - return; /* leave this for another sibling */ - } - -@@ -1582,7 +1582,7 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - */ - sched_engine->queue_priority_hint = queue_prio(sched_engine); - i915_sched_engine_reset_on_empty(sched_engine); -- spin_unlock(&sched_engine->lock); -+ spin_unlock_irq(&sched_engine->lock); - - /* - * We can skip poking the HW if we ended up with exactly the same set -@@ -1608,13 +1608,6 @@ static void execlists_dequeue(struct intel_engine_cs *engine) - } - } - --static void execlists_dequeue_irq(struct intel_engine_cs *engine) --{ -- local_irq_disable(); /* Suspend interrupts across request submission */ -- execlists_dequeue(engine); -- local_irq_enable(); /* flush irq_work (e.g. breadcrumb enabling) */ --} -- - static void clear_ports(struct i915_request **ports, int count) - { - memset_p((void **)ports, NULL, count); -@@ -2469,7 +2462,7 @@ static void execlists_submission_tasklet(struct tasklet_struct *t) - } - - if (!engine->execlists.pending[0]) { -- execlists_dequeue_irq(engine); -+ execlists_dequeue(engine); - start_timeslice(engine); - } - --- -2.43.0 - diff --git a/SPECS/kernel/0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c b/SPECS/kernel/0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c deleted file mode 100644 index 769c09cb7..000000000 --- a/SPECS/kernel/0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c +++ /dev/null @@ -1,48 +0,0 @@ -From 5813378b99d2bf90c5dc4502ccdaae64df478f12 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 22 Aug 2025 13:56:30 +0300 -Subject: [PATCH 05/11] i3c: mipi-i3c-hci: Use own DMA bounce buffer management - for I2C transfers - -Stop using I2C DMA-safe API for two reasons: -- Not needed if driver is using PIO mode. -- DMA transfers needs a DWORD align sized receive bounce buffer when the - device DMA is IOMMU mapped, which is causing needless double bounce - buffering in that case. - -Cc: Billy Tsai -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250822105630.2820009-5-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 6 +----- - 1 file changed, 1 insertion(+), 5 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index b2977b6ac9f7..7a467ef65787 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -348,7 +348,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - return -ENOMEM; - - for (i = 0; i < nxfers; i++) { -- xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1); -+ xfer[i].data = i2c_xfers[i].buf; - xfer[i].data_len = i2c_xfers[i].len; - xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD; - hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]); -@@ -374,10 +374,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - } - - out: -- for (i = 0; i < nxfers; i++) -- i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i], -- ret ? false : true); -- - hci_free_xfer(xfer, nxfers); - return ret; - } --- -2.43.0 - diff --git a/SPECS/kernel/0005-ipu-bridge-add-CPHY-support.ipu b/SPECS/kernel/0005-ipu-bridge-add-CPHY-support.ipu deleted file mode 100644 index 5395dfc32..000000000 --- a/SPECS/kernel/0005-ipu-bridge-add-CPHY-support.ipu +++ /dev/null @@ -1,110 +0,0 @@ -From 9c7744f1489c6c1f4be1ac2b59ad08b059d633aa Mon Sep 17 00:00:00 2001 -From: Chen Meng J -Date: Wed, 20 Nov 2024 17:56:07 +0800 -Subject: [PATCH 05/11] ipu-bridge: add CPHY support - -get DPHY or CPHY mode when parse ssdb - -Signed-off-by: Chen Meng J -Signed-off-by: zouxiaoh ---- - drivers/media/pci/intel/ipu-bridge.c | 23 ++++++++++++++++++++++- - include/media/ipu-bridge.h | 7 +++++-- - 2 files changed, 27 insertions(+), 3 deletions(-) - -diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c -index e58b354ad9e6..3c947d1a487c 100644 ---- a/drivers/media/pci/intel/ipu-bridge.c -+++ b/drivers/media/pci/intel/ipu-bridge.c -@@ -35,6 +35,9 @@ - */ - #define IVSC_DEV_NAME "intel_vsc" - -+#define PHY_MODE_DPHY 0 -+#define PHY_MODE_CPHY 1 -+ - /* - * Extend this array with ACPI Hardware IDs of devices known to be working - * plus the number of link-frequencies expected by their drivers, along with -@@ -314,6 +317,7 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor) - - sensor->link = ssdb.link; - sensor->lanes = ssdb.lanes; -+ sensor->phyconfig = ssdb.phyconfig; - sensor->mclkspeed = ssdb.mclkspeed; - sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb); - sensor->orientation = ipu_bridge_parse_orientation(adev); -@@ -332,6 +336,7 @@ static void ipu_bridge_create_fwnode_properties( - { - struct ipu_property_names *names = &sensor->prop_names; - struct software_node *nodes = sensor->swnodes; -+ u8 bus_type; - - sensor->prop_names = prop_names; - -@@ -389,9 +394,16 @@ static void ipu_bridge_create_fwnode_properties( - PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref); - } - -+ if (sensor->phyconfig == PHY_MODE_DPHY) -+ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_DPHY; -+ else if (sensor->phyconfig == PHY_MODE_CPHY) -+ bus_type = V4L2_FWNODE_BUS_TYPE_CSI2_CPHY; -+ else -+ bus_type = V4L2_FWNODE_BUS_TYPE_GUESS; -+ - sensor->ep_properties[0] = PROPERTY_ENTRY_U32( - sensor->prop_names.bus_type, -- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY); -+ bus_type); - sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN( - sensor->prop_names.data_lanes, - bridge->data_lanes, sensor->lanes); -@@ -411,6 +423,15 @@ static void ipu_bridge_create_fwnode_properties( - sensor->ipu_properties[1] = PROPERTY_ENTRY_REF_ARRAY( - sensor->prop_names.remote_endpoint, - sensor->remote_ref); -+ -+ /* -+ * TODO: Remove the bus_type property for IPU -+ * 1. keep fwnode property list no change. -+ * 2. IPU driver needs to get bus_type from remote sensor ep. -+ */ -+ sensor->ipu_properties[2] = PROPERTY_ENTRY_U32 -+ (sensor->prop_names.bus_type, -+ bus_type); - } - - static void ipu_bridge_init_swnode_names(struct ipu_sensor *sensor) -diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h -index 16fac765456e..f8642d09968d 100644 ---- a/include/media/ipu-bridge.h -+++ b/include/media/ipu-bridge.h -@@ -91,7 +91,9 @@ struct ipu_sensor_ssdb { - u8 controllogicid; - u8 reserved1[3]; - u8 mclkport; -- u8 reserved2[13]; -+ u8 reserved2[5]; -+ u8 phyconfig; -+ u8 reserved3[7]; - } __packed; - - struct ipu_property_names { -@@ -139,11 +141,12 @@ struct ipu_sensor { - u32 rotation; - enum v4l2_fwnode_orientation orientation; - const char *vcm_type; -+ u8 phyconfig; - - struct ipu_property_names prop_names; - struct property_entry ep_properties[5]; - struct property_entry dev_properties[5]; -- struct property_entry ipu_properties[3]; -+ struct property_entry ipu_properties[4]; - struct property_entry ivsc_properties[1]; - struct property_entry ivsc_sensor_ep_properties[4]; - struct property_entry ivsc_ipu_ep_properties[4]; --- -2.43.0 - diff --git a/SPECS/kernel/0005-issei-update-MAINTAINERS-file.security b/SPECS/kernel/0005-issei-update-MAINTAINERS-file.security index e41c30d60..e58b2a59d 100644 --- a/SPECS/kernel/0005-issei-update-MAINTAINERS-file.security +++ b/SPECS/kernel/0005-issei-update-MAINTAINERS-file.security @@ -1,7 +1,7 @@ -From ed686509ba6d9731d3c7824f1745f05a6c8fd372 Mon Sep 17 00:00:00 2001 +From 61f2cb1a90329ae3ca5ee90a8f8695663bf6fed2 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Wed, 14 May 2025 11:31:24 +0300 -Subject: [PATCH 5/5] issei: update MAINTAINERS file +Subject: [PATCH 5/7] issei: update MAINTAINERS file Add ISSEI entry to the MAINTAINERS file. @@ -11,10 +11,10 @@ Signed-off-by: Alexander Usyskin 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS -index 97d958c945e4..37c00034d0a0 100644 +index e8f06145fb54c..0a36f929d6f4b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -12624,6 +12624,15 @@ F: drivers/platform/x86/intel/sdsi.c +@@ -12848,6 +12848,15 @@ F: drivers/platform/x86/intel/sdsi.c F: tools/arch/x86/intel_sdsi/ F: tools/testing/selftests/drivers/sdsi/ diff --git a/SPECS/kernel/0005-media-mc-Add-INTERNAL-pad-flag.ipu b/SPECS/kernel/0005-media-mc-Add-INTERNAL-pad-flag.ipu deleted file mode 100644 index ab1b6ce39..000000000 --- a/SPECS/kernel/0005-media-mc-Add-INTERNAL-pad-flag.ipu +++ /dev/null @@ -1,107 +0,0 @@ -From 8ca39c83ce030a6bf6826b230b91408d5a74c9d1 Mon Sep 17 00:00:00 2001 -From: Sakari Ailus -Date: Fri, 14 Nov 2025 16:51:41 +0200 -Subject: [PATCH 5/6] media: mc: Add INTERNAL pad flag - -Internal sink pads will be used as routing endpoints in V4L2 [GS]_ROUTING -IOCTLs, to indicate that the stream begins in the entity. Internal sink -pads are pads that have both SINK and INTERNAL flags set. - -Also prevent creating links to pads that have been flagged as internal and -initialising SOURCE pads with INTERNAL flag set. - -Signed-off-by: Sakari Ailus -Reviewed-by: Tomi Valkeinen -Reviewed-by: Laurent Pinchart -Reviewed-by: Mirela Rabulea ---- - .../userspace-api/media/mediactl/media-types.rst | 9 +++++++++ - drivers/media/mc/mc-entity.c | 15 ++++++++++++--- - include/uapi/linux/media.h | 1 + - 3 files changed, 22 insertions(+), 3 deletions(-) - -diff --git a/Documentation/userspace-api/media/mediactl/media-types.rst b/Documentation/userspace-api/media/mediactl/media-types.rst -index 6332e8395263..200c37a1da26 100644 ---- a/Documentation/userspace-api/media/mediactl/media-types.rst -+++ b/Documentation/userspace-api/media/mediactl/media-types.rst -@@ -361,6 +361,7 @@ Types and flags used to represent the media graph elements - .. _MEDIA-PAD-FL-SINK: - .. _MEDIA-PAD-FL-SOURCE: - .. _MEDIA-PAD-FL-MUST-CONNECT: -+.. _MEDIA-PAD-FL-INTERNAL: - - .. flat-table:: Media pad flags - :header-rows: 0 -@@ -381,6 +382,14 @@ Types and flags used to represent the media graph elements - enabled links even when this flag isn't set; the absence of the flag - doesn't imply there is none. - -+ * - ``MEDIA_PAD_FL_INTERNAL`` -+ - The internal flag indicates an internal pad that has no external -+ connections. As they are internal to entities, internal pads shall not -+ be connected with links. -+ -+ The internal flag may currently be present only in a sink pad where it -+ indicates that the :ref:``stream `` originates -+ from within the entity. - - One and only one of ``MEDIA_PAD_FL_SINK`` and ``MEDIA_PAD_FL_SOURCE`` - must be set for every pad. -diff --git a/drivers/media/mc/mc-entity.c b/drivers/media/mc/mc-entity.c -index 307920c8b354..928613d60e8f 100644 ---- a/drivers/media/mc/mc-entity.c -+++ b/drivers/media/mc/mc-entity.c -@@ -209,11 +209,16 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads, - mutex_lock(&mdev->graph_mutex); - - media_entity_for_each_pad(entity, iter) { -+ const u32 pad_flags = iter->flags & (MEDIA_PAD_FL_SINK | -+ MEDIA_PAD_FL_SOURCE | -+ MEDIA_PAD_FL_INTERNAL); -+ - iter->entity = entity; - iter->index = i++; - -- if (hweight32(iter->flags & (MEDIA_PAD_FL_SINK | -- MEDIA_PAD_FL_SOURCE)) != 1) { -+ if (pad_flags != MEDIA_PAD_FL_SINK && -+ pad_flags != (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_INTERNAL) && -+ pad_flags != MEDIA_PAD_FL_SOURCE) { - ret = -EINVAL; - break; - } -@@ -1118,7 +1123,8 @@ int media_get_pad_index(struct media_entity *entity, u32 pad_type, - - for (i = 0; i < entity->num_pads; i++) { - if ((entity->pads[i].flags & -- (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE)) != pad_type) -+ (MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_SOURCE | -+ MEDIA_PAD_FL_INTERNAL)) != pad_type) - continue; - - if (entity->pads[i].sig_type == sig_type) -@@ -1148,6 +1154,9 @@ media_create_pad_link(struct media_entity *source, u16 source_pad, - return -EINVAL; - if (WARN_ON(!(sink->pads[sink_pad].flags & MEDIA_PAD_FL_SINK))) - return -EINVAL; -+ if (WARN_ON(source->pads[source_pad].flags & MEDIA_PAD_FL_INTERNAL) || -+ WARN_ON(sink->pads[sink_pad].flags & MEDIA_PAD_FL_INTERNAL)) -+ return -EINVAL; - - link = media_add_link(&source->links); - if (link == NULL) -diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h -index 1c80b1d6bbaf..80cfd12a43fc 100644 ---- a/include/uapi/linux/media.h -+++ b/include/uapi/linux/media.h -@@ -208,6 +208,7 @@ struct media_entity_desc { - #define MEDIA_PAD_FL_SINK (1U << 0) - #define MEDIA_PAD_FL_SOURCE (1U << 1) - #define MEDIA_PAD_FL_MUST_CONNECT (1U << 2) -+#define MEDIA_PAD_FL_INTERNAL (1U << 3) - - struct media_pad_desc { - __u32 entity; /* entity ID */ --- -2.43.0 - diff --git a/SPECS/kernel/0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet b/SPECS/kernel/0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet new file mode 100644 index 000000000..8586713f4 --- /dev/null +++ b/SPECS/kernel/0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet @@ -0,0 +1,54 @@ +From 222770f6becdaf5dac70947d371aa235313f563d Mon Sep 17 00:00:00 2001 +From: Gan Yi Fang +Date: Mon, 21 Nov 2022 01:28:57 -0500 +Subject: [PATCH 05/18] net: phy: dp83867: perform restart AN after modifying + AN setting + +When changing link speed, the MAC side is not ready when the PHY +side is up. This is causing the configuration is not updated at +the MAC side. By restarting the auto negotiation, MAC side is able +to update the in-band message. + +Fixes: 50ca4e7f91ff ("net: phy: dp83867: retrigger SGMII AN when link change") +Signed-off-by: Gan Yi Fang +--- + drivers/net/phy/Kconfig | 1 + + drivers/net/phy/dp83867.c | 3 +++ + 2 files changed, 4 insertions(+) + +diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig +index 98700d069191a..075bc9a5fe9dc 100644 +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -428,6 +428,7 @@ config DP83848_PHY + + config DP83867_PHY + tristate "Texas Instruments DP83867 Gigabit PHY" ++ depends on PHYLINK + help + Currently supports the DP83867 PHY. + +diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c +index 36a0c1b7f59c7..324e73b4eda45 100644 +--- a/drivers/net/phy/dp83867.c ++++ b/drivers/net/phy/dp83867.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include + +@@ -947,6 +948,8 @@ static void dp83867_link_change_notify(struct phy_device *phydev) + phy_set_bits(phydev, DP83867_CFG2, + DP83867_SGMII_AUTONEG_EN); + } ++ if (phydev->state == PHY_NOLINK) ++ phylink_mii_c22_pcs_an_restart(&phydev->mdio); + } + + static int dp83867_loopback(struct phy_device *phydev, bool enable, int speed) +-- +2.43.0 + diff --git a/SPECS/kernel/0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu b/SPECS/kernel/0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu deleted file mode 100644 index afd14a3ac..000000000 --- a/SPECS/kernel/0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu +++ /dev/null @@ -1,404 +0,0 @@ -From abb1539a9573373c7d63922be8fcb4c34dd45a34 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:16:13 +0800 -Subject: [PATCH 05/21] patch: staging add enable CONFIG_INTEL_IPU_ACPI - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 231 ++++++++++++++++++++++++- - drivers/staging/media/ipu7/ipu7-isys.h | 15 ++ - drivers/staging/media/ipu7/ipu7.c | 10 ++ - drivers/staging/media/ipu7/ipu7.h | 3 + - 4 files changed, 257 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index 44ea4eef1976..547d5fd6a036 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -96,6 +96,126 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, - return ret; - } - -+struct isys_i2c_test { -+ u8 bus_nr; -+ u16 addr; -+ struct i2c_client *client; -+}; -+ -+static int isys_i2c_test(struct device *dev, void *priv) -+{ -+ struct i2c_client *client = i2c_verify_client(dev); -+ struct isys_i2c_test *test = priv; -+ -+ if (!client) -+ return 0; -+ -+ if (i2c_adapter_id(client->adapter) != test->bus_nr || -+ client->addr != test->addr) -+ return 0; -+ -+ test->client = client; -+ -+ return 0; -+} -+ -+static -+struct i2c_client *isys_find_i2c_subdev(struct i2c_adapter *adapter, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct i2c_board_info *info = &sd_info->i2c.board_info; -+ struct isys_i2c_test test = { -+ .bus_nr = i2c_adapter_id(adapter), -+ .addr = info->addr, -+ }; -+ int ret; -+ -+ ret = i2c_for_each_dev(&test, isys_i2c_test); -+ if (ret || !test.client) -+ return NULL; -+ return test.client; -+} -+ -+static int isys_register_ext_subdev(struct ipu7_isys *isys, -+ struct ipu7_isys_subdev_info *sd_info) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct i2c_adapter *adapter; -+ struct v4l2_subdev *sd; -+ struct i2c_client *client; -+ int ret; -+ int bus; -+ -+ bus = sd_info->i2c.i2c_adapter_id; -+ adapter = i2c_get_adapter(bus); -+ if (!adapter) { -+ dev_warn(dev, "can't find adapter\n"); -+ return -ENOENT; -+ } -+ -+ dev_info(dev, "creating i2c subdev for %s (address %2.2x, bus %d)\n", -+ sd_info->i2c.board_info.type, sd_info->i2c.board_info.addr, -+ bus); -+ -+ if (sd_info->csi2) { -+ dev_info(dev, "sensor device on CSI port: %d\n", -+ sd_info->csi2->port); -+ if (sd_info->csi2->port >= isys->pdata->ipdata->csi2.nports || -+ !isys->csi2[sd_info->csi2->port].isys) { -+ dev_warn(dev, "invalid csi2 port %u\n", -+ sd_info->csi2->port); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ } else { -+ dev_info(dev, "No camera subdevice\n"); -+ } -+ -+ client = isys_find_i2c_subdev(adapter, sd_info); -+ if (client) { -+ dev_warn(dev, "Device exists\n"); -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ /* TODO: remove i2c_unregister_device() */ -+ i2c_unregister_device(client); -+#else -+ ret = 0; -+ goto skip_put_adapter; -+#endif -+ } -+ -+ sd = v4l2_i2c_new_subdev_board(&isys->v4l2_dev, adapter, -+ &sd_info->i2c.board_info, NULL); -+ if (!sd) { -+ dev_warn(dev, "can't create new i2c subdev\n"); -+ ret = -EINVAL; -+ goto skip_put_adapter; -+ } -+ -+ if (!sd_info->csi2) -+ return 0; -+ -+ return isys_complete_ext_device_registration(isys, sd, sd_info->csi2); -+ -+skip_put_adapter: -+ i2c_put_adapter(adapter); -+ -+ return ret; -+} -+ -+static void isys_register_ext_subdevs(struct ipu7_isys *isys) -+{ -+ struct ipu7_isys_subdev_pdata *spdata = isys->pdata->spdata; -+ struct ipu7_isys_subdev_info **sd_info; -+ -+ if (!spdata) { -+ dev_info(&isys->adev->auxdev.dev, -+ "no subdevice info provided\n"); -+ return; -+ } -+ for (sd_info = spdata->subdevs; *sd_info; sd_info++) -+ isys_register_ext_subdev(isys, *sd_info); -+} -+ - static void isys_stream_init(struct ipu7_isys *isys) - { - unsigned int i; -@@ -142,6 +262,7 @@ static int isys_fw_log_init(struct ipu7_isys *isys) - return 0; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) - /* The .bound() notifier callback when a match is found */ - static int isys_notifier_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *sd, -@@ -260,6 +381,7 @@ static void isys_notifier_cleanup(struct ipu7_isys *isys) - v4l2_async_nf_unregister(&isys->notifier); - v4l2_async_nf_cleanup(&isys->notifier); - } -+#endif - - static void isys_unregister_video_devices(struct ipu7_isys *isys) - { -@@ -374,6 +496,73 @@ static int isys_csi2_create_media_links(struct ipu7_isys *isys) - return 0; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+static int isys_register_devices(struct ipu7_isys *isys) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct pci_dev *pdev = isys->adev->isp->pdev; -+ int ret; -+ -+ media_device_pci_init(&isys->media_dev, -+ pdev, IPU_MEDIA_DEV_MODEL_NAME); -+ -+ strscpy(isys->v4l2_dev.name, isys->media_dev.model, -+ sizeof(isys->v4l2_dev.name)); -+ -+ ret = media_device_register(&isys->media_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ isys->v4l2_dev.mdev = &isys->media_dev; -+ isys->v4l2_dev.ctrl_handler = NULL; -+ -+ ret = v4l2_device_register(dev, &isys->v4l2_dev); -+ if (ret < 0) -+ goto out_media_device_unregister; -+ -+ ret = isys_register_video_devices(isys); -+ if (ret) -+ goto out_v4l2_device_unregister; -+ -+ ret = isys_csi2_register_subdevices(isys); -+ if (ret) -+ goto out_video_unregister_device; -+ -+ ret = isys_csi2_create_media_links(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ -+ if (!isys->pdata->spdata) { -+ ret = isys_notifier_init(isys); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } else { -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); -+ if (ret) -+ goto out_csi2_unregister_subdevices; -+ } -+ -+ return 0; -+ -+out_csi2_unregister_subdevices: -+ isys_csi2_unregister_subdevices(isys); -+ -+out_video_unregister_device: -+ isys_unregister_video_devices(isys); -+ -+out_v4l2_device_unregister: -+ v4l2_device_unregister(&isys->v4l2_dev); -+ -+out_media_device_unregister: -+ media_device_unregister(&isys->media_dev); -+ media_device_cleanup(&isys->media_dev); -+ -+ dev_err(dev, "failed to register isys devices\n"); -+ -+ return ret; -+} -+#else - static int isys_register_devices(struct ipu7_isys *isys) - { - struct device *dev = &isys->adev->auxdev.dev; -@@ -409,7 +598,8 @@ static int isys_register_devices(struct ipu7_isys *isys) - if (ret) - goto out_csi2_unregister_subdevices; - -- ret = isys_notifier_init(isys); -+ isys_register_ext_subdevs(isys); -+ ret = v4l2_device_register_subdev_nodes(&isys->v4l2_dev); - if (ret) - goto out_csi2_unregister_subdevices; - -@@ -432,6 +622,7 @@ static int isys_register_devices(struct ipu7_isys *isys) - - return ret; - } -+#endif - - static void isys_unregister_devices(struct ipu7_isys *isys) - { -@@ -579,6 +770,7 @@ static const struct dev_pm_ops isys_pm_ops = { - .resume = isys_resume, - }; - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) - static void isys_remove(struct auxiliary_device *auxdev) - { - struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -@@ -600,7 +792,9 @@ static void isys_remove(struct auxiliary_device *auxdev) - ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), - fwmsg, fwmsg->dma_addr, 0); - -- isys_notifier_cleanup(isys); -+ if (!isys->pdata->spdata) -+ isys_notifier_cleanup(isys); -+ - isys_unregister_devices(isys); - - cpu_latency_qos_remove_request(&isys->pm_qos); -@@ -611,6 +805,39 @@ static void isys_remove(struct auxiliary_device *auxdev) - mutex_destroy(&isys->reset_mutex); - #endif - } -+#else -+static void isys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev); -+ struct isys_fw_msgs *fwmsg, *safe; -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ -+#ifdef CONFIG_DEBUG_FS -+ if (adev->isp->ipu7_dir) -+ debugfs_remove_recursive(isys->debugfsdir); -+#endif -+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++) -+ mutex_destroy(&isys->streams[i].mutex); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head) -+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs), -+ fwmsg, fwmsg->dma_addr, 0); -+ -+ isys_unregister_devices(isys); -+ -+ cpu_latency_qos_remove_request(&isys->pm_qos); -+ -+ mutex_destroy(&isys->stream_mutex); -+ mutex_destroy(&isys->mutex); -+#ifdef CONFIG_VIDEO_INTEL_IPU7_ISYS_RESET -+ mutex_destroy(&isys->reset_mutex); -+#endif -+} -+#endif - - #ifdef CONFIG_DEBUG_FS - static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h -index cd7375417452..2e45258bb6ab 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-isys.h -@@ -150,6 +150,21 @@ struct ipu7_isys_csi2_config { - enum v4l2_mbus_type bus_type; - }; - -+struct ipu7_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu7_isys_subdev_info { -+ struct ipu7_isys_csi2_config *csi2; -+ struct ipu7_isys_subdev_i2c_info i2c; -+}; -+ -+struct ipu7_isys_subdev_pdata { -+ struct ipu7_isys_subdev_info **subdevs; -+}; -+ - struct sensor_async_sd { - struct v4l2_async_connection asc; - struct ipu7_isys_csi2_config csi2; -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 5649d8e1c352..06e5f08bed4d 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -40,6 +40,10 @@ - #include "ipu7-mmu.h" - #include "ipu7-platform-regs.h" - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+#include -+ -+#endif - #define IPU_PCI_BAR 0 - #define IPU_PCI_PBBAR 4 - -@@ -2130,6 +2134,7 @@ static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode) - static struct ipu7_bus_device * - ipu7_isys_init(struct pci_dev *pdev, struct device *parent, - const struct ipu_buttress_ctrl *ctrl, void __iomem *base, -+ struct ipu7_isys_subdev_pdata *spdata, - const struct ipu_isys_internal_pdata *ipdata, - unsigned int nr) - { -@@ -2160,6 +2165,7 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent, - - pdata->base = base; - pdata->ipdata = ipdata; -+ pdata->spdata = spdata; - - isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl, - IPU_ISYS_NAME); -@@ -2582,7 +2588,11 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) - goto out_ipu_bus_del_devices; - } - -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ ipu_get_acpi_devices_new(&dev->platform_data); -+#endif - isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base, -+ dev->platform_data, - isys_ipdata, 0); - if (IS_ERR(isp->isys)) { - ret = PTR_ERR(isp->isys); -diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h -index 3b3ea5fb613f..21988ce41acf 100644 ---- a/drivers/staging/media/ipu7/ipu7.h -+++ b/drivers/staging/media/ipu7/ipu7.h -@@ -144,6 +144,8 @@ struct ipu7_device { - /* Currently chosen arbitration mechanism for VC1 */ - #define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB - -+struct ipu7_isys_subdev_pdata; -+ - /* One L2 entry maps 1024 L1 entries and one L1 entry per page */ - #define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE) - /* Max L2 blocks per stream */ -@@ -226,6 +228,7 @@ struct ipu_isys_internal_pdata { - struct ipu7_isys_pdata { - void __iomem *base; - const struct ipu_isys_internal_pdata *ipdata; -+ struct ipu7_isys_subdev_pdata *spdata; - }; - - struct ipu_psys_internal_pdata { --- -2.43.0 - diff --git a/SPECS/kernel/0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf b/SPECS/kernel/0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf deleted file mode 100644 index 8631728c5..000000000 --- a/SPECS/kernel/0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf +++ /dev/null @@ -1,129 +0,0 @@ -From c945e3b2bac5e06b93d5b331fd99a20a0e22a595 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 11 Aug 2025 17:00:31 +0800 -Subject: [PATCH 005/100] perf/x86: Check if cpuc->events[*] pointer exists - before accessing it - -The PMI handler could disable some events as the interrupt throttling -and clear the corresponding items in cpuc->events[] array. - -perf_event_overflow() - -> __perf_event_overflow() - ->__perf_event_account_interrupt() - -> perf_event_throttle_group() - -> perf_event_throttle() - -> event->pmu->stop() - -> x86_pmu_stop() - -Moreover PMI is NMI on x86 platform and it could interrupt other perf -code like setup_pebs_adaptive_sample_data(). So once PMI handling -finishes and returns into setup_pebs_adaptive_sample_data() and it could -find the cpuc->events[*] becomes NULL and accessing this NULL pointer -triggers an invalid memory access and leads to kernel crashes eventually. - -Thus add NULL check before accessing cpuc->events[*] pointer. - -Reported-by: kernel test robot -Closes: https://lore.kernel.org/oe-lkp/202507042103.a15d2923-lkp@intel.com -Fixes: 9734e25fbf5a ("perf: Fix the throttle logic for a group") -Signed-off-by: Dapeng Mi -Tested-by: kernel test robot ---- - arch/x86/events/core.c | 3 +++ - arch/x86/events/intel/core.c | 6 +++++- - arch/x86/events/intel/ds.c | 13 ++++++------- - 3 files changed, 14 insertions(+), 8 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 7610f26dfbd9..f0a3bc57157d 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1711,6 +1711,9 @@ int x86_pmu_handle_irq(struct pt_regs *regs) - continue; - - event = cpuc->events[idx]; -+ if (!event) -+ continue; -+ - last_period = event->hw.last_period; - - val = static_call(x86_pmu_update)(event); -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index b08896e06f72..602a7c33f711 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -2718,6 +2718,8 @@ static void update_saved_topdown_regs(struct perf_event *event, u64 slots, - if (!is_topdown_idx(idx)) - continue; - other = cpuc->events[idx]; -+ if (!other) -+ continue; - other->hw.saved_slots = slots; - other->hw.saved_metric = metrics; - } -@@ -2761,6 +2763,8 @@ static u64 intel_update_topdown_event(struct perf_event *event, int metric_end, - if (!is_topdown_idx(idx)) - continue; - other = cpuc->events[idx]; -+ if (!other) -+ continue; - __icl_update_topdown_event(other, slots, metrics, - event ? event->hw.saved_slots : 0, - event ? event->hw.saved_metric : 0); -@@ -3138,7 +3142,7 @@ static void x86_pmu_handle_guest_pebs(struct pt_regs *regs, - - for_each_set_bit(bit, (unsigned long *)&guest_pebs_idxs, X86_PMC_IDX_MAX) { - event = cpuc->events[bit]; -- if (!event->attr.precise_ip) -+ if (!event || !event->attr.precise_ip) - continue; - - perf_sample_data_init(data, 0, event->hw.last_period); -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index c0b7ac1c7594..b23c49e2e06f 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2480,6 +2480,8 @@ static void intel_pmu_pebs_event_update_no_drain(struct cpu_hw_events *cpuc, u64 - */ - for_each_set_bit(bit, (unsigned long *)&pebs_enabled, X86_PMC_IDX_MAX) { - event = cpuc->events[bit]; -+ if (!event) -+ continue; - if (event->hw.flags & PERF_X86_EVENT_AUTO_RELOAD) - intel_pmu_save_and_restart_reload(event, 0); - } -@@ -2579,10 +2581,7 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d - continue; - - event = cpuc->events[bit]; -- if (WARN_ON_ONCE(!event)) -- continue; -- -- if (WARN_ON_ONCE(!event->attr.precise_ip)) -+ if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) - continue; - - /* log dropped samples number */ -@@ -2645,9 +2644,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - pebs_status = basic->applicable_counters & cpuc->pebs_enabled & mask; - for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) { - event = cpuc->events[bit]; -- -- if (WARN_ON_ONCE(!event) || -- WARN_ON_ONCE(!event->attr.precise_ip)) -+ if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) - continue; - - if (counts[bit]++) { -@@ -2663,6 +2660,8 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - continue; - - event = cpuc->events[bit]; -+ if (!event) -+ continue; - - __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit], - counts[bit], setup_pebs_adaptive_sample_data); --- -2.43.0 - diff --git a/SPECS/kernel/0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf b/SPECS/kernel/0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf new file mode 100644 index 000000000..b01d55fd2 --- /dev/null +++ b/SPECS/kernel/0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf @@ -0,0 +1,349 @@ +From 11a27e519981f2409c3efc10f085f0b2dad547dc Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Tue, 12 Aug 2025 12:12:08 -0700 +Subject: [PATCH 05/13] perf/x86/intel/uncore: Add IMH PMON support for Diamond + Rapids + +DMR supports IMH PMON units for PCU, UBox, iMC, and CXL: +- PCU and UBox are same with SPR. +- iMC is similar to SPR but uses different offsets for fixed registers. +- CXL introduces a new port_enable field and changes the position of + the threshold field. + +DMR also introduces additional PMON units: SCA, HAMVF, D2D_ULA, UBR, +PCIE4, CRS, CPC, ITC, OTC, CMS, and PCIE6. Among these, PCIE4 and +PCIE6 use different unit types, but share the same config register +layout, and the generic PCIe PMON events apply to both. + +Additionally, ignore the broken MSE unit. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 9 + + arch/x86/events/intel/uncore.h | 3 + + arch/x86/events/intel/uncore_discovery.h | 2 + + arch/x86/events/intel/uncore_snbep.c | 229 +++++++++++++++++++++++ + 4 files changed, 243 insertions(+) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index 5135a39d81f6b..c07d2c88604ef 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1831,6 +1831,14 @@ static const struct uncore_plat_init gnr_uncore_init __initconst = { + .domain[0].units_ignore = gnr_uncore_units_ignore, + }; + ++static const struct uncore_plat_init dmr_uncore_init __initconst = { ++ .pci_init = dmr_uncore_pci_init, ++ .mmio_init = dmr_uncore_mmio_init, ++ .domain[0].base_is_pci = true, ++ .domain[0].discovery_base = DMR_UNCORE_DISCOVERY_TABLE_DEVICE, ++ .domain[0].units_ignore = dmr_uncore_imh_units_ignore, ++}; ++ + static const struct uncore_plat_init generic_uncore_init __initconst = { + .cpu_init = intel_uncore_generic_uncore_cpu_init, + .pci_init = intel_uncore_generic_uncore_pci_init, +@@ -1898,6 +1906,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = { + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &gnr_uncore_init), + X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &gnr_uncore_init), + X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &gnr_uncore_init), ++ X86_MATCH_VFM(INTEL_DIAMONDRAPIDS_X, &dmr_uncore_init), + {}, + }; + MODULE_DEVICE_TABLE(x86cpu, intel_uncore_match); +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 1574ffc7ee053..1e4b3a22403c5 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -614,6 +614,7 @@ extern struct pci_extra_dev *uncore_extra_pci_dev; + extern struct event_constraint uncore_constraint_empty; + extern int spr_uncore_units_ignore[]; + extern int gnr_uncore_units_ignore[]; ++extern int dmr_uncore_imh_units_ignore[]; + + /* uncore_snb.c */ + int snb_uncore_pci_init(void); +@@ -662,6 +663,8 @@ void spr_uncore_mmio_init(void); + int gnr_uncore_pci_init(void); + void gnr_uncore_cpu_init(void); + void gnr_uncore_mmio_init(void); ++int dmr_uncore_pci_init(void); ++void dmr_uncore_mmio_init(void); + + /* uncore_nhmex.c */ + void nhmex_uncore_cpu_init(void); +diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h +index dfc237a2b6dfc..618788c30ac62 100644 +--- a/arch/x86/events/intel/uncore_discovery.h ++++ b/arch/x86/events/intel/uncore_discovery.h +@@ -5,6 +5,8 @@ + + /* Generic device ID of a discovery table device */ + #define UNCORE_DISCOVERY_TABLE_DEVICE 0x09a7 ++/* Device ID used on DMR */ ++#define DMR_UNCORE_DISCOVERY_TABLE_DEVICE 0x09a1 + /* Capability ID for a discovery table device */ + #define UNCORE_EXT_CAP_ID_DISCOVERY 0x23 + /* First DVSEC offset */ +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index e1f370b8d065f..4b72560dc13ff 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -471,6 +471,14 @@ + + #define SPR_C0_MSR_PMON_BOX_FILTER0 0x200e + ++/* DMR */ ++#define DMR_CXLCM_EVENT_MASK_EXT 0xf ++#define DMR_HAMVF_EVENT_MASK_EXT 0xffffffff ++#define DMR_PCIE4_EVENT_MASK_EXT 0xffffff ++ ++#define DMR_IMC_PMON_FIXED_CTR 0x18 ++#define DMR_IMC_PMON_FIXED_CTL 0x10 ++ + DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); + DEFINE_UNCORE_FORMAT_ATTR(event2, event, "config:0-6"); + DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21"); +@@ -486,6 +494,10 @@ DEFINE_UNCORE_FORMAT_ATTR(edge, edge, "config:18"); + DEFINE_UNCORE_FORMAT_ATTR(tid_en, tid_en, "config:19"); + DEFINE_UNCORE_FORMAT_ATTR(tid_en2, tid_en, "config:16"); + DEFINE_UNCORE_FORMAT_ATTR(inv, inv, "config:23"); ++DEFINE_UNCORE_FORMAT_ATTR(inv2, inv, "config:21"); ++DEFINE_UNCORE_FORMAT_ATTR(thresh_ext, thresh_ext, "config:32-35"); ++DEFINE_UNCORE_FORMAT_ATTR(thresh10, thresh, "config:23-32"); ++DEFINE_UNCORE_FORMAT_ATTR(thresh9_2, thresh, "config:23-31"); + DEFINE_UNCORE_FORMAT_ATTR(thresh9, thresh, "config:24-35"); + DEFINE_UNCORE_FORMAT_ATTR(thresh8, thresh, "config:24-31"); + DEFINE_UNCORE_FORMAT_ATTR(thresh6, thresh, "config:24-29"); +@@ -494,6 +506,13 @@ DEFINE_UNCORE_FORMAT_ATTR(occ_sel, occ_sel, "config:14-15"); + DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30"); + DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51"); + DEFINE_UNCORE_FORMAT_ATTR(occ_edge_det, occ_edge_det, "config:31"); ++DEFINE_UNCORE_FORMAT_ATTR(port_en, port_en, "config:32-35"); ++DEFINE_UNCORE_FORMAT_ATTR(rs3_sel, rs3_sel, "config:36"); ++DEFINE_UNCORE_FORMAT_ATTR(rx_sel, rx_sel, "config:37"); ++DEFINE_UNCORE_FORMAT_ATTR(tx_sel, tx_sel, "config:38"); ++DEFINE_UNCORE_FORMAT_ATTR(iep_sel, iep_sel, "config:39"); ++DEFINE_UNCORE_FORMAT_ATTR(vc_sel, vc_sel, "config:40-47"); ++DEFINE_UNCORE_FORMAT_ATTR(port_sel, port_sel, "config:48-55"); + DEFINE_UNCORE_FORMAT_ATTR(ch_mask, ch_mask, "config:36-43"); + DEFINE_UNCORE_FORMAT_ATTR(ch_mask2, ch_mask, "config:36-47"); + DEFINE_UNCORE_FORMAT_ATTR(fc_mask, fc_mask, "config:44-46"); +@@ -6709,3 +6728,213 @@ void gnr_uncore_mmio_init(void) + } + + /* end of GNR uncore support */ ++ ++/* DMR uncore support */ ++#define UNCORE_DMR_NUM_UNCORE_TYPES 52 ++ ++static struct attribute *dmr_imc_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv.attr, ++ &format_attr_thresh10.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_imc_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_imc_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_imc = { ++ .name = "imc", ++ .fixed_ctr_bits = 48, ++ .fixed_ctr = DMR_IMC_PMON_FIXED_CTR, ++ .fixed_ctl = DMR_IMC_PMON_FIXED_CTL, ++ .ops = &spr_uncore_mmio_ops, ++ .format_group = &dmr_imc_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct attribute *dmr_sca_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask_ext5.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv.attr, ++ &format_attr_thresh8.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_sca_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_sca_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_sca = { ++ .name = "sca", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct attribute *dmr_cxlcm_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv2.attr, ++ &format_attr_thresh9_2.attr, ++ &format_attr_port_en.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_cxlcm_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_cxlcm_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cxlcm = { ++ .name = "cxlcm", ++ .event_mask = GENERIC_PMON_RAW_EVENT_MASK, ++ .event_mask_ext = DMR_CXLCM_EVENT_MASK_EXT, ++ .format_group = &dmr_cxlcm_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_hamvf = { ++ .name = "hamvf", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_ula = { ++ .name = "ula", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_ubr = { ++ .name = "ubr", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct attribute *dmr_pcie4_uncore_formats_attr[] = { ++ &format_attr_event.attr, ++ &format_attr_umask.attr, ++ &format_attr_edge.attr, ++ &format_attr_inv.attr, ++ &format_attr_thresh8.attr, ++ &format_attr_thresh_ext.attr, ++ &format_attr_rs3_sel.attr, ++ &format_attr_rx_sel.attr, ++ &format_attr_tx_sel.attr, ++ &format_attr_iep_sel.attr, ++ &format_attr_vc_sel.attr, ++ &format_attr_port_sel.attr, ++ NULL, ++}; ++ ++static const struct attribute_group dmr_pcie4_uncore_format_group = { ++ .name = "format", ++ .attrs = dmr_pcie4_uncore_formats_attr, ++}; ++ ++static struct intel_uncore_type dmr_uncore_pcie4 = { ++ .name = "pcie4", ++ .event_mask_ext = DMR_PCIE4_EVENT_MASK_EXT, ++ .format_group = &dmr_pcie4_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_crs = { ++ .name = "crs", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cpc = { ++ .name = "cpc", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_itc = { ++ .name = "itc", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_otc = { ++ .name = "otc", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cms = { ++ .name = "cms", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_pcie6 = { ++ .name = "pcie6", ++ .event_mask_ext = DMR_PCIE4_EVENT_MASK_EXT, ++ .format_group = &dmr_pcie4_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type *dmr_uncores[UNCORE_DMR_NUM_UNCORE_TYPES] = { ++ NULL, NULL, NULL, NULL, ++ &spr_uncore_pcu, ++ &gnr_uncore_ubox, ++ &dmr_uncore_imc, ++ NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, ++ &dmr_uncore_sca, ++ &dmr_uncore_cxlcm, ++ NULL, NULL, NULL, ++ NULL, NULL, ++ &dmr_uncore_hamvf, ++ NULL, ++ NULL, NULL, NULL, ++ &dmr_uncore_ula, ++ NULL, NULL, NULL, NULL, ++ NULL, NULL, NULL, ++ &dmr_uncore_ubr, ++ NULL, ++ &dmr_uncore_pcie4, ++ &dmr_uncore_crs, ++ &dmr_uncore_cpc, ++ &dmr_uncore_itc, ++ &dmr_uncore_otc, ++ &dmr_uncore_cms, ++ &dmr_uncore_pcie6, ++}; ++ ++int dmr_uncore_imh_units_ignore[] = { ++ 0x13, /* MSE */ ++ UNCORE_IGNORE_END ++}; ++ ++int dmr_uncore_pci_init(void) ++{ ++ uncore_pci_uncores = uncore_get_uncores(UNCORE_ACCESS_PCI, 0, NULL, ++ UNCORE_DMR_NUM_UNCORE_TYPES, ++ dmr_uncores); ++ return 0; ++} ++void dmr_uncore_mmio_init(void) ++{ ++ uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, 0, NULL, ++ UNCORE_DMR_NUM_UNCORE_TYPES, ++ dmr_uncores); ++} ++ ++/* end of DMR uncore support */ +-- +2.43.0 + diff --git a/SPECS/kernel/0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt b/SPECS/kernel/0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt new file mode 100644 index 000000000..b06a1591b --- /dev/null +++ b/SPECS/kernel/0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt @@ -0,0 +1,59 @@ +From 30c1cb566d0e0ca91a48cbef72e9d31be27a12a8 Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Tue, 14 Oct 2025 14:45:34 -0700 +Subject: [PATCH 5/6] platform/x86:intel/pmc: Remove redundant has_die_c6 + variable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Remove has_die_c6 variable from the pmc_dev struct. This variable +is unnecessary as the availability of die C6 could be inferred by +the punit_ep variable. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251014214548.629023-7-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/core.c | 4 +--- + drivers/platform/x86/intel/pmc/core.h | 1 - + 2 files changed, 1 insertion(+), 4 deletions(-) + +diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c +index e272c971d73a5..7d7ae8a40b0ec 100644 +--- a/drivers/platform/x86/intel/pmc/core.c ++++ b/drivers/platform/x86/intel/pmc/core.c +@@ -1316,8 +1316,6 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) + } + + pmcdev->punit_ep = ep; +- +- pmcdev->has_die_c6 = true; + pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; + } + +@@ -1437,7 +1435,7 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info + pmcdev->dbgfs_dir, primary_pmc, &pmc_core_pson_residency); + } + +- if (pmcdev->has_die_c6) { ++ if (pmcdev->punit_ep) { + debugfs_create_file("die_c6_us_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_die_c6_us_fops); +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index cccd3bcafe00d..61c8d3c5faa0f 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -468,7 +468,6 @@ struct pmc_dev { + u64 *pkgc_res_cnt; + u8 num_of_pkgc; + +- bool has_die_c6; + u32 die_c6_offset; + struct telem_endpoint *punit_ep; + struct pmc_info *regmap_list; +-- +2.43.0 + diff --git a/SPECS/kernel/0005-rtnetlink-Add-return-value-check.ethernet b/SPECS/kernel/0005-rtnetlink-Add-return-value-check.ethernet new file mode 100644 index 000000000..dc4816e2f --- /dev/null +++ b/SPECS/kernel/0005-rtnetlink-Add-return-value-check.ethernet @@ -0,0 +1,36 @@ +From 2946c5c1c060d0fb4fe339f304477df6335035ac Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Tue, 3 Aug 2021 08:49:34 +0800 +Subject: [PATCH 05/14] rtnetlink: Add return value check + +This patch add return value checking for both of the nla_put_u32() and +nla_put_u8() in rtnl_xdp_fill(). + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + net/core/rtnetlink.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c +index b4ac5dedaed0a..5e2da0bcffb95 100644 +--- a/net/core/rtnetlink.c ++++ b/net/core/rtnetlink.c +@@ -1773,8 +1773,12 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) + if (!md_btf_id) + goto err_cancel; + +- nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); +- nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); ++ err = nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); ++ if (err) ++ goto err_cancel; ++ err = nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); ++ if (err) ++ goto err_cancel; + + nla_nest_end(skb, xdp); + return 0; +-- +2.43.0 + diff --git a/SPECS/kernel/0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet b/SPECS/kernel/0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet deleted file mode 100644 index fa0cca452..000000000 --- a/SPECS/kernel/0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet +++ /dev/null @@ -1,32 +0,0 @@ -From c274317949704de49a2f7c002fc461be620f5e73 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Fri, 30 Jul 2021 08:33:53 +0800 -Subject: [PATCH 05/19] rtnetlink: Fix unchecked return value of - dev_xdp_query_md_btf() - -This patch is to check the return value of dev_xdp_query_md_btf() -whether it contain a valid btf id or vice versa. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - net/core/rtnetlink.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -index aa18bd5b8c63..e4716ed7643b 100644 ---- a/net/core/rtnetlink.c -+++ b/net/core/rtnetlink.c -@@ -1768,6 +1768,9 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - } - - md_btf_id = dev_xdp_query_md_btf(dev, &md_btf_enabled); -+ if (!md_btf_id) -+ goto err_cancel; -+ - nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); - nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); - --- -2.43.0 - diff --git a/SPECS/kernel/0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss b/SPECS/kernel/0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss deleted file mode 100644 index 00f7f1d9d..000000000 --- a/SPECS/kernel/0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss +++ /dev/null @@ -1,29 +0,0 @@ -From a11716020c1ebcccaf2cba042eec1a7f3366ee0e Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Wed, 25 Oct 2023 11:30:29 +0300 -Subject: [PATCH 05/16] spi: intel-pci: Add support for Arrow Lake-H SPI serial - flash - -Add Intel Arrow Lake-H PCI ID to the driver list of supported devices. -This is the same controller found in previous generations. - -Signed-off-by: Mika Westerberg ---- - drivers/spi/spi-intel-pci.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c -index 4b63cb98df9c..49b4d3061197 100644 ---- a/drivers/spi/spi-intel-pci.c -+++ b/drivers/spi/spi-intel-pci.c -@@ -79,6 +79,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x5794), (unsigned long)&cnl_info }, -+ { PCI_VDEVICE(INTEL, 0x7723), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7a24), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x7e23), (unsigned long)&cnl_info }, --- -2.43.0 - diff --git a/SPECS/kernel/0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal b/SPECS/kernel/0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal deleted file mode 100644 index c24a73c31..000000000 --- a/SPECS/kernel/0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal +++ /dev/null @@ -1,102 +0,0 @@ -From b49702e5fcfff00a428b019b48b97020d6cd3555 Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 25 Aug 2025 06:23:15 -0700 -Subject: [PATCH 05/11] thermal: intel: int340x: Add module parameter to change - slider offset - -SoC slider value is set by the user (or the default when user has not -modified it). To enhance power efficiency dynamically, the firmware can -optionally auto-adjust the slider value based on the current workload. -This adjustment is governed by an additional parameter known as the -"slider offset". This offset permits the firmware to increase the slider -value up to and including "SoC slider + slider offset". - -Add a module parameter to specify this "slier offset" value. - -By default, the SoC slider offset is set to 0. This means that SoC is not -allowed to switch slider position. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250825132315.75521-5-srinivas.pandruvada@linux.intel.com -[ rjw: Comment and module param description adjustments ] -Signed-off-by: Rafael J. Wysocki ---- - .../processor_thermal_soc_slider.c | 48 +++++++++++++++++++ - 1 file changed, 48 insertions(+) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -index ffbad6b4326e..20d70cb01542 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -90,6 +90,47 @@ static const struct kernel_param_ops slider_def_balance_ops = { - module_param_cb(slider_balance, &slider_def_balance_ops, NULL, 0644); - MODULE_PARM_DESC(slider_balance, "Set slider default value for balance"); - -+static u8 slider_offset; -+ -+static int slider_def_offset_set(const char *arg, const struct kernel_param *kp) -+{ -+ u8 offset; -+ int ret; -+ -+ guard(mutex)(&slider_param_lock); -+ -+ ret = kstrtou8(arg, 16, &offset); -+ if (!ret) { -+ if (offset > SOC_SLIDER_VALUE_MAXIMUM) -+ return -EINVAL; -+ -+ slider_offset = offset; -+ } -+ -+ return ret; -+} -+ -+static int slider_def_offset_get(char *buf, const struct kernel_param *kp) -+{ -+ guard(mutex)(&slider_param_lock); -+ return sysfs_emit(buf, "%02x\n", slider_offset); -+} -+ -+static const struct kernel_param_ops slider_offset_ops = { -+ .set = slider_def_offset_set, -+ .get = slider_def_offset_get, -+}; -+ -+/* -+ * To enhance power efficiency dynamically, the firmware can optionally -+ * auto-adjust the slider value based on the current workload. This -+ * adjustment is controlled by the "slider_offset" module parameter. -+ * This offset permits the firmware to increase the slider value -+ * up to and including "SoC slider + slider offset,". -+ */ -+module_param_cb(slider_offset, &slider_offset_ops, NULL, 0644); -+MODULE_PARM_DESC(slider_offset, "Set slider offset"); -+ - /* Convert from platform power profile option to SoC slider value */ - static int convert_profile_to_power_slider(enum platform_profile_option profile) - { -@@ -130,6 +171,8 @@ static inline void write_soc_slider(struct proc_thermal_device *proc_priv, u64 v - writeq(val, proc_priv->mmio_base + SOC_POWER_SLIDER_OFFSET); - } - -+#define SLIDER_OFFSET_MASK GENMASK_ULL(6, 4) -+ - static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int slider) - { - u64 val; -@@ -137,6 +180,11 @@ static void set_soc_power_profile(struct proc_thermal_device *proc_priv, int sli - val = read_soc_slider(proc_priv); - val &= ~SLIDER_MASK; - val |= FIELD_PREP(SLIDER_MASK, slider) | BIT(SLIDER_ENABLE_BIT); -+ -+ /* Set the slider offset from module params */ -+ val &= ~SLIDER_OFFSET_MASK; -+ val |= FIELD_PREP(SLIDER_OFFSET_MASK, slider_offset); -+ - write_soc_slider(proc_priv, val); - } - --- -2.43.0 - diff --git a/SPECS/kernel/0005-tools-power-turbostat.8-Update-example.turbo b/SPECS/kernel/0005-tools-power-turbostat.8-Update-example.turbo new file mode 100644 index 000000000..50beb6d09 --- /dev/null +++ b/SPECS/kernel/0005-tools-power-turbostat.8-Update-example.turbo @@ -0,0 +1,56 @@ +From d0927b8efda675c1b4dea7718a967e9f213befb9 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 18:48:39 -0300 +Subject: [PATCH 05/21] tools/power turbostat.8: Update example + +Update the added-counters example to print counters in decimal +rather than hex -- now that it is working... + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.8 | 21 ++++++++++----------- + 1 file changed, 10 insertions(+), 11 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 +index 3340def58d015..ad3fc201552f7 100644 +--- a/tools/power/x86/turbostat/turbostat.8 ++++ b/tools/power/x86/turbostat/turbostat.8 +@@ -410,25 +410,24 @@ CPU pCPU%c1 CPU%c1 + .fi + + .SH ADD PERF COUNTER EXAMPLE #2 (using virtual cpu device) +-Here we run on hybrid, Raptor Lake platform. +-We limit turbostat to show output for just cpu0 (pcore) and cpu12 (ecore). ++Here we run on hybrid, Meteor Lake platform. ++We limit turbostat to show output for just cpu0 (pcore) and cpu4 (ecore). + We add a counter showing number of L3 cache misses, using virtual "cpu" device, + labeling it with the column header, "VCMISS". + We add a counter showing number of L3 cache misses, using virtual "cpu_core" device, +-labeling it with the column header, "PCMISS". This will fail on ecore cpu12. ++labeling it with the column header, "PCMISS". This will fail on ecore cpu4. + We add a counter showing number of L3 cache misses, using virtual "cpu_atom" device, + labeling it with the column header, "ECMISS". This will fail on pcore cpu0. + We display it only once, after the conclusion of 0.1 second sleep. + .nf +-sudo ./turbostat --quiet --cpu 0,12 --show CPU --add perf/cpu/cache-misses,cpu,delta,raw,VCMISS --add perf/cpu_core/cache-misses,cpu,delta,raw,PCMISS --add perf/cpu_atom/cache-misses,cpu,delta,raw,ECMISS sleep .1 ++sudo ./turbostat --quiet --cpu 0,4 --show CPU --add perf/cpu/cache-misses,cpu,delta,VCMISS --add perf/cpu_core/cache-misses,cpu,delta,PCMISS --add perf/cpu_atom/cache-misses,cpu,delta,ECMISS sleep 5 + turbostat: added_perf_counters_init_: perf/cpu_atom/cache-misses: failed to open counter on cpu0 +-turbostat: added_perf_counters_init_: perf/cpu_core/cache-misses: failed to open counter on cpu12 +-0.104630 sec +-CPU ECMISS PCMISS VCMISS +-- 0x0000000000000000 0x0000000000000000 0x0000000000000000 +-0 0x0000000000000000 0x0000000000007951 0x0000000000007796 +-12 0x000000000001137a 0x0000000000000000 0x0000000000011392 +- ++turbostat: added_perf_counters_init_: perf/cpu_core/cache-misses: failed to open counter on cpu4 ++5.001207 sec ++CPU ECMISS PCMISS VCMISS ++- 41586506 46291219 87877749 ++4 83173012 0 83173040 ++0 0 92582439 92582458 + .fi + + .SH ADD PMT COUNTER EXAMPLE +-- +2.43.0 + diff --git a/SPECS/kernel/0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi b/SPECS/kernel/0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi deleted file mode 100644 index 055435632..000000000 --- a/SPECS/kernel/0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi +++ /dev/null @@ -1,216 +0,0 @@ -From c61e1f8da92e23e84dcf5c395d4d8db7e33870a0 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Tue, 20 Aug 2024 23:31:06 -0700 -Subject: [PATCH 05/44] x86/cea: Export an API to get per CPU exception stacks - for KVM to use - -FRED introduced new fields in the host-state area of the VMCS for -stack levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively -corresponding to per CPU exception stacks for #DB, NMI and #DF. -KVM must populate these each time a vCPU is loaded onto a CPU. - -Convert the __this_cpu_ist_{top,bottom}_va() macros into real -functions and export __this_cpu_ist_top_va(). - -Suggested-by: Christoph Hellwig -Suggested-by: Dave Hansen -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Export accessor instead of data (Christoph Hellwig). -* Add TB from Xuelian Guo. - -Change in v4: -* Rewrite the change log and add comments to the export (Dave Hansen). ---- - arch/x86/coco/sev/sev-nmi.c | 4 ++-- - arch/x86/coco/sev/vc-handle.c | 2 +- - arch/x86/include/asm/cpu_entry_area.h | 17 ++++------------- - arch/x86/kernel/cpu/common.c | 10 +++++----- - arch/x86/kernel/fred.c | 6 +++--- - arch/x86/kernel/traps.c | 2 +- - arch/x86/mm/cpu_entry_area.c | 21 +++++++++++++++++++++ - arch/x86/mm/fault.c | 2 +- - 8 files changed, 38 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/coco/sev/sev-nmi.c b/arch/x86/coco/sev/sev-nmi.c -index d8dfaddfb367..73e34ad7a1a9 100644 ---- a/arch/x86/coco/sev/sev-nmi.c -+++ b/arch/x86/coco/sev/sev-nmi.c -@@ -30,7 +30,7 @@ static __always_inline bool on_vc_stack(struct pt_regs *regs) - if (ip_within_syscall_gap(regs)) - return false; - -- return ((sp >= __this_cpu_ist_bottom_va(VC)) && (sp < __this_cpu_ist_top_va(VC))); -+ return ((sp >= __this_cpu_ist_bottom_va(ESTACK_VC)) && (sp < __this_cpu_ist_top_va(ESTACK_VC))); - } - - /* -@@ -82,7 +82,7 @@ void noinstr __sev_es_ist_exit(void) - /* Read IST entry */ - ist = __this_cpu_read(cpu_tss_rw.x86_tss.ist[IST_INDEX_VC]); - -- if (WARN_ON(ist == __this_cpu_ist_top_va(VC))) -+ if (WARN_ON(ist == __this_cpu_ist_top_va(ESTACK_VC))) - return; - - /* Read back old IST entry and write it to the TSS */ -diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c -index c3b4acbde0d8..88b6bc518a5a 100644 ---- a/arch/x86/coco/sev/vc-handle.c -+++ b/arch/x86/coco/sev/vc-handle.c -@@ -859,7 +859,7 @@ static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt, - - static __always_inline bool is_vc2_stack(unsigned long sp) - { -- return (sp >= __this_cpu_ist_bottom_va(VC2) && sp < __this_cpu_ist_top_va(VC2)); -+ return (sp >= __this_cpu_ist_bottom_va(ESTACK_VC2) && sp < __this_cpu_ist_top_va(ESTACK_VC2)); - } - - static __always_inline bool vc_from_invalid_context(struct pt_regs *regs) -diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h -index 462fc34f1317..8e17f0ca74e6 100644 ---- a/arch/x86/include/asm/cpu_entry_area.h -+++ b/arch/x86/include/asm/cpu_entry_area.h -@@ -46,7 +46,7 @@ struct cea_exception_stacks { - * The exception stack ordering in [cea_]exception_stacks - */ - enum exception_stack_ordering { -- ESTACK_DF, -+ ESTACK_DF = 0, - ESTACK_NMI, - ESTACK_DB, - ESTACK_MCE, -@@ -58,18 +58,15 @@ enum exception_stack_ordering { - #define CEA_ESTACK_SIZE(st) \ - sizeof(((struct cea_exception_stacks *)0)->st## _stack) - --#define CEA_ESTACK_BOT(ceastp, st) \ -- ((unsigned long)&(ceastp)->st## _stack) -- --#define CEA_ESTACK_TOP(ceastp, st) \ -- (CEA_ESTACK_BOT(ceastp, st) + CEA_ESTACK_SIZE(st)) -- - #define CEA_ESTACK_OFFS(st) \ - offsetof(struct cea_exception_stacks, st## _stack) - - #define CEA_ESTACK_PAGES \ - (sizeof(struct cea_exception_stacks) / PAGE_SIZE) - -+extern unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack); -+extern unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack); -+ - #endif - - #ifdef CONFIG_X86_32 -@@ -144,10 +141,4 @@ static __always_inline struct entry_stack *cpu_entry_stack(int cpu) - return &get_cpu_entry_area(cpu)->entry_stack_page.stack; - } - --#define __this_cpu_ist_top_va(name) \ -- CEA_ESTACK_TOP(__this_cpu_read(cea_exception_stacks), name) -- --#define __this_cpu_ist_bottom_va(name) \ -- CEA_ESTACK_BOT(__this_cpu_read(cea_exception_stacks), name) -- - #endif -diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c -index 34a054181c4d..cb14919f92da 100644 ---- a/arch/x86/kernel/cpu/common.c -+++ b/arch/x86/kernel/cpu/common.c -@@ -2307,12 +2307,12 @@ static inline void setup_getcpu(int cpu) - static inline void tss_setup_ist(struct tss_struct *tss) - { - /* Set up the per-CPU TSS IST stacks */ -- tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(DF); -- tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(NMI); -- tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(DB); -- tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(MCE); -+ tss->x86_tss.ist[IST_INDEX_DF] = __this_cpu_ist_top_va(ESTACK_DF); -+ tss->x86_tss.ist[IST_INDEX_NMI] = __this_cpu_ist_top_va(ESTACK_NMI); -+ tss->x86_tss.ist[IST_INDEX_DB] = __this_cpu_ist_top_va(ESTACK_DB); -+ tss->x86_tss.ist[IST_INDEX_MCE] = __this_cpu_ist_top_va(ESTACK_MCE); - /* Only mapped when SEV-ES is active */ -- tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(VC); -+ tss->x86_tss.ist[IST_INDEX_VC] = __this_cpu_ist_top_va(ESTACK_VC); - } - #else /* CONFIG_X86_64 */ - static inline void tss_setup_ist(struct tss_struct *tss) { } -diff --git a/arch/x86/kernel/fred.c b/arch/x86/kernel/fred.c -index 816187da3a47..06d944a3d051 100644 ---- a/arch/x86/kernel/fred.c -+++ b/arch/x86/kernel/fred.c -@@ -87,7 +87,7 @@ void cpu_init_fred_rsps(void) - FRED_STKLVL(X86_TRAP_DF, FRED_DF_STACK_LEVEL)); - - /* The FRED equivalents to IST stacks... */ -- wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(DB)); -- wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(NMI)); -- wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(DF)); -+ wrmsrq(MSR_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); -+ wrmsrq(MSR_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); -+ wrmsrq(MSR_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); - } -diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c -index 36354b470590..5c9c5ebf5e73 100644 ---- a/arch/x86/kernel/traps.c -+++ b/arch/x86/kernel/traps.c -@@ -954,7 +954,7 @@ asmlinkage __visible noinstr struct pt_regs *vc_switch_off_ist(struct pt_regs *r - - if (!get_stack_info_noinstr(stack, current, &info) || info.type == STACK_TYPE_ENTRY || - info.type > STACK_TYPE_EXCEPTION_LAST) -- sp = __this_cpu_ist_top_va(VC2); -+ sp = __this_cpu_ist_top_va(ESTACK_VC2); - - sync: - /* -diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c -index 575f863f3c75..eedaf103c8ad 100644 ---- a/arch/x86/mm/cpu_entry_area.c -+++ b/arch/x86/mm/cpu_entry_area.c -@@ -18,6 +18,27 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage) - static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks); - DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks); - -+/* -+ * FRED introduced new fields in the host-state area of the VMCS for -+ * stack levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively -+ * corresponding to per CPU stacks for #DB, NMI and #DF. KVM must -+ * populate these each time a vCPU is loaded onto a CPU. -+ * -+ * Called from entry code, so must be noinstr. -+ */ -+noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack) -+{ -+ unsigned long base = (unsigned long)&(__this_cpu_read(cea_exception_stacks)->DF_stack); -+ return base + EXCEPTION_STKSZ + stack * (EXCEPTION_STKSZ + PAGE_SIZE); -+} -+EXPORT_SYMBOL(__this_cpu_ist_top_va); -+ -+noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack) -+{ -+ unsigned long base = (unsigned long)&(__this_cpu_read(cea_exception_stacks)->DF_stack); -+ return base + stack * (EXCEPTION_STKSZ + PAGE_SIZE); -+} -+ - static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset); - - static __always_inline unsigned int cea_offset(unsigned int cpu) -diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c -index 998bd807fc7b..6d59ff7c73e1 100644 ---- a/arch/x86/mm/fault.c -+++ b/arch/x86/mm/fault.c -@@ -671,7 +671,7 @@ page_fault_oops(struct pt_regs *regs, unsigned long error_code, - * and then double-fault, though, because we're likely to - * break the console driver and lose most of the stack dump. - */ -- call_on_stack(__this_cpu_ist_top_va(DF) - sizeof(void*), -+ call_on_stack(__this_cpu_ist_top_va(ESTACK_DF) - sizeof(void *), - handle_stack_overflow, - ASM_CALL_ARG3, - , [arg1] "r" (regs), [arg2] "r" (address), [arg3] "r" (&info)); --- -2.43.0 - diff --git a/SPECS/kernel/0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi b/SPECS/kernel/0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi new file mode 100644 index 000000000..04da0600f --- /dev/null +++ b/SPECS/kernel/0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi @@ -0,0 +1,181 @@ +From c1fdba5f755f5423b3b90b7554164da03ba9fdb6 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Thu, 28 Aug 2025 20:16:09 -0400 +Subject: [PATCH 05/44] x86/cea: Use array indexing to simplify exception stack + access + +Refactor struct cea_exception_stacks to leverage array indexing for +exception stack access, improving code clarity and eliminating the +need for the ESTACKS_MEMBERS() macro. + +Convert __this_cpu_ist_{bottom,top}_va() from macros to functions, +allowing removal of the now-obsolete CEA_ESTACK_BOT and CEA_ESTACK_TOP +macros. + +Also drop CEA_ESTACK_SIZE, which just duplicated EXCEPTION_STKSZ. + +Signed-off-by: Xin Li (Intel) +--- + +Change in v9: +* Refactor first and then export in a separate patch (Dave Hansen). + +Change in v7: +* Access cea_exception_stacks using array indexing (Dave Hansen). +* Use BUILD_BUG_ON(ESTACK_DF != 0) to ensure the starting index is 0 + (Dave Hansen). +* Remove Suggested-bys (Dave Hansen). +* Move rename code in a separate patch (Dave Hansen). + +Change in v5: +* Export accessor instead of data (Christoph Hellwig). +* Add TB from Xuelian Guo. + +Change in v4: +* Rewrite the change log and add comments to the export (Dave Hansen). +--- + arch/x86/include/asm/cpu_entry_area.h | 52 ++++++++++++--------------- + arch/x86/kernel/dumpstack_64.c | 4 +-- + arch/x86/mm/cpu_entry_area.c | 21 ++++++++++- + 3 files changed, 44 insertions(+), 33 deletions(-) + +diff --git a/arch/x86/include/asm/cpu_entry_area.h b/arch/x86/include/asm/cpu_entry_area.h +index d0f884c281787..509e52fc3a0f6 100644 +--- a/arch/x86/include/asm/cpu_entry_area.h ++++ b/arch/x86/include/asm/cpu_entry_area.h +@@ -16,6 +16,19 @@ + #define VC_EXCEPTION_STKSZ 0 + #endif + ++/* ++ * The exception stack ordering in [cea_]exception_stacks ++ */ ++enum exception_stack_ordering { ++ ESTACK_DF, ++ ESTACK_NMI, ++ ESTACK_DB, ++ ESTACK_MCE, ++ ESTACK_VC, ++ ESTACK_VC2, ++ N_EXCEPTION_STACKS ++}; ++ + /* Macro to enforce the same ordering and stack sizes */ + #define ESTACKS_MEMBERS(guardsize, optional_stack_size) \ + char ESTACK_DF_stack_guard[guardsize]; \ +@@ -39,37 +52,22 @@ struct exception_stacks { + + /* The effective cpu entry area mapping with guard pages. */ + struct cea_exception_stacks { +- ESTACKS_MEMBERS(PAGE_SIZE, EXCEPTION_STKSZ) +-}; +- +-/* +- * The exception stack ordering in [cea_]exception_stacks +- */ +-enum exception_stack_ordering { +- ESTACK_DF, +- ESTACK_NMI, +- ESTACK_DB, +- ESTACK_MCE, +- ESTACK_VC, +- ESTACK_VC2, +- N_EXCEPTION_STACKS ++ struct { ++ char stack_guard[PAGE_SIZE]; ++ char stack[EXCEPTION_STKSZ]; ++ } event_stacks[N_EXCEPTION_STACKS]; ++ char IST_top_guard[PAGE_SIZE]; + }; + +-#define CEA_ESTACK_SIZE(st) \ +- sizeof(((struct cea_exception_stacks *)0)->st## _stack) +- +-#define CEA_ESTACK_BOT(ceastp, st) \ +- ((unsigned long)&(ceastp)->st## _stack) +- +-#define CEA_ESTACK_TOP(ceastp, st) \ +- (CEA_ESTACK_BOT(ceastp, st) + CEA_ESTACK_SIZE(st)) +- + #define CEA_ESTACK_OFFS(st) \ +- offsetof(struct cea_exception_stacks, st## _stack) ++ offsetof(struct cea_exception_stacks, event_stacks[st].stack) + + #define CEA_ESTACK_PAGES \ + (sizeof(struct cea_exception_stacks) / PAGE_SIZE) + ++extern unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack); ++extern unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack); ++ + #endif + + #ifdef CONFIG_X86_32 +@@ -144,10 +142,4 @@ static __always_inline struct entry_stack *cpu_entry_stack(int cpu) + return &get_cpu_entry_area(cpu)->entry_stack_page.stack; + } + +-#define __this_cpu_ist_top_va(name) \ +- CEA_ESTACK_TOP(__this_cpu_read(cea_exception_stacks), name) +- +-#define __this_cpu_ist_bottom_va(name) \ +- CEA_ESTACK_BOT(__this_cpu_read(cea_exception_stacks), name) +- + #endif +diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c +index 40f51e2781715..93b10b264e53b 100644 +--- a/arch/x86/kernel/dumpstack_64.c ++++ b/arch/x86/kernel/dumpstack_64.c +@@ -70,9 +70,9 @@ struct estack_pages { + + #define EPAGERANGE(st) \ + [PFN_DOWN(CEA_ESTACK_OFFS(st)) ... \ +- PFN_DOWN(CEA_ESTACK_OFFS(st) + CEA_ESTACK_SIZE(st) - 1)] = { \ ++ PFN_DOWN(CEA_ESTACK_OFFS(st) + EXCEPTION_STKSZ - 1)] = { \ + .offs = CEA_ESTACK_OFFS(st), \ +- .size = CEA_ESTACK_SIZE(st), \ ++ .size = EXCEPTION_STKSZ, \ + .type = STACK_TYPE_EXCEPTION + st, } + + /* +diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c +index 9fa371af8abc7..b3d90f9cfbb11 100644 +--- a/arch/x86/mm/cpu_entry_area.c ++++ b/arch/x86/mm/cpu_entry_area.c +@@ -18,6 +18,25 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct entry_stack_page, entry_stack_storage) + static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks); + DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks); + ++/* ++ * Typically invoked by entry code, so must be noinstr. ++ */ ++noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack) ++{ ++ struct cea_exception_stacks *s; ++ ++ BUILD_BUG_ON(ESTACK_DF != 0); ++ ++ s = __this_cpu_read(cea_exception_stacks); ++ ++ return (unsigned long)&s->event_stacks[stack].stack; ++} ++ ++noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack) ++{ ++ return __this_cpu_ist_bottom_va(stack) + EXCEPTION_STKSZ; ++} ++ + static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset); + + static __always_inline unsigned int cea_offset(unsigned int cpu) +@@ -132,7 +151,7 @@ static void __init percpu_setup_debug_store(unsigned int cpu) + + #define cea_map_stack(name) do { \ + npages = sizeof(estacks->name## _stack) / PAGE_SIZE; \ +- cea_map_percpu_pages(cea->estacks.name## _stack, \ ++ cea_map_percpu_pages(cea->estacks.event_stacks[name].stack, \ + estacks->name## _stack, npages, PAGE_KERNEL); \ + } while (0) + +-- +2.43.0 + diff --git a/SPECS/kernel/0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac b/SPECS/kernel/0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac deleted file mode 100644 index 0ab3e5ba0..000000000 --- a/SPECS/kernel/0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac +++ /dev/null @@ -1,37 +0,0 @@ -From d30443ac72e0c28fc76598befc751143987d6107 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Tue, 29 Jul 2025 15:22:48 +0800 -Subject: [PATCH 06/13] EDAC/skx_common: Remove redundant upper bound check for - res->imc - -The following upper bound check for the memory controller physical index -decoded by ADXL is the only place where use the macro 'NUM_IMC' is used: - - res->imc > NUM_IMC - 1 - -Since this check is already covered by skx_get_mc_mapping(), meaning no -memory controller logical index exists for an invalid memory controller -physical index decoded by ADXL, remove the redundant upper bound check -so that the definition for 'NUM_IMC' can be cleaned up (in another patch). - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c -index 09187043c045..5899a1110495 100644 ---- a/drivers/edac/skx_common.c -+++ b/drivers/edac/skx_common.c -@@ -207,7 +207,7 @@ static bool skx_adxl_decode(struct decoded_addr *res, enum error_source err_src) - res->cs = (int)adxl_values[component_indices[INDEX_CS]]; - } - -- if (res->imc > NUM_IMC - 1 || res->imc < 0) { -+ if (res->imc < 0) { - skx_printk(KERN_ERR, "Bad imc %d\n", res->imc); - return false; - } --- -2.43.0 - diff --git a/SPECS/kernel/0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security b/SPECS/kernel/0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security new file mode 100644 index 000000000..db2617fce --- /dev/null +++ b/SPECS/kernel/0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security @@ -0,0 +1,461 @@ +From 511d9f589f959d58452c83753b1d9b402d100298 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Mon, 5 Oct 2020 13:04:48 +0300 +Subject: [PATCH 6/8] INTEL_DII: mei: iaf: add iaf (Intel Accelerator Fabric) + component driver + +On every boot the GSC firmware programs +"the IAF minimum allowed SVN register" with its minimal allowed value +in all tiles. SVN stands for security version number. + +mei_iaf driver provides an interface for IAF to increment the +minimal allowed SVN. +mei_aif is represented as a device over mei client bus. + +To enable this select CONFIG_INTEL_MEI_IAF + +V2: +Updated the parsing of values returned by the firmware. + +V3: +Replaced error messages in cases of SVN DISABLED, SVM SAME +and SVM SMALLER with the debug messages instead of the error ones. +Those cases are legitimate and should not be reported as errors. + +v4: +Move MCHI_GROUP_ID definition to this patch. + +Co-developed-by: Tomas Winkler +Co-developed-by: Vitaly Lubart +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/Kconfig | 1 + + drivers/misc/mei/Makefile | 1 + + drivers/misc/mei/iaf/Kconfig | 12 ++ + drivers/misc/mei/iaf/Makefile | 9 + + drivers/misc/mei/iaf/mei_iaf.c | 295 +++++++++++++++++++++++++++ + drivers/misc/mei/mkhi.h | 2 + + include/drm/i915_mei_iaf_interface.h | 25 +++ + include/drm/intel/i915_component.h | 1 + + 8 files changed, 346 insertions(+) + create mode 100644 drivers/misc/mei/iaf/Kconfig + create mode 100644 drivers/misc/mei/iaf/Makefile + create mode 100644 drivers/misc/mei/iaf/mei_iaf.c + create mode 100644 include/drm/i915_mei_iaf_interface.h + +diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig +index 2b1330b35454e..729aea4fe3212 100644 +--- a/drivers/misc/mei/Kconfig ++++ b/drivers/misc/mei/Kconfig +@@ -106,5 +106,6 @@ config INTEL_MEI_VIRTIO + source "drivers/misc/mei/hdcp/Kconfig" + source "drivers/misc/mei/pxp/Kconfig" + source "drivers/misc/mei/gsc_proxy/Kconfig" ++source "drivers/misc/mei/iaf/Kconfig" + + endif +diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile +index 9de79da3d7027..e14c0cb5e5c89 100644 +--- a/drivers/misc/mei/Makefile ++++ b/drivers/misc/mei/Makefile +@@ -35,6 +35,7 @@ obj-$(CONFIG_INTEL_MEI_HDCP) += hdcp/ + obj-$(CONFIG_INTEL_MEI_PXP) += pxp/ + obj-$(CONFIG_INTEL_MEI_GSC_PROXY) += gsc_proxy/ + obj-$(CONFIG_INTEL_MEI_LB) += mei_lb.o ++obj-$(CONFIG_INTEL_MEI_IAF) += iaf/ + + obj-$(CONFIG_INTEL_MEI_VSC_HW) += mei-vsc-hw.o + mei-vsc-hw-y := vsc-tp.o +diff --git a/drivers/misc/mei/iaf/Kconfig b/drivers/misc/mei/iaf/Kconfig +new file mode 100644 +index 0000000000000..0690b96e2dd76 +--- /dev/null ++++ b/drivers/misc/mei/iaf/Kconfig +@@ -0,0 +1,12 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# Copyright (c) 2020-2021, Intel Corporation. All rights reserved. ++# ++config INTEL_MEI_IAF ++ tristate "Intel Accelerator Fabric services of ME Interface" ++ select INTEL_MEI_ME ++ depends on DRM_I915 ++ help ++ MEI Support for IAF Services on Intel graphics card. ++ ++ Enables the ME FW services required for IAF support through ++ I915 display driver of Intel. +diff --git a/drivers/misc/mei/iaf/Makefile b/drivers/misc/mei/iaf/Makefile +new file mode 100644 +index 0000000000000..8eb709cf536f5 +--- /dev/null ++++ b/drivers/misc/mei/iaf/Makefile +@@ -0,0 +1,9 @@ ++# SPDX-License-Identifier: GPL-2.0 ++# ++# Copyright (c) 2020-2021, Intel Corporation. All rights reserved. ++# ++# Makefile - IAF client driver for Intel MEI Bus Driver. ++ ++subdir-ccflags-y += -I$(srctree)/drivers/misc/mei/ ++ ++obj-$(CONFIG_INTEL_MEI_IAF) += mei_iaf.o +diff --git a/drivers/misc/mei/iaf/mei_iaf.c b/drivers/misc/mei/iaf/mei_iaf.c +new file mode 100644 +index 0000000000000..e81280ea20a8c +--- /dev/null ++++ b/drivers/misc/mei/iaf/mei_iaf.c +@@ -0,0 +1,295 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * Copyright © 2020-2021 Intel Corporation ++ */ ++ ++/** ++ * DOC: MEI_IAF Client Driver ++ * ++ * The IAF (Intel Accelerator Fabric) component driver acts as an interface ++ * between IAF i915 driver and GSC. The only api this interface provides is ++ * the 'commit svn' call. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mkhi.h" ++ ++#define MCA_ARBSVN_COMMIT_COMMAND_ID 0x1B ++ ++enum arbsvn_nvar_usage { ++ ARBSVN_NVAR_USAGE_FW_MIN_VER = 0, ++ ARBSVN_NVAR_USAGE_MAX ++}; ++ ++struct mca_arbsvn_commit_req { ++ struct mkhi_msg_hdr mkhi_header; ++ u8 usage_id; ++ u8 reserved0; ++ u16 reserved1; ++} __packed; ++ ++struct mca_arbsvn_commit_resp { ++ struct mkhi_msg_hdr mkhi_header; ++}; ++ ++#define MCA_OK 0x0 /* on successful commit */ ++#define MCA_INVALID_INPUT 0xb /* if usage id is invalid */ ++/* if disabled in the file or any other error (generic, reading or writing file) */ ++#define MCA_ARB_SVN_DISABLED 0x20 ++/* SVN was not updated, same value */ ++#define MCA_ARB_SVN_SAME 0x28 ++/* SVN was not updated, older value */ ++#define MCA_ARB_SVN_SMALLER 0x29 ++ ++static int get_error_code(const struct device *dev, u8 result) ++{ ++ int ret; ++ ++ switch (result) { ++ case MCA_OK: ++ ret = 0; ++ break; ++ case MCA_ARB_SVN_DISABLED: ++ dev_dbg(dev, "Arb Svn disabled (error code 0x%x)\n", ++ MCA_ARB_SVN_DISABLED); ++ ret = -ENOENT; ++ break; ++ case MCA_INVALID_INPUT: ++ dev_err(dev, "Wrong usage id(error code 0x%x)\n", ++ MCA_INVALID_INPUT); ++ ret = -EINVAL; ++ break; ++ case MCA_ARB_SVN_SAME: ++ dev_dbg(dev, "SVN was not updated, same value(error code 0x%x)\n", ++ MCA_ARB_SVN_SAME); ++ ret = -EACCES; ++ break; ++ case MCA_ARB_SVN_SMALLER: ++ dev_dbg(dev, "SVN was not updated, older value(error code 0x%x)\n", ++ MCA_ARB_SVN_SMALLER); ++ ret = -EBADF; ++ break; ++ default: ++ dev_err(dev, "Unknown error code 0x%x\n", result); ++ ret = -EIO; ++ } ++ ++ return ret; ++} ++ ++static int mei_iaf_check_response(const struct device *dev, ++ struct mkhi_msg_hdr *hdr) ++{ ++ if (hdr->group_id != MCHI_GROUP_ID) { ++ dev_err(dev, "Mismatch group id: 0x%x instead of 0x%x\n", ++ hdr->group_id, MCHI_GROUP_ID); ++ return -EINVAL; ++ } ++ ++ if (hdr->command != (MCA_ARBSVN_COMMIT_COMMAND_ID | 0x80)) { ++ dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", ++ hdr->command, MCA_ARBSVN_COMMIT_COMMAND_ID | 0x80); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * mei_iaf_commit_svn() - Commits current SVN. ++ * @dev: device corresponding to the mei_cl_device ++ * Return: 0 on Success ++ * * -EINVAL : Invalid usage id parameter ++ * * -ENOENT : ARB SVN is disabled in the file or any other error ++ * (generic, reading or writing file) ++ * * -EIO : Unknown I/O error ++ */ ++static int mei_iaf_commit_svn(const struct device *dev) ++{ ++ struct mei_cl_device *cldev; ++ struct mca_arbsvn_commit_req commit_req = { }; ++ struct mca_arbsvn_commit_resp commit_resp = { }; ++ int ret; ++ ++ dev_dbg(dev, "in %s\n", __func__); ++ ++ if (!dev) ++ return -EINVAL; ++ ++ cldev = to_mei_cl_device(dev); ++ ++ dev_dbg(dev, "after to_mei_cl_device cldev %p\n", cldev); ++ ++ ret = mei_cldev_enable(cldev); ++ if (ret < 0) { ++ dev_dbg(dev, "mei_cldev_enable Failed. %d\n", ret); ++ return -EBUSY; ++ } ++ ++ dev_dbg(dev, "after mei_cldev_enable, ret=%d\n", ret); ++ commit_req.mkhi_header.group_id = MCHI_GROUP_ID; ++ commit_req.mkhi_header.command = MCA_ARBSVN_COMMIT_COMMAND_ID; ++ commit_req.usage_id = ARBSVN_NVAR_USAGE_FW_MIN_VER; ++ ++ ret = mei_cldev_send(cldev, (u8 *)&commit_req, sizeof(commit_req)); ++ if (ret < 0) { ++ dev_err(dev, "mei_cldev_send failed. %d\n", ret); ++ goto end; ++ } ++ dev_dbg(dev, "after send, ret=%d\n", ret); ++ print_hex_dump_debug("sent svn commit message: ", DUMP_PREFIX_OFFSET, ++ 16, 1, (u8 *)&commit_req, ret, false); ++ ++ ret = mei_cldev_recv(cldev, (u8 *)&commit_resp, sizeof(commit_resp)); ++ if (ret < 0) { ++ dev_err(dev, "mei_cldev_recv failed. %d\n", ret); ++ goto end; ++ } ++ dev_dbg(dev, "after recv, ret=%d\n", ret); ++ print_hex_dump_debug("mei_iaf_commit_response ", DUMP_PREFIX_OFFSET, ++ 16, 1, (u8 *)&commit_resp, ret, false); ++ ++ ret = mei_iaf_check_response(dev, &commit_resp.mkhi_header); ++ if (ret) { ++ dev_err(dev, "bad result response from the firmware: 0x%x\n", ++ *(uint32_t *)&commit_resp.mkhi_header); ++ goto end; ++ } ++ dev_dbg(dev, "after check_response\n"); ++ ret = get_error_code(dev, commit_resp.mkhi_header.result); ++ ++end: ++ dev_dbg(dev, "returning with %d\n", ret); ++ mei_cldev_disable(cldev); ++ return ret; ++} ++ ++static const struct i915_iaf_component_ops mei_iaf_ops = { ++ .owner = THIS_MODULE, ++ .commit_svn = mei_iaf_commit_svn, ++}; ++ ++static int mei_component_master_bind(struct device *dev) ++{ ++ int ret; ++ ++ dev_dbg(dev, "mei_iaf_ops addr %p\n", &mei_iaf_ops); ++ ++ ret = component_bind_all(dev, (void *)&mei_iaf_ops); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++static void mei_component_master_unbind(struct device *dev) ++{ ++ dev_dbg(dev, "in %s\n", __func__); ++ component_unbind_all(dev, (void *)&mei_iaf_ops); ++} ++ ++static const struct component_master_ops mei_component_master_ops = { ++ .bind = mei_component_master_bind, ++ .unbind = mei_component_master_unbind, ++}; ++ ++/** ++ * mei_iaf_component_match - compare function for matching mei iaf. ++ * ++ * The function checks if the driver is i915, the subcomponent is IAF ++ * and the parent of iaf and the grand parent of mei_if are the same ++ * i915 device. ++ * ++ * @dev: master device ++ * @subcomponent: subcomponent to match (I915_COMPONENT_IAF) ++ * @data: compare data (mei iaf device) ++ * ++ * Return: ++ * * 1 - if components match ++ * * 0 - otherwise ++ */ ++static int mei_iaf_component_match(struct device *dev, int subcomponent, ++ void *data) ++{ ++ struct device *base = data; ++ ++ dev_dbg(dev, "trying to match %s\n", dev->driver->name); ++ if (subcomponent != I915_COMPONENT_IAF) ++ return 0; ++ ++ if (strcmp(dev->driver->name, "iaf")) ++ return 0; ++ ++ base = base->parent; ++ if (!base) ++ return 0; ++ ++ base = base->parent; ++ dev = dev->parent; ++ ++ return (base && dev && dev == base); ++} ++ ++static int mei_iaf_probe(struct mei_cl_device *cldev, ++ const struct mei_cl_device_id *id) ++{ ++ struct component_match *master_match; ++ int ret; ++ ++ master_match = NULL; ++ component_match_add_typed(&cldev->dev, &master_match, ++ mei_iaf_component_match, &cldev->dev); ++ if (IS_ERR_OR_NULL(master_match)) { ++ ret = -ENOMEM; ++ goto err_exit; ++ } ++ ++ ret = component_master_add_with_match(&cldev->dev, ++ &mei_component_master_ops, ++ master_match); ++ if (ret < 0) { ++ dev_err(&cldev->dev, "Master comp add failed %d\n", ret); ++ goto err_exit; ++ } ++ ++ return 0; ++ ++err_exit: ++ return ret; ++} ++ ++static void mei_iaf_remove(struct mei_cl_device *cldev) ++{ ++ component_master_del(&cldev->dev, &mei_component_master_ops); ++} ++ ++/* fe2af7a6-ef22-4b45-872f-176b0bbc8b43: MCHIF GUID */ ++#define MEI_GUID_MCHIF UUID_LE(0xfe2af7a6, 0xef22, 0x4b45, \ ++ 0x87, 0x2f, 0x17, 0x6b, 0x0b, 0xbc, 0x8b, 0x43) ++ ++static struct mei_cl_device_id mei_iaf_tbl[] = { ++ { .uuid = MEI_GUID_MCHIF, .version = MEI_CL_VERSION_ANY }, ++ { } ++}; ++MODULE_DEVICE_TABLE(mei, mei_iaf_tbl); ++ ++static struct mei_cl_driver mei_iaf_driver = { ++ .id_table = mei_iaf_tbl, ++ .name = KBUILD_MODNAME, ++ .probe = mei_iaf_probe, ++ .remove = mei_iaf_remove, ++}; ++ ++module_mei_cl_driver(mei_iaf_driver); ++ ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MEI IAF"); +diff --git a/drivers/misc/mei/mkhi.h b/drivers/misc/mei/mkhi.h +index 1473ea4896662..63d9f02e7341f 100644 +--- a/drivers/misc/mei/mkhi.h ++++ b/drivers/misc/mei/mkhi.h +@@ -16,6 +16,8 @@ + #define MKHI_GEN_GROUP_ID 0xFF + #define MKHI_GEN_GET_FW_VERSION_CMD 0x2 + ++#define MCHI_GROUP_ID 0xA ++ + #define MKHI_GROUP_ID_GFX 0x30 + #define MKHI_GFX_RESET_WARN_CMD_REQ 0x0 + #define MKHI_GFX_MEMORY_READY_CMD_REQ 0x1 +diff --git a/include/drm/i915_mei_iaf_interface.h b/include/drm/i915_mei_iaf_interface.h +new file mode 100644 +index 0000000000000..dde938dd0ea2c +--- /dev/null ++++ b/include/drm/i915_mei_iaf_interface.h +@@ -0,0 +1,25 @@ ++/* SPDX-License-Identifier: (GPL-2.0+) */ ++/* ++ * Copyright © 2020-2021 Intel Corporation ++ */ ++ ++#ifndef _I915_MEI_IAF_INTERFACE_H_ ++#define _I915_MEI_IAF_INTERFACE_H_ ++ ++#include ++ ++/** ++ * struct i915_iaf_component_ops- ops for IAF services. ++ * @owner: Module providing the ops ++ * @commit_svn: commits current FW SVN ++ */ ++struct i915_iaf_component_ops { ++ /** ++ * @owner: mei_iaf module ++ */ ++ struct module *owner; ++ ++ int (*commit_svn)(const struct device *dev); ++}; ++ ++#endif /* _I915_MEI_IAF_INTERFACE_H_ */ +diff --git a/include/drm/intel/i915_component.h b/include/drm/intel/i915_component.h +index 8082db222e00e..b598f34e6db28 100644 +--- a/include/drm/intel/i915_component.h ++++ b/include/drm/intel/i915_component.h +@@ -32,6 +32,7 @@ enum i915_component_type { + I915_COMPONENT_PXP, + I915_COMPONENT_GSC_PROXY, + INTEL_COMPONENT_LB, ++ I915_COMPONENT_IAF, + }; + + /* MAX_PORT is the number of port +-- +2.43.0 + diff --git a/SPECS/kernel/0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi b/SPECS/kernel/0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi deleted file mode 100644 index 59fc03702..000000000 --- a/SPECS/kernel/0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi +++ /dev/null @@ -1,197 +0,0 @@ -From bfc3da6b878cbec9bfe8034f8e817d0cab069ab5 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 31 Aug 2023 01:52:58 -0700 -Subject: [PATCH 06/44] KVM: VMX: Initialize VMCS FRED fields - -Initialize host VMCS FRED fields with host FRED MSRs' value and -guest VMCS FRED fields to 0. - -FRED CPU state is managed in 9 new FRED MSRs: - IA32_FRED_CONFIG, - IA32_FRED_STKLVLS, - IA32_FRED_RSP0, - IA32_FRED_RSP1, - IA32_FRED_RSP2, - IA32_FRED_RSP3, - IA32_FRED_SSP1, - IA32_FRED_SSP2, - IA32_FRED_SSP3, -as well as a few existing CPU registers and MSRs: - CR4.FRED, - IA32_STAR, - IA32_KERNEL_GS_BASE, - IA32_PL0_SSP (also known as IA32_FRED_SSP0). - -CR4, IA32_KERNEL_GS_BASE and IA32_STAR are already well managed. -Except IA32_FRED_RSP0 and IA32_FRED_SSP0, all other FRED CPU state -MSRs have corresponding VMCS fields in both the host-state and -guest-state areas. So KVM just needs to initialize them, and with -proper VM entry/exit FRED controls, a FRED CPU will keep tracking -host and guest FRED CPU state in VMCS automatically. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Initialize host SSP[1-3] to 0s in vmx_set_constant_host_state() - because Linux doesn't support kernel shadow stacks (Chao Gao). - -Change in v3: -* Use structure kvm_host_values to keep host fred config & stack levels - (Sean Christopherson). - -Changes in v2: -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() to decouple - KVM's capability to virtualize a feature and host's enabling of a - feature (Chao Gao). -* Move guest FRED state init into __vmx_vcpu_reset() (Chao Gao). ---- - arch/x86/include/asm/vmx.h | 32 ++++++++++++++++++++++++++++++ - arch/x86/kvm/vmx/vmx.c | 40 +++++++++++++++++++++++++++++++++++++- - arch/x86/kvm/x86.h | 3 +++ - 3 files changed, 74 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index ab70f52798d4..d222fd1517ce 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -294,12 +294,44 @@ enum vmcs_field { - GUEST_BNDCFGS_HIGH = 0x00002813, - GUEST_IA32_RTIT_CTL = 0x00002814, - GUEST_IA32_RTIT_CTL_HIGH = 0x00002815, -+ GUEST_IA32_FRED_CONFIG = 0x0000281a, -+ GUEST_IA32_FRED_CONFIG_HIGH = 0x0000281b, -+ GUEST_IA32_FRED_RSP1 = 0x0000281c, -+ GUEST_IA32_FRED_RSP1_HIGH = 0x0000281d, -+ GUEST_IA32_FRED_RSP2 = 0x0000281e, -+ GUEST_IA32_FRED_RSP2_HIGH = 0x0000281f, -+ GUEST_IA32_FRED_RSP3 = 0x00002820, -+ GUEST_IA32_FRED_RSP3_HIGH = 0x00002821, -+ GUEST_IA32_FRED_STKLVLS = 0x00002822, -+ GUEST_IA32_FRED_STKLVLS_HIGH = 0x00002823, -+ GUEST_IA32_FRED_SSP1 = 0x00002824, -+ GUEST_IA32_FRED_SSP1_HIGH = 0x00002825, -+ GUEST_IA32_FRED_SSP2 = 0x00002826, -+ GUEST_IA32_FRED_SSP2_HIGH = 0x00002827, -+ GUEST_IA32_FRED_SSP3 = 0x00002828, -+ GUEST_IA32_FRED_SSP3_HIGH = 0x00002829, - HOST_IA32_PAT = 0x00002c00, - HOST_IA32_PAT_HIGH = 0x00002c01, - HOST_IA32_EFER = 0x00002c02, - HOST_IA32_EFER_HIGH = 0x00002c03, - HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04, - HOST_IA32_PERF_GLOBAL_CTRL_HIGH = 0x00002c05, -+ HOST_IA32_FRED_CONFIG = 0x00002c08, -+ HOST_IA32_FRED_CONFIG_HIGH = 0x00002c09, -+ HOST_IA32_FRED_RSP1 = 0x00002c0a, -+ HOST_IA32_FRED_RSP1_HIGH = 0x00002c0b, -+ HOST_IA32_FRED_RSP2 = 0x00002c0c, -+ HOST_IA32_FRED_RSP2_HIGH = 0x00002c0d, -+ HOST_IA32_FRED_RSP3 = 0x00002c0e, -+ HOST_IA32_FRED_RSP3_HIGH = 0x00002c0f, -+ HOST_IA32_FRED_STKLVLS = 0x00002c10, -+ HOST_IA32_FRED_STKLVLS_HIGH = 0x00002c11, -+ HOST_IA32_FRED_SSP1 = 0x00002c12, -+ HOST_IA32_FRED_SSP1_HIGH = 0x00002c13, -+ HOST_IA32_FRED_SSP2 = 0x00002c14, -+ HOST_IA32_FRED_SSP2_HIGH = 0x00002c15, -+ HOST_IA32_FRED_SSP3 = 0x00002c16, -+ HOST_IA32_FRED_SSP3_HIGH = 0x00002c17, - PIN_BASED_VM_EXEC_CONTROL = 0x00004000, - CPU_BASED_VM_EXEC_CONTROL = 0x00004002, - EXCEPTION_BITMAP = 0x00004004, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 24c447935e9b..4a183ffb2154 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1462,6 +1462,15 @@ void vmx_vcpu_load_vmcs(struct kvm_vcpu *vcpu, int cpu) - (unsigned long)(cpu_entry_stack(cpu) + 1)); - } - -+ /* Per-CPU FRED MSRs */ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+#ifdef CONFIG_X86_64 -+ vmcs_write64(HOST_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); -+ vmcs_write64(HOST_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); -+ vmcs_write64(HOST_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); -+#endif -+ } -+ - vmx->loaded_vmcs->cpu = cpu; - } - } -@@ -4370,6 +4379,17 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) - */ - vmcs_write16(HOST_DS_SELECTOR, 0); - vmcs_write16(HOST_ES_SELECTOR, 0); -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+ /* FRED CONFIG and STKLVLS are the same on all CPUs */ -+ vmcs_write64(HOST_IA32_FRED_CONFIG, kvm_host.fred_config); -+ vmcs_write64(HOST_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); -+ -+ /* Linux doesn't support kernel shadow stacks, thus SSPs are 0s */ -+ vmcs_write64(HOST_IA32_FRED_SSP1, 0); -+ vmcs_write64(HOST_IA32_FRED_SSP2, 0); -+ vmcs_write64(HOST_IA32_FRED_SSP3, 0); -+ } - #else - vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS); /* 22.2.4 */ - vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS); /* 22.2.4 */ -@@ -4892,6 +4912,17 @@ static void init_vmcs(struct vcpu_vmx *vmx) - } - - vmx_setup_uret_msrs(vmx); -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, 0); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, 0); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, 0); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, 0); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, 0); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, 0); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, 0); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, 0); -+ } - } - - static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu) -@@ -8722,7 +8753,14 @@ __init int vmx_hardware_setup(void) - */ - if (!static_cpu_has(X86_FEATURE_SELFSNOOP)) - kvm_caps.supported_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; -- kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; -+ -+ kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { -+ rdmsrl(MSR_IA32_FRED_CONFIG, kvm_host.fred_config); -+ rdmsrl(MSR_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); -+ } -+ - return r; - } - -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 41ab002f6471..b12749a2a67c 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -52,6 +52,9 @@ struct kvm_host_values { - u64 xss; - u64 s_cet; - u64 arch_capabilities; -+ -+ u64 fred_config; -+ u64 fred_stklvls; - }; - - void kvm_spurious_fault(void); --- -2.43.0 - diff --git a/SPECS/kernel/0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet b/SPECS/kernel/0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet deleted file mode 100644 index 191b608d9..000000000 --- a/SPECS/kernel/0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet +++ /dev/null @@ -1,145 +0,0 @@ -From 02cbe8cd399a62d4bfc1621d081628a08cb4b136 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:36 -0700 -Subject: [PATCH 06/24] KVM: x86: Introduce KVM_{G,S}ET_ONE_REG uAPIs support - -Enable KVM_{G,S}ET_ONE_REG uAPIs so that userspace can access HW MSR or -KVM synthetic MSR through it. - -In CET KVM series [1], KVM "steals" an MSR from PV MSR space and access -it via KVM_{G,S}ET_MSRs uAPIs, but the approach pollutes PV MSR space -and hides the difference of synthetic MSRs and normal HW defined MSRs. - -Now carve out a separate room in KVM-customized MSR address space for -synthetic MSRs. The synthetic MSRs are not exposed to userspace via -KVM_GET_MSR_INDEX_LIST, instead userspace complies with KVM's setup and -composes the uAPI params. KVM synthetic MSR indices start from 0 and -increase linearly. Userspace caller should tag MSR type correctly in -order to access intended HW or synthetic MSR. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Link: https://lore.kernel.org/all/20240219074733.122080-18-weijiang.yang@intel.com/ [1] -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/uapi/asm/kvm.h | 10 +++++ - arch/x86/kvm/x86.c | 66 +++++++++++++++++++++++++++++++++ - 2 files changed, 76 insertions(+) - -diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h -index 0f15d683817d..e72d9e6c1739 100644 ---- a/arch/x86/include/uapi/asm/kvm.h -+++ b/arch/x86/include/uapi/asm/kvm.h -@@ -411,6 +411,16 @@ struct kvm_xcrs { - __u64 padding[16]; - }; - -+#define KVM_X86_REG_MSR (1 << 2) -+#define KVM_X86_REG_SYNTHETIC (1 << 3) -+ -+struct kvm_x86_reg_id { -+ __u32 index; -+ __u8 type; -+ __u8 rsvd; -+ __u16 rsvd16; -+}; -+ - #define KVM_SYNC_X86_REGS (1UL << 0) - #define KVM_SYNC_X86_SREGS (1UL << 1) - #define KVM_SYNC_X86_EVENTS (1UL << 2) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a6765d1d1741..f2832a644226 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2218,6 +2218,31 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data) - return kvm_set_msr_ignored_check(vcpu, index, *data, true); - } - -+static int kvm_get_one_msr(struct kvm_vcpu *vcpu, u32 msr, u64 __user *value) -+{ -+ u64 val; -+ int r; -+ -+ r = do_get_msr(vcpu, msr, &val); -+ if (r) -+ return r; -+ -+ if (put_user(val, value)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static int kvm_set_one_msr(struct kvm_vcpu *vcpu, u32 msr, u64 __user *value) -+{ -+ u64 val; -+ -+ if (get_user(val, value)) -+ return -EFAULT; -+ -+ return do_set_msr(vcpu, msr, &val); -+} -+ - #ifdef CONFIG_X86_64 - struct pvclock_clock { - int vclock_mode; -@@ -5880,6 +5905,11 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, - } - } - -+static int kvm_translate_synthetic_msr(struct kvm_x86_reg_id *reg) -+{ -+ return -EINVAL; -+} -+ - long kvm_arch_vcpu_ioctl(struct file *filp, - unsigned int ioctl, unsigned long arg) - { -@@ -5996,6 +6026,42 @@ long kvm_arch_vcpu_ioctl(struct file *filp, - srcu_read_unlock(&vcpu->kvm->srcu, idx); - break; - } -+ case KVM_GET_ONE_REG: -+ case KVM_SET_ONE_REG: { -+ struct kvm_x86_reg_id *id; -+ struct kvm_one_reg reg; -+ u64 __user *value; -+ -+ r = -EFAULT; -+ if (copy_from_user(®, argp, sizeof(reg))) -+ break; -+ -+ r = -EINVAL; -+ id = (struct kvm_x86_reg_id *)®.id; -+ if (id->rsvd || id->rsvd16) -+ break; -+ -+ if (id->type != KVM_X86_REG_MSR && -+ id->type != KVM_X86_REG_SYNTHETIC) -+ break; -+ -+ if (id->type == KVM_X86_REG_SYNTHETIC) { -+ r = kvm_translate_synthetic_msr(id); -+ if (r) -+ break; -+ } -+ -+ r = -EINVAL; -+ if (id->type != KVM_X86_REG_MSR) -+ break; -+ -+ value = u64_to_user_ptr(reg.addr); -+ if (ioctl == KVM_GET_ONE_REG) -+ r = kvm_get_one_msr(vcpu, id->index, value); -+ else -+ r = kvm_set_one_msr(vcpu, id->index, value); -+ break; -+ } - case KVM_TPR_ACCESS_REPORTING: { - struct kvm_tpr_access_ctl tac; - --- -2.43.0 - diff --git a/SPECS/kernel/0006-bfs-Reconstruct-file-type-when-loading-from-disk.patch b/SPECS/kernel/0006-bfs-Reconstruct-file-type-when-loading-from-disk.patch deleted file mode 100644 index 63b432ed4..000000000 --- a/SPECS/kernel/0006-bfs-Reconstruct-file-type-when-loading-from-disk.patch +++ /dev/null @@ -1,68 +0,0 @@ -From ee5d4d074c348bbdf9d0ab304ff12a66e74cfa35 Mon Sep 17 00:00:00 2001 -From: Tetsuo Handa -Date: Thu, 23 Oct 2025 22:25:49 +0900 -Subject: [PATCH 06/16] bfs: Reconstruct file type when loading from disk - -syzbot is reporting that S_IFMT bits of inode->i_mode can become bogus when -the S_IFMT bits of the 32bits "mode" field loaded from disk are corrupted -or when the 32bits "attributes" field loaded from disk are corrupted. - -A documentation says that BFS uses only lower 9 bits of the "mode" field. -But I can't find an explicit explanation that the unused upper 23 bits -(especially, the S_IFMT bits) are initialized with 0. - -Therefore, ignore the S_IFMT bits of the "mode" field loaded from disk. -Also, verify that the value of the "attributes" field loaded from disk is -either BFS_VREG or BFS_VDIR (because BFS supports only regular files and -the root directory). - -Reported-by: syzbot+895c23f6917da440ed0d@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=895c23f6917da440ed0d -Signed-off-by: Tetsuo Handa -Link: https://patch.msgid.link/fabce673-d5b9-4038-8287-0fd65d80203b@I-love.SAKURA.ne.jp -Reviewed-by: Tigran Aivazian -Signed-off-by: Christian Brauner ---- - fs/bfs/inode.c | 19 ++++++++++++++++++- - 1 file changed, 18 insertions(+), 1 deletion(-) - -diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c -index 1d41ce477df5..984b365df046 100644 ---- a/fs/bfs/inode.c -+++ b/fs/bfs/inode.c -@@ -61,7 +61,19 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino) - off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; - di = (struct bfs_inode *)bh->b_data + off; - -- inode->i_mode = 0x0000FFFF & le32_to_cpu(di->i_mode); -+ /* -+ * https://martin.hinner.info/fs/bfs/bfs-structure.html explains that -+ * BFS in SCO UnixWare environment used only lower 9 bits of di->i_mode -+ * value. This means that, although bfs_write_inode() saves whole -+ * inode->i_mode bits (which include S_IFMT bits and S_IS{UID,GID,VTX} -+ * bits), middle 7 bits of di->i_mode value can be garbage when these -+ * bits were not saved by bfs_write_inode(). -+ * Since we can't tell whether middle 7 bits are garbage, use only -+ * lower 12 bits (i.e. tolerate S_IS{UID,GID,VTX} bits possibly being -+ * garbage) and reconstruct S_IFMT bits for Linux environment from -+ * di->i_vtype value. -+ */ -+ inode->i_mode = 0x00000FFF & le32_to_cpu(di->i_mode); - if (le32_to_cpu(di->i_vtype) == BFS_VDIR) { - inode->i_mode |= S_IFDIR; - inode->i_op = &bfs_dir_inops; -@@ -71,6 +83,11 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino) - inode->i_op = &bfs_file_inops; - inode->i_fop = &bfs_file_operations; - inode->i_mapping->a_ops = &bfs_aops; -+ } else { -+ brelse(bh); -+ printf("Unknown vtype=%u %s:%08lx\n", -+ le32_to_cpu(di->i_vtype), inode->i_sb->s_id, ino); -+ goto error; - } - - BFS_I(inode)->i_sblock = le32_to_cpu(di->i_sblock); --- -2.43.0 - diff --git a/SPECS/kernel/0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl b/SPECS/kernel/0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl new file mode 100644 index 000000000..5d6bbc4ac --- /dev/null +++ b/SPECS/kernel/0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl @@ -0,0 +1,64 @@ +From 53c4a79d130e27d8bef41e702ef186c8e876cf9b Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Wed, 12 Nov 2025 17:23:24 +0100 +Subject: [PATCH 06/17] cpuidle: governors: teo: Drop redundant function + parameter + +The last no_poll parameter of teo_find_shallower_state() is always +false, so drop it. + +No intentional functional impact. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Tested-by: Christian Loehle +Link: https://patch.msgid.link/2253109.irdbgypaU6@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index cc74cecbea7f4..ada42e2ca7593 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -239,17 +239,15 @@ static bool teo_state_ok(int i, struct cpuidle_driver *drv) + * @dev: Target CPU. + * @state_idx: Index of the capping idle state. + * @duration_ns: Idle duration value to match. +- * @no_poll: Don't consider polling states. + */ + static int teo_find_shallower_state(struct cpuidle_driver *drv, + struct cpuidle_device *dev, int state_idx, +- s64 duration_ns, bool no_poll) ++ s64 duration_ns) + { + int i; + + for (i = state_idx - 1; i >= 0; i--) { +- if (dev->states_usage[i].disable || +- (no_poll && drv->states[i].flags & CPUIDLE_FLAG_POLLING)) ++ if (dev->states_usage[i].disable) + continue; + + state_idx = i; +@@ -459,7 +457,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + * candidate state, a shallower one needs to be found. + */ + if (drv->states[idx].target_residency_ns > duration_ns) +- idx = teo_find_shallower_state(drv, dev, idx, duration_ns, false); ++ idx = teo_find_shallower_state(drv, dev, idx, duration_ns); + + /* + * If the selected state's target residency is below the tick length +@@ -487,7 +485,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + */ + if (idx > idx0 && + drv->states[idx].target_residency_ns > delta_tick) +- idx = teo_find_shallower_state(drv, dev, idx, delta_tick, false); ++ idx = teo_find_shallower_state(drv, dev, idx, delta_tick); + + out_tick: + *stop_tick = false; +-- +2.43.0 + diff --git a/SPECS/kernel/0006-drm-i915-Drop-the-irqs_disabled-check.rt b/SPECS/kernel/0006-drm-i915-Drop-the-irqs_disabled-check.rt deleted file mode 100644 index 2eb58e9c7..000000000 --- a/SPECS/kernel/0006-drm-i915-Drop-the-irqs_disabled-check.rt +++ /dev/null @@ -1,45 +0,0 @@ -From 4b5149296b521b622cfce2240f87c0193c7866bb Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Fri, 1 Oct 2021 20:01:03 +0200 -Subject: [PATCH 6/9] drm/i915: Drop the irqs_disabled() check - -The !irqs_disabled() check triggers on PREEMPT_RT even with -i915_sched_engine::lock acquired. The reason is the lock is transformed -into a sleeping lock on PREEMPT_RT and does not disable interrupts. - -There is no need to check for disabled interrupts. The lockdep -annotation below already check if the lock has been acquired by the -caller and will yell if the interrupts are not disabled. - -Remove the !irqs_disabled() check. - -Reported-by: Maarten Lankhorst -Acked-by: Tvrtko Ursulin -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/i915_request.c | 2 -- - 1 file changed, 2 deletions(-) - -diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c -index b9a2b2194c8f..e24798e4b44e 100644 ---- a/drivers/gpu/drm/i915/i915_request.c -+++ b/drivers/gpu/drm/i915/i915_request.c -@@ -608,7 +608,6 @@ bool __i915_request_submit(struct i915_request *request) - - RQ_TRACE(request, "\n"); - -- GEM_BUG_ON(!irqs_disabled()); - lockdep_assert_held(&engine->sched_engine->lock); - - /* -@@ -717,7 +716,6 @@ void __i915_request_unsubmit(struct i915_request *request) - */ - RQ_TRACE(request, "\n"); - -- GEM_BUG_ON(!irqs_disabled()); - lockdep_assert_held(&engine->sched_engine->lock); - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt b/SPECS/kernel/0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt new file mode 100644 index 000000000..ec8285db2 --- /dev/null +++ b/SPECS/kernel/0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt @@ -0,0 +1,35 @@ +From 7c2b752beb65a449e5bc914881ae5631168ca110 Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Tue, 3 Oct 2023 21:37:21 +0200 +Subject: [PATCH 6/9] drm/i915/guc: Consider also RCU depth in busy loop. + +intel_guc_send_busy_loop() looks at in_atomic() and irqs_disabled() to +decide if it should busy-spin while waiting or if it may sleep. +Both checks will report false on PREEMPT_RT if sleeping spinlocks are +acquired leading to RCU splats while the function sleeps. + +Check also if RCU has been disabled. + +Reported-by: "John B. Wyatt IV" +Reviewed-by: Rodrigo Vivi +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h +index 053780f562c1a..b25fa8f4dc4bd 100644 +--- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h ++++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h +@@ -362,7 +362,7 @@ static inline int intel_guc_send_busy_loop(struct intel_guc *guc, + { + int err; + unsigned int sleep_period_ms = 1; +- bool not_atomic = !in_atomic() && !irqs_disabled(); ++ bool not_atomic = !in_atomic() && !irqs_disabled() && !rcu_preempt_depth(); + + /* + * FIXME: Have caller pass in if we are in an atomic context to avoid +-- +2.43.0 + diff --git a/SPECS/kernel/0006-i2c-atr-Remove-COMPILE_TEST-check.ipu b/SPECS/kernel/0006-i2c-atr-Remove-COMPILE_TEST-check.ipu deleted file mode 100644 index d63a16b20..000000000 --- a/SPECS/kernel/0006-i2c-atr-Remove-COMPILE_TEST-check.ipu +++ /dev/null @@ -1,34 +0,0 @@ -From 01ef232671beb365ed72d760c4f5aac0f1249cf8 Mon Sep 17 00:00:00 2001 -From: "Yew, Chang Ching" -Date: Wed, 21 Jan 2026 02:59:04 +0800 -Subject: [PATCH 6/6] i2c: atr: Remove COMPILE_TEST check - -The I2C Address Translator (ATR) driver was previously only visible when -COMPILE_TEST was enabled. Since the driver is functional and can be built -normally, drop the `if COMPILE_TEST` condition so that the ATR option is -always available in Kconfig. - -This allows platforms and drivers that actually use ATR hardware to enable -I2C ATR without relying on COMPILE_TEST. - -Signed-off-by: Yew, Chang Ching ---- - drivers/i2c/Kconfig | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig -index c232054fddd6..a7450ea9de41 100644 ---- a/drivers/i2c/Kconfig -+++ b/drivers/i2c/Kconfig -@@ -64,7 +64,7 @@ config I2C_MUX - source "drivers/i2c/muxes/Kconfig" - - config I2C_ATR -- tristate "I2C Address Translator (ATR) support" if COMPILE_TEST -+ tristate "I2C Address Translator (ATR) support" - help - Enable support for I2C Address Translator (ATR) chips. - --- -2.43.0 - diff --git a/SPECS/kernel/0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c b/SPECS/kernel/0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c deleted file mode 100644 index 233c0ab93..000000000 --- a/SPECS/kernel/0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c +++ /dev/null @@ -1,85 +0,0 @@ -From 780fd3c28a580c2657cfa58a430837220abf55e7 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:05 +0300 -Subject: [PATCH 06/11] i3c: mipi-i3c-hci: Change interrupt status prints to - dev_dbg() - -Change interrupt status prints from local DBG() macro to dev_dbg() in -order to make it easier to enable them without needing to recompile code -with DEBUG defined. - -While doing so, spell out the status register names as they are in the -specification to make it easier to differentiate between different -interrupt status registers. - -Since dynamic debug prints can include the line number remove the "(in)" -and "(out)" markers from the PIO interrupt status prints. - -Prefix the ring interrupt status print using "Ring %d" instead of "rh%d" -to make it uniform across all other prints showing the ring number. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250827103009.243771-2-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 2 +- - drivers/i3c/master/mipi-i3c-hci/dma.c | 3 ++- - drivers/i3c/master/mipi-i3c-hci/pio.c | 7 ++++--- - 3 files changed, 7 insertions(+), 5 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index 7a467ef65787..d532933ac7ab 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -553,7 +553,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) - - val = reg_read(INTR_STATUS); - reg_write(INTR_STATUS, val); -- DBG("INTR_STATUS = %#x", val); -+ dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); - - if (val) - result = IRQ_HANDLED; -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 09688ada4912..f5f5ab4db172 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -760,7 +760,8 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - - rh = &rings->headers[i]; - status = rh_reg_read(INTR_STATUS); -- DBG("rh%d status: %#x", i, status); -+ dev_dbg(&hci->master.dev, "Ring %d: RH_INTR_STATUS %#x", -+ i, status); - if (!status) - continue; - rh_reg_write(INTR_STATUS, status); -diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c -index 2fc71e696911..cde883137bc7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/pio.c -+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c -@@ -986,7 +986,8 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci) - - spin_lock(&pio->lock); - status = pio_reg_read(INTR_STATUS); -- DBG("(in) status: %#x/%#x", status, pio->enabled_irqs); -+ dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x", -+ status, pio->enabled_irqs); - status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS; - if (!status) { - spin_unlock(&pio->lock); -@@ -1023,8 +1024,8 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci) - pio->enabled_irqs &= ~STAT_CMD_QUEUE_READY; - - pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs); -- DBG("(out) status: %#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x", -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); - spin_unlock(&pio->lock); - return true; - } --- -2.43.0 - diff --git a/SPECS/kernel/0006-issei-host_client-add-dma-allocation-support.security b/SPECS/kernel/0006-issei-host_client-add-dma-allocation-support.security new file mode 100644 index 000000000..adfea3980 --- /dev/null +++ b/SPECS/kernel/0006-issei-host_client-add-dma-allocation-support.security @@ -0,0 +1,151 @@ +From 2b5fd680f307eaec54d036dc29e7d70316e82040 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Wed, 17 Dec 2025 09:31:31 +0200 +Subject: [PATCH 6/7] issei: host_client: add dma allocation support + +Add functions to allow dedicated DMA buffer allocated on current +hardware device per host client. + +Signed-off-by: Alexander Usyskin +--- + drivers/misc/issei/host_client.c | 55 ++++++++++++++++++++++++++++++++ + drivers/misc/issei/host_client.h | 13 ++++++++ + 2 files changed, 68 insertions(+) + +diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_client.c +index 6b8dbe566f5f3..1c85d4ee05c0d 100644 +--- a/drivers/misc/issei/host_client.c ++++ b/drivers/misc/issei/host_client.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -12,6 +13,8 @@ + #include "host_client.h" + #include "fw_client.h" + ++#define ISSEI_HOST_DMA_MAX_SIZE SZ_32M ++ + static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) + { + return cl->fw_cl ? cl->fw_cl->id : 0; +@@ -104,6 +107,18 @@ static void __issei_cl_disconnect(struct issei_device *idev, struct issei_host_c + cl_dbg(idev, cl, "Disconnected\n"); + } + ++static void __issei_cl_dma_unmap(struct issei_host_client *cl) ++{ ++ if (!cl->dma_size) ++ return; ++ ++ dmam_free_coherent(cl->idev->parent, ++ cl->dma_size, cl->dma_vaddr, cl->dma_daddr); ++ cl->dma_size = 0; ++ cl->dma_vaddr = NULL; ++ cl->dma_daddr = 0; ++} ++ + static void issei_cl_init(struct issei_host_client *cl, struct issei_device *idev, + u16 id, struct file *fp) + { +@@ -179,6 +194,8 @@ void issei_cl_remove(struct issei_host_client *cl) + + __issei_cl_disconnect(idev, cl); + ++ __issei_cl_dma_unmap(cl); ++ + cl_dbg(idev, cl, "Removed\n"); + kfree(cl); + } +@@ -487,3 +504,41 @@ int issei_cl_check_write(struct issei_host_client *cl) + return 1; + return 0; + } ++ ++int issei_cl_dma_map(struct issei_host_client *cl, size_t size, ++ dma_addr_t *daddr, void **vaddr) ++{ ++ struct issei_device *idev = cl->idev; ++ ++ if (size == 0 || size > ISSEI_HOST_DMA_MAX_SIZE) { ++ cl_err(idev, cl, "The size is out of bounds."); ++ return -EINVAL; ++ } ++ ++ guard(mutex)(&idev->host_client_lock); ++ ++ if (cl->dma_size) { ++ cl_err(idev, cl, "DMA already allocated."); ++ return -EALREADY; ++ } ++ ++ cl->dma_vaddr = dmam_alloc_coherent(idev->parent, size, ++ &cl->dma_daddr, GFP_KERNEL); ++ if (!cl->dma_vaddr) ++ return -ENOMEM; ++ ++ cl->dma_size = size; ++ ++ *daddr = cl->dma_daddr; ++ *vaddr = cl->dma_vaddr; ++ return 0; ++} ++ ++void issei_cl_dma_unmap(struct issei_host_client *cl) ++{ ++ struct issei_device *idev = cl->idev; ++ ++ guard(mutex)(&idev->host_client_lock); ++ ++ __issei_cl_dma_unmap(cl); ++} +diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_client.h +index 9f081bed190d2..ceee45b20143a 100644 +--- a/drivers/misc/issei/host_client.h ++++ b/drivers/misc/issei/host_client.h +@@ -3,6 +3,7 @@ + #ifndef _ISSEI_HOST_CLIENT_H_ + #define _ISSEI_HOST_CLIENT_H_ + ++#include + #include + #include + #include +@@ -32,6 +33,10 @@ enum issei_host_client_state { + * @read_wait: waitqueue for read object + * @read_data: received data pointer + * @read_data_size: received data size ++ * ++ * @dma_vaddr: allocated DMA buffer virtual address ++ * @dma_daddr: allocated DMA buffer physical address ++ * @dma_size: allocated DMA buffer size, zero if no buffer allocated + */ + struct issei_host_client { + struct list_head list; +@@ -48,6 +53,10 @@ struct issei_host_client { + wait_queue_head_t read_wait; + u8 *read_data; + size_t read_data_size; ++ ++ void *dma_vaddr; ++ dma_addr_t dma_daddr; ++ size_t dma_size; + }; + + struct issei_host_client *issei_cl_create(struct issei_device *idev, struct file *fp); +@@ -67,4 +76,8 @@ int issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t *buf_size); + int issei_cl_check_read(struct issei_host_client *cl); + int issei_cl_check_write(struct issei_host_client *cl); + ++int issei_cl_dma_map(struct issei_host_client *cl, size_t size, ++ dma_addr_t *daddr, void **vaddr); ++void issei_cl_dma_unmap(struct issei_host_client *cl); ++ + #endif /* ISSEI_HOST_CLIENT_H_ */ +-- +2.43.0 + diff --git a/SPECS/kernel/0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu b/SPECS/kernel/0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu deleted file mode 100644 index c360df904..000000000 --- a/SPECS/kernel/0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu +++ /dev/null @@ -1,43 +0,0 @@ -From 7d518c359a265f77443e1282c38fa576dc8a2fc2 Mon Sep 17 00:00:00 2001 -From: Bingbu Cao -Date: Thu, 20 Mar 2025 16:41:37 +0800 -Subject: [PATCH 06/11] media: ipu: Dma sync at buffer_prepare callback as DMA - is non-coherent - -Test Platform: -PTLRVP -LNLRVP - -Signed-off-by: Bingbu Cao -Signed-off-by: zouxiaoh ---- - drivers/media/pci/intel/ipu7/ipu7-isys-queue.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -index 9cee0fb4440c..43955c6fa8cf 100644 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys-queue.c -@@ -92,7 +92,9 @@ static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, - static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - { - struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); - struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); - struct device *dev = &av->isys->adev->auxdev.dev; - u32 bytesperline = av->pix_fmt.bytesperline; - u32 height = av->pix_fmt.height; -@@ -107,6 +109,9 @@ static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - av->vdev.name, bytesperline, height); - vb2_set_plane_payload(vb, 0, bytesperline * height); - -+ /* assume IPU is not DMA coherent */ -+ ipu7_dma_sync_sgtable(isys->adev, sg); -+ - return 0; - } - --- -2.43.0 - diff --git a/SPECS/kernel/0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss b/SPECS/kernel/0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss deleted file mode 100644 index 51ec033aa..000000000 --- a/SPECS/kernel/0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss +++ /dev/null @@ -1,45 +0,0 @@ -From 930d67dbe1c6fb97824f857ee19d81e7aea7a221 Mon Sep 17 00:00:00 2001 -From: Aapo Vienamo -Date: Thu, 7 Mar 2024 15:04:18 +0200 -Subject: [PATCH 06/16] mtd: core: Don't fail mtd_device_parse_register() if - OTP is unsupported - -Handle the case where -EOPNOTSUPP is returned from OTP driver. - -This addresses an issue that occurs with the Intel SPI flash controller, -which has a limited supported opcode set. Whilst the OTP functionality -is not available due to this restriction, other parts of the MTD -functionality of the device are intact. This change allows the driver -to gracefully handle the restriction by allowing the supported -functionality to remain available instead of failing the probe -altogether. - -Signed-off-by: Aapo Vienamo -Reviewed-by: Mika Westerberg ---- - drivers/mtd/mtdcore.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c -index 5ba9a741f5ac..2739d5a555c8 100644 ---- a/drivers/mtd/mtdcore.c -+++ b/drivers/mtd/mtdcore.c -@@ -1060,8 +1060,14 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types, - - mtd_set_dev_defaults(mtd); - -+ /* -+ * Don't abort MTD init if OTP functionality is unsupported. The -+ * cleanup of the OTP init is contained within mtd_otp_nvmem_add(). -+ * Omitting goto out here is safe since the cleanup code there -+ * should be no-ops. -+ */ - ret = mtd_otp_nvmem_add(mtd); -- if (ret) -+ if (ret && ret != -EOPNOTSUPP) - goto out; - - if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) { --- -2.43.0 - diff --git a/SPECS/kernel/0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet b/SPECS/kernel/0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet new file mode 100644 index 000000000..469920aab --- /dev/null +++ b/SPECS/kernel/0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet @@ -0,0 +1,68 @@ +From 7d8fc568184270a121d0aa1aab5d2efe706359d7 Mon Sep 17 00:00:00 2001 +From: Song Yoong Siang +Date: Tue, 2 Aug 2022 22:57:40 +0800 +Subject: [PATCH 06/18] net: stmmac: Adjust mac_capabilities for Intel mGbE + 2.5G mode + +In the case where kernel driver has no access to modify the clock rate +after it is increased by 2.5 times in the BIOS to support 2.5G mode, +link speeds other than 2.5Gbps are not supported. Therefore, this commit +remove 10 Mbps, 100 Mbps, and 1Gbps support from mac_capabilities list. + +Signed-off-by: Tan Tee Min +Signed-off-by: Song Yoong Siang +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 2 ++ + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++++ + include/linux/stmmac.h | 1 + + 3 files changed, 12 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index e74d00984b889..e2915815ee655 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -300,8 +300,10 @@ static void tgl_get_interfaces(struct stmmac_priv *priv, void *bsp_priv, + dev_info(priv->device, "Link Speed Mode: 2.5Gbps\n"); + priv->plat->mdio_bus_data->default_an_inband = false; + interface = PHY_INTERFACE_MODE_2500BASEX; ++ priv->plat->fixed_2G5_clock_rate = true; + } else { + interface = PHY_INTERFACE_MODE_SGMII; ++ priv->plat->fixed_2G5_clock_rate = false; + } + + __set_bit(interface, interfaces); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 2cd7548edd82f..f4b06854653fd 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1255,6 +1255,15 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) + if (!fwnode) + fwnode = dev_fwnode(priv->device); + ++ /* In the case where kernel driver has no access to modify the clock ++ * rate after it is increased by 2.5 times in the BIOS to support 2.5G ++ * mode, link speeds other than 2.5Gbps are not supported. Thus, remove ++ * them from mac_capabilities. ++ */ ++ if (priv->plat->fixed_2G5_clock_rate && priv->plat->max_speed == 2500) ++ priv->phylink_config.mac_capabilities &= ++ ~(MAC_10 | MAC_100 | MAC_1000); ++ + phylink = phylink_create(config, fwnode, priv->plat->phy_interface, + &stmmac_phylink_mac_ops); + if (IS_ERR(phylink)) +diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h +index d7d6998fa5bd0..323021322a770 100644 +--- a/include/linux/stmmac.h ++++ b/include/linux/stmmac.h +@@ -300,5 +300,6 @@ struct plat_stmmacenet_data { + const struct dwmac4_addrs *dwmac4_addrs; + unsigned int flags; + bool skip_reset; ++ bool fixed_2G5_clock_rate; + }; + #endif +-- +2.43.0 + diff --git a/SPECS/kernel/0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu b/SPECS/kernel/0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu deleted file mode 100644 index 49ad47434..000000000 --- a/SPECS/kernel/0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu +++ /dev/null @@ -1,112 +0,0 @@ -From ff009ddd8692d77eae757b9d5b593b3fdabdf524 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:38:44 +0800 -Subject: [PATCH 06/21] patch: staging add enable ENABLE_FW_OFFLINE_LOGGER - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-fw-isys.c | 59 +++++++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-fw-isys.h | 3 ++ - drivers/staging/media/ipu7/ipu7-isys.c | 4 ++ - 3 files changed, 66 insertions(+) - -diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c -index c98326bd9fee..2958e39a359e 100644 ---- a/drivers/staging/media/ipu7/ipu7-fw-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c -@@ -205,6 +205,65 @@ void ipu7_fw_isys_put_resp(struct ipu7_isys *isys) - ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE); - } - -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys) -+{ -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ struct device *dev = &isys->adev->auxdev.dev; -+ struct isys_fw_log *fw_log = isys->fw_log; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ void *token; -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ -+ token = ipu7_syscom_get_token(isys->adev->syscom, -+ IPU_INSYS_OUTPUT_LOG_QUEUE); -+ }; -+ -+ return 0; -+} -+ -+#endif - void ipu7_fw_isys_dump_stream_cfg(struct device *dev, - struct ipu7_insys_stream_cfg *cfg) - { -diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.h b/drivers/staging/media/ipu7/ipu7-fw-isys.h -index b556feda6b08..1235adc9694e 100644 ---- a/drivers/staging/media/ipu7/ipu7-fw-isys.h -+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.h -@@ -36,4 +36,7 @@ int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys, - size_t size, u16 send_type); - struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys); - void ipu7_fw_isys_put_resp(struct ipu7_isys *isys); -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+int ipu7_fw_isys_get_log(struct ipu7_isys *isys); -+#endif - #endif -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index 547d5fd6a036..dae8700dd7f5 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -1224,6 +1224,10 @@ int isys_isr_one(struct ipu7_bus_device *adev) - if (!isys->adev->syscom) - return 1; - -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_isys_get_log(isys); -+#endif -+ - resp = ipu7_fw_isys_get_resp(isys); - if (!resp) - return 1; --- -2.43.0 - diff --git a/SPECS/kernel/0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf b/SPECS/kernel/0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf deleted file mode 100644 index e18272382..000000000 --- a/SPECS/kernel/0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf +++ /dev/null @@ -1,78 +0,0 @@ -From 9ac64a4fcc7659344116d6b150578c42b75e032e Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 11 Aug 2025 17:00:32 +0800 -Subject: [PATCH 006/100] perf/x86: Add PERF_CAP_PEBS_TIMING_INFO flag - -IA32_PERF_CAPABILITIES.PEBS_TIMING_INFO[bit 17] is introduced to -indicate whether timed PEBS is supported. Timed PEBS adds a new "retired -latency" field in basic info group to show the timing info. Please find -detailed information about timed PEBS in section 8.4.1 "Timed Processor -Event Based Sampling" of "Intel Architecture Instruction Set Extensions -and Future Features". - -This patch adds PERF_CAP_PEBS_TIMING_INFO flag and KVM module leverages -this flag to expose timed PEBS feature to guest. - -Moreover, opportunistically refine the indents and make the macros -share consistent indents. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - arch/x86/include/asm/msr-index.h | 14 ++++++++------ - tools/arch/x86/include/asm/msr-index.h | 14 ++++++++------ - 2 files changed, 16 insertions(+), 12 deletions(-) - -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index b65c3ba5fa14..f627196eb796 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -315,12 +315,14 @@ - #define PERF_CAP_PT_IDX 16 - - #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 --#define PERF_CAP_PEBS_TRAP BIT_ULL(6) --#define PERF_CAP_ARCH_REG BIT_ULL(7) --#define PERF_CAP_PEBS_FORMAT 0xf00 --#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) --#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -- PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE) -+#define PERF_CAP_PEBS_TRAP BIT_ULL(6) -+#define PERF_CAP_ARCH_REG BIT_ULL(7) -+#define PERF_CAP_PEBS_FORMAT 0xf00 -+#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) -+#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17) -+#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -+ PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \ -+ PERF_CAP_PEBS_TIMING_INFO) - - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) -diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h -index 5cfb5d74dd5f..daebfd926f08 100644 ---- a/tools/arch/x86/include/asm/msr-index.h -+++ b/tools/arch/x86/include/asm/msr-index.h -@@ -315,12 +315,14 @@ - #define PERF_CAP_PT_IDX 16 - - #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 --#define PERF_CAP_PEBS_TRAP BIT_ULL(6) --#define PERF_CAP_ARCH_REG BIT_ULL(7) --#define PERF_CAP_PEBS_FORMAT 0xf00 --#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) --#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -- PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE) -+#define PERF_CAP_PEBS_TRAP BIT_ULL(6) -+#define PERF_CAP_ARCH_REG BIT_ULL(7) -+#define PERF_CAP_PEBS_FORMAT 0xf00 -+#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) -+#define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17) -+#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -+ PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \ -+ PERF_CAP_PEBS_TIMING_INFO) - - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) --- -2.43.0 - diff --git a/SPECS/kernel/0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf b/SPECS/kernel/0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf new file mode 100644 index 000000000..0b1b552c4 --- /dev/null +++ b/SPECS/kernel/0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf @@ -0,0 +1,157 @@ +From 52e51ae23156389c49671eb14ab8a3cb224bd281 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 19 Nov 2025 12:54:50 -0800 +Subject: [PATCH 06/13] perf/x86/intel/uncore: Add CBB PMON support for Diamond + Rapids +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +On DMR, PMON units inside the Core Building Block (CBB) are enumerated +separately from those in the Integrated Memory and I/O Hub (IMH). + +A new per-CBB MSR (0x710) is introduced for discovery table enumeration. + +For counter control registers, the tid_en bit (bit 16) exists on CBO, +SBO, and Santa, but it is not used by any events. Mark this bit as +reserved. + +Similarly, disallow extended umask (bits 32–63) on Santa and sNCU. + +Additionally, ignore broken SB2UCIE unit. + +Reviewed-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 2 + + arch/x86/events/intel/uncore.h | 1 + + arch/x86/events/intel/uncore_discovery.h | 2 + + arch/x86/events/intel/uncore_snbep.c | 52 ++++++++++++++++++++++-- + 4 files changed, 54 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index c07d2c88604ef..eb57eb7159959 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1837,6 +1837,8 @@ static const struct uncore_plat_init dmr_uncore_init __initconst = { + .domain[0].base_is_pci = true, + .domain[0].discovery_base = DMR_UNCORE_DISCOVERY_TABLE_DEVICE, + .domain[0].units_ignore = dmr_uncore_imh_units_ignore, ++ .domain[1].discovery_base = CBB_UNCORE_DISCOVERY_MSR, ++ .domain[1].units_ignore = dmr_uncore_cbb_units_ignore, + }; + + static const struct uncore_plat_init generic_uncore_init __initconst = { +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 1e4b3a22403c5..83d01a9cefc00 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -615,6 +615,7 @@ extern struct event_constraint uncore_constraint_empty; + extern int spr_uncore_units_ignore[]; + extern int gnr_uncore_units_ignore[]; + extern int dmr_uncore_imh_units_ignore[]; ++extern int dmr_uncore_cbb_units_ignore[]; + + /* uncore_snb.c */ + int snb_uncore_pci_init(void); +diff --git a/arch/x86/events/intel/uncore_discovery.h b/arch/x86/events/intel/uncore_discovery.h +index 618788c30ac62..63b8f7634e42e 100644 +--- a/arch/x86/events/intel/uncore_discovery.h ++++ b/arch/x86/events/intel/uncore_discovery.h +@@ -2,6 +2,8 @@ + + /* Store the full address of the global discovery table */ + #define UNCORE_DISCOVERY_MSR 0x201e ++/* Base address of uncore perfmon discovery table for CBB domain */ ++#define CBB_UNCORE_DISCOVERY_MSR 0x710 + + /* Generic device ID of a discovery table device */ + #define UNCORE_DISCOVERY_TABLE_DEVICE 0x09a7 +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index 4b72560dc13ff..df173534637a6 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -6807,6 +6807,28 @@ static struct intel_uncore_type dmr_uncore_hamvf = { + .attr_update = uncore_alias_groups, + }; + ++static struct intel_uncore_type dmr_uncore_cbo = { ++ .name = "cbo", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_santa = { ++ .name = "santa", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_cncu = { ++ .name = "cncu", ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_sncu = { ++ .name = "sncu", ++ .attr_update = uncore_alias_groups, ++}; ++ + static struct intel_uncore_type dmr_uncore_ula = { + .name = "ula", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, +@@ -6814,6 +6836,20 @@ static struct intel_uncore_type dmr_uncore_ula = { + .attr_update = uncore_alias_groups, + }; + ++static struct intel_uncore_type dmr_uncore_dda = { ++ .name = "dda", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ ++static struct intel_uncore_type dmr_uncore_sbo = { ++ .name = "sbo", ++ .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .format_group = &dmr_sca_uncore_format_group, ++ .attr_update = uncore_alias_groups, ++}; ++ + static struct intel_uncore_type dmr_uncore_ubr = { + .name = "ubr", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, +@@ -6902,10 +6938,15 @@ static struct intel_uncore_type *dmr_uncores[UNCORE_DMR_NUM_UNCORE_TYPES] = { + NULL, NULL, NULL, + NULL, NULL, + &dmr_uncore_hamvf, +- NULL, +- NULL, NULL, NULL, ++ &dmr_uncore_cbo, ++ &dmr_uncore_santa, ++ &dmr_uncore_cncu, ++ &dmr_uncore_sncu, + &dmr_uncore_ula, +- NULL, NULL, NULL, NULL, ++ &dmr_uncore_dda, ++ NULL, ++ &dmr_uncore_sbo, ++ NULL, + NULL, NULL, NULL, + &dmr_uncore_ubr, + NULL, +@@ -6923,6 +6964,11 @@ int dmr_uncore_imh_units_ignore[] = { + UNCORE_IGNORE_END + }; + ++int dmr_uncore_cbb_units_ignore[] = { ++ 0x25, /* SB2UCIE */ ++ UNCORE_IGNORE_END ++}; ++ + int dmr_uncore_pci_init(void) + { + uncore_pci_uncores = uncore_get_uncores(UNCORE_ACCESS_PCI, 0, NULL, +-- +2.43.0 + diff --git a/SPECS/kernel/0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt b/SPECS/kernel/0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt new file mode 100644 index 000000000..499e48f8b --- /dev/null +++ b/SPECS/kernel/0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt @@ -0,0 +1,83 @@ +From cb1c72d420130019bdebc3528bc25a867b94f87a Mon Sep 17 00:00:00 2001 +From: Xi Pardee +Date: Wed, 5 Nov 2025 13:50:15 -0800 +Subject: [PATCH 6/6] platform/x86:intel/pmc: Enable SSRAM support for Wildcat + Lake +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Enable Wildcat Lake platforms to achieve PMC information from +Intel PMC SSRAM Telemetry driver and substate requirements data +from telemetry region. + +Signed-off-by: Xi Pardee +Link: https://patch.msgid.link/20251105215020.1984036-2-xi.pardee@linux.intel.com +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +--- + drivers/platform/x86/intel/pmc/core.h | 2 ++ + drivers/platform/x86/intel/pmc/wcl.c | 18 ++++++++++++++++++ + 2 files changed, 20 insertions(+) + +diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h +index 61c8d3c5faa0f..272fb4f57f346 100644 +--- a/drivers/platform/x86/intel/pmc/core.h ++++ b/drivers/platform/x86/intel/pmc/core.h +@@ -304,6 +304,8 @@ enum ppfear_regs { + /* Wildcat Lake */ + #define WCL_PMC_LTR_RESERVED 0x1B64 + #define WCL_PCD_PMC_MMIO_REG_LEN 0x3178 ++#define WCL_NUM_S0IX_BLOCKER 94 ++#define WCL_BLK_REQ_OFFSET 50 + + /* SSRAM PMC Device ID */ + /* LNL */ +diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c +index 85e90a639e651..a45707e6364f2 100644 +--- a/drivers/platform/x86/intel/pmc/wcl.c ++++ b/drivers/platform/x86/intel/pmc/wcl.c +@@ -11,6 +11,9 @@ + + #include "core.h" + ++/* PMC SSRAM PMT Telemetry GUIDS */ ++#define PCDN_LPM_REQ_GUID 0x33747648 ++ + static const struct pmc_bit_map wcl_pcdn_pfear_map[] = { + {"PMC_0", BIT(0)}, + {"FUSE_OSSE", BIT(1)}, +@@ -453,6 +456,17 @@ static const struct pmc_reg_map wcl_pcdn_reg_map = { + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = wcl_pcdn_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, ++ .num_s0ix_blocker = WCL_NUM_S0IX_BLOCKER, ++ .blocker_req_offset = WCL_BLK_REQ_OFFSET, ++ .lpm_req_guid = PCDN_LPM_REQ_GUID, ++}; ++ ++static struct pmc_info wcl_pmc_info_list[] = { ++ { ++ .devid = PMC_DEVID_WCL_PCDN, ++ .map = &wcl_pcdn_reg_map, ++ }, ++ {} + }; + + #define WCL_NPU_PCI_DEV 0xfd3e +@@ -479,8 +493,12 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in + } + + struct pmc_dev_info wcl_pmc_dev = { ++ .pci_func = 2, ++ .regmap_list = wcl_pmc_info_list, + .map = &wcl_pcdn_reg_map, ++ .sub_req_show = &pmc_core_substate_blk_req_fops, + .suspend = cnl_suspend, + .resume = wcl_resume, + .init = wcl_core_init, ++ .sub_req = pmc_core_pmt_get_blk_sub_req, + }; +-- +2.43.0 + diff --git a/SPECS/kernel/0006-rtnetlink-Add-return-value-check.ethernet b/SPECS/kernel/0006-rtnetlink-Add-return-value-check.ethernet deleted file mode 100644 index d52d1b9b2..000000000 --- a/SPECS/kernel/0006-rtnetlink-Add-return-value-check.ethernet +++ /dev/null @@ -1,36 +0,0 @@ -From d3e468e980806df6fb6e9e823ce703cdc3d5a65a Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Tue, 3 Aug 2021 08:49:34 +0800 -Subject: [PATCH 06/19] rtnetlink: Add return value check - -This patch add return value checking for both of the nla_put_u32() and -nla_put_u8() in rtnl_xdp_fill(). - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - net/core/rtnetlink.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c -index e4716ed7643b..39d7c7c2ba26 100644 ---- a/net/core/rtnetlink.c -+++ b/net/core/rtnetlink.c -@@ -1771,8 +1771,12 @@ static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) - if (!md_btf_id) - goto err_cancel; - -- nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); -- nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); -+ err = nla_put_u32(skb, IFLA_XDP_MD_BTF_ID, md_btf_id); -+ if (err) -+ goto err_cancel; -+ err = nla_put_u8(skb, IFLA_XDP_MD_BTF_STATE, md_btf_enabled); -+ if (err) -+ goto err_cancel; - - nla_nest_end(skb, xdp); - return 0; --- -2.43.0 - diff --git a/SPECS/kernel/0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal b/SPECS/kernel/0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal deleted file mode 100644 index 8f67d3762..000000000 --- a/SPECS/kernel/0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal +++ /dev/null @@ -1,44 +0,0 @@ -From 46f7fde538aaa55443ee44c23e5fd9596f09cf6b Mon Sep 17 00:00:00 2001 -From: "Rafael J. Wysocki" -Date: Mon, 25 Aug 2025 15:27:46 +0200 -Subject: [PATCH 06/11] thermal: gov_step_wise: Clean up local variable - initialization - -Make the initialization of local variable throttle in -thermal_zone_trip_update() more straightforward. - -No intentional functional impact. - -Signed-off-by: Rafael J. Wysocki -Reviewed-by: Lukasz Luba -Link: https://patch.msgid.link/6203592.lOV4Wx5bFT@rafael.j.wysocki ---- - drivers/thermal/gov_step_wise.c | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c -index b7938bddd9a6..44945af3ce17 100644 ---- a/drivers/thermal/gov_step_wise.c -+++ b/drivers/thermal/gov_step_wise.c -@@ -82,16 +82,14 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, - const struct thermal_trip_desc *td, - int trip_threshold) - { -+ bool throttle = tz->temperature >= trip_threshold; - const struct thermal_trip *trip = &td->trip; - enum thermal_trend trend = get_tz_trend(tz, trip); - int trip_id = thermal_zone_trip_id(tz, trip); - struct thermal_instance *instance; -- bool throttle = false; - -- if (tz->temperature >= trip_threshold) { -- throttle = true; -+ if (throttle) - trace_thermal_zone_trip(tz, trip_id, trip->type); -- } - - dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d\n", - trip_id, trip->type, trip_threshold, trend, throttle); --- -2.43.0 - diff --git a/SPECS/kernel/0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet b/SPECS/kernel/0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet new file mode 100644 index 000000000..1c978d298 --- /dev/null +++ b/SPECS/kernel/0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet @@ -0,0 +1,83 @@ +From 03189c50b96902c65b0a7f1bd39a3b720c0755ae Mon Sep 17 00:00:00 2001 +From: Saeed Mahameed +Date: Tue, 9 Apr 2019 14:52:02 -0700 +Subject: [PATCH 06/14] tools/bpf: Query XDP metadata BTF ID + +When dumping bpf net information, also query XDP MD BTF attributes: + +$ /usr/local/sbin/bpftool net +xdp: +mlx0(3) md_btf_id(1) md_btf_enabled(0) + +tc: + +flow_dissector: + +Signed-off-by: Saeed Mahameed +Signed-off-by: Aravindhan Gunasekaran +--- + tools/bpf/bpftool/netlink_dumper.c | 21 +++++++++++++++++---- + tools/include/uapi/linux/if_link.h | 2 ++ + 2 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/tools/bpf/bpftool/netlink_dumper.c b/tools/bpf/bpftool/netlink_dumper.c +index 0a3c7e96c797a..949779c3f9eeb 100644 +--- a/tools/bpf/bpftool/netlink_dumper.c ++++ b/tools/bpf/bpftool/netlink_dumper.c +@@ -29,23 +29,36 @@ static void xdp_dump_prog_id(struct nlattr **tb, int attr, + static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex, + const char *name) + { ++ unsigned char mode = XDP_ATTACHED_NONE; + struct nlattr *tb[IFLA_XDP_MAX + 1]; +- unsigned char mode; ++ unsigned char md_btf_enabled = 0; ++ unsigned int md_btf_id = 0; ++ bool attached; + + if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0) + return -1; + +- if (!tb[IFLA_XDP_ATTACHED]) ++ if (!tb[IFLA_XDP_ATTACHED] && !tb[IFLA_XDP_MD_BTF_ID]) + return 0; + +- mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); +- if (mode == XDP_ATTACHED_NONE) ++ if (tb[IFLA_XDP_ATTACHED]) ++ mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); ++ ++ if (tb[IFLA_XDP_MD_BTF_ID]) { ++ md_btf_id = libbpf_nla_getattr_u32(tb[IFLA_XDP_MD_BTF_ID]); ++ md_btf_enabled = libbpf_nla_getattr_u8(tb[IFLA_XDP_MD_BTF_STATE]); ++ } ++ ++ attached = (mode != XDP_ATTACHED_NONE); ++ if (!attached && !md_btf_id) + return 0; + + NET_START_OBJECT; + if (name) + NET_DUMP_STR("devname", "%s", name); + NET_DUMP_UINT("ifindex", "(%u)", ifindex); ++ NET_DUMP_UINT("md_btf_id", " md_btf_id(%d)", md_btf_id); ++ NET_DUMP_UINT("md_btf_enabled", " md_btf_enabled(%d)", md_btf_enabled); + + if (mode == XDP_ATTACHED_MULTI) { + if (json_output) { +diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h +index 7e46ca4cd31bb..4d5b143d48719 100644 +--- a/tools/include/uapi/linux/if_link.h ++++ b/tools/include/uapi/linux/if_link.h +@@ -1899,6 +1899,8 @@ enum { + IFLA_XDP_SKB_PROG_ID, + IFLA_XDP_HW_PROG_ID, + IFLA_XDP_EXPECTED_FD, ++ IFLA_XDP_MD_BTF_ID, ++ IFLA_XDP_MD_BTF_STATE, + __IFLA_XDP_MAX, + }; + +-- +2.43.0 + diff --git a/SPECS/kernel/0006-tools-power-turbostat-Refactor-floating-point-printo.turbo b/SPECS/kernel/0006-tools-power-turbostat-Refactor-floating-point-printo.turbo new file mode 100644 index 000000000..eef6544f3 --- /dev/null +++ b/SPECS/kernel/0006-tools-power-turbostat-Refactor-floating-point-printo.turbo @@ -0,0 +1,161 @@ +From 3e9357c67083cec57193d01e1b154264e996177e Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Mon, 20 Oct 2025 20:16:10 -0300 +Subject: [PATCH 06/21] tools/power turbostat: Refactor floating point printout + code + +Too many copies of (usually) the same printf code... + +Also, unify code for added-counter FORMAT_AVERAGE, +which was correct where it was tested, but neglected elsewhere. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 60 +++++++++++++-------------- + 1 file changed, 28 insertions(+), 32 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index f9b99940b2478..47cb723430389 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2736,6 +2736,10 @@ static inline int print_decimal_value(int width, int *printed, char *delim, unsi + else + return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + } ++static inline int print_float_value(int *printed, char *delim, double value) ++{ ++ return (sprintf(outp, "%s%0.2f", (*printed++ ? delim : ""), value)); ++} + + void print_header(char *delim) + { +@@ -3243,11 +3247,9 @@ int format_counters(PER_THREAD_PARAMS) + outp += print_decimal_value(mp->width, &printed, delim, t->counter[i]); + else if (mp->format == FORMAT_PERCENT) { + if (mp->type == COUNTER_USEC) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- t->counter[i] / interval_float / 10000); ++ outp += print_float_value(&printed, delim, t->counter[i] / interval_float / 10000); + else +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, 100.0 * t->counter[i] / tsc); + } + } + +@@ -3255,16 +3257,13 @@ int format_counters(PER_THREAD_PARAMS) + for (i = 0, pp = sys.perf_tp; pp; ++i, pp = pp->next) { + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, t->perf_counter[i]); +- else if (pp->format == FORMAT_DELTA) ++ else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, t->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) { + if (pp->type == COUNTER_USEC) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- t->perf_counter[i] / interval_float / 10000); ++ outp += print_float_value(&printed, delim, t->perf_counter[i] / interval_float / 10000); + else +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, 100.0 * t->perf_counter[i] / tsc); + } + } + +@@ -3320,20 +3319,18 @@ int format_counters(PER_THREAD_PARAMS) + outp += print_hex_value(mp->width, &printed, delim, c->counter[i]); + else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(mp->width, &printed, delim, c->counter[i]); +- else if (mp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->counter[i] / tsc); +- } ++ else if (mp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * c->counter[i] / tsc); + } + + /* Added perf Core counters */ + for (i = 0, pp = sys.perf_cp; pp; i++, pp = pp->next) { + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, c->perf_counter[i]); +- else if (pp->format == FORMAT_DELTA) ++ else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); +- else if (pp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->perf_counter[i] / tsc); +- } ++ else if (pp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * c->perf_counter[i] / tsc); + } + + /* Added PMT Core counters */ +@@ -3347,12 +3344,12 @@ int format_counters(PER_THREAD_PARAMS) + + case PMT_TYPE_XTAL_TIME: + value_converted = 100.0 * value_raw / crystal_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: + value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + } + } + +@@ -3500,26 +3497,25 @@ int format_counters(PER_THREAD_PARAMS) + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) + outp += print_hex_value(mp->width, &printed, delim, p->counter[i]); +- else if (mp->format == FORMAT_DELTA) +- outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); +- else if (mp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->counter[i] / tsc); +- } else if (mp->type == COUNTER_K2M) ++ else if (mp->type == COUNTER_K2M) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->counter[i] / 1000); ++ else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); ++ else if (mp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * p->counter[i] / tsc); + } + + /* Added perf Package Counters */ + for (i = 0, pp = sys.perf_pp; pp; i++, pp = pp->next) { + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); +- else if (pp->format == FORMAT_DELTA) { +- outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); +- } else if (pp->format == FORMAT_PERCENT) { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->perf_counter[i] / tsc); +- } else if (pp->type == COUNTER_K2M) { ++ else if (pp->type == COUNTER_K2M) + outp += + sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); +- } ++ else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) ++ outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); ++ else if (pp->format == FORMAT_PERCENT) ++ outp += print_float_value(&printed, delim, 100.0 * p->perf_counter[i] / tsc); + } + + /* Added PMT Package Counters */ +@@ -3533,12 +3529,12 @@ int format_counters(PER_THREAD_PARAMS) + + case PMT_TYPE_XTAL_TIME: + value_converted = 100.0 * value_raw / crystal_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: + value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); ++ outp += print_float_value(&printed, delim, value_converted); + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi b/SPECS/kernel/0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi new file mode 100644 index 000000000..9c1900661 --- /dev/null +++ b/SPECS/kernel/0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi @@ -0,0 +1,45 @@ +From 543b5b749bd79a5cd3301151e3fa1aa95751014f Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Fri, 24 Oct 2025 11:52:32 -0700 +Subject: [PATCH 06/44] x86/cea: Export __this_cpu_ist_top_va() to KVM + +Export __this_cpu_ist_top_va() to allow KVM to retrieve the per-CPU +exception stack top. + +FRED introduced new fields in the host-state area of the VMCS for stack +levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively corresponding to +per-CPU exception stacks for #DB, NMI and #DF. KVM must populate these +fields each time a vCPU is loaded onto a CPU. + +Signed-off-by: Xin Li (Intel) +--- + arch/x86/mm/cpu_entry_area.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/arch/x86/mm/cpu_entry_area.c b/arch/x86/mm/cpu_entry_area.c +index b3d90f9cfbb11..e507621d5c209 100644 +--- a/arch/x86/mm/cpu_entry_area.c ++++ b/arch/x86/mm/cpu_entry_area.c +@@ -19,6 +19,11 @@ static DEFINE_PER_CPU_PAGE_ALIGNED(struct exception_stacks, exception_stacks); + DEFINE_PER_CPU(struct cea_exception_stacks*, cea_exception_stacks); + + /* ++ * FRED introduced new fields in the host-state area of the VMCS for ++ * stack levels 1->3 (HOST_IA32_FRED_RSP[123]), each respectively ++ * corresponding to per CPU stacks for #DB, NMI and #DF. KVM must ++ * populate these each time a vCPU is loaded onto a CPU. ++ * + * Typically invoked by entry code, so must be noinstr. + */ + noinstr unsigned long __this_cpu_ist_bottom_va(enum exception_stack_ordering stack) +@@ -36,6 +41,7 @@ noinstr unsigned long __this_cpu_ist_top_va(enum exception_stack_ordering stack) + { + return __this_cpu_ist_bottom_va(stack) + EXCEPTION_STKSZ; + } ++EXPORT_SYMBOL_FOR_MODULES(__this_cpu_ist_top_va, "kvm-intel"); + + static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, _cea_offset); + +-- +2.43.0 + diff --git a/SPECS/kernel/0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac b/SPECS/kernel/0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac deleted file mode 100644 index 50e959c82..000000000 --- a/SPECS/kernel/0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac +++ /dev/null @@ -1,56 +0,0 @@ -From e35a77a69c52d16e62a1f6c041fd208bf985df4e Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Thu, 24 Jul 2025 14:17:23 +0800 -Subject: [PATCH 07/13] EDAC/i10nm: Reallocate skx_dev list if preconfigured - cnt != runtime cnt - -Ideally, read the present DDR memory controller count first and then -allocate the skx_dev list using this count. However, this approach -requires adding a significant amount of code similar to -skx_get_all_bus_mappings() to obtain the PCI bus mappings for the first -socket and use these mappings along with the related PCI register offset -to read the memory controller count. - -Given that the Granite Rapids CPU is the only one that can detect the -count of memory controllers at runtime (other CPUs use the count in the -configuration data), to reduce code complexity, reallocate the skx_dev -list only if the preconfigured count of DDR memory controllers differs -from the count read at runtime for Granite Rapids CPU. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/i10nm_base.c | 13 +++++++------ - 1 file changed, 7 insertions(+), 6 deletions(-) - -diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c -index 9d00f247f4e0..2010a47149f4 100644 ---- a/drivers/edac/i10nm_base.c -+++ b/drivers/edac/i10nm_base.c -@@ -468,17 +468,18 @@ static int i10nm_get_imc_num(struct res_config *cfg) - return -ENODEV; - } - -- if (imc_num > I10NM_NUM_DDR_IMC) { -- i10nm_printk(KERN_ERR, "Need to make I10NM_NUM_DDR_IMC >= %d\n", imc_num); -- return -EINVAL; -- } -- - if (cfg->ddr_imc_num != imc_num) { - /* -- * Store the number of present DDR memory controllers. -+ * Update the configuration data to reflect the number of -+ * present DDR memory controllers. - */ - cfg->ddr_imc_num = imc_num; - edac_dbg(2, "Set DDR MC number: %d", imc_num); -+ -+ /* Release and reallocate skx_dev list with the updated number. */ -+ skx_remove(); -+ if (skx_get_all_bus_mappings(cfg, &i10nm_edac_list) <= 0) -+ return -ENODEV; - } - - return 0; --- -2.43.0 - diff --git a/SPECS/kernel/0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security b/SPECS/kernel/0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security new file mode 100644 index 000000000..fffdd320b --- /dev/null +++ b/SPECS/kernel/0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security @@ -0,0 +1,462 @@ +From 3140756d9ae49d63b6a6f478a8a5451f5f17b5e4 Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Wed, 19 Jan 2022 23:08:30 +0200 +Subject: [PATCH 7/8] INTEL_DII: mei: add check for offline bit in every + register access + +Added check for offline in every register access function. +When offline bit is set the driver should not access any mei hw. + +Co-developed-by: Tomas Winkler +Co-developed-by: Vitaly Lubart +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/hw-me.c | 151 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 144 insertions(+), 7 deletions(-) + +diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c +index 78b3050f2f801..803466460dbdb 100644 +--- a/drivers/misc/mei/hw-me.c ++++ b/drivers/misc/mei/hw-me.c +@@ -59,6 +59,9 @@ static inline void mei_me_reg_write(const struct mei_me_hw *hw, + */ + static inline u32 mei_me_mecbrw_read(const struct mei_device *dev) + { ++ if (dev->parent->offline) ++ return 0; ++ + return mei_me_reg_read(to_me_hw(dev), ME_CB_RW); + } + +@@ -70,6 +73,9 @@ static inline u32 mei_me_mecbrw_read(const struct mei_device *dev) + */ + static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data) + { ++ if (dev->parent->offline) ++ return; ++ + mei_me_reg_write(to_me_hw(dev), H_CB_WW, data); + } + +@@ -84,6 +90,9 @@ static inline u32 mei_me_mecsr_read(const struct mei_device *dev) + { + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA); + trace_mei_reg_read(&dev->dev, "ME_CSR_HA", ME_CSR_HA, reg); + +@@ -101,6 +110,9 @@ static inline u32 mei_hcsr_read(const struct mei_device *dev) + { + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_reg_read(to_me_hw(dev), H_CSR); + trace_mei_reg_read(&dev->dev, "H_CSR", H_CSR, reg); + +@@ -115,6 +127,9 @@ static inline u32 mei_hcsr_read(const struct mei_device *dev) + */ + static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) + { ++ if (dev->parent->offline) ++ return; ++ + trace_mei_reg_write(&dev->dev, "H_CSR", H_CSR, reg); + mei_me_reg_write(to_me_hw(dev), H_CSR, reg); + } +@@ -128,6 +143,9 @@ static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) + */ + static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) + { ++ if (dev->parent->offline) ++ return; ++ + reg &= ~H_CSR_IS_MASK; + mei_hcsr_write(dev, reg); + } +@@ -141,6 +159,9 @@ static inline void mei_hcsr_set_hig(struct mei_device *dev) + { + u32 hcsr; + ++ if (dev->parent->offline) ++ return; ++ + hcsr = mei_hcsr_read(dev) | H_IG; + mei_hcsr_set(dev, hcsr); + } +@@ -156,6 +177,9 @@ static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) + { + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C); + trace_mei_reg_read(&dev->dev, "H_D0I3C", H_D0I3C, reg); + +@@ -170,6 +194,9 @@ static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) + */ + static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) + { ++ if (dev->parent->offline) ++ return; ++ + trace_mei_reg_write(&dev->dev, "H_D0I3C", H_D0I3C, reg); + mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg); + } +@@ -186,6 +213,9 @@ static int mei_me_trc_status(struct mei_device *dev, u32 *trc) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return -EOPNOTSUPP; ++ + if (!hw->cfg->hw_trc_supported) + return -EOPNOTSUPP; + +@@ -211,6 +241,9 @@ static int mei_me_fw_status(struct mei_device *dev, + int ret; + int i; + ++ if (dev->parent->offline) ++ return -EINVAL; ++ + if (!fw_status || !hw->read_fws) + return -EINVAL; + +@@ -243,6 +276,9 @@ static int mei_me_hw_config(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr, reg; + ++ if (dev->parent->offline) ++ return -EINVAL; ++ + if (WARN_ON(!hw->read_fws)) + return -EINVAL; + +@@ -295,6 +331,9 @@ static inline u32 me_intr_src(u32 hcsr) + */ + static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) + { ++ if (dev->parent->offline) ++ return; ++ + hcsr &= ~H_CSR_IE_MASK; + mei_hcsr_set(dev, hcsr); + } +@@ -307,6 +346,9 @@ static inline void me_intr_disable(struct mei_device *dev, u32 hcsr) + */ + static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) + { ++ if (dev->parent->offline) ++ return; ++ + if (me_intr_src(hcsr)) + mei_hcsr_write(dev, hcsr); + } +@@ -318,7 +360,12 @@ static inline void me_intr_clear(struct mei_device *dev, u32 hcsr) + */ + static void mei_me_intr_clear(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + + me_intr_clear(dev, hcsr); + } +@@ -331,6 +378,9 @@ static void mei_me_intr_enable(struct mei_device *dev) + { + u32 hcsr; + ++ if (dev->parent->offline) ++ return; ++ + if (mei_me_hw_use_polling(to_me_hw(dev))) + return; + +@@ -345,8 +395,12 @@ static void mei_me_intr_enable(struct mei_device *dev) + */ + static void mei_me_intr_disable(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; + ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + me_intr_disable(dev, hcsr); + } + +@@ -359,6 +413,9 @@ static void mei_me_synchronize_irq(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (mei_me_hw_use_polling(hw)) + return; + +@@ -372,7 +429,12 @@ static void mei_me_synchronize_irq(struct mei_device *dev) + */ + static void mei_me_hw_reset_release(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + + hcsr |= H_IG; + hcsr &= ~H_RST; +@@ -386,7 +448,12 @@ static void mei_me_hw_reset_release(struct mei_device *dev) + */ + static void mei_me_host_set_ready(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return; ++ ++ hcsr = mei_hcsr_read(dev); + + if (!mei_me_hw_use_polling(to_me_hw(dev))) + hcsr |= H_CSR_IE_MASK; +@@ -403,7 +470,12 @@ static void mei_me_host_set_ready(struct mei_device *dev) + */ + static bool mei_me_host_is_ready(struct mei_device *dev) + { +- u32 hcsr = mei_hcsr_read(dev); ++ u32 hcsr; ++ ++ if (dev->parent->offline) ++ return true; ++ ++ hcsr = mei_hcsr_read(dev); + + return (hcsr & H_RDY) == H_RDY; + } +@@ -416,7 +488,12 @@ static bool mei_me_host_is_ready(struct mei_device *dev) + */ + static bool mei_me_hw_is_ready(struct mei_device *dev) + { +- u32 mecsr = mei_me_mecsr_read(dev); ++ u32 mecsr; ++ ++ if (dev->parent->offline) ++ return true; ++ ++ mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RDY_HRA) == ME_RDY_HRA; + } +@@ -429,7 +506,12 @@ static bool mei_me_hw_is_ready(struct mei_device *dev) + */ + static bool mei_me_hw_is_resetting(struct mei_device *dev) + { +- u32 mecsr = mei_me_mecsr_read(dev); ++ u32 mecsr; ++ ++ if (dev->parent->offline) ++ return false; ++ ++ mecsr = mei_me_mecsr_read(dev); + + return (mecsr & ME_RST_HRA) == ME_RST_HRA; + } +@@ -444,6 +526,9 @@ static void mei_gsc_pxp_check(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 fwsts5 = 0; + ++ if (dev->parent->offline) ++ return; ++ + if (!kind_is_gsc(dev) && !kind_is_gscfi(dev)) + return; + +@@ -504,6 +589,9 @@ static int mei_me_hw_start(struct mei_device *dev) + { + int ret = mei_me_hw_ready_wait(dev); + ++ if (dev->parent->offline) ++ return 0; ++ + if ((kind_is_gsc(dev) || kind_is_gscfi(dev)) && + dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED) + dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE; +@@ -605,6 +693,9 @@ static int mei_me_hbuf_write(struct mei_device *dev, + u32 dw_cnt; + int empty_slots; + ++ if (dev->parent->offline) ++ return -EINVAL; ++ + if (WARN_ON(!hdr || hdr_len & 0x3)) + return -EINVAL; + +@@ -689,6 +780,9 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer, + { + u32 *reg_buf = (u32 *)buffer; + ++ if (dev->parent->offline) ++ return 0; ++ + for (; buffer_length >= MEI_SLOT_SIZE; buffer_length -= MEI_SLOT_SIZE) + *reg_buf++ = mei_me_mecbrw_read(dev); + +@@ -712,6 +806,9 @@ static void mei_me_pg_set(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + ++ if (dev->parent->offline) ++ return; ++ + reg = mei_me_reg_read(hw, H_HPG_CSR); + trace_mei_reg_read(&dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); + +@@ -731,6 +828,9 @@ static void mei_me_pg_unset(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + ++ if (dev->parent->offline) ++ return; ++ + reg = mei_me_reg_read(hw, H_HPG_CSR); + trace_mei_reg_read(&dev->dev, "H_HPG_CSR", H_HPG_CSR, reg); + +@@ -754,6 +854,9 @@ static int mei_me_pg_legacy_enter_sync(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + int ret; + ++ if (dev->parent->offline) ++ return 0; ++ + dev->pg_event = MEI_PG_EVENT_WAIT; + + ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD); +@@ -791,6 +894,9 @@ static int mei_me_pg_legacy_exit_sync(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + int ret; + ++ if (dev->parent->offline) ++ return 0; ++ + if (dev->pg_event == MEI_PG_EVENT_RECEIVED) + goto reply; + +@@ -936,6 +1042,9 @@ static int mei_me_d0i3_enter_sync(struct mei_device *dev) + int ret; + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + /* we are in d0i3, nothing to do */ +@@ -1011,6 +1120,9 @@ static int mei_me_d0i3_enter(struct mei_device *dev) + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + /* we are in d0i3, nothing to do */ +@@ -1039,6 +1151,9 @@ static int mei_me_d0i3_exit_sync(struct mei_device *dev) + int ret; + u32 reg; + ++ if (dev->parent->offline) ++ return 0; ++ + dev->pg_event = MEI_PG_EVENT_INTR_WAIT; + + reg = mei_me_d0i3c_read(dev); +@@ -1090,6 +1205,9 @@ static void mei_me_pg_legacy_intr(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (dev->pg_event != MEI_PG_EVENT_INTR_WAIT) + return; + +@@ -1109,6 +1227,9 @@ static void mei_me_d0i3_intr(struct mei_device *dev, u32 intr_source) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && + (intr_source & H_D0I3C_IS)) { + dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; +@@ -1150,6 +1271,9 @@ static void mei_me_pg_intr(struct mei_device *dev, u32 intr_source) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return; ++ + if (hw->d0i3_supported) + mei_me_d0i3_intr(dev, intr_source); + else +@@ -1167,6 +1291,9 @@ int mei_me_pg_enter_sync(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return 0; ++ + if (hw->d0i3_supported) + return mei_me_d0i3_enter_sync(dev); + else +@@ -1184,6 +1311,9 @@ int mei_me_pg_exit_sync(struct mei_device *dev) + { + struct mei_me_hw *hw = to_me_hw(dev); + ++ if (dev->parent->offline) ++ return 0; ++ + if (hw->d0i3_supported) + return mei_me_d0i3_exit_sync(dev); + else +@@ -1274,6 +1404,9 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) + struct mei_device *dev = (struct mei_device *)dev_id; + u32 hcsr; + ++ if (dev->parent->offline) ++ return IRQ_HANDLED; ++ + hcsr = mei_hcsr_read(dev); + if (!me_intr_src(hcsr)) + return IRQ_NONE; +@@ -1306,6 +1439,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) + int rets = 0; + + dev_dbg(&dev->dev, "function called after ISR to handle the interrupt processing.\n"); ++ ++ if (dev->parent->offline) ++ return IRQ_HANDLED; ++ + /* initialize our complete list */ + mutex_lock(&dev->device_lock); + +-- +2.43.0 + diff --git a/SPECS/kernel/0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi b/SPECS/kernel/0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi new file mode 100644 index 000000000..40b0a972d --- /dev/null +++ b/SPECS/kernel/0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi @@ -0,0 +1,193 @@ +From 4e2ef01a100b0278869dd565367f3cb6c7c25d66 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 31 Aug 2023 01:52:58 -0700 +Subject: [PATCH 07/44] KVM: VMX: Initialize VMCS FRED fields + +Initialize host VMCS FRED fields with host FRED MSRs' value and +guest VMCS FRED fields to 0. + +FRED CPU state is managed in 9 new FRED MSRs: + IA32_FRED_CONFIG, + IA32_FRED_STKLVLS, + IA32_FRED_RSP0, + IA32_FRED_RSP1, + IA32_FRED_RSP2, + IA32_FRED_RSP3, + IA32_FRED_SSP1, + IA32_FRED_SSP2, + IA32_FRED_SSP3, +as well as a few existing CPU registers and MSRs: + CR4.FRED, + IA32_STAR, + IA32_KERNEL_GS_BASE, + IA32_PL0_SSP (also known as IA32_FRED_SSP0). + +CR4, IA32_KERNEL_GS_BASE and IA32_STAR are already well managed. +Except IA32_FRED_RSP0 and IA32_FRED_SSP0, all other FRED CPU state +MSRs have corresponding VMCS fields in both the host-state and +guest-state areas. So KVM just needs to initialize them, and with +proper VM entry/exit FRED controls, a FRED CPU will keep tracking +host and guest FRED CPU state in VMCS automatically. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Initialize host SSP[1-3] to 0s in vmx_set_constant_host_state() + because Linux doesn't support kernel shadow stacks (Chao Gao). + +Change in v3: +* Use structure kvm_host_values to keep host fred config & stack levels + (Sean Christopherson). + +Changes in v2: +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() to decouple + KVM's capability to virtualize a feature and host's enabling of a + feature (Chao Gao). +* Move guest FRED state init into __vmx_vcpu_reset() (Chao Gao). +--- + arch/x86/include/asm/vmx.h | 32 ++++++++++++++++++++++++++++++++ + arch/x86/kvm/vmx/vmx.c | 36 ++++++++++++++++++++++++++++++++++++ + arch/x86/kvm/x86.h | 3 +++ + 3 files changed, 71 insertions(+) + +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index dd79d027ea700..6f8b8947c60cd 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -293,12 +293,44 @@ enum vmcs_field { + GUEST_BNDCFGS_HIGH = 0x00002813, + GUEST_IA32_RTIT_CTL = 0x00002814, + GUEST_IA32_RTIT_CTL_HIGH = 0x00002815, ++ GUEST_IA32_FRED_CONFIG = 0x0000281a, ++ GUEST_IA32_FRED_CONFIG_HIGH = 0x0000281b, ++ GUEST_IA32_FRED_RSP1 = 0x0000281c, ++ GUEST_IA32_FRED_RSP1_HIGH = 0x0000281d, ++ GUEST_IA32_FRED_RSP2 = 0x0000281e, ++ GUEST_IA32_FRED_RSP2_HIGH = 0x0000281f, ++ GUEST_IA32_FRED_RSP3 = 0x00002820, ++ GUEST_IA32_FRED_RSP3_HIGH = 0x00002821, ++ GUEST_IA32_FRED_STKLVLS = 0x00002822, ++ GUEST_IA32_FRED_STKLVLS_HIGH = 0x00002823, ++ GUEST_IA32_FRED_SSP1 = 0x00002824, ++ GUEST_IA32_FRED_SSP1_HIGH = 0x00002825, ++ GUEST_IA32_FRED_SSP2 = 0x00002826, ++ GUEST_IA32_FRED_SSP2_HIGH = 0x00002827, ++ GUEST_IA32_FRED_SSP3 = 0x00002828, ++ GUEST_IA32_FRED_SSP3_HIGH = 0x00002829, + HOST_IA32_PAT = 0x00002c00, + HOST_IA32_PAT_HIGH = 0x00002c01, + HOST_IA32_EFER = 0x00002c02, + HOST_IA32_EFER_HIGH = 0x00002c03, + HOST_IA32_PERF_GLOBAL_CTRL = 0x00002c04, + HOST_IA32_PERF_GLOBAL_CTRL_HIGH = 0x00002c05, ++ HOST_IA32_FRED_CONFIG = 0x00002c08, ++ HOST_IA32_FRED_CONFIG_HIGH = 0x00002c09, ++ HOST_IA32_FRED_RSP1 = 0x00002c0a, ++ HOST_IA32_FRED_RSP1_HIGH = 0x00002c0b, ++ HOST_IA32_FRED_RSP2 = 0x00002c0c, ++ HOST_IA32_FRED_RSP2_HIGH = 0x00002c0d, ++ HOST_IA32_FRED_RSP3 = 0x00002c0e, ++ HOST_IA32_FRED_RSP3_HIGH = 0x00002c0f, ++ HOST_IA32_FRED_STKLVLS = 0x00002c10, ++ HOST_IA32_FRED_STKLVLS_HIGH = 0x00002c11, ++ HOST_IA32_FRED_SSP1 = 0x00002c12, ++ HOST_IA32_FRED_SSP1_HIGH = 0x00002c13, ++ HOST_IA32_FRED_SSP2 = 0x00002c14, ++ HOST_IA32_FRED_SSP2_HIGH = 0x00002c15, ++ HOST_IA32_FRED_SSP3 = 0x00002c16, ++ HOST_IA32_FRED_SSP3_HIGH = 0x00002c17, + PIN_BASED_VM_EXEC_CONTROL = 0x00004000, + CPU_BASED_VM_EXEC_CONTROL = 0x00004002, + EXCEPTION_BITMAP = 0x00004004, +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 90cbc73b21288..94f88bf8bb785 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1459,6 +1459,15 @@ void vmx_vcpu_load_vmcs(struct kvm_vcpu *vcpu, int cpu) + (unsigned long)(cpu_entry_stack(cpu) + 1)); + } + ++ /* Per-CPU FRED MSRs */ ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++#ifdef CONFIG_X86_64 ++ vmcs_write64(HOST_IA32_FRED_RSP1, __this_cpu_ist_top_va(ESTACK_DB)); ++ vmcs_write64(HOST_IA32_FRED_RSP2, __this_cpu_ist_top_va(ESTACK_NMI)); ++ vmcs_write64(HOST_IA32_FRED_RSP3, __this_cpu_ist_top_va(ESTACK_DF)); ++#endif ++ } ++ + vmx->loaded_vmcs->cpu = cpu; + } + } +@@ -4311,6 +4320,17 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) + */ + vmcs_write16(HOST_DS_SELECTOR, 0); + vmcs_write16(HOST_ES_SELECTOR, 0); ++ ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++ /* FRED CONFIG and STKLVLS are the same on all CPUs */ ++ vmcs_write64(HOST_IA32_FRED_CONFIG, kvm_host.fred_config); ++ vmcs_write64(HOST_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); ++ ++ /* Linux doesn't support kernel shadow stacks, thus SSPs are 0s */ ++ vmcs_write64(HOST_IA32_FRED_SSP1, 0); ++ vmcs_write64(HOST_IA32_FRED_SSP2, 0); ++ vmcs_write64(HOST_IA32_FRED_SSP3, 0); ++ } + #else + vmcs_write16(HOST_DS_SELECTOR, __KERNEL_DS); /* 22.2.4 */ + vmcs_write16(HOST_ES_SELECTOR, __KERNEL_DS); /* 22.2.4 */ +@@ -4822,6 +4842,17 @@ static void init_vmcs(struct vcpu_vmx *vmx) + } + + vmx_setup_uret_msrs(vmx); ++ ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, 0); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, 0); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, 0); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, 0); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, 0); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, 0); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, 0); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, 0); ++ } + } + + static void __vmx_vcpu_reset(struct kvm_vcpu *vcpu) +@@ -8706,6 +8737,11 @@ __init int vmx_hardware_setup(void) + + kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_IGNORE_GUEST_PAT; + ++ if (kvm_cpu_cap_has(X86_FEATURE_FRED)) { ++ rdmsrl(MSR_IA32_FRED_CONFIG, kvm_host.fred_config); ++ rdmsrl(MSR_IA32_FRED_STKLVLS, kvm_host.fred_stklvls); ++ } ++ + return r; + } + +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index f3dc77f006f90..0c1fbf75442b7 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -52,6 +52,9 @@ struct kvm_host_values { + u64 xss; + u64 s_cet; + u64 arch_capabilities; ++ ++ u64 fred_config; ++ u64 fred_stklvls; + }; + + void kvm_spurious_fault(void); +-- +2.43.0 + diff --git a/SPECS/kernel/0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi b/SPECS/kernel/0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi deleted file mode 100644 index 2a02004cb..000000000 --- a/SPECS/kernel/0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi +++ /dev/null @@ -1,95 +0,0 @@ -From cde72e614a686e09074d412ac5ebde282d14e944 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 31 Aug 2023 00:56:30 -0700 -Subject: [PATCH 07/44] KVM: VMX: Set FRED MSR intercepts - -On a userspace MSR filter change, set FRED MSR intercepts. - -8 FRED MSRs, i.e., MSR_IA32_FRED_RSP[123], MSR_IA32_FRED_STKLVLS, -MSR_IA32_FRED_SSP[123] and MSR_IA32_FRED_CONFIG, are all safe to -be passthrough, because they all have a pair of corresponding host -and guest VMCS fields. - -Both MSR_IA32_FRED_RSP0 and MSR_IA32_FRED_SSP0 are dedicated for -userspace event delivery only, IOW they are NOT used in any kernel -event delivery and the execution of ERETS. Thus KVM can run safely -with guest values in the two MSRs. As a result, save and restore of -their guest values are deferred until vCPU context switch and their -host values are restored upon host returning to userspace. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Skip execution of vmx_set_intercept_for_fred_msr() if FRED is - not available or enabled (Sean). -* Use 'intercept' as the variable name to indicate whether MSR - interception should be enabled (Sean). -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/vmx/vmx.c | 39 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 39 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 4a183ffb2154..2a40dfe9f034 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4177,6 +4177,43 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) - MSR_TYPE_RW, intercept); - } - -+static void vmx_set_intercept_for_fred_msr(struct kvm_vcpu *vcpu) -+{ -+ bool intercept = !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED); -+ -+ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) -+ return; -+ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP1, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP2, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP3, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_STKLVLS, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP1, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP2, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP3, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_CONFIG, MSR_TYPE_RW, intercept); -+ -+ /* -+ * MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) are -+ * designated for event delivery while executing in userspace. Since -+ * KVM operates exclusively in kernel mode (the CPL is always 0 after -+ * any VM exit), KVM can safely retain and operate with the guest-defined -+ * values for MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP. -+ * -+ * Therefore, interception of MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP -+ * is not required. -+ * -+ * Note, save and restore of MSR_IA32_PL0_SSP belong to CET supervisor -+ * context management. However the FRED SSP MSRs, including -+ * MSR_IA32_PL0_SSP, are supported by any processor that enumerates FRED. -+ * If such a processor does not support CET, FRED transitions will not -+ * use the MSRs, but the MSRs would still be accessible using MSR-access -+ * instructions (e.g., RDMSR, WRMSR). -+ */ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP0, MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL0_SSP, MSR_TYPE_RW, intercept); -+} -+ - static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - bool set; -@@ -4245,6 +4282,8 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_U_CET, MSR_TYPE_RW, set); - vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, set); - } -+ -+ vmx_set_intercept_for_fred_msr(vcpu); - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be - * filtered by userspace. --- -2.43.0 - diff --git a/SPECS/kernel/0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet b/SPECS/kernel/0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet deleted file mode 100644 index e8c3c412a..000000000 --- a/SPECS/kernel/0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet +++ /dev/null @@ -1,51 +0,0 @@ -From cc0952258b17a0cb7dfa675a97f736a2efc4d6ff Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 4 Jul 2025 01:49:37 -0700 -Subject: [PATCH 07/24] KVM: x86: Report XSS as to-be-saved if there are - supported features - -Add MSR_IA32_XSS to list of MSRs reported to userspace if supported_xss -is non-zero, i.e. KVM supports at least one XSS based feature. - -Before enabling CET virtualization series, guest IA32_MSR_XSS is -guaranteed to be 0, i.e., XSAVES/XRSTORS is executed in non-root mode -with XSS == 0, which equals to the effect of XSAVE/XRSTOR. - -Signed-off-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index f2832a644226..3445a446e4ba 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -339,7 +339,7 @@ static const u32 msrs_to_save_base[] = { - MSR_IA32_RTIT_ADDR3_A, MSR_IA32_RTIT_ADDR3_B, - MSR_IA32_UMWAIT_CONTROL, - -- MSR_IA32_XFD, MSR_IA32_XFD_ERR, -+ MSR_IA32_XFD, MSR_IA32_XFD_ERR, MSR_IA32_XSS, - }; - - static const u32 msrs_to_save_pmu[] = { -@@ -7426,6 +7426,10 @@ static void kvm_probe_msr_to_save(u32 msr_index) - if (!(kvm_get_arch_capabilities() & ARCH_CAP_TSX_CTRL_MSR)) - return; - break; -+ case MSR_IA32_XSS: -+ if (!kvm_caps.supported_xss) -+ return; -+ break; - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel/0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl b/SPECS/kernel/0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl new file mode 100644 index 000000000..8ff55a8fa --- /dev/null +++ b/SPECS/kernel/0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl @@ -0,0 +1,49 @@ +From 3ee8d1f2be3fb17d4775de5f7cf84f8c6f2ea086 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Wed, 12 Nov 2025 17:24:40 +0100 +Subject: [PATCH 07/17] cpuidle: governors: teo: Use s64 consistently in + teo_update() + +Two local variables in teo_update() are defined as u64, but their +values are then compared with s64 values, so it is more consistent +to use s64 as their data type. + +No intentional functional impact. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Tested-by: Christian Loehle +Link: https://patch.msgid.link/3026616.e9J7NaK4W3@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index ada42e2ca7593..88ed47e868b9c 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -157,8 +157,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + { + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + int i, idx_timer = 0, idx_duration = 0; +- s64 target_residency_ns; +- u64 measured_ns; ++ s64 target_residency_ns, measured_ns; + + cpu_data->short_idles -= cpu_data->short_idles >> DECAY_SHIFT; + +@@ -167,9 +166,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + * If one of the safety nets has triggered, assume that this + * might have been a long sleep. + */ +- measured_ns = U64_MAX; ++ measured_ns = S64_MAX; + } else { +- u64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns; ++ s64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns; + + measured_ns = dev->last_residency_ns; + /* +-- +2.43.0 + diff --git a/SPECS/kernel/0007-drm-i915-Consider-RCU-read-section-as-atomic.rt b/SPECS/kernel/0007-drm-i915-Consider-RCU-read-section-as-atomic.rt new file mode 100644 index 000000000..7817c3940 --- /dev/null +++ b/SPECS/kernel/0007-drm-i915-Consider-RCU-read-section-as-atomic.rt @@ -0,0 +1,37 @@ +From 19b951398ea5f6225cc8c67d2e5a63c7649c9b7c Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Mon, 14 Jul 2025 17:39:53 +0200 +Subject: [PATCH 7/9] drm/i915: Consider RCU read section as atomic. + +Locking and/ or running inside interrupt handler will not lead to an +atomic section on PREEMPT_RT. The RCU read section needs to be +considered because all locks, which become sleeping locks on +PREEMPT_RT, start a RCU read section. Scheduling/ sleeping while within +a RCU read section is invalid. + +Check for also for RCU read section in stop_timeout() to determine if it +is safe to sleep. + +Reviewed-by: Rodrigo Vivi +Link: https://lore.kernel.org/r/20250714153954.629393-9-bigeasy@linutronix.de +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/gt/intel_engine_cs.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c +index b721bbd233567..f5a6143ea8a24 100644 +--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c ++++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c +@@ -1609,7 +1609,7 @@ u64 intel_engine_get_last_batch_head(const struct intel_engine_cs *engine) + + static unsigned long stop_timeout(const struct intel_engine_cs *engine) + { +- if (in_atomic() || irqs_disabled()) /* inside atomic preempt-reset? */ ++ if (in_atomic() || irqs_disabled() || rcu_preempt_depth()) /* inside atomic preempt-reset? */ + return 0; + + /* +-- +2.43.0 + diff --git a/SPECS/kernel/0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt b/SPECS/kernel/0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt deleted file mode 100644 index af1d5d6ce..000000000 --- a/SPECS/kernel/0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt +++ /dev/null @@ -1,35 +0,0 @@ -From 7f3224ea657c916f0414480c8411923478d6b9bb Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Tue, 3 Oct 2023 21:37:21 +0200 -Subject: [PATCH 7/9] drm/i915/guc: Consider also RCU depth in busy loop. - -intel_guc_send_busy_loop() looks at in_atomic() and irqs_disabled() to -decide if it should busy-spin while waiting or if it may sleep. -Both checks will report false on PREEMPT_RT if sleeping spinlocks are -acquired leading to RCU splats while the function sleeps. - -Check also if RCU has been disabled. - -Reported-by: "John B. Wyatt IV" -Reviewed-by: Rodrigo Vivi -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h -index 053780f562c1..b25fa8f4dc4b 100644 ---- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h -+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h -@@ -362,7 +362,7 @@ static inline int intel_guc_send_busy_loop(struct intel_guc *guc, - { - int err; - unsigned int sleep_period_ms = 1; -- bool not_atomic = !in_atomic() && !irqs_disabled(); -+ bool not_atomic = !in_atomic() && !irqs_disabled() && !rcu_preempt_depth(); - - /* - * FIXME: Have caller pass in if we are in an atomic context to avoid --- -2.43.0 - diff --git a/SPECS/kernel/0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c b/SPECS/kernel/0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c deleted file mode 100644 index a1cb90661..000000000 --- a/SPECS/kernel/0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c +++ /dev/null @@ -1,51 +0,0 @@ -From f9cc3911f65a04a9524d8f49fd465323dbbf5f6f Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:06 +0300 -Subject: [PATCH 07/11] i3c: mipi-i3c-hci: Remove nonexistent ring interrupt - -Ring interrupt bit 7, INTR_WARN_INS_STOP_MODE was probably drafted at -some point but is marked as reserved in the MIPI I3C HCI specification -versions 1.1 and 1.2 that came out after the initial code and also in -the earlier specification versions so remove it. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250827103009.243771-3-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/dma.c | 5 ----- - 1 file changed, 5 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index f5f5ab4db172..8bc9de189543 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -77,7 +77,6 @@ - #define INTR_TRANSFER_COMPLETION BIT(11) - #define INTR_RING_OP BIT(10) - #define INTR_TRANSFER_ERR BIT(9) --#define INTR_WARN_INS_STOP_MODE BIT(7) - #define INTR_IBI_RING_FULL BIT(6) - #define INTR_TRANSFER_ABORT BIT(5) - -@@ -278,7 +277,6 @@ static int hci_dma_init(struct i3c_hci *hci) - INTR_TRANSFER_COMPLETION | - INTR_RING_OP | - INTR_TRANSFER_ERR | -- INTR_WARN_INS_STOP_MODE | - INTR_IBI_RING_FULL | - INTR_TRANSFER_ABORT); - -@@ -795,9 +793,6 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - RING_CTRL_RUN_STOP); - } - } -- if (status & INTR_WARN_INS_STOP_MODE) -- dev_warn_ratelimited(&hci->master.dev, -- "ring %d: Inserted Stop on Mode Change\n", i); - if (status & INTR_IBI_RING_FULL) - dev_err_ratelimited(&hci->master.dev, - "ring %d: IBI Ring Full Condition\n", i); --- -2.43.0 - diff --git a/SPECS/kernel/0007-issei-add-driver-to-driver-interface.security b/SPECS/kernel/0007-issei-add-driver-to-driver-interface.security new file mode 100644 index 000000000..1d8874a97 --- /dev/null +++ b/SPECS/kernel/0007-issei-add-driver-to-driver-interface.security @@ -0,0 +1,338 @@ +From ca7c252c19765c0eacda46f17c347c2318c4733b Mon Sep 17 00:00:00 2001 +From: "Abliyev, Reuven" +Date: Thu, 27 Mar 2025 17:55:25 +0200 +Subject: [PATCH 7/7] issei: add driver to driver interface + +This enables communication with ISSEI device from the kernel modules + +Signed-off-by: Abliyev, Reuven +Signed-off-by: Alexander Usyskin +--- + drivers/misc/issei/Makefile | 1 + + drivers/misc/issei/issei_device.c | 264 ++++++++++++++++++++++++++++++ + include/linux/issei_device.h | 30 ++++ + 3 files changed, 295 insertions(+) + create mode 100644 drivers/misc/issei/issei_device.c + create mode 100644 include/linux/issei_device.h + +diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile +index fb904fda39499..e4dc3599342f2 100644 +--- a/drivers/misc/issei/Makefile ++++ b/drivers/misc/issei/Makefile +@@ -9,6 +9,7 @@ issei-objs += fw_client.o + issei-objs += host_client.o + issei-objs += ham.o + issei-objs += main.o ++issei-objs += issei_device.o + + obj-$(CONFIG_INTEL_SSEI_HW_HECI) += issei-heci.o + issei-heci-objs := pci_heci.o +diff --git a/drivers/misc/issei/issei_device.c b/drivers/misc/issei/issei_device.c +new file mode 100644 +index 0000000000000..33417bd160497 +--- /dev/null ++++ b/drivers/misc/issei/issei_device.c +@@ -0,0 +1,264 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* Copyright (C) 2025 Intel Corporation */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "linux/uuid.h" ++#include "linux/wait.h" ++ ++#include "issei_dev.h" ++#include "host_client.h" ++ ++/** ++ * issei_device_open - find create issei device object. ++ * @match: pointer to callback that should return non-zero when matching device is found ++ * @data: user data to pass to match callback ++ * ++ * Return: pointer to opaque context on success, <0 on error ++ */ ++void *issei_device_open(int (*match)(struct device *, const void *), const void *data) ++{ ++ struct issei_host_client *cl; ++ struct issei_device *idev; ++ struct device *dev; ++ ++ dev = class_find_device(issei_class, NULL, data, match); ++ if (!dev) ++ return ERR_PTR(-ENODEV); ++ ++ idev = dev_get_drvdata(dev); ++ ++ cl = issei_cl_create(idev, NULL); ++ if (IS_ERR(cl)) ++ put_device(dev); ++ ++ return cl; ++} ++EXPORT_SYMBOL_GPL(issei_device_open); ++ ++/** ++ * issei_device_release - release issei device object. ++ * @ctx: opaque context ++ */ ++void issei_device_release(void *ctx) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ ++ issei_cl_remove(cl); ++ put_device(&idev->dev); ++} ++EXPORT_SYMBOL_GPL(issei_device_release); ++ ++/** ++ * issei_device_connect - connect to firmware client. ++ * @ctx: opaque context ++ * @client_uuid: UUID of firmware client to connect ++ * @conn: pointer for client data to fill on successful connection ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ int ret; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -ENODEV; ++ } ++ ret = issei_cl_connect(cl, client_uuid, &conn->max_msg_length, ++ &conn->protocol_version, &conn->flags); ++ if (ret) { ++ dev_info(&idev->dev, "failed to connect ret = %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(issei_device_connect); ++ ++/** ++ * issei_device_disconnect - disconnect from firmware client. ++ * @ctx: opaque context ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_disconnect(void *ctx) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ int ret; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -ENODEV; ++ } ++ ++ ret = issei_cl_disconnect(cl); ++ if (ret) { ++ dev_info(&idev->dev, "failed to disconnect ret = %d\n", ret); ++ return ret; ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(issei_device_disconnect); ++ ++/** ++ * issei_device_write - send data to firmware client. ++ * @ctx: opaque context ++ * @ubuf: data to send ++ * @length: data length ++ * ++ * Return: >=0 data length on success, <0 on error ++ */ ++ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ const u8 *buf; ++ ssize_t ret; ++ ++ if (!length) ++ return 0; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -EBUSY; ++ } ++ ++ /* sanity check */ ++ if (length > idev->dma.length.h2f) { ++ dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", length, ++ idev->dma.length.h2f); ++ return -EFBIG; ++ } ++ ++ buf = kmemdup(ubuf, length, GFP_KERNEL); ++ if (!buf) { ++ dev_dbg(&idev->dev, "Failed to allocate buf size %zu\n", length); ++ return -ENOMEM; ++ } ++ ++ do { ++ ret = issei_cl_write(cl, buf, length); ++ if (ret < 0 && ret != -EAGAIN) { ++ kfree(buf); ++ dev_dbg(&idev->dev, "failed to write %zd\n", ret); ++ return ret; ++ } ++ if (wait_event_interruptible(cl->write_wait, ++ (issei_cl_check_write(cl) != 1))) { ++ if (signal_pending(current)) ++ return -EINTR; ++ return -ERESTARTSYS; ++ } ++ } while (ret == -EAGAIN); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(issei_device_write); ++ ++/** ++ * issei_device_read - receive data from firmware client. ++ * @ctx: opaque context ++ * @ubuf: buffer for data ++ * @length: buffer length ++ * ++ * Return: >=0 data length on success, <0 on error ++ */ ++ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ struct issei_device *idev = cl->idev; ++ u8 *data = NULL; ++ size_t data_size = length; ++ ssize_t ret; ++ ++ if (!length) ++ return 0; ++ ++ if (idev->rst_state != ISSEI_RST_STATE_DONE) { ++ dev_dbg(&idev->dev, "Device is in transition\n"); ++ return -EBUSY; ++ } ++ ++ /* sanity check */ ++ if (length > idev->dma.length.f2h) { ++ dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", length, ++ idev->dma.length.f2h); ++ return -EFBIG; ++ } ++ ++ ret = issei_cl_read(cl, &data, &data_size); ++ if (!ret) ++ goto copy; ++ if (ret != -ENOENT) ++ return ret; ++ ++ if (wait_event_interruptible(cl->read_wait, ++ (issei_cl_check_read(cl) != 0))) { ++ if (signal_pending(current)) ++ return -EINTR; ++ return -ERESTARTSYS; ++ } ++ ++ ret = issei_cl_read(cl, &data, &data_size); ++ if (ret) ++ return ret; ++ ++copy: ++ memcpy(ubuf, data, data_size); ++ ret = data_size; ++ kfree(data); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(issei_device_read); ++ ++/** ++ * issei_device_dma_map - allocate DMA buffer on parent device. ++ * @ctx: opaque context ++ * @size: buffer size ++ * @daddr: pointer for buffer physical address ++ * @vaddr: pointer for buffer virtual address ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ ++ return issei_cl_dma_map(cl, size, daddr, vaddr); ++} ++EXPORT_SYMBOL_GPL(issei_device_dma_map); ++ ++/** ++ * issei_device_dma_unmap - deallocate DMA buffer. ++ * @ctx: opaque context ++ */ ++void issei_device_dma_unmap(void *ctx) ++{ ++ struct issei_host_client *cl = (struct issei_host_client *)ctx; ++ ++ issei_cl_dma_unmap(cl); ++} ++EXPORT_SYMBOL_GPL(issei_device_dma_unmap); ++ ++/** ++ * issei_device_register_interface - register class interface for issei class. ++ * @intf: interface structure ++ * ++ * Return: 0 on success, <0 on error ++ */ ++int issei_device_register_interface(struct class_interface *intf) ++{ ++ intf->class = issei_class; ++ ++ return class_interface_register(intf); ++} ++EXPORT_SYMBOL_GPL(issei_device_register_interface); +diff --git a/include/linux/issei_device.h b/include/linux/issei_device.h +new file mode 100644 +index 0000000000000..862abdb004199 +--- /dev/null ++++ b/include/linux/issei_device.h +@@ -0,0 +1,30 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++/* Copyright (C) 2025 Intel Corporation */ ++#ifndef _ISSEI_DEVICE_H_ ++#define _ISSEI_DEVICE_H_ ++ ++#include ++#include ++#include ++ ++struct class_interface; ++struct device; ++struct issei_client; ++ ++void *issei_device_open(int (*match)(struct device *, const void *), const void *data); ++void issei_device_release(void *ctx); ++ ++int issei_device_connect(void *ctx, const uuid_t *client_uuid, struct issei_client *conn); ++int issei_device_disconnect(void *ctx); ++ ++ssize_t issei_device_write(void *ctx, const u8 *ubuf, size_t length); ++ssize_t issei_device_read(void *ctx, u8 *ubuf, size_t length); ++ ++int issei_device_dma_map(void *ctx, size_t size, dma_addr_t *daddr, void **vaddr); ++void issei_device_dma_unmap(void *ctx); ++ ++int issei_device_register_interface(struct class_interface *intf); ++#define issei_device_unregister_interface(intf) \ ++ class_interface_unregister(intf) ++ ++#endif /* _ISSEI_DEVICE_H_ */ +-- +2.43.0 + diff --git a/SPECS/kernel/0007-ocfs2-relax-BUG-to-ocfs2_error-in-__ocfs2_move_exten.patch b/SPECS/kernel/0007-ocfs2-relax-BUG-to-ocfs2_error-in-__ocfs2_move_exten.patch deleted file mode 100644 index 534712ce7..000000000 --- a/SPECS/kernel/0007-ocfs2-relax-BUG-to-ocfs2_error-in-__ocfs2_move_exten.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 43cd5558c2039ad1b0864f366c4c592eaecfd93a Mon Sep 17 00:00:00 2001 -From: Dmitry Antipov -Date: Thu, 9 Oct 2025 13:23:49 +0300 -Subject: [PATCH 07/51] ocfs2: relax BUG() to ocfs2_error() in - __ocfs2_move_extent() - -In '__ocfs2_move_extent()', relax 'BUG()' to 'ocfs2_error()' just -to avoid crashing the whole kernel due to a filesystem corruption. - -Fixes: 8f603e567aa7 ("Ocfs2/move_extents: move a range of extent.") -Link: https://lkml.kernel.org/r/20251009102349.181126-2-dmantipov@yandex.ru -Signed-off-by: Dmitry Antipov -Closes: https://syzkaller.appspot.com/bug?extid=727d161855d11d81e411 -Reported-by: syzbot+727d161855d11d81e411@syzkaller.appspotmail.com -Reviewed-by: Joseph Qi -Cc: Mark Fasheh -Cc: Joel Becker -Cc: Junxiao Bi -Cc: Changwei Ge -Cc: Jun Piao -Signed-off-by: Andrew Morton ---- - fs/ocfs2/move_extents.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/fs/ocfs2/move_extents.c b/fs/ocfs2/move_extents.c -index 80ebb0b7265a..5a0228c51ec3 100644 ---- a/fs/ocfs2/move_extents.c -+++ b/fs/ocfs2/move_extents.c -@@ -98,7 +98,13 @@ static int __ocfs2_move_extent(handle_t *handle, - - rec = &el->l_recs[index]; - -- BUG_ON(ext_flags != rec->e_flags); -+ if (ext_flags != rec->e_flags) { -+ ret = ocfs2_error(inode->i_sb, -+ "Inode %llu has corrupted extent %d with flags 0x%x at cpos %u\n", -+ (unsigned long long)ino, index, rec->e_flags, cpos); -+ goto out; -+ } -+ - /* - * after moving/defraging to new location, the extent is not going - * to be refcounted anymore. --- -2.43.0 - diff --git a/SPECS/kernel/0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu b/SPECS/kernel/0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu deleted file mode 100644 index 3cc14c739..000000000 --- a/SPECS/kernel/0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu +++ /dev/null @@ -1,33 +0,0 @@ -From ec8261555865cea6e5aaad3722e986e6dfefe44e Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 12:44:25 +0800 -Subject: [PATCH 07/21] patch: staging add patch for use DPHY as the default - phy mode - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c -index dae8700dd7f5..101f080df552 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys.c -+++ b/drivers/staging/media/ipu7/ipu7-isys.c -@@ -84,10 +84,10 @@ isys_complete_ext_device_registration(struct ipu7_isys *isys, - } - - isys->csi2[csi2->port].nlanes = csi2->nlanes; -- if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY) -- isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; -- else -+ if (csi2->bus_type == V4L2_MBUS_CSI2_CPHY) - isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY; -+ else -+ isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY; - - return 0; - --- -2.43.0 - diff --git a/SPECS/kernel/0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf b/SPECS/kernel/0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf deleted file mode 100644 index c039cd150..000000000 --- a/SPECS/kernel/0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf +++ /dev/null @@ -1,71 +0,0 @@ -From 57f44dd44c91cafad6a44c6c3650a28db51fa149 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Tue, 12 Aug 2025 15:58:20 +0800 -Subject: [PATCH 007/100] perf/x86/intel: Change macro - GLOBAL_CTRL_EN_PERF_METRICS to BIT_ULL(48) - -Macro GLOBAL_CTRL_EN_PERF_METRICS is defined to 48 instead of -BIT_ULL(48), it's inconsistent with other similar macros. This leads to -this macro is quite easily used wrongly since users thinks it's a -bit-mask just like other similar macros. - -Thus change GLOBAL_CTRL_EN_PERF_METRICS to BIT_ULL(48) and eliminate -this potential misuse. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - arch/x86/events/intel/core.c | 8 ++++---- - arch/x86/include/asm/perf_event.h | 2 +- - 2 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 602a7c33f711..3b51d8ffa5ac 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5468,9 +5468,9 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) - 0, x86_pmu_num_counters(&pmu->pmu), 0, 0); - - if (pmu->intel_cap.perf_metrics) -- pmu->intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS; -+ pmu->intel_ctrl |= GLOBAL_CTRL_EN_PERF_METRICS; - else -- pmu->intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS); -+ pmu->intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS; - - intel_pmu_check_event_constraints_all(&pmu->pmu); - -@@ -5602,7 +5602,7 @@ static void intel_pmu_cpu_starting(int cpu) - rdmsrq(MSR_IA32_PERF_CAPABILITIES, perf_cap.capabilities); - if (!perf_cap.perf_metrics) { - x86_pmu.intel_cap.perf_metrics = 0; -- x86_pmu.intel_ctrl &= ~(1ULL << GLOBAL_CTRL_EN_PERF_METRICS); -+ x86_pmu.intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS; - } - } - -@@ -7934,7 +7934,7 @@ __init int intel_pmu_init(void) - } - - if (!is_hybrid() && x86_pmu.intel_cap.perf_metrics) -- x86_pmu.intel_ctrl |= 1ULL << GLOBAL_CTRL_EN_PERF_METRICS; -+ x86_pmu.intel_ctrl |= GLOBAL_CTRL_EN_PERF_METRICS; - - if (x86_pmu.intel_cap.pebs_timing_info) - x86_pmu.flags |= PMU_FL_RETIRE_LATENCY; -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 70d1d94aca7e..f8247ac276c4 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -430,7 +430,7 @@ static inline bool is_topdown_idx(int idx) - #define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(GLOBAL_STATUS_TRACE_TOPAPMI_BIT) - #define GLOBAL_STATUS_PERF_METRICS_OVF_BIT 48 - --#define GLOBAL_CTRL_EN_PERF_METRICS 48 -+#define GLOBAL_CTRL_EN_PERF_METRICS BIT_ULL(48) - /* - * We model guest LBR event tracing as another fixed-mode PMC like BTS. - * --- -2.43.0 - diff --git a/SPECS/kernel/0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf b/SPECS/kernel/0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf new file mode 100644 index 000000000..3863acc96 --- /dev/null +++ b/SPECS/kernel/0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf @@ -0,0 +1,82 @@ +From dfc75ded70ed507b85d920b00ce199d2821c8b61 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Mon, 29 Dec 2025 11:41:02 -0800 +Subject: [PATCH 07/13] perf/x86/intel/uncore: Add domain global init callback + +In the Intel uncore self-describing mechanism, the Global Control +Register freeze_all bit is SoC-wide and propagates to all uncore PMUs. + +On Diamond Rapids, this bit is set at power-on, unlike some prior +platforms. Add a global_init callback to unfreeze all PMON units. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 16 ++++++++++++++++ + arch/x86/events/intel/uncore.h | 2 ++ + arch/x86/events/intel/uncore_discovery.c | 3 +++ + 3 files changed, 21 insertions(+) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index eb57eb7159959..34d0822dc0022 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1697,6 +1697,21 @@ static int __init uncore_mmio_init(void) + return ret; + } + ++static int uncore_mmio_global_init(u64 ctl) ++{ ++ void __iomem *io_addr; ++ ++ io_addr = ioremap(ctl, sizeof(ctl)); ++ if (!io_addr) ++ return -ENOMEM; ++ ++ /* Clear bit 0, all other bits are reserved */ ++ writel(0, io_addr); ++ ++ iounmap(io_addr); ++ return 0; ++} ++ + static const struct uncore_plat_init nhm_uncore_init __initconst = { + .cpu_init = nhm_uncore_cpu_init, + }; +@@ -1839,6 +1854,7 @@ static const struct uncore_plat_init dmr_uncore_init __initconst = { + .domain[0].units_ignore = dmr_uncore_imh_units_ignore, + .domain[1].discovery_base = CBB_UNCORE_DISCOVERY_MSR, + .domain[1].units_ignore = dmr_uncore_cbb_units_ignore, ++ .domain[1].global_init = uncore_mmio_global_init, + }; + + static const struct uncore_plat_init generic_uncore_init __initconst = { +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 83d01a9cefc00..55e3aebf4b5e0 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -51,6 +51,8 @@ struct uncore_discovery_domain { + /* MSR address or PCI device used as the discovery base */ + u32 discovery_base; + bool base_is_pci; ++ int (*global_init)(u64 ctl); ++ + /* The units in the discovery table should be ignored. */ + int *units_ignore; + }; +diff --git a/arch/x86/events/intel/uncore_discovery.c b/arch/x86/events/intel/uncore_discovery.c +index 24b8da18ee6c8..9779e38e272e0 100644 +--- a/arch/x86/events/intel/uncore_discovery.c ++++ b/arch/x86/events/intel/uncore_discovery.c +@@ -287,6 +287,9 @@ static int __parse_discovery_table(char *type, + if (!io_addr) + return -ENOMEM; + ++ if (domain->global_init && domain->global_init(global.ctl)) ++ return -ENODEV; ++ + /* Parsing Unit Discovery State */ + for (i = 0; i < global.max_units; i++) { + memcpy_fromio(&unit, io_addr + (i + 1) * (global.stride * 8), +-- +2.43.0 + diff --git a/SPECS/kernel/0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss b/SPECS/kernel/0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss deleted file mode 100644 index f34cab9a1..000000000 --- a/SPECS/kernel/0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss +++ /dev/null @@ -1,29 +0,0 @@ -From 41e6ad2f14364ace8d33778371c0e438605eb6d1 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Thu, 17 Apr 2025 11:29:27 +0300 -Subject: [PATCH 07/16] spi: intel: Add support for Intel Wildcat Lake SPI - serial flash - -Add Intel Wildcat Lake SPI serial flash PCI ID to the list of supported -devices. - -Signed-off-by: Mika Westerberg ---- - drivers/spi/spi-intel-pci.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/spi/spi-intel-pci.c b/drivers/spi/spi-intel-pci.c -index 49b4d3061197..7765fb27c37c 100644 ---- a/drivers/spi/spi-intel-pci.c -+++ b/drivers/spi/spi-intel-pci.c -@@ -75,6 +75,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x38a4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x43a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, -+ { PCI_VDEVICE(INTEL, 0x4d23), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, - { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, - { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, --- -2.43.0 - diff --git a/SPECS/kernel/0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu b/SPECS/kernel/0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu deleted file mode 100644 index d93aab2b4..000000000 --- a/SPECS/kernel/0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu +++ /dev/null @@ -1,38 +0,0 @@ -From 3d690acb7efc7810580da8caa2a8aa0df7321882 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Thu, 11 Sep 2025 15:24:17 +0800 -Subject: [PATCH 07/11] staging: media: ipu7 remove from the Makefile & Kconfig - -Signed-off-by: linya14x ---- - drivers/staging/media/Kconfig | 2 +- - drivers/staging/media/Makefile | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig -index ab250c89cd4d..6da885037207 100644 ---- a/drivers/staging/media/Kconfig -+++ b/drivers/staging/media/Kconfig -@@ -28,7 +28,7 @@ source "drivers/staging/media/imx/Kconfig" - - source "drivers/staging/media/ipu3/Kconfig" - --source "drivers/staging/media/ipu7/Kconfig" -+#source "drivers/staging/media/ipu7/Kconfig" - - source "drivers/staging/media/max96712/Kconfig" - -diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile -index 4a073938b2b2..22d19580e91f 100644 ---- a/drivers/staging/media/Makefile -+++ b/drivers/staging/media/Makefile -@@ -8,5 +8,5 @@ obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ - obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ - obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ - obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ --obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -+#obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ - obj-$(CONFIG_DVB_AV7110) += av7110/ --- -2.43.0 - diff --git a/SPECS/kernel/0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet b/SPECS/kernel/0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet new file mode 100644 index 000000000..eed26cacc --- /dev/null +++ b/SPECS/kernel/0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet @@ -0,0 +1,38 @@ +From 4f6ac597346ccc5da9307428dd7e955ebe25329e Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Fri, 4 Jun 2021 11:03:50 +0800 +Subject: [PATCH 07/18] stmmac: intel: skip xpcs reset for 2.5Gbps on Intel + AlderLake + +Unlike any other platforms, Intel AlderLake has most of the SerDes +PLL clock configuration done in the BIOS. Hence, we need to avoid +performing a xPCS soft reset on driver load. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index e2915815ee655..bf6544ec769b2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -985,6 +985,7 @@ static int adls_sgmii_phy0_data(struct pci_dev *pdev, + struct plat_stmmacenet_data *plat) + { + plat->bus_id = 1; ++ plat->skip_reset = 1; + + /* SerDes power up and power down are done in BIOS for ADL */ + +@@ -999,6 +1000,7 @@ static int adls_sgmii_phy1_data(struct pci_dev *pdev, + struct plat_stmmacenet_data *plat) + { + plat->bus_id = 2; ++ plat->skip_reset = 1; + + /* SerDes power up and power down are done in BIOS for ADL */ + +-- +2.43.0 + diff --git a/SPECS/kernel/0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal b/SPECS/kernel/0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal deleted file mode 100644 index 323a3da61..000000000 --- a/SPECS/kernel/0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal +++ /dev/null @@ -1,38 +0,0 @@ -From ee41dfa118a71f9a655f827d0bf98d69c2a39624 Mon Sep 17 00:00:00 2001 -From: "Rafael J. Wysocki" -Date: Mon, 25 Aug 2025 15:28:34 +0200 -Subject: [PATCH 07/11] thermal: gov_step_wise: Clarify cooling logic - description comment - -The cooling logic description comment next to the get_target_state() -definition is slightly ambiguous in what it means by "lower cooling -state", so clarify that by replacing the ambuguous phrase with "the -minimum applicable cooling state". - -No functional impact. - -Signed-off-by: Rafael J. Wysocki -Reviewed-by: Lukasz Luba -Link: https://patch.msgid.link/4690596.LvFx2qVVIh@rafael.j.wysocki ---- - drivers/thermal/gov_step_wise.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/thermal/gov_step_wise.c b/drivers/thermal/gov_step_wise.c -index 44945af3ce17..ea277c466d8d 100644 ---- a/drivers/thermal/gov_step_wise.c -+++ b/drivers/thermal/gov_step_wise.c -@@ -25,8 +25,8 @@ - * minimum - * If the temperature is lower than a trip point, - * a. if the trend is THERMAL_TREND_RAISING, do nothing -- * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling -- * state for this trip point, if the cooling state already -+ * b. if the trend is THERMAL_TREND_DROPPING, use the minimum applicable -+ * cooling state for this trip point, or if the cooling state already - * equals lower limit, deactivate the thermal instance - */ - static unsigned long get_target_state(struct thermal_instance *instance, --- -2.43.0 - diff --git a/SPECS/kernel/0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet b/SPECS/kernel/0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet new file mode 100644 index 000000000..87cd1ed48 --- /dev/null +++ b/SPECS/kernel/0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet @@ -0,0 +1,491 @@ +From da173fd67211ecc052baf182b880c5cc69122982 Mon Sep 17 00:00:00 2001 +From: Saeed Mahameed +Date: Tue, 9 Apr 2019 22:01:47 -0700 +Subject: [PATCH 07/14] tools/bpf: Add xdp set command for md btf + +Add new bpftool net subcommand and use it to report and set XDP attrs: + +$ /usr/local/sbin/bpftool net xdp help +Usage: /usr/local/sbin/bpftool net xdp + { show | list | set | md_btf} [dev ] + /usr/local/sbin/bpftool xdp help + +$ /usr/local/sbin/bpftool net xdp set dev mlx0 md_btf on + +$ /usr/local/sbin/bpftool net xdp show +xdp: +mlx0(3) md_btf_id(1) md_btf_enabled(1) + +Signed-off-by: Saeed Mahameed +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + tools/bpf/bpftool/main.h | 2 + + tools/bpf/bpftool/net.c | 7 +- + tools/bpf/bpftool/xdp.c | 308 +++++++++++++++++++++++++++++++++++++++ + tools/lib/bpf/libbpf.h | 1 + + tools/lib/bpf/libbpf.map | 1 + + tools/lib/bpf/netlink.c | 49 +++++++ + 6 files changed, 365 insertions(+), 3 deletions(-) + create mode 100644 tools/bpf/bpftool/xdp.c + +diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h +index 1130299cede0b..64cd05b1072e3 100644 +--- a/tools/bpf/bpftool/main.h ++++ b/tools/bpf/bpftool/main.h +@@ -165,6 +165,7 @@ int do_btf(int argc, char **argv); + + /* non-bootstrap only commands */ + int do_prog(int argc, char **arg) __weak; ++int do_xdp(int argc, char **argv); + int do_map(int argc, char **arg) __weak; + int do_link(int argc, char **arg) __weak; + int do_event_pipe(int argc, char **argv) __weak; +@@ -252,6 +253,7 @@ struct tcmsg; + int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb); + int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind, + const char *devname, int ifindex); ++int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb); + + int print_all_levels(__maybe_unused enum libbpf_print_level level, + const char *format, va_list args); +diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c +index cfc6f944f7c33..f313f5be0960c 100644 +--- a/tools/bpf/bpftool/net.c ++++ b/tools/bpf/bpftool/net.c +@@ -362,7 +362,7 @@ static int netlink_get_link(int sock, unsigned int nl_pid, + dump_link_nlmsg, cookie); + } + +-static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) ++int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) + { + struct bpf_netdev_t *netinfo = cookie; + struct ifinfomsg *ifinfo = msg; +@@ -937,7 +937,7 @@ static int do_show(int argc, char **argv) + jsonw_start_array(json_wtr); + NET_START_OBJECT; + NET_START_ARRAY("xdp", "%s:\n"); +- ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); ++ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); + NET_END_ARRAY("\n"); + + if (!ret) { +@@ -984,7 +984,7 @@ static int do_help(int argc, char **argv) + } + + fprintf(stderr, +- "Usage: %1$s %2$s { show | list } [dev ]\n" ++ "Usage: %1$s %2$s { show | list | xdp } [dev ]\n" + " %1$s %2$s attach ATTACH_TYPE PROG dev [ overwrite ]\n" + " %1$s %2$s detach ATTACH_TYPE dev \n" + " %1$s %2$s help\n" +@@ -1011,6 +1011,7 @@ static const struct cmd cmds[] = { + { "list", do_show }, + { "attach", do_attach }, + { "detach", do_detach }, ++ { "xdp", do_xdp }, + { "help", do_help }, + { 0 } + }; +diff --git a/tools/bpf/bpftool/xdp.c b/tools/bpf/bpftool/xdp.c +new file mode 100644 +index 0000000000000..d33671de877b8 +--- /dev/null ++++ b/tools/bpf/bpftool/xdp.c +@@ -0,0 +1,308 @@ ++// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) ++// Copyright (C) 2019 Mellanox. ++ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "bpf/nlattr.h" ++#include "main.h" ++#include "netlink_dumper.h" ++ ++/* TODO: reuse form net.c */ ++#ifndef SOL_NETLINK ++#define SOL_NETLINK 270 ++#endif ++ ++static int netlink_open(__u32 *nl_pid) ++{ ++ struct sockaddr_nl sa; ++ socklen_t addrlen; ++ int one = 1, ret; ++ int sock; ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.nl_family = AF_NETLINK; ++ ++ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); ++ if (sock < 0) ++ return -errno; ++ ++ if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, ++ &one, sizeof(one)) < 0) { ++ p_err("Netlink error reporting not supported"); ++ } ++ ++ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { ++ ret = -errno; ++ goto cleanup; ++ } ++ ++ addrlen = sizeof(sa); ++ if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { ++ ret = -errno; ++ goto cleanup; ++ } ++ ++ if (addrlen != sizeof(sa)) { ++ ret = -LIBBPF_ERRNO__INTERNAL; ++ goto cleanup; ++ } ++ ++ *nl_pid = sa.nl_pid; ++ return sock; ++ ++cleanup: ++ close(sock); ++ return ret; ++} ++ ++typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); ++ ++typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie); ++ ++static int netlink_recv(int sock, __u32 nl_pid, __u32 seq, ++ __dump_nlmsg_t _fn, dump_nlmsg_t fn, ++ void *cookie) ++{ ++ bool multipart = true; ++ struct nlmsgerr *err; ++ struct nlmsghdr *nh; ++ char buf[4096]; ++ int len, ret; ++ ++ while (multipart) { ++ multipart = false; ++ len = recv(sock, buf, sizeof(buf), 0); ++ if (len < 0) { ++ ret = -errno; ++ goto done; ++ } ++ ++ if (len == 0) ++ break; ++ ++ for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); ++ nh = NLMSG_NEXT(nh, len)) { ++ if (nh->nlmsg_pid != nl_pid) { ++ ret = -LIBBPF_ERRNO__WRNGPID; ++ goto done; ++ } ++ if (nh->nlmsg_seq != seq) { ++ ret = -LIBBPF_ERRNO__INVSEQ; ++ goto done; ++ } ++ if (nh->nlmsg_flags & NLM_F_MULTI) ++ multipart = true; ++ switch (nh->nlmsg_type) { ++ case NLMSG_ERROR: ++ err = (struct nlmsgerr *)NLMSG_DATA(nh); ++ if (!err->error) ++ continue; ++ ret = err->error; ++ libbpf_nla_dump_errormsg(nh); ++ goto done; ++ case NLMSG_DONE: ++ return 0; ++ default: ++ break; ++ } ++ if (_fn) { ++ ret = _fn(nh, fn, cookie); ++ if (ret) ++ return ret; ++ } ++ } ++ } ++ ret = 0; ++done: ++ return ret; ++} ++ ++static int __dump_link_nlmsg(struct nlmsghdr *nlh, ++ dump_nlmsg_t dump_link_nlmsg, void *cookie) ++{ ++ struct nlattr *tb[IFLA_MAX + 1], *attr; ++ struct ifinfomsg *ifi = NLMSG_DATA(nlh); ++ int len; ++ ++ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); ++ attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); ++ if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) ++ return -LIBBPF_ERRNO__NLPARSE; ++ ++ return dump_link_nlmsg(cookie, ifi, tb); ++} ++ ++static int netlink_get_link(int sock, unsigned int nl_pid, ++ dump_nlmsg_t dump_link_nlmsg, void *cookie) ++{ ++ struct { ++ struct nlmsghdr nlh; ++ struct ifinfomsg ifm; ++ } req = { ++ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), ++ .nlh.nlmsg_type = RTM_GETLINK, ++ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, ++ .ifm.ifi_family = AF_PACKET, ++ }; ++ int seq = time(NULL); ++ ++ req.nlh.nlmsg_seq = seq; ++ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) ++ return -errno; ++ ++ return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, ++ dump_link_nlmsg, cookie); ++} ++ ++struct ip_devname_ifindex { ++ char devname[64]; ++ int ifindex; ++}; ++ ++struct bpf_netdev_t { ++ struct ip_devname_ifindex *devices; ++ int used_len; ++ int array_len; ++ int filter_idx; ++}; ++ ++static int do_show(int argc, char **argv) ++{ ++ int sock, ret, filter_idx = -1; ++ struct bpf_netdev_t dev_array; ++ unsigned int nl_pid = 0; ++ char err_buf[256]; ++ ++ if (argc == 2) { ++ if (strcmp(argv[0], "dev") != 0) ++ usage(); ++ filter_idx = if_nametoindex(argv[1]); ++ if (filter_idx == 0) { ++ fprintf(stderr, "invalid dev name %s\n", argv[1]); ++ return -1; ++ } ++ } else if (argc != 0) { ++ usage(); ++ } ++ ++ sock = netlink_open(&nl_pid); ++ if (sock < 0) { ++ fprintf(stderr, "failed to open netlink sock\n"); ++ return -1; ++ } ++ ++ dev_array.devices = NULL; ++ dev_array.used_len = 0; ++ dev_array.array_len = 0; ++ dev_array.filter_idx = filter_idx; ++ ++ if (json_output) ++ jsonw_start_array(json_wtr); ++ NET_START_OBJECT; ++ NET_START_ARRAY("xdp", "%s:\n"); ++ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); ++ NET_END_ARRAY("\n"); ++ ++ NET_END_OBJECT; ++ if (json_output) ++ jsonw_end_array(json_wtr); ++ ++ if (ret) { ++ if (json_output) ++ jsonw_null(json_wtr); ++ libbpf_strerror(ret, err_buf, sizeof(err_buf)); ++ fprintf(stderr, "Error: %s\n", err_buf); ++ } ++ free(dev_array.devices); ++ close(sock); ++ return ret; ++} ++ ++static int set_usage(void) ++{ ++ fprintf(stderr, ++ "Usage: %s net xdp set dev {md_btf {on|off}}\n" ++ " %s net xdp set help\n" ++ " md_btf {on|off}: enable/disable meta data btf\n", ++ bin_name, bin_name); ++ ++ return -1; ++} ++ ++static int xdp_set_md_btf(int ifindex, char *arg) ++{ ++ __u8 enable = (strcmp(arg, "on") == 0) ? 1 : 0; ++ int ret; ++ ++ ret = bpf_set_link_xdp_md_btf(ifindex, enable); ++ if (ret) ++ fprintf(stderr, "Failed to setup xdp md, err=%d\n", ret); ++ ++ return -ret; ++} ++ ++static int do_set(int argc, char **argv) ++{ ++ char *set_cmd, *set_arg; ++ int dev_idx = -1; ++ ++ if (argc < 4) ++ return set_usage(); ++ ++ if (strcmp(argv[0], "dev") != 0) ++ return set_usage(); ++ ++ dev_idx = if_nametoindex(argv[1]); ++ if (dev_idx == 0) { ++ fprintf(stderr, "invalid dev name %s\n", argv[1]); ++ return -1; ++ } ++ ++ set_cmd = argv[2]; ++ set_arg = argv[3]; ++ ++ if (strcmp(set_cmd, "md_btf") != 0) ++ return set_usage(); ++ ++ if (strcmp(set_arg, "on") != 0 && strcmp(set_arg, "off") != 0) ++ return set_usage(); ++ ++ return xdp_set_md_btf(dev_idx, set_arg); ++} ++ ++static int do_help(int argc, char **argv) ++{ ++ if (json_output) { ++ jsonw_null(json_wtr); ++ return 0; ++ } ++ ++ fprintf(stderr, ++ "Usage: %s %s xdp { show | list | set } [dev ]\n" ++ " %s %s help\n", ++ bin_name, argv[-2], bin_name, argv[-2]); ++ ++ return 0; ++} ++ ++static const struct cmd cmds[] = { ++ { "show", do_show }, ++ { "list", do_show }, ++ { "set", do_set }, ++ { "help", do_help }, ++ { 0 } ++}; ++ ++int do_xdp(int argc, char **argv) ++{ ++ return cmd_select(cmds, argc, argv, do_help); ++} +diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h +index 5118d0a90e243..846309ddcb246 100644 +--- a/tools/lib/bpf/libbpf.h ++++ b/tools/lib/bpf/libbpf.h +@@ -1644,6 +1644,7 @@ LIBBPF_API struct perf_buffer * + perf_buffer__new(int map_fd, size_t page_cnt, + perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx, + const struct perf_buffer_opts *opts); ++LIBBPF_API int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable); + + enum bpf_perf_event_ret { + LIBBPF_PERF_EVENT_DONE = 0, +diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map +index 8ed8749907d47..c20311e4ce122 100644 +--- a/tools/lib/bpf/libbpf.map ++++ b/tools/lib/bpf/libbpf.map +@@ -73,6 +73,7 @@ LIBBPF_0.0.2 { + bpf_map_lookup_elem_flags; + bpf_object__btf; + bpf_object__find_map_fd_by_name; ++ bpf_set_link_xdp_md_btf; + btf__get_raw_data; + btf_ext__free; + btf_ext__get_raw_data; +diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c +index c997e69d507fe..204827a4380bf 100644 +--- a/tools/lib/bpf/netlink.c ++++ b/tools/lib/bpf/netlink.c +@@ -341,6 +341,55 @@ int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *o + return bpf_xdp_attach(ifindex, -1, flags, opts); + } + ++int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable) ++{ ++ struct nlattr *nla, *nla_xdp; ++ int sock, seq = 0, ret; ++ __u32 nl_pid; ++ struct { ++ struct nlmsghdr nh; ++ struct ifinfomsg ifinfo; ++ char attrbuf[64]; ++ } req; ++ ++ sock = libbpf_netlink_open(&nl_pid, NETLINK_ROUTE); ++ if (sock < 0) ++ return sock; ++ ++ memset(&req, 0, sizeof(req)); ++ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); ++ req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; ++ req.nh.nlmsg_type = RTM_SETLINK; ++ req.nh.nlmsg_pid = 0; ++ req.nh.nlmsg_seq = ++seq; ++ req.ifinfo.ifi_family = AF_UNSPEC; ++ req.ifinfo.ifi_index = ifindex; ++ ++ /* started nested attribute for XDP */ ++ nla = (struct nlattr *)(((char *)&req) ++ + NLMSG_ALIGN(req.nh.nlmsg_len)); ++ nla->nla_type = NLA_F_NESTED | IFLA_XDP; ++ nla->nla_len = NLA_HDRLEN; ++ /* add XDP MD setup */ ++ nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); ++ nla_xdp->nla_type = IFLA_XDP_MD_BTF_STATE; ++ nla_xdp->nla_len = NLA_HDRLEN + sizeof(__u8); ++ memcpy((char *)nla_xdp + NLA_HDRLEN, &enable, sizeof(__u8)); ++ nla->nla_len += nla_xdp->nla_len; ++ ++ req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); ++ ++ if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { ++ ret = -errno; ++ goto cleanup; ++ } ++ ret = libbpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); ++ ++cleanup: ++ close(sock); ++ return ret; ++} ++ + static int __dump_link_nlmsg(struct nlmsghdr *nlh, + libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) + { +-- +2.43.0 + diff --git a/SPECS/kernel/0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet b/SPECS/kernel/0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet deleted file mode 100644 index b41a6629e..000000000 --- a/SPECS/kernel/0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet +++ /dev/null @@ -1,83 +0,0 @@ -From 0b9a15dbd24a0f814fa8862eb7c889b705fa8918 Mon Sep 17 00:00:00 2001 -From: Saeed Mahameed -Date: Tue, 9 Apr 2019 14:52:02 -0700 -Subject: [PATCH 07/19] tools/bpf: Query XDP metadata BTF ID - -When dumping bpf net information, also query XDP MD BTF attributes: - -$ /usr/local/sbin/bpftool net -xdp: -mlx0(3) md_btf_id(1) md_btf_enabled(0) - -tc: - -flow_dissector: - -Signed-off-by: Saeed Mahameed -Signed-off-by: Aravindhan Gunasekaran ---- - tools/bpf/bpftool/netlink_dumper.c | 21 +++++++++++++++++---- - tools/include/uapi/linux/if_link.h | 2 ++ - 2 files changed, 19 insertions(+), 4 deletions(-) - -diff --git a/tools/bpf/bpftool/netlink_dumper.c b/tools/bpf/bpftool/netlink_dumper.c -index 0a3c7e96c797..949779c3f9ee 100644 ---- a/tools/bpf/bpftool/netlink_dumper.c -+++ b/tools/bpf/bpftool/netlink_dumper.c -@@ -29,23 +29,36 @@ static void xdp_dump_prog_id(struct nlattr **tb, int attr, - static int do_xdp_dump_one(struct nlattr *attr, unsigned int ifindex, - const char *name) - { -+ unsigned char mode = XDP_ATTACHED_NONE; - struct nlattr *tb[IFLA_XDP_MAX + 1]; -- unsigned char mode; -+ unsigned char md_btf_enabled = 0; -+ unsigned int md_btf_id = 0; -+ bool attached; - - if (libbpf_nla_parse_nested(tb, IFLA_XDP_MAX, attr, NULL) < 0) - return -1; - -- if (!tb[IFLA_XDP_ATTACHED]) -+ if (!tb[IFLA_XDP_ATTACHED] && !tb[IFLA_XDP_MD_BTF_ID]) - return 0; - -- mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); -- if (mode == XDP_ATTACHED_NONE) -+ if (tb[IFLA_XDP_ATTACHED]) -+ mode = libbpf_nla_getattr_u8(tb[IFLA_XDP_ATTACHED]); -+ -+ if (tb[IFLA_XDP_MD_BTF_ID]) { -+ md_btf_id = libbpf_nla_getattr_u32(tb[IFLA_XDP_MD_BTF_ID]); -+ md_btf_enabled = libbpf_nla_getattr_u8(tb[IFLA_XDP_MD_BTF_STATE]); -+ } -+ -+ attached = (mode != XDP_ATTACHED_NONE); -+ if (!attached && !md_btf_id) - return 0; - - NET_START_OBJECT; - if (name) - NET_DUMP_STR("devname", "%s", name); - NET_DUMP_UINT("ifindex", "(%u)", ifindex); -+ NET_DUMP_UINT("md_btf_id", " md_btf_id(%d)", md_btf_id); -+ NET_DUMP_UINT("md_btf_enabled", " md_btf_enabled(%d)", md_btf_enabled); - - if (mode == XDP_ATTACHED_MULTI) { - if (json_output) { -diff --git a/tools/include/uapi/linux/if_link.h b/tools/include/uapi/linux/if_link.h -index 7e46ca4cd31b..4d5b143d4871 100644 ---- a/tools/include/uapi/linux/if_link.h -+++ b/tools/include/uapi/linux/if_link.h -@@ -1899,6 +1899,8 @@ enum { - IFLA_XDP_SKB_PROG_ID, - IFLA_XDP_HW_PROG_ID, - IFLA_XDP_EXPECTED_FD, -+ IFLA_XDP_MD_BTF_ID, -+ IFLA_XDP_MD_BTF_STATE, - __IFLA_XDP_MAX, - }; - --- -2.43.0 - diff --git a/SPECS/kernel/0007-tools-power-turbostat-Remove-dead-code.turbo b/SPECS/kernel/0007-tools-power-turbostat-Remove-dead-code.turbo new file mode 100644 index 000000000..d1b11729b --- /dev/null +++ b/SPECS/kernel/0007-tools-power-turbostat-Remove-dead-code.turbo @@ -0,0 +1,40 @@ +From 286a9f326584d99b206a35959e027ae8f2424b24 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Wed, 22 Oct 2025 20:26:33 -0300 +Subject: [PATCH 07/21] tools/power turbostat: Remove dead code + +amperf_group_fd is never used. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 47cb723430389..f63525a1877c7 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -466,8 +466,6 @@ static void bic_groups_init(void) + #define PCL_10 14 /* PC10 */ + #define PCLUNL 15 /* Unlimited */ + +-struct amperf_group_fd; +- + char *proc_stat = "/proc/stat"; + FILE *outf; + int *fd_percpu; +@@ -4418,11 +4416,6 @@ int get_core_throt_cnt(int cpu, unsigned long long *cnt) + return 0; + } + +-struct amperf_group_fd { +- int aperf; /* Also the group descriptor */ +- int mperf; +-}; +- + static int read_perf_counter_info(const char *const path, const char *const parse_format, void *value_ptr) + { + int fdmt; +-- +2.43.0 + diff --git a/SPECS/kernel/0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac b/SPECS/kernel/0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac deleted file mode 100644 index e0e724d8a..000000000 --- a/SPECS/kernel/0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac +++ /dev/null @@ -1,43 +0,0 @@ -From 243c1b5ae04cd5b3358dc03d33cb37deaf5a7b9c Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Tue, 29 Jul 2025 16:14:39 +0800 -Subject: [PATCH 08/13] EDAC/skx_common: Remove unused *NUM*_IMC macros - -There are no references to the *NUM*_IMC macros, so remove them. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/skx_common.h | 5 ----- - 1 file changed, 5 deletions(-) - -diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h -index e7038fd45d06..73ba89786cdf 100644 ---- a/drivers/edac/skx_common.h -+++ b/drivers/edac/skx_common.h -@@ -29,23 +29,18 @@ - #define GET_BITFIELD(v, lo, hi) \ - (((v) & GENMASK_ULL((hi), (lo))) >> (lo)) - --#define SKX_NUM_IMC 2 /* Memory controllers per socket */ - #define SKX_NUM_CHANNELS 3 /* Channels per memory controller */ - #define SKX_NUM_DIMMS 2 /* Max DIMMS per channel */ - --#define I10NM_NUM_DDR_IMC 12 - #define I10NM_NUM_DDR_CHANNELS 2 - #define I10NM_NUM_DDR_DIMMS 2 - --#define I10NM_NUM_HBM_IMC 16 - #define I10NM_NUM_HBM_CHANNELS 2 - #define I10NM_NUM_HBM_DIMMS 1 - --#define I10NM_NUM_IMC (I10NM_NUM_DDR_IMC + I10NM_NUM_HBM_IMC) - #define I10NM_NUM_CHANNELS MAX(I10NM_NUM_DDR_CHANNELS, I10NM_NUM_HBM_CHANNELS) - #define I10NM_NUM_DIMMS MAX(I10NM_NUM_DDR_DIMMS, I10NM_NUM_HBM_DIMMS) - --#define NUM_IMC MAX(SKX_NUM_IMC, I10NM_NUM_IMC) - #define NUM_CHANNELS MAX(SKX_NUM_CHANNELS, I10NM_NUM_CHANNELS) - #define NUM_DIMMS MAX(SKX_NUM_DIMMS, I10NM_NUM_DIMMS) - --- -2.43.0 - diff --git a/SPECS/kernel/0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security b/SPECS/kernel/0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security new file mode 100644 index 000000000..07e4fecb7 --- /dev/null +++ b/SPECS/kernel/0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security @@ -0,0 +1,338 @@ +From d500ed859e1abc17c8f388f8e98632d8d48ada1e Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Thu, 20 Jan 2022 10:33:17 +0200 +Subject: [PATCH 8/8] INTEL_DII: mei: add empty handlers for ops functions + +When the offline bit is set and the device is being removed +we should prevent the driver from accessing the hardware because +at this stage the hardware may be no longer available. +Replace the operation handlers with the empty ones to ensure +no hardware registers are being accessed by the driver. + +Co-developed-by: Tomas Winkler +Co-developed-by: Vitaly Lubart +Signed-off-by: Alexander Usyskin +--- + drivers/misc/mei/gsc-me.c | 292 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 292 insertions(+) + +diff --git a/drivers/misc/mei/gsc-me.c b/drivers/misc/mei/gsc-me.c +index 72e1d195dce9a..4e84f75a7efd0 100644 +--- a/drivers/misc/mei/gsc-me.c ++++ b/drivers/misc/mei/gsc-me.c +@@ -23,6 +23,11 @@ + + #define MEI_GSC_RPM_TIMEOUT 2000 + ++static inline bool mei_gsc_hw_is_unavailable(const struct device *dev) ++{ ++ return dev->offline; ++} ++ + static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val) + { + struct mei_me_hw *hw = to_me_hw(dev); +@@ -43,6 +48,291 @@ static void mei_gsc_set_ext_op_mem(const struct mei_me_hw *hw, struct resource * + iowrite32(limit, hw->mem_addr + H_GSC_EXT_OP_MEM_LIMIT_REG); + } + ++/** ++ * mei_gsc_mecbrw_read_null - read 32bit data from ME circular buffer (empty implementation) ++ * read window register ++ * ++ * @dev: the device structure ++ * ++ * Return: always 0 ++ */ ++static inline u32 mei_gsc_mecbrw_read_null(const struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_trc_status_null - read trc status register (empty implementation) ++ * ++ * @dev: mei device ++ * @trc: trc status register value ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_trc_status_null(struct mei_device *dev, u32 *trc) ++{ ++ *trc = 0; ++ return 0; ++} ++ ++/** ++ * mei_gsc_fw_status_null - read fw status register from pci config space (empty implementation) ++ * ++ * @dev: mei device ++ * @fw_status: fw status register values ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_fw_status_null(struct mei_device *dev, ++ struct mei_fw_status *fw_status) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_hw_config_null - configure hw dependent settings (empty implementation) ++ * ++ * @dev: mei device ++ * ++ * Return: always 0 ++ * ++ */ ++static int mei_gsc_hw_config_null(struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_pg_state_null - translate internal pg state (empty implementation) ++ * to the mei power gating state ++ * ++ * @dev: mei device ++ * ++ * Return: always MEI_PG_OFF ++ */ ++static inline enum mei_pg_state mei_gsc_pg_state_null(struct mei_device *dev) ++{ ++ return MEI_PG_OFF; ++} ++ ++/** ++ * mei_gsc_intr_clear_null - clear and stop interrupts (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_intr_clear_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_intr_enable_null - enables mei device interrupts (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_intr_enable_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_intr_disable_null - disables mei device interrupts (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_intr_disable_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_synchronize_irq_null - wait for pending IRQ handlers (empty implementation) ++ * ++ * @dev: the device structure ++ */ ++static void mei_gsc_synchronize_irq_null(struct mei_device *dev) ++{ ++} ++ ++/** ++ * mei_gsc_host_is_ready_null - check whether the host has turned ready (empty implementation) ++ * ++ * @dev: mei device ++ * Return: always true ++ */ ++static bool mei_gsc_host_is_ready_null(struct mei_device *dev) ++{ ++ return true; ++} ++ ++/** ++ * mei_gsc_hw_start_null - hw start routine (empty implementation) ++ * ++ * @dev: mei device ++ * Return: always 0 ++ */ ++static int mei_gsc_hw_start_null(struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_hbuf_is_empty_null - checks if host buffer is empty (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always true ++ */ ++static bool mei_gsc_hbuf_is_empty_null(struct mei_device *dev) ++{ ++ return true; ++} ++ ++/** ++ * mei_gsc_hbuf_empty_slots_null - counts write empty slots (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always -EOVERFLOW ++ */ ++static int mei_gsc_hbuf_empty_slots_null(struct mei_device *dev) ++{ ++ return -EOVERFLOW; ++} ++ ++/** ++ * mei_gsc_hbuf_depth_null - returns depth of the hw buffer (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always 1 ++ */ ++static u32 mei_gsc_hbuf_depth_null(const struct mei_device *dev) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_hbuf_write_null - writes a message to host hw buffer (empty implementation) ++ * ++ * @dev: the device structure ++ * @hdr: header of message ++ * @hdr_len: header length in bytes: must be multiplication of a slot (4bytes) ++ * @data: payload ++ * @data_len: payload length in bytes ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_hbuf_write_null(struct mei_device *dev, ++ const void *hdr, size_t hdr_len, ++ const void *data, size_t data_len) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_count_full_read_slots_null - counts read full slots (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always -EOVERFLOW ++ */ ++static int mei_gsc_count_full_read_slots_null(struct mei_device *dev) ++{ ++ return -EOVERFLOW; ++} ++ ++/** ++ * mei_gsc_read_slots_null - reads a message from mei device (empty implementation) ++ * ++ * @dev: the device structure ++ * @buffer: message buffer will be written ++ * @buffer_length: message size will be read ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_read_slots_null(struct mei_device *dev, unsigned char *buffer, ++ unsigned long buffer_length) ++{ ++ return 0; ++} ++ ++/** ++ * mei_gsc_pg_in_transition_null - is device now in pg transition (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always false ++ */ ++static bool mei_gsc_pg_in_transition_null(struct mei_device *dev) ++{ ++ return false; ++} ++ ++/** ++ * mei_gsc_pg_is_enabled_null - detect if PG is supported by HW (empty implementation) ++ * ++ * @dev: the device structure ++ * ++ * Return: always false ++ */ ++static bool mei_gsc_pg_is_enabled_null(struct mei_device *dev) ++{ ++ return false; ++} ++ ++/** ++ * mei_gsc_hw_is_ready_null - check whether the me(hw) has turned ready (empty implementation) ++ * ++ * @dev: mei device ++ * Return: always true ++ */ ++static bool mei_gsc_hw_is_ready_null(struct mei_device *dev) ++{ ++ return true; ++} ++ ++/** ++ * mei_gsc_hw_reset_null - resets fw via mei csr register (empty implementation) ++ * ++ * @dev: the device structure ++ * @intr_enable: if interrupt should be enabled after reset. ++ * ++ * Return: always 0 ++ */ ++static int mei_gsc_hw_reset_null(struct mei_device *dev, bool intr_enable) ++{ ++ return 0; ++} ++ ++static const struct mei_hw_ops mei_gsc_hw_ops_null = { ++ .trc_status = mei_gsc_trc_status_null, ++ .fw_status = mei_gsc_fw_status_null, ++ .pg_state = mei_gsc_pg_state_null, ++ ++ .host_is_ready = mei_gsc_host_is_ready_null, ++ ++ .hw_is_ready = mei_gsc_hw_is_ready_null, ++ .hw_reset = mei_gsc_hw_reset_null, ++ .hw_config = mei_gsc_hw_config_null, ++ .hw_start = mei_gsc_hw_start_null, ++ ++ .pg_in_transition = mei_gsc_pg_in_transition_null, ++ .pg_is_enabled = mei_gsc_pg_is_enabled_null, ++ ++ .intr_clear = mei_gsc_intr_clear_null, ++ .intr_enable = mei_gsc_intr_enable_null, ++ .intr_disable = mei_gsc_intr_disable_null, ++ .synchronize_irq = mei_gsc_synchronize_irq_null, ++ ++ .hbuf_free_slots = mei_gsc_hbuf_empty_slots_null, ++ .hbuf_is_ready = mei_gsc_hbuf_is_empty_null, ++ .hbuf_depth = mei_gsc_hbuf_depth_null, ++ ++ .write = mei_gsc_hbuf_write_null, ++ ++ .rdbuf_full_slots = mei_gsc_count_full_read_slots_null, ++ .read_hdr = mei_gsc_mecbrw_read_null, ++ .read = mei_gsc_read_slots_null ++}; ++ + static int mei_gsc_probe(struct auxiliary_device *aux_dev, + const struct auxiliary_device_id *aux_dev_id) + { +@@ -143,6 +433,8 @@ static void mei_gsc_remove(struct auxiliary_device *aux_dev) + + dev = dev_get_drvdata(&aux_dev->dev); + hw = to_me_hw(dev); ++ if (mei_gsc_hw_is_unavailable(&aux_dev->dev)) ++ dev->ops = &mei_gsc_hw_ops_null; + + mei_stop(dev); + +-- +2.43.0 + diff --git a/SPECS/kernel/0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi b/SPECS/kernel/0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi deleted file mode 100644 index 16a00f006..000000000 --- a/SPECS/kernel/0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi +++ /dev/null @@ -1,86 +0,0 @@ -From f2415943450c73de269f65d31e4fb685167447e5 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 31 Aug 2023 01:23:00 -0700 -Subject: [PATCH 08/44] KVM: VMX: Save/restore guest FRED RSP0 - -Save guest FRED RSP0 in vmx_prepare_switch_to_host() and restore it -in vmx_prepare_switch_to_guest() because MSR_IA32_FRED_RSP0 is passed -through to the guest, thus is volatile/unknown. - -Note, host FRED RSP0 is restored in arch_exit_to_user_mode_prepare(), -regardless of whether it is modified in KVM. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Remove the cpu_feature_enabled() check when set/get guest - MSR_IA32_FRED_RSP0, as guest_cpu_cap_has() should suffice (Sean). -* Add a comment when synchronizing current MSR_IA32_FRED_RSP0 MSR to - the kernel's local cache, because its handling is different from - the MSR_KERNEL_GS_BASE handling (Sean). -* Add TB from Xuelian Guo. - -Changes in v3: -* KVM only needs to save/restore guest FRED RSP0 now as host FRED RSP0 - is restored in arch_exit_to_user_mode_prepare() (Sean Christopherson). - -Changes in v2: -* Don't use guest_cpuid_has() in vmx_prepare_switch_to_{host,guest}(), - which are called from IRQ-disabled context (Chao Gao). -* Reset msr_guest_fred_rsp0 in __vmx_vcpu_reset() (Chao Gao). ---- - arch/x86/kvm/vmx/vmx.c | 14 ++++++++++++++ - arch/x86/kvm/vmx/vmx.h | 1 + - 2 files changed, 15 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 2a40dfe9f034..30322c4897cb 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1295,6 +1295,10 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) - } - - wrmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ wrmsrns(MSR_IA32_FRED_RSP0, vmx->msr_guest_fred_rsp0); -+ - #else - savesegment(fs, fs_sel); - savesegment(gs, gs_sel); -@@ -1339,6 +1343,16 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx) - invalidate_tss_limit(); - #ifdef CONFIG_X86_64 - wrmsrq(MSR_KERNEL_GS_BASE, vmx->vt.msr_host_kernel_gs_base); -+ -+ if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED)) { -+ vmx->msr_guest_fred_rsp0 = read_msr(MSR_IA32_FRED_RSP0); -+ /* -+ * Synchronize the current value in hardware to the kernel's -+ * local cache. The desired host RSP0 will be set when the -+ * CPU exits to userspace (RSP0 is a per-task value). -+ */ -+ fred_sync_rsp0(vmx->msr_guest_fred_rsp0); -+ } - #endif - load_fixmap_gdt(raw_smp_processor_id()); - vmx->vt.guest_state_loaded = false; -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 50656aa3f57a..05fa52a7581c 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -227,6 +227,7 @@ struct vcpu_vmx { - bool guest_uret_msrs_loaded; - #ifdef CONFIG_X86_64 - u64 msr_guest_kernel_gs_base; -+ u64 msr_guest_fred_rsp0; - #endif - - u64 spec_ctrl; --- -2.43.0 - diff --git a/SPECS/kernel/0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi b/SPECS/kernel/0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi new file mode 100644 index 000000000..872a4c8a3 --- /dev/null +++ b/SPECS/kernel/0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi @@ -0,0 +1,120 @@ +From d88c4e614915e2887af1eaf55bd4d49cda3ba8d6 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 31 Aug 2023 00:56:30 -0700 +Subject: [PATCH 08/44] KVM: VMX: Set FRED MSR intercepts + +On a userspace MSR filter change, set FRED MSR intercepts. + +The eight FRED MSRs, MSR_IA32_FRED_RSP[123], MSR_IA32_FRED_STKLVLS, +MSR_IA32_FRED_SSP[123] and MSR_IA32_FRED_CONFIG, are all safe to +passthrough, because each has a corresponding host and guest field +in VMCS. + +Both MSR_IA32_FRED_RSP0 and MSR_IA32_FRED_SSP0 (aka MSR_IA32_PL0_SSP) +are dedicated for userspace event delivery, IOW they are NOT used in +any kernel event delivery and the execution of ERETS. Thus KVM can +run safely with guest values in the two MSRs. As a result, save and +restore of their guest values are deferred until vCPU context switch, +Host MSR_IA32_FRED_RSP0 is restored upon returning to userspace, and +Host MSR_IA32_PL0_SSP is managed with XRSTORS/XSAVES. + +Note, FRED SSP MSRs, including MSR_IA32_PL0_SSP, are available on +any processor that enumerates FRED. On processors that support FRED +but not CET, FRED transitions do not use these MSRs, but they remain +accessible via MSR instructions such as RDMSR and WRMSR. + +Intercept MSR_IA32_PL0_SSP when CET shadow stack is not supported, +regardless of FRED support. This ensures the guest value remains +fully virtual and does not modify the hardware FRED SSP0 MSR. + +This behavior is consistent with the current setup in +vmx_recalc_msr_intercepts(), so no change is needed to the interception +logic for MSR_IA32_PL0_SSP. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v7: +* Rewrite the changelog and comment, majorly for MSR_IA32_PL0_SSP. + +Changes in v5: +* Skip execution of vmx_set_intercept_for_fred_msr() if FRED is + not available or enabled (Sean). +* Use 'intercept' as the variable name to indicate whether MSR + interception should be enabled (Sean). +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/vmx/vmx.c | 47 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 47 insertions(+) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 94f88bf8bb785..1c639496a7562 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -4127,6 +4127,51 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) + } + } + ++static void vmx_set_intercept_for_fred_msr(struct kvm_vcpu *vcpu) ++{ ++ bool intercept = !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED); ++ ++ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) ++ return; ++ ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP1, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP2, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP3, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_STKLVLS, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP1, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP2, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_SSP3, MSR_TYPE_RW, intercept); ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_CONFIG, MSR_TYPE_RW, intercept); ++ ++ /* ++ * MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP (aka MSR_IA32_FRED_SSP0) are ++ * designed for event delivery while executing in userspace. Since KVM ++ * operates entirely in kernel mode (CPL is always 0 after any VM exit), ++ * it can safely retain and operate with guest-defined values for these ++ * MSRs. ++ * ++ * As a result, interception of MSR_IA32_FRED_RSP0 and MSR_IA32_PL0_SSP ++ * is unnecessary. ++ * ++ * Note: Saving and restoring MSR_IA32_PL0_SSP is part of CET supervisor ++ * context management. However, FRED SSP MSRs, including MSR_IA32_PL0_SSP, ++ * are available on any processor that enumerates FRED. ++ * ++ * On processors that support FRED but not CET, FRED transitions do not ++ * use these MSRs, but they remain accessible via MSR instructions such ++ * as RDMSR and WRMSR. ++ * ++ * Intercept MSR_IA32_PL0_SSP when CET shadow stack is not supported, ++ * regardless of FRED support. This ensures the guest value remains ++ * fully virtual and does not modify the hardware FRED SSP0 MSR. ++ * ++ * This behavior is consistent with the current setup in ++ * vmx_recalc_msr_intercepts(), so no change is needed to the interception ++ * logic for MSR_IA32_PL0_SSP. ++ */ ++ vmx_set_intercept_for_msr(vcpu, MSR_IA32_FRED_RSP0, MSR_TYPE_RW, intercept); ++} ++ + static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) + { + bool intercept; +@@ -4193,6 +4238,8 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) + vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, intercept); + } + ++ vmx_set_intercept_for_fred_msr(vcpu); ++ + /* + * x2APIC and LBR MSR intercepts are modified on-demand and cannot be + * filtered by userspace. +-- +2.43.0 + diff --git a/SPECS/kernel/0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet b/SPECS/kernel/0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet deleted file mode 100644 index afc7673c6..000000000 --- a/SPECS/kernel/0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet +++ /dev/null @@ -1,123 +0,0 @@ -From aa07693d9d006cf602f6fc1140a5d93b76f874b7 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:38 -0700 -Subject: [PATCH 08/24] KVM: x86: Refresh CPUID on write to guest MSR_IA32_XSS - -Update CPUID.(EAX=0DH,ECX=1).EBX to reflect current required xstate size -due to XSS MSR modification. -CPUID(EAX=0DH,ECX=1).EBX reports the required storage size of all enabled -xstate features in (XCR0 | IA32_XSS). The CPUID value can be used by guest -before allocate sufficient xsave buffer. - -Note, KVM does not yet support any XSS based features, i.e. supported_xss -is guaranteed to be zero at this time. - -Opportunistically return KVM_MSR_RET_UNSUPPORTED if guest CPUID doesn't -enumerate it. Since KVM_MSR_RET_UNSUPPORTED takes care of host_initiated -cases, drop the host_initiated check. - -Suggested-by: Sean Christopherson -Co-developed-by: Zhang Yi Z -Signed-off-by: Zhang Yi Z -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 3 ++- - arch/x86/kvm/cpuid.c | 15 ++++++++++++++- - arch/x86/kvm/x86.c | 9 +++++---- - 3 files changed, 21 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 475a81620e88..6b567512984a 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -816,7 +816,6 @@ struct kvm_vcpu_arch { - bool at_instruction_boundary; - bool tpr_access_reporting; - bool xfd_no_write_intercept; -- u64 ia32_xss; - u64 microcode_version; - u64 arch_capabilities; - u64 perf_capabilities; -@@ -877,6 +876,8 @@ struct kvm_vcpu_arch { - - u64 xcr0; - u64 guest_supported_xcr0; -+ u64 guest_supported_xss; -+ u64 ia32_xss; - - struct kvm_pio_request pio; - void *pio_data; -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index 5e43a4999a4b..b9f278efb907 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -263,6 +263,17 @@ static u64 cpuid_get_supported_xcr0(struct kvm_vcpu *vcpu) - return (best->eax | ((u64)best->edx << 32)) & kvm_caps.supported_xcr0; - } - -+static u64 cpuid_get_supported_xss(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_cpuid_entry2 *best; -+ -+ best = kvm_find_cpuid_entry_index(vcpu, 0xd, 1); -+ if (!best) -+ return 0; -+ -+ return (best->ecx | ((u64)best->edx << 32)) & kvm_caps.supported_xss; -+} -+ - static __always_inline void kvm_update_feature_runtime(struct kvm_vcpu *vcpu, - struct kvm_cpuid_entry2 *entry, - unsigned int x86_feature, -@@ -305,7 +316,8 @@ static void kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu) - best = kvm_find_cpuid_entry_index(vcpu, 0xD, 1); - if (best && (cpuid_entry_has(best, X86_FEATURE_XSAVES) || - cpuid_entry_has(best, X86_FEATURE_XSAVEC))) -- best->ebx = xstate_required_size(vcpu->arch.xcr0, true); -+ best->ebx = xstate_required_size(vcpu->arch.xcr0 | -+ vcpu->arch.ia32_xss, true); - } - - static bool kvm_cpuid_has_hyperv(struct kvm_vcpu *vcpu) -@@ -424,6 +436,7 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - } - - vcpu->arch.guest_supported_xcr0 = cpuid_get_supported_xcr0(vcpu); -+ vcpu->arch.guest_supported_xss = cpuid_get_supported_xss(vcpu); - - vcpu->arch.pv_cpuid.features = kvm_apply_cpuid_pv_features_quirk(vcpu); - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 3445a446e4ba..3057ae88b6ba 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -3976,16 +3976,17 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - } - break; - case MSR_IA32_XSS: -- if (!msr_info->host_initiated && -- !guest_cpuid_has(vcpu, X86_FEATURE_XSAVES)) -- return 1; -+ if (!guest_cpuid_has(vcpu, X86_FEATURE_XSAVES)) -+ return KVM_MSR_RET_UNSUPPORTED; - /* - * KVM supports exposing PT to the guest, but does not support - * IA32_XSS[bit 8]. Guests have to use RDMSR/WRMSR rather than - * XSAVES/XRSTORS to save/restore PT MSRs. - */ -- if (data & ~kvm_caps.supported_xss) -+ if (data & ~vcpu->arch.guest_supported_xss) - return 1; -+ if (vcpu->arch.ia32_xss == data) -+ break; - vcpu->arch.ia32_xss = data; - vcpu->arch.cpuid_dynamic_bits_dirty = true; - break; --- -2.43.0 - diff --git a/SPECS/kernel/0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt b/SPECS/kernel/0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt new file mode 100644 index 000000000..1c26af588 --- /dev/null +++ b/SPECS/kernel/0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt @@ -0,0 +1,29 @@ +From 38d8a801c66aa0397b4503490dc3a092650b6a0c Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Mon, 21 Feb 2022 17:59:14 +0100 +Subject: [PATCH 8/9] Revert "drm/i915: Depend on !PREEMPT_RT." + +Once the known issues are addressed, it should be safe to enable the +driver. + +Acked-by: Tvrtko Ursulin +Signed-off-by: Sebastian Andrzej Siewior +--- + drivers/gpu/drm/i915/Kconfig | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig +index 5e939004b6463..40a9234e6e5dc 100644 +--- a/drivers/gpu/drm/i915/Kconfig ++++ b/drivers/gpu/drm/i915/Kconfig +@@ -3,7 +3,6 @@ config DRM_I915 + tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" + depends on DRM + depends on X86 && PCI +- depends on !PREEMPT_RT + select INTEL_GTT if X86 + select INTERVAL_TREE + # we need shmfs for the swappable backing store, and in particular +-- +2.43.0 + diff --git a/SPECS/kernel/0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl b/SPECS/kernel/0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl new file mode 100644 index 000000000..9dde435ad --- /dev/null +++ b/SPECS/kernel/0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl @@ -0,0 +1,91 @@ +From aabb3d25c197c2cc2d8b7d0234e9ce73a0725125 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Wed, 12 Nov 2025 19:03:08 +0100 +Subject: [PATCH 08/17] cpuidle: governors: teo: Decay metrics below + DECAY_SHIFT threshold + +If a given governor metric falls below a certain value (8 for +DECAY_SHIFT equal to 3), it will not decay any more due to the +simplistic decay implementation. This may in some cases lead to +subtle inconsistencies in the governor behavior, so change the +decay implementation to take it into account and set the metric +at hand to 0 in that case. + +Suggested-by: Christian Loehle +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Tested-by: Christian Loehle +Link: https://patch.msgid.link/2819353.mvXUDI8C0e@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 26 +++++++++++++++++++------- + 1 file changed, 19 insertions(+), 7 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 88ed47e868b9c..8b80d73e518ed 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -148,6 +148,16 @@ struct teo_cpu { + + static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); + ++static void teo_decay(unsigned int *metric) ++{ ++ unsigned int delta = *metric >> DECAY_SHIFT; ++ ++ if (delta) ++ *metric -= delta; ++ else ++ *metric = 0; ++} ++ + /** + * teo_update - Update CPU metrics after wakeup. + * @drv: cpuidle driver containing state data. +@@ -158,8 +168,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + int i, idx_timer = 0, idx_duration = 0; + s64 target_residency_ns, measured_ns; ++ unsigned int total = 0; + +- cpu_data->short_idles -= cpu_data->short_idles >> DECAY_SHIFT; ++ teo_decay(&cpu_data->short_idles); + + if (cpu_data->artificial_wakeup) { + /* +@@ -195,8 +206,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + for (i = 0; i < drv->state_count; i++) { + struct teo_bin *bin = &cpu_data->state_bins[i]; + +- bin->hits -= bin->hits >> DECAY_SHIFT; +- bin->intercepts -= bin->intercepts >> DECAY_SHIFT; ++ teo_decay(&bin->hits); ++ total += bin->hits; ++ teo_decay(&bin->intercepts); ++ total += bin->intercepts; + + target_residency_ns = drv->states[i].target_residency_ns; + +@@ -207,7 +220,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + } + } + +- cpu_data->tick_intercepts -= cpu_data->tick_intercepts >> DECAY_SHIFT; ++ cpu_data->total = total + PULSE; ++ ++ teo_decay(&cpu_data->tick_intercepts); + /* + * If the measured idle duration falls into the same bin as the sleep + * length, this is a "hit", so update the "hits" metric for that bin. +@@ -221,9 +236,6 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + if (TICK_NSEC <= measured_ns) + cpu_data->tick_intercepts += PULSE; + } +- +- cpu_data->total -= cpu_data->total >> DECAY_SHIFT; +- cpu_data->total += PULSE; + } + + static bool teo_state_ok(int i, struct cpuidle_driver *drv) +-- +2.43.0 + diff --git a/SPECS/kernel/0008-drm-i915-Consider-RCU-read-section-as-atomic.rt b/SPECS/kernel/0008-drm-i915-Consider-RCU-read-section-as-atomic.rt deleted file mode 100644 index 8de405be5..000000000 --- a/SPECS/kernel/0008-drm-i915-Consider-RCU-read-section-as-atomic.rt +++ /dev/null @@ -1,37 +0,0 @@ -From 71b2435a33f7ac8a2b1ae42a0d7730fc246fcb82 Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Mon, 14 Jul 2025 17:39:53 +0200 -Subject: [PATCH 8/9] drm/i915: Consider RCU read section as atomic. - -Locking and/ or running inside interrupt handler will not lead to an -atomic section on PREEMPT_RT. The RCU read section needs to be -considered because all locks, which become sleeping locks on -PREEMPT_RT, start a RCU read section. Scheduling/ sleeping while within -a RCU read section is invalid. - -Check for also for RCU read section in stop_timeout() to determine if it -is safe to sleep. - -Reviewed-by: Rodrigo Vivi -Link: https://lore.kernel.org/r/20250714153954.629393-9-bigeasy@linutronix.de -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/gt/intel_engine_cs.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c -index b721bbd23356..f5a6143ea8a2 100644 ---- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c -+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c -@@ -1609,7 +1609,7 @@ u64 intel_engine_get_last_batch_head(const struct intel_engine_cs *engine) - - static unsigned long stop_timeout(const struct intel_engine_cs *engine) - { -- if (in_atomic() || irqs_disabled()) /* inside atomic preempt-reset? */ -+ if (in_atomic() || irqs_disabled() || rcu_preempt_depth()) /* inside atomic preempt-reset? */ - return 0; - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0008-erofs-limit-the-level-of-fs-stacking-for-file-backed.patch b/SPECS/kernel/0008-erofs-limit-the-level-of-fs-stacking-for-file-backed.patch deleted file mode 100644 index a1df1facd..000000000 --- a/SPECS/kernel/0008-erofs-limit-the-level-of-fs-stacking-for-file-backed.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 9faaff36c08f369350f515943ad8ceb47bbfd8c7 Mon Sep 17 00:00:00 2001 -From: Gao Xiang -Date: Sat, 22 Nov 2025 14:23:32 +0800 -Subject: [PATCH 08/51] erofs: limit the level of fs stacking for file-backed - mounts - -Otherwise, it could cause potential kernel stack overflow (e.g., EROFS -mounting itself). - -Reviewed-by: Sheng Yong -Fixes: fb176750266a ("erofs: add file-backed mount support") -Reviewed-by: Chao Yu -Reviewed-by: Hongbo Li -Signed-off-by: Gao Xiang ---- - fs/erofs/super.c | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/fs/erofs/super.c b/fs/erofs/super.c -index db13b40a78e0..09807699b15c 100644 ---- a/fs/erofs/super.c -+++ b/fs/erofs/super.c -@@ -632,6 +632,22 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc) - - sbi->blkszbits = PAGE_SHIFT; - if (!sb->s_bdev) { -+ /* -+ * (File-backed mounts) EROFS claims it's safe to nest other -+ * fs contexts (including its own) due to self-controlled RO -+ * accesses/contexts and no side-effect changes that need to -+ * context save & restore so it can reuse the current thread -+ * context. However, it still needs to bump `s_stack_depth` to -+ * avoid kernel stack overflow from nested filesystems. -+ */ -+ if (erofs_is_fileio_mode(sbi)) { -+ sb->s_stack_depth = -+ file_inode(sbi->dif0.file)->i_sb->s_stack_depth + 1; -+ if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { -+ erofs_err(sb, "maximum fs stacking depth exceeded"); -+ return -ENOTBLK; -+ } -+ } - sb->s_blocksize = PAGE_SIZE; - sb->s_blocksize_bits = PAGE_SHIFT; - --- -2.43.0 - diff --git a/SPECS/kernel/0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c b/SPECS/kernel/0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c deleted file mode 100644 index 8e201c8b7..000000000 --- a/SPECS/kernel/0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c +++ /dev/null @@ -1,40 +0,0 @@ -From 1d4ae1aec57b03cebaa41feda361f1ed93357373 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:07 +0300 -Subject: [PATCH 08/11] i3c: mipi-i3c-hci: Uniform ring number printouts - -Use the same "Ring" prefix in all prints that print out the ring number. - -Signed-off-by: Jarkko Nikula -Reviewed-by: Frank Li -Link: https://lore.kernel.org/r/20250827103009.243771-4-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/dma.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 8bc9de189543..3fadacbda582 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -775,7 +775,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - u32 ring_status; - - dev_notice_ratelimited(&hci->master.dev, -- "ring %d: Transfer Aborted\n", i); -+ "Ring %d: Transfer Aborted\n", i); - mipi_i3c_hci_resume(hci); - ring_status = rh_reg_read(RING_STATUS); - if (!(ring_status & RING_STATUS_RUNNING) && -@@ -795,7 +795,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci) - } - if (status & INTR_IBI_RING_FULL) - dev_err_ratelimited(&hci->master.dev, -- "ring %d: IBI Ring Full Condition\n", i); -+ "Ring %d: IBI Ring Full Condition\n", i); - - handled = true; - } --- -2.43.0 - diff --git a/SPECS/kernel/0008-igc-Add-BTF-based-metadata-for-XDP.ethernet b/SPECS/kernel/0008-igc-Add-BTF-based-metadata-for-XDP.ethernet new file mode 100644 index 000000000..a583b6144 --- /dev/null +++ b/SPECS/kernel/0008-igc-Add-BTF-based-metadata-for-XDP.ethernet @@ -0,0 +1,235 @@ +From 9a080a8e0dd97fb5f78348640863abc59ba8c448 Mon Sep 17 00:00:00 2001 +From: Vedang Patel +Date: Mon, 14 Jun 2021 00:35:20 +0800 +Subject: [PATCH 08/14] igc: Add BTF based metadata for XDP + +This commit adds support for BTF based metadata for XDP. Currently, the +support has only been tested on receive side. Following is the struct +describing the metadata: + +struct xdp_md_desc { + u64 timestamp; +}; + +Note that only a single member is added to the struct. More members will +be added in the future. + +Signed-off-by: Vedang Patel +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc.h | 2 + + drivers/net/ethernet/intel/igc/igc_main.c | 12 +++ + drivers/net/ethernet/intel/igc/igc_xdp.c | 114 ++++++++++++++++++++++ + drivers/net/ethernet/intel/igc/igc_xdp.h | 11 +++ + 4 files changed, 139 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index b9f846389e75e..64d22481be91e 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -331,6 +331,8 @@ struct igc_adapter { + char fw_version[32]; + + struct bpf_prog *xdp_prog; ++ struct btf *btf; ++ u8 btf_enabled; + + bool pps_sys_wrap_on; + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 9b52d60ca315c..526b98b603c05 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -14,6 +14,7 @@ + #include + #include + ++#include + #include + + #include "igc.h" +@@ -6880,6 +6881,12 @@ static int igc_bpf(struct net_device *dev, struct netdev_bpf *bpf) + case XDP_SETUP_XSK_POOL: + return igc_xdp_setup_pool(adapter, bpf->xsk.pool, + bpf->xsk.queue_id); ++ case XDP_SETUP_MD_BTF: ++ return igc_xdp_set_btf_md(dev, bpf->btf_enable); ++ case XDP_QUERY_MD_BTF: ++ bpf->btf_id = igc_xdp_query_btf(dev, &bpf->btf_enable); ++ return 0; ++ + default: + return -EOPNOTSUPP; + } +@@ -7420,6 +7427,11 @@ static void igc_remove(struct pci_dev *pdev) + if (IS_ENABLED(CONFIG_IGC_LEDS) && adapter->leds_available) + igc_led_free(adapter); + ++ if (adapter->btf) { ++ adapter->btf_enabled = 0; ++ btf_unregister(adapter->btf); ++ } ++ + /* Release control of h/w to f/w. If f/w is AMT enabled, this + * would have already happened in close and is redundant. + */ +diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.c b/drivers/net/ethernet/intel/igc/igc_xdp.c +index 9eb47b4beb062..c23e0385fc74a 100644 +--- a/drivers/net/ethernet/intel/igc/igc_xdp.c ++++ b/drivers/net/ethernet/intel/igc/igc_xdp.c +@@ -3,10 +3,124 @@ + + #include + #include ++#include + + #include "igc.h" + #include "igc_xdp.h" + ++#define BTF_INFO_ENC(kind, kind_flag, vlen) \ ++ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) ++ ++#define BTF_TYPE_ENC(name, info, size_or_type) \ ++ (name), (info), (size_or_type) ++ ++#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \ ++ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits)) ++ ++#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \ ++ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \ ++ BTF_INT_ENC(encoding, bits_offset, bits) ++ ++#define BTF_STRUCT_ENC(name, nr_elems, sz) \ ++ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, nr_elems), sz) ++ ++#define BTF_MEMBER_ENC(name, type, bits_offset) \ ++ (name), (type), (bits_offset) ++ ++/* struct xdp_md_desc { ++ * u64 timestamp; ++ * }; ++ */ ++#define IGC_MD_NUM_MMBRS 1 ++static const char names_str[] = "\0xdp_md_desc\0timestamp\0"; ++ ++/* Must match struct xdp_md_desc */ ++static const u32 igc_md_raw_types[] = { ++ /* #define u64 */ ++ BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* type [1] */ ++ /* struct xdp_md_desc { */ ++ BTF_STRUCT_ENC(1, IGC_MD_NUM_MMBRS, 8), ++ BTF_MEMBER_ENC(13, 1, 0), /* u64 timestamp; */ ++ /* } */ ++}; ++ ++static int igc_xdp_register_btf(struct igc_adapter *priv) ++{ ++ unsigned int type_sec_sz, str_sec_sz; ++ char *types_sec, *str_sec; ++ struct btf_header *hdr; ++ unsigned int btf_size; ++ void *raw_btf = NULL; ++ int err = 0; ++ ++ type_sec_sz = sizeof(igc_md_raw_types); ++ str_sec_sz = sizeof(names_str); ++ ++ btf_size = sizeof(*hdr) + type_sec_sz + str_sec_sz; ++ raw_btf = kzalloc(btf_size, GFP_KERNEL); ++ if (!raw_btf) ++ return -ENOMEM; ++ ++ hdr = raw_btf; ++ hdr->magic = BTF_MAGIC; ++ hdr->version = BTF_VERSION; ++ hdr->hdr_len = sizeof(*hdr); ++ hdr->type_off = 0; ++ hdr->type_len = type_sec_sz; ++ hdr->str_off = type_sec_sz; ++ hdr->str_len = str_sec_sz; ++ ++ types_sec = raw_btf + sizeof(*hdr); ++ str_sec = types_sec + type_sec_sz; ++ memcpy(types_sec, igc_md_raw_types, type_sec_sz); ++ memcpy(str_sec, names_str, str_sec_sz); ++ ++ priv->btf = btf_register(priv->netdev->name, raw_btf, btf_size); ++ if (IS_ERR(priv->btf)) { ++ err = PTR_ERR(priv->btf); ++ priv->btf = NULL; ++ netdev_err(priv->netdev, "failed to register BTF MD, err (%d)\n", err); ++ } ++ ++ kfree(raw_btf); ++ return err; ++} ++ ++int igc_xdp_query_btf(struct net_device *dev, u8 *enabled) ++{ ++ struct igc_adapter *priv = netdev_priv(dev); ++ u32 md_btf_id = 0; ++ ++ if (!IS_ENABLED(CONFIG_BPF_SYSCALL)) ++ return md_btf_id; ++ ++ if (!priv->btf) ++ igc_xdp_register_btf(priv); ++ ++ *enabled = !!priv->btf_enabled; ++ md_btf_id = priv->btf ? btf_obj_id(priv->btf) : 0; ++ ++ return md_btf_id; ++} ++ ++int igc_xdp_set_btf_md(struct net_device *dev, u8 enable) ++{ ++ struct igc_adapter *priv = netdev_priv(dev); ++ int err = 0; ++ ++ if (enable && !priv->btf) { ++ igc_xdp_register_btf(priv); ++ if (!priv->btf) { ++ err = -EINVAL; ++ goto unlock; ++ } ++ } ++ ++ priv->btf_enabled = enable; ++unlock: ++ return err; ++} ++ + int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, + struct netlink_ext_ack *extack) + { +diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.h b/drivers/net/ethernet/intel/igc/igc_xdp.h +index a74e5487d1998..644dd8a49a3a6 100644 +--- a/drivers/net/ethernet/intel/igc/igc_xdp.h ++++ b/drivers/net/ethernet/intel/igc/igc_xdp.h +@@ -4,6 +4,12 @@ + #ifndef _IGC_XDP_H_ + #define _IGC_XDP_H_ + ++#include ++ ++struct igc_md_desc { ++ u64 timestamp; ++}; ++ + int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, + struct netlink_ext_ack *extack); + int igc_xdp_setup_pool(struct igc_adapter *adapter, struct xsk_buff_pool *pool, +@@ -14,4 +20,9 @@ static inline bool igc_xdp_is_enabled(struct igc_adapter *adapter) + return !!adapter->xdp_prog; + } + ++int igc_xdp_register_rxq_info(struct igc_ring *ring); ++void igc_xdp_unregister_rxq_info(struct igc_ring *ring); ++int igc_xdp_query_btf(struct net_device *dev, u8 *enabled); ++int igc_xdp_set_btf_md(struct net_device *dev, u8 enable); ++ + #endif /* _IGC_XDP_H_ */ +-- +2.43.0 + diff --git a/SPECS/kernel/0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu b/SPECS/kernel/0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu deleted file mode 100644 index 8e5ed354d..000000000 --- a/SPECS/kernel/0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu +++ /dev/null @@ -1,34 +0,0 @@ -From 643f25a3e375b8e907fc17630159b06ee9ad4fc9 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Thu, 4 Dec 2025 11:19:32 +0800 -Subject: [PATCH 08/21] media: ipu: invalidate MMU TLB in dma buffers creation - -This patch ensures that the MMU TLB is properly invalidated during -the creation and mapping of DMA buffers for IPU devices. Without -explicit invalidation, stale or incorrect entries in the TLB can cause -invalid memory access in hardware. - -Test Platform: -PTL CRB FAB B B0 silicon - -Signed-off-by: hepengpx ---- - drivers/staging/media/ipu7/ipu7-dma.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c -index a118b41b2f34..a0bdf6c37f96 100644 ---- a/drivers/staging/media/ipu7/ipu7-dma.c -+++ b/drivers/staging/media/ipu7/ipu7-dma.c -@@ -206,6 +206,8 @@ void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size, - } - } - -+ mmu->tlb_invalidate(mmu); -+ - info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL); - if (!info->vaddr) - goto out_unmap; --- -2.43.0 - diff --git a/SPECS/kernel/0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu b/SPECS/kernel/0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu deleted file mode 100644 index 884b2e6d5..000000000 --- a/SPECS/kernel/0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu +++ /dev/null @@ -1,35 +0,0 @@ -From c31079215991ef56964aa96acfd8516590c263da Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 6 Oct 2025 08:13:16 +0800 -Subject: [PATCH 08/11] media: pci: Enable IPU7 in Makefile & Kconfig - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/Kconfig | 1 + - drivers/media/pci/intel/Makefile | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index d9fcddce028b..948cda08fff5 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -2,6 +2,7 @@ - - source "drivers/media/pci/intel/ipu3/Kconfig" - source "drivers/media/pci/intel/ipu6/Kconfig" -+source "drivers/media/pci/intel/ipu7/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - - config IPU_BRIDGE -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index 3a2cc6567159..ff0fea13422d 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -6,3 +6,4 @@ obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ - obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ --- -2.43.0 - diff --git a/SPECS/kernel/0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet b/SPECS/kernel/0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet new file mode 100644 index 000000000..62f828446 --- /dev/null +++ b/SPECS/kernel/0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet @@ -0,0 +1,32 @@ +From 674b9153b1c57fa9deac9efb6b0debaf52b746a5 Mon Sep 17 00:00:00 2001 +From: Michael Sit Wei Hong +Date: Tue, 23 Aug 2022 12:05:21 +0800 +Subject: [PATCH 08/18] net: stmmac: add check for 2.5G mode to prevent MAC + capability update + +Checking the 2.5G capabilities to prevent other capabilities +from turning on, since we cannot switch from 2.5G to other +speeds dynamically. + +Signed-off-by: Michael Sit Wei Hong +--- + drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +index d85bc0bb5c3c0..07357df01a1d2 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +@@ -69,7 +69,8 @@ static void dwmac4_core_init(struct mac_device_info *hw, + + static void dwmac4_update_caps(struct stmmac_priv *priv) + { +- if (priv->plat->tx_queues_to_use > 1) ++ if (priv->plat->tx_queues_to_use > 1 && ++ !priv->plat->fixed_2G5_clock_rate) + priv->hw->link.caps &= ~(MAC_10HD | MAC_100HD | MAC_1000HD); + else + priv->hw->link.caps |= (MAC_10HD | MAC_100HD | MAC_1000HD); +-- +2.43.0 + diff --git a/SPECS/kernel/0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf b/SPECS/kernel/0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf new file mode 100644 index 000000000..45a58242c --- /dev/null +++ b/SPECS/kernel/0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf @@ -0,0 +1,156 @@ +From 8dfdb82882eb7e7783c54be3085ceb1b810f0f45 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Thu, 11 Dec 2025 10:04:29 -0800 +Subject: [PATCH 08/13] perf/x86/intel/uncore: Add freerunning event descriptor + helper macro + +Freerunning counter events are repetitive: the event code is fixed to +0xff, the unit is always "MiB", and the scale is identical across all +counters on a given PMON unit. + +Introduce a new helper macro, INTEL_UNCORE_FR_EVENT_DESC(), to populate +the event, scale, and unit descriptor triplet. This reduces duplicated +lines and improves readability. + +No functional change intended. + +Reviewed-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore_snbep.c | 95 ++++++++-------------------- + 1 file changed, 28 insertions(+), 67 deletions(-) + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index df173534637a6..09a3bdbd188aa 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -4068,34 +4068,24 @@ static struct freerunning_counters skx_iio_freerunning[] = { + [SKX_IIO_MSR_UTIL] = { 0xb08, 0x1, 0x10, 8, 36 }, + }; + ++#define INTEL_UNCORE_FR_EVENT_DESC(name, umask, scl) \ ++ INTEL_UNCORE_EVENT_DESC(name, \ ++ "event=0xff,umask=" __stringify(umask)), \ ++ INTEL_UNCORE_EVENT_DESC(name.scale, __stringify(scl)), \ ++ INTEL_UNCORE_EVENT_DESC(name.unit, "MiB") ++ + static struct uncore_event_desc skx_uncore_iio_freerunning_events[] = { + /* Free-Running IO CLOCKS Counter */ + INTEL_UNCORE_EVENT_DESC(ioclk, "event=0xff,umask=0x10"), + /* Free-Running IIO BANDWIDTH Counters */ +- INTEL_UNCORE_EVENT_DESC(bw_in_port0, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2, "event=0xff,umask=0x22"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3, "event=0xff,umask=0x23"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port0, "event=0xff,umask=0x24"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port0.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port0.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port1, "event=0xff,umask=0x25"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port1.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port1.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port2, "event=0xff,umask=0x26"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port2.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port2.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port3, "event=0xff,umask=0x27"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port3.scale, "3.814697266e-6"), +- INTEL_UNCORE_EVENT_DESC(bw_out_port3.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port0, 0x24, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port1, 0x25, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port2, 0x26, 3.814697266e-6), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port3, 0x27, 3.814697266e-6), + /* Free-running IIO UTILIZATION Counters */ + INTEL_UNCORE_EVENT_DESC(util_in_port0, "event=0xff,umask=0x30"), + INTEL_UNCORE_EVENT_DESC(util_out_port0, "event=0xff,umask=0x31"), +@@ -4910,30 +4900,14 @@ static struct uncore_event_desc snr_uncore_iio_freerunning_events[] = { + /* Free-Running IIO CLOCKS Counter */ + INTEL_UNCORE_EVENT_DESC(ioclk, "event=0xff,umask=0x10"), + /* Free-Running IIO BANDWIDTH IN Counters */ +- INTEL_UNCORE_EVENT_DESC(bw_in_port0, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port0.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port1.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2, "event=0xff,umask=0x22"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port2.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3, "event=0xff,umask=0x23"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port3.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port4, "event=0xff,umask=0x24"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port4.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port4.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port5, "event=0xff,umask=0x25"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port5.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port5.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port6, "event=0xff,umask=0x26"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port6.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port6.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port7, "event=0xff,umask=0x27"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port7.scale, "3.0517578125e-5"), +- INTEL_UNCORE_EVENT_DESC(bw_in_port7.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port4, 0x24, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port5, 0x25, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port6, 0x26, 3.0517578125e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port7, 0x27, 3.0517578125e-5), + { /* end: all zeroes */ }, + }; + +@@ -5266,12 +5240,8 @@ static struct freerunning_counters snr_imc_freerunning[] = { + static struct uncore_event_desc snr_uncore_imc_freerunning_events[] = { + INTEL_UNCORE_EVENT_DESC(dclk, "event=0xff,umask=0x10"), + +- INTEL_UNCORE_EVENT_DESC(read, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(read.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(read.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(write, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(write.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(write.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(read, 0x20, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(write, 0x21, 6.103515625e-5), + { /* end: all zeroes */ }, + }; + +@@ -5836,19 +5806,10 @@ static struct freerunning_counters icx_imc_freerunning[] = { + static struct uncore_event_desc icx_uncore_imc_freerunning_events[] = { + INTEL_UNCORE_EVENT_DESC(dclk, "event=0xff,umask=0x10"), + +- INTEL_UNCORE_EVENT_DESC(read, "event=0xff,umask=0x20"), +- INTEL_UNCORE_EVENT_DESC(read.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(read.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(write, "event=0xff,umask=0x21"), +- INTEL_UNCORE_EVENT_DESC(write.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(write.unit, "MiB"), +- +- INTEL_UNCORE_EVENT_DESC(ddrt_read, "event=0xff,umask=0x30"), +- INTEL_UNCORE_EVENT_DESC(ddrt_read.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(ddrt_read.unit, "MiB"), +- INTEL_UNCORE_EVENT_DESC(ddrt_write, "event=0xff,umask=0x31"), +- INTEL_UNCORE_EVENT_DESC(ddrt_write.scale, "6.103515625e-5"), +- INTEL_UNCORE_EVENT_DESC(ddrt_write.unit, "MiB"), ++ INTEL_UNCORE_FR_EVENT_DESC(read, 0x20, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(write, 0x21, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(ddrt_read, 0x30, 6.103515625e-5), ++ INTEL_UNCORE_FR_EVENT_DESC(ddrt_write, 0x31, 6.103515625e-5), + { /* end: all zeroes */ }, + }; + +-- +2.43.0 + diff --git a/SPECS/kernel/0008-spi-intel-Add-support-for-128M-component-density.lpss b/SPECS/kernel/0008-spi-intel-Add-support-for-128M-component-density.lpss deleted file mode 100644 index a79bb01d4..000000000 --- a/SPECS/kernel/0008-spi-intel-Add-support-for-128M-component-density.lpss +++ /dev/null @@ -1,42 +0,0 @@ -From d68c39a40f5b4504b044183ff28a8bad5912c655 Mon Sep 17 00:00:00 2001 -From: Mika Westerberg -Date: Wed, 28 May 2025 11:07:28 +0300 -Subject: [PATCH 08/16] spi: intel: Add support for 128M component density - -With the recent hardware the flash component density can be increased to -128M. Update the driver to support this. While there log a warning if we -encounter a unsupported value in this field. - -Signed-off-by: Mika Westerberg ---- - drivers/spi/spi-intel.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/spi/spi-intel.c b/drivers/spi/spi-intel.c -index 13bbb2133507..1775ad39e633 100644 ---- a/drivers/spi/spi-intel.c -+++ b/drivers/spi/spi-intel.c -@@ -132,6 +132,7 @@ - #define FLCOMP_C0DEN_16M 0x05 - #define FLCOMP_C0DEN_32M 0x06 - #define FLCOMP_C0DEN_64M 0x07 -+#define FLCOMP_C0DEN_128M 0x08 - - #define INTEL_SPI_TIMEOUT 5000 /* ms */ - #define INTEL_SPI_FIFO_SZ 64 -@@ -1347,7 +1348,12 @@ static int intel_spi_read_desc(struct intel_spi *ispi) - case FLCOMP_C0DEN_64M: - ispi->chip0_size = SZ_64M; - break; -+ case FLCOMP_C0DEN_128M: -+ ispi->chip0_size = SZ_128M; -+ break; - default: -+ dev_warn(ispi->dev, "unsupported C0DEN: %#lx\n", -+ flcomp & FLCOMP_C0DEN_MASK); - return -EINVAL; - } - --- -2.43.0 - diff --git a/SPECS/kernel/0008-thermal-testing-Rearrange-variable-declarations-in.thermal b/SPECS/kernel/0008-thermal-testing-Rearrange-variable-declarations-in.thermal deleted file mode 100644 index 53d16bbbf..000000000 --- a/SPECS/kernel/0008-thermal-testing-Rearrange-variable-declarations-in.thermal +++ /dev/null @@ -1,125 +0,0 @@ -From f355872ec97e33a9d2ac43196c8c24304e5f5220 Mon Sep 17 00:00:00 2001 -From: "Rafael J. Wysocki" -Date: Wed, 3 Sep 2025 16:52:45 +0200 -Subject: [PATCH 08/11] thermal: testing: Rearrange variable declarations - involving __free() - -Follow cleanup.h recommendations and always define and assign variables -in one statement when __free() is used. - -No intentional functional impact. - -Signed-off-by: Rafael J. Wysocki -Reviewed-by: Krzysztof Kozlowski -Link: https://patch.msgid.link/5934556.DvuYhMxLoT@rafael.j.wysocki ---- - drivers/thermal/testing/zone.c | 31 +++++++++++-------------------- - 1 file changed, 11 insertions(+), 20 deletions(-) - -diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c -index 4257d813d572..c12c405225bb 100644 ---- a/drivers/thermal/testing/zone.c -+++ b/drivers/thermal/testing/zone.c -@@ -184,15 +184,14 @@ static void tt_add_tz_work_fn(struct work_struct *work) - - int tt_add_tz(void) - { -- struct tt_thermal_zone *tt_zone __free(kfree); -- struct tt_work *tt_work __free(kfree) = NULL; - int ret; - -- tt_zone = kzalloc(sizeof(*tt_zone), GFP_KERNEL); -+ struct tt_thermal_zone *tt_zone __free(kfree) = kzalloc(sizeof(*tt_zone), -+ GFP_KERNEL); - if (!tt_zone) - return -ENOMEM; - -- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); -+ struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL); - if (!tt_work) - return -ENOMEM; - -@@ -237,7 +236,6 @@ static void tt_zone_unregister_tz(struct tt_thermal_zone *tt_zone) - - int tt_del_tz(const char *arg) - { -- struct tt_work *tt_work __free(kfree) = NULL; - struct tt_thermal_zone *tt_zone, *aux; - int ret; - int id; -@@ -246,7 +244,7 @@ int tt_del_tz(const char *arg) - if (ret != 1) - return -EINVAL; - -- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); -+ struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL); - if (!tt_work) - return -ENOMEM; - -@@ -330,20 +328,17 @@ static void tt_zone_add_trip_work_fn(struct work_struct *work) - - int tt_zone_add_trip(const char *arg) - { -- struct tt_thermal_zone *tt_zone __free(put_tt_zone) = NULL; -- struct tt_trip *tt_trip __free(kfree) = NULL; -- struct tt_work *tt_work __free(kfree); - int id; - -- tt_work = kzalloc(sizeof(*tt_work), GFP_KERNEL); -+ struct tt_work *tt_work __free(kfree) = kzalloc(sizeof(*tt_work), GFP_KERNEL); - if (!tt_work) - return -ENOMEM; - -- tt_trip = kzalloc(sizeof(*tt_trip), GFP_KERNEL); -+ struct tt_trip *tt_trip __free(kfree) = kzalloc(sizeof(*tt_trip), GFP_KERNEL); - if (!tt_trip) - return -ENOMEM; - -- tt_zone = tt_get_tt_zone(arg); -+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg); - if (IS_ERR(tt_zone)) - return PTR_ERR(tt_zone); - -@@ -387,7 +382,6 @@ static const struct thermal_zone_device_ops tt_zone_ops = { - - static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) - { -- struct thermal_trip *trips __free(kfree) = NULL; - struct thermal_zone_device *tz; - struct tt_trip *tt_trip; - int i; -@@ -397,7 +391,8 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) - if (tt_zone->tz) - return -EINVAL; - -- trips = kcalloc(tt_zone->num_trips, sizeof(*trips), GFP_KERNEL); -+ struct thermal_trip *trips __free(kfree) = kcalloc(tt_zone->num_trips, -+ sizeof(*trips), GFP_KERNEL); - if (!trips) - return -ENOMEM; - -@@ -421,9 +416,7 @@ static int tt_zone_register_tz(struct tt_thermal_zone *tt_zone) - - int tt_zone_reg(const char *arg) - { -- struct tt_thermal_zone *tt_zone __free(put_tt_zone); -- -- tt_zone = tt_get_tt_zone(arg); -+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg); - if (IS_ERR(tt_zone)) - return PTR_ERR(tt_zone); - -@@ -432,9 +425,7 @@ int tt_zone_reg(const char *arg) - - int tt_zone_unreg(const char *arg) - { -- struct tt_thermal_zone *tt_zone __free(put_tt_zone); -- -- tt_zone = tt_get_tt_zone(arg); -+ struct tt_thermal_zone *tt_zone __free(put_tt_zone) = tt_get_tt_zone(arg); - if (IS_ERR(tt_zone)) - return PTR_ERR(tt_zone); - --- -2.43.0 - diff --git a/SPECS/kernel/0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet b/SPECS/kernel/0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet deleted file mode 100644 index 44974924d..000000000 --- a/SPECS/kernel/0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet +++ /dev/null @@ -1,491 +0,0 @@ -From 4138882641dc56cd30b13d410a01f9463a7bb2ff Mon Sep 17 00:00:00 2001 -From: Saeed Mahameed -Date: Tue, 9 Apr 2019 22:01:47 -0700 -Subject: [PATCH 08/19] tools/bpf: Add xdp set command for md btf - -Add new bpftool net subcommand and use it to report and set XDP attrs: - -$ /usr/local/sbin/bpftool net xdp help -Usage: /usr/local/sbin/bpftool net xdp - { show | list | set | md_btf} [dev ] - /usr/local/sbin/bpftool xdp help - -$ /usr/local/sbin/bpftool net xdp set dev mlx0 md_btf on - -$ /usr/local/sbin/bpftool net xdp show -xdp: -mlx0(3) md_btf_id(1) md_btf_enabled(1) - -Signed-off-by: Saeed Mahameed -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - tools/bpf/bpftool/main.h | 2 + - tools/bpf/bpftool/net.c | 7 +- - tools/bpf/bpftool/xdp.c | 308 +++++++++++++++++++++++++++++++++++++++ - tools/lib/bpf/libbpf.h | 1 + - tools/lib/bpf/libbpf.map | 1 + - tools/lib/bpf/netlink.c | 49 +++++++ - 6 files changed, 365 insertions(+), 3 deletions(-) - create mode 100644 tools/bpf/bpftool/xdp.c - -diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h -index 6db704fda5c0..56afb1e81b42 100644 ---- a/tools/bpf/bpftool/main.h -+++ b/tools/bpf/bpftool/main.h -@@ -156,6 +156,7 @@ int do_btf(int argc, char **argv); - - /* non-bootstrap only commands */ - int do_prog(int argc, char **arg) __weak; -+int do_xdp(int argc, char **argv); - int do_map(int argc, char **arg) __weak; - int do_link(int argc, char **arg) __weak; - int do_event_pipe(int argc, char **argv) __weak; -@@ -242,6 +243,7 @@ struct tcmsg; - int do_xdp_dump(struct ifinfomsg *ifinfo, struct nlattr **tb); - int do_filter_dump(struct tcmsg *ifinfo, struct nlattr **tb, const char *kind, - const char *devname, int ifindex); -+int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb); - - int print_all_levels(__maybe_unused enum libbpf_print_level level, - const char *format, va_list args); -diff --git a/tools/bpf/bpftool/net.c b/tools/bpf/bpftool/net.c -index cfc6f944f7c3..f313f5be0960 100644 ---- a/tools/bpf/bpftool/net.c -+++ b/tools/bpf/bpftool/net.c -@@ -362,7 +362,7 @@ static int netlink_get_link(int sock, unsigned int nl_pid, - dump_link_nlmsg, cookie); - } - --static int dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) -+int xdp_dump_link_nlmsg(void *cookie, void *msg, struct nlattr **tb) - { - struct bpf_netdev_t *netinfo = cookie; - struct ifinfomsg *ifinfo = msg; -@@ -937,7 +937,7 @@ static int do_show(int argc, char **argv) - jsonw_start_array(json_wtr); - NET_START_OBJECT; - NET_START_ARRAY("xdp", "%s:\n"); -- ret = netlink_get_link(sock, nl_pid, dump_link_nlmsg, &dev_array); -+ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); - NET_END_ARRAY("\n"); - - if (!ret) { -@@ -984,7 +984,7 @@ static int do_help(int argc, char **argv) - } - - fprintf(stderr, -- "Usage: %1$s %2$s { show | list } [dev ]\n" -+ "Usage: %1$s %2$s { show | list | xdp } [dev ]\n" - " %1$s %2$s attach ATTACH_TYPE PROG dev [ overwrite ]\n" - " %1$s %2$s detach ATTACH_TYPE dev \n" - " %1$s %2$s help\n" -@@ -1011,6 +1011,7 @@ static const struct cmd cmds[] = { - { "list", do_show }, - { "attach", do_attach }, - { "detach", do_detach }, -+ { "xdp", do_xdp }, - { "help", do_help }, - { 0 } - }; -diff --git a/tools/bpf/bpftool/xdp.c b/tools/bpf/bpftool/xdp.c -new file mode 100644 -index 000000000000..d33671de877b ---- /dev/null -+++ b/tools/bpf/bpftool/xdp.c -@@ -0,0 +1,308 @@ -+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) -+// Copyright (C) 2019 Mellanox. -+ -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "bpf/nlattr.h" -+#include "main.h" -+#include "netlink_dumper.h" -+ -+/* TODO: reuse form net.c */ -+#ifndef SOL_NETLINK -+#define SOL_NETLINK 270 -+#endif -+ -+static int netlink_open(__u32 *nl_pid) -+{ -+ struct sockaddr_nl sa; -+ socklen_t addrlen; -+ int one = 1, ret; -+ int sock; -+ -+ memset(&sa, 0, sizeof(sa)); -+ sa.nl_family = AF_NETLINK; -+ -+ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); -+ if (sock < 0) -+ return -errno; -+ -+ if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK, -+ &one, sizeof(one)) < 0) { -+ p_err("Netlink error reporting not supported"); -+ } -+ -+ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { -+ ret = -errno; -+ goto cleanup; -+ } -+ -+ addrlen = sizeof(sa); -+ if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) { -+ ret = -errno; -+ goto cleanup; -+ } -+ -+ if (addrlen != sizeof(sa)) { -+ ret = -LIBBPF_ERRNO__INTERNAL; -+ goto cleanup; -+ } -+ -+ *nl_pid = sa.nl_pid; -+ return sock; -+ -+cleanup: -+ close(sock); -+ return ret; -+} -+ -+typedef int (*dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb); -+ -+typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, dump_nlmsg_t, void *cookie); -+ -+static int netlink_recv(int sock, __u32 nl_pid, __u32 seq, -+ __dump_nlmsg_t _fn, dump_nlmsg_t fn, -+ void *cookie) -+{ -+ bool multipart = true; -+ struct nlmsgerr *err; -+ struct nlmsghdr *nh; -+ char buf[4096]; -+ int len, ret; -+ -+ while (multipart) { -+ multipart = false; -+ len = recv(sock, buf, sizeof(buf), 0); -+ if (len < 0) { -+ ret = -errno; -+ goto done; -+ } -+ -+ if (len == 0) -+ break; -+ -+ for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); -+ nh = NLMSG_NEXT(nh, len)) { -+ if (nh->nlmsg_pid != nl_pid) { -+ ret = -LIBBPF_ERRNO__WRNGPID; -+ goto done; -+ } -+ if (nh->nlmsg_seq != seq) { -+ ret = -LIBBPF_ERRNO__INVSEQ; -+ goto done; -+ } -+ if (nh->nlmsg_flags & NLM_F_MULTI) -+ multipart = true; -+ switch (nh->nlmsg_type) { -+ case NLMSG_ERROR: -+ err = (struct nlmsgerr *)NLMSG_DATA(nh); -+ if (!err->error) -+ continue; -+ ret = err->error; -+ libbpf_nla_dump_errormsg(nh); -+ goto done; -+ case NLMSG_DONE: -+ return 0; -+ default: -+ break; -+ } -+ if (_fn) { -+ ret = _fn(nh, fn, cookie); -+ if (ret) -+ return ret; -+ } -+ } -+ } -+ ret = 0; -+done: -+ return ret; -+} -+ -+static int __dump_link_nlmsg(struct nlmsghdr *nlh, -+ dump_nlmsg_t dump_link_nlmsg, void *cookie) -+{ -+ struct nlattr *tb[IFLA_MAX + 1], *attr; -+ struct ifinfomsg *ifi = NLMSG_DATA(nlh); -+ int len; -+ -+ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); -+ attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi))); -+ if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0) -+ return -LIBBPF_ERRNO__NLPARSE; -+ -+ return dump_link_nlmsg(cookie, ifi, tb); -+} -+ -+static int netlink_get_link(int sock, unsigned int nl_pid, -+ dump_nlmsg_t dump_link_nlmsg, void *cookie) -+{ -+ struct { -+ struct nlmsghdr nlh; -+ struct ifinfomsg ifm; -+ } req = { -+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), -+ .nlh.nlmsg_type = RTM_GETLINK, -+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, -+ .ifm.ifi_family = AF_PACKET, -+ }; -+ int seq = time(NULL); -+ -+ req.nlh.nlmsg_seq = seq; -+ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0) -+ return -errno; -+ -+ return netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg, -+ dump_link_nlmsg, cookie); -+} -+ -+struct ip_devname_ifindex { -+ char devname[64]; -+ int ifindex; -+}; -+ -+struct bpf_netdev_t { -+ struct ip_devname_ifindex *devices; -+ int used_len; -+ int array_len; -+ int filter_idx; -+}; -+ -+static int do_show(int argc, char **argv) -+{ -+ int sock, ret, filter_idx = -1; -+ struct bpf_netdev_t dev_array; -+ unsigned int nl_pid = 0; -+ char err_buf[256]; -+ -+ if (argc == 2) { -+ if (strcmp(argv[0], "dev") != 0) -+ usage(); -+ filter_idx = if_nametoindex(argv[1]); -+ if (filter_idx == 0) { -+ fprintf(stderr, "invalid dev name %s\n", argv[1]); -+ return -1; -+ } -+ } else if (argc != 0) { -+ usage(); -+ } -+ -+ sock = netlink_open(&nl_pid); -+ if (sock < 0) { -+ fprintf(stderr, "failed to open netlink sock\n"); -+ return -1; -+ } -+ -+ dev_array.devices = NULL; -+ dev_array.used_len = 0; -+ dev_array.array_len = 0; -+ dev_array.filter_idx = filter_idx; -+ -+ if (json_output) -+ jsonw_start_array(json_wtr); -+ NET_START_OBJECT; -+ NET_START_ARRAY("xdp", "%s:\n"); -+ ret = netlink_get_link(sock, nl_pid, xdp_dump_link_nlmsg, &dev_array); -+ NET_END_ARRAY("\n"); -+ -+ NET_END_OBJECT; -+ if (json_output) -+ jsonw_end_array(json_wtr); -+ -+ if (ret) { -+ if (json_output) -+ jsonw_null(json_wtr); -+ libbpf_strerror(ret, err_buf, sizeof(err_buf)); -+ fprintf(stderr, "Error: %s\n", err_buf); -+ } -+ free(dev_array.devices); -+ close(sock); -+ return ret; -+} -+ -+static int set_usage(void) -+{ -+ fprintf(stderr, -+ "Usage: %s net xdp set dev {md_btf {on|off}}\n" -+ " %s net xdp set help\n" -+ " md_btf {on|off}: enable/disable meta data btf\n", -+ bin_name, bin_name); -+ -+ return -1; -+} -+ -+static int xdp_set_md_btf(int ifindex, char *arg) -+{ -+ __u8 enable = (strcmp(arg, "on") == 0) ? 1 : 0; -+ int ret; -+ -+ ret = bpf_set_link_xdp_md_btf(ifindex, enable); -+ if (ret) -+ fprintf(stderr, "Failed to setup xdp md, err=%d\n", ret); -+ -+ return -ret; -+} -+ -+static int do_set(int argc, char **argv) -+{ -+ char *set_cmd, *set_arg; -+ int dev_idx = -1; -+ -+ if (argc < 4) -+ return set_usage(); -+ -+ if (strcmp(argv[0], "dev") != 0) -+ return set_usage(); -+ -+ dev_idx = if_nametoindex(argv[1]); -+ if (dev_idx == 0) { -+ fprintf(stderr, "invalid dev name %s\n", argv[1]); -+ return -1; -+ } -+ -+ set_cmd = argv[2]; -+ set_arg = argv[3]; -+ -+ if (strcmp(set_cmd, "md_btf") != 0) -+ return set_usage(); -+ -+ if (strcmp(set_arg, "on") != 0 && strcmp(set_arg, "off") != 0) -+ return set_usage(); -+ -+ return xdp_set_md_btf(dev_idx, set_arg); -+} -+ -+static int do_help(int argc, char **argv) -+{ -+ if (json_output) { -+ jsonw_null(json_wtr); -+ return 0; -+ } -+ -+ fprintf(stderr, -+ "Usage: %s %s xdp { show | list | set } [dev ]\n" -+ " %s %s help\n", -+ bin_name, argv[-2], bin_name, argv[-2]); -+ -+ return 0; -+} -+ -+static const struct cmd cmds[] = { -+ { "show", do_show }, -+ { "list", do_show }, -+ { "set", do_set }, -+ { "help", do_help }, -+ { 0 } -+}; -+ -+int do_xdp(int argc, char **argv) -+{ -+ return cmd_select(cmds, argc, argv, do_help); -+} -diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h -index 455a957cb702..987e3e0ab681 100644 ---- a/tools/lib/bpf/libbpf.h -+++ b/tools/lib/bpf/libbpf.h -@@ -1597,6 +1597,7 @@ LIBBPF_API struct perf_buffer * - perf_buffer__new(int map_fd, size_t page_cnt, - perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx, - const struct perf_buffer_opts *opts); -+LIBBPF_API int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable); - - enum bpf_perf_event_ret { - LIBBPF_PERF_EVENT_DONE = 0, -diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map -index d7bd463e7017..ee2851c7fb3d 100644 ---- a/tools/lib/bpf/libbpf.map -+++ b/tools/lib/bpf/libbpf.map -@@ -73,6 +73,7 @@ LIBBPF_0.0.2 { - bpf_map_lookup_elem_flags; - bpf_object__btf; - bpf_object__find_map_fd_by_name; -+ bpf_set_link_xdp_md_btf; - btf__get_raw_data; - btf_ext__free; - btf_ext__get_raw_data; -diff --git a/tools/lib/bpf/netlink.c b/tools/lib/bpf/netlink.c -index c997e69d507f..204827a4380b 100644 ---- a/tools/lib/bpf/netlink.c -+++ b/tools/lib/bpf/netlink.c -@@ -341,6 +341,55 @@ int bpf_xdp_detach(int ifindex, __u32 flags, const struct bpf_xdp_attach_opts *o - return bpf_xdp_attach(ifindex, -1, flags, opts); - } - -+int bpf_set_link_xdp_md_btf(int ifindex, __u8 enable) -+{ -+ struct nlattr *nla, *nla_xdp; -+ int sock, seq = 0, ret; -+ __u32 nl_pid; -+ struct { -+ struct nlmsghdr nh; -+ struct ifinfomsg ifinfo; -+ char attrbuf[64]; -+ } req; -+ -+ sock = libbpf_netlink_open(&nl_pid, NETLINK_ROUTE); -+ if (sock < 0) -+ return sock; -+ -+ memset(&req, 0, sizeof(req)); -+ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); -+ req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; -+ req.nh.nlmsg_type = RTM_SETLINK; -+ req.nh.nlmsg_pid = 0; -+ req.nh.nlmsg_seq = ++seq; -+ req.ifinfo.ifi_family = AF_UNSPEC; -+ req.ifinfo.ifi_index = ifindex; -+ -+ /* started nested attribute for XDP */ -+ nla = (struct nlattr *)(((char *)&req) -+ + NLMSG_ALIGN(req.nh.nlmsg_len)); -+ nla->nla_type = NLA_F_NESTED | IFLA_XDP; -+ nla->nla_len = NLA_HDRLEN; -+ /* add XDP MD setup */ -+ nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len); -+ nla_xdp->nla_type = IFLA_XDP_MD_BTF_STATE; -+ nla_xdp->nla_len = NLA_HDRLEN + sizeof(__u8); -+ memcpy((char *)nla_xdp + NLA_HDRLEN, &enable, sizeof(__u8)); -+ nla->nla_len += nla_xdp->nla_len; -+ -+ req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); -+ -+ if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { -+ ret = -errno; -+ goto cleanup; -+ } -+ ret = libbpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL); -+ -+cleanup: -+ close(sock); -+ return ret; -+} -+ - static int __dump_link_nlmsg(struct nlmsghdr *nlh, - libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie) - { --- -2.43.0 - diff --git a/SPECS/kernel/0008-tools-power-turbostat-Add-LLC-stats.turbo b/SPECS/kernel/0008-tools-power-turbostat-Add-LLC-stats.turbo new file mode 100644 index 000000000..b7159f16a --- /dev/null +++ b/SPECS/kernel/0008-tools-power-turbostat-Add-LLC-stats.turbo @@ -0,0 +1,551 @@ +From 190569df9b9508257304a7ce06fce2238b88c82a Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 21 Oct 2025 20:23:49 -0300 +Subject: [PATCH 08/21] tools/power turbostat: Add LLC stats + +LLCkRPS = Last Level Cache Thousands of References Per Second +LLC%hit = Last Level Cache Hit % + +These columns are enabled by-default. +They can be controlled with the --show/--hide options +by individual column names above, +or together using the "llc" or "cache" groups. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.8 | 6 +- + tools/power/x86/turbostat/turbostat.c | 186 ++++++++++++++++++++++---- + 2 files changed, 164 insertions(+), 28 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 +index ad3fc201552f7..1551fcdbfd8a0 100644 +--- a/tools/power/x86/turbostat/turbostat.8 ++++ b/tools/power/x86/turbostat/turbostat.8 +@@ -101,7 +101,7 @@ The column name "all" can be used to enable all disabled-by-default built-in cou + .PP + \fB--show column\fP show only the specified built-in columns. May be invoked multiple times, or with a comma-separated list of column names. + .PP +-\fB--show CATEGORY --hide CATEGORY\fP Show and hide also accept a single CATEGORY of columns: "all", "topology", "idle", "frequency", "power", "cpuidle", "hwidle", "swidle", "other". "idle" (enabled by default), includes "hwidle" and "pct_idle". "cpuidle" (default disabled) includes cpuidle software invocation counters. "swidle" includes "cpuidle" plus "pct_idle". "hwidle" includes only hardware based idle residency counters. Older versions of turbostat used the term "sysfs" for what is now "swidle". ++\fB--show CATEGORY --hide CATEGORY\fP Show and hide also accept a comma-separated-list of CATEGORIES of columns: "all", "topology", "idle", "frequency", "power", "cpuidle", "hwidle", "swidle", "cache", "llc", "other". "idle" (enabled by default), includes "hwidle" and "pct_idle". "cpuidle" (default disabled) includes cpuidle software invocation counters. "swidle" includes "cpuidle" plus "pct_idle". "hwidle" includes only hardware based idle residency counters. Older versions of turbostat used the term "sysfs" for what is now "swidle". + .PP + \fB--Dump\fP displays the raw counter values. + .PP +@@ -159,6 +159,10 @@ The system configuration dump (if --quiet is not used) is followed by statistics + .PP + \fBSMI\fP The number of System Management Interrupts serviced CPU during the measurement interval. While this counter is actually per-CPU, SMI are triggered on all processors, so the number should be the same for all CPUs. + .PP ++\fBLLCkRPS\fP Last Level Cache Thousands of References Per Second. For CPUs with an L3 LLC, this is the number of references that CPU made to the L3 (and the number of misses that CPU made to it's L2). For CPUs with an L2 LLC, this is the number of references to the L2 (and the number of misses to the CPU's L1). The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1000 in the interest of usually fitting into 8 columns. ++.PP ++\fBLLC%hit\fP Last Level Cache Hit Rate %. Hit Rate Percent = 100.0 * (References - Misses)/References. The system summary row shows the weighted average for all CPUs (100.0 * (Sum_References - Sum_Misses)/Sum_References). ++.PP + \fBC1, C2, C3...\fP The number times Linux requested the C1, C2, C3 idle state during the measurement interval. The system summary line shows the sum for all CPUs. These are C-state names as exported in /sys/devices/system/cpu/cpu*/cpuidle/state*/name. While their names are generic, their attributes are processor specific. They the system description section of output shows what MWAIT sub-states they are mapped to on each system. These counters are in the "cpuidle" group, which is disabled, by default. + .PP + \fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Inidcates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default. +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index f63525a1877c7..2854b66eb7480 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -209,6 +209,8 @@ struct msr_counter bic[] = { + { 0x0, "NMI", NULL, 0, 0, 0, NULL, 0 }, + { 0x0, "CPU%c1e", NULL, 0, 0, 0, NULL, 0 }, + { 0x0, "pct_idle", NULL, 0, 0, 0, NULL, 0 }, ++ { 0x0, "LLCkRPS", NULL, 0, 0, 0, NULL, 0 }, ++ { 0x0, "LLC%hit", NULL, 0, 0, 0, NULL, 0 }, + }; + + /* n.b. bic_names must match the order in bic[], above */ +@@ -278,6 +280,8 @@ enum bic_names { + BIC_NMI, + BIC_CPU_c1e, + BIC_pct_idle, ++ BIC_LLC_RPS, ++ BIC_LLC_HIT, + MAX_BIC + }; + +@@ -305,6 +309,7 @@ static cpu_set_t bic_group_frequency; + static cpu_set_t bic_group_hw_idle; + static cpu_set_t bic_group_sw_idle; + static cpu_set_t bic_group_idle; ++static cpu_set_t bic_group_cache; + static cpu_set_t bic_group_other; + static cpu_set_t bic_group_disabled_by_default; + static cpu_set_t bic_enabled; +@@ -413,9 +418,14 @@ static void bic_groups_init(void) + SET_BIC(BIC_pct_idle, &bic_group_sw_idle); + + BIC_INIT(&bic_group_idle); ++ + CPU_OR(&bic_group_idle, &bic_group_idle, &bic_group_hw_idle); + SET_BIC(BIC_pct_idle, &bic_group_idle); + ++ BIC_INIT(&bic_group_cache); ++ SET_BIC(BIC_LLC_RPS, &bic_group_cache); ++ SET_BIC(BIC_LLC_HIT, &bic_group_cache); ++ + BIC_INIT(&bic_group_other); + SET_BIC(BIC_IRQ, &bic_group_other); + SET_BIC(BIC_NMI, &bic_group_other); +@@ -470,6 +480,7 @@ char *proc_stat = "/proc/stat"; + FILE *outf; + int *fd_percpu; + int *fd_instr_count_percpu; ++int *fd_llc_percpu; + struct timeval interval_tv = { 5, 0 }; + struct timespec interval_ts = { 5, 0 }; + +@@ -1992,6 +2003,10 @@ void pmt_counter_resize(struct pmt_counter *pcounter, unsigned int new_size) + pmt_counter_resize_(pcounter, new_size); + } + ++struct llc_stats { ++ unsigned long long references; ++ unsigned long long misses; ++}; + struct thread_data { + struct timeval tv_begin; + struct timeval tv_end; +@@ -2004,6 +2019,7 @@ struct thread_data { + unsigned long long irq_count; + unsigned long long nmi_count; + unsigned int smi_count; ++ struct llc_stats llc; + unsigned int cpu_id; + unsigned int apic_id; + unsigned int x2apic_id; +@@ -2363,23 +2379,19 @@ int for_all_cpus(int (func) (struct thread_data *, struct core_data *, struct pk + return retval; + } + +-int is_cpu_first_thread_in_core(PER_THREAD_PARAMS) ++int is_cpu_first_thread_in_core(struct thread_data *t, struct core_data *c) + { +- UNUSED(p); +- + return ((int)t->cpu_id == c->base_cpu || c->base_cpu < 0); + } + +-int is_cpu_first_core_in_package(PER_THREAD_PARAMS) ++int is_cpu_first_core_in_package(struct thread_data *t, struct pkg_data *p) + { +- UNUSED(c); +- + return ((int)t->cpu_id == p->base_cpu || p->base_cpu < 0); + } + +-int is_cpu_first_thread_in_package(PER_THREAD_PARAMS) ++int is_cpu_first_thread_in_package(struct thread_data *t, struct core_data *c, struct pkg_data *p) + { +- return is_cpu_first_thread_in_core(t, c, p) && is_cpu_first_core_in_package(t, c, p); ++ return is_cpu_first_thread_in_core(t, c) && is_cpu_first_core_in_package(t, p); + } + + int cpu_migrate(int cpu) +@@ -2657,6 +2669,12 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + } else if (!strcmp(name_list, "idle")) { + CPU_OR(ret_set, ret_set, &bic_group_idle); + break; ++ } else if (!strcmp(name_list, "cache")) { ++ CPU_OR(ret_set, ret_set, &bic_group_cache); ++ break; ++ } else if (!strcmp(name_list, "llc")) { ++ CPU_OR(ret_set, ret_set, &bic_group_cache); ++ break; + } else if (!strcmp(name_list, "swidle")) { + CPU_OR(ret_set, ret_set, &bic_group_sw_idle); + break; +@@ -2794,6 +2812,12 @@ void print_header(char *delim) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%sSMI", (printed++ ? delim : "")); + ++ if (DO_BIC(BIC_LLC_RPS)) ++ outp += sprintf(outp, "%sLLCkRPS", (printed++ ? delim : "")); ++ ++ if (DO_BIC(BIC_LLC_HIT)) ++ outp += sprintf(outp, "%sLLC%%hit", (printed++ ? delim : "")); ++ + for (mp = sys.tp; mp; mp = mp->next) + outp += print_name(mp->width, &printed, delim, mp->name); + +@@ -2864,7 +2888,6 @@ void print_header(char *delim) + + ppmt = ppmt->next; + } +- + if (DO_BIC(BIC_PkgTmp)) + outp += sprintf(outp, "%sPkgTmp", (printed++ ? delim : "")); + +@@ -3001,6 +3024,10 @@ int dump_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "SMI: %d\n", t->smi_count); + ++ outp += sprintf(outp, "LLC refs: %lld", t->llc.references); ++ outp += sprintf(outp, "LLC miss: %lld", t->llc.misses); ++ outp += sprintf(outp, "LLC Hit%%: %.2f", 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + outp += + sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +@@ -3008,7 +3035,7 @@ int dump_counters(PER_THREAD_PARAMS) + } + } + +- if (c && is_cpu_first_thread_in_core(t, c, p)) { ++ if (c && is_cpu_first_thread_in_core(t, c)) { + outp += sprintf(outp, "core: %d\n", c->core_id); + outp += sprintf(outp, "c3: %016llX\n", c->c3); + outp += sprintf(outp, "c6: %016llX\n", c->c6); +@@ -3030,7 +3057,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "mc6_us: %016llX\n", c->mc6_us); + } + +- if (p && is_cpu_first_core_in_package(t, c, p)) { ++ if (p && is_cpu_first_core_in_package(t, p)) { + outp += sprintf(outp, "package: %d\n", p->package_id); + + outp += sprintf(outp, "Weighted cores: %016llX\n", p->pkg_wtd_core_c0); +@@ -3088,6 +3115,26 @@ double rapl_counter_get_value(const struct rapl_counter *c, enum rapl_unit desir + return scaled; + } + ++void get_perf_llc_stats(int cpu, struct llc_stats *llc) ++{ ++ struct read_format { ++ unsigned long long num_read; ++ struct llc_stats llc; ++ } r; ++ const ssize_t expected_read_size = sizeof(r); ++ ssize_t actual_read_size; ++ ++ actual_read_size = read(fd_llc_percpu[cpu], &r, expected_read_size); ++ ++ if (actual_read_size == -1) ++ err(-1, "%s(cpu%d,) %d,,%ld\n", __func__, cpu, fd_llc_percpu[cpu], expected_read_size); ++ ++ llc->references = r.llc.references; ++ llc->misses = r.llc.misses; ++ if (actual_read_size != expected_read_size) ++ warn("%s: failed to read perf_data (req %zu act %zu)", __func__, expected_read_size, actual_read_size); ++} ++ + /* + * column formatting convention & formats + */ +@@ -3097,7 +3144,8 @@ int format_counters(PER_THREAD_PARAMS) + + struct platform_counters *pplat_cnt = NULL; + double interval_float, tsc; +- char *fmt8; ++ char *fmt8 = "%s%.2f"; ++ + int i; + struct msr_counter *mp; + struct perf_counter_info *pp; +@@ -3111,11 +3159,11 @@ int format_counters(PER_THREAD_PARAMS) + } + + /* if showing only 1st thread in core and this isn't one, bail out */ +- if (show_core_only && !is_cpu_first_thread_in_core(t, c, p)) ++ if (show_core_only && !is_cpu_first_thread_in_core(t, c)) + return 0; + + /* if showing only 1st thread in pkg and this isn't one, bail out */ +- if (show_pkg_only && !is_cpu_first_core_in_package(t, c, p)) ++ if (show_pkg_only && !is_cpu_first_core_in_package(t, p)) + return 0; + + /*if not summary line and --cpu is used */ +@@ -3237,6 +3285,16 @@ int format_counters(PER_THREAD_PARAMS) + if (DO_BIC(BIC_SMI)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), t->smi_count); + ++ /* LLC Stats */ ++ if (DO_BIC(BIC_LLC_RPS) || DO_BIC(BIC_LLC_HIT)) { ++ if (DO_BIC(BIC_LLC_RPS)) ++ outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), t->llc.references / interval_float / 1000); ++ ++ if (DO_BIC(BIC_LLC_HIT)) ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ } ++ ++ + /* Added Thread Counters */ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) +@@ -3290,7 +3348,7 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->c1 / tsc); + + /* print per-core data only for 1st thread in core */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + goto done; + + if (DO_BIC(BIC_CPU_c3)) +@@ -3351,8 +3409,6 @@ int format_counters(PER_THREAD_PARAMS) + } + } + +- fmt8 = "%s%.2f"; +- + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) + outp += + sprintf(outp, fmt8, (printed++ ? delim : ""), +@@ -3362,7 +3418,7 @@ int format_counters(PER_THREAD_PARAMS) + rapl_counter_get_value(&c->core_energy, RAPL_UNIT_JOULES, interval_float)); + + /* print per-package data only for 1st core in package */ +- if (!is_cpu_first_core_in_package(t, c, p)) ++ if (!is_cpu_first_core_in_package(t, p)) + goto done; + + /* PkgTmp */ +@@ -3808,6 +3864,12 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + if (DO_BIC(BIC_SMI)) + old->smi_count = new->smi_count - old->smi_count; + ++ if (DO_BIC(BIC_LLC_RPS)) ++ old->llc.references = new->llc.references - old->llc.references; ++ ++ if (DO_BIC(BIC_LLC_HIT)) ++ old->llc.misses = new->llc.misses - old->llc.misses; ++ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW || mp->format == FORMAT_AVERAGE) + old->counter[i] = new->counter[i]; +@@ -3838,14 +3900,14 @@ int delta_cpu(struct thread_data *t, struct core_data *c, + int retval = 0; + + /* calculate core delta only for 1st thread in core */ +- if (is_cpu_first_thread_in_core(t, c, p)) ++ if (is_cpu_first_thread_in_core(t, c)) + delta_core(c, c2); + + /* always calculate thread delta */ + retval = delta_thread(t, t2, c2); /* c2 is core delta */ + + /* calculate package delta only for 1st core in package */ +- if (is_cpu_first_core_in_package(t, c, p)) ++ if (is_cpu_first_core_in_package(t, p)) + retval |= delta_package(p, p2); + + return retval; +@@ -3886,6 +3948,9 @@ void clear_counters(PER_THREAD_PARAMS) + t->nmi_count = 0; + t->smi_count = 0; + ++ t->llc.references = 0; ++ t->llc.misses = 0; ++ + c->c3 = 0; + c->c6 = 0; + c->c7 = 0; +@@ -3894,6 +3959,9 @@ void clear_counters(PER_THREAD_PARAMS) + rapl_counter_clear(&c->core_energy); + c->core_throt_cnt = 0; + ++ t->llc.references = 0; ++ t->llc.misses = 0; ++ + p->pkg_wtd_core_c0 = 0; + p->pkg_any_core_c0 = 0; + p->pkg_any_gfxe_c0 = 0; +@@ -3991,6 +4059,9 @@ int sum_counters(PER_THREAD_PARAMS) + average.threads.nmi_count += t->nmi_count; + average.threads.smi_count += t->smi_count; + ++ average.threads.llc.references += t->llc.references; ++ average.threads.llc.misses += t->llc.misses; ++ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) + continue; +@@ -4008,7 +4079,7 @@ int sum_counters(PER_THREAD_PARAMS) + } + + /* sum per-core values only for 1st thread in core */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + return 0; + + average.cores.c3 += c->c3; +@@ -4038,7 +4109,7 @@ int sum_counters(PER_THREAD_PARAMS) + } + + /* sum per-pkg values only for 1st core in pkg */ +- if (!is_cpu_first_core_in_package(t, c, p)) ++ if (!is_cpu_first_core_in_package(t, p)) + return 0; + + if (DO_BIC(BIC_Totl_c0)) +@@ -5009,6 +5080,9 @@ int get_counters(PER_THREAD_PARAMS) + + get_smi_aperf_mperf(cpu, t); + ++ if (DO_BIC(BIC_LLC_RPS) || DO_BIC(BIC_LLC_HIT)) ++ get_perf_llc_stats(cpu, &t->llc); ++ + if (DO_BIC(BIC_IPC)) + if (read(get_instr_count_fd(cpu), &t->instr_count, sizeof(long long)) != sizeof(long long)) + return -4; +@@ -5032,7 +5106,7 @@ int get_counters(PER_THREAD_PARAMS) + t->pmt_counter[i] = pmt_read_counter(pp, t->cpu_id); + + /* collect core counters only for 1st thread in core */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + goto done; + + if (platform->has_per_core_rapl) { +@@ -5076,7 +5150,7 @@ int get_counters(PER_THREAD_PARAMS) + c->pmt_counter[i] = pmt_read_counter(pp, c->core_id); + + /* collect package counters only for 1st core in package */ +- if (!is_cpu_first_core_in_package(t, c, p)) ++ if (!is_cpu_first_core_in_package(t, p)) + goto done; + + if (DO_BIC(BIC_Totl_c0)) { +@@ -5631,6 +5705,20 @@ void free_fd_instr_count_percpu(void) + fd_instr_count_percpu = NULL; + } + ++void free_fd_llc_percpu(void) ++{ ++ if (!fd_llc_percpu) ++ return; ++ ++ for (int i = 0; i < topo.max_cpu_num + 1; ++i) { ++ if (fd_llc_percpu[i] != 0) ++ close(fd_llc_percpu[i]); ++ } ++ ++ free(fd_llc_percpu); ++ fd_llc_percpu = NULL; ++} ++ + void free_fd_cstate(void) + { + if (!ccstate_counter_info) +@@ -5755,6 +5843,7 @@ void free_all_buffers(void) + + free_fd_percpu(); + free_fd_instr_count_percpu(); ++ free_fd_llc_percpu(); + free_fd_msr(); + free_fd_rapl_percpu(); + free_fd_cstate(); +@@ -6101,6 +6190,7 @@ void linux_perf_init(void); + void msr_perf_init(void); + void rapl_perf_init(void); + void cstate_perf_init(void); ++void perf_llc_init(void); + void added_perf_counters_init(void); + void pmt_init(void); + +@@ -6112,6 +6202,7 @@ void re_initialize(void) + msr_perf_init(); + rapl_perf_init(); + cstate_perf_init(); ++ perf_llc_init(); + added_perf_counters_init(); + pmt_init(); + fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, +@@ -7976,7 +8067,7 @@ int print_thermal(PER_THREAD_PARAMS) + cpu = t->cpu_id; + + /* DTS is per-core, no need to print for each thread */ +- if (!is_cpu_first_thread_in_core(t, c, p)) ++ if (!is_cpu_first_thread_in_core(t, c)) + return 0; + + if (cpu_migrate(cpu)) { +@@ -7984,7 +8075,7 @@ int print_thermal(PER_THREAD_PARAMS) + return -1; + } + +- if (do_ptm && is_cpu_first_core_in_package(t, c, p)) { ++ if (do_ptm && is_cpu_first_core_in_package(t, p)) { + if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_STATUS, &msr)) + return 0; + +@@ -8263,6 +8354,11 @@ void linux_perf_init(void) + if (fd_instr_count_percpu == NULL) + err(-1, "calloc fd_instr_count_percpu"); + } ++ if (BIC_IS_ENABLED(BIC_LLC_RPS)) { ++ fd_llc_percpu = calloc(topo.max_cpu_num + 1, sizeof(int)); ++ if (fd_llc_percpu == NULL) ++ err(-1, "calloc fd_llc_percpu"); ++ } + } + + void rapl_perf_init(void) +@@ -8933,6 +9029,40 @@ void probe_pm_features(void) + decode_misc_feature_control(); + } + ++void perf_llc_init(void) ++{ ++ int cpu; ++ int retval; ++ ++ if (no_perf) ++ return; ++ if (!(BIC_IS_ENABLED(BIC_LLC_RPS) && BIC_IS_ENABLED(BIC_LLC_HIT))) ++ return; ++ ++ for (cpu = 0; cpu <= topo.max_cpu_num; ++cpu) { ++ ++ if (cpu_is_not_allowed(cpu)) ++ continue; ++ ++ assert(fd_llc_percpu != 0); ++ fd_llc_percpu[cpu] = open_perf_counter(cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, -1, PERF_FORMAT_GROUP); ++ if (fd_llc_percpu[cpu] == -1) { ++ warnx("%s: perf REFS: failed to open counter on cpu%d", __func__, cpu); ++ free_fd_llc_percpu(); ++ return; ++ } ++ assert(fd_llc_percpu != 0); ++ retval = open_perf_counter(cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, fd_llc_percpu[cpu], PERF_FORMAT_GROUP); ++ if (retval == -1) { ++ warnx("%s: perf MISS: failed to open counter on cpu%d", __func__, cpu); ++ free_fd_llc_percpu(); ++ return; ++ } ++ } ++ BIC_PRESENT(BIC_LLC_RPS); ++ BIC_PRESENT(BIC_LLC_HIT); ++} ++ + /* + * in /dev/cpu/ return success for names that are numbers + * ie. filter out ".", "..", "microcode". +@@ -9239,6 +9369,7 @@ void init_counter(struct thread_data *thread_base, struct core_data *core_base, + + t->cpu_id = cpu_id; + if (!cpu_is_not_allowed(cpu_id)) { ++ + if (c->base_cpu < 0) + c->base_cpu = t->cpu_id; + if (pkg_base[pkg_id].base_cpu < 0) +@@ -9909,6 +10040,7 @@ void turbostat_init() + linux_perf_init(); + rapl_perf_init(); + cstate_perf_init(); ++ perf_llc_init(); + added_perf_counters_init(); + pmt_init(); + +@@ -10014,7 +10146,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.10.18 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.10.24 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel/0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi b/SPECS/kernel/0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi deleted file mode 100644 index 148023976..000000000 --- a/SPECS/kernel/0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi +++ /dev/null @@ -1,242 +0,0 @@ -From 42342460efa55247a870c163fbcea60501dfb4a4 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 13:22:02 -0700 -Subject: [PATCH 09/44] KVM: VMX: Add support for FRED context save/restore - -Handle FRED MSR access requests, allowing FRED context to be set/get -from both host and guest. - -During VM save/restore and live migration, FRED context needs to be -saved/restored, which requires FRED MSRs to be accessed from userspace, -e.g., Qemu. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v6: -* Return KVM_MSR_RET_UNSUPPORTED instead of 1 when FRED is not available - (Chao Gao) -* Handle MSR_IA32_PL0_SSP when FRED is enumerated but CET not. - -Change in v5: -* Use the newly added guest MSR read/write helpers (Sean). -* Check the size of fred_msr_vmcs_fields[] using static_assert() (Sean). -* Rewrite setting FRED MSRs to make it much easier to read (Sean). -* Add TB from Xuelian Guo. - -Changes since v2: -* Add a helper to convert FRED MSR index to VMCS field encoding to - make the code more compact (Chao Gao). -* Get rid of the "host_initiated" check because userspace has to set - CPUID before MSRs (Chao Gao & Sean Christopherson). -* Address a few cleanup comments (Sean Christopherson). - -Changes since v1: -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). -* Fail host requested FRED MSRs access if KVM cannot virtualize FRED - (Chao Gao). -* Handle the case FRED MSRs are valid but KVM cannot virtualize FRED - (Chao Gao). -* Add sanity checks when writing to FRED MSRs. ---- - arch/x86/kvm/vmx/vmx.c | 45 +++++++++++++++++++++++++++ - arch/x86/kvm/x86.c | 69 ++++++++++++++++++++++++++++++++++++++++-- - 2 files changed, 111 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 30322c4897cb..ba0177a97417 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1390,6 +1390,18 @@ static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) - vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data, - &vmx->msr_guest_kernel_gs_base); - } -+ -+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) -+{ -+ return vmx_read_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, -+ &vmx->msr_guest_fred_rsp0); -+} -+ -+static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) -+{ -+ vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, -+ &vmx->msr_guest_fred_rsp0); -+} - #endif - - static void grow_ple_window(struct kvm_vcpu *vcpu) -@@ -1991,6 +2003,27 @@ int vmx_get_feature_msr(u32 msr, u64 *data) - } - } - -+#ifdef CONFIG_X86_64 -+static const u32 fred_msr_vmcs_fields[] = { -+ GUEST_IA32_FRED_RSP1, -+ GUEST_IA32_FRED_RSP2, -+ GUEST_IA32_FRED_RSP3, -+ GUEST_IA32_FRED_STKLVLS, -+ GUEST_IA32_FRED_SSP1, -+ GUEST_IA32_FRED_SSP2, -+ GUEST_IA32_FRED_SSP3, -+ GUEST_IA32_FRED_CONFIG, -+}; -+ -+static_assert(MSR_IA32_FRED_CONFIG - MSR_IA32_FRED_RSP1 == -+ ARRAY_SIZE(fred_msr_vmcs_fields) - 1); -+ -+static u32 fred_msr_to_vmcs(u32 msr) -+{ -+ return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1]; -+} -+#endif -+ - /* - * Reads an msr value (of 'msr_info->index') into 'msr_info->data'. - * Returns 0 on success, non-0 otherwise. -@@ -2013,6 +2046,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - case MSR_KERNEL_GS_BASE: - msr_info->data = vmx_read_guest_kernel_gs_base(vmx); - break; -+ case MSR_IA32_FRED_RSP0: -+ msr_info->data = vmx_read_guest_fred_rsp0(vmx); -+ break; -+ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: -+ msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index)); -+ break; - #endif - case MSR_EFER: - return kvm_get_msr_common(vcpu, msr_info); -@@ -2245,6 +2284,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - vmx_update_exception_bitmap(vcpu); - } - break; -+ case MSR_IA32_FRED_RSP0: -+ vmx_write_guest_fred_rsp0(vmx, data); -+ break; -+ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: -+ vmcs_write64(fred_msr_to_vmcs(msr_index), data); -+ break; - #endif - case MSR_IA32_SYSENTER_CS: - if (is_guest_mode(vcpu)) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index da04331a4200..bd1c2eb7c538 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -333,6 +333,9 @@ static const u32 msrs_to_save_base[] = { - MSR_STAR, - #ifdef CONFIG_X86_64 - MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR, -+ MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2, -+ MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1, -+ MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG, - #endif - MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA, - MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX, -@@ -1902,7 +1905,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, - if (!host_initiated) - return 1; - fallthrough; -- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: - if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) - return KVM_MSR_RET_UNSUPPORTED; - if (is_noncanonical_msr_address(data, vcpu)) -@@ -1911,6 +1914,48 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, - if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4)) - return 1; - break; -+ case MSR_IA32_FRED_STKLVLS: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: -+ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_CONFIG: -+ u64 reserved_bits = 0; -+ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ -+ if (is_noncanonical_msr_address(data, vcpu)) -+ return 1; -+ -+ switch (index) { -+ case MSR_IA32_FRED_CONFIG: -+ reserved_bits = BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2); -+ break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: -+ reserved_bits = GENMASK_ULL(5, 0); -+ break; -+ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_SSP3: -+ reserved_bits = GENMASK_ULL(2, 0); -+ break; -+ default: -+ WARN_ON_ONCE(1); -+ return 1; -+ } -+ if (data & reserved_bits) -+ return 1; -+ break; -+ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ -+ if (is_noncanonical_msr_address(data, vcpu)) -+ return 1; -+ -+ if (!IS_ALIGNED(data, 4)) -+ return 1; -+ break; - } - - msr.data = data; -@@ -1965,10 +2010,19 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, - if (!host_initiated) - return 1; - fallthrough; -- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: - if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) - return KVM_MSR_RET_UNSUPPORTED; - break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; -+ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; - } - - msr.index = index; -@@ -7538,10 +7592,19 @@ static void kvm_probe_msr_to_save(u32 msr_index) - if (!kvm_cpu_cap_has(X86_FEATURE_LM)) - return; - fallthrough; -- case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ case MSR_IA32_PL1_SSP ... MSR_IA32_PL3_SSP: - if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK)) - return; - break; -+ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: -+ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) -+ return; -+ break; -+ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && -+ !kvm_cpu_cap_has(X86_FEATURE_FRED)) -+ return; -+ break; - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel/0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi b/SPECS/kernel/0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi new file mode 100644 index 000000000..43a3841e2 --- /dev/null +++ b/SPECS/kernel/0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi @@ -0,0 +1,85 @@ +From adf1a7131298285f5f222362b94a0b9f37a4a1c1 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 31 Aug 2023 01:23:00 -0700 +Subject: [PATCH 09/44] KVM: VMX: Save/restore guest FRED RSP0 + +Save guest FRED RSP0 in vmx_prepare_switch_to_host() and restore it +in vmx_prepare_switch_to_guest() because MSR_IA32_FRED_RSP0 is passed +through to the guest, thus is volatile/unknown. + +Note, host FRED RSP0 is restored in arch_exit_to_user_mode_prepare(), +regardless of whether it is modified in KVM. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v5: +* Remove the cpu_feature_enabled() check when set/get guest + MSR_IA32_FRED_RSP0, as guest_cpu_cap_has() should suffice (Sean). +* Add a comment when synchronizing current MSR_IA32_FRED_RSP0 MSR to + the kernel's local cache, because its handling is different from + the MSR_KERNEL_GS_BASE handling (Sean). +* Add TB from Xuelian Guo. + +Changes in v3: +* KVM only needs to save/restore guest FRED RSP0 now as host FRED RSP0 + is restored in arch_exit_to_user_mode_prepare() (Sean Christopherson). + +Changes in v2: +* Don't use guest_cpuid_has() in vmx_prepare_switch_to_{host,guest}(), + which are called from IRQ-disabled context (Chao Gao). +* Reset msr_guest_fred_rsp0 in __vmx_vcpu_reset() (Chao Gao). +--- + arch/x86/kvm/vmx/vmx.c | 13 +++++++++++++ + arch/x86/kvm/vmx/vmx.h | 1 + + 2 files changed, 14 insertions(+) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 1c639496a7562..3ac89ccf47210 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1292,6 +1292,9 @@ void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu) + } + + wrmsrq(MSR_KERNEL_GS_BASE, vmx->msr_guest_kernel_gs_base); ++ ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ wrmsrns(MSR_IA32_FRED_RSP0, vmx->msr_guest_fred_rsp0); + #else + savesegment(fs, fs_sel); + savesegment(gs, gs_sel); +@@ -1336,6 +1339,16 @@ static void vmx_prepare_switch_to_host(struct vcpu_vmx *vmx) + invalidate_tss_limit(); + #ifdef CONFIG_X86_64 + wrmsrq(MSR_KERNEL_GS_BASE, vmx->vt.msr_host_kernel_gs_base); ++ ++ if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED)) { ++ vmx->msr_guest_fred_rsp0 = read_msr(MSR_IA32_FRED_RSP0); ++ /* ++ * Synchronize the current value in hardware to the kernel's ++ * local cache. The desired host RSP0 will be set when the ++ * CPU exits to userspace (RSP0 is a per-task value). ++ */ ++ fred_sync_rsp0(vmx->msr_guest_fred_rsp0); ++ } + #endif + load_fixmap_gdt(raw_smp_processor_id()); + vmx->vt.guest_state_loaded = false; +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index 2cf599211ab30..c4a3b28553fbd 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -227,6 +227,7 @@ struct vcpu_vmx { + bool guest_uret_msrs_loaded; + #ifdef CONFIG_X86_64 + u64 msr_guest_kernel_gs_base; ++ u64 msr_guest_fred_rsp0; + #endif + + u64 spec_ctrl; +-- +2.43.0 + diff --git a/SPECS/kernel/0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet b/SPECS/kernel/0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet deleted file mode 100644 index 9c0adb9b4..000000000 --- a/SPECS/kernel/0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet +++ /dev/null @@ -1,59 +0,0 @@ -From 2ee63e38a5bc5418e85af793c33ff0cb36d8b546 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:39 -0700 -Subject: [PATCH 09/24] KVM: x86: Initialize kvm_caps.supported_xss - -Set original kvm_caps.supported_xss to (host_xss & KVM_SUPPORTED_XSS) if -XSAVES is supported. host_xss contains the host supported xstate feature -bits for thread FPU context switch, KVM_SUPPORTED_XSS includes all KVM -enabled XSS feature bits, the resulting value represents the supervisor -xstates that are available to guest and are backed by host FPU framework -for swapping {guest,host} XSAVE-managed registers/MSRs. - -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 11 ++++++++--- - 1 file changed, 8 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 3057ae88b6ba..46b45eeadf47 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -224,6 +224,8 @@ static struct kvm_user_return_msrs __percpu *user_return_msrs; - | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 \ - | XFEATURE_MASK_PKRU | XFEATURE_MASK_XTILE) - -+#define KVM_SUPPORTED_XSS 0 -+ - bool __read_mostly allow_smaller_maxphyaddr = 0; - EXPORT_SYMBOL_GPL(allow_smaller_maxphyaddr); - -@@ -9744,14 +9746,17 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - kvm_host.xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); - kvm_caps.supported_xcr0 = kvm_host.xcr0 & KVM_SUPPORTED_XCR0; - } -+ -+ if (boot_cpu_has(X86_FEATURE_XSAVES)) { -+ rdmsrq(MSR_IA32_XSS, kvm_host.xss); -+ kvm_caps.supported_xss = kvm_host.xss & KVM_SUPPORTED_XSS; -+ } -+ - kvm_caps.supported_quirks = KVM_X86_VALID_QUIRKS; - kvm_caps.inapplicable_quirks = KVM_X86_CONDITIONAL_QUIRKS; - - rdmsrq_safe(MSR_EFER, &kvm_host.efer); - -- if (boot_cpu_has(X86_FEATURE_XSAVES)) -- rdmsrq(MSR_IA32_XSS, kvm_host.xss); -- - kvm_init_pmu_capability(ops->pmu_ops); - - if (boot_cpu_has(X86_FEATURE_ARCH_CAPABILITIES)) --- -2.43.0 - diff --git a/SPECS/kernel/0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt b/SPECS/kernel/0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt deleted file mode 100644 index aeaf00e4a..000000000 --- a/SPECS/kernel/0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt +++ /dev/null @@ -1,29 +0,0 @@ -From 17f438779c1aa42aefb4f97da991a1a1bc72722c Mon Sep 17 00:00:00 2001 -From: Sebastian Andrzej Siewior -Date: Mon, 21 Feb 2022 17:59:14 +0100 -Subject: [PATCH 9/9] Revert "drm/i915: Depend on !PREEMPT_RT." - -Once the known issues are addressed, it should be safe to enable the -driver. - -Acked-by: Tvrtko Ursulin -Signed-off-by: Sebastian Andrzej Siewior ---- - drivers/gpu/drm/i915/Kconfig | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig -index 5e939004b646..40a9234e6e5d 100644 ---- a/drivers/gpu/drm/i915/Kconfig -+++ b/drivers/gpu/drm/i915/Kconfig -@@ -3,7 +3,6 @@ config DRM_I915 - tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" - depends on DRM - depends on X86 && PCI -- depends on !PREEMPT_RT - select INTEL_GTT if X86 - select INTERVAL_TREE - # we need shmfs for the swappable backing store, and in particular --- -2.43.0 - diff --git a/SPECS/kernel/0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c b/SPECS/kernel/0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c deleted file mode 100644 index bc62e501d..000000000 --- a/SPECS/kernel/0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c +++ /dev/null @@ -1,97 +0,0 @@ -From 87e39c8a0d700c78c5467465551cf812ed4123dc Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:08 +0300 -Subject: [PATCH 09/11] i3c: mipi-i3c-hci: Remove function enter DBG() - printouts - -These function enter DBG("") printouts are not very useful in error -report point of view because they require code recompile. In which case -they can be replaced with more informative debug prints if needed so -remove them for now. - -Signed-off-by: Jarkko Nikula -Link: https://lore.kernel.org/r/20250827103009.243771-5-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/core.c | 16 ---------------- - 1 file changed, 16 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index d532933ac7ab..9932945ecf06 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -121,8 +121,6 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) - struct i3c_device_info info; - int ret; - -- DBG(""); -- - if (hci->cmd == &mipi_i3c_hci_cmd_v1) { - ret = mipi_i3c_hci_dat_v1.init(hci); - if (ret) -@@ -159,8 +157,6 @@ static void i3c_hci_bus_cleanup(struct i3c_master_controller *m) - struct i3c_hci *hci = to_i3c_hci(m); - struct platform_device *pdev = to_platform_device(m->dev.parent); - -- DBG(""); -- - reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE); - synchronize_irq(platform_get_irq(pdev, 0)); - hci->io->cleanup(hci); -@@ -267,8 +263,6 @@ static int i3c_hci_daa(struct i3c_master_controller *m) - { - struct i3c_hci *hci = to_i3c_hci(m); - -- DBG(""); -- - return hci->cmd->perform_daa(hci); - } - -@@ -385,8 +379,6 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev) - struct i3c_hci_dev_data *dev_data; - int ret; - -- DBG(""); -- - dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); - if (!dev_data) - return -ENOMEM; -@@ -410,8 +402,6 @@ static int i3c_hci_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr) - struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - -- DBG(""); -- - if (hci->cmd == &mipi_i3c_hci_cmd_v1) - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dev_data->dat_idx, - dev->info.dyn_addr); -@@ -424,8 +414,6 @@ static void i3c_hci_detach_i3c_dev(struct i3c_dev_desc *dev) - struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - -- DBG(""); -- - i3c_dev_set_master_data(dev, NULL); - if (hci->cmd == &mipi_i3c_hci_cmd_v1) - mipi_i3c_hci_dat_v1.free_entry(hci, dev_data->dat_idx); -@@ -439,8 +427,6 @@ static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *dev) - struct i3c_hci_dev_data *dev_data; - int ret; - -- DBG(""); -- - if (hci->cmd != &mipi_i3c_hci_cmd_v1) - return 0; - dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); -@@ -464,8 +450,6 @@ static void i3c_hci_detach_i2c_dev(struct i2c_dev_desc *dev) - struct i3c_hci *hci = to_i3c_hci(m); - struct i3c_hci_dev_data *dev_data = i2c_dev_get_master_data(dev); - -- DBG(""); -- - if (dev_data) { - i2c_dev_set_master_data(dev, NULL); - if (hci->cmd == &mipi_i3c_hci_cmd_v1) --- -2.43.0 - diff --git a/SPECS/kernel/0009-igc-Add-BTF-based-metadata-for-XDP.ethernet b/SPECS/kernel/0009-igc-Add-BTF-based-metadata-for-XDP.ethernet deleted file mode 100644 index e2cca1fdf..000000000 --- a/SPECS/kernel/0009-igc-Add-BTF-based-metadata-for-XDP.ethernet +++ /dev/null @@ -1,235 +0,0 @@ -From fcd22d765ef29817abe0958e372bb4825e5a39ef Mon Sep 17 00:00:00 2001 -From: Vedang Patel -Date: Mon, 14 Jun 2021 00:35:20 +0800 -Subject: [PATCH 09/19] igc: Add BTF based metadata for XDP - -This commit adds support for BTF based metadata for XDP. Currently, the -support has only been tested on receive side. Following is the struct -describing the metadata: - -struct xdp_md_desc { - u64 timestamp; -}; - -Note that only a single member is added to the struct. More members will -be added in the future. - -Signed-off-by: Vedang Patel -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc.h | 2 + - drivers/net/ethernet/intel/igc/igc_main.c | 12 +++ - drivers/net/ethernet/intel/igc/igc_xdp.c | 114 ++++++++++++++++++++++ - drivers/net/ethernet/intel/igc/igc_xdp.h | 11 +++ - 4 files changed, 139 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h -index b9f846389e75..64d22481be91 100644 ---- a/drivers/net/ethernet/intel/igc/igc.h -+++ b/drivers/net/ethernet/intel/igc/igc.h -@@ -331,6 +331,8 @@ struct igc_adapter { - char fw_version[32]; - - struct bpf_prog *xdp_prog; -+ struct btf *btf; -+ u8 btf_enabled; - - bool pps_sys_wrap_on; - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index caf7bed4c8ae..b581e96d0735 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -14,6 +14,7 @@ - #include - #include - -+#include - #include - - #include "igc.h" -@@ -6880,6 +6881,12 @@ static int igc_bpf(struct net_device *dev, struct netdev_bpf *bpf) - case XDP_SETUP_XSK_POOL: - return igc_xdp_setup_pool(adapter, bpf->xsk.pool, - bpf->xsk.queue_id); -+ case XDP_SETUP_MD_BTF: -+ return igc_xdp_set_btf_md(dev, bpf->btf_enable); -+ case XDP_QUERY_MD_BTF: -+ bpf->btf_id = igc_xdp_query_btf(dev, &bpf->btf_enable); -+ return 0; -+ - default: - return -EOPNOTSUPP; - } -@@ -7420,6 +7427,11 @@ static void igc_remove(struct pci_dev *pdev) - if (IS_ENABLED(CONFIG_IGC_LEDS) && adapter->leds_available) - igc_led_free(adapter); - -+ if (adapter->btf) { -+ adapter->btf_enabled = 0; -+ btf_unregister(adapter->btf); -+ } -+ - /* Release control of h/w to f/w. If f/w is AMT enabled, this - * would have already happened in close and is redundant. - */ -diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.c b/drivers/net/ethernet/intel/igc/igc_xdp.c -index 9eb47b4beb06..c23e0385fc74 100644 ---- a/drivers/net/ethernet/intel/igc/igc_xdp.c -+++ b/drivers/net/ethernet/intel/igc/igc_xdp.c -@@ -3,10 +3,124 @@ - - #include - #include -+#include - - #include "igc.h" - #include "igc_xdp.h" - -+#define BTF_INFO_ENC(kind, kind_flag, vlen) \ -+ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN)) -+ -+#define BTF_TYPE_ENC(name, info, size_or_type) \ -+ (name), (info), (size_or_type) -+ -+#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \ -+ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits)) -+ -+#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \ -+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \ -+ BTF_INT_ENC(encoding, bits_offset, bits) -+ -+#define BTF_STRUCT_ENC(name, nr_elems, sz) \ -+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_STRUCT, 1, nr_elems), sz) -+ -+#define BTF_MEMBER_ENC(name, type, bits_offset) \ -+ (name), (type), (bits_offset) -+ -+/* struct xdp_md_desc { -+ * u64 timestamp; -+ * }; -+ */ -+#define IGC_MD_NUM_MMBRS 1 -+static const char names_str[] = "\0xdp_md_desc\0timestamp\0"; -+ -+/* Must match struct xdp_md_desc */ -+static const u32 igc_md_raw_types[] = { -+ /* #define u64 */ -+ BTF_TYPE_INT_ENC(0, 0, 0, 64, 8), /* type [1] */ -+ /* struct xdp_md_desc { */ -+ BTF_STRUCT_ENC(1, IGC_MD_NUM_MMBRS, 8), -+ BTF_MEMBER_ENC(13, 1, 0), /* u64 timestamp; */ -+ /* } */ -+}; -+ -+static int igc_xdp_register_btf(struct igc_adapter *priv) -+{ -+ unsigned int type_sec_sz, str_sec_sz; -+ char *types_sec, *str_sec; -+ struct btf_header *hdr; -+ unsigned int btf_size; -+ void *raw_btf = NULL; -+ int err = 0; -+ -+ type_sec_sz = sizeof(igc_md_raw_types); -+ str_sec_sz = sizeof(names_str); -+ -+ btf_size = sizeof(*hdr) + type_sec_sz + str_sec_sz; -+ raw_btf = kzalloc(btf_size, GFP_KERNEL); -+ if (!raw_btf) -+ return -ENOMEM; -+ -+ hdr = raw_btf; -+ hdr->magic = BTF_MAGIC; -+ hdr->version = BTF_VERSION; -+ hdr->hdr_len = sizeof(*hdr); -+ hdr->type_off = 0; -+ hdr->type_len = type_sec_sz; -+ hdr->str_off = type_sec_sz; -+ hdr->str_len = str_sec_sz; -+ -+ types_sec = raw_btf + sizeof(*hdr); -+ str_sec = types_sec + type_sec_sz; -+ memcpy(types_sec, igc_md_raw_types, type_sec_sz); -+ memcpy(str_sec, names_str, str_sec_sz); -+ -+ priv->btf = btf_register(priv->netdev->name, raw_btf, btf_size); -+ if (IS_ERR(priv->btf)) { -+ err = PTR_ERR(priv->btf); -+ priv->btf = NULL; -+ netdev_err(priv->netdev, "failed to register BTF MD, err (%d)\n", err); -+ } -+ -+ kfree(raw_btf); -+ return err; -+} -+ -+int igc_xdp_query_btf(struct net_device *dev, u8 *enabled) -+{ -+ struct igc_adapter *priv = netdev_priv(dev); -+ u32 md_btf_id = 0; -+ -+ if (!IS_ENABLED(CONFIG_BPF_SYSCALL)) -+ return md_btf_id; -+ -+ if (!priv->btf) -+ igc_xdp_register_btf(priv); -+ -+ *enabled = !!priv->btf_enabled; -+ md_btf_id = priv->btf ? btf_obj_id(priv->btf) : 0; -+ -+ return md_btf_id; -+} -+ -+int igc_xdp_set_btf_md(struct net_device *dev, u8 enable) -+{ -+ struct igc_adapter *priv = netdev_priv(dev); -+ int err = 0; -+ -+ if (enable && !priv->btf) { -+ igc_xdp_register_btf(priv); -+ if (!priv->btf) { -+ err = -EINVAL; -+ goto unlock; -+ } -+ } -+ -+ priv->btf_enabled = enable; -+unlock: -+ return err; -+} -+ - int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, - struct netlink_ext_ack *extack) - { -diff --git a/drivers/net/ethernet/intel/igc/igc_xdp.h b/drivers/net/ethernet/intel/igc/igc_xdp.h -index a74e5487d199..644dd8a49a3a 100644 ---- a/drivers/net/ethernet/intel/igc/igc_xdp.h -+++ b/drivers/net/ethernet/intel/igc/igc_xdp.h -@@ -4,6 +4,12 @@ - #ifndef _IGC_XDP_H_ - #define _IGC_XDP_H_ - -+#include -+ -+struct igc_md_desc { -+ u64 timestamp; -+}; -+ - int igc_xdp_set_prog(struct igc_adapter *adapter, struct bpf_prog *prog, - struct netlink_ext_ack *extack); - int igc_xdp_setup_pool(struct igc_adapter *adapter, struct xsk_buff_pool *pool, -@@ -14,4 +20,9 @@ static inline bool igc_xdp_is_enabled(struct igc_adapter *adapter) - return !!adapter->xdp_prog; - } - -+int igc_xdp_register_rxq_info(struct igc_ring *ring); -+void igc_xdp_unregister_rxq_info(struct igc_ring *ring); -+int igc_xdp_query_btf(struct net_device *dev, u8 *enabled); -+int igc_xdp_set_btf_md(struct net_device *dev, u8 enable); -+ - #endif /* _IGC_XDP_H_ */ --- -2.43.0 - diff --git a/SPECS/kernel/0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel/0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet new file mode 100644 index 000000000..b6db57848 --- /dev/null +++ b/SPECS/kernel/0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet @@ -0,0 +1,43 @@ +From 84f1e9b417eb559e6b2b65280e5f656d0f9a1eea Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Wed, 9 Jun 2021 01:56:30 +0800 +Subject: [PATCH 09/14] igc: Enable HW RX Timestamp for AF_XDP ZC + +Enable the RX HW Timestamp using meta data to userspace. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc_main.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 526b98b603c05..5b797a0bd870d 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -2815,6 +2815,7 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) + u16 cleaned_count = igc_desc_unused(ring); + int total_bytes = 0, total_packets = 0; + u16 ntc = ring->next_to_clean; ++ struct igc_md_desc *md; + struct bpf_prog *prog; + bool failure = false; + int xdp_status = 0; +@@ -2863,6 +2864,14 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) + bi->xdp->data_end = bi->xdp->data + size; + xsk_buff_dma_sync_for_cpu(bi->xdp); + ++ if (adapter->btf_enabled) { ++ md = bi->xdp->data - sizeof(*md); ++ md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); ++ bi->xdp->data_meta = md; ++ } else { ++ xdp_set_data_meta_invalid(bi->xdp); ++ } ++ + res = __igc_xdp_run_prog(adapter, prog, bi->xdp); + switch (res) { + case IGC_XDP_PASS: +-- +2.43.0 + diff --git a/SPECS/kernel/0009-max9x-add-config-in-makefile-kconfig.ipu b/SPECS/kernel/0009-max9x-add-config-in-makefile-kconfig.ipu deleted file mode 100644 index 75642a5a7..000000000 --- a/SPECS/kernel/0009-max9x-add-config-in-makefile-kconfig.ipu +++ /dev/null @@ -1,53 +0,0 @@ -From ee759f8dcd8fe7bc5720ddfe2a5e55bf6da57a16 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Wed, 27 Aug 2025 11:09:54 +0800 -Subject: [PATCH 09/11] max9x: add config in makefile & kconfig - -Signed-off-by: hepengpx ---- - drivers/media/i2c/Kconfig | 15 +++++++++++++++ - drivers/media/i2c/Makefile | 2 ++ - 2 files changed, 17 insertions(+) - -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index 7ec788adeec1..f65a2f6118ea 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -278,6 +278,21 @@ config VIDEO_LT6911GXD - To compile this driver as a module, choose M here: the - module will be called lt6911gxd. - -+config VIDEO_ISX031 -+ tristate "ISX031 sensor support" -+ depends on VIDEO_DEV && I2C -+ select VIDEO_V4L2_SUBDEV_API -+ depends on MEDIA_CAMERA_SUPPORT -+ help -+ This is a Video4Linux2 sensor-level driver for ISX031 camera. -+ -+config VIDEO_MAX9X -+ tristate "MAX9X serdes support" -+ depends on VIDEO_DEV && I2C -+ select VIDEO_V4L2_SUBDEV_API -+ depends on MEDIA_CAMERA_SUPPORT -+ help -+ This is a Video4Linux2 sensor-level driver for MAX9X serdes. - - config VIDEO_MAX9271_LIB - tristate -diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile -index 2fbdd4c181d1..085c0e0e15dd 100644 ---- a/drivers/media/i2c/Makefile -+++ b/drivers/media/i2c/Makefile -@@ -61,6 +61,8 @@ obj-$(CONFIG_VIDEO_IMX412) += imx412.o - obj-$(CONFIG_VIDEO_IMX415) += imx415.o - obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o - obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o -+obj-$(CONFIG_VIDEO_MAX9X) += max9x/ -+obj-$(CONFIG_VIDEO_ISX031) += isx031.o - obj-$(CONFIG_VIDEO_KS0127) += ks0127.o - obj-$(CONFIG_VIDEO_LM3560) += lm3560.o - obj-$(CONFIG_VIDEO_LM3646) += lm3646.o --- -2.43.0 - diff --git a/SPECS/kernel/0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu b/SPECS/kernel/0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu deleted file mode 100644 index 6ec1374c0..000000000 --- a/SPECS/kernel/0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu +++ /dev/null @@ -1,30 +0,0 @@ -From ed2fcce3d2b80e56411b1b7216885402fadac835 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 14:58:47 +0800 -Subject: [PATCH 09/21] patch: staging add fixup some PCI probe and release - issues - -Update IPU7 on pci functions on IPU7 driver. - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 06e5f08bed4d..3a6c129015e7 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -2300,7 +2300,7 @@ static void ipu7_remove_debugfs(struct ipu7_device *isp) - } - #endif /* CONFIG_DEBUG_FS */ - --static int ipu7_pci_config_setup(struct pci_dev *dev) -+static void ipu7_pci_config_setup(struct pci_dev *dev) - { - u16 pci_command; - --- -2.43.0 - diff --git a/SPECS/kernel/0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf b/SPECS/kernel/0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf deleted file mode 100644 index 05679822a..000000000 --- a/SPECS/kernel/0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf +++ /dev/null @@ -1,41 +0,0 @@ -From 70cd5c863e317ca281602b5399395eda5b79dfbe Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Wed, 6 Mar 2024 06:58:31 +0800 -Subject: [PATCH 009/100] perf/x86: Remove helper perf_events_lapic_init() from - x86_pmu_enable() - -The helper perf_events_lapic_init() mainly writes APIC_LVTPC MSR to -configure PMI to NMI and clear MASK bit, but it seems unnecessary to -call it in x86_pmu_enable(). perf_events_lapic_init() is already called -by init_hw_perf_events() and MASK bit would be always cleared in PMI -interrupt handler. - -Since x86_pmu_enable() could be called very frequently in some high -context-switch cases, the performance overhead from APIC_LVTPC write -could not be ignored especially in Guest environment. - -For example, the Geekbench performance in perf-stat multiplexing case -increases 1% and perf-sched benchmark performance increases 7% in Guest -environment after removing perf_events_lapic_init() from -x86_pmu_enable(). - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index f0a3bc57157d..2007cfe1a0f5 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1368,7 +1368,6 @@ static void x86_pmu_enable(struct pmu *pmu) - x86_pmu_start(event, PERF_EF_RELOAD); - } - cpuc->n_added = 0; -- perf_events_lapic_init(); - } - - cpuc->enabled = 1; --- -2.43.0 - diff --git a/SPECS/kernel/0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf b/SPECS/kernel/0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf new file mode 100644 index 000000000..8669ac358 --- /dev/null +++ b/SPECS/kernel/0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf @@ -0,0 +1,181 @@ +From 516488d4800dc8a9a24a51861e0534aa89c071ff Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 8 Oct 2025 09:57:48 -0700 +Subject: [PATCH 09/13] perf/x86/intel/uncore: Support IIO free-running + counters on DMR + +The free-running counters for IIO uncore blocks on Diamond Rapids are +similar to Sapphire Rapids IMC freecounters, with the following +differences: + +- The counters are MMIO based. +- Only a subset of IP blocks implement free-running counters: + HIOP0 (IP Base Addr: 2E7000h) + HIOP1 (IP Base Addr: 2EF000h) + HIOP3 (IP Base Addr: 2FF000h) + HIOP4 (IP Base Addr: 307000h) +- IMH2 (Secondary IMH) does not provide free-running counters. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore_snbep.c | 118 +++++++++++++++++++++++++-- + 1 file changed, 113 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index 09a3bdbd188aa..28bcccf5cdfe9 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -472,10 +472,14 @@ + #define SPR_C0_MSR_PMON_BOX_FILTER0 0x200e + + /* DMR */ ++#define DMR_IMH1_HIOP_MMIO_BASE 0x1ffff6ae7000 ++#define DMR_HIOP_MMIO_SIZE 0x8000 + #define DMR_CXLCM_EVENT_MASK_EXT 0xf + #define DMR_HAMVF_EVENT_MASK_EXT 0xffffffff + #define DMR_PCIE4_EVENT_MASK_EXT 0xffffff + ++#define UNCORE_DMR_ITC 0x30 ++ + #define DMR_IMC_PMON_FIXED_CTR 0x18 + #define DMR_IMC_PMON_FIXED_CTL 0x10 + +@@ -6442,7 +6446,11 @@ static int uncore_type_max_boxes(struct intel_uncore_type **types, + for (node = rb_first(type->boxes); node; node = rb_next(node)) { + unit = rb_entry(node, struct intel_uncore_discovery_unit, node); + +- if (unit->id > max) ++ /* ++ * on DMR IMH2, the unit id starts from 0x8000, ++ * and we don't need to count it. ++ */ ++ if ((unit->id > max) && (unit->id < 0x8000)) + max = unit->id; + } + return max + 1; +@@ -6930,6 +6938,101 @@ int dmr_uncore_cbb_units_ignore[] = { + UNCORE_IGNORE_END + }; + ++static unsigned int dmr_iio_freerunning_box_offsets[] = { ++ 0x0, 0x8000, 0x18000, 0x20000 ++}; ++ ++static void dmr_uncore_freerunning_init_box(struct intel_uncore_box *box) ++{ ++ struct intel_uncore_type *type = box->pmu->type; ++ u64 mmio_base; ++ ++ if (box->pmu->pmu_idx >= type->num_boxes) ++ return; ++ ++ mmio_base = DMR_IMH1_HIOP_MMIO_BASE; ++ mmio_base += dmr_iio_freerunning_box_offsets[box->pmu->pmu_idx]; ++ ++ box->io_addr = ioremap(mmio_base, type->mmio_map_size); ++ if (!box->io_addr) ++ pr_warn("perf uncore: Failed to ioremap for %s.\n", type->name); ++} ++ ++static struct intel_uncore_ops dmr_uncore_freerunning_ops = { ++ .init_box = dmr_uncore_freerunning_init_box, ++ .exit_box = uncore_mmio_exit_box, ++ .read_counter = uncore_mmio_read_counter, ++ .hw_config = uncore_freerunning_hw_config, ++}; ++ ++enum perf_uncore_dmr_iio_freerunning_type_id { ++ DMR_ITC_INB_DATA_BW, ++ DMR_ITC_BW_IN, ++ DMR_OTC_BW_OUT, ++ DMR_OTC_CLOCK_TICKS, ++ ++ DMR_IIO_FREERUNNING_TYPE_MAX, ++}; ++ ++static struct freerunning_counters dmr_iio_freerunning[] = { ++ [DMR_ITC_INB_DATA_BW] = { 0x4d40, 0x8, 0, 8, 48}, ++ [DMR_ITC_BW_IN] = { 0x6b00, 0x8, 0, 8, 48}, ++ [DMR_OTC_BW_OUT] = { 0x6b60, 0x8, 0, 8, 48}, ++ [DMR_OTC_CLOCK_TICKS] = { 0x6bb0, 0x8, 0, 1, 48}, ++}; ++ ++static struct uncore_event_desc dmr_uncore_iio_freerunning_events[] = { ++ /* ITC Free Running Data BW counter for inbound traffic */ ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port0, 0x10, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port1, 0x11, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port2, 0x12, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port3, 0x13, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port4, 0x14, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port5, 0x15, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port6, 0x16, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(inb_data_port7, 0x17, "3.814697266e-6"), ++ ++ /* ITC Free Running BW IN counters */ ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port0, 0x20, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port1, 0x21, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port2, 0x22, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port3, 0x23, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port4, 0x24, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port5, 0x25, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port6, 0x26, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_in_port7, 0x27, "3.814697266e-6"), ++ ++ /* ITC Free Running BW OUT counters */ ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port0, 0x30, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port1, 0x31, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port2, 0x32, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port3, 0x33, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port4, 0x34, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port5, 0x35, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port6, 0x36, "3.814697266e-6"), ++ INTEL_UNCORE_FR_EVENT_DESC(bw_out_port7, 0x37, "3.814697266e-6"), ++ ++ /* Free Running Clock Counter */ ++ INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x40"), ++ { /* end: all zeroes */ }, ++}; ++ ++static struct intel_uncore_type dmr_uncore_iio_free_running = { ++ .name = "iio_free_running", ++ .num_counters = 25, ++ .mmio_map_size = DMR_HIOP_MMIO_SIZE, ++ .num_freerunning_types = DMR_IIO_FREERUNNING_TYPE_MAX, ++ .freerunning = dmr_iio_freerunning, ++ .ops = &dmr_uncore_freerunning_ops, ++ .event_descs = dmr_uncore_iio_freerunning_events, ++ .format_group = &skx_uncore_iio_freerunning_format_group, ++}; ++ ++#define UNCORE_DMR_MMIO_EXTRA_UNCORES 1 ++static struct intel_uncore_type *dmr_mmio_uncores[UNCORE_DMR_MMIO_EXTRA_UNCORES] = { ++ &dmr_uncore_iio_free_running, ++}; ++ + int dmr_uncore_pci_init(void) + { + uncore_pci_uncores = uncore_get_uncores(UNCORE_ACCESS_PCI, 0, NULL, +@@ -6937,11 +7040,16 @@ int dmr_uncore_pci_init(void) + dmr_uncores); + return 0; + } ++ + void dmr_uncore_mmio_init(void) + { +- uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, 0, NULL, +- UNCORE_DMR_NUM_UNCORE_TYPES, +- dmr_uncores); +-} ++ uncore_mmio_uncores = uncore_get_uncores(UNCORE_ACCESS_MMIO, ++ UNCORE_DMR_MMIO_EXTRA_UNCORES, ++ dmr_mmio_uncores, ++ UNCORE_DMR_NUM_UNCORE_TYPES, ++ dmr_uncores); + ++ dmr_uncore_iio_free_running.num_boxes = ++ uncore_type_max_boxes(uncore_mmio_uncores, UNCORE_DMR_ITC); ++} + /* end of DMR uncore support */ +-- +2.43.0 + diff --git a/SPECS/kernel/0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet b/SPECS/kernel/0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet new file mode 100644 index 000000000..e63fef9b7 --- /dev/null +++ b/SPECS/kernel/0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet @@ -0,0 +1,27 @@ +From 60ecf14a41389646b286adc9987e0042380bc217 Mon Sep 17 00:00:00 2001 +From: Gan Yi Fang +Date: Thu, 22 Jun 2023 21:27:11 -0400 +Subject: [PATCH 09/18] stmmac: intel: Enable PHY WoL in ADL-N + +Enable PHY Wake On LAN in ADL-N Intel platform. + +Signed-off-by: Gan Yi Fang +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index bf6544ec769b2..ca16568374d5a 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -1019,6 +1019,7 @@ static int adln_common_data(struct pci_dev *pdev, + plat->rx_queues_to_use = 6; + plat->tx_queues_to_use = 4; + plat->clk_ptp_rate = 204800000; ++ plat->flags |= STMMAC_FLAG_USE_PHY_WOL; + + plat->safety_feat_cfg->tsoee = 1; + plat->safety_feat_cfg->mrxpee = 0; +-- +2.43.0 + diff --git a/SPECS/kernel/0009-sysfs-Add-sys-kernel-realtime-entry.rt b/SPECS/kernel/0009-sysfs-Add-sys-kernel-realtime-entry.rt new file mode 100644 index 000000000..4a9dec296 --- /dev/null +++ b/SPECS/kernel/0009-sysfs-Add-sys-kernel-realtime-entry.rt @@ -0,0 +1,54 @@ +From 80ad4b089250eab455aa32fbd3755dafe16d78a2 Mon Sep 17 00:00:00 2001 +From: Clark Williams +Date: Sat, 30 Jul 2011 21:55:53 -0500 +Subject: [PATCH 9/9] sysfs: Add /sys/kernel/realtime entry + +Add a /sys/kernel entry to indicate that the kernel is a +realtime kernel. + +Clark says that he needs this for udev rules, udev needs to evaluate +if its a PREEMPT_RT kernel a few thousand times and parsing uname +output is too slow or so. + +Are there better solutions? Should it exist and return 0 on !-rt? + +Signed-off-by: Clark Williams +Signed-off-by: Peter Zijlstra +Signed-off-by: Thomas Gleixner +--- + kernel/ksysfs.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c +index eefb67d9883c2..20ac8a5ac27d7 100644 +--- a/kernel/ksysfs.c ++++ b/kernel/ksysfs.c +@@ -188,6 +188,15 @@ KERNEL_ATTR_RO(crash_elfcorehdr_size); + + #endif /* CONFIG_VMCORE_INFO */ + ++#if defined(CONFIG_PREEMPT_RT) ++static ssize_t realtime_show(struct kobject *kobj, ++ struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d\n", 1); ++} ++KERNEL_ATTR_RO(realtime); ++#endif ++ + /* whether file capabilities are enabled */ + static ssize_t fscaps_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +@@ -271,6 +280,9 @@ static struct attribute * kernel_attrs[] = { + #ifndef CONFIG_TINY_RCU + &rcu_expedited_attr.attr, + &rcu_normal_attr.attr, ++#endif ++#ifdef CONFIG_PREEMPT_RT ++ &realtime_attr.attr, + #endif + NULL + }; +-- +2.43.0 + diff --git a/SPECS/kernel/0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal b/SPECS/kernel/0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal deleted file mode 100644 index 530a1baf7..000000000 --- a/SPECS/kernel/0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal +++ /dev/null @@ -1,35 +0,0 @@ -From 48c98aeb5aaf5b357d8ab559ccf6b36103539c3b Mon Sep 17 00:00:00 2001 -From: Osama Abdelkader -Date: Wed, 3 Sep 2025 21:20:59 +0200 -Subject: [PATCH 09/11] thermal: hwmon: replace deprecated strcpy() with - strscpy() - -Since strcpy() is deprecated and the last user of it in the thermal -subsystem is thermal_hwmon_lookup_by_type(), replace strcpy() in that -function with strscpy(). - -Signed-off-by: Osama Abdelkader -Reviewed-by: Lukasz Luba -Link: https://patch.msgid.link/20250903192059.11353-1-osama.abdelkader@gmail.com -[ rjw: Changelog rewrite ] -Signed-off-by: Rafael J. Wysocki ---- - drivers/thermal/thermal_hwmon.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c -index 0ecccd4d8556..64cc3ab949fe 100644 ---- a/drivers/thermal/thermal_hwmon.c -+++ b/drivers/thermal/thermal_hwmon.c -@@ -96,7 +96,7 @@ thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) - - mutex_lock(&thermal_hwmon_list_lock); - list_for_each_entry(hwmon, &thermal_hwmon_list, node) { -- strcpy(type, tz->type); -+ strscpy(type, tz->type); - strreplace(type, '-', '_'); - if (!strcmp(hwmon->type, type)) { - mutex_unlock(&thermal_hwmon_list_lock); --- -2.43.0 - diff --git a/SPECS/kernel/0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo b/SPECS/kernel/0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo new file mode 100644 index 000000000..12edbae0c --- /dev/null +++ b/SPECS/kernel/0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo @@ -0,0 +1,30 @@ +From 1b1e446c16021a5bc12c9ff3dc6f49d1d8d5e531 Mon Sep 17 00:00:00 2001 +From: Emily Ehlert +Date: Thu, 13 Nov 2025 19:16:08 +0000 +Subject: [PATCH 09/21] tools/power turbostat: Set per_cpu_msr_sum to NULL + after free + +Set per_cpu_msr_sum to NULL after freeing it in the error path +of msr_sum_record() to prevent potential use-after-free issues. + +Signed-off-by: Emily Ehlert +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 2854b66eb7480..8154d110dd07d 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -6652,6 +6652,7 @@ void msr_sum_record(void) + timer_delete(timerid); + release_msr: + free(per_cpu_msr_sum); ++ per_cpu_msr_sum = NULL; + } + + /* +-- +2.43.0 + diff --git a/SPECS/kernel/0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac b/SPECS/kernel/0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac deleted file mode 100644 index 6b52477d8..000000000 --- a/SPECS/kernel/0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac +++ /dev/null @@ -1,44 +0,0 @@ -From c217243dd29072f467eadbbf01719bfb4a5453b2 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Sun, 27 Oct 2019 09:23:32 +0800 -Subject: [PATCH 09/13] x86/mce: Add MCACOD code for generic I/O error - -Errors of some I/O devices can be signaled by MCE and logged in -IOMCA bank. Add MCACOD code of generic I/O error and related macros -for MCi_MISC to support IOMCA logging. - -See Intel Software Developers' Manual, version 071, volume 3B, -section "IOMCA". - -Signed-off-by: Qiuxu Zhuo ---- - arch/x86/include/asm/mce.h | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h -index 6c77c03139f7..fcb369287dd2 100644 ---- a/arch/x86/include/asm/mce.h -+++ b/arch/x86/include/asm/mce.h -@@ -81,6 +81,7 @@ - #define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */ - #define MCACOD_DATA 0x0134 /* Data Load */ - #define MCACOD_INSTR 0x0150 /* Instruction Fetch */ -+#define MCACOD_IOERR 0x0e0b /* Generic I/O error */ - - /* MCi_MISC register defines */ - #define MCI_MISC_ADDR_LSB(m) ((m) & 0x3f) -@@ -94,6 +95,11 @@ - /* MCi_ADDR register defines */ - #define MCI_ADDR_PHYSADDR GENMASK_ULL(boot_cpu_data.x86_phys_bits - 1, 0) - -+#define MCI_MISC_PCISEG_MASK GENMASK_ULL(39, 32) -+#define MCI_MISC_PCISEG(m) (((m) & MCI_MISC_PCISEG_MASK) >> 32) -+#define MCI_MISC_PCIRID_MASK GENMASK_ULL(31, 16) -+#define MCI_MISC_PCIRID(m) (((m) & MCI_MISC_PCIRID_MASK) >> 16) -+ - /* CTL2 register defines */ - #define MCI_CTL2_CMCI_EN BIT_ULL(30) - #define MCI_CTL2_CMCI_THRESHOLD_MASK 0x7fffULL --- -2.43.0 - diff --git a/SPECS/kernel/0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac b/SPECS/kernel/0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac deleted file mode 100644 index 9eade3b33..000000000 --- a/SPECS/kernel/0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac +++ /dev/null @@ -1,850 +0,0 @@ -From 244e4b1754eececf2398814b99a7c1392bc46d5a Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Sun, 27 Oct 2019 12:50:01 +0800 -Subject: [PATCH 10/13] EDAC/ieh: Add I/O device EDAC driver for Intel CPUs - with IEH - -Integrated Error Handlers (IEHs) are PCIe devices which aggregate and -report error events of different severities (correctable, non-fatal -uncorrectable, and fatal uncorrectable) from various I/O devices, e.g., -PCIe devices, legacy PCI devices. Each error severity is notified by -one of {SMI, NMI, MCE} which is configured by BIOS/platform firmware. - -The first IEH-supported platform is Intel Tiger Lake-U CPU. The driver -reads/prints the error severity and error source (bus/device/function) -logged in the IEH(s) and restarts the system on fatal I/O device error. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/Kconfig | 10 + - drivers/edac/Makefile | 1 + - drivers/edac/ieh_edac.c | 784 ++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 795 insertions(+) - create mode 100644 drivers/edac/ieh_edac.c - -diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig -index 19ad3c3b675d..ca22dd8bf253 100644 ---- a/drivers/edac/Kconfig -+++ b/drivers/edac/Kconfig -@@ -311,6 +311,16 @@ config EDAC_IGEN6 - This In-Band ECC is first used on the Elkhart Lake SoC but - may appear on others in the future. - -+config EDAC_IEH -+ tristate "Intel Integrated Error Handler" -+ depends on PCI && X86_64 -+ help -+ Support for error detection and correction on the Intel -+ CPU using I/O IEH (Integrated Error Handler). IEHs are PCIe -+ devices which aggregate and report error events of different -+ severities from various I/O devices, e.g., PCIe devices and -+ legacy PCI devices. -+ - config EDAC_MPC85XX - bool "Freescale MPC83xx / MPC85xx" - depends on FSL_SOC && EDAC=y -diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile -index a8f2d8f6c894..9ca5ae143a6b 100644 ---- a/drivers/edac/Makefile -+++ b/drivers/edac/Makefile -@@ -36,6 +36,7 @@ obj-$(CONFIG_EDAC_I7CORE) += i7core_edac.o - obj-$(CONFIG_EDAC_SBRIDGE) += sb_edac.o - obj-$(CONFIG_EDAC_PND2) += pnd2_edac.o - obj-$(CONFIG_EDAC_IGEN6) += igen6_edac.o -+obj-$(CONFIG_EDAC_IEH) += ieh_edac.o - obj-$(CONFIG_EDAC_E7XXX) += e7xxx_edac.o - obj-$(CONFIG_EDAC_E752X) += e752x_edac.o - obj-$(CONFIG_EDAC_I82443BXGX) += i82443bxgx_edac.o -diff --git a/drivers/edac/ieh_edac.c b/drivers/edac/ieh_edac.c -new file mode 100644 -index 000000000000..81bba5aa775e ---- /dev/null -+++ b/drivers/edac/ieh_edac.c -@@ -0,0 +1,784 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * Driver for Intel Integrated Error Handler (IEH) -+ * -+ * Copyright (C) 2020 Intel Corporation -+ * -+ * IEH centralizes and standardizes how I/O device errors are reported. -+ * They are PCIe devices which aggregate and report error events of different -+ * severities (correctable, non-fatal uncorrectable, and fatal uncorrectable) -+ * from various I/O devices, e.g., PCIe devices, legacy PCI devices. -+ * -+ * There is a global IEH and optional north/south satellite IEH(s) logically -+ * connected to global IEH. The global IEH is the root to process all incoming -+ * error messages from satellite IEH(s) and local devices (if some devices -+ * are connected directly to the global IEH) and generate interrupts(SMI/NMI/MCE -+ * configured by BIOS/platform firmware). The first IEH-supported platform is -+ * Tiger Lake-U. This driver reads/prints the error severity and error source -+ * (bus/device/function) logged in the IEH(s) and reboots the system on fatal -+ * IEH errors. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "edac_mc.h" -+ -+#define IEH_REVISION "v1.7" -+ -+#define EDAC_MOD_STR "ieh_edac" -+#define IEH_NMI_NAME "ieh" -+ -+#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo)) -+ -+/* Global correctable error status */ -+#define GCOERRSTS_OFFSET 0x200 -+/* Global non-fatal error status */ -+#define GNFERRSTS_OFFSET 0x210 -+/* Global fatal error status */ -+#define GFAERRSTS_OFFSET 0x220 -+ -+/* Global correctable error mask */ -+#define GCOERRMSK_OFFSET 0x230 -+#define GCOERRMSK 0xffffffff -+/* Global nonfatal error mask */ -+#define GNFERRMSK_OFFSET 0x234 -+#define GNFERRMSK 0xffffffff -+/* Global fatal error mask */ -+#define GFAERRMSK_OFFSET 0x238 -+#define GFAERRMSK 0xffffffff -+ -+/* Global system event status */ -+#define GSYSEVTSTS_OFFSET 0x260 -+ -+/* Global system event mask */ -+#define GSYSEVTMSK_OFFSET 0x264 -+#define GSYSEVTMSK 0x7 -+#define GSYSEVTMSK_CORR BIT(0) -+#define GSYSEVTMSK_NONFATAL BIT(1) -+#define GSYSEVTMSK_FATAL BIT(2) -+ -+/* Global system event map */ -+#define GSYSEVTMAP_OFFSET 0x268 -+#define GSYSEVTMAP_CORR(m) GET_BITFIELD(m, 0, 1) -+#define GSYSEVTMAP_NONFATAL(m) GET_BITFIELD(m, 2, 3) -+#define GSYSEVTMAP_FATAL(m) GET_BITFIELD(m, 4, 5) -+#define GSYSEVTMAP_MCE 0x3f -+ -+/* IEH type and version */ -+#define IEHTYPEVER_OFFSET 0x26c -+#define IEHTYPEVER_TYPE(t) GET_BITFIELD(t, 0, 3) -+#define IEHTYPEVER_VER(t) GET_BITFIELD(t, 4, 7) -+#define IEHTYPEVER_BUS(t) GET_BITFIELD(t, 8, 15) -+ -+/* Bitmap field of satellite IEH */ -+#define BITMAP_OFFSET 0x27c -+#define BITMAP(m) GET_BITFIELD(m, 0, 4) -+ -+/* Local uncorrectable error mask */ -+#define LERRUNCMSK_OFFSET 0x298 -+#define LERRUNCMSK 0xffffffff -+/* Local correctable error mask */ -+#define LERRCORMSK_OFFSET 0x2c0 -+#define LERRCORMSK 0xffffffff -+ -+/* Device number and function number of the device reporting to IEH */ -+#define DEVFUN_OFFSET 0x300 -+#define DEVFUN_FUN(d) GET_BITFIELD(d, 0, 2) -+#define DEVFUN_DEV(d) GET_BITFIELD(d, 3, 7) -+ -+#define ieh_printk(level, fmt, arg...) \ -+ edac_printk(level, "ieh", fmt, ##arg) -+ -+#define PCI_ADDR(sbdf) (sbdf)->seg, (sbdf)->bus, (sbdf)->dev, (sbdf)->fun -+ -+/* Error notification methods */ -+enum evt_map { -+ IEH_IGN, -+ IEH_SMI, -+ IEH_NMI, -+ IEH_MCE, -+}; -+ -+enum severity_level { -+ IEH_CORR_ERR, -+ IEH_NONFATAL_ERR, -+ IEH_FATAL_ERR, -+}; -+ -+enum ieh_type { -+ /* Global IEH */ -+ IEH_GLOBAL, -+ /* North satellite IEH logically connected to global IEH */ -+ IEH_NORTH, -+ /* South satellite IEH logically connected to north IEH */ -+ IEH_SOUTH, -+ /* -+ * Superset south satellite IEH with physical ERR[2:0] signals output. -+ * It's used as a global IEH (when it present, system has only one IEH). -+ */ -+ IEH_SUPERSET, -+}; -+ -+enum action_on_fatal_err { -+ NOP, -+ RESTART, -+ POWER_OFF, -+}; -+ -+struct pci_sbdf { -+ u32 seg : 16; -+ u32 bus : 8; -+ u32 dev : 5; -+ u32 fun : 3; -+}; -+ -+struct ieh_dev { -+ struct list_head list; -+ struct pci_dev *pdev; -+ struct pci_sbdf sbdf; -+ enum ieh_type type; -+ u8 ver; -+ /* Global IEH fields */ -+ enum evt_map corr_map; -+ enum evt_map nonfatal_map; -+ enum evt_map fatal_map; -+}; -+ -+static struct ieh_config { -+ u16 did; -+ enum action_on_fatal_err action; -+} *ieh_cfg; -+ -+struct decoded_res { -+ enum severity_level sev; -+ struct pci_sbdf sbdf; -+}; -+ -+static LIST_HEAD(global_ieh_list); -+static LIST_HEAD(north_ieh_list); -+static LIST_HEAD(south_ieh_list); -+ -+/* Tiger Lake-U SoC */ -+#define IEH_DID_TGL_U 0xa0af -+ -+static struct ieh_config tgl_u_cfg = { -+ .did = IEH_DID_TGL_U, -+ .action = RESTART, -+}; -+ -+static const char * const severities[] = { -+ [IEH_CORR_ERR] = "correctable", -+ [IEH_NONFATAL_ERR] = "non-fatal uncorrectable", -+ [IEH_FATAL_ERR] = "fatal uncorrectable", -+}; -+ -+static struct irq_work ieh_irq_work; -+ -+static int dev_idx(u32 status, int start) -+{ -+ int i; -+ -+ for (i = start; i < 32; i++) { -+ if (status & (1 << i)) -+ return i; -+ } -+ -+ return -1; -+} -+ -+static inline bool has_notification_by(enum evt_map map) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ if (ieh->corr_map == map || ieh->nonfatal_map == map || -+ ieh->fatal_map == map) -+ return true; -+ } -+ -+ return false; -+} -+ -+static void ieh_output_error(struct decoded_res *res) -+{ -+ struct pci_sbdf *p = &res->sbdf; -+ -+ ieh_printk(KERN_ERR, "Device %04x:%02x:%02x.%x - %s error\n", -+ p->seg, p->bus, p->dev, -+ p->fun, severities[res->sev]); -+ -+ if (res->sev != IEH_FATAL_ERR) -+ return; -+ -+ switch (ieh_cfg->action) { -+ case RESTART: -+ ieh_printk(KERN_EMERG, "Restart system on device fatal error!\n"); -+ kernel_restart(NULL); -+ break; -+ -+ case POWER_OFF: -+ ieh_printk(KERN_EMERG, "Power off system on device fatal error!\n"); -+ kernel_power_off(); -+ break; -+ default: -+ break; -+ } -+ -+ /* TODO: Further report error information from the error source */ -+} -+ -+static bool is_same_pdev(struct pci_sbdf *p, struct pci_sbdf *q) -+{ -+ return (p->seg == q->seg && p->bus == q->bus && -+ p->dev == q->dev && p->fun == q->fun); -+} -+ -+static struct ieh_dev *__get_ieh(struct list_head *ieh_list, -+ struct pci_sbdf *sbdf) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, ieh_list, list) { -+ if (is_same_pdev(sbdf, &ieh->sbdf)) -+ return ieh; -+ } -+ -+ return NULL; -+} -+ -+static struct ieh_dev *get_global_ieh(struct pci_sbdf *sbdf) -+{ -+ return __get_ieh(&global_ieh_list, sbdf); -+} -+ -+static inline struct ieh_dev *get_north_sat_ieh(struct pci_sbdf *sbdf) -+{ -+ return __get_ieh(&north_ieh_list, sbdf); -+} -+ -+static inline struct ieh_dev *get_south_sat_ieh(struct pci_sbdf *sbdf) -+{ -+ return __get_ieh(&south_ieh_list, sbdf); -+} -+ -+static int read_and_clear(struct pci_dev *pdev, int offset, u32 *val) -+{ -+ if (pci_read_config_dword(pdev, offset, val)) { -+ ieh_printk(KERN_ERR, "Failed to read 0x%x\n", offset); -+ return -ENODEV; -+ } -+ -+ /* Write 1s to clear status */ -+ if (pci_write_config_dword(pdev, offset, *val)) { -+ ieh_printk(KERN_ERR, "Failed to write 0x%x\n", offset); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+#define UNMASK_ERR_EVENT(ieh, name) \ -+ do { \ -+ u32 val; \ -+ if (pci_read_config_dword(ieh->pdev, name##MSK_OFFSET, &val)) \ -+ return -ENODEV; \ -+ val &= ~name##MSK; \ -+ if (pci_write_config_dword(ieh->pdev, name##MSK_OFFSET, val)) \ -+ return -ENODEV; \ -+ } while (0) -+ -+static int unmask_all_err_events(void) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ UNMASK_ERR_EVENT(ieh, GFAERR); -+ UNMASK_ERR_EVENT(ieh, GNFERR); -+ UNMASK_ERR_EVENT(ieh, GCOERR); -+ UNMASK_ERR_EVENT(ieh, LERRUNC); -+ UNMASK_ERR_EVENT(ieh, LERRCOR); -+ UNMASK_ERR_EVENT(ieh, GSYSEVT); -+ } -+ -+ return 0; -+} -+ -+#define MASK_ERR_EVENT(ieh, name) \ -+ do { \ -+ u32 val; \ -+ if (pci_read_config_dword(ieh->pdev, name##MSK_OFFSET, &val)) \ -+ return -ENODEV; \ -+ val |= name##MSK; \ -+ if (pci_write_config_dword(ieh->pdev, name##MSK_OFFSET, val)) \ -+ return -ENODEV; \ -+ } while (0) -+ -+static int mask_all_err_events(void) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ MASK_ERR_EVENT(ieh, GFAERR); -+ MASK_ERR_EVENT(ieh, GNFERR); -+ MASK_ERR_EVENT(ieh, GCOERR); -+ MASK_ERR_EVENT(ieh, LERRUNC); -+ MASK_ERR_EVENT(ieh, LERRCOR); -+ MASK_ERR_EVENT(ieh, GSYSEVT); -+ } -+ -+ return 0; -+} -+ -+static int ieh_handle_error(struct ieh_dev *d, enum severity_level sev) -+{ -+ struct decoded_res res; -+ struct pci_sbdf *sbdf = &res.sbdf; -+ struct ieh_dev *ieh; -+ int i, start = 0; -+ u32 sts, reg; -+ -+ switch (sev) { -+ case IEH_CORR_ERR: -+ if (read_and_clear(d->pdev, GCOERRSTS_OFFSET, &sts)) -+ return -ENODEV; -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GCOERRSTS: 0x%x\n", -+ PCI_ADDR(&d->sbdf), sts); -+ break; -+ case IEH_NONFATAL_ERR: -+ if (read_and_clear(d->pdev, GNFERRSTS_OFFSET, &sts)) -+ return -ENODEV; -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GNFERRSTS: 0x%x\n", -+ PCI_ADDR(&d->sbdf), sts); -+ break; -+ case IEH_FATAL_ERR: -+ if (read_and_clear(d->pdev, GFAERRSTS_OFFSET, &sts)) -+ return -ENODEV; -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GFAERRSTS: 0x%x\n", -+ PCI_ADDR(&d->sbdf), sts); -+ break; -+ } -+ -+ while ((i = dev_idx(sts, start)) != -1) { -+ if (pci_read_config_dword(d->pdev, DEVFUN_OFFSET + i * 4, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read DEVFUN %d\n", i); -+ return -ENODEV; -+ } -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x DEVFUN %d: 0x%x\n", -+ PCI_ADDR(&d->sbdf), i, reg); -+ -+ memset(&res, 0, sizeof(res)); -+ res.sev = sev; -+ sbdf->seg = d->sbdf.seg; -+ sbdf->bus = d->sbdf.bus; -+ sbdf->dev = DEVFUN_DEV(reg); -+ sbdf->fun = DEVFUN_FUN(reg); -+ -+ switch (d->type) { -+ case IEH_GLOBAL: -+ ieh = get_north_sat_ieh(sbdf); -+ if (!ieh) -+ ieh_output_error(&res); -+ else if (ieh->type == IEH_NORTH) -+ ieh_handle_error(ieh, sev); -+ else -+ ieh_printk(KERN_ERR, "Invalid global IEH\n"); -+ break; -+ case IEH_NORTH: -+ ieh = get_south_sat_ieh(sbdf); -+ if (!ieh) -+ ieh_output_error(&res); -+ else if (ieh->type == IEH_SOUTH) -+ ieh_handle_error(ieh, sev); -+ else -+ ieh_printk(KERN_ERR, "Invalid north IEH\n"); -+ break; -+ case IEH_SOUTH: -+ case IEH_SUPERSET: -+ ieh_output_error(&res); -+ break; -+ } -+ -+ start = i + 1; -+ } -+ -+ return 0; -+} -+ -+static void __ieh_check_error(struct ieh_dev *ieh) -+{ -+ struct pci_dev *pdev = ieh->pdev; -+ u32 sts; -+ -+ if (pci_read_config_dword(pdev, GSYSEVTSTS_OFFSET, &sts)) { -+ ieh_printk(KERN_ERR, "Failed to read GSYSEVTSTS\n"); -+ return; -+ } -+ -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GSYSEVTSTS: 0x%x\n", -+ PCI_ADDR(&ieh->sbdf), sts); -+ -+ if ((sts & (1 << IEH_FATAL_ERR)) && ieh->fatal_map == IEH_NMI) -+ ieh_handle_error(ieh, IEH_FATAL_ERR); -+ -+ if ((sts & (1 << IEH_NONFATAL_ERR)) && ieh->nonfatal_map == IEH_NMI) -+ ieh_handle_error(ieh, IEH_NONFATAL_ERR); -+ -+ if ((sts & (1 << IEH_CORR_ERR)) && ieh->corr_map == IEH_NMI) -+ ieh_handle_error(ieh, IEH_CORR_ERR); -+} -+ -+static void ieh_check_error(void) -+{ -+ struct ieh_dev *ieh; -+ -+ list_for_each_entry(ieh, &global_ieh_list, list) { -+ __ieh_check_error(ieh); -+ } -+} -+ -+static void ieh_irq_work_cb(struct irq_work *irq_work) -+{ -+ ieh_check_error(); -+} -+ -+static int ieh_nmi_handler(unsigned int cmd, struct pt_regs *regs) -+{ -+ irq_work_queue(&ieh_irq_work); -+ return 0; -+} -+ -+static int mce_check_error(struct notifier_block *nb, unsigned long val, -+ void *data) -+{ -+ struct mce *mce = (struct mce *)data; -+ struct decoded_res res; -+ struct pci_sbdf *sbdf = &res.sbdf; -+ struct ieh_dev *ieh; -+ u64 rid; -+ -+ /* TODO: For debug only. Remove them later. */ -+ ieh_printk(KERN_DEBUG, "MCi_STATUS 0x%llx\n", mce->status); -+ ieh_printk(KERN_DEBUG, "MCi_MISC 0x%llx\n", mce->misc); -+ ieh_printk(KERN_DEBUG, "MCi_ADDR 0x%llx\n", mce->addr); -+ ieh_printk(KERN_DEBUG, "MCGSTATUS 0x%llx\n", mce->mcgstatus); -+ ieh_printk(KERN_DEBUG, "MCGSCAP 0x%llx\n", mce->mcgcap); -+ ieh_printk(KERN_DEBUG, "IP 0x%llx\n", mce->ip); -+ ieh_printk(KERN_DEBUG, "MC bank 0x%x\n", mce->bank); -+ -+ if ((mce->status & MCACOD) != MCACOD_IOERR) -+ return NOTIFY_DONE; -+ -+ if (!(mce->status & MCI_STATUS_MISCV)) -+ return NOTIFY_DONE; -+ -+ memset(&res, 0, sizeof(res)); -+ rid = MCI_MISC_PCIRID(mce->misc); -+ sbdf->seg = MCI_MISC_PCISEG(mce->misc); -+ sbdf->bus = GET_BITFIELD(rid, 8, 15); -+ sbdf->dev = GET_BITFIELD(rid, 3, 7); -+ sbdf->fun = GET_BITFIELD(rid, 0, 2); -+ -+ if (mce->status & MCI_STATUS_PCC) -+ res.sev = IEH_FATAL_ERR; -+ else if (mce->status & MCI_STATUS_UC) -+ res.sev = IEH_NONFATAL_ERR; -+ else -+ res.sev = IEH_CORR_ERR; -+ -+ ieh = get_global_ieh(sbdf); -+ if (ieh) -+ goto handle; -+ -+ ieh = get_north_sat_ieh(sbdf); -+ if (ieh) -+ goto handle; -+ -+ ieh = get_south_sat_ieh(sbdf); -+ if (ieh) -+ goto handle; -+ -+ goto output; -+ -+handle: -+ ieh_handle_error(ieh, res.sev); -+ mce->kflags |= MCE_HANDLED_EDAC; -+ return NOTIFY_DONE; -+ -+output: -+ ieh_output_error(&res); -+ return NOTIFY_DONE; -+} -+ -+static struct notifier_block ieh_mce_dec = { -+ .notifier_call = mce_check_error, -+ .priority = MCE_PRIO_EDAC, -+}; -+ -+static const struct x86_cpu_id ieh_cpuids[] = { -+ X86_MATCH_VFM(INTEL_TIGERLAKE_L, &tgl_u_cfg), -+ {} -+}; -+MODULE_DEVICE_TABLE(x86cpu, ieh_cpuids); -+ -+static void __put_ieh(struct ieh_dev *ieh) -+{ -+ if (!ieh) -+ return; -+ if (ieh->pdev) { -+ pci_disable_device(ieh->pdev); -+ pci_dev_put(ieh->pdev); -+ } -+ kfree(ieh); -+} -+ -+static void __put_iehs(struct list_head *ieh_list) -+{ -+ struct ieh_dev *ieh, *tmp; -+ -+ edac_dbg(0, "\n"); -+ -+ list_for_each_entry_safe(ieh, tmp, ieh_list, list) { -+ list_del(&ieh->list); -+ __put_ieh(ieh); -+ } -+} -+ -+static void put_all_iehs(void) -+{ -+ __put_iehs(&global_ieh_list); -+ __put_iehs(&north_ieh_list); -+ __put_iehs(&south_ieh_list); -+} -+ -+static int __get_all_iehs(u16 did) -+{ -+ struct pci_dev *pdev, *prev = NULL; -+ int rc = -ENODEV, n = 0; -+ struct pci_sbdf *sbdf; -+ struct ieh_dev *ieh; -+ u32 reg; -+ -+ edac_dbg(0, "\n"); -+ -+ for (;;) { -+ pdev = pci_get_device(PCI_VENDOR_ID_INTEL, did, prev); -+ if (!pdev) -+ break; -+ -+ if (pci_enable_device(pdev)) { -+ ieh_printk(KERN_ERR, "Failed to enable %04x:%04x\n", -+ pdev->vendor, pdev->device); -+ goto fail; -+ } -+ -+ ieh = kzalloc(sizeof(*ieh), GFP_KERNEL); -+ if (!ieh) { -+ rc = -ENOMEM; -+ goto fail2; -+ } -+ -+ if (pci_read_config_dword(pdev, IEHTYPEVER_OFFSET, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read IEHTYPEVER\n"); -+ return -ENODEV; -+ } -+ -+ ieh->pdev = pdev; -+ ieh->ver = IEHTYPEVER_VER(reg); -+ ieh->type = IEHTYPEVER_TYPE(reg); -+ sbdf = &ieh->sbdf; -+ sbdf->seg = pci_domain_nr(pdev->bus); -+ sbdf->bus = IEHTYPEVER_BUS(reg); -+ sbdf->dev = PCI_SLOT(pdev->devfn); -+ sbdf->fun = PCI_FUNC(pdev->devfn); -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x IEHTYPEVER: 0x%x\n", -+ PCI_ADDR(sbdf), reg); -+ -+ if (sbdf->bus != pdev->bus->number) { -+ ieh_printk(KERN_ERR, "Mismatched IEH bus\n"); -+ rc = -EINVAL; -+ goto fail3; -+ } -+ -+ switch (ieh->type) { -+ case IEH_SUPERSET: -+ case IEH_GLOBAL: -+ /* Set notification to MCE */ -+ if (pci_read_config_dword(pdev, GSYSEVTMAP_OFFSET, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read old GSYSEVTMAP\n"); -+ return -ENODEV; -+ } -+ -+ reg |= GSYSEVTMAP_MCE; -+ if (pci_write_config_dword(pdev, GSYSEVTMAP_OFFSET, reg)) { -+ ieh_printk(KERN_ERR, "Failed to write GSYSEVTMAP\n"); -+ return -ENODEV; -+ } -+ -+ if (pci_read_config_dword(pdev, GSYSEVTMAP_OFFSET, ®)) { -+ ieh_printk(KERN_ERR, "Failed to read new GSYSEVTMAP\n"); -+ return -ENODEV; -+ } -+ ieh_printk(KERN_DEBUG, "Read %04x:%02x:%02x.%x GSYSEVTMAP: 0x%x\n", -+ PCI_ADDR(sbdf), reg); -+ -+ ieh->corr_map = GSYSEVTMAP_CORR(reg); -+ ieh->nonfatal_map = GSYSEVTMAP_NONFATAL(reg); -+ ieh->fatal_map = GSYSEVTMAP_FATAL(reg); -+ list_add_tail(&ieh->list, &global_ieh_list); -+ ieh_printk(KERN_DEBUG, "Global/Superset IEH %04x:%02x:%02x.%x\n", -+ PCI_ADDR(sbdf)); -+ break; -+ case IEH_NORTH: -+ list_add_tail(&ieh->list, &north_ieh_list); -+ ieh_printk(KERN_DEBUG, "North IEH %04x:%02x:%02x.%x\n", -+ PCI_ADDR(sbdf)); -+ break; -+ case IEH_SOUTH: -+ list_add_tail(&ieh->list, &south_ieh_list); -+ ieh_printk(KERN_DEBUG, "South IEH %04x:%02x:%02x.%x\n", -+ PCI_ADDR(sbdf)); -+ break; -+ } -+ -+ pci_dev_get(pdev); -+ prev = pdev; -+ n++; -+ } -+ -+ return n; -+fail3: -+ kfree(ieh); -+fail2: -+ pci_disable_device(pdev); -+fail: -+ pci_dev_put(pdev); -+ put_all_iehs(); -+ return rc; -+} -+ -+static int get_all_iehs(u16 did) -+{ -+ int rc; -+ -+ rc = __get_all_iehs(did); -+ if (rc < 0) -+ return rc; -+ -+ if (rc == 0) { -+ ieh_printk(KERN_DEBUG, "No IEHs found\n"); -+ return -ENODEV; -+ } -+ -+ if (list_empty(&global_ieh_list)) { -+ ieh_printk(KERN_ERR, "No global IEH found\n"); -+ put_all_iehs(); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static int register_err_handler(void) -+{ -+ bool os_visible = false; -+ int rc; -+ -+ if (has_notification_by(IEH_NMI)) { -+ init_irq_work(&ieh_irq_work, ieh_irq_work_cb); -+ rc = register_nmi_handler(NMI_SERR, ieh_nmi_handler, -+ 0, IEH_NMI_NAME); -+ if (rc) { -+ ieh_printk(KERN_ERR, "Can't register NMI handler\n"); -+ return rc; -+ } -+ -+ os_visible = true; -+ } -+ -+ if (has_notification_by(IEH_MCE)) { -+ mce_register_decode_chain(&ieh_mce_dec); -+ os_visible = true; -+ } -+ -+ if (!os_visible) { -+ ieh_printk(KERN_INFO, "No OS-visible IEH events\n"); -+ return -ENODEV; -+ } -+ -+ return 0; -+} -+ -+static void unregister_err_handler(void) -+{ -+ if (has_notification_by(IEH_NMI)) { -+ unregister_nmi_handler(NMI_SERR, IEH_NMI_NAME); -+ irq_work_sync(&ieh_irq_work); -+ } -+ -+ if (has_notification_by(IEH_MCE)) -+ mce_unregister_decode_chain(&ieh_mce_dec); -+} -+ -+static int __init ieh_init(void) -+{ -+ const struct x86_cpu_id *id; -+ struct ieh_dev *ieh; -+ int rc; -+ -+ edac_dbg(2, "\n"); -+ -+ id = x86_match_cpu(ieh_cpuids); -+ if (!id) -+ return -ENODEV; -+ ieh_cfg = (struct ieh_config *)id->driver_data; -+ -+ rc = get_all_iehs(ieh_cfg->did); -+ if (rc) -+ return rc; -+ -+ rc = register_err_handler(); -+ if (rc) -+ goto fail; -+ -+ rc = unmask_all_err_events(); -+ if (rc) -+ goto fail2; -+ -+ ieh = list_first_entry(&global_ieh_list, struct ieh_dev, list); -+ ieh_printk(KERN_INFO, "hw v%d, drv %s\n", ieh->ver, IEH_REVISION); -+ -+ return 0; -+fail2: -+ unregister_err_handler(); -+fail: -+ put_all_iehs(); -+ return rc; -+} -+ -+static void __exit ieh_exit(void) -+{ -+ edac_dbg(2, "\n"); -+ mask_all_err_events(); -+ unregister_err_handler(); -+ put_all_iehs(); -+} -+ -+module_init(ieh_init); -+module_exit(ieh_exit); -+ -+MODULE_LICENSE("GPL v2"); -+MODULE_AUTHOR("Qiuxu Zhuo"); -+MODULE_DESCRIPTION("IEH Driver for Intel CPU using I/O IEH"); --- -2.43.0 - diff --git a/SPECS/kernel/0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi b/SPECS/kernel/0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi new file mode 100644 index 000000000..3d920ecda --- /dev/null +++ b/SPECS/kernel/0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi @@ -0,0 +1,297 @@ +From 72fdaf6f276eef1443e29b45b03a78b1fe48bce8 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 13:22:02 -0700 +Subject: [PATCH 10/44] KVM: VMX: Add support for saving and restoring FRED + MSRs + +Introduce support for handling FRED MSR access requests, enabling both +host and guest to read and write FRED MSRs, which is essential for VM +save/restore and live migration, and allows userspace tools such as QEMU +to access the relevant MSRs. + +Specially, intercept accesses to the FRED SSP0 MSR (IA32_PL0_SSP), which +remains accessible when FRED is enumerated even if CET is not. This +ensures the guest value is fully virtual and does not alter the hardware +FRED SSP0 MSR. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v7: +* Intercept accesses to FRED SSP0, i.e., IA32_PL0_SSP, which remains + accessible when FRED but !CET (Sean). + +Change in v6: +* Return KVM_MSR_RET_UNSUPPORTED instead of 1 when FRED is not available + (Chao Gao) +* Handle MSR_IA32_PL0_SSP when FRED is enumerated but CET not. + +Change in v5: +* Use the newly added guest MSR read/write helpers (Sean). +* Check the size of fred_msr_vmcs_fields[] using static_assert() (Sean). +* Rewrite setting FRED MSRs to make it much easier to read (Sean). +* Add TB from Xuelian Guo. + +Changes since v2: +* Add a helper to convert FRED MSR index to VMCS field encoding to + make the code more compact (Chao Gao). +* Get rid of the "host_initiated" check because userspace has to set + CPUID before MSRs (Chao Gao & Sean Christopherson). +* Address a few cleanup comments (Sean Christopherson). + +Changes since v1: +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). +* Fail host requested FRED MSRs access if KVM cannot virtualize FRED + (Chao Gao). +* Handle the case FRED MSRs are valid but KVM cannot virtualize FRED + (Chao Gao). +* Add sanity checks when writing to FRED MSRs. +--- + arch/x86/include/asm/kvm_host.h | 5 ++ + arch/x86/kvm/vmx/vmx.c | 45 +++++++++++++++++ + arch/x86/kvm/x86.c | 85 +++++++++++++++++++++++++++++++-- + 3 files changed, 132 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 48598d017d6f3..43a18e265289b 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -1092,6 +1092,11 @@ struct kvm_vcpu_arch { + #if IS_ENABLED(CONFIG_HYPERV) + hpa_t hv_root_tdp; + #endif ++ /* ++ * Stores the FRED SSP0 MSR when CET is not supported, prompting KVM ++ * to intercept its accesses. ++ */ ++ u64 fred_ssp0_fallback; + }; + + struct kvm_lpage_info { +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 3ac89ccf47210..b51f2c4fcdb8c 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1386,6 +1386,18 @@ static void vmx_write_guest_kernel_gs_base(struct vcpu_vmx *vmx, u64 data) + vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data, + &vmx->msr_guest_kernel_gs_base); + } ++ ++static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) ++{ ++ return vmx_read_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, ++ &vmx->msr_guest_fred_rsp0); ++} ++ ++static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) ++{ ++ vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, ++ &vmx->msr_guest_fred_rsp0); ++} + #endif + + static void grow_ple_window(struct kvm_vcpu *vcpu) +@@ -1987,6 +1999,27 @@ int vmx_get_feature_msr(u32 msr, u64 *data) + } + } + ++#ifdef CONFIG_X86_64 ++static const u32 fred_msr_vmcs_fields[] = { ++ GUEST_IA32_FRED_RSP1, ++ GUEST_IA32_FRED_RSP2, ++ GUEST_IA32_FRED_RSP3, ++ GUEST_IA32_FRED_STKLVLS, ++ GUEST_IA32_FRED_SSP1, ++ GUEST_IA32_FRED_SSP2, ++ GUEST_IA32_FRED_SSP3, ++ GUEST_IA32_FRED_CONFIG, ++}; ++ ++static_assert(MSR_IA32_FRED_CONFIG - MSR_IA32_FRED_RSP1 == ++ ARRAY_SIZE(fred_msr_vmcs_fields) - 1); ++ ++static u32 fred_msr_to_vmcs(u32 msr) ++{ ++ return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1]; ++} ++#endif ++ + /* + * Reads an msr value (of 'msr_info->index') into 'msr_info->data'. + * Returns 0 on success, non-0 otherwise. +@@ -2009,6 +2042,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + case MSR_KERNEL_GS_BASE: + msr_info->data = vmx_read_guest_kernel_gs_base(vmx); + break; ++ case MSR_IA32_FRED_RSP0: ++ msr_info->data = vmx_read_guest_fred_rsp0(vmx); ++ break; ++ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: ++ msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index)); ++ break; + #endif + case MSR_EFER: + return kvm_get_msr_common(vcpu, msr_info); +@@ -2241,6 +2280,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + vmx_update_exception_bitmap(vcpu); + } + break; ++ case MSR_IA32_FRED_RSP0: ++ vmx_write_guest_fred_rsp0(vmx, data); ++ break; ++ case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG: ++ vmcs_write64(fred_msr_to_vmcs(msr_index), data); ++ break; + #endif + case MSR_IA32_SYSENTER_CS: + if (is_guest_mode(vcpu)) +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index c9c2aa6f4705e..af7543e7c8063 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -331,6 +331,9 @@ static const u32 msrs_to_save_base[] = { + MSR_STAR, + #ifdef CONFIG_X86_64 + MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR, ++ MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2, ++ MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1, ++ MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG, + #endif + MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA, + MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX, +@@ -1919,7 +1922,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, + * architecture. Intercepting XRSTORS/XSAVES for this + * special case isn't deemed worthwhile. + */ +- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: ++ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) + return KVM_MSR_RET_UNSUPPORTED; + /* +@@ -1934,6 +1937,52 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, + if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4)) + return 1; + break; ++ case MSR_IA32_FRED_STKLVLS: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: ++ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_CONFIG: { ++ u64 reserved_bits = 0; ++ ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ ++ if (is_noncanonical_msr_address(data, vcpu)) ++ return 1; ++ ++ switch (index) { ++ case MSR_IA32_FRED_CONFIG: ++ reserved_bits = BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2); ++ break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3: ++ reserved_bits = GENMASK_ULL(5, 0); ++ break; ++ case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_SSP3: ++ reserved_bits = GENMASK_ULL(2, 0); ++ break; ++ default: ++ WARN_ON_ONCE(1); ++ return 1; ++ } ++ ++ if (data & reserved_bits) ++ return 1; ++ ++ break; ++ } ++ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && ++ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ ++ if (is_noncanonical_msr_address(data, vcpu)) ++ return 1; ++ ++ if (!IS_ALIGNED(data, 4)) ++ return 1; ++ ++ break; + } + + msr.data = data; +@@ -1988,10 +2037,19 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, + if (!host_initiated) + return 1; + fallthrough; +- case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: ++ case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB: + if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) + return KVM_MSR_RET_UNSUPPORTED; + break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ break; ++ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && ++ !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) ++ return KVM_MSR_RET_UNSUPPORTED; ++ break; + } + + msr.index = index; +@@ -4315,6 +4373,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + #endif + case MSR_IA32_U_CET: + case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { ++ WARN_ON_ONCE(msr != MSR_IA32_FRED_SSP0); ++ vcpu->arch.fred_ssp0_fallback = data; ++ break; ++ } ++ + kvm_set_xstate_msr(vcpu, msr_info); + break; + default: +@@ -4668,6 +4732,12 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) + #endif + case MSR_IA32_U_CET: + case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: ++ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { ++ WARN_ON_ONCE(msr_info->index != MSR_IA32_FRED_SSP0); ++ msr_info->data = vcpu->arch.fred_ssp0_fallback; ++ break; ++ } ++ + kvm_get_xstate_msr(vcpu, msr_info); + break; + default: +@@ -7711,10 +7781,19 @@ static void kvm_probe_msr_to_save(u32 msr_index) + if (!kvm_cpu_cap_has(X86_FEATURE_LM)) + return; + fallthrough; +- case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: ++ case MSR_IA32_PL1_SSP ... MSR_IA32_PL3_SSP: + if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK)) + return; + break; ++ case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG: ++ if (!kvm_cpu_cap_has(X86_FEATURE_FRED)) ++ return; ++ break; ++ case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */ ++ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && ++ !kvm_cpu_cap_has(X86_FEATURE_FRED)) ++ return; ++ break; + default: + break; + } +-- +2.43.0 + diff --git a/SPECS/kernel/0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi b/SPECS/kernel/0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi deleted file mode 100644 index 63e132654..000000000 --- a/SPECS/kernel/0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi +++ /dev/null @@ -1,49 +0,0 @@ -From 4d54ce3cf01e889088b73b3c67f75ce5161061f9 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Wed, 7 Feb 2024 09:26:31 -0800 -Subject: [PATCH 10/44] KVM: x86: Add a helper to detect if FRED is enabled for - a vCPU - -Signed-off-by: Xin Li -[ Sean: removed the "kvm_" prefix from the function name ] -Signed-off-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/kvm_cache_regs.h | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - -diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h -index 36a8786db291..31b446b6cbd7 100644 ---- a/arch/x86/kvm/kvm_cache_regs.h -+++ b/arch/x86/kvm/kvm_cache_regs.h -@@ -204,6 +204,21 @@ static __always_inline bool kvm_is_cr4_bit_set(struct kvm_vcpu *vcpu, - return !!kvm_read_cr4_bits(vcpu, cr4_bit); - } - -+/* -+ * It's enough to check just CR4.FRED (X86_CR4_FRED) to tell if -+ * a vCPU is running with FRED enabled, because: -+ * 1) CR4.FRED can be set to 1 only _after_ IA32_EFER.LMA = 1. -+ * 2) To leave IA-32e mode, CR4.FRED must be cleared first. -+ */ -+static inline bool is_fred_enabled(struct kvm_vcpu *vcpu) -+{ -+#ifdef CONFIG_X86_64 -+ return kvm_is_cr4_bit_set(vcpu, X86_CR4_FRED); -+#else -+ return false; -+#endif -+} -+ - static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu) - { - if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) --- -2.43.0 - diff --git a/SPECS/kernel/0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet b/SPECS/kernel/0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet deleted file mode 100644 index 74615669d..000000000 --- a/SPECS/kernel/0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet +++ /dev/null @@ -1,139 +0,0 @@ -From 72c45e1f7d755c5b002da53053179f8931a5a599 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 4 Jul 2025 01:49:40 -0700 -Subject: [PATCH 10/24] KVM: x86: Load guest FPU state when access - XSAVE-managed MSRs - -Load the guest's FPU state if userspace is accessing MSRs whose values -are managed by XSAVES. Introduce two helpers, kvm_{get,set}_xstate_msr(), -to facilitate access to such kind of MSRs. - -If MSRs supported in kvm_caps.supported_xss are passed through to guest, -the guest MSRs are swapped with host's before vCPU exits to userspace and -after it reenters kernel before next VM-entry. - -Because the modified code is also used for the KVM_GET_MSRS device ioctl(), -explicitly check @vcpu is non-null before attempting to load guest state. -The XSAVE-managed MSRs cannot be retrieved via the device ioctl() without -loading guest FPU state (which doesn't exist). - -Note that guest_cpuid_has() is not queried as host userspace is allowed to -access MSRs that have not been exposed to the guest, e.g. it might do -KVM_SET_MSRS prior to KVM_SET_CPUID2. - -The two helpers are put here in order to manifest accessing xsave-managed -MSRs requires special check and handling to guarantee the correctness of -read/write to the MSRs. - -Signed-off-by: Sean Christopherson -Co-developed-by: Yang Weijiang -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 35 ++++++++++++++++++++++++++++++++++- - arch/x86/kvm/x86.h | 24 ++++++++++++++++++++++++ - 2 files changed, 58 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 46b45eeadf47..0b0468348ee0 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -136,6 +136,9 @@ static int __set_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2); - static void __get_sregs2(struct kvm_vcpu *vcpu, struct kvm_sregs2 *sregs2); - - static DEFINE_MUTEX(vendor_module_lock); -+static void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); -+static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu); -+ - struct kvm_x86_ops kvm_x86_ops __read_mostly; - - #define KVM_X86_OP(func) \ -@@ -4531,6 +4534,21 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - } - EXPORT_SYMBOL_GPL(kvm_get_msr_common); - -+/* -+ * Returns true if the MSR in question is managed via XSTATE, i.e. is context -+ * switched with the rest of guest FPU state. -+ */ -+static bool is_xstate_managed_msr(u32 index) -+{ -+ switch (index) { -+ case MSR_IA32_U_CET: -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ return true; -+ default: -+ return false; -+ } -+} -+ - /* - * Read or write a bunch of msrs. All parameters are kernel addresses. - * -@@ -4541,11 +4559,26 @@ static int __msr_io(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs, - int (*do_msr)(struct kvm_vcpu *vcpu, - unsigned index, u64 *data)) - { -+ bool fpu_loaded = false; - int i; - -- for (i = 0; i < msrs->nmsrs; ++i) -+ for (i = 0; i < msrs->nmsrs; ++i) { -+ /* -+ * If userspace is accessing one or more XSTATE-managed MSRs, -+ * temporarily load the guest's FPU state so that the guest's -+ * MSR value(s) is resident in hardware, i.e. so that KVM can -+ * get/set the MSR via RDMSR/WRMSR. -+ */ -+ if (vcpu && !fpu_loaded && kvm_caps.supported_xss && -+ is_xstate_managed_msr(entries[i].index)) { -+ kvm_load_guest_fpu(vcpu); -+ fpu_loaded = true; -+ } - if (do_msr(vcpu, entries[i].index, &entries[i].data)) - break; -+ } -+ if (fpu_loaded) -+ kvm_put_guest_fpu(vcpu); - - return i; - } -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index bd1149768acc..97e258a3e88e 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -701,4 +701,28 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, - - int kvm_emulate_hypercall(struct kvm_vcpu *vcpu); - -+/* -+ * Lock and/or reload guest FPU and access xstate MSRs. For accesses initiated -+ * by host, guest FPU is loaded in __msr_io(). For accesses initiated by guest, -+ * guest FPU should have been loaded already. -+ */ -+ -+static inline void kvm_get_xstate_msr(struct kvm_vcpu *vcpu, -+ struct msr_data *msr_info) -+{ -+ KVM_BUG_ON(!vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm); -+ kvm_fpu_get(); -+ rdmsrl(msr_info->index, msr_info->data); -+ kvm_fpu_put(); -+} -+ -+static inline void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, -+ struct msr_data *msr_info) -+{ -+ KVM_BUG_ON(!vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm); -+ kvm_fpu_get(); -+ wrmsrl(msr_info->index, msr_info->data); -+ kvm_fpu_put(); -+} -+ - #endif --- -2.43.0 - diff --git a/SPECS/kernel/0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl b/SPECS/kernel/0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl new file mode 100644 index 000000000..cd7678e2c --- /dev/null +++ b/SPECS/kernel/0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl @@ -0,0 +1,129 @@ +From 387b4220e1ad94cbc517a4180af34b3ac1b023a0 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Thu, 13 Nov 2025 17:56:27 +0100 +Subject: [PATCH 10/17] cpuidle: governors: teo: Rework the handling of tick + wakeups + +If the wakeup pattern is clearly dominated by tick wakeups, count those +wakeups as hits on the deepest available idle state to increase the +likelihood of stopping the tick, especially on systems where there are +only 2 usable idle states and the tick can only be stopped when the +deeper state is selected. + +This change is expected to reduce power on some systems where state 0 is +selected relatively often even though they are almost idle. Without it, +the governor may end up selecting the shallowest idle state all the time +even if the system is almost completely idle due all tick wakeups being +counted as hits on that state and preventing the tick from being stopped +at all. + +Fixes: 4b20b07ce72f ("cpuidle: teo: Don't count non-existent intercepts") +Reported-by: Reka Norman +Closes: https://lore.kernel.org/linux-pm/CAEmPcwsNMNnNXuxgvHTQ93Mx-q3Oz9U57THQsU_qdcCx1m4w5g@mail.gmail.com/ +Tested-by: Reka Norman +Tested-by: Christian Loehle +Cc: 6.11+ # 6.11+: 92ce5c07b7a1: cpuidle: teo: Reorder candidate state index checks +Cc: 6.11+ # 6.11+: ea185406d1ed: cpuidle: teo: Combine candidate state index checks against 0 +Cc: 6.11+ # 6.11+: b9a6af26bd83: cpuidle: teo: Drop local variable prev_intercept_idx +Cc: 6.11+ # 6.11+: e24f8a55de50: cpuidle: teo: Clarify two code comments +Cc: 6.11+ # 6.11+: d619b5cc6780: cpuidle: teo: Simplify counting events used for tick management +Cc: 6.11+ # 6.11+: 13ed5c4a6d9c: cpuidle: teo: Skip getting the sleep length if wakeups are very frequent +Cc: 6.11+ # 6.11+: ddcfa7964677: cpuidle: teo: Simplify handling of total events count +Cc: 6.11+ # 6.11+: 65e18e654475: cpuidle: teo: Replace time_span_ns with a flag +Cc: 6.11+ # 6.11+: 0796ddf4a7f0: cpuidle: teo: Use this_cpu_ptr() where possible +Cc: 6.11+ # 6.11+: 8f3f01082d7a: cpuidle: governors: teo: Use s64 consistently in teo_update() +Cc: 6.11+ # 6.11+: b54df61c7428: cpuidle: governors: teo: Decay metrics below DECAY_SHIFT threshold +Cc: 6.11+ # 6.11+ +Signed-off-by: Rafael J. Wysocki +[ rjw: Rebase on commit 0796ddf4a7f0, changelog update ] +Link: https://patch.msgid.link/6228387.lOV4Wx5bFT@rafael.j.wysocki +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/governors/teo.c | 39 ++++++++++++++++++++------------- + 1 file changed, 24 insertions(+), 15 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 8b80d73e518ed..94ba00b7617d7 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -133,17 +133,19 @@ struct teo_bin { + * @sleep_length_ns: Time till the closest timer event (at the selection time). + * @state_bins: Idle state data bins for this CPU. + * @total: Grand total of the "intercepts" and "hits" metrics for all bins. ++ * @total_tick: Wakeups by the scheduler tick. + * @tick_intercepts: "Intercepts" before TICK_NSEC. + * @short_idles: Wakeups after short idle periods. +- * @artificial_wakeup: Set if the wakeup has been triggered by a safety net. ++ * @tick_wakeup: Set if the last wakeup was by the scheduler tick. + */ + struct teo_cpu { + s64 sleep_length_ns; + struct teo_bin state_bins[CPUIDLE_STATE_MAX]; + unsigned int total; ++ unsigned int total_tick; + unsigned int tick_intercepts; + unsigned int short_idles; +- bool artificial_wakeup; ++ bool tick_wakeup; + }; + + static DEFINE_PER_CPU(struct teo_cpu, teo_cpus); +@@ -172,9 +174,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + + teo_decay(&cpu_data->short_idles); + +- if (cpu_data->artificial_wakeup) { ++ if (dev->poll_time_limit) { ++ dev->poll_time_limit = false; + /* +- * If one of the safety nets has triggered, assume that this ++ * Polling state timeout has triggered, so assume that this + * might have been a long sleep. + */ + measured_ns = S64_MAX; +@@ -223,6 +226,21 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + cpu_data->total = total + PULSE; + + teo_decay(&cpu_data->tick_intercepts); ++ ++ teo_decay(&cpu_data->total_tick); ++ if (cpu_data->tick_wakeup) { ++ cpu_data->total_tick += PULSE; ++ /* ++ * If tick wakeups dominate the wakeup pattern, count this one ++ * as a hit on the deepest available idle state to increase the ++ * likelihood of stopping the tick. ++ */ ++ if (3 * cpu_data->total_tick > 2 * cpu_data->total) { ++ cpu_data->state_bins[drv->state_count-1].hits += PULSE; ++ return; ++ } ++ } ++ + /* + * If the measured idle duration falls into the same bin as the sleep + * length, this is a "hit", so update the "hits" metric for that bin. +@@ -512,18 +530,9 @@ static void teo_reflect(struct cpuidle_device *dev, int state) + { + struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus); + ++ cpu_data->tick_wakeup = tick_nohz_idle_got_tick(); ++ + dev->last_state_idx = state; +- if (dev->poll_time_limit || +- (tick_nohz_idle_got_tick() && cpu_data->sleep_length_ns > TICK_NSEC)) { +- /* +- * The wakeup was not "genuine", but triggered by one of the +- * safety nets. +- */ +- dev->poll_time_limit = false; +- cpu_data->artificial_wakeup = true; +- } else { +- cpu_data->artificial_wakeup = false; +- } + } + + /** +-- +2.43.0 + diff --git a/SPECS/kernel/0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu b/SPECS/kernel/0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu deleted file mode 100644 index 97f0c6255..000000000 --- a/SPECS/kernel/0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu +++ /dev/null @@ -1,27 +0,0 @@ -From 02c24ef6b67f79e73b6db09af3e253738869b273 Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Wed, 27 Aug 2025 11:10:06 +0800 -Subject: [PATCH 10/11] drivers: media: set v4l2_subdev_enable_streams_api=true - for WA - -Signed-off-by: hepengpx ---- - drivers/media/v4l2-core/v4l2-subdev.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c -index 4fd25fea3b58..60a05561f305 100644 ---- a/drivers/media/v4l2-core/v4l2-subdev.c -+++ b/drivers/media/v4l2-core/v4l2-subdev.c -@@ -32,7 +32,7 @@ - * 'v4l2_subdev_enable_streams_api' to 1 below. - */ - --static bool v4l2_subdev_enable_streams_api; -+static bool v4l2_subdev_enable_streams_api = true; - #endif - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss b/SPECS/kernel/0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss deleted file mode 100644 index 66e5296a4..000000000 --- a/SPECS/kernel/0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss +++ /dev/null @@ -1,69 +0,0 @@ -From 643efa49322625dad779c5b02473d1e4ccc11951 Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Fri, 8 Aug 2025 16:05:50 +0300 -Subject: [PATCH 10/16] i2c: i801: Add support for Intel Wildcat Lake-U - -Add SMBus IDs on Intel Wildcat Lake-U. - -Signed-off-by: Jarkko Nikula ---- - Documentation/i2c/busses/i2c-i801.rst | 1 + - drivers/i2c/busses/Kconfig | 1 + - drivers/i2c/busses/i2c-i801.c | 3 +++ - 3 files changed, 5 insertions(+) - -diff --git a/Documentation/i2c/busses/i2c-i801.rst b/Documentation/i2c/busses/i2c-i801.rst -index 47e8ac5b7099..36c563ad3f06 100644 ---- a/Documentation/i2c/busses/i2c-i801.rst -+++ b/Documentation/i2c/busses/i2c-i801.rst -@@ -50,6 +50,7 @@ Supported adapters: - * Intel Birch Stream (SOC) - * Intel Arrow Lake (SOC) - * Intel Panther Lake (SOC) -+ * Intel Wildcat Lake (SOC) - - Datasheets: Publicly available at the Intel website - -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 070d014fdc5d..0c77b1d4c260 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -165,6 +165,7 @@ config I2C_I801 - Birch Stream (SOC) - Arrow Lake (SOC) - Panther Lake (SOC) -+ Wildcat Lake (SOC) - - This driver can also be built as a module. If so, the module - will be called i2c-i801. -diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c -index a7f89946dad4..1e54fed4cac1 100644 ---- a/drivers/i2c/busses/i2c-i801.c -+++ b/drivers/i2c/busses/i2c-i801.c -@@ -83,6 +83,7 @@ - * Arrow Lake-H (SOC) 0x7722 32 hard yes yes yes - * Panther Lake-H (SOC) 0xe322 32 hard yes yes yes - * Panther Lake-P (SOC) 0xe422 32 hard yes yes yes -+ * Wildcat Lake-U (SOC) 0x4d22 32 hard yes yes yes - * - * Features supported by this driver: - * Software PEC no -@@ -236,6 +237,7 @@ - #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 - #define PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS 0x43a3 - #define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23 -+#define PCI_DEVICE_ID_INTEL_WILDCAT_LAKE_U_SMBUS 0x4d22 - #define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3 - #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3 - #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3 -@@ -1056,6 +1058,7 @@ static const struct pci_device_id i801_ids[] = { - { PCI_DEVICE_DATA(INTEL, ARROW_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, - { PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_H_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, - { PCI_DEVICE_DATA(INTEL, PANTHER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, -+ { PCI_DEVICE_DATA(INTEL, WILDCAT_LAKE_U_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, - { 0, } - }; - --- -2.43.0 - diff --git a/SPECS/kernel/0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c b/SPECS/kernel/0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c deleted file mode 100644 index db242a097..000000000 --- a/SPECS/kernel/0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c +++ /dev/null @@ -1,445 +0,0 @@ -From e90cfbe370ad12807a9c16f0313ce6fec37ab8da Mon Sep 17 00:00:00 2001 -From: Jarkko Nikula -Date: Wed, 27 Aug 2025 13:30:09 +0300 -Subject: [PATCH 10/11] i3c: mipi-i3c-hci: Convert remaining DBG() prints to - dev_dbg() - -Get rid of local DBG() macro and convert remaining debug prints to -dev_dbg() which can be controlled without code recompile when kernel is -built with dynamic debug support. - -Signed-off-by: Jarkko Nikula -Link: https://lore.kernel.org/r/20250827103009.243771-6-jarkko.nikula@linux.intel.com -Signed-off-by: Alexandre Belloni ---- - drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 9 ++- - drivers/i3c/master/mipi-i3c-hci/cmd_v2.c | 7 ++- - drivers/i3c/master/mipi-i3c-hci/core.c | 16 ++--- - drivers/i3c/master/mipi-i3c-hci/dma.c | 15 +++-- - drivers/i3c/master/mipi-i3c-hci/ext_caps.c | 11 ++-- - drivers/i3c/master/mipi-i3c-hci/hci.h | 3 - - drivers/i3c/master/mipi-i3c-hci/pio.c | 68 +++++++++++++--------- - 7 files changed, 73 insertions(+), 56 deletions(-) - -diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c -index dd636094b07f..eb8a3ae2990d 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c -+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c -@@ -317,7 +317,9 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) - break; - next_addr = ret; - -- DBG("next_addr = 0x%02x, DAA using DAT %d", next_addr, dat_idx); -+ dev_dbg(&hci->master.dev, -+ "next_addr = 0x%02x, DAA using DAT %d", -+ next_addr, dat_idx); - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx, next_addr); - mipi_i3c_hci_dct_index_reset(hci); - -@@ -349,8 +351,9 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) - } - - i3c_hci_dct_get_val(hci, 0, &pid, &dcr, &bcr); -- DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -- next_addr, pid, dcr, bcr); -+ dev_dbg(&hci->master.dev, -+ "assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -+ next_addr, pid, dcr, bcr); - - mipi_i3c_hci_dat_v1.free_entry(hci, dat_idx); - dat_idx = -1; -diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c -index 4493b2b067cb..efb4326a25b7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c -+++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v2.c -@@ -261,7 +261,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci) - if (ret < 0) - break; - next_addr = ret; -- DBG("next_addr = 0x%02x", next_addr); -+ dev_dbg(&hci->master.dev, "next_addr = 0x%02x", next_addr); - xfer[0].cmd_tid = hci_get_tid(); - xfer[0].cmd_desc[0] = - CMD_0_ATTR_A | -@@ -293,8 +293,9 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci) - pid = (pid << 32) | device_id[0]; - bcr = FIELD_GET(W1_MASK(55, 48), device_id[1]); - dcr = FIELD_GET(W1_MASK(63, 56), device_id[1]); -- DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -- next_addr, pid, dcr, bcr); -+ dev_dbg(&hci->master.dev, -+ "assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", -+ next_addr, pid, dcr, bcr); - /* - * TODO: Extend the subsystem layer to allow for registering - * new device and provide BCR/DCR/PID at the same time. -diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c -index 9932945ecf06..47e42cb4dbe7 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/core.c -+++ b/drivers/i3c/master/mipi-i3c-hci/core.c -@@ -147,7 +147,7 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) - amd_set_resp_buf_thld(hci); - - reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE); -- DBG("HC_CONTROL = %#x", reg_read(HC_CONTROL)); -+ dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL)); - - return 0; - } -@@ -192,8 +192,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, - DECLARE_COMPLETION_ONSTACK(done); - int i, last, ret = 0; - -- DBG("cmd=%#x rnw=%d ndests=%d data[0].len=%d", -- ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len); -+ dev_dbg(&hci->master.dev, "cmd=%#x rnw=%d ndests=%d data[0].len=%d", -+ ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len); - - xfer = hci_alloc_xfer(nxfers); - if (!xfer) -@@ -251,8 +251,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, - } - - if (ccc->rnw) -- DBG("got: %*ph", -- ccc->dests[0].payload.len, ccc->dests[0].payload.data); -+ dev_dbg(&hci->master.dev, "got: %*ph", -+ ccc->dests[0].payload.len, ccc->dests[0].payload.data); - - out: - hci_free_xfer(xfer, nxfers); -@@ -277,7 +277,7 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, - unsigned int size_limit; - int i, last, ret = 0; - -- DBG("nxfers = %d", nxfers); -+ dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); - - xfer = hci_alloc_xfer(nxfers); - if (!xfer) -@@ -335,7 +335,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - DECLARE_COMPLETION_ONSTACK(done); - int i, last, ret = 0; - -- DBG("nxfers = %d", nxfers); -+ dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); - - xfer = hci_alloc_xfer(nxfers); - if (!xfer) -@@ -587,7 +587,7 @@ static int i3c_hci_init(struct i3c_hci *hci) - } - - hci->caps = reg_read(HC_CAPABILITIES); -- DBG("caps = %#x", hci->caps); -+ dev_dbg(&hci->master.dev, "caps = %#x", hci->caps); - - size_in_dwords = hci->version_major < 1 || - (hci->version_major == 1 && hci->version_minor < 1); -diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c -index 3fadacbda582..c401a9425cdc 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/dma.c -+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c -@@ -248,8 +248,9 @@ static int hci_dma_init(struct i3c_hci *hci) - regval = rh_reg_read(CR_SETUP); - rh->xfer_struct_sz = FIELD_GET(CR_XFER_STRUCT_SIZE, regval); - rh->resp_struct_sz = FIELD_GET(CR_RESP_STRUCT_SIZE, regval); -- DBG("xfer_struct_sz = %d, resp_struct_sz = %d", -- rh->xfer_struct_sz, rh->resp_struct_sz); -+ dev_dbg(&hci->master.dev, -+ "xfer_struct_sz = %d, resp_struct_sz = %d", -+ rh->xfer_struct_sz, rh->resp_struct_sz); - xfers_sz = rh->xfer_struct_sz * rh->xfer_entries; - resps_sz = rh->resp_struct_sz * rh->xfer_entries; - -@@ -523,11 +524,11 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh) - ring_resp = rh->resp + rh->resp_struct_sz * done_ptr; - resp = *ring_resp; - tid = RESP_TID(resp); -- DBG("resp = 0x%08x", resp); -+ dev_dbg(&hci->master.dev, "resp = 0x%08x", resp); - - xfer = rh->src_xfers[done_ptr]; - if (!xfer) { -- DBG("orphaned ring entry"); -+ dev_dbg(&hci->master.dev, "orphaned ring entry"); - } else { - hci_dma_unmap_xfer(hci, xfer, 1); - xfer->ring_entry = -1; -@@ -630,7 +631,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - - ring_ibi_status = rh->ibi_status + rh->ibi_status_sz * ptr; - ibi_status = *ring_ibi_status; -- DBG("status = %#x", ibi_status); -+ dev_dbg(&hci->master.dev, "status = %#x", ibi_status); - - if (ibi_status_error) { - /* we no longer care */ -@@ -658,7 +659,9 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh) - - if (last_ptr == -1) { - /* this IBI sequence is not yet complete */ -- DBG("no LAST_STATUS available (e=%d d=%d)", enq_ptr, deq_ptr); -+ dev_dbg(&hci->master.dev, -+ "no LAST_STATUS available (e=%d d=%d)", -+ enq_ptr, deq_ptr); - return; - } - deq_ptr = last_ptr + 1; -diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c -index 2e9b23efdc45..7714f00ea9cc 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c -+++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c -@@ -35,7 +35,7 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base) - switch (hci->vendor_mipi_id) { - case MIPI_VENDOR_NXP: - hci->quirks |= HCI_QUIRK_RAW_CCC; -- DBG("raw CCC quirks set"); -+ dev_dbg(&hci->master.dev, "raw CCC quirks set"); - break; - } - -@@ -77,7 +77,8 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base) - for (index = 0; index < entries; index++) { - u32 mode_entry = readl(base); - -- DBG("mode %d: 0x%08x", index, mode_entry); -+ dev_dbg(&hci->master.dev, "mode %d: 0x%08x", -+ index, mode_entry); - /* TODO: will be needed when I3C core does more than SDR */ - base += 4; - } -@@ -97,7 +98,8 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base) - dev_info(&hci->master.dev, "available data rates:\n"); - for (index = 0; index < entries; index++) { - rate_entry = readl(base); -- DBG("entry %d: 0x%08x", index, rate_entry); -+ dev_dbg(&hci->master.dev, "entry %d: 0x%08x", -+ index, rate_entry); - rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry); - rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry); - mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry); -@@ -268,7 +270,8 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci) - cap_header = readl(curr_cap); - cap_id = FIELD_GET(CAP_HEADER_ID, cap_header); - cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header); -- DBG("id=0x%02x length=%d", cap_id, cap_length); -+ dev_dbg(&hci->master.dev, "id=0x%02x length=%d", -+ cap_id, cap_length); - if (!cap_length) - break; - if (curr_cap + cap_length * 4 >= end) { -diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h -index 33bc4906df1f..249ccb13c909 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/hci.h -+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h -@@ -12,9 +12,6 @@ - - #include - --/* Handy logging macro to save on line length */ --#define DBG(x, ...) pr_devel("%s: " x "\n", __func__, ##__VA_ARGS__) -- - /* 32-bit word aware bit and mask macros */ - #define W0_MASK(h, l) GENMASK((h) - 0, (l) - 0) - #define W1_MASK(h, l) GENMASK((h) - 32, (l) - 32) -diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mipi-i3c-hci/pio.c -index cde883137bc7..710faa46a00f 100644 ---- a/drivers/i3c/master/mipi-i3c-hci/pio.c -+++ b/drivers/i3c/master/mipi-i3c-hci/pio.c -@@ -213,8 +213,8 @@ static void hci_pio_cleanup(struct i3c_hci *hci) - pio_reg_write(INTR_SIGNAL_ENABLE, 0x0); - - if (pio) { -- DBG("status = %#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "status = %#x/%#x", -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); - BUG_ON(pio->curr_xfer); - BUG_ON(pio->curr_rx); - BUG_ON(pio->curr_tx); -@@ -226,13 +226,17 @@ static void hci_pio_cleanup(struct i3c_hci *hci) - - static void hci_pio_write_cmd(struct i3c_hci *hci, struct hci_xfer *xfer) - { -- DBG("cmd_desc[%d] = 0x%08x", 0, xfer->cmd_desc[0]); -- DBG("cmd_desc[%d] = 0x%08x", 1, xfer->cmd_desc[1]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 0, xfer->cmd_desc[0]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 1, xfer->cmd_desc[1]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[0]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[1]); - if (hci->cmd == &mipi_i3c_hci_cmd_v2) { -- DBG("cmd_desc[%d] = 0x%08x", 2, xfer->cmd_desc[2]); -- DBG("cmd_desc[%d] = 0x%08x", 3, xfer->cmd_desc[3]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 2, xfer->cmd_desc[2]); -+ dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x", -+ 3, xfer->cmd_desc[3]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[2]); - pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[3]); - } -@@ -254,7 +258,8 @@ static bool hci_pio_do_rx(struct i3c_hci *hci, struct hci_pio_data *pio) - nr_words = min(xfer->data_left / 4, pio->rx_thresh_size); - /* extract data from FIFO */ - xfer->data_left -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, xfer->data_left); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, xfer->data_left); - while (nr_words--) - *p++ = pio_reg_read(XFER_DATA_PORT); - } -@@ -269,7 +274,7 @@ static void hci_pio_do_trailing_rx(struct i3c_hci *hci, - struct hci_xfer *xfer = pio->curr_rx; - u32 *p; - -- DBG("%d remaining", count); -+ dev_dbg(&hci->master.dev, "%d remaining", count); - - p = xfer->data; - p += (xfer->data_len - xfer->data_left) / 4; -@@ -278,7 +283,8 @@ static void hci_pio_do_trailing_rx(struct i3c_hci *hci, - unsigned int nr_words = count / 4; - /* extract data from FIFO */ - xfer->data_left -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, xfer->data_left); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, xfer->data_left); - while (nr_words--) - *p++ = pio_reg_read(XFER_DATA_PORT); - } -@@ -321,7 +327,8 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio) - nr_words = min(xfer->data_left / 4, pio->tx_thresh_size); - /* push data into the FIFO */ - xfer->data_left -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, xfer->data_left); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, xfer->data_left); - while (nr_words--) - pio_reg_write(XFER_DATA_PORT, *p++); - } -@@ -336,7 +343,7 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio) - */ - if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD)) - return false; -- DBG("trailing %d", xfer->data_left); -+ dev_dbg(&hci->master.dev, "trailing %d", xfer->data_left); - pio_reg_write(XFER_DATA_PORT, *p); - xfer->data_left = 0; - } -@@ -481,7 +488,7 @@ static bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio) - u32 resp = pio_reg_read(RESPONSE_QUEUE_PORT); - unsigned int tid = RESP_TID(resp); - -- DBG("resp = 0x%08x", resp); -+ dev_dbg(&hci->master.dev, "resp = 0x%08x", resp); - if (tid != xfer->cmd_tid) { - dev_err(&hci->master.dev, - "response tid=%d when expecting %d\n", -@@ -522,14 +529,15 @@ static bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio) - * still exists. - */ - if (pio->curr_rx == xfer) { -- DBG("short RX ?"); -+ dev_dbg(&hci->master.dev, "short RX ?"); - pio->curr_rx = pio->curr_rx->next_data; - } else if (pio->curr_tx == xfer) { -- DBG("short TX ?"); -+ dev_dbg(&hci->master.dev, "short TX ?"); - pio->curr_tx = pio->curr_tx->next_data; - } else if (xfer->data_left) { -- DBG("PIO xfer count = %d after response", -- xfer->data_left); -+ dev_dbg(&hci->master.dev, -+ "PIO xfer count = %d after response", -+ xfer->data_left); - } - - pio->curr_resp = xfer->next_resp; -@@ -591,7 +599,7 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n) - struct hci_xfer *prev_queue_tail; - int i; - -- DBG("n = %d", n); -+ dev_dbg(&hci->master.dev, "n = %d", n); - - /* link xfer instances together and initialize data count */ - for (i = 0; i < n; i++) { -@@ -611,8 +619,9 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n) - if (!hci_pio_process_cmd(hci, pio)) - pio->enabled_irqs |= STAT_CMD_QUEUE_READY; - pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs); -- DBG("status = %#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "status = %#x/%#x", -+ pio_reg_read(INTR_STATUS), -+ pio_reg_read(INTR_SIGNAL_ENABLE)); - } - spin_unlock_irq(&pio->lock); - return 0; -@@ -686,10 +695,10 @@ static bool hci_pio_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int - int ret; - - spin_lock_irq(&pio->lock); -- DBG("n=%d status=%#x/%#x", n, -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -- DBG("main_status = %#x/%#x", -- readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28)); -+ dev_dbg(&hci->master.dev, "n=%d status=%#x/%#x", n, -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "main_status = %#x/%#x", -+ readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28)); - - ret = hci_pio_dequeue_xfer_common(hci, pio, xfer, n); - spin_unlock_irq(&pio->lock); -@@ -733,8 +742,8 @@ static void hci_pio_err(struct i3c_hci *hci, struct hci_pio_data *pio, - mipi_i3c_hci_pio_reset(hci); - mipi_i3c_hci_resume(hci); - -- DBG("status=%#x/%#x", -- pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); -+ dev_dbg(&hci->master.dev, "status=%#x/%#x", -+ pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE)); - } - - static void hci_pio_set_ibi_thresh(struct i3c_hci *hci, -@@ -749,7 +758,7 @@ static void hci_pio_set_ibi_thresh(struct i3c_hci *hci, - if (regval != pio->reg_queue_thresh) { - pio_reg_write(QUEUE_THLD_CTRL, regval); - pio->reg_queue_thresh = regval; -- DBG("%d", thresh_val); -+ dev_dbg(&hci->master.dev, "%d", thresh_val); - } - } - -@@ -773,7 +782,8 @@ static bool hci_pio_get_ibi_segment(struct i3c_hci *hci, - /* extract the data from the IBI port */ - nr_words = thresh_val; - ibi->seg_cnt -= nr_words * 4; -- DBG("now %d left %d", nr_words * 4, ibi->seg_cnt); -+ dev_dbg(&hci->master.dev, "now %d left %d", -+ nr_words * 4, ibi->seg_cnt); - while (nr_words--) - *p++ = pio_reg_read(IBI_PORT); - } -@@ -791,7 +801,7 @@ static bool hci_pio_get_ibi_segment(struct i3c_hci *hci, - hci_pio_set_ibi_thresh(hci, pio, 1); - if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD)) - return false; -- DBG("trailing %d", ibi->seg_cnt); -+ dev_dbg(&hci->master.dev, "trailing %d", ibi->seg_cnt); - data = pio_reg_read(IBI_PORT); - data = (__force u32) cpu_to_le32(data); - while (ibi->seg_cnt--) { -@@ -820,7 +830,7 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio) - */ - - ibi_status = pio_reg_read(IBI_PORT); -- DBG("status = %#x", ibi_status); -+ dev_dbg(&hci->master.dev, "status = %#x", ibi_status); - ibi->addr = FIELD_GET(IBI_TARGET_ADDR, ibi_status); - if (ibi_status & IBI_ERROR) { - dev_err(&hci->master.dev, "IBI error from %#x\n", ibi->addr); --- -2.43.0 - diff --git a/SPECS/kernel/0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel/0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet deleted file mode 100644 index e85563962..000000000 --- a/SPECS/kernel/0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet +++ /dev/null @@ -1,43 +0,0 @@ -From c2697c3fa88d42d534846cfe97afc44ef236f4a8 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Wed, 9 Jun 2021 01:56:30 +0800 -Subject: [PATCH 10/19] igc: Enable HW RX Timestamp for AF_XDP ZC - -Enable the RX HW Timestamp using meta data to userspace. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc_main.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index b581e96d0735..8df729810af5 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -2815,6 +2815,7 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) - u16 cleaned_count = igc_desc_unused(ring); - int total_bytes = 0, total_packets = 0; - u16 ntc = ring->next_to_clean; -+ struct igc_md_desc *md; - struct bpf_prog *prog; - bool failure = false; - int xdp_status = 0; -@@ -2863,6 +2864,14 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) - bi->xdp->data_end = bi->xdp->data + size; - xsk_buff_dma_sync_for_cpu(bi->xdp); - -+ if (adapter->btf_enabled) { -+ md = bi->xdp->data - sizeof(*md); -+ md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); -+ bi->xdp->data_meta = md; -+ } else { -+ xdp_set_data_meta_invalid(bi->xdp); -+ } -+ - res = __igc_xdp_run_prog(adapter, prog, bi->xdp); - switch (res) { - case IGC_XDP_PASS: --- -2.43.0 - diff --git a/SPECS/kernel/0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet b/SPECS/kernel/0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet new file mode 100644 index 000000000..c5812252d --- /dev/null +++ b/SPECS/kernel/0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet @@ -0,0 +1,34 @@ +From af7b03b8fad0f872c89d775d77545d4fe9ab61f4 Mon Sep 17 00:00:00 2001 +From: Aravindhan Gunasekaran +Date: Tue, 3 Aug 2021 17:32:26 +0000 +Subject: [PATCH 10/14] igc: Take care of DMA timestamp rollover + +This patch is to fix the spike in driver Tx Path when measuring between +two timestamp of TX HW Timestamp during profiling stage. + +Rollover is identified by checking the 32-bit SYSTIM_L(say, present-time) +value which should be greater that LS 32bits from DMA WB(time in past). + +Signed-off-by: Aravindhan Gunasekaran +Signed-off-by: Muhammad Husaini Zulkifli +--- + drivers/net/ethernet/intel/igc/igc_ptp.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c +index c1af5edae7aae..ad786c816144f 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ptp.c ++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c +@@ -459,6 +459,9 @@ static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, + nsec = rd32(IGC_SYSTIML); + sec = rd32(IGC_SYSTIMH); + ++ if (unlikely(nsec < (systim & 0xFFFFFFFF))) ++ --sec; ++ + switch (adapter->hw.mac.type) { + case igc_i225: + memset(hwtstamps, 0, sizeof(*hwtstamps)); +-- +2.43.0 + diff --git a/SPECS/kernel/0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet b/SPECS/kernel/0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet new file mode 100644 index 000000000..f5042a29b --- /dev/null +++ b/SPECS/kernel/0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet @@ -0,0 +1,121 @@ +From 43595d6f6842ca5bcb642a947af5cc5f48fc9e98 Mon Sep 17 00:00:00 2001 +From: Gan Yi Fang +Date: Mon, 24 Jul 2023 03:10:06 -0400 +Subject: [PATCH 10/18] net: phy: reconfigure PHY WoL when WoL option is + enabled + +This patch reconfigures the PHY WoL event from two scenarios. +They are needed for the PHY that operated in PHY_POLL mode +where there is no ISR available to handle the WoL event. + +1. PHY WoL status will only be clear after hard reboot. PHY WoL +reconfiguration is needed in init_phy for the WoL to set properly +after soft reboot. +2. After the PHY enables the WoL event, arm the WoL INT bit +before suspending to ensure the WoL is set properly. + +Below are additional background. +In order for the WoL to work, the interrupt header needs to be +connected to the PMC for the platform to wake up when there is a +rising edge from the WoL INT bit. + +Ideal case to trigger the WoL: +1. User enables WoL (WoL INT bit: 0) +2. Sleep the platform +3. Platform receives magic packet (WoL INT bit: change from 0 to 1) +4. Platform wakes up + +Issue might occur when: +1. User enables WoL (WoL INT bit: 0) +2. Platform receives 1st magic packet (WoL INT bit: change from 0 to 1) +3. Without handling the INT, sleeps the platform (WoL INT bit: 1) +4. Platform receives second magic packet (There is no change in +WoL INT bit) +5. Platform cannot wake up + +Check and set the WoL INT bit to 0 will reset the PHY interrupt +before suspending so the issue can be avoided. + +Signed-off-by: Gan Yi Fang +--- + .../net/ethernet/stmicro/stmmac/dwmac-intel.c | 5 ++++ + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 25 ++++++++++++++++++- + 3 files changed, 30 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index ca16568374d5a..932432846800d 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -1239,8 +1239,13 @@ static int stmmac_config_multi_msi(struct pci_dev *pdev, + static int intel_eth_pci_suspend(struct device *dev, void *bsp_priv) + { + struct pci_dev *pdev = to_pci_dev(dev); ++ struct net_device *ndev = dev_get_drvdata(&pdev->dev); + int ret; + ++ rtnl_lock(); ++ stmmac_rearm_wol(ndev); ++ rtnl_unlock(); ++ + ret = pci_save_state(pdev); + if (ret) + return ret; +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index 7ca5477be390b..56acc67bd6044 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -396,6 +396,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv); + void stmmac_ptp_unregister(struct stmmac_priv *priv); + int stmmac_xdp_open(struct net_device *dev); + void stmmac_xdp_release(struct net_device *dev); ++void stmmac_rearm_wol(struct net_device *dev); + int stmmac_resume(struct device *dev); + int stmmac_suspend(struct device *dev); + void stmmac_dvr_remove(struct device *dev); +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index f4b06854653fd..361b29c2c9854 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -4096,6 +4096,29 @@ static int stmmac_release(struct net_device *dev) + return 0; + } + ++void stmmac_rearm_wol(struct net_device *dev) ++{ ++ struct stmmac_priv *priv = netdev_priv(dev); ++ ++ if (priv->plat->flags & STMMAC_FLAG_USE_PHY_WOL) { ++ struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; ++ ++ phylink_ethtool_get_wol(priv->phylink, &wol); ++ ++ if (wol.wolopts) { ++ phylink_ethtool_set_wol(priv->phylink, &wol); ++ device_set_wakeup_enable(priv->device, !!wol.wolopts); ++ } ++ } ++} ++EXPORT_SYMBOL_GPL(stmmac_rearm_wol); ++ ++static int stmmac_stop(struct net_device *dev) ++{ ++ stmmac_rearm_wol(dev); ++ return stmmac_release(dev); ++} ++ + static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb, + struct stmmac_tx_queue *tx_q) + { +@@ -7099,7 +7122,7 @@ static void stmmac_get_stats64(struct net_device *dev, struct rtnl_link_stats64 + static const struct net_device_ops stmmac_netdev_ops = { + .ndo_open = stmmac_open, + .ndo_start_xmit = stmmac_xmit, +- .ndo_stop = stmmac_release, ++ .ndo_stop = stmmac_stop, + .ndo_change_mtu = stmmac_change_mtu, + .ndo_fix_features = stmmac_fix_features, + .ndo_set_features = stmmac_set_features, +-- +2.43.0 + diff --git a/SPECS/kernel/0010-patch-staging-add-IPU8_PCI_ID-support.ipu b/SPECS/kernel/0010-patch-staging-add-IPU8_PCI_ID-support.ipu deleted file mode 100644 index 2096b513e..000000000 --- a/SPECS/kernel/0010-patch-staging-add-IPU8_PCI_ID-support.ipu +++ /dev/null @@ -1,26 +0,0 @@ -From 9d9778e7b94ab66297f5d87aa7028520654c39be Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 15:01:40 +0800 -Subject: [PATCH 10/21] patch: staging add IPU8_PCI_ID support - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c -index 3a6c129015e7..8448c5e2161b 100644 ---- a/drivers/staging/media/ipu7/ipu7.c -+++ b/drivers/staging/media/ipu7/ipu7.c -@@ -2818,6 +2818,7 @@ static const struct dev_pm_ops ipu7_pm_ops = { - static const struct pci_device_id ipu7_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)}, -+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU8_PCI_ID)}, - {0,} - }; - MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl); --- -2.43.0 - diff --git a/SPECS/kernel/0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf b/SPECS/kernel/0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf deleted file mode 100644 index 6a08d5e83..000000000 --- a/SPECS/kernel/0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf +++ /dev/null @@ -1,29 +0,0 @@ -From aeb93d23991eff302911cc7331b7acb8d1d734b1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 26 Jan 2024 13:47:26 +0800 -Subject: [PATCH 010/100] perf/x86/intel: Fix typo in comments of - intel_put_event_constraints() - -As title said. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 465f740cc187..0b57a9146b82 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3922,7 +3922,7 @@ static void intel_put_event_constraints(struct cpu_hw_events *cpuc, - intel_put_shared_regs_event_constraints(cpuc, event); - - /* -- * is PMU has exclusive counter restrictions, then -+ * If PMU has exclusive counter restrictions, then - * all events are subject to and must call the - * put_excl_constraints() routine - */ --- -2.43.0 - diff --git a/SPECS/kernel/0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf b/SPECS/kernel/0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf new file mode 100644 index 000000000..ef141e859 --- /dev/null +++ b/SPECS/kernel/0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf @@ -0,0 +1,329 @@ +From 2eceb42aa55ab2f8fe6302a18e673a5fb91fdc5b Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 24 Dec 2025 13:30:40 -0800 +Subject: [PATCH 10/13] perf/x86/intel/uncore: Support uncore constraint ranges + +Add UNCORE_EVENT_CONSTRAINT_RANGE macro for uncore constraints, +similar to INTEL_EVENT_CONSTRAINT_RANGE, to reduce duplication when +defining consecutive uncore event constraints. + +No functional change intended. + +Suggested-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 2 +- + arch/x86/events/intel/uncore.h | 2 + + arch/x86/events/intel/uncore_snbep.c | 183 ++++++--------------------- + 3 files changed, 44 insertions(+), 143 deletions(-) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index 34d0822dc0022..e47483d1207d4 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -436,7 +436,7 @@ uncore_get_event_constraint(struct intel_uncore_box *box, struct perf_event *eve + + if (type->constraints) { + for_each_event_constraint(c, type->constraints) { +- if ((event->hw.config & c->cmask) == c->code) ++ if (constraint_match(c, event->hw.config)) + return c; + } + } +diff --git a/arch/x86/events/intel/uncore.h b/arch/x86/events/intel/uncore.h +index 55e3aebf4b5e0..564cb26c44680 100644 +--- a/arch/x86/events/intel/uncore.h ++++ b/arch/x86/events/intel/uncore.h +@@ -33,6 +33,8 @@ + #define UNCORE_EXTRA_PCI_DEV_MAX 4 + + #define UNCORE_EVENT_CONSTRAINT(c, n) EVENT_CONSTRAINT(c, n, 0xff) ++#define UNCORE_EVENT_CONSTRAINT_RANGE(c, e, n) \ ++ EVENT_CONSTRAINT_RANGE(c, e, n, 0xff) + + #define UNCORE_IGNORE_END -1 + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index 28bcccf5cdfe9..fac2be780276d 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -836,76 +836,37 @@ static struct intel_uncore_ops snbep_uncore_pci_ops = { + static struct event_constraint snbep_uncore_cbox_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x1), + UNCORE_EVENT_CONSTRAINT(0x02, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x04, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x05, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x04, 0x5, 0x3), + UNCORE_EVENT_CONSTRAINT(0x07, 0x3), + UNCORE_EVENT_CONSTRAINT(0x09, 0x3), + UNCORE_EVENT_CONSTRAINT(0x11, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x12, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x13, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x1b, 0xc), +- UNCORE_EVENT_CONSTRAINT(0x1c, 0xc), +- UNCORE_EVENT_CONSTRAINT(0x1d, 0xc), +- UNCORE_EVENT_CONSTRAINT(0x1e, 0xc), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x12, 0x13, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1b, 0x1e, 0xc), + UNCORE_EVENT_CONSTRAINT(0x1f, 0xe), + UNCORE_EVENT_CONSTRAINT(0x21, 0x3), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x31, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x35, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x31, 0x35, 0x3), + UNCORE_EVENT_CONSTRAINT(0x36, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x37, 0x39, 0x3), + UNCORE_EVENT_CONSTRAINT(0x3b, 0x1), + EVENT_CONSTRAINT_END + }; + + static struct event_constraint snbep_uncore_r2pcie_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x12, 0x1), + UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x24, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x24, 0x26, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x32, 0x34, 0x3), + EVENT_CONSTRAINT_END + }; + + static struct event_constraint snbep_uncore_r3qpi_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x12, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x12, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x20, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x21, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x22, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x24, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2a, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2b, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x30, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x31, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x36, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x20, 0x26, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x36, 0x39, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3034,24 +2995,15 @@ static struct intel_uncore_type hswep_uncore_qpi = { + }; + + static struct event_constraint hswep_uncore_r2pcie_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x24, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x23, 0x25, 0x1), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x27, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x29, 0x3), + UNCORE_EVENT_CONSTRAINT(0x2a, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x2b, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x35, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2b, 0x2d, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x32, 0x35, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3066,38 +3018,17 @@ static struct intel_uncore_type hswep_uncore_r2pcie = { + + static struct event_constraint hswep_uncore_r3qpi_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x07, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x08, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x09, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x0a, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x7, 0x0a, 0x7), + UNCORE_EVENT_CONSTRAINT(0x0e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x12, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x12, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x14, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x15, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x1f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x20, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x21, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x22, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x25, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x31, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x32, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x36, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x14, 0x15, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1f, 0x23, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x25, 0x26, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x29, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2c, 0x2f, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x31, 0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x36, 0x39, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3371,8 +3302,7 @@ static struct event_constraint bdx_uncore_r2pcie_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x25, 0x1), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), + UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2c, 0x2d, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3387,35 +3317,18 @@ static struct intel_uncore_type bdx_uncore_r2pcie = { + + static struct event_constraint bdx_uncore_r3qpi_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x01, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x07, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x08, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x09, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x0a, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x07, 0x0a, 0x7), + UNCORE_EVENT_CONSTRAINT(0x0e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x10, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x11, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x10, 0x11, 0x3), + UNCORE_EVENT_CONSTRAINT(0x13, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x14, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x15, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x1f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x20, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x21, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x22, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x23, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x14, 0x15, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1f, 0x23, 0x3), + UNCORE_EVENT_CONSTRAINT(0x25, 0x3), + UNCORE_EVENT_CONSTRAINT(0x26, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x28, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x29, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x33, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x34, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x36, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x37, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x38, 0x3), +- UNCORE_EVENT_CONSTRAINT(0x39, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x28, 0x29, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x2c, 0x2f, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x33, 0x34, 0x3), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x36, 0x39, 0x3), + EVENT_CONSTRAINT_END + }; + +@@ -3722,8 +3635,7 @@ static struct event_constraint skx_uncore_iio_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x95, 0xc), + UNCORE_EVENT_CONSTRAINT(0xc0, 0xc), + UNCORE_EVENT_CONSTRAINT(0xc5, 0xc), +- UNCORE_EVENT_CONSTRAINT(0xd4, 0xc), +- UNCORE_EVENT_CONSTRAINT(0xd5, 0xc), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0xd4, 0xd5, 0xc), + EVENT_CONSTRAINT_END + }; + +@@ -4479,14 +4391,9 @@ static struct intel_uncore_type skx_uncore_m2pcie = { + }; + + static struct event_constraint skx_uncore_m3upi_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x1d, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1e, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1d, 0x1e, 0x1), + UNCORE_EVENT_CONSTRAINT(0x40, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4f, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x50, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x51, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x52, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x4e, 0x52, 0x7), + EVENT_CONSTRAINT_END + }; + +@@ -5652,14 +5559,9 @@ static struct intel_uncore_type icx_uncore_upi = { + }; + + static struct event_constraint icx_uncore_m3upi_constraints[] = { +- UNCORE_EVENT_CONSTRAINT(0x1c, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1d, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1e, 0x1), +- UNCORE_EVENT_CONSTRAINT(0x1f, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1c, 0x1f, 0x1), + UNCORE_EVENT_CONSTRAINT(0x40, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4e, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x4f, 0x7), +- UNCORE_EVENT_CONSTRAINT(0x50, 0x7), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x4e, 0x50, 0x7), + EVENT_CONSTRAINT_END + }; + +@@ -6142,10 +6044,7 @@ static struct intel_uncore_ops spr_uncore_mmio_offs8_ops = { + static struct event_constraint spr_uncore_cxlcm_constraints[] = { + UNCORE_EVENT_CONSTRAINT(0x02, 0x0f), + UNCORE_EVENT_CONSTRAINT(0x05, 0x0f), +- UNCORE_EVENT_CONSTRAINT(0x40, 0xf0), +- UNCORE_EVENT_CONSTRAINT(0x41, 0xf0), +- UNCORE_EVENT_CONSTRAINT(0x42, 0xf0), +- UNCORE_EVENT_CONSTRAINT(0x43, 0xf0), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x40, 0x43, 0xf0), + UNCORE_EVENT_CONSTRAINT(0x4b, 0xf0), + UNCORE_EVENT_CONSTRAINT(0x52, 0xf0), + EVENT_CONSTRAINT_END +-- +2.43.0 + diff --git a/SPECS/kernel/0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal b/SPECS/kernel/0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal deleted file mode 100644 index 0b56f019f..000000000 --- a/SPECS/kernel/0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal +++ /dev/null @@ -1,48 +0,0 @@ -From fa14de5297eb00d640f1b87ae2522142a525a60e Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Tue, 23 Sep 2025 13:56:31 -0700 -Subject: [PATCH 10/11] thermal: intel: int340x: Power Slider: Validate - slider_balance range - -When the module parameter slider_balance is set to the performance -slider value of 0, the SoC slider profile switches to the performance -mode. - -This can cause the Linux power-profiles-daemon to change the system -power mode to performance from balanced mode. This happens when there -is only one platform profile registered as there will be no conflict -with other platform profiles. - -Same issue occurs when the slider_balance is set to the power-saver -slider value. - -Prevent module parameter slider_balance from overlapping with -performance and power-saver slider values by adding range validation. - -Return an error when an invalid value is provided. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20250923205631.3056590-1-srinivas.pandruvada@linux.intel.com -[ rjw: Changelog edits ] -Signed-off-by: Rafael J. Wysocki ---- - .../intel/int340x_thermal/processor_thermal_soc_slider.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -index 20d70cb01542..49ff3bae7271 100644 ---- a/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_soc_slider.c -@@ -67,7 +67,8 @@ static int slider_def_balance_set(const char *arg, const struct kernel_param *kp - - ret = kstrtou8(arg, 16, &slider_val); - if (!ret) { -- if (slider_val > SOC_SLIDER_VALUE_MAXIMUM) -+ if (slider_val <= slider_values[SOC_POWER_SLIDER_PERFORMANCE] || -+ slider_val >= slider_values[SOC_POWER_SLIDER_POWERSAVE]) - return -EINVAL; - - slider_balanced_param = slider_val; --- -2.43.0 - diff --git a/SPECS/kernel/0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo b/SPECS/kernel/0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo new file mode 100644 index 000000000..7355353b6 --- /dev/null +++ b/SPECS/kernel/0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo @@ -0,0 +1,138 @@ +From 968520318c7c179db8729828b9d934b1d1dd7cfa Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Sat, 29 Nov 2025 22:57:50 -0500 +Subject: [PATCH 10/21] tools/power turbostat: Add run-time MSR driver probe + +Rather than starting down the conditional-compile road... + +Probe the location of the MSR files at run-time. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 68 +++++++++++++++------------ + 1 file changed, 39 insertions(+), 29 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 8154d110dd07d..e85bdb00f24a5 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -142,6 +142,7 @@ struct msr_counter { + #define FLAGS_SHOW (1 << 1) + #define SYSFS_PERCPU (1 << 1) + }; ++static int use_android_msr_path; + + struct msr_counter bic[] = { + { 0x0, "usec", NULL, 0, 0, 0, NULL, 0 }, +@@ -2413,20 +2414,11 @@ int get_msr_fd(int cpu) + + if (fd) + return fd; +-#if defined(ANDROID) +- sprintf(pathname, "/dev/msr%d", cpu); +-#else +- sprintf(pathname, "/dev/cpu/%d/msr", cpu); +-#endif ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDONLY); + if (fd < 0) +-#if defined(ANDROID) +- err(-1, "%s open failed, try chown or chmod +r /dev/msr*, " +- "or run with --no-msr, or run as root", pathname); +-#else +- err(-1, "%s open failed, try chown or chmod +r /dev/cpu/*/msr, " +- "or run with --no-msr, or run as root", pathname); +-#endif ++ err(-1, "%s open failed, try chown or chmod +r %s, " ++ "or run with --no-msr, or run as root", pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); + fd_percpu[cpu] = fd; + + return fd; +@@ -6777,21 +6769,43 @@ void turbostat_loop() + } + } + +-void check_dev_msr() ++int probe_dev_msr(void) ++{ ++ struct stat sb; ++ char pathname[32]; ++ ++ sprintf(pathname, "/dev/msr%d", base_cpu); ++ return !stat(pathname, &sb); ++} ++ ++int probe_dev_cpu_msr(void) + { + struct stat sb; + char pathname[32]; + +- if (no_msr) +- return; +-#if defined(ANDROID) +- sprintf(pathname, "/dev/msr%d", base_cpu); +-#else + sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); +-#endif +- if (stat(pathname, &sb)) +- if (system("/sbin/modprobe msr > /dev/null 2>&1")) +- no_msr = 1; ++ return !stat(pathname, &sb); ++} ++ ++int probe_msr_driver(void) ++{ ++ if (probe_dev_msr()) { ++ use_android_msr_path = 1; ++ return 1; ++ } ++ return probe_dev_cpu_msr(); ++} ++ ++void check_msr_driver(void) ++{ ++ if (probe_msr_driver()) ++ return; ++ ++ if (system("/sbin/modprobe msr > /dev/null 2>&1")) ++ no_msr = 1; ++ ++ if (!probe_msr_driver()) ++ no_msr = 1; + } + + /* +@@ -6846,11 +6860,7 @@ void check_msr_permission(void) + failed += check_for_cap_sys_rawio(); + + /* test file permissions */ +-#if defined(ANDROID) +- sprintf(pathname, "/dev/msr%d", base_cpu); +-#else +- sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); +-#endif ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu); + if (euidaccess(pathname, R_OK)) { + failed++; + } +@@ -9476,7 +9486,7 @@ bool has_added_counters(void) + + void check_msr_access(void) + { +- check_dev_msr(); ++ check_msr_driver(); + check_msr_permission(); + + if (no_msr) +@@ -10147,7 +10157,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.10.24 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.11.29 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel/0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac b/SPECS/kernel/0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac deleted file mode 100644 index f6e6232fe..000000000 --- a/SPECS/kernel/0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac +++ /dev/null @@ -1,55 +0,0 @@ -From f63777b3f954d064c2f2230d8def8b88615f2645 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Fri, 20 Mar 2020 20:19:48 +0800 -Subject: [PATCH 11/13] EDAC/ieh: Add I/O device EDAC support for Intel Tiger - Lake-H SoC - -Tiger Lake-H SoC shares the same Integrated Error Handler(IEH) architecture -with Tiger Lake-U, so can use the same ieh_edac driver. - -Add Tiger Lake-H IEH device ID for I/O device EDAC support. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/ieh_edac.c | 11 ++++++++++- - 1 file changed, 10 insertions(+), 1 deletion(-) - -diff --git a/drivers/edac/ieh_edac.c b/drivers/edac/ieh_edac.c -index 81bba5aa775e..46eca4bddea8 100644 ---- a/drivers/edac/ieh_edac.c -+++ b/drivers/edac/ieh_edac.c -@@ -36,7 +36,7 @@ - - #include "edac_mc.h" - --#define IEH_REVISION "v1.7" -+#define IEH_REVISION "v1.8" - - #define EDAC_MOD_STR "ieh_edac" - #define IEH_NMI_NAME "ieh" -@@ -179,6 +179,14 @@ static struct ieh_config tgl_u_cfg = { - .action = RESTART, - }; - -+/* Tiger Lake-H SoC */ -+#define IEH_DID_TGL_H 0x43af -+ -+static struct ieh_config tgl_h_cfg = { -+ .did = IEH_DID_TGL_H, -+ .action = RESTART, -+}; -+ - static const char * const severities[] = { - [IEH_CORR_ERR] = "correctable", - [IEH_NONFATAL_ERR] = "non-fatal uncorrectable", -@@ -529,6 +537,7 @@ static struct notifier_block ieh_mce_dec = { - - static const struct x86_cpu_id ieh_cpuids[] = { - X86_MATCH_VFM(INTEL_TIGERLAKE_L, &tgl_u_cfg), -+ X86_MATCH_VFM(INTEL_TIGERLAKE, &tgl_h_cfg), - {} - }; - MODULE_DEVICE_TABLE(x86cpu, ieh_cpuids); --- -2.43.0 - diff --git a/SPECS/kernel/0011-KVM-VMX-Virtualize-FRED-event_data.nmi b/SPECS/kernel/0011-KVM-VMX-Virtualize-FRED-event_data.nmi deleted file mode 100644 index 7716dbdc2..000000000 --- a/SPECS/kernel/0011-KVM-VMX-Virtualize-FRED-event_data.nmi +++ /dev/null @@ -1,224 +0,0 @@ -From 2fb61e63bcd7ac26da234d4be9b1474391f68dab Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 13 Jun 2024 09:07:46 -0700 -Subject: [PATCH 11/44] KVM: VMX: Virtualize FRED event_data - -Set injected-event data when injecting a #PF, #DB, or #NM caused -by extended feature disable using FRED event delivery, and save -original-event data for being used as injected-event data. - -Unlike IDT using some extra CPU register as part of an event -context, e.g., %cr2 for #PF, FRED saves a complete event context -in its stack frame, e.g., FRED saves the faulting linear address -of a #PF into the event data field defined in its stack frame. - -Thus a new VMX control field called injected-event data is added -to provide the event data that will be pushed into a FRED stack -frame for VM entries that inject an event using FRED event delivery. -In addition, a new VM exit information field called original-event -data is added to store the event data that would have saved into a -FRED stack frame for VM exits that occur during FRED event delivery. -After such a VM exit is handled to allow the original-event to be -delivered, the data in the original-event data VMCS field needs to -be set into the injected-event data VMCS field for the injection of -the original event. - -Signed-off-by: Xin Li -[ Sean: reworked event data injection for nested ] -Signed-off-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v3: -* Rework event data injection for nested (Chao Gao & Sean Christopherson). - -Changes in v2: -* Document event data should be equal to CR2/DR6/IA32_XFD_ERR instead - of using WARN_ON() (Chao Gao). -* Zero event data if a #NM was not caused by extended feature disable - (Chao Gao). ---- - arch/x86/include/asm/kvm_host.h | 3 ++- - arch/x86/include/asm/vmx.h | 4 ++++ - arch/x86/kvm/svm/svm.c | 2 +- - arch/x86/kvm/vmx/vmx.c | 22 ++++++++++++++++++---- - arch/x86/kvm/x86.c | 16 +++++++++++++++- - 5 files changed, 40 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 3b64414a6730..40d2c5e1b7e7 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -761,6 +761,7 @@ struct kvm_queued_exception { - u32 error_code; - unsigned long payload; - bool has_payload; -+ u64 event_data; - }; - - /* -@@ -2208,7 +2209,7 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); - void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); - void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code); -+ bool has_error_code, u32 error_code, u64 event_data); - void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); - void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault); -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index d222fd1517ce..613deb8cfb78 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -270,8 +270,12 @@ enum vmcs_field { - PID_POINTER_TABLE_HIGH = 0x00002043, - SECONDARY_VM_EXIT_CONTROLS = 0x00002044, - SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, -+ INJECTED_EVENT_DATA = 0x00002052, -+ INJECTED_EVENT_DATA_HIGH = 0x00002053, - GUEST_PHYSICAL_ADDRESS = 0x00002400, - GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, -+ ORIGINAL_EVENT_DATA = 0x00002404, -+ ORIGINAL_EVENT_DATA_HIGH = 0x00002405, - VMCS_LINK_POINTER = 0x00002800, - VMCS_LINK_POINTER_HIGH = 0x00002801, - GUEST_IA32_DEBUGCTL = 0x00002802, -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 239436ae074b..8ee9f66a34f8 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4187,7 +4187,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) - - kvm_requeue_exception(vcpu, vector, - exitintinfo & SVM_EXITINTINFO_VALID_ERR, -- error_code); -+ error_code, 0); - break; - } - case SVM_EXITINTINFO_TYPE_INTR: -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index ba0177a97417..89841d2f3e45 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1864,6 +1864,9 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); - -+ if (is_fred_enabled(vcpu)) -+ vmcs_write64(INJECTED_EVENT_DATA, ex->event_data); -+ - vmx_clear_hlt(vcpu); - } - -@@ -7309,7 +7312,8 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) - static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - u32 idt_vectoring_info, - int instr_len_field, -- int error_code_field) -+ int error_code_field, -+ int event_data_field) - { - u8 vector; - int type; -@@ -7344,13 +7348,17 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - fallthrough; - case INTR_TYPE_HARD_EXCEPTION: { - u32 error_code = 0; -+ u64 event_data = 0; - - if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK) - error_code = vmcs_read32(error_code_field); -+ if (is_fred_enabled(vcpu)) -+ event_data = vmcs_read64(event_data_field); - - kvm_requeue_exception(vcpu, vector, - idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, -- error_code); -+ error_code, -+ event_data); - break; - } - case INTR_TYPE_SOFT_INTR: -@@ -7368,7 +7376,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx) - { - __vmx_complete_interrupts(&vmx->vcpu, vmx->idt_vectoring_info, - VM_EXIT_INSTRUCTION_LEN, -- IDT_VECTORING_ERROR_CODE); -+ IDT_VECTORING_ERROR_CODE, -+ ORIGINAL_EVENT_DATA); - } - - void vmx_cancel_injection(struct kvm_vcpu *vcpu) -@@ -7376,7 +7385,8 @@ void vmx_cancel_injection(struct kvm_vcpu *vcpu) - __vmx_complete_interrupts(vcpu, - vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), - VM_ENTRY_INSTRUCTION_LEN, -- VM_ENTRY_EXCEPTION_ERROR_CODE); -+ VM_ENTRY_EXCEPTION_ERROR_CODE, -+ INJECTED_EVENT_DATA); - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); - } -@@ -7530,6 +7540,10 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, - - vmx_disable_fb_clear(vmx); - -+ /* -+ * Note, even though FRED delivers the faulting linear address via the -+ * event data field on the stack, CR2 is still updated. -+ */ - if (vcpu->arch.cr2 != native_read_cr2()) - native_write_cr2(vcpu->arch.cr2); - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index bd1c2eb7c538..090f9fc0363f 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -810,9 +810,22 @@ void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu, - * breakpoint), it is reserved and must be zero in DR6. - */ - vcpu->arch.dr6 &= ~BIT(12); -+ -+ /* -+ * FRED #DB event data matches DR6, but follows the polarity of -+ * VMX's pending debug exceptions, not DR6. -+ */ -+ ex->event_data = ex->payload & ~BIT(12); -+ break; -+ case NM_VECTOR: -+ ex->event_data = ex->payload; - break; - case PF_VECTOR: - vcpu->arch.cr2 = ex->payload; -+ ex->event_data = ex->payload; -+ break; -+ default: -+ ex->event_data = 0; - break; - } - -@@ -920,7 +933,7 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, - } - - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code) -+ bool has_error_code, u32 error_code, u64 event_data) - { - - /* -@@ -945,6 +958,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.error_code = error_code; - vcpu->arch.exception.has_payload = false; - vcpu->arch.exception.payload = 0; -+ vcpu->arch.exception.event_data = event_data; - } - EXPORT_SYMBOL_GPL(kvm_requeue_exception); - --- -2.43.0 - diff --git a/SPECS/kernel/0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi b/SPECS/kernel/0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi new file mode 100644 index 000000000..0c65a8924 --- /dev/null +++ b/SPECS/kernel/0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi @@ -0,0 +1,49 @@ +From 01246fe551a7415adaae6d94f89bf6353859f793 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Wed, 7 Feb 2024 09:26:31 -0800 +Subject: [PATCH 11/44] KVM: x86: Add a helper to detect if FRED is enabled for + a vCPU + +Signed-off-by: Xin Li +[ Sean: removed the "kvm_" prefix from the function name ] +Signed-off-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/kvm_cache_regs.h | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h +index 8ddb01191d6f6..3c8dbb77d7d48 100644 +--- a/arch/x86/kvm/kvm_cache_regs.h ++++ b/arch/x86/kvm/kvm_cache_regs.h +@@ -205,6 +205,21 @@ static __always_inline bool kvm_is_cr4_bit_set(struct kvm_vcpu *vcpu, + return !!kvm_read_cr4_bits(vcpu, cr4_bit); + } + ++/* ++ * It's enough to check just CR4.FRED (X86_CR4_FRED) to tell if ++ * a vCPU is running with FRED enabled, because: ++ * 1) CR4.FRED can be set to 1 only _after_ IA32_EFER.LMA = 1. ++ * 2) To leave IA-32e mode, CR4.FRED must be cleared first. ++ */ ++static inline bool is_fred_enabled(struct kvm_vcpu *vcpu) ++{ ++#ifdef CONFIG_X86_64 ++ return kvm_is_cr4_bit_set(vcpu, X86_CR4_FRED); ++#else ++ return false; ++#endif ++} ++ + static inline ulong kvm_read_cr3(struct kvm_vcpu *vcpu) + { + if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) +-- +2.43.0 + diff --git a/SPECS/kernel/0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet b/SPECS/kernel/0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet deleted file mode 100644 index 0a64ba2db..000000000 --- a/SPECS/kernel/0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet +++ /dev/null @@ -1,48 +0,0 @@ -From 19e43999ba3d8d5a8f08ce836ea23f306adce236 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:41 -0700 -Subject: [PATCH 11/24] KVM: x86: Add fault checks for guest CR4.CET setting - -Check potential faults for CR4.CET setting per Intel SDM requirements. -CET can be enabled if and only if CR0.WP == 1, i.e. setting CR4.CET == -1 faults if CR0.WP == 0 and setting CR0.WP == 0 fails if CR4.CET == 1. - -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Reviewed-by: Chao Gao -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 0b0468348ee0..fbe3d1bea657 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1176,6 +1176,9 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) - (is_64_bit_mode(vcpu) || kvm_is_cr4_bit_set(vcpu, X86_CR4_PCIDE))) - return 1; - -+ if (!(cr0 & X86_CR0_WP) && kvm_is_cr4_bit_set(vcpu, X86_CR4_CET)) -+ return 1; -+ - kvm_x86_call(set_cr0)(vcpu, cr0); - - kvm_post_set_cr0(vcpu, old_cr0, cr0); -@@ -1375,6 +1378,9 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) - return 1; - } - -+ if ((cr4 & X86_CR4_CET) && !kvm_is_cr0_bit_set(vcpu, X86_CR0_WP)) -+ return 1; -+ - kvm_x86_call(set_cr4)(vcpu, cr4); - - kvm_post_set_cr4(vcpu, old_cr4, cr4); --- -2.43.0 - diff --git a/SPECS/kernel/0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl b/SPECS/kernel/0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl new file mode 100644 index 000000000..83a459ecf --- /dev/null +++ b/SPECS/kernel/0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl @@ -0,0 +1,38 @@ +From bbea5bdc46ea316869d78e041c5ec340ae5383f9 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Sun, 16 Nov 2025 13:34:29 +0100 +Subject: [PATCH 11/17] cpuidle: governors: teo: Fix tick_intercepts handling + in teo_update() + +The condition deciding whether or not to increase cpu_data->tick_intercepts +in teo_update() is reverse, so fix it. + +Fixes: d619b5cc6780 ("cpuidle: teo: Simplify counting events used for tick management") +Cc: 6.14+ # 6.14+: 0796ddf4a7f0: cpuidle: teo: Use this_cpu_ptr() where possible +Cc: 6.14+ # 6.14+: 8f3f01082d7a: cpuidle: governors: teo: Use s64 consistently in teo_update() +Cc: 6.14+ # 6.14+: b54df61c7428: cpuidle: governors: teo: Decay metrics below DECAY_SHIFT threshold +Cc: 6.14+ 6.14+: 083654ded547: cpuidle: governors: teo: Rework the handling of tick wakeups +Cc: 6.14+ +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Link: https://patch.msgid.link/5085160.31r3eYUQgx@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 94ba00b7617d7..85b5517067d16 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -251,7 +251,7 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + cpu_data->state_bins[idx_timer].hits += PULSE; + } else { + cpu_data->state_bins[idx_duration].intercepts += PULSE; +- if (TICK_NSEC <= measured_ns) ++ if (measured_ns <= TICK_NSEC) + cpu_data->tick_intercepts += PULSE; + } + } +-- +2.43.0 + diff --git a/SPECS/kernel/0011-i2c-designware-Preliminary-SMBus-support.lpss b/SPECS/kernel/0011-i2c-designware-Preliminary-SMBus-support.lpss deleted file mode 100644 index 74cd75789..000000000 --- a/SPECS/kernel/0011-i2c-designware-Preliminary-SMBus-support.lpss +++ /dev/null @@ -1,258 +0,0 @@ -From 3830284c347eb6ba17227cc9dcd2a769fe962247 Mon Sep 17 00:00:00 2001 -From: Heikki Krogerus -Date: Tue, 17 Dec 2024 16:15:44 +0200 -Subject: [PATCH 11/16] i2c: designware: Preliminary SMBus support - -This adds only the bare minimum handling for SMBus Alert -Signal, and only in host mode. - -Signed-off-by: Heikki Krogerus ---- - drivers/i2c/busses/Kconfig | 1 + - drivers/i2c/busses/Makefile | 1 + - drivers/i2c/busses/i2c-designware-core.h | 25 ++++++ - drivers/i2c/busses/i2c-designware-master.c | 5 ++ - drivers/i2c/busses/i2c-designware-pcidrv.c | 1 + - drivers/i2c/busses/i2c-designware-platdrv.c | 4 +- - drivers/i2c/busses/i2c-designware-smbus.c | 84 +++++++++++++++++++++ - 7 files changed, 120 insertions(+), 1 deletion(-) - create mode 100644 drivers/i2c/busses/i2c-designware-smbus.c - -diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig -index 0c77b1d4c260..bafb2794d06b 100644 ---- a/drivers/i2c/busses/Kconfig -+++ b/drivers/i2c/busses/Kconfig -@@ -564,6 +564,7 @@ config I2C_DAVINCI - config I2C_DESIGNWARE_CORE - tristate "Synopsys DesignWare I2C adapter" - select REGMAP -+ select I2C_SMBUS - help - This option enables support for the Synopsys DesignWare I2C adapter. - This driver includes support for the I2C host on the Synopsys -diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile -index 04db855fdfd6..b362e7250ae7 100644 ---- a/drivers/i2c/busses/Makefile -+++ b/drivers/i2c/busses/Makefile -@@ -53,6 +53,7 @@ obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o - obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o - i2c-designware-core-y := i2c-designware-common.o - i2c-designware-core-y += i2c-designware-master.o -+i2c-designware-core-y += i2c-designware-smbus.o - i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o - obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o - i2c-designware-platform-y := i2c-designware-platdrv.o -diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h -index 347843b4f5dd..5cfc909713d9 100644 ---- a/drivers/i2c/busses/i2c-designware-core.h -+++ b/drivers/i2c/busses/i2c-designware-core.h -@@ -78,9 +78,14 @@ - #define DW_IC_TX_ABRT_SOURCE 0x80 - #define DW_IC_ENABLE_STATUS 0x9c - #define DW_IC_CLR_RESTART_DET 0xa8 -+#define DW_IC_SMBUS_INTR_STAT 0xc8 -+#define DW_IC_SMBUS_INTR_MASK 0xcc -+#define DW_IC_SMBUS_RAW_INTR_STAT 0xd0 -+#define DW_IC_CLR_SMBUS_INTR 0xd4 - #define DW_IC_COMP_PARAM_1 0xf4 - #define DW_IC_COMP_VERSION 0xf8 - #define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A /* "111*" == v1.11* */ -+#define DW_IC_SMBUS_MIN_VER 0x3230302a /* "200*" == v2.00* */ - #define DW_IC_COMP_TYPE 0xfc - #define DW_IC_COMP_TYPE_VALUE 0x44570140 /* "DW" + 0x0140 */ - -@@ -118,6 +123,8 @@ - #define DW_IC_STATUS_SLAVE_ACTIVITY BIT(6) - #define DW_IC_STATUS_MASTER_HOLD_TX_FIFO_EMPTY BIT(7) - -+#define DW_IC_SMBUS_INTR_ALERT BIT(10) -+ - #define DW_IC_SDA_HOLD_RX_SHIFT 16 - #define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, 16) - -@@ -262,6 +269,7 @@ struct dw_i2c_dev { - struct clk *pclk; - struct reset_control *rst; - struct i2c_client *slave; -+ struct i2c_client *smbus_alert; - u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev); - int cmd_err; - struct i2c_msg *msgs; -@@ -311,6 +319,7 @@ struct dw_i2c_dev { - #define ACCESS_NO_IRQ_SUSPEND BIT(1) - #define ARBITRATION_SEMAPHORE BIT(2) - #define ACCESS_POLLING BIT(3) -+#define IS_SMBUS BIT(4) - - #define MODEL_MSCC_OCELOT BIT(8) - #define MODEL_BAIKAL_BT1 BIT(9) -@@ -413,3 +422,19 @@ int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev); - #endif - - int i2c_dw_fw_parse_and_configure(struct dw_i2c_dev *dev); -+ -+irqreturn_t i2c_dw_smbus_isr(struct dw_i2c_dev *dev); -+int i2c_dw_smbus_host_register(struct dw_i2c_dev *dev); -+ -+/** -+ * i2c_dw_smbus_unregister - Helper to remove SMBus resources -+ * @dev: handle to the controller -+ * -+ * This function removes the SMBus alert device if it exists. -+ */ -+static inline void i2c_dw_smbus_unregister(struct dw_i2c_dev *dev) -+{ -+ regmap_set_bits(dev->map, DW_IC_SMBUS_INTR_MASK, DW_IC_SMBUS_INTR_ALERT); -+ i2c_unregister_device(dev->smbus_alert); -+ dev->smbus_alert = NULL; -+} -diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c -index cbd88ffa5610..ffe41e1c9855 100644 ---- a/drivers/i2c/busses/i2c-designware-master.c -+++ b/drivers/i2c/busses/i2c-designware-master.c -@@ -747,6 +747,9 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) - struct dw_i2c_dev *dev = dev_id; - unsigned int stat, enabled; - -+ if (i2c_dw_smbus_isr(dev)) -+ return IRQ_HANDLED; -+ - regmap_read(dev->map, DW_IC_ENABLE, &enabled); - regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &stat); - if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY)) -@@ -1089,6 +1092,8 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) - ret = i2c_add_numbered_adapter(adap); - if (ret) - dev_err(dev->dev, "failure adding adapter: %d\n", ret); -+ else if (dev->flags & IS_SMBUS) -+ ret = i2c_dw_smbus_host_register(dev); - pm_runtime_put_noidle(dev->dev); - - return ret; -diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c -index f21f9877c040..1e7b4bd20964 100644 ---- a/drivers/i2c/busses/i2c-designware-pcidrv.c -+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c -@@ -303,6 +303,7 @@ static void i2c_dw_pci_remove(struct pci_dev *pdev) - pm_runtime_forbid(device); - pm_runtime_get_noresume(device); - -+ i2c_dw_smbus_unregister(dev); - i2c_del_adapter(&dev->adapter); - } - -diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c -index a35e4c64a1d4..dc9fd89e9f32 100644 ---- a/drivers/i2c/busses/i2c-designware-platdrv.c -+++ b/drivers/i2c/busses/i2c-designware-platdrv.c -@@ -326,6 +326,8 @@ static void dw_i2c_plat_remove(struct platform_device *pdev) - - pm_runtime_get_sync(device); - -+ i2c_dw_smbus_unregister(dev); -+ - i2c_del_adapter(&dev->adapter); - - i2c_dw_disable(dev); -@@ -365,7 +367,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { - { "INT33C3", 0 }, - { "INT3432", 0 }, - { "INT3433", 0 }, -- { "INTC10EF", 0 }, -+ { "INTC10EF", IS_SMBUS }, - {} - }; - MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match); -diff --git a/drivers/i2c/busses/i2c-designware-smbus.c b/drivers/i2c/busses/i2c-designware-smbus.c -new file mode 100644 -index 000000000000..2e01d070a913 ---- /dev/null -+++ b/drivers/i2c/busses/i2c-designware-smbus.c -@@ -0,0 +1,84 @@ -+// SPDX-License-Identifier: GPL-2.0-or-later -+/* -+ * Synopsys DesignWare SMBus driver. -+ * -+ * Copyright (C) 2024 Intel Corporation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "i2c-designware-core.h" -+ -+static struct i2c_smbus_alert_setup i2c_dw_smbus_setup; -+ -+/** -+ * i2c_dw_smbus_isr - Interrupt service routine for SMBus interrupts -+ * @dev: handle to the controller -+ * -+ * This function currently only handles the SMBus Alert signal. -+ * -+ * Return: IRQ_HANDLED if the interrupt was caused by the SMBUS Alert, -+ * otherwise IRQ_NONE. -+ */ -+irqreturn_t i2c_dw_smbus_isr(struct dw_i2c_dev *dev) -+{ -+ u32 stat; -+ -+ if (!dev->smbus_alert) -+ return IRQ_NONE; -+ -+ regmap_read(dev->map, DW_IC_SMBUS_INTR_STAT, &stat); -+ if (!stat) -+ return IRQ_NONE; -+ -+ regmap_write(dev->map, DW_IC_CLR_SMBUS_INTR, stat); -+ -+ if (stat & DW_IC_SMBUS_INTR_ALERT) -+ i2c_handle_smbus_alert(dev->smbus_alert); -+ -+ return IRQ_HANDLED; -+} -+ -+/** -+ * i2c_dw_smbus_host_register - Register the SMBus alert device for the host -+ * @dev: handle to the controller -+ * -+ * This function checks is the SMBus feature available, and then registers the -+ * alert device if it is. If the SMBus feature is not available the function -+ * returns silently with a success. -+ * -+ * The SMBus alert device needs to be unregistered by calling -+ * i2c_dw_smbus_unregister(). -+ * -+ * Return: 0 on success, errno on error. -+ */ -+int i2c_dw_smbus_host_register(struct dw_i2c_dev *dev) -+{ -+ struct i2c_client *alert; -+ u32 ic_version; -+ int ret; -+ -+ ret = regmap_read(dev->map, DW_IC_COMP_VERSION, &ic_version); -+ if (ret) -+ return ret; -+ -+ if (ic_version < DW_IC_SMBUS_MIN_VER) -+ return 0; -+ -+ alert = i2c_new_smbus_alert_device(&dev->adapter, &i2c_dw_smbus_setup); -+ if (IS_ERR(alert)) -+ return PTR_ERR(alert); -+ -+ dev->smbus_alert = alert; -+ -+ ret = regmap_clear_bits(dev->map, DW_IC_SMBUS_INTR_MASK, DW_IC_SMBUS_INTR_ALERT); -+ if (ret) -+ i2c_dw_smbus_unregister(dev); -+ -+ return ret; -+} --- -2.43.0 - diff --git a/SPECS/kernel/0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel/0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet new file mode 100644 index 000000000..7c391c49c --- /dev/null +++ b/SPECS/kernel/0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet @@ -0,0 +1,97 @@ +From e3ca62b0f8e5880890ac1134c29d50e9081f226a Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Thu, 10 Jun 2021 12:44:46 +0800 +Subject: [PATCH 11/14] igc: Enable HW TX Timestamp for AF_XDP ZC + +Enable HW TX Timestamp per-packet using dma write back descriptor. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc.h | 1 + + drivers/net/ethernet/intel/igc/igc_main.c | 7 ++++++- + drivers/net/ethernet/intel/igc/igc_ptp.c | 24 +++++++++++++++++++++++ + 3 files changed, 31 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h +index 64d22481be91e..3f8969c73a6d2 100644 +--- a/drivers/net/ethernet/intel/igc/igc.h ++++ b/drivers/net/ethernet/intel/igc/igc.h +@@ -788,6 +788,7 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, + void igc_ptp_tx_hang(struct igc_adapter *adapter); + void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); + void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); ++ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp); + + int igc_led_setup(struct igc_adapter *adapter); + void igc_led_free(struct igc_adapter *adapter); +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 5b797a0bd870d..cabdcacc4c923 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -3119,6 +3119,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) + tx_desc->read.buffer_addr = cpu_to_le64(dma); + + bi->type = IGC_TX_BUFFER_TYPE_XSK; ++ bi->tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; + bi->protocol = 0; + bi->bytecount = xdp_desc.len; + bi->gso_segs = 1; +@@ -3159,6 +3160,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + unsigned int i = tx_ring->next_to_clean; + struct igc_tx_buffer *tx_buffer; + union igc_adv_tx_desc *tx_desc; ++ ktime_t timestamp = 0; + u32 xsk_frames = 0; + + if (test_bit(__IGC_DOWN, &adapter->state)) +@@ -3197,7 +3199,10 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { + u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); + +- igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); ++ if (tx_ring->xsk_pool && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) ++ timestamp = igc_tx_dma_hw_tstamp(adapter, tstamp); ++ else ++ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); + } + + /* clear next_to_watch to prevent false hangs */ +diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c +index ad786c816144f..23a55ca30677b 100644 +--- a/drivers/net/ethernet/intel/igc/igc_ptp.c ++++ b/drivers/net/ethernet/intel/igc/igc_ptp.c +@@ -916,6 +916,30 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, + skb_tstamp_tx(skb, &shhwtstamps); + } + ++ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp) ++{ ++ struct skb_shared_hwtstamps shhwtstamps; ++ int adjust = 0; ++ ++ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); ++ ++ switch (adapter->link_speed) { ++ case SPEED_10: ++ adjust = IGC_I225_TX_LATENCY_10; ++ break; ++ case SPEED_100: ++ adjust = IGC_I225_TX_LATENCY_100; ++ break; ++ case SPEED_1000: ++ adjust = IGC_I225_TX_LATENCY_1000; ++ break; ++ case SPEED_2500: ++ adjust = IGC_I225_TX_LATENCY_2500; ++ break; ++ } ++ return ktime_add_ns(shhwtstamps.hwtstamp, adjust); ++} ++ + /** + * igc_ptp_tx_tstamp_event + * @adapter: board private structure +-- +2.43.0 + diff --git a/SPECS/kernel/0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet b/SPECS/kernel/0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet deleted file mode 100644 index 37413f6e4..000000000 --- a/SPECS/kernel/0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet +++ /dev/null @@ -1,34 +0,0 @@ -From df31134af3bc30f219b7f0a0414c852b551cc081 Mon Sep 17 00:00:00 2001 -From: Aravindhan Gunasekaran -Date: Tue, 3 Aug 2021 17:32:26 +0000 -Subject: [PATCH 11/19] igc: Take care of DMA timestamp rollover - -This patch is to fix the spike in driver Tx Path when measuring between -two timestamp of TX HW Timestamp during profiling stage. - -Rollover is identified by checking the 32-bit SYSTIM_L(say, present-time) -value which should be greater that LS 32bits from DMA WB(time in past). - -Signed-off-by: Aravindhan Gunasekaran -Signed-off-by: Muhammad Husaini Zulkifli ---- - drivers/net/ethernet/intel/igc/igc_ptp.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index 61fe47c2a5d3..7c220a6f601f 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -459,6 +459,9 @@ static void igc_ptp_dma_time_to_hwtstamp(struct igc_adapter *adapter, - nsec = rd32(IGC_SYSTIML); - sec = rd32(IGC_SYSTIMH); - -+ if (unlikely(nsec < (systim & 0xFFFFFFFF))) -+ --sec; -+ - switch (adapter->hw.mac.type) { - case igc_i225: - memset(hwtstamps, 0, sizeof(*hwtstamps)); --- -2.43.0 - diff --git a/SPECS/kernel/0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu b/SPECS/kernel/0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu deleted file mode 100644 index fcaf4fe87..000000000 --- a/SPECS/kernel/0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu +++ /dev/null @@ -1,317 +0,0 @@ -From 2a9fc94ed5cd0f8d8f54e7537337bdc1567cf4e9 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 6 Oct 2025 10:01:19 +0800 -Subject: [PATCH 11/11] ipu7: media: Fix allyesconfig & allmodconfig - -Remove IPU6 config in Kconfig & Makefile. Remove declared but -unused functions. Update Kconfig to block arm64 build. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/Kconfig | 1 + - drivers/media/i2c/isx031.c | 4 +- - drivers/media/i2c/max9x/max96724.c | 192 ----------------------- - drivers/media/pci/intel/Kconfig | 1 - - drivers/media/pci/intel/Makefile | 1 - - drivers/media/pci/intel/ipu7/ipu7-isys.c | 3 +- - drivers/media/platform/intel/ipu-acpi.c | 1 - - 7 files changed, 5 insertions(+), 198 deletions(-) - -diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig -index f65a2f6118ea..d7e8a0b986a3 100644 ---- a/drivers/media/i2c/Kconfig -+++ b/drivers/media/i2c/Kconfig -@@ -289,6 +289,7 @@ config VIDEO_ISX031 - config VIDEO_MAX9X - tristate "MAX9X serdes support" - depends on VIDEO_DEV && I2C -+ depends on VIDEO_INTEL_IPU7 && VIDEO_INTEL_IPU_USE_PLATFORMDATA - select VIDEO_V4L2_SUBDEV_API - depends on MEDIA_CAMERA_SUPPORT - help -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index e7d9e417745a..ddc3e512efa4 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -769,11 +769,11 @@ static int isx031_probe(struct i2c_client *client) - return ret; - } - -- if (isx031->platform_data && isx031->platform_data->suffix) -+ if (isx031->platform_data) - snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %s", - isx031->platform_data->suffix); - -- if (isx031->platform_data && isx031->platform_data->lanes) -+ if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - - mutex_init(&isx031->mutex); -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index 1c1059c0c9de..b214e1176963 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -64,8 +64,6 @@ static int max96724_enable_serial_link(struct max9x_common *common, unsigned int - static int max96724_set_remote_control_channel_enabled(struct max9x_common *common, unsigned int link_id, bool enabled); - static int max96724_select_serial_link(struct max9x_common *common, unsigned int link); - static int max96724_deselect_serial_link(struct max9x_common *common, unsigned int link); --static int max96724_enable_native_frame_sync(struct max9x_common *common); --static int max96724_enable_gpio_frame_sync(struct max9x_common *common); - static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line); - static int max96724_enable_line_fault(struct max9x_common *common, unsigned int line); - static int max96724_set_line_fault(struct max9x_common *common, unsigned int line, bool enable); -@@ -877,196 +875,6 @@ static struct max9x_serial_link_ops max96724_serial_link_ops = { - .get_locked = max96724_get_serial_link_lock, - }; - --static int max96724_enable_native_frame_sync(struct max9x_common *common) --{ -- struct device_node *node = common->dev->of_node; -- struct device *dev = common->dev; -- struct regmap *map = common->map; -- int ret, i; -- unsigned int val; -- enum max96724_fsync_pin pin; -- unsigned int fsync_freq; -- unsigned int pclk_freq; -- unsigned int fsync_period; -- unsigned int fsync_tx_id; -- bool fsync_master; -- -- if (!of_property_read_bool(node, "frame-sync-enable")) { -- dev_info(dev, "Native frame sync not enabled"); -- return regmap_write(map, MAX96724_FSYNC_0, -- MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -- MAX96724_FSYNC_GEN_OFF_GPIO_OFF)); -- } -- -- fsync_master = of_property_read_bool(node, "frame-sync-master"); -- if (fsync_master) -- dev_dbg(dev, "Frame sync master mode"); -- else -- dev_dbg(dev, "Frame sync slave mode"); -- -- ret = of_property_read_u32(node, "frame-sync-pin", &val); -- if (ret) { -- dev_err(dev, "Missing property: frame-sync-pin"); -- return ret; -- } -- -- // check value of pin -- switch (val) { -- case MAX96724_FSYNC_PIN_MFP0: -- case MAX96724_FSYNC_PIN_MFP7: -- pin = val; -- break; -- -- default: -- dev_err(dev, "Invalid frame-sync-pin"); -- return -EINVAL; -- }; -- -- ret = of_property_read_u32(node, "frame-sync-tx-id", &val); -- if (ret) { -- dev_err(dev, "Missing property: frame-sync-tx-id"); -- return -EINVAL; -- } -- -- // check value of frame-sync-tx-id -- fsync_tx_id = val & 0x1F; -- if (fsync_tx_id != val) -- dev_warn(dev, "Truncated frame-sync-tx-id to 5 bits!"); -- -- ret = of_property_read_u32(node, "pclk-freq", &pclk_freq); -- if (ret) { -- dev_err(dev, "Missing property: pclk-freq"); -- return -EINVAL; -- } -- -- ret = of_property_read_u32(node, "frame-sync-freq", &fsync_freq); -- if (ret) { -- dev_err(dev, "Missing property: frame-sync-freq;"); -- return -EINVAL; -- } -- -- // Reset register to known state -- ret = regmap_write(map, MAX96724_FSYNC_15, 0xDF); -- if (ret) { -- dev_dbg(dev, "Failed to reset FSYNC state"); -- return ret; -- } -- -- // Disable AUTO FS links -- val = MAX9X_FIELD_PREP(MAX96724_FS_GPIO_TYPE_FIELD, MAX96724_FS_GPIO_TYPE_GMSL2) | -- MAX9X_FIELD_PREP(MAX96724_FS_USE_XTAL_FIELD, true) | -- MAX9X_FIELD_PREP(MAX96724_AUTO_FS_LINKS_FIELD, 0); -- // Enable all FS links manually -- for (i = 0; i < 4; ++i) -- val |= MAX9X_FIELD_PREP(MAX96724_FS_LINK_FIELD(i), 1); -- -- ret = regmap_write(map, MAX96724_FSYNC_15, val); -- if (ret) { -- dev_dbg(dev, "Failed to write FSYNC_15"); -- return ret; -- } -- -- // Calculate value of FSYNC_PERIOD registers -- // FSYNC_PERIOD = number of pclk cycles per fsync period -- fsync_period = pclk_freq / fsync_freq; -- dev_dbg(dev, "Calculated FSYNC_PERIOD: 0x%06x", fsync_period); -- -- for (val = MAX96724_FSYNC_5; val <= MAX96724_FSYNC_7; ++val) { -- ret = regmap_write(map, val, (uint8_t) fsync_period); -- if (ret) { -- dev_err(dev, "Failed to write FSYNC_PERIOD registers to 0x%03x", val); -- return ret; -- } -- -- fsync_period = fsync_period >> 8; -- } -- -- ret = regmap_write(map, MAX96724_FSYNC_17, -- MAX9X_FIELD_PREP(MAX96724_FSYNC_TX_ID_FIELD, fsync_tx_id) | -- MAX9X_FIELD_PREP(MAX96724_FSYNC_ERR_THR_FIELD, 0)); -- if (ret) { -- dev_err(dev, "Failed to set FSYNC_17"); -- return ret; -- } -- -- ret = regmap_write(map, MAX96724_FSYNC_0, -- MAX9X_FIELD_PREP(MAX96724_FSYNC_OUT_PIN_FIELD, pin) | -- MAX9X_FIELD_PREP(MAX96724_EN_VS_GEN_FIELD, 0) | -- MAX9X_FIELD_PREP(MAX96724_FSYNC_MODE_FIELD, -- (fsync_master ? MAX96724_FSYNC_GEN_ON_GPIO_OUTPUT : MAX96724_FSYNC_GEN_OFF_GPIO_INPUT)) | -- MAX9X_FIELD_PREP(MAX96724_FSYNC_METH_FIELD, MAX96724_FSYNC_METHOD_MANUAL)); -- -- return 0; --} -- --static int max96724_enable_gpio_frame_sync(struct max9x_common *common) --{ -- struct device_node *node = common->dev->of_node; -- struct device *dev = common->dev; -- struct regmap *map = common->map; -- -- u32 fsync_gpios[MAX96724_NUM_GPIOS]; -- int num_fsync_gpios; -- int i, gpio, gpio_tx_val, ret; -- -- // Clean up any previous values in the event the chip was not reset -- // or GPIO forwarding needs to be toggled off -- dev_dbg(dev, "Setting GPIO registers to default value"); -- for (i = 0; i < MAX96724_NUM_GPIOS; i++) { -- // Default values per the datasheet -- TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), (BIT(7) | BIT(0)))); -- -- // Link A register has different fields from Links B, C, D -- TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), (BIT(7) | BIT(5) | i))); -- TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), i)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), i)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), i)); -- } -- -- // Read DT to find fsync GPIOs -- ret = of_property_read_variable_u32_array(node, "frame-sync-ports", -- fsync_gpios, 0, MAX96724_NUM_GPIOS); -- -- if (ret == -ENODATA || ret == -EINVAL) { -- dev_dbg(dev, "No frame sync GPIOs specified in DT"); -- return 0; -- } -- -- if (ret < 0) { -- dev_err(dev, "Failed to parse DT frame-sync-ports, error %d", ret); -- return ret; -- } -- -- num_fsync_gpios = ret; -- dev_info(dev, "Enabling %d frame sync GPIOs", num_fsync_gpios); -- -- // Configure MAX96724 to forward specified GPIOs -- for (i = 0; i < num_fsync_gpios; i++) { -- gpio = fsync_gpios[i]; -- -- if (gpio >= MAX96724_NUM_GPIOS) { -- dev_warn(dev, "Skipping invalid GPIO %d in DT", gpio); -- continue; -- } -- -- // See: MAX96724 Users Guide "Configuring GPIO forwarding" -- -- // Enable GPIO for transmission -- TRY(ret, regmap_write(map, MAX96724_GPIO_REG(gpio), -- MAX96724_GPIO_RES_CFG | MAX96724_GPIO_TX_ENABLE | MAX96724_GPIO_OUTDRV_DISABLE)); -- -- // Configure transmission registers on Links A-D. -- gpio_tx_val = MAX96724_GPIO_PUSH_PULL | gpio; -- -- TRY(ret, regmap_write(map, MAX96724_GPIO_A_REG(gpio), gpio_tx_val)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_B_REG(gpio), gpio_tx_val)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_C_REG(gpio), gpio_tx_val)); -- TRY(ret, regmap_write(map, MAX96724_GPIO_D_REG(gpio), gpio_tx_val)); -- } -- -- return 0; --} -- - static int max96724_disable_line_fault(struct max9x_common *common, unsigned int line) - { - return max96724_set_line_fault(common, line, false); -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index 948cda08fff5..a92a7fabeac9 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -1,7 +1,6 @@ - # SPDX-License-Identifier: GPL-2.0-only - - source "drivers/media/pci/intel/ipu3/Kconfig" --source "drivers/media/pci/intel/ipu6/Kconfig" - source "drivers/media/pci/intel/ipu7/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index ff0fea13422d..b022340db8ba 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -5,5 +5,4 @@ - obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ --obj-$(CONFIG_VIDEO_INTEL_IPU6) += ipu6/ - obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -diff --git a/drivers/media/pci/intel/ipu7/ipu7-isys.c b/drivers/media/pci/intel/ipu7/ipu7-isys.c -index e5ba50142a1a..44e860d3db24 100644 ---- a/drivers/media/pci/intel/ipu7/ipu7-isys.c -+++ b/drivers/media/pci/intel/ipu7/ipu7-isys.c -@@ -517,6 +517,7 @@ static void isys_tpg_unregister_subdevices(struct ipu7_isys *isys) - isys->tpg = NULL; - } - -+/* - static int isys_tpg_register_subdevices(struct ipu7_isys *isys) - { - const struct ipu7_isys_internal_tpg_pdata *tpg_pdata = -@@ -577,7 +578,7 @@ static int isys_tpg_create_media_links(struct ipu7_isys *isys) - - return 0; - } -- -+*/ - #endif - - #if IS_ENABLED(CONFIG_INTEL_IPU7_ACPI) -diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c -index deed3bba52bb..c254c8299930 100644 ---- a/drivers/media/platform/intel/ipu-acpi.c -+++ b/drivers/media/platform/intel/ipu-acpi.c -@@ -191,7 +191,6 @@ static int ipu_acpi_test(struct device *dev, void *priv) - - int ipu_get_acpi_devices(void **spdata) - { -- struct ipu_i2c_helper helper = {0}; - int rval; - struct ipu7_isys_subdev_pdata *ptr = NULL; - --- -2.43.0 - diff --git a/SPECS/kernel/0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet b/SPECS/kernel/0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet new file mode 100644 index 000000000..e2983f559 --- /dev/null +++ b/SPECS/kernel/0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet @@ -0,0 +1,117 @@ +From 14e35c66b1dbba600374e06ad117fd1d5f0368e9 Mon Sep 17 00:00:00 2001 +From: Choong Yong Liang +Date: Fri, 15 Dec 2023 09:09:21 +0800 +Subject: [PATCH 11/18] net: stmmac: Set mac_managed_pm flag from stmmac to + resolve race condition + +When WoL is set to 'g' (e.g., using the command +'ethtool -s enp0s30f4 wol g'), waking up from hibernation will result +in the error messages 'PM: failed to quiesce: error -16' and +'PM: hibernation: Failed to load image, recovering.' + +During 'hibernation_restore()', it will eventually call the +'mdio_bus_phy_suspend()' function, and the function will check the +'mac_managed_pm' flag. If the flag is disabled, it will proceed to the +'phy_suspend()' function and return a -16 error. + +For 'stmmac', the 'mac_managed_pm' flag is always set to 'true' for the +'phylink', and 'phylink' will set the 'mac_managed_pm' flag for the 'phy' +during 'phylink_bringup_phy()'. The process of setting the 'mac_managed_pm' +flag from 'stmmac' -> 'phylink' -> 'phy' takes a while to complete. + +During wake-up from hibernation, there is a race condition that depends on +whether 'mac_managed_pm' was set for the 'phy' first or 'phy_suspend()' +function is called first. + +To address the race condition, 'stmmac' directly setting the +'mac_managed_pm' during 'stmmac_dvr_probe()' will resolve the issue. + +Fixes: f151c147b3af ("net: stmmac: Enable mac_managed_pm phylink config") +Signed-off-by: Choong Yong Liang +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 40 +++++++++++++------ + 1 file changed, 27 insertions(+), 13 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 361b29c2c9854..92a8075ef9399 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -1102,6 +1102,24 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) + } + } + ++static int stmmac_get_phydev(struct stmmac_priv *priv, struct phy_device **phydev) ++{ ++ int addr = priv->plat->phy_addr; ++ ++ if (addr < 0) { ++ netdev_err(priv->dev, "no phy found\n"); ++ return -ENODEV; ++ } ++ ++ *phydev = mdiobus_get_phy(priv->mii, addr); ++ if (!*phydev) { ++ netdev_err(priv->dev, "no phy at addr %d\n", addr); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ + /** + * stmmac_init_phy - PHY initialization + * @dev: net device structure +@@ -1139,19 +1157,10 @@ static int stmmac_init_phy(struct net_device *dev) + * manually parse it + */ + if (!phy_fwnode || IS_ERR(phy_fwnode)) { +- int addr = priv->plat->phy_addr; + struct phy_device *phydev; +- +- if (addr < 0) { +- netdev_err(priv->dev, "no phy found\n"); +- return -ENODEV; +- } +- +- phydev = mdiobus_get_phy(priv->mii, addr); +- if (!phydev) { +- netdev_err(priv->dev, "no phy at addr %d\n", addr); +- return -ENODEV; +- } ++ ret = stmmac_get_phydev(priv, &phydev); ++ if (ret) ++ return ret; + + ret = phylink_connect_phy(priv->phylink, phydev); + } else { +@@ -1198,7 +1207,6 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) + + config->dev = &priv->dev->dev; + config->type = PHYLINK_NETDEV; +- config->mac_managed_pm = true; + + /* Stmmac always requires an RX clock for hardware initialization */ + config->mac_requires_rxc = true; +@@ -7446,6 +7454,7 @@ int stmmac_dvr_probe(struct device *device, + { + struct net_device *ndev = NULL; + struct stmmac_priv *priv; ++ struct phy_device *phydev; + u32 rxq; + int i, ret = 0; + +@@ -7694,6 +7703,11 @@ int stmmac_dvr_probe(struct device *device, + if (ret) + goto error_pcs_setup; + ++ ret = stmmac_get_phydev(priv, &phydev); ++ if (ret) ++ return ret; ++ phydev->mac_managed_pm = true; ++ + ret = stmmac_phy_setup(priv); + if (ret) { + netdev_err(ndev, "failed to setup phy (%d)\n", ret); +-- +2.43.0 + diff --git a/SPECS/kernel/0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu b/SPECS/kernel/0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu deleted file mode 100644 index cb63d9e11..000000000 --- a/SPECS/kernel/0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu +++ /dev/null @@ -1,78 +0,0 @@ -From d18ecf4f258ad566e3e85b194e61047c12bdb56b Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 15:39:28 +0800 -Subject: [PATCH 11/21] patch: staging add patch for ipu7 Kconfig Makefile - -Support kernel v6.10 - -Due to change "kbuild: use $(src) instead of $(srctree)/$(src) for -source directory" at https://lore.kernel.org/lkml/20240416121838. -95427-5-masahiroy@kernel.org/, the old include paths in Makefile -can't work on kernel v6.10. To keep compatible with < v6.10 kernel, -add another check in Makefile. - -Signed-off-by: linya14x -Signed-off-by: Hao Yao ---- - drivers/staging/media/ipu7/Kconfig | 11 ++++++++--- - drivers/staging/media/ipu7/Makefile | 11 +++++++++++ - 2 files changed, 19 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig -index c4eee7c3e6d7..0a6e68c7632d 100644 ---- a/drivers/staging/media/ipu7/Kconfig -+++ b/drivers/staging/media/ipu7/Kconfig -@@ -4,7 +4,12 @@ config VIDEO_INTEL_IPU7 - depends on VIDEO_DEV - depends on X86 && HAS_DMA - depends on IPU_BRIDGE || !IPU_BRIDGE -- depends on PCI -+ # -+ # This driver incorrectly tries to override the dma_ops. It should -+ # never have done that, but for now keep it working on architectures -+ # that use dma ops -+ # -+ depends on ARCH_HAS_DMA_OPS - select AUXILIARY_BUS - select IOMMU_IOVA - select VIDEO_V4L2_SUBDEV_API -@@ -15,8 +20,8 @@ config VIDEO_INTEL_IPU7 - This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs - and used for capturing images and video from camera sensors. - -- To compile this driver, say Y here! It contains 2 modules - -- intel_ipu7 and intel_ipu7_isys. -+ To compile this driver, say Y here! It contains 3 modules - -+ intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. - - config VIDEO_INTEL_IPU7_ISYS_RESET - bool "IPU7 ISYS RESET" -diff --git a/drivers/staging/media/ipu7/Makefile b/drivers/staging/media/ipu7/Makefile -index 6d2aec219e65..3c35ca566473 100644 ---- a/drivers/staging/media/ipu7/Makefile -+++ b/drivers/staging/media/ipu7/Makefile -@@ -1,6 +1,13 @@ - # SPDX-License-Identifier: GPL-2.0 - # Copyright (c) 2017 - 2025 Intel Corporation. - -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ - intel-ipu7-objs += ipu7.o \ - ipu7-bus.o \ - ipu7-dma.o \ -@@ -21,3 +28,7 @@ intel-ipu7-isys-objs += ipu7-isys.o \ - ipu7-isys-subdev.o - - obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += psys/ -+ -+ccflags-y += -I$(src)/ --- -2.43.0 - diff --git a/SPECS/kernel/0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf b/SPECS/kernel/0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf deleted file mode 100644 index 327902cf0..000000000 --- a/SPECS/kernel/0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf +++ /dev/null @@ -1,40 +0,0 @@ -From ac8185284e5f69dfb3652c262cd9c7729886012e Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 21 Jul 2023 20:31:26 +0800 -Subject: [PATCH 011/100] perf/x86: Fix typos and inconsistent indents in - perf_event header - -There is one typo and some inconsistent indents in perf_event.h header -file. Fix them. - -Signed-off-by: Dapeng Mi ---- - arch/x86/include/asm/perf_event.h | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 49a4d442f3fc..1eb31cd9fceb 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -444,15 +444,15 @@ static inline bool is_topdown_idx(int idx) - * - * With this fake counter assigned, the guest LBR event user (such as KVM), - * can program the LBR registers on its own, and we don't actually do anything -- * with then in the host context. -+ * with them in the host context. - */ --#define INTEL_PMC_IDX_FIXED_VLBR (GLOBAL_STATUS_LBRS_FROZEN_BIT) -+#define INTEL_PMC_IDX_FIXED_VLBR (GLOBAL_STATUS_LBRS_FROZEN_BIT) - - /* - * Pseudo-encoding the guest LBR event as event=0x00,umask=0x1b, - * since it would claim bit 58 which is effectively Fixed26. - */ --#define INTEL_FIXED_VLBR_EVENT 0x1b00 -+#define INTEL_FIXED_VLBR_EVENT 0x1b00 - - /* - * Adaptive PEBS v4 --- -2.43.0 - diff --git a/SPECS/kernel/0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf b/SPECS/kernel/0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf new file mode 100644 index 000000000..f6b9501ae --- /dev/null +++ b/SPECS/kernel/0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf @@ -0,0 +1,78 @@ +From f14332ef9553da5ad187f29c0a4f49bd73622dbb Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Fri, 21 Nov 2025 14:47:18 -0800 +Subject: [PATCH 11/13] perf/x86/intel/uncore: Update DMR uncore constraints + preliminarily + +Update event constraints base on the latest DMR uncore event list. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore_snbep.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c +index fac2be780276d..3f96fcc8562b7 100644 +--- a/arch/x86/events/intel/uncore_snbep.c ++++ b/arch/x86/events/intel/uncore_snbep.c +@@ -6660,10 +6660,19 @@ static const struct attribute_group dmr_cxlcm_uncore_format_group = { + .attrs = dmr_cxlcm_uncore_formats_attr, + }; + ++static struct event_constraint dmr_uncore_cxlcm_constraints[] = { ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x1, 0x24, 0x0f), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x41, 0x41, 0xf0), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x50, 0x5e, 0xf0), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x60, 0x61, 0xf0), ++ EVENT_CONSTRAINT_END ++}; ++ + static struct intel_uncore_type dmr_uncore_cxlcm = { + .name = "cxlcm", + .event_mask = GENERIC_PMON_RAW_EVENT_MASK, + .event_mask_ext = DMR_CXLCM_EVENT_MASK_EXT, ++ .constraints = dmr_uncore_cxlcm_constraints, + .format_group = &dmr_cxlcm_uncore_format_group, + .attr_update = uncore_alias_groups, + }; +@@ -6675,9 +6684,20 @@ static struct intel_uncore_type dmr_uncore_hamvf = { + .attr_update = uncore_alias_groups, + }; + ++static struct event_constraint dmr_uncore_cbo_constraints[] = { ++ UNCORE_EVENT_CONSTRAINT(0x11, 0x1), ++ UNCORE_EVENT_CONSTRAINT_RANGE(0x19, 0x1a, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x1f, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x21, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x25, 0x1), ++ UNCORE_EVENT_CONSTRAINT(0x36, 0x1), ++ EVENT_CONSTRAINT_END ++}; ++ + static struct intel_uncore_type dmr_uncore_cbo = { + .name = "cbo", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .constraints = dmr_uncore_cbo_constraints, + .format_group = &dmr_sca_uncore_format_group, + .attr_update = uncore_alias_groups, + }; +@@ -6711,9 +6731,16 @@ static struct intel_uncore_type dmr_uncore_dda = { + .attr_update = uncore_alias_groups, + }; + ++static struct event_constraint dmr_uncore_sbo_constraints[] = { ++ UNCORE_EVENT_CONSTRAINT(0x1f, 0x01), ++ UNCORE_EVENT_CONSTRAINT(0x25, 0x01), ++ EVENT_CONSTRAINT_END ++}; ++ + static struct intel_uncore_type dmr_uncore_sbo = { + .name = "sbo", + .event_mask_ext = DMR_HAMVF_EVENT_MASK_EXT, ++ .constraints = dmr_uncore_sbo_constraints, + .format_group = &dmr_sca_uncore_format_group, + .attr_update = uncore_alias_groups, + }; +-- +2.43.0 + diff --git a/SPECS/kernel/0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal b/SPECS/kernel/0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal deleted file mode 100644 index d6af217b1..000000000 --- a/SPECS/kernel/0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal +++ /dev/null @@ -1,33 +0,0 @@ -From 3c8cf98b0c1d91cbe3df0c34fa640ab5a03977da Mon Sep 17 00:00:00 2001 -From: Srinivas Pandruvada -Date: Mon, 10 Nov 2025 15:50:41 -0800 -Subject: [PATCH 11/11] platform/x86/intel/hid: Add Nova Lake support -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add ACPI ID for Nova Lake. - -Signed-off-by: Srinivas Pandruvada -Link: https://patch.msgid.link/20251110235041.123685-1-srinivas.pandruvada@linux.intel.com -Reviewed-by: Ilpo Järvinen -Signed-off-by: Ilpo Järvinen ---- - drivers/platform/x86/intel/hid.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c -index f25a427cccda..9c07a7faf18f 100644 ---- a/drivers/platform/x86/intel/hid.c -+++ b/drivers/platform/x86/intel/hid.c -@@ -55,6 +55,7 @@ static const struct acpi_device_id intel_hid_ids[] = { - { "INTC10CB" }, - { "INTC10CC" }, - { "INTC10F1" }, -+ { "INTC10F2" }, - { } - }; - MODULE_DEVICE_TABLE(acpi, intel_hid_ids); --- -2.43.0 - diff --git a/SPECS/kernel/0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo b/SPECS/kernel/0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo new file mode 100644 index 000000000..c81f3896c --- /dev/null +++ b/SPECS/kernel/0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo @@ -0,0 +1,126 @@ +From 585bad8fa06a1be2c4016ed6ea7cfdba37a4f69a Mon Sep 17 00:00:00 2001 +From: Kaushlendra Kumar +Date: Fri, 3 Oct 2025 16:33:19 +0530 +Subject: [PATCH 11/21] tools/power x86_energy_perf_policy: Add Android MSR + device support + +Add support for Android MSR device paths which use /dev/msrN format +instead of the standard Linux /dev/cpu/N/msr format. The tool now +probes both path formats at startup and uses the appropriate one. + +This enables x86_energy_perf_policy to work on Android systems where +MSR devices follow a different naming convention while maintaining +full compatibility with standard Linux systems. + +Signed-off-by: Kaushlendra Kumar +Signed-off-by: Len Brown +--- + .../x86_energy_perf_policy.c | 54 ++++++++++++++++--- + 1 file changed, 46 insertions(+), 8 deletions(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index 884a4c746f32e..5301efc741cee 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -95,6 +95,9 @@ unsigned int bdx_highest_ratio; + #define PATH_TO_CPU "/sys/devices/system/cpu/" + #define SYSFS_PATH_MAX 255 + ++/* keep Default as a linux path */ ++static int use_android_msr_path; ++ + /* + * maintain compatibility with original implementation, but don't document it: + */ +@@ -678,16 +681,41 @@ void err_on_hypervisor(void) + "not supported on this virtual machine"); + } + ++static void probe_msr_path_format(void) ++{ ++ struct stat sb; ++ char test_path[32]; ++ ++ /* Test standard Linux path */ ++ sprintf(test_path, "/dev/cpu/%d/msr", base_cpu); ++ if (stat(test_path, &sb) == 0) { ++ use_android_msr_path = 0; ++ return; ++ } ++ ++ /* Test Android-style path */ ++ sprintf(test_path, "/dev/msr%d", base_cpu); ++ if (stat(test_path, &sb) == 0) { ++ use_android_msr_path = 1; ++ return; ++ } ++ ++ /* If neither exists, keep the default Linux format */ ++ use_android_msr_path = 0; ++} ++ + int get_msr(int cpu, int offset, unsigned long long *msr) + { + int retval; + char pathname[32]; + int fd; + +- sprintf(pathname, "/dev/cpu/%d/msr", cpu); ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDONLY); + if (fd < 0) +- err(-1, "%s open failed, try chown or chmod +r /dev/cpu/*/msr, or run as root", pathname); ++ err(-1, "%s open failed, try chown or chmod +r %s, or run as root", ++ pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); ++ + + retval = pread(fd, msr, sizeof(*msr), offset); + if (retval != sizeof(*msr)) { +@@ -708,10 +736,11 @@ int put_msr(int cpu, int offset, unsigned long long new_msr) + int retval; + int fd; + +- sprintf(pathname, "/dev/cpu/%d/msr", cpu); ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDWR); + if (fd < 0) +- err(-1, "%s open failed, try chown or chmod +r /dev/cpu/*/msr, or run as root", pathname); ++ err(-1, "%s open failed, try chown or chmod +r %s, or run as root", ++ pathname, use_android_msr_path ? "/dev/msr*" : "/dev/cpu/*/msr"); + + retval = pwrite(fd, &new_msr, sizeof(new_msr), offset); + if (retval != sizeof(new_msr)) +@@ -1427,10 +1456,15 @@ void probe_dev_msr(void) + struct stat sb; + char pathname[32]; + +- sprintf(pathname, "/dev/cpu/%d/msr", base_cpu); +- if (stat(pathname, &sb)) +- if (system("/sbin/modprobe msr > /dev/null 2>&1")) +- err(-5, "no /dev/cpu/0/msr, Try \"# modprobe msr\" "); ++ sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu); ++ if (stat(pathname, &sb)) { ++ if (system("/sbin/modprobe msr > /dev/null 2>&1")) { ++ if (use_android_msr_path) ++ err(-5, "no /dev/msr0, Try \"# modprobe msr\" "); ++ else ++ err(-5, "no /dev/cpu/0/msr, Try \"# modprobe msr\" "); ++ } ++ } + } + + static void get_cpuid_or_exit(unsigned int leaf, +@@ -1547,6 +1581,10 @@ void parse_cpuid(void) + int main(int argc, char **argv) + { + set_base_cpu(); ++ ++ /* probe MSR path */ ++ probe_msr_path_format(); ++ + probe_dev_msr(); + init_data_structures(); + +-- +2.43.0 + diff --git a/SPECS/kernel/0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac b/SPECS/kernel/0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac deleted file mode 100644 index 039571548..000000000 --- a/SPECS/kernel/0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac +++ /dev/null @@ -1,104 +0,0 @@ -From 1a38a5e8463321de4e625950e55003cd06e20082 Mon Sep 17 00:00:00 2001 -From: Qiuxu Zhuo -Date: Fri, 10 May 2019 23:04:28 +0800 -Subject: [PATCH 12/13] EDAC/igen6: Add registration APIs for In-Band ECC error - notification - -The igen6_edac driver is the root to capture the In-Band ECC error -event. There are some external modules which want to be notified about -the In-Band ECC errors for specific error handling. So add the -registration APIs for those external modules for the In-Band ECC errors. - -Signed-off-by: Qiuxu Zhuo ---- - drivers/edac/igen6_edac.c | 23 +++++++++++++++++++++++ - drivers/edac/igen6_edac.h | 22 ++++++++++++++++++++++ - 2 files changed, 45 insertions(+) - create mode 100644 drivers/edac/igen6_edac.h - -diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c -index 2fc59f9eed69..3ec58cae8d2b 100644 ---- a/drivers/edac/igen6_edac.c -+++ b/drivers/edac/igen6_edac.c -@@ -26,6 +26,7 @@ - - #include "edac_mc.h" - #include "edac_module.h" -+#include "igen6_edac.h" - - #define IGEN6_REVISION "v2.5.1" - -@@ -641,6 +642,20 @@ static struct pci_device_id igen6_pci_tbl[] = { - }; - MODULE_DEVICE_TABLE(pci, igen6_pci_tbl); - -+static BLOCKING_NOTIFIER_HEAD(ibecc_err_handler_chain); -+ -+int ibecc_err_register_notifer(struct notifier_block *nb) -+{ -+ return blocking_notifier_chain_register(&ibecc_err_handler_chain, nb); -+} -+EXPORT_SYMBOL_GPL(ibecc_err_register_notifer); -+ -+int ibecc_err_unregister_notifer(struct notifier_block *nb) -+{ -+ return blocking_notifier_chain_unregister(&ibecc_err_handler_chain, nb); -+} -+EXPORT_SYMBOL_GPL(ibecc_err_unregister_notifer); -+ - static enum dev_type get_width(int dimm_l, u32 mad_dimm) - { - u32 w = dimm_l ? MAD_DIMM_CH_DLW(mad_dimm) : -@@ -758,6 +773,7 @@ static void igen6_output_error(struct decoded_addr *res, - enum hw_event_mc_err_type type = ecclog & ECC_ERROR_LOG_UE ? - HW_EVENT_ERR_UNCORRECTED : - HW_EVENT_ERR_CORRECTED; -+ struct ibecc_err_info e; - - edac_mc_handle_error(type, mci, 1, - res->sys_addr >> PAGE_SHIFT, -@@ -765,6 +781,13 @@ static void igen6_output_error(struct decoded_addr *res, - ECC_ERROR_LOG_SYND(ecclog), - res->channel_idx, res->sub_channel_idx, - -1, "", ""); -+ -+ /* Notify other handlers for further IBECC error handling */ -+ memset(&e, 0, sizeof(e)); -+ e.type = type; -+ e.sys_addr = res->sys_addr; -+ e.ecc_log = ecclog; -+ blocking_notifier_call_chain(&ibecc_err_handler_chain, 0, &e); - } - - static struct gen_pool *ecclog_gen_pool_create(void) -diff --git a/drivers/edac/igen6_edac.h b/drivers/edac/igen6_edac.h -new file mode 100644 -index 000000000000..ca447593bdf8 ---- /dev/null -+++ b/drivers/edac/igen6_edac.h -@@ -0,0 +1,22 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Registration for IBECC error notification -+ * Copyright (C) 2020 Intel Corporation -+ */ -+ -+#ifndef _IGEN6_EDAC_H -+#define _IGEN6_EDAC_H -+ -+#include -+#include -+ -+struct ibecc_err_info { -+ enum hw_event_mc_err_type type; -+ u64 sys_addr; -+ u64 ecc_log; -+}; -+ -+int ibecc_err_register_notifer(struct notifier_block *nb); -+int ibecc_err_unregister_notifer(struct notifier_block *nb); -+ -+#endif /* _IGEN6_EDAC_H */ --- -2.43.0 - diff --git a/SPECS/kernel/0012-KVM-VMX-Virtualize-FRED-event_data.nmi b/SPECS/kernel/0012-KVM-VMX-Virtualize-FRED-event_data.nmi new file mode 100644 index 000000000..cd0a36a54 --- /dev/null +++ b/SPECS/kernel/0012-KVM-VMX-Virtualize-FRED-event_data.nmi @@ -0,0 +1,224 @@ +From dc37448492c27bcfa1f0e6c2924988e5795a948c Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 13 Jun 2024 09:07:46 -0700 +Subject: [PATCH 12/44] KVM: VMX: Virtualize FRED event_data + +Set injected-event data when injecting a #PF, #DB, or #NM caused +by extended feature disable using FRED event delivery, and save +original-event data for being used as injected-event data. + +Unlike IDT using some extra CPU register as part of an event +context, e.g., %cr2 for #PF, FRED saves a complete event context +in its stack frame, e.g., FRED saves the faulting linear address +of a #PF into the event data field defined in its stack frame. + +Thus a new VMX control field called injected-event data is added +to provide the event data that will be pushed into a FRED stack +frame for VM entries that inject an event using FRED event delivery. +In addition, a new VM exit information field called original-event +data is added to store the event data that would have saved into a +FRED stack frame for VM exits that occur during FRED event delivery. +After such a VM exit is handled to allow the original-event to be +delivered, the data in the original-event data VMCS field needs to +be set into the injected-event data VMCS field for the injection of +the original event. + +Signed-off-by: Xin Li +[ Sean: reworked event data injection for nested ] +Signed-off-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v3: +* Rework event data injection for nested (Chao Gao & Sean Christopherson). + +Changes in v2: +* Document event data should be equal to CR2/DR6/IA32_XFD_ERR instead + of using WARN_ON() (Chao Gao). +* Zero event data if a #NM was not caused by extended feature disable + (Chao Gao). +--- + arch/x86/include/asm/kvm_host.h | 3 ++- + arch/x86/include/asm/vmx.h | 4 ++++ + arch/x86/kvm/svm/svm.c | 2 +- + arch/x86/kvm/vmx/vmx.c | 22 ++++++++++++++++++---- + arch/x86/kvm/x86.c | 16 +++++++++++++++- + 5 files changed, 40 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 43a18e265289b..550a8716a2272 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -760,6 +760,7 @@ struct kvm_queued_exception { + u32 error_code; + unsigned long payload; + bool has_payload; ++ u64 event_data; + }; + + /* +@@ -2230,7 +2231,7 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); + void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); + void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code); ++ bool has_error_code, u32 error_code, u64 event_data); + void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); + void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault); +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index 6f8b8947c60cd..539af190ad3e5 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -269,8 +269,12 @@ enum vmcs_field { + PID_POINTER_TABLE_HIGH = 0x00002043, + SECONDARY_VM_EXIT_CONTROLS = 0x00002044, + SECONDARY_VM_EXIT_CONTROLS_HIGH = 0x00002045, ++ INJECTED_EVENT_DATA = 0x00002052, ++ INJECTED_EVENT_DATA_HIGH = 0x00002053, + GUEST_PHYSICAL_ADDRESS = 0x00002400, + GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, ++ ORIGINAL_EVENT_DATA = 0x00002404, ++ ORIGINAL_EVENT_DATA_HIGH = 0x00002405, + VMCS_LINK_POINTER = 0x00002800, + VMCS_LINK_POINTER_HIGH = 0x00002801, + GUEST_IA32_DEBUGCTL = 0x00002802, +diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c +index 9d29b2e7e855d..147b644488f21 100644 +--- a/arch/x86/kvm/svm/svm.c ++++ b/arch/x86/kvm/svm/svm.c +@@ -4095,7 +4095,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) + + kvm_requeue_exception(vcpu, vector, + exitintinfo & SVM_EXITINTINFO_VALID_ERR, +- error_code); ++ error_code, 0); + break; + } + case SVM_EXITINTINFO_TYPE_INTR: +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index b51f2c4fcdb8c..5cefe7024ac6e 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1860,6 +1860,9 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) + + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); + ++ if (is_fred_enabled(vcpu)) ++ vmcs_write64(INJECTED_EVENT_DATA, ex->event_data); ++ + vmx_clear_hlt(vcpu); + } + +@@ -7288,7 +7291,8 @@ static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) + static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + u32 idt_vectoring_info, + int instr_len_field, +- int error_code_field) ++ int error_code_field, ++ int event_data_field) + { + u8 vector; + int type; +@@ -7323,13 +7327,17 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + fallthrough; + case INTR_TYPE_HARD_EXCEPTION: { + u32 error_code = 0; ++ u64 event_data = 0; + + if (idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK) + error_code = vmcs_read32(error_code_field); ++ if (is_fred_enabled(vcpu)) ++ event_data = vmcs_read64(event_data_field); + + kvm_requeue_exception(vcpu, vector, + idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, +- error_code); ++ error_code, ++ event_data); + break; + } + case INTR_TYPE_SOFT_INTR: +@@ -7347,7 +7355,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx) + { + __vmx_complete_interrupts(&vmx->vcpu, vmx->idt_vectoring_info, + VM_EXIT_INSTRUCTION_LEN, +- IDT_VECTORING_ERROR_CODE); ++ IDT_VECTORING_ERROR_CODE, ++ ORIGINAL_EVENT_DATA); + } + + void vmx_cancel_injection(struct kvm_vcpu *vcpu) +@@ -7355,7 +7364,8 @@ void vmx_cancel_injection(struct kvm_vcpu *vcpu) + __vmx_complete_interrupts(vcpu, + vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), + VM_ENTRY_INSTRUCTION_LEN, +- VM_ENTRY_EXCEPTION_ERROR_CODE); ++ VM_ENTRY_EXCEPTION_ERROR_CODE, ++ INJECTED_EVENT_DATA); + + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); + } +@@ -7509,6 +7519,10 @@ static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu, + + vmx_disable_fb_clear(vmx); + ++ /* ++ * Note, even though FRED delivers the faulting linear address via the ++ * event data field on the stack, CR2 is still updated. ++ */ + if (vcpu->arch.cr2 != native_read_cr2()) + native_write_cr2(vcpu->arch.cr2); + +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index af7543e7c8063..71b651d4567ff 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -815,9 +815,22 @@ void kvm_deliver_exception_payload(struct kvm_vcpu *vcpu, + * breakpoint), it is reserved and must be zero in DR6. + */ + vcpu->arch.dr6 &= ~BIT(12); ++ ++ /* ++ * FRED #DB event data matches DR6, but follows the polarity of ++ * VMX's pending debug exceptions, not DR6. ++ */ ++ ex->event_data = ex->payload & ~BIT(12); ++ break; ++ case NM_VECTOR: ++ ex->event_data = ex->payload; + break; + case PF_VECTOR: + vcpu->arch.cr2 = ex->payload; ++ ex->event_data = ex->payload; ++ break; ++ default: ++ ex->event_data = 0; + break; + } + +@@ -925,7 +938,7 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, + } + + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code) ++ bool has_error_code, u32 error_code, u64 event_data) + { + + /* +@@ -950,6 +963,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.error_code = error_code; + vcpu->arch.exception.has_payload = false; + vcpu->arch.exception.payload = 0; ++ vcpu->arch.exception.event_data = event_data; + } + EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception); + +-- +2.43.0 + diff --git a/SPECS/kernel/0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi b/SPECS/kernel/0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi deleted file mode 100644 index 917bc7561..000000000 --- a/SPECS/kernel/0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi +++ /dev/null @@ -1,206 +0,0 @@ -From 45ca6a4965644bb66e47eaad8c677d32d69aec6e Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 13 Jun 2024 09:48:43 -0700 -Subject: [PATCH 12/44] KVM: VMX: Virtualize FRED nested exception tracking - -Set the VMX nested exception bit in VM-entry interruption information -field when injecting a nested exception using FRED event delivery to -ensure: - 1) A nested exception is injected on a correct stack level. - 2) The nested bit defined in FRED stack frame is set. - -The event stack level used by FRED event delivery depends on whether -the event was a nested exception encountered during delivery of an -earlier event, because a nested exception is "regarded" as happening -on ring 0. E.g., when #PF is configured to use stack level 1 in -IA32_FRED_STKLVLS MSR: - - nested #PF will be delivered on the stack pointed by IA32_FRED_RSP1 - MSR when encountered in ring 3 and ring 0. - - normal #PF will be delivered on the stack pointed by IA32_FRED_RSP0 - MSR when encountered in ring 3. - -The VMX nested-exception support ensures a correct event stack level is -chosen when a VM entry injects a nested exception. - -Signed-off-by: Xin Li -[ Sean: reworked kvm_requeue_exception() to simply the code changes ] -Signed-off-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Move the check is_fred_enable() from kvm_multiple_exception() to - vmx_inject_exception() thus avoid bleeding FRED details into - kvm_multiple_exception() (Chao Gao). - -Change in v3: -* Rework kvm_requeue_exception() to simply the code changes (Sean - Christopherson). - -Change in v2: -* Set the nested flag when there is an original interrupt (Chao Gao). ---- - arch/x86/include/asm/kvm_host.h | 4 +++- - arch/x86/include/asm/vmx.h | 5 ++++- - arch/x86/kvm/svm/svm.c | 2 +- - arch/x86/kvm/vmx/vmx.c | 6 +++++- - arch/x86/kvm/x86.c | 13 ++++++++++++- - arch/x86/kvm/x86.h | 1 + - 6 files changed, 26 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 40d2c5e1b7e7..0d4cdb704f97 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -761,6 +761,7 @@ struct kvm_queued_exception { - u32 error_code; - unsigned long payload; - bool has_payload; -+ bool nested; - u64 event_data; - }; - -@@ -2209,7 +2210,8 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); - void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); - void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code, u64 event_data); -+ bool has_error_code, u32 error_code, bool nested, -+ u64 event_data); - void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); - void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, - struct x86_exception *fault); -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index 613deb8cfb78..f766791367d2 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -141,6 +141,7 @@ - #define VMX_BASIC_INOUT BIT_ULL(54) - #define VMX_BASIC_TRUE_CTLS BIT_ULL(55) - #define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56) -+#define VMX_BASIC_NESTED_EXCEPTION BIT_ULL(58) - - static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic) - { -@@ -443,13 +444,15 @@ enum vmcs_field { - #define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */ - #define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */ - #define INTR_INFO_UNBLOCK_NMI 0x1000 /* 12 */ -+#define INTR_INFO_NESTED_EXCEPTION_MASK 0x2000 /* 13 */ - #define INTR_INFO_VALID_MASK 0x80000000 /* 31 */ --#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000 -+#define INTR_INFO_RESVD_BITS_MASK 0x7fffd000 - - #define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK - #define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK - #define VECTORING_INFO_DELIVER_CODE_MASK INTR_INFO_DELIVER_CODE_MASK - #define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK -+#define VECTORING_INFO_NESTED_EXCEPTION_MASK INTR_INFO_NESTED_EXCEPTION_MASK - - #define INTR_TYPE_EXT_INTR (EVENT_TYPE_EXTINT << 8) /* external interrupt */ - #define INTR_TYPE_RESERVED (EVENT_TYPE_RESERVED << 8) /* reserved */ -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 8ee9f66a34f8..5408f84fa263 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4187,7 +4187,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) - - kvm_requeue_exception(vcpu, vector, - exitintinfo & SVM_EXITINTINFO_VALID_ERR, -- error_code, 0); -+ error_code, false, 0); - break; - } - case SVM_EXITINTINFO_TYPE_INTR: -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 89841d2f3e45..f225778115dc 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1859,8 +1859,11 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) - vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, - vmx->vcpu.arch.event_exit_inst_len); - intr_info |= INTR_TYPE_SOFT_EXCEPTION; -- } else -+ } else { - intr_info |= INTR_TYPE_HARD_EXCEPTION; -+ if (ex->nested && is_fred_enabled(vcpu)) -+ intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; -+ } - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); - -@@ -7358,6 +7361,7 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - kvm_requeue_exception(vcpu, vector, - idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, - error_code, -+ idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK, - event_data); - break; - } -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 090f9fc0363f..6029125a518d 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -874,6 +874,10 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.pending = true; - vcpu->arch.exception.injected = false; - -+ vcpu->arch.exception.nested = vcpu->arch.exception.nested || -+ vcpu->arch.nmi_injected || -+ vcpu->arch.interrupt.injected; -+ - vcpu->arch.exception.has_error_code = has_error; - vcpu->arch.exception.vector = nr; - vcpu->arch.exception.error_code = error_code; -@@ -903,8 +907,13 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.injected = false; - vcpu->arch.exception.pending = false; - -+ /* #DF is NOT a nested event, per its definition. */ -+ vcpu->arch.exception.nested = false; -+ - kvm_queue_exception_e(vcpu, DF_VECTOR, 0); - } else { -+ vcpu->arch.exception.nested = true; -+ - /* replace previous exception with a new one in a hope - that instruction re-execution will regenerate lost - exception */ -@@ -933,7 +942,8 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, - } - - void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, -- bool has_error_code, u32 error_code, u64 event_data) -+ bool has_error_code, u32 error_code, bool nested, -+ u64 event_data) - { - - /* -@@ -958,6 +968,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, - vcpu->arch.exception.error_code = error_code; - vcpu->arch.exception.has_payload = false; - vcpu->arch.exception.payload = 0; -+ vcpu->arch.exception.nested = nested; - vcpu->arch.exception.event_data = event_data; - } - EXPORT_SYMBOL_GPL(kvm_requeue_exception); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index b12749a2a67c..25c2d7173b89 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -198,6 +198,7 @@ static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu) - { - vcpu->arch.exception.pending = false; - vcpu->arch.exception.injected = false; -+ vcpu->arch.exception.nested = false; - vcpu->arch.exception_vmexit.pending = false; - } - --- -2.43.0 - diff --git a/SPECS/kernel/0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet b/SPECS/kernel/0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet deleted file mode 100644 index 17f5254b6..000000000 --- a/SPECS/kernel/0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet +++ /dev/null @@ -1,56 +0,0 @@ -From 2bc8dfbeccd96bc388c97ea050a6c4d524293f6e Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:42 -0700 -Subject: [PATCH 12/24] KVM: x86: Report KVM supported CET MSRs as to-be-saved - -Add CET MSRs to the list of MSRs reported to userspace if the feature, -i.e. IBT or SHSTK, associated with the MSRs is supported by KVM. - -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/x86.c | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index fbe3d1bea657..cbefc0228ba5 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -345,6 +345,10 @@ static const u32 msrs_to_save_base[] = { - MSR_IA32_UMWAIT_CONTROL, - - MSR_IA32_XFD, MSR_IA32_XFD_ERR, MSR_IA32_XSS, -+ -+ MSR_IA32_U_CET, MSR_IA32_S_CET, -+ MSR_IA32_PL0_SSP, MSR_IA32_PL1_SSP, MSR_IA32_PL2_SSP, -+ MSR_IA32_PL3_SSP, MSR_IA32_INT_SSP_TAB, - }; - - static const u32 msrs_to_save_pmu[] = { -@@ -7472,6 +7476,20 @@ static void kvm_probe_msr_to_save(u32 msr_index) - if (!kvm_caps.supported_xss) - return; - break; -+ case MSR_IA32_U_CET: -+ case MSR_IA32_S_CET: -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && -+ !kvm_cpu_cap_has(X86_FEATURE_IBT)) -+ return; -+ break; -+ case MSR_IA32_INT_SSP_TAB: -+ if (!kvm_cpu_cap_has(X86_FEATURE_LM)) -+ return; -+ fallthrough; -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK)) -+ return; -+ break; - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel/0012-comedi-check-device-s-attached-status-in-compat-ioct.patch b/SPECS/kernel/0012-comedi-check-device-s-attached-status-in-compat-ioct.patch deleted file mode 100644 index 107d3287d..000000000 --- a/SPECS/kernel/0012-comedi-check-device-s-attached-status-in-compat-ioct.patch +++ /dev/null @@ -1,147 +0,0 @@ -From b6d371e15f5bc91054b76f685309c33bfb5c7335 Mon Sep 17 00:00:00 2001 -From: Nikita Zhandarovich -Date: Thu, 23 Oct 2025 16:22:32 +0300 -Subject: [PATCH 12/16] comedi: check device's attached status in compat ioctls - -Syzbot identified an issue [1] that crashes kernel, seemingly due to -unexistent callback dev->get_valid_routes(). By all means, this should -not occur as said callback must always be set to -get_zero_valid_routes() in __comedi_device_postconfig(). - -As the crash seems to appear exclusively in i386 kernels, at least, -judging from [1] reports, the blame lies with compat versions -of standard IOCTL handlers. Several of them are modified and -do not use comedi_unlocked_ioctl(). While functionality of these -ioctls essentially copy their original versions, they do not -have required sanity check for device's attached status. This, -in turn, leads to a possibility of calling select IOCTLs on a -device that has not been properly setup, even via COMEDI_DEVCONFIG. - -Doing so on unconfigured devices means that several crucial steps -are missed, for instance, specifying dev->get_valid_routes() -callback. - -Fix this somewhat crudely by ensuring device's attached status before -performing any ioctls, improving logic consistency between modern -and compat functions. - -[1] Syzbot report: -BUG: kernel NULL pointer dereference, address: 0000000000000000 -... -CR2: ffffffffffffffd6 CR3: 000000006c717000 CR4: 0000000000352ef0 -Call Trace: - - get_valid_routes drivers/comedi/comedi_fops.c:1322 [inline] - parse_insn+0x78c/0x1970 drivers/comedi/comedi_fops.c:1401 - do_insnlist_ioctl+0x272/0x700 drivers/comedi/comedi_fops.c:1594 - compat_insnlist drivers/comedi/comedi_fops.c:3208 [inline] - comedi_compat_ioctl+0x810/0x990 drivers/comedi/comedi_fops.c:3273 - __do_compat_sys_ioctl fs/ioctl.c:695 [inline] - __se_compat_sys_ioctl fs/ioctl.c:638 [inline] - __ia32_compat_sys_ioctl+0x242/0x370 fs/ioctl.c:638 - do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:83 [inline] -... - -Reported-by: syzbot+ab8008c24e84adee93ff@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=ab8008c24e84adee93ff -Fixes: 3fbfd2223a27 ("comedi: get rid of compat_alloc_user_space() mess in COMEDI_CHANINFO compat") -Cc: stable -Reviewed-by: Ian Abbott -Signed-off-by: Nikita Zhandarovich -Link: https://patch.msgid.link/20251023132234.395794-1-n.zhandarovich@fintech.ru -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/comedi_fops.c | 42 ++++++++++++++++++++++++++++++------ - 1 file changed, 36 insertions(+), 6 deletions(-) - -diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c -index 7e2f2b1a1c36..b2e62e04afd9 100644 ---- a/drivers/comedi/comedi_fops.c -+++ b/drivers/comedi/comedi_fops.c -@@ -3023,7 +3023,12 @@ static int compat_chaninfo(struct file *file, unsigned long arg) - chaninfo.rangelist = compat_ptr(chaninfo32.rangelist); - - mutex_lock(&dev->mutex); -- err = do_chaninfo_ioctl(dev, &chaninfo); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ err = -ENODEV; -+ } else { -+ err = do_chaninfo_ioctl(dev, &chaninfo); -+ } - mutex_unlock(&dev->mutex); - return err; - } -@@ -3044,7 +3049,12 @@ static int compat_rangeinfo(struct file *file, unsigned long arg) - rangeinfo.range_ptr = compat_ptr(rangeinfo32.range_ptr); - - mutex_lock(&dev->mutex); -- err = do_rangeinfo_ioctl(dev, &rangeinfo); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ err = -ENODEV; -+ } else { -+ err = do_rangeinfo_ioctl(dev, &rangeinfo); -+ } - mutex_unlock(&dev->mutex); - return err; - } -@@ -3120,7 +3130,12 @@ static int compat_cmd(struct file *file, unsigned long arg) - return rc; - - mutex_lock(&dev->mutex); -- rc = do_cmd_ioctl(dev, &cmd, ©, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_cmd_ioctl(dev, &cmd, ©, file); -+ } - mutex_unlock(&dev->mutex); - if (copy) { - /* Special case: copy cmd back to user. */ -@@ -3145,7 +3160,12 @@ static int compat_cmdtest(struct file *file, unsigned long arg) - return rc; - - mutex_lock(&dev->mutex); -- rc = do_cmdtest_ioctl(dev, &cmd, ©, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_cmdtest_ioctl(dev, &cmd, ©, file); -+ } - mutex_unlock(&dev->mutex); - if (copy) { - err = put_compat_cmd(compat_ptr(arg), &cmd); -@@ -3205,7 +3225,12 @@ static int compat_insnlist(struct file *file, unsigned long arg) - } - - mutex_lock(&dev->mutex); -- rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file); -+ } - mutex_unlock(&dev->mutex); - kfree(insns); - return rc; -@@ -3224,7 +3249,12 @@ static int compat_insn(struct file *file, unsigned long arg) - return rc; - - mutex_lock(&dev->mutex); -- rc = do_insn_ioctl(dev, &insn, file); -+ if (!dev->attached) { -+ dev_dbg(dev->class_dev, "no driver attached\n"); -+ rc = -ENODEV; -+ } else { -+ rc = do_insn_ioctl(dev, &insn, file); -+ } - mutex_unlock(&dev->mutex); - return rc; - } --- -2.43.0 - diff --git a/SPECS/kernel/0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl b/SPECS/kernel/0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl new file mode 100644 index 000000000..c09bae034 --- /dev/null +++ b/SPECS/kernel/0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl @@ -0,0 +1,127 @@ +From 5e1834e3a4a2efe85b595f96a462230bf54c772d Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Sun, 16 Nov 2025 13:35:14 +0100 +Subject: [PATCH 12/17] cpuidle: governors: teo: Simplify intercepts-based + state lookup + +Simplify the loop looking up a candidate idle state in the case when an +intercept is likely to occur by adding a search for the state index limit +if the tick is stopped before it. + +First, call tick_nohz_tick_stopped() just once and if it returns true, +look for the shallowest state index below the current candidate one with +target residency at least equal to the tick period length. + +Next, simply look for a state that is not shallower than the one found +in the previous step and satisfies the intercepts majority condition (if +there are no such states, the shallowest state that is not shallower +than the one found in the previous step becomes the new candidate). + +Since teo_state_ok() has no callers any more after the above changes, +drop it. + +No intentional functional impact. + +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +[ rjw: Changelog clarification and code comment edit ] +Link: https://patch.msgid.link/2418792.ElGaqSPkdT@rafael.j.wysocki +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/governors/teo.c | 62 +++++++++------------------------ + 1 file changed, 16 insertions(+), 46 deletions(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index 85b5517067d16..bab186336bf4a 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -256,12 +256,6 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev) + } + } + +-static bool teo_state_ok(int i, struct cpuidle_driver *drv) +-{ +- return !tick_nohz_tick_stopped() || +- drv->states[i].target_residency_ns >= TICK_NSEC; +-} +- + /** + * teo_find_shallower_state - Find shallower idle state matching given duration. + * @drv: cpuidle driver containing state data. +@@ -383,7 +377,18 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + * better choice. + */ + if (2 * idx_intercept_sum > cpu_data->total - idx_hit_sum) { +- int first_suitable_idx = idx; ++ int min_idx = idx0; ++ ++ if (tick_nohz_tick_stopped()) { ++ /* ++ * Look for the shallowest idle state below the current ++ * candidate one whose target residency is at least ++ * equal to the tick period length. ++ */ ++ while (min_idx < idx && ++ drv->states[min_idx].target_residency_ns < TICK_NSEC) ++ min_idx++; ++ } + + /* + * Look for the deepest idle state whose target residency had +@@ -393,49 +398,14 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, + * Take the possible duration limitation present if the tick + * has been stopped already into account. + */ +- intercept_sum = 0; +- +- for (i = idx - 1; i >= 0; i--) { +- struct teo_bin *bin = &cpu_data->state_bins[i]; +- +- intercept_sum += bin->intercepts; +- +- if (2 * intercept_sum > idx_intercept_sum) { +- /* +- * Use the current state unless it is too +- * shallow or disabled, in which case take the +- * first enabled state that is deep enough. +- */ +- if (teo_state_ok(i, drv) && +- !dev->states_usage[i].disable) { +- idx = i; +- break; +- } +- idx = first_suitable_idx; +- break; +- } ++ for (i = idx - 1, intercept_sum = 0; i >= min_idx; i--) { ++ intercept_sum += cpu_data->state_bins[i].intercepts; + + if (dev->states_usage[i].disable) + continue; + +- if (teo_state_ok(i, drv)) { +- /* +- * The current state is deep enough, but still +- * there may be a better one. +- */ +- first_suitable_idx = i; +- continue; +- } +- +- /* +- * The current state is too shallow, so if no suitable +- * states other than the initial candidate have been +- * found, give up (the remaining states to check are +- * shallower still), but otherwise the first suitable +- * state other than the initial candidate may turn out +- * to be preferable. +- */ +- if (first_suitable_idx == idx) ++ idx = i; ++ if (2 * intercept_sum > idx_intercept_sum) + break; + } + } +-- +2.43.0 + diff --git a/SPECS/kernel/0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet b/SPECS/kernel/0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet new file mode 100644 index 000000000..7629ce038 --- /dev/null +++ b/SPECS/kernel/0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet @@ -0,0 +1,36 @@ +From cea39eca48e8b06d14799ad9d289adc27e03a144 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Fri, 23 Jul 2021 21:27:36 +0800 +Subject: [PATCH 12/14] igc: Enable trace for HW TX Timestamp AF_XDP ZC + +This is a temporary solution as it uses trace_printk as a means to +log tx timestamps. + +Future implementation should use xdp_frame's data_meta to let user +applications retrieve it directly. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc_main.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index cabdcacc4c923..571cc380de5ad 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -3214,6 +3214,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + + switch (tx_buffer->type) { + case IGC_TX_BUFFER_TYPE_XSK: ++#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) ++ /* Only use for RTCP KPI Measurement on Q2 */ ++ if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) ++ trace_printk("TX HW TS %lld\n", timestamp); ++#endif + xsk_frames++; + break; + case IGC_TX_BUFFER_TYPE_XDP: +-- +2.43.0 + diff --git a/SPECS/kernel/0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu b/SPECS/kernel/0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu deleted file mode 100644 index 2499a2c40..000000000 --- a/SPECS/kernel/0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu +++ /dev/null @@ -1,87 +0,0 @@ -From e2baf64f19d5e0b8d76986e1647ff0c894f570ee Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 14 Nov 2025 18:41:08 +0800 -Subject: [PATCH 12/21] media: ipu: Update firmware ABI version to - 1.2.1.20251215_224531 - -Signed-off-by: Hao Yao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h | 1 + - drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h | 11 +++++++---- - drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h | 2 +- - 3 files changed, 9 insertions(+), 5 deletions(-) - -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h -index a1519c4fe661..4ce304f54e4b 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h -@@ -153,6 +153,7 @@ enum ia_gofo_boot_state { - IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U, - IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U, - IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U, -+ IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_FAILURE = 0xDEAD1016U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U, - IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U, - IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U, -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -index 45db85eb13ec..dc63449e3bc1 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h -@@ -47,7 +47,6 @@ enum ipu7_insys_resp_type { - IPU_INSYS_RESP_TYPE_FRAME_EOF = 8, - IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9, - IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10, -- IPU_INSYS_RESP_TYPE_PWM_IRQ = 11, - N_IPU_INSYS_RESP_TYPE - }; - -@@ -201,7 +200,8 @@ enum ipu7_insys_mipi_dt_rename_mode { - enum ipu7_insys_output_link_dest { - IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0, - IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1, -- IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2 -+ IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2, -+ N_IPU_INSYS_OUTPUT_LINK_DEST - }; - - enum ipu7_insys_dpcm_type { -@@ -220,9 +220,12 @@ enum ipu7_insys_dpcm_predictor { - - enum ipu7_insys_send_queue_token_flag { - IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0, -- IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1 -+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1, -+ N_IPU_INSYS_SEND_QUEUE_TOKEN_FLAG - }; - -+#define IPU_INSYS_MIPI_FRAME_NUMBER_DONT_CARE UINT16_MAX -+ - #pragma pack(push, 1) - struct ipu7_insys_resolution { - u32 width; -@@ -359,7 +362,7 @@ struct ipu7_insys_resp { - u8 pin_id; - u8 frame_id; - u8 skip_frame; -- u8 pad[2]; -+ u16 mipi_fn; - }; - - struct ipu7_insys_resp_queue_token { -diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h -index 8a78dd0936df..1319f0eb6319 100644 ---- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h -+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h -@@ -217,7 +217,7 @@ struct ipu7_msg_task { - u8 frag_id; - u8 req_done_msg; - u8 req_done_irq; -- u8 reserved[1]; -+ u8 disable_save; - ipu7_msg_teb_t payload_reuse_bm; - ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS]; - }; --- -2.43.0 - diff --git a/SPECS/kernel/0012-net-phylink-Add-module_exit.ethernet b/SPECS/kernel/0012-net-phylink-Add-module_exit.ethernet new file mode 100644 index 000000000..f16496408 --- /dev/null +++ b/SPECS/kernel/0012-net-phylink-Add-module_exit.ethernet @@ -0,0 +1,39 @@ +From 096687e863b9acf6eeccc25e85c7d8527991eb0c Mon Sep 17 00:00:00 2001 +From: Choong Yong Liang +Date: Wed, 3 Jan 2024 14:59:29 +0800 +Subject: [PATCH 12/18] net: phylink: Add module_exit() + +In free_module(), if mod->init callback is defined but mod->exit callback +is not defined, it will assume the module cannot be removed and return +EBUSY. The module_exit() is missing from current phylink module drive +causing failure while unloading it. + +This patch introduces phylink_exit() for phylink module removal. + +Fixes: eca68a3c7d05 ("net: phylink: pass supported host PHY interface modes to phylib for SFP's PHYs") +Signed-off-by: Lai Peter Jun Ann +Signed-off-by: Gan, Yi Fang +Signed-off-by: Choong Yong Liang +--- + drivers/net/phy/phylink.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c +index 9182443082158..e9d0dcfa4c7b1 100644 +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -4285,5 +4285,11 @@ static int __init phylink_init(void) + + module_init(phylink_init); + ++static void __exit phylink_exit(void) ++{ ++} ++ ++module_exit(phylink_exit); ++ + MODULE_LICENSE("GPL v2"); + MODULE_DESCRIPTION("phylink models the MAC to optional PHY connection"); +-- +2.43.0 + diff --git a/SPECS/kernel/0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf b/SPECS/kernel/0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf new file mode 100644 index 000000000..d8c3a17c6 --- /dev/null +++ b/SPECS/kernel/0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf @@ -0,0 +1,60 @@ +From 7302a5d52e119f352b20bff0ea104d1c5f97aecb Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Mon, 17 Nov 2025 15:31:01 -0800 +Subject: [PATCH 12/13] perf pmu: Relax uncore wildcard matching to allow + numeric suffix + +Diamond Rapids introduces two types of PCIe related uncore PMUs: +"uncore_pcie4_*" and "uncore_pcie6_*". + +To ensure that generic PCIe events (e.g., UNC_PCIE_CLOCKTICKS) can match +and collect events from both PMU types, slightly relax the wildcard +matching logic in perf_pmu__match_wildcard(). + +This change allows a wildcard such as "pcie" to match PMU names that +include a numeric suffix, such as "pcie4_*" and "pcie6_*". + +Co-developed-by: Dapeng Mi +Signed-off-by: Dapeng Mi +Reviewed-by: Dapeng Mi +Signed-off-by: Zide Chen +--- + tools/perf/util/pmu.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c +index 3d1f975e8db9f..00cb72615621d 100644 +--- a/tools/perf/util/pmu.c ++++ b/tools/perf/util/pmu.c +@@ -905,6 +905,7 @@ static bool perf_pmu__match_wildcard(const char *pmu_name, const char *tok) + { + const char *p, *suffix; + bool has_hex = false; ++ bool has_underscore = false; + size_t tok_len = strlen(tok); + + /* Check start of pmu_name for equality. */ +@@ -915,13 +916,14 @@ static bool perf_pmu__match_wildcard(const char *pmu_name, const char *tok) + if (*p == 0) + return true; + +- if (*p == '_') { +- ++p; +- ++suffix; +- } +- +- /* Ensure we end in a number */ ++ /* Ensure we end in a number or a mix of number and "_". */ + while (1) { ++ if (!has_underscore && (*p == '_')) { ++ has_underscore = true; ++ ++p; ++ ++suffix; ++ } ++ + if (!isxdigit(*p)) + return false; + if (!has_hex) +-- +2.43.0 + diff --git a/SPECS/kernel/0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf b/SPECS/kernel/0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf deleted file mode 100644 index 5333e3779..000000000 --- a/SPECS/kernel/0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf +++ /dev/null @@ -1,44 +0,0 @@ -From 11bad8db8b8652abe061f50c7973691d58763802 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 25 Jan 2024 11:31:22 +0000 -Subject: [PATCH 012/100] perf/x86/intel: Print more information in - x86_pmu_show_pmu_cap() - -Adjust the PMU message output sequence and print more PMU information in -helper x86_pmu_show_pmu_cap(). - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 16 +++++++++------- - 1 file changed, 9 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 2007cfe1a0f5..24fae86f2682 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -2071,13 +2071,15 @@ static void _x86_pmu_read(struct perf_event *event) - - void x86_pmu_show_pmu_cap(struct pmu *pmu) - { -- pr_info("... version: %d\n", x86_pmu.version); -- pr_info("... bit width: %d\n", x86_pmu.cntval_bits); -- pr_info("... generic registers: %d\n", x86_pmu_num_counters(pmu)); -- pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask); -- pr_info("... max period: %016Lx\n", x86_pmu.max_period); -- pr_info("... fixed-purpose events: %d\n", x86_pmu_num_counters_fixed(pmu)); -- pr_info("... event mask: %016Lx\n", hybrid(pmu, intel_ctrl)); -+ pr_info("... version: %d\n", x86_pmu.version); -+ pr_info("... bit width: %d\n", x86_pmu.cntval_bits); -+ pr_info("... generic counters: %d\n", x86_pmu_num_counters(pmu)); -+ pr_info("... generic bitmap: %016Lx\n", hybrid(pmu, cntr_mask64)); -+ pr_info("... fixed-purpose counters: %d\n", x86_pmu_num_counters_fixed(pmu)); -+ pr_info("... fixed-purpose bitmap: %016Lx\n", hybrid(pmu, fixed_cntr_mask64)); -+ pr_info("... value mask: %016Lx\n", x86_pmu.cntval_mask); -+ pr_info("... max period: %016Lx\n", x86_pmu.max_period); -+ pr_info("... global_ctrl mask: %016Lx\n", hybrid(pmu, intel_ctrl)); - } - - static int __init init_hw_perf_events(void) --- -2.43.0 - diff --git a/SPECS/kernel/0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo b/SPECS/kernel/0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo new file mode 100644 index 000000000..0d3d0dbad --- /dev/null +++ b/SPECS/kernel/0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo @@ -0,0 +1,92 @@ +From 74b6b483cba4e31ffce21e1c5364a14d55dcc8f6 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 25 Nov 2025 11:47:04 -0500 +Subject: [PATCH 12/21] tools/power x86_energy_perf_policy: Simplify Android + MSR probe + +no functional change + +Signed-off-by: Len Brown +--- + .../x86_energy_perf_policy.c | 38 ++++++------------- + 1 file changed, 11 insertions(+), 27 deletions(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index 5301efc741cee..e68eaa9f7cd4d 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -95,7 +95,6 @@ unsigned int bdx_highest_ratio; + #define PATH_TO_CPU "/sys/devices/system/cpu/" + #define SYSFS_PATH_MAX 255 + +-/* keep Default as a linux path */ + static int use_android_msr_path; + + /* +@@ -681,29 +680,6 @@ void err_on_hypervisor(void) + "not supported on this virtual machine"); + } + +-static void probe_msr_path_format(void) +-{ +- struct stat sb; +- char test_path[32]; +- +- /* Test standard Linux path */ +- sprintf(test_path, "/dev/cpu/%d/msr", base_cpu); +- if (stat(test_path, &sb) == 0) { +- use_android_msr_path = 0; +- return; +- } +- +- /* Test Android-style path */ +- sprintf(test_path, "/dev/msr%d", base_cpu); +- if (stat(test_path, &sb) == 0) { +- use_android_msr_path = 1; +- return; +- } +- +- /* If neither exists, keep the default Linux format */ +- use_android_msr_path = 0; +-} +- + int get_msr(int cpu, int offset, unsigned long long *msr) + { + int retval; +@@ -1450,12 +1426,23 @@ void set_base_cpu(void) + err(-ENODEV, "No valid cpus found"); + } + ++static void probe_android_msr_path(void) ++{ ++ struct stat sb; ++ char test_path[32]; ++ ++ sprintf(test_path, "/dev/msr%d", base_cpu); ++ if (stat(test_path, &sb) == 0) ++ use_android_msr_path = 1; ++} + + void probe_dev_msr(void) + { + struct stat sb; + char pathname[32]; + ++ probe_android_msr_path(); ++ + sprintf(pathname, use_android_msr_path ? "/dev/msr%d" : "/dev/cpu/%d/msr", base_cpu); + if (stat(pathname, &sb)) { + if (system("/sbin/modprobe msr > /dev/null 2>&1")) { +@@ -1582,9 +1569,6 @@ int main(int argc, char **argv) + { + set_base_cpu(); + +- /* probe MSR path */ +- probe_msr_path_format(); +- + probe_dev_msr(); + init_data_structures(); + +-- +2.43.0 + diff --git a/SPECS/kernel/0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet b/SPECS/kernel/0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet deleted file mode 100644 index 1bcc082b8..000000000 --- a/SPECS/kernel/0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet +++ /dev/null @@ -1,111 +0,0 @@ -From ed3c1c22e1d76f87fdbc728415b59c472e7baab5 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:43 -0700 -Subject: [PATCH 13/24] KVM: VMX: Introduce CET VMCS fields and control bits - -Control-flow Enforcement Technology (CET) is a kind of CPU feature used -to prevent Return/CALL/Jump-Oriented Programming (ROP/COP/JOP) attacks. -It provides two sub-features(SHSTK,IBT) to defend against ROP/COP/JOP -style control-flow subversion attacks. - -Shadow Stack (SHSTK): - A shadow stack is a second stack used exclusively for control transfer - operations. The shadow stack is separate from the data/normal stack and - can be enabled individually in user and kernel mode. When shadow stack - is enabled, CALL pushes the return address on both the data and shadow - stack. RET pops the return address from both stacks and compares them. - If the return addresses from the two stacks do not match, the processor - generates a #CP. - -Indirect Branch Tracking (IBT): - IBT introduces instruction(ENDBRANCH)to mark valid target addresses of - indirect branches (CALL, JMP etc...). If an indirect branch is executed - and the next instruction is _not_ an ENDBRANCH, the processor generates - a #CP. These instruction behaves as a NOP on platforms that have no CET. - -Several new CET MSRs are defined to support CET: - MSR_IA32_{U,S}_CET: CET settings for {user,supervisor} CET respectively. - - MSR_IA32_PL{0,1,2,3}_SSP: SHSTK pointer linear address for CPL{0,1,2,3}. - - MSR_IA32_INT_SSP_TAB: Linear address of SHSTK pointer table, whose entry - is indexed by IST of interrupt gate desc. - -Two XSAVES state bits are introduced for CET: - IA32_XSS:[bit 11]: Control saving/restoring user mode CET states - IA32_XSS:[bit 12]: Control saving/restoring supervisor mode CET states. - -Six VMCS fields are introduced for CET: - {HOST,GUEST}_S_CET: Stores CET settings for kernel mode. - {HOST,GUEST}_SSP: Stores current active SSP. - {HOST,GUEST}_INTR_SSP_TABLE: Stores current active MSR_IA32_INT_SSP_TAB. - -On Intel platforms, two additional bits are defined in VM_EXIT and VM_ENTRY -control fields: -If VM_EXIT_LOAD_CET_STATE = 1, host CET states are loaded from following -VMCS fields at VM-Exit: - HOST_S_CET - HOST_SSP - HOST_INTR_SSP_TABLE - -If VM_ENTRY_LOAD_CET_STATE = 1, guest CET states are loaded from following -VMCS fields at VM-Entry: - GUEST_S_CET - GUEST_SSP - GUEST_INTR_SSP_TABLE - -Co-developed-by: Zhang Yi Z -Signed-off-by: Zhang Yi Z -Signed-off-by: Yang Weijiang -Reviewed-by: Chao Gao -Reviewed-by: Maxim Levitsky -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/vmx.h | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index af71666c3a37..6a3fb81e852a 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -106,6 +106,7 @@ - #define VM_EXIT_CLEAR_BNDCFGS 0x00800000 - #define VM_EXIT_PT_CONCEAL_PIP 0x01000000 - #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 -+#define VM_EXIT_LOAD_CET_STATE 0x10000000 - #define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 - - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff -@@ -120,6 +121,7 @@ - #define VM_ENTRY_LOAD_BNDCFGS 0x00010000 - #define VM_ENTRY_PT_CONCEAL_PIP 0x00020000 - #define VM_ENTRY_LOAD_IA32_RTIT_CTL 0x00040000 -+#define VM_ENTRY_LOAD_CET_STATE 0x00100000 - - #define VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR 0x000011ff - -@@ -370,6 +372,9 @@ enum vmcs_field { - GUEST_PENDING_DBG_EXCEPTIONS = 0x00006822, - GUEST_SYSENTER_ESP = 0x00006824, - GUEST_SYSENTER_EIP = 0x00006826, -+ GUEST_S_CET = 0x00006828, -+ GUEST_SSP = 0x0000682a, -+ GUEST_INTR_SSP_TABLE = 0x0000682c, - HOST_CR0 = 0x00006c00, - HOST_CR3 = 0x00006c02, - HOST_CR4 = 0x00006c04, -@@ -382,6 +387,9 @@ enum vmcs_field { - HOST_IA32_SYSENTER_EIP = 0x00006c12, - HOST_RSP = 0x00006c14, - HOST_RIP = 0x00006c16, -+ HOST_S_CET = 0x00006c18, -+ HOST_SSP = 0x00006c1a, -+ HOST_INTR_SSP_TABLE = 0x00006c1c - }; - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi b/SPECS/kernel/0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi new file mode 100644 index 000000000..e41090c24 --- /dev/null +++ b/SPECS/kernel/0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi @@ -0,0 +1,206 @@ +From 7fd175dd0952e80fd3d7f04d10a4926b52780c77 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 13 Jun 2024 09:48:43 -0700 +Subject: [PATCH 13/44] KVM: VMX: Virtualize FRED nested exception tracking + +Set the VMX nested exception bit in VM-entry interruption information +field when injecting a nested exception using FRED event delivery to +ensure: + 1) A nested exception is injected on a correct stack level. + 2) The nested bit defined in FRED stack frame is set. + +The event stack level used by FRED event delivery depends on whether +the event was a nested exception encountered during delivery of an +earlier event, because a nested exception is "regarded" as happening +on ring 0. E.g., when #PF is configured to use stack level 1 in +IA32_FRED_STKLVLS MSR: + - nested #PF will be delivered on the stack pointed by IA32_FRED_RSP1 + MSR when encountered in ring 3 and ring 0. + - normal #PF will be delivered on the stack pointed by IA32_FRED_RSP0 + MSR when encountered in ring 3. + +The VMX nested-exception support ensures a correct event stack level is +chosen when a VM entry injects a nested exception. + +Signed-off-by: Xin Li +[ Sean: reworked kvm_requeue_exception() to simply the code changes ] +Signed-off-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Move the check is_fred_enable() from kvm_multiple_exception() to + vmx_inject_exception() thus avoid bleeding FRED details into + kvm_multiple_exception() (Chao Gao). + +Change in v3: +* Rework kvm_requeue_exception() to simply the code changes (Sean + Christopherson). + +Change in v2: +* Set the nested flag when there is an original interrupt (Chao Gao). +--- + arch/x86/include/asm/kvm_host.h | 4 +++- + arch/x86/include/asm/vmx.h | 5 ++++- + arch/x86/kvm/svm/svm.c | 2 +- + arch/x86/kvm/vmx/vmx.c | 6 +++++- + arch/x86/kvm/x86.c | 13 ++++++++++++- + arch/x86/kvm/x86.h | 1 + + 6 files changed, 26 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 550a8716a2272..3b6dadf368eb6 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -760,6 +760,7 @@ struct kvm_queued_exception { + u32 error_code; + unsigned long payload; + bool has_payload; ++ bool nested; + u64 event_data; + }; + +@@ -2231,7 +2232,8 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); + void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); + void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code, u64 event_data); ++ bool has_error_code, u32 error_code, bool nested, ++ u64 event_data); + void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault); + void kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu, + struct x86_exception *fault); +diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h +index 539af190ad3e5..7b34a9357b288 100644 +--- a/arch/x86/include/asm/vmx.h ++++ b/arch/x86/include/asm/vmx.h +@@ -140,6 +140,7 @@ + #define VMX_BASIC_INOUT BIT_ULL(54) + #define VMX_BASIC_TRUE_CTLS BIT_ULL(55) + #define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56) ++#define VMX_BASIC_NESTED_EXCEPTION BIT_ULL(58) + + static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic) + { +@@ -442,13 +443,15 @@ enum vmcs_field { + #define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */ + #define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */ + #define INTR_INFO_UNBLOCK_NMI 0x1000 /* 12 */ ++#define INTR_INFO_NESTED_EXCEPTION_MASK 0x2000 /* 13 */ + #define INTR_INFO_VALID_MASK 0x80000000 /* 31 */ +-#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000 ++#define INTR_INFO_RESVD_BITS_MASK 0x7fffd000 + + #define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK + #define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK + #define VECTORING_INFO_DELIVER_CODE_MASK INTR_INFO_DELIVER_CODE_MASK + #define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK ++#define VECTORING_INFO_NESTED_EXCEPTION_MASK INTR_INFO_NESTED_EXCEPTION_MASK + + #define INTR_TYPE_EXT_INTR (EVENT_TYPE_EXTINT << 8) /* external interrupt */ + #define INTR_TYPE_RESERVED (EVENT_TYPE_RESERVED << 8) /* reserved */ +diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c +index 147b644488f21..f4ccb3e666355 100644 +--- a/arch/x86/kvm/svm/svm.c ++++ b/arch/x86/kvm/svm/svm.c +@@ -4095,7 +4095,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) + + kvm_requeue_exception(vcpu, vector, + exitintinfo & SVM_EXITINTINFO_VALID_ERR, +- error_code, 0); ++ error_code, false, 0); + break; + } + case SVM_EXITINTINFO_TYPE_INTR: +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 5cefe7024ac6e..9d12c4a01ab2f 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1855,8 +1855,11 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu) + vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, + vmx->vcpu.arch.event_exit_inst_len); + intr_info |= INTR_TYPE_SOFT_EXCEPTION; +- } else ++ } else { + intr_info |= INTR_TYPE_HARD_EXCEPTION; ++ if (ex->nested && is_fred_enabled(vcpu)) ++ intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; ++ } + + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info); + +@@ -7337,6 +7340,7 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + kvm_requeue_exception(vcpu, vector, + idt_vectoring_info & VECTORING_INFO_DELIVER_CODE_MASK, + error_code, ++ idt_vectoring_info & VECTORING_INFO_NESTED_EXCEPTION_MASK, + event_data); + break; + } +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 71b651d4567ff..881c5543a77a5 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -879,6 +879,10 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.pending = true; + vcpu->arch.exception.injected = false; + ++ vcpu->arch.exception.nested = vcpu->arch.exception.nested || ++ vcpu->arch.nmi_injected || ++ vcpu->arch.interrupt.injected; ++ + vcpu->arch.exception.has_error_code = has_error; + vcpu->arch.exception.vector = nr; + vcpu->arch.exception.error_code = error_code; +@@ -908,8 +912,13 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.injected = false; + vcpu->arch.exception.pending = false; + ++ /* #DF is NOT a nested event, per its definition. */ ++ vcpu->arch.exception.nested = false; ++ + kvm_queue_exception_e(vcpu, DF_VECTOR, 0); + } else { ++ vcpu->arch.exception.nested = true; ++ + /* replace previous exception with a new one in a hope + that instruction re-execution will regenerate lost + exception */ +@@ -938,7 +947,8 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, unsigned nr, + } + + void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, +- bool has_error_code, u32 error_code, u64 event_data) ++ bool has_error_code, u32 error_code, bool nested, ++ u64 event_data) + { + + /* +@@ -963,6 +973,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr, + vcpu->arch.exception.error_code = error_code; + vcpu->arch.exception.has_payload = false; + vcpu->arch.exception.payload = 0; ++ vcpu->arch.exception.nested = nested; + vcpu->arch.exception.event_data = event_data; + } + EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception); +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index 0c1fbf75442b7..4f5d12d7136ee 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -198,6 +198,7 @@ static inline void kvm_clear_exception_queue(struct kvm_vcpu *vcpu) + { + vcpu->arch.exception.pending = false; + vcpu->arch.exception.injected = false; ++ vcpu->arch.exception.nested = false; + vcpu->arch.exception_vmexit.pending = false; + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi b/SPECS/kernel/0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi deleted file mode 100644 index bca2d873f..000000000 --- a/SPECS/kernel/0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi +++ /dev/null @@ -1,200 +0,0 @@ -From aa3e2401705ede04e2e4d8e54149942d6408a5e2 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Thu, 13 Jun 2024 09:48:43 -0700 -Subject: [PATCH 13/44] KVM: x86: Save/restore the nested flag of an exception - -Save/restore the nested flag of an exception during VM save/restore -and live migration to ensure a correct event stack level is chosen -when a nested exception is injected through FRED event delivery. - -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Add live migration support for exception nested flag (Chao Gao). ---- - Documentation/virt/kvm/api.rst | 21 ++++++++++++++++++++- - arch/x86/include/asm/kvm_host.h | 1 + - arch/x86/include/uapi/asm/kvm.h | 4 +++- - arch/x86/kvm/x86.c | 19 ++++++++++++++++++- - include/uapi/linux/kvm.h | 1 + - 5 files changed, 43 insertions(+), 3 deletions(-) - -diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst -index 6aa40ee05a4a..c496b0883a7f 100644 ---- a/Documentation/virt/kvm/api.rst -+++ b/Documentation/virt/kvm/api.rst -@@ -1184,6 +1184,10 @@ The following bits are defined in the flags field: - fields contain a valid state. This bit will be set whenever - KVM_CAP_EXCEPTION_PAYLOAD is enabled. - -+- KVM_VCPUEVENT_VALID_NESTED_FLAG may be set to inform that the -+ exception is a nested exception. This bit will be set whenever -+ KVM_CAP_EXCEPTION_NESTED_FLAG is enabled. -+ - - KVM_VCPUEVENT_VALID_TRIPLE_FAULT may be set to signal that the - triple_fault_pending field contains a valid state. This bit will - be set whenever KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled. -@@ -1283,6 +1287,10 @@ can be set in the flags field to signal that the - exception_has_payload, exception_payload, and exception.pending fields - contain a valid state and shall be written into the VCPU. - -+If KVM_CAP_EXCEPTION_NESTED_FLAG is enabled, KVM_VCPUEVENT_VALID_NESTED_FLAG -+can be set in the flags field to inform that the exception is a nested -+exception and exception_is_nested shall be written into the VCPU. -+ - If KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled, KVM_VCPUEVENT_VALID_TRIPLE_FAULT - can be set in flags field to signal that the triple_fault field contains - a valid state and shall be written into the VCPU. -@@ -8651,7 +8659,7 @@ given VM. - When this capability is enabled, KVM resets the VCPU when setting - MP_STATE_INIT_RECEIVED through IOCTL. The original MP_STATE is preserved. - --7.43 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED -+7.44 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED - ------------------------------------------- - - :Architectures: arm64 -@@ -8662,6 +8670,17 @@ This capability indicate to the userspace whether a PFNMAP memory region - can be safely mapped as cacheable. This relies on the presence of - force write back (FWB) feature support on the hardware. - -+7.45 KVM_CAP_EXCEPTION_NESTED_FLAG -+---------------------------------- -+ -+:Architectures: x86 -+:Parameters: args[0] whether feature should be enabled or not -+ -+With this capability enabled, an exception is save/restored with the -+additional information of whether it was nested or not. FRED event -+delivery uses this information to ensure a correct event stack level -+is chosen when a VM entry injects a nested exception. -+ - 8. Other capabilities. - ====================== - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 0d4cdb704f97..3b38c82b4c1c 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -1471,6 +1471,7 @@ struct kvm_arch { - bool has_mapped_host_mmio; - bool guest_can_read_msr_platform_info; - bool exception_payload_enabled; -+ bool exception_nested_flag_enabled; - - bool triple_fault_event; - -diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h -index a4870d9c9279..7b6b0491b157 100644 ---- a/arch/x86/include/uapi/asm/kvm.h -+++ b/arch/x86/include/uapi/asm/kvm.h -@@ -326,6 +326,7 @@ struct kvm_reinject_control { - #define KVM_VCPUEVENT_VALID_SMM 0x00000008 - #define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 - #define KVM_VCPUEVENT_VALID_TRIPLE_FAULT 0x00000020 -+#define KVM_VCPUEVENT_VALID_NESTED_FLAG 0x00000040 - - /* Interrupt shadow states */ - #define KVM_X86_SHADOW_INT_MOV_SS 0x01 -@@ -363,7 +364,8 @@ struct kvm_vcpu_events { - struct { - __u8 pending; - } triple_fault; -- __u8 reserved[26]; -+ __u8 reserved[25]; -+ __u8 exception_is_nested; - __u8 exception_has_payload; - __u64 exception_payload; - }; -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 6029125a518d..eb0a1e537a88 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -4875,6 +4875,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) - case KVM_CAP_GET_MSR_FEATURES: - case KVM_CAP_MSR_PLATFORM_INFO: - case KVM_CAP_EXCEPTION_PAYLOAD: -+ case KVM_CAP_EXCEPTION_NESTED_FLAG: - case KVM_CAP_X86_TRIPLE_FAULT_EVENT: - case KVM_CAP_SET_GUEST_DEBUG: - case KVM_CAP_LAST_CPU: -@@ -5619,6 +5620,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, - events->exception.error_code = ex->error_code; - events->exception_has_payload = ex->has_payload; - events->exception_payload = ex->payload; -+ events->exception_is_nested = ex->nested; - - events->interrupt.injected = - vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft; -@@ -5644,6 +5646,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, - | KVM_VCPUEVENT_VALID_SMM); - if (vcpu->kvm->arch.exception_payload_enabled) - events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD; -+ if (vcpu->kvm->arch.exception_nested_flag_enabled) -+ events->flags |= KVM_VCPUEVENT_VALID_NESTED_FLAG; - if (vcpu->kvm->arch.triple_fault_event) { - events->triple_fault.pending = kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu); - events->flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; -@@ -5658,7 +5662,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, - | KVM_VCPUEVENT_VALID_SHADOW - | KVM_VCPUEVENT_VALID_SMM - | KVM_VCPUEVENT_VALID_PAYLOAD -- | KVM_VCPUEVENT_VALID_TRIPLE_FAULT)) -+ | KVM_VCPUEVENT_VALID_TRIPLE_FAULT -+ | KVM_VCPUEVENT_VALID_NESTED_FLAG)) - return -EINVAL; - - if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) { -@@ -5673,6 +5678,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, - events->exception_has_payload = 0; - } - -+ if (events->flags & KVM_VCPUEVENT_VALID_NESTED_FLAG) { -+ if (!vcpu->kvm->arch.exception_nested_flag_enabled) -+ return -EINVAL; -+ } else { -+ events->exception_is_nested = 0; -+ } -+ - if ((events->exception.injected || events->exception.pending) && - (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR)) - return -EINVAL; -@@ -5698,6 +5710,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, - vcpu->arch.exception.error_code = events->exception.error_code; - vcpu->arch.exception.has_payload = events->exception_has_payload; - vcpu->arch.exception.payload = events->exception_payload; -+ vcpu->arch.exception.nested = events->exception_is_nested; - - vcpu->arch.interrupt.injected = events->interrupt.injected; - vcpu->arch.interrupt.nr = events->interrupt.nr; -@@ -6732,6 +6745,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, - kvm->arch.exception_payload_enabled = cap->args[0]; - r = 0; - break; -+ case KVM_CAP_EXCEPTION_NESTED_FLAG: -+ kvm->arch.exception_nested_flag_enabled = cap->args[0]; -+ r = 0; -+ break; - case KVM_CAP_X86_TRIPLE_FAULT_EVENT: - kvm->arch.triple_fault_event = cap->args[0]; - r = 0; -diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h -index f0f0d49d2544..fe4a822b3c09 100644 ---- a/include/uapi/linux/kvm.h -+++ b/include/uapi/linux/kvm.h -@@ -962,6 +962,7 @@ struct kvm_enable_cap { - #define KVM_CAP_ARM_EL2_E2H0 241 - #define KVM_CAP_RISCV_MP_STATE_RESET 242 - #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 -+#define KVM_CAP_EXCEPTION_NESTED_FLAG 244 - - struct kvm_irq_routing_irqchip { - __u32 irqchip; --- -2.43.0 - diff --git a/SPECS/kernel/0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet b/SPECS/kernel/0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet new file mode 100644 index 000000000..6144f79bf --- /dev/null +++ b/SPECS/kernel/0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet @@ -0,0 +1,31 @@ +From 9dd63a8bddee2d398483bb8996a30b26f6f209b8 Mon Sep 17 00:00:00 2001 +From: Muhammad Husaini Zulkifli +Date: Fri, 30 Jul 2021 10:17:52 +0800 +Subject: [PATCH 13/14] igc: Remove the CONFIG_DEBUG_MISC condition for trace + +This patch is to remove the CONFIG_DEBUG_MISC for trace_printk. +CONFIG_DEBUG_MISC was enabled in ER89 config but not enable in +latest config. + +Signed-off-by: Muhammad Husaini Zulkifli +Signed-off-by: Aravindhan Gunasekaran +--- + drivers/net/ethernet/intel/igc/igc_main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index 571cc380de5ad..bfef9555a12ea 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -3214,7 +3214,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) + + switch (tx_buffer->type) { + case IGC_TX_BUFFER_TYPE_XSK: +-#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) ++#if defined(CONFIG_TRACING) + /* Only use for RTCP KPI Measurement on Q2 */ + if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) + trace_printk("TX HW TS %lld\n", timestamp); +-- +2.43.0 + diff --git a/SPECS/kernel/0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet b/SPECS/kernel/0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet new file mode 100644 index 000000000..60a51e9cc --- /dev/null +++ b/SPECS/kernel/0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet @@ -0,0 +1,152 @@ +From 8cf346c678cf84a00c374a398d4b39742d98d584 Mon Sep 17 00:00:00 2001 +From: "Song, Yoong Siang" +Date: Sun, 11 Apr 2021 16:19:28 +0800 +Subject: [PATCH 13/18] net: stmmac: restructure Rx & Tx hardware timestamping + functions + +We rearrange the functions for getting Rx & Tx time-stampings for +skb so that these functions can also be reused for XDP later. + +Signed-off-by: Ong Boon Leong +Signed-off-by: Song, Yoong Siang +--- + .../net/ethernet/stmicro/stmmac/stmmac_main.c | 46 ++++++++++--------- + 1 file changed, 24 insertions(+), 22 deletions(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 92a8075ef9399..641b011035087 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -388,25 +388,20 @@ static void stmmac_eee_ctrl_timer(struct timer_list *t) + /* stmmac_get_tx_hwtstamp - get HW TX timestamps + * @priv: driver private structure + * @p : descriptor pointer +- * @skb : the socket buffer ++ * @hwtstamp: hardware timestamp + * Description : + * This function will read timestamp from the descriptor & pass it to stack. + * and also perform some sanity checks. + */ + static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, +- struct dma_desc *p, struct sk_buff *skb) ++ struct dma_desc *p, ktime_t *hwtstamp) + { +- struct skb_shared_hwtstamps shhwtstamp; + bool found = false; + u64 ns = 0; + + if (!priv->hwts_tx_en) + return; + +- /* exit if skb doesn't support hw tstamp */ +- if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))) +- return; +- + /* check tx tstamp status */ + if (stmmac_get_tx_timestamp_status(priv, p)) { + stmmac_get_timestamp(priv, p, priv->adv_ts, &ns); +@@ -418,12 +413,8 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, + if (found) { + ns -= priv->plat->cdc_error_adj; + +- memset(&shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); +- shhwtstamp.hwtstamp = ns_to_ktime(ns); +- ++ *hwtstamp = ns_to_ktime(ns); + netdev_dbg(priv->dev, "get valid TX hw timestamp %llu\n", ns); +- /* pass tstamp to stack */ +- skb_tstamp_tx(skb, &shhwtstamp); + } + } + +@@ -431,15 +422,14 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv, + * @priv: driver private structure + * @p : descriptor pointer + * @np : next descriptor pointer +- * @skb : the socket buffer ++ * @hwtstamp: hardware timestamp + * Description : + * This function will read received packet's timestamp from the descriptor + * and pass it to stack. It also perform some sanity checks. + */ + static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, +- struct dma_desc *np, struct sk_buff *skb) ++ struct dma_desc *np, ktime_t *hwtstamp) + { +- struct skb_shared_hwtstamps *shhwtstamp = NULL; + struct dma_desc *desc = p; + u64 ns = 0; + +@@ -456,11 +446,10 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p, + ns -= priv->plat->cdc_error_adj; + + netdev_dbg(priv->dev, "get valid RX hw timestamp %llu\n", ns); +- shhwtstamp = skb_hwtstamps(skb); +- memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); +- shhwtstamp->hwtstamp = ns_to_ktime(ns); ++ *hwtstamp = ns_to_ktime(ns); + } else { + netdev_dbg(priv->dev, "cannot get RX hw timestamp\n"); ++ *hwtstamp = 0; + } + } + +@@ -2729,8 +2718,15 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, + } else { + tx_packets++; + } +- if (skb) { +- stmmac_get_tx_hwtstamp(priv, p, skb); ++ if (skb && ++ skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS) { ++ struct skb_shared_hwtstamps shhwtstamp; ++ ++ memset(&shhwtstamp, 0, ++ sizeof(struct skb_shared_hwtstamps)); ++ stmmac_get_tx_hwtstamp(priv, p, ++ &shhwtstamp.hwtstamp); ++ skb_tstamp_tx(skb, &shhwtstamp); + } else if (tx_q->xsk_pool && + xp_tx_metadata_enabled(tx_q->xsk_pool)) { + struct stmmac_xsk_tx_complete tx_compl = { +@@ -5157,6 +5153,7 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, + { + struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue]; + struct stmmac_channel *ch = &priv->channel[queue]; ++ struct skb_shared_hwtstamps *shhwtstamp = NULL; + unsigned int len = xdp->data_end - xdp->data; + enum pkt_hash_types hash_type; + int coe = priv->hw->rx_csum; +@@ -5169,7 +5166,9 @@ static void stmmac_dispatch_skb_zc(struct stmmac_priv *priv, u32 queue, + return; + } + +- stmmac_get_rx_hwtstamp(priv, p, np, skb); ++ shhwtstamp = skb_hwtstamps(skb); ++ memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); ++ stmmac_get_rx_hwtstamp(priv, p, np, &shhwtstamp->hwtstamp); + if (priv->hw->hw_vlan_en) + /* MAC level stripping. */ + stmmac_rx_hw_vlan(priv, priv->hw, p, skb); +@@ -5493,6 +5492,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) + rx_q->dma_rx_phy, desc_size); + } + while (count < limit) { ++ struct skb_shared_hwtstamps *shhwtstamp = NULL; + unsigned int buf1_len = 0, buf2_len = 0; + enum pkt_hash_types hash_type; + struct stmmac_rx_buffer *buf; +@@ -5686,7 +5686,9 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) + + /* Got entire packet into SKB. Finish it. */ + +- stmmac_get_rx_hwtstamp(priv, p, np, skb); ++ shhwtstamp = skb_hwtstamps(skb); ++ memset(shhwtstamp, 0, sizeof(struct skb_shared_hwtstamps)); ++ stmmac_get_rx_hwtstamp(priv, p, np, &shhwtstamp->hwtstamp); + + if (priv->hw->hw_vlan_en) + /* MAC level stripping. */ +-- +2.43.0 + diff --git a/SPECS/kernel/0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu b/SPECS/kernel/0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu deleted file mode 100644 index 94feea209..000000000 --- a/SPECS/kernel/0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu +++ /dev/null @@ -1,814 +0,0 @@ -From 0dee21c66fcb4ee0f58535f368f9fe5e81c857f2 Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Fri, 24 Oct 2025 16:47:40 +0800 -Subject: [PATCH 13/21] patch: staging add ipu7 isys tpg and MGC config - -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/Kconfig | 11 + - drivers/staging/media/ipu7/ipu7-isys-tpg.c | 693 +++++++++++++++++++++ - drivers/staging/media/ipu7/ipu7-isys-tpg.h | 70 +++ - 3 files changed, 774 insertions(+) - create mode 100644 drivers/staging/media/ipu7/ipu7-isys-tpg.c - create mode 100644 drivers/staging/media/ipu7/ipu7-isys-tpg.h - -diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig -index 0a6e68c7632d..91954fdadc8b 100644 ---- a/drivers/staging/media/ipu7/Kconfig -+++ b/drivers/staging/media/ipu7/Kconfig -@@ -23,6 +23,17 @@ config VIDEO_INTEL_IPU7 - To compile this driver, say Y here! It contains 3 modules - - intel_ipu7, intel_ipu7_isys and intel_ipu7_psys. - -+config VIDEO_INTEL_IPU7_MGC -+ bool "Compile for IPU7 MGC driver" -+ depends on VIDEO_INTEL_IPU7 -+ help -+ If selected, MGC device nodes would be created. -+ -+ Recommended for driver developers only. -+ -+ If you want to the MGC devices exposed to user as media entity, -+ you must select this option, otherwise no. -+ - config VIDEO_INTEL_IPU7_ISYS_RESET - bool "IPU7 ISYS RESET" - depends on VIDEO_INTEL_IPU7 -diff --git a/drivers/staging/media/ipu7/ipu7-isys-tpg.c b/drivers/staging/media/ipu7/ipu7-isys-tpg.c -new file mode 100644 -index 000000000000..35b6298e4f8c ---- /dev/null -+++ b/drivers/staging/media/ipu7/ipu7-isys-tpg.c -@@ -0,0 +1,693 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-isys.h" -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-tpg.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-csi2-regs.h" -+#include "ipu7-platform-regs.h" -+ -+static const u32 tpg_supported_codes[] = { -+ MEDIA_BUS_FMT_SBGGR8_1X8, -+ MEDIA_BUS_FMT_SGBRG8_1X8, -+ MEDIA_BUS_FMT_SGRBG8_1X8, -+ MEDIA_BUS_FMT_SRGGB8_1X8, -+ MEDIA_BUS_FMT_SBGGR10_1X10, -+ MEDIA_BUS_FMT_SGBRG10_1X10, -+ MEDIA_BUS_FMT_SGRBG10_1X10, -+ MEDIA_BUS_FMT_SRGGB10_1X10, -+ MEDIA_BUS_FMT_SBGGR12_1X12, -+ MEDIA_BUS_FMT_SGBRG12_1X12, -+ MEDIA_BUS_FMT_SGRBG12_1X12, -+ MEDIA_BUS_FMT_SRGGB12_1X12, -+ 0, -+}; -+ -+#define IPU_ISYS_FREQ 533000000UL -+ -+static u32 isys_mbus_code_to_bpp(u32 code) -+{ -+ switch (code) { -+ case MEDIA_BUS_FMT_RGB888_1X24: -+ return 24; -+ case MEDIA_BUS_FMT_YUYV10_1X20: -+ return 20; -+ case MEDIA_BUS_FMT_Y10_1X10: -+ case MEDIA_BUS_FMT_RGB565_1X16: -+ case MEDIA_BUS_FMT_UYVY8_1X16: -+ case MEDIA_BUS_FMT_YUYV8_1X16: -+ return 16; -+ case MEDIA_BUS_FMT_SBGGR12_1X12: -+ case MEDIA_BUS_FMT_SGBRG12_1X12: -+ case MEDIA_BUS_FMT_SGRBG12_1X12: -+ case MEDIA_BUS_FMT_SRGGB12_1X12: -+ return 12; -+ case MEDIA_BUS_FMT_SBGGR10_1X10: -+ case MEDIA_BUS_FMT_SGBRG10_1X10: -+ case MEDIA_BUS_FMT_SGRBG10_1X10: -+ case MEDIA_BUS_FMT_SRGGB10_1X10: -+ return 10; -+ case MEDIA_BUS_FMT_SBGGR8_1X8: -+ case MEDIA_BUS_FMT_SGBRG8_1X8: -+ case MEDIA_BUS_FMT_SGRBG8_1X8: -+ case MEDIA_BUS_FMT_SRGGB8_1X8: -+ return 8; -+ default: -+ WARN_ON(1); -+ return 0; -+ } -+} -+ -+static const struct v4l2_subdev_video_ops tpg_sd_video_ops = { -+ .s_stream = tpg_set_stream, -+}; -+ -+static int ipu7_isys_tpg_s_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ struct ipu7_isys_tpg *tpg = container_of(container_of(ctrl->handler, -+ struct -+ ipu7_isys_subdev, -+ ctrl_handler), -+ struct ipu7_isys_tpg, asd); -+ switch (ctrl->id) { -+ case V4L2_CID_HBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_HBLANK); -+ break; -+ case V4L2_CID_VBLANK: -+ writel(ctrl->val, tpg->base + MGC_MG_VBLANK); -+ break; -+ case V4L2_CID_TEST_PATTERN: -+ writel(ctrl->val, tpg->base + MGC_MG_TPG_MODE); -+ break; -+ } -+ -+ return 0; -+} -+ -+static const struct v4l2_ctrl_ops ipu7_isys_tpg_ctrl_ops = { -+ .s_ctrl = ipu7_isys_tpg_s_ctrl, -+}; -+ -+static u64 ipu7_isys_tpg_rate(struct ipu7_isys_tpg *tpg, unsigned int bpp) -+{ -+ return MGC_PPC * IPU_ISYS_FREQ / bpp; -+} -+ -+static const char *const tpg_mode_items[] = { -+ "Ramp", -+ "Checkerboard", -+ "Monochrome per frame", -+ "Color palette", -+}; -+ -+static struct v4l2_ctrl_config tpg_mode = { -+ .ops = &ipu7_isys_tpg_ctrl_ops, -+ .id = V4L2_CID_TEST_PATTERN, -+ .name = "Test Pattern", -+ .type = V4L2_CTRL_TYPE_MENU, -+ .min = TPG_MODE_RAMP, -+ .max = ARRAY_SIZE(tpg_mode_items) - 1, -+ .def = TPG_MODE_COLOR_PALETTE, -+ .menu_skip_mask = 0x2, -+ .qmenu = tpg_mode_items, -+}; -+ -+static void ipu7_isys_tpg_init_controls(struct v4l2_subdev *sd) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ int hblank; -+ u64 default_pixel_rate; -+ -+ hblank = 1024; -+ -+ tpg->hblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_HBLANK, 8, 65535, 1, hblank); -+ -+ tpg->vblank = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_VBLANK, 8, 65535, 1, 1024); -+ -+ default_pixel_rate = ipu7_isys_tpg_rate(tpg, 8); -+ tpg->pixel_rate = v4l2_ctrl_new_std(&tpg->asd.ctrl_handler, -+ &ipu7_isys_tpg_ctrl_ops, -+ V4L2_CID_PIXEL_RATE, -+ default_pixel_rate, -+ default_pixel_rate, -+ 1, default_pixel_rate); -+ if (tpg->pixel_rate) { -+ tpg->pixel_rate->cur.val = default_pixel_rate; -+ tpg->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ } -+ -+ v4l2_ctrl_new_custom(&tpg->asd.ctrl_handler, &tpg_mode, NULL); -+} -+ -+static int tpg_sd_init_cfg(struct v4l2_subdev *sd, -+ struct v4l2_subdev_state *state) -+{ -+ struct v4l2_subdev_route routes[] = { -+ { -+ .source_pad = 0, -+ .source_stream = 0, -+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, -+ } -+ }; -+ -+ struct v4l2_subdev_krouting routing = { -+ .num_routes = 1, -+ .routes = routes, -+ }; -+ -+ static const struct v4l2_mbus_framefmt format = { -+ .width = 1920, -+ .height = 1080, -+ .code = MEDIA_BUS_FMT_SBGGR10_1X10, -+ .field = V4L2_FIELD_NONE, -+ }; -+ -+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing, &format); -+} -+ -+static const struct v4l2_subdev_internal_ops ipu7_isys_tpg_internal_ops = { -+ .init_state = tpg_sd_init_cfg, -+}; -+ -+static const struct v4l2_subdev_pad_ops tpg_sd_pad_ops = { -+ .get_fmt = v4l2_subdev_get_fmt, -+ .set_fmt = ipu7_isys_subdev_set_fmt, -+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code, -+}; -+ -+static int subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, -+ struct v4l2_event_subscription *sub) -+{ -+ switch (sub->type) { -+ case V4L2_EVENT_FRAME_SYNC: -+ return v4l2_event_subscribe(fh, sub, 10, NULL); -+ case V4L2_EVENT_CTRL: -+ return v4l2_ctrl_subscribe_event(fh, sub); -+ default: -+ return -EINVAL; -+ } -+}; -+ -+/* V4L2 subdev core operations */ -+static const struct v4l2_subdev_core_ops tpg_sd_core_ops = { -+ .subscribe_event = subscribe_event, -+ .unsubscribe_event = v4l2_event_subdev_unsubscribe, -+}; -+ -+static const struct v4l2_subdev_ops tpg_sd_ops = { -+ .core = &tpg_sd_core_ops, -+ .video = &tpg_sd_video_ops, -+ .pad = &tpg_sd_pad_ops, -+}; -+ -+static struct media_entity_operations tpg_entity_ops = { -+ .link_validate = v4l2_subdev_link_validate, -+}; -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ struct video_device *vdev = tpg->asd.sd.devnode; -+ struct v4l2_event ev = { -+ .type = V4L2_EVENT_FRAME_SYNC, -+ }; -+ -+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence); -+ -+ v4l2_event_queue(vdev, &ev); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "sof_event::tpg-%i sequence: %i\n", tpg->index, -+ ev.u.frame_sync.frame_sequence); -+} -+ -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream) -+{ -+ struct ipu7_isys_tpg *tpg = ipu7_isys_subdev_to_tpg(stream->asd); -+ u32 frame_sequence = atomic_read(&stream->sequence); -+ -+ dev_dbg(&stream->isys->adev->auxdev.dev, -+ "eof_event::tpg-%i sequence: %i\n", -+ tpg->index, frame_sequence); -+} -+ -+#define DEFAULT_VC_ID 0 -+static bool is_metadata_enabled(const struct ipu7_isys_tpg *tpg) -+{ -+ return false; -+} -+ -+static void ipu7_mipigen_regdump(const struct ipu7_isys_tpg *tpg, -+ void __iomem *mg_base) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ -+ dev_dbg(dev, "---------MGC REG DUMP START----------"); -+ -+ dev_dbg(dev, "MGC RX_TYPE_REG 0x%x = 0x%x", -+ MGC_MG_CSI_ADAPT_LAYER_TYPE, -+ readl(mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE)); -+ dev_dbg(dev, "MGC MG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_MODE, readl(mg_base + MGC_MG_MODE)); -+ dev_dbg(dev, "MGC MIPI_VC_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_VC, readl(mg_base + MGC_MG_MIPI_VC)); -+ dev_dbg(dev, "MGC MIPI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MIPI_DTYPES, readl(mg_base + MGC_MG_MIPI_DTYPES)); -+ dev_dbg(dev, "MGC MULTI_DTYPES_REG 0x%x = 0x%x", -+ MGC_MG_MULTI_DTYPES_MODE, -+ readl(mg_base + MGC_MG_MULTI_DTYPES_MODE)); -+ dev_dbg(dev, "MGC NOF_FRAMES_REG 0x%x = 0x%x", -+ MGC_MG_NOF_FRAMES, readl(mg_base + MGC_MG_NOF_FRAMES)); -+ dev_dbg(dev, "MGC FRAME_DIM_REG 0x%x = 0x%x", -+ MGC_MG_FRAME_DIM, readl(mg_base + MGC_MG_FRAME_DIM)); -+ dev_dbg(dev, "MGC HBLANK_REG 0x%x = 0x%x", -+ MGC_MG_HBLANK, readl(mg_base + MGC_MG_HBLANK)); -+ dev_dbg(dev, "MGC VBLANK_REG 0x%x = 0x%x", -+ MGC_MG_VBLANK, readl(mg_base + MGC_MG_VBLANK)); -+ dev_dbg(dev, "MGC TPG_MODE_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MODE, readl(mg_base + MGC_MG_TPG_MODE)); -+ dev_dbg(dev, "MGC R0=0x%x G0=0x%x B0=0x%x", -+ readl(mg_base + MGC_MG_TPG_R0), -+ readl(mg_base + MGC_MG_TPG_G0), -+ readl(mg_base + MGC_MG_TPG_B0)); -+ dev_dbg(dev, "MGC R1=0x%x G1=0x%x B1=0x%x", -+ readl(mg_base + MGC_MG_TPG_R1), -+ readl(mg_base + MGC_MG_TPG_G1), -+ readl(mg_base + MGC_MG_TPG_B1)); -+ dev_dbg(dev, "MGC TPG_MASKS_REG 0x%x = 0x%x", -+ MGC_MG_TPG_MASKS, readl(mg_base + MGC_MG_TPG_MASKS)); -+ dev_dbg(dev, "MGC TPG_XY_MASK_REG 0x%x = 0x%x", -+ MGC_MG_TPG_XY_MASK, readl(mg_base + MGC_MG_TPG_XY_MASK)); -+ dev_dbg(dev, "MGC TPG_TILE_DIM_REG 0x%x = 0x%x", -+ MGC_MG_TPG_TILE_DIM, readl(mg_base + MGC_MG_TPG_TILE_DIM)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_EN_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_EN, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_EN)); -+ dev_dbg(dev, "MGC DTO_SPEED_CTRL_INCR_VAL_REG 0x%x = 0x%x", -+ MGC_MG_DTO_SPEED_CTRL_INCR_VAL, -+ readl(mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL)); -+ dev_dbg(dev, "MGC MG_FRAME_NUM_STTS 0x%x = 0x%x", -+ MGC_MG_FRAME_NUM_STTS, -+ readl(mg_base + MGC_MG_FRAME_NUM_STTS)); -+ -+ dev_dbg(dev, "---------MGC REG DUMP END----------"); -+} -+ -+#define TPG_STOP_TIMEOUT 500000 -+static int tpg_stop_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ unsigned int port; -+ u32 status; -+ void __iomem *reg; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ -+ port = 1 << tpg->index; -+ -+ dev_dbg(dev, "MG%d generated %u frames", tpg->index, -+ readl(mgc_base + MGC_MG_FRAME_NUM_STTS)); -+ writel(port, mgc_base + MGC_ASYNC_STOP); -+ -+ dev_dbg(dev, "wait for MG%d stop", tpg->index); -+ -+ reg = mg_base + MGC_MG_STOPPED_STTS; -+ ret = readl_poll_timeout(reg, status, status & 0x1, 200, -+ TPG_STOP_TIMEOUT); -+ if (ret < 0) { -+ dev_err(dev, "mgc stop timeout"); -+ return ret; -+ } -+ -+ dev_dbg(dev, "MG%d STOPPED", tpg->index); -+ -+ return 0; -+} -+ -+#define IS_IO_CLK (IPU7_IS_FREQ_CTL_DEFAULT_RATIO * 100 / 6) -+#define TPG_FRAME_RATE 30 -+#define TPG_BLANK_RATIO (4 / 3) -+static void tpg_get_timing(const struct ipu7_isys_tpg *tpg, u32 *dto, -+ u32 *hblank_cycles, u32 *vblank_cycles) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 width, height; -+ u32 code; -+ u32 bpp; -+ u32 bits_per_line; -+ u64 line_time_ns, frame_time_us, cycles, ns_per_cycle, rate; -+ u64 vblank_us, hblank_us; -+ u32 ref_clk; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ u32 dto_incr_val = 0x100; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return; -+ -+ dev_dbg(dev, "MG%d code = 0x%x bpp = %u\n", tpg->index, code, bpp); -+ bits_per_line = width * bpp * TPG_BLANK_RATIO; -+ -+ cycles = div_u64(bits_per_line, 64); -+ dev_dbg(dev, "MG%d bits_per_line = %u cycles = %llu\n", tpg->index, -+ bits_per_line, cycles); -+ -+ do { -+ dev_dbg(dev, "MG%d try dto_incr_val 0x%x\n", tpg->index, -+ dto_incr_val); -+ rate = div_u64(1 << 16, dto_incr_val); -+ ns_per_cycle = div_u64(rate * 1000, IS_IO_CLK); -+ dev_dbg(dev, "MG%d ns_per_cycles = %llu\n", tpg->index, -+ ns_per_cycle); -+ -+ line_time_ns = cycles * ns_per_cycle; -+ frame_time_us = line_time_ns * height / 1000; -+ dev_dbg(dev, "MG%d line_time_ns = %llu frame_time_us = %llu\n", -+ tpg->index, line_time_ns, frame_time_us); -+ -+ if (frame_time_us * TPG_FRAME_RATE < USEC_PER_SEC) -+ break; -+ -+ /* dto incr val step 0x100 */ -+ dto_incr_val += 0x100; -+ } while (dto_incr_val < (1 << 16)); -+ -+ if (dto_incr_val >= (1 << 16)) { -+ dev_warn(dev, "No DTO_INCR_VAL found\n"); -+ hblank_us = 10; /* 10us */ -+ vblank_us = 10000; /* 10ms */ -+ dto_incr_val = 0x1000; -+ } else { -+ hblank_us = line_time_ns * (TPG_BLANK_RATIO - 1) / 1000; -+ vblank_us = div_u64(1000000, TPG_FRAME_RATE) - frame_time_us; -+ } -+ -+ dev_dbg(dev, "hblank_us = %llu, vblank_us = %llu dto_incr_val = %u\n", -+ hblank_us, vblank_us, dto_incr_val); -+ -+ ref_clk = tpg->isys->adev->isp->buttress.ref_clk; -+ -+ *dto = dto_incr_val; -+ *hblank_cycles = hblank_us * ref_clk / 10; -+ *vblank_cycles = vblank_us * ref_clk / 10; -+ dev_dbg(dev, "hblank_cycles = %u, vblank_cycles = %u\n", -+ *hblank_cycles, *vblank_cycles); -+} -+ -+static int tpg_start_stream(const struct ipu7_isys_tpg *tpg) -+{ -+ struct v4l2_mbus_framefmt format; -+ u32 port_map; -+ u32 csi_port; -+ u32 code, bpp; -+ u32 width, height; -+ u32 dto, hblank, vblank; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ void __iomem *mgc_base = tpg->isys->pdata->base + IS_IO_MGC_BASE; -+ void __iomem *mg_base = tpg->base; -+ int ret; -+ -+ ret = ipu7_isys_get_stream_pad_fmt((struct v4l2_subdev *)&tpg->asd.sd, -+ 0, 0, &format); -+ if (ret) -+ return ret; -+ -+ width = format.width; -+ height = format.height; -+ code = format.code; -+ dev_dbg(dev, "MG%d code: 0x%x resolution: %ux%u\n", -+ tpg->index, code, width, height); -+ bpp = isys_mbus_code_to_bpp(code); -+ if (!bpp) -+ return -EINVAL; -+ -+ csi_port = tpg->index; -+ if (csi_port >= 4) -+ dev_err(dev, "invalid tpg index %u\n", tpg->index); -+ -+ dev_dbg(dev, "INSYS MG%d was mapped to CSI%d\n", -+ DEFAULT_VC_ID, csi_port); -+ -+ /* config port map -+ * TODO: add VC support and TPG with multiple -+ * source pads. Currently, for simplicity, only map 1 mg to 1 csi port -+ */ -+ port_map = 1 << tpg->index; -+ writel(port_map, mgc_base + MGC_CSI_PORT_MAP(csi_port)); -+ -+ /* configure adapt layer type */ -+ writel(1, mg_base + MGC_MG_CSI_ADAPT_LAYER_TYPE); -+ -+ /* configure MGC mode -+ * 0 - Disable MGC -+ * 1 - Enable PRBS -+ * 2 - Enable TPG -+ * 3 - Reserved [Write phase: SW/FW debug] -+ */ -+ writel(2, mg_base + MGC_MG_MODE); -+ -+ /* config mg init counter */ -+ writel(0, mg_base + MGC_MG_INIT_COUNTER); -+ -+ /* -+ * configure virtual channel -+ * TODO: VC support if need -+ * currently each MGC just uses 1 virtual channel -+ */ -+ writel(DEFAULT_VC_ID, mg_base + MGC_MG_MIPI_VC); -+ -+ /* -+ * configure data type and multi dtypes mode -+ * TODO: it needs to add the metedata flow. -+ */ -+ if (is_metadata_enabled(tpg)) { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(2, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } else { -+ writel(MGC_DTYPE_RAW(bpp) << 4 | MGC_DTYPE_RAW(bpp), -+ mg_base + MGC_MG_MIPI_DTYPES); -+ writel(0, mg_base + MGC_MG_MULTI_DTYPES_MODE); -+ } -+ -+ /* -+ * configure frame information -+ */ -+ writel(0, mg_base + MGC_MG_NOF_FRAMES); -+ writel(width | height << 16, mg_base + MGC_MG_FRAME_DIM); -+ -+ tpg_get_timing(tpg, &dto, &hblank, &vblank); -+ writel(hblank, mg_base + MGC_MG_HBLANK); -+ writel(vblank, mg_base + MGC_MG_VBLANK); -+ -+ /* -+ * configure tpg mode, colors, mask, tile dimension -+ * Mode was set by user configuration -+ * 0 - Ramp mode -+ * 1 - Checkerboard -+ * 2 - Monochrome per frame -+ * 3 - Color palette -+ */ -+ writel(TPG_MODE_COLOR_PALETTE, mg_base + MGC_MG_TPG_MODE); -+ -+ /* red and green for checkerboard, n/a for other modes */ -+ writel(58, mg_base + MGC_MG_TPG_R0); -+ writel(122, mg_base + MGC_MG_TPG_G0); -+ writel(46, mg_base + MGC_MG_TPG_B0); -+ writel(123, mg_base + MGC_MG_TPG_R1); -+ writel(85, mg_base + MGC_MG_TPG_G1); -+ writel(67, mg_base + MGC_MG_TPG_B1); -+ -+ writel(0x0, mg_base + MGC_MG_TPG_FACTORS); -+ -+ /* hor_mask [15:0] ver_mask [31:16] */ -+ writel(0xffffffff, mg_base + MGC_MG_TPG_MASKS); -+ /* xy_mask [11:0] */ -+ writel(0xfff, mg_base + MGC_MG_TPG_XY_MASK); -+ writel(((MGC_TPG_TILE_WIDTH << 16) | MGC_TPG_TILE_HEIGHT), -+ mg_base + MGC_MG_TPG_TILE_DIM); -+ -+ writel(dto, mg_base + MGC_MG_DTO_SPEED_CTRL_INCR_VAL); -+ writel(1, mg_base + MGC_MG_DTO_SPEED_CTRL_EN); -+ -+ /* disable err_injection */ -+ writel(0, mg_base + MGC_MG_ERR_INJECT); -+ writel(0, mg_base + MGC_MG_ERR_LOCATION); -+ -+ ipu7_mipigen_regdump(tpg, mg_base); -+ -+ dev_dbg(dev, "starting MG%d streaming...\n", csi_port); -+ -+ /* kick and start */ -+ writel(port_map, mgc_base + MGC_KICK); -+ -+ return 0; -+} -+ -+static void ipu7_isys_ungate_mgc(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct ipu7_isys_csi2 *csi2; -+ u32 offset; -+ struct ipu7_isys *isys = tpg->isys; -+ -+ csi2 = &isys->csi2[tpg->index]; -+ offset = IS_IO_GPREGS_BASE; -+ -+ /* MGC is in use by SW or not */ -+ if (enable) -+ writel(1, csi2->base + offset + MGC_CLK_GATE); -+ else -+ writel(0, csi2->base + offset + MGC_CLK_GATE); -+} -+ -+static void ipu7_isys_mgc_csi2_s_stream(struct ipu7_isys_tpg *tpg, int enable) -+{ -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ struct ipu7_isys *isys = tpg->isys; -+ struct ipu7_isys_csi2 *csi2; -+ u32 port, offset, val; -+ void __iomem *isys_base = isys->pdata->base; -+ -+ port = tpg->index; -+ csi2 = &isys->csi2[port]; -+ -+ offset = IS_IO_GPREGS_BASE; -+ val = readl(isys_base + offset + CSI_PORT_CLK_GATE); -+ dev_dbg(dev, "current CSI port %u clk gate 0x%x\n", port, val); -+ -+ if (!enable) { -+ writel(~(1 << port) & val, -+ isys_base + offset + CSI_PORT_CLK_GATE); -+ return; -+ } -+ -+ /* set csi port is using by SW */ -+ writel(1 << port | val, isys_base + offset + CSI_PORT_CLK_GATE); -+ /* input is coming from MGC */ -+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port); -+ writel(CSI_MIPIGEN_INPUT, -+ csi2->base + offset + CSI2_ADPL_INPUT_MODE); -+} -+ -+/* TODO: add the processing of vc */ -+int tpg_set_stream(struct v4l2_subdev *sd, int enable) -+{ -+ struct ipu7_isys_tpg *tpg = to_ipu7_isys_tpg(sd); -+ struct ipu7_isys_stream *stream = tpg->av->stream; -+ struct device *dev = &tpg->isys->adev->auxdev.dev; -+ int ret; -+ -+ if (tpg->index >= IPU7_ISYS_CSI_PORT_NUM) { -+ dev_err(dev, "invalid MGC index %d\n", tpg->index); -+ return -EINVAL; -+ } -+ -+ if (!enable) { -+ /* Stop MGC */ -+ stream->asd->is_tpg = false; -+ stream->asd = NULL; -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ ret = tpg_stop_stream(tpg); -+ ipu7_isys_ungate_mgc(tpg, enable); -+ -+ return ret; -+ } -+ -+ stream->asd = &tpg->asd; -+ /* ungate the MGC clock to program */ -+ ipu7_isys_ungate_mgc(tpg, enable); -+ /* Start MGC */ -+ ret = tpg_start_stream(tpg); -+ v4l2_ctrl_handler_setup(&tpg->asd.ctrl_handler); -+ ipu7_isys_mgc_csi2_s_stream(tpg, enable); -+ -+ return ret; -+} -+ -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg) -+{ -+ v4l2_device_unregister_subdev(&tpg->asd.sd); -+ ipu7_isys_subdev_cleanup(&tpg->asd); -+} -+ -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index) -+{ -+ struct device *dev = &isys->adev->auxdev.dev; -+ int ret; -+ -+ tpg->isys = isys; -+ tpg->base = base; -+ tpg->sel = sel; -+ tpg->index = index; -+ -+ tpg->asd.sd.entity.ops = &tpg_entity_ops; -+ tpg->asd.ctrl_init = ipu7_isys_tpg_init_controls; -+ tpg->asd.isys = isys; -+ -+ ret = ipu7_isys_subdev_init(&tpg->asd, &tpg_sd_ops, 5, -+ NR_OF_TPG_SINK_PADS, NR_OF_TPG_SOURCE_PADS); -+ if (ret) -+ return ret; -+ -+ tpg->asd.sd.flags &= ~V4L2_SUBDEV_FL_STREAMS; -+ tpg->asd.sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; -+ tpg->asd.pad[TPG_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; -+ -+ tpg->asd.source = IPU_INSYS_MIPI_PORT_0 + index; -+ tpg->asd.supported_codes = tpg_supported_codes; -+ tpg->asd.sd.internal_ops = &ipu7_isys_tpg_internal_ops; -+ -+ snprintf(tpg->asd.sd.name, sizeof(tpg->asd.sd.name), -+ IPU_ISYS_ENTITY_PREFIX " TPG %u", index); -+ v4l2_set_subdevdata(&tpg->asd.sd, &tpg->asd); -+ -+ ret = v4l2_subdev_init_finalize(&tpg->asd.sd); -+ if (ret) { -+ dev_err(dev, "failed to finalize subdev (%d)\n", ret); -+ goto fail; -+ } -+ -+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &tpg->asd.sd); -+ if (ret) { -+ dev_info(dev, "can't register v4l2 subdev\n"); -+ goto fail; -+ } -+ -+ return 0; -+ -+fail: -+ ipu7_isys_tpg_cleanup(tpg); -+ -+ return ret; -+} -diff --git a/drivers/staging/media/ipu7/ipu7-isys-tpg.h b/drivers/staging/media/ipu7/ipu7-isys-tpg.h -new file mode 100644 -index 000000000000..e2542a6472e3 ---- /dev/null -+++ b/drivers/staging/media/ipu7/ipu7-isys-tpg.h -@@ -0,0 +1,70 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2013 - 2025 Intel Corporation -+ */ -+ -+#ifndef IPU7_ISYS_TPG_H -+#define IPU7_ISYS_TPG_H -+ -+#include -+#include -+#include -+ -+#include "ipu7-isys-subdev.h" -+#include "ipu7-isys-video.h" -+#include "ipu7-isys-queue.h" -+ -+struct ipu7_isys_tpg_pdata; -+struct ipu7_isys; -+ -+#define TPG_PAD_SOURCE 0 -+#define NR_OF_TPG_PADS 1 -+#define NR_OF_TPG_SOURCE_PADS 1 -+#define NR_OF_TPG_SINK_PADS 0 -+#define NR_OF_TPG_STREAMS 1 -+ -+enum isys_tpg_mode { -+ TPG_MODE_RAMP = 0, -+ TPG_MODE_CHECKERBOARD = 1, -+ TPG_MODE_MONO = 2, -+ TPG_MODE_COLOR_PALETTE = 3, -+}; -+ -+/* -+ * struct ipu7_isys_tpg -+ * -+ * @nlanes: number of lanes in the receiver -+ */ -+struct ipu7_isys_tpg { -+ struct ipu7_isys_subdev asd; -+ struct ipu7_isys_tpg_pdata *pdata; -+ struct ipu7_isys *isys; -+ struct ipu7_isys_video *av; -+ -+ /* MG base not MGC */ -+ void __iomem *base; -+ void __iomem *sel; -+ unsigned int index; -+ -+ struct v4l2_ctrl *hblank; -+ struct v4l2_ctrl *vblank; -+ struct v4l2_ctrl *pixel_rate; -+}; -+ -+#define ipu7_isys_subdev_to_tpg(__sd) \ -+ container_of(__sd, struct ipu7_isys_tpg, asd) -+ -+#define to_ipu7_isys_tpg(sd) \ -+ container_of(to_ipu7_isys_subdev(sd), \ -+ struct ipu7_isys_tpg, asd) -+ -+void ipu7_isys_tpg_sof_event_by_stream(struct ipu7_isys_stream *stream); -+void ipu7_isys_tpg_eof_event_by_stream(struct ipu7_isys_stream *stream); -+int ipu7_isys_tpg_init(struct ipu7_isys_tpg *tpg, -+ struct ipu7_isys *isys, -+ void __iomem *base, void __iomem *sel, -+ unsigned int index); -+void ipu7_isys_tpg_cleanup(struct ipu7_isys_tpg *tpg); -+int tpg_set_stream(struct v4l2_subdev *sd, int enable); -+ -+#endif /* IPU7_ISYS_TPG_H */ --- -2.43.0 - diff --git a/SPECS/kernel/0013-perf-x86-intel-Initialize-architectural-PEBS.perf b/SPECS/kernel/0013-perf-x86-intel-Initialize-architectural-PEBS.perf deleted file mode 100644 index f4c7bb59a..000000000 --- a/SPECS/kernel/0013-perf-x86-intel-Initialize-architectural-PEBS.perf +++ /dev/null @@ -1,381 +0,0 @@ -From b4743cb44b027e0b485dfe784b53a0b2c95cfda6 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 17 Feb 2025 11:01:45 +0000 -Subject: [PATCH 013/100] perf/x86/intel: Initialize architectural PEBS - -arch-PEBS leverages CPUID.23H.4/5 sub-leaves enumerate arch-PEBS -supported capabilities and counters bitmap. This patch parses these 2 -sub-leaves and initializes arch-PEBS capabilities and corresponding -structures. - -Since IA32_PEBS_ENABLE and MSR_PEBS_DATA_CFG MSRs are no longer existed -for arch-PEBS, arch-PEBS doesn't need to manipulate these MSRs. Thus add -a simple pair of __intel_pmu_pebs_enable/disable() callbacks for -arch-PEBS. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 21 ++++++++++--- - arch/x86/events/intel/core.c | 46 ++++++++++++++++++--------- - arch/x86/events/intel/ds.c | 52 ++++++++++++++++++++++++++----- - arch/x86/events/perf_event.h | 25 +++++++++++++-- - arch/x86/include/asm/perf_event.h | 7 ++++- - 5 files changed, 120 insertions(+), 31 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 24fae86f2682..94a8ea4389e4 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -554,14 +554,22 @@ static inline int precise_br_compat(struct perf_event *event) - return m == b; - } - --int x86_pmu_max_precise(void) -+int x86_pmu_max_precise(struct pmu *pmu) - { - int precise = 0; - -- /* Support for constant skid */ - if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) { -- precise++; -+ /* arch PEBS */ -+ if (x86_pmu.arch_pebs) { -+ precise = 2; -+ if (hybrid(pmu, arch_pebs_cap).pdists) -+ precise++; -+ -+ return precise; -+ } - -+ /* legacy PEBS - support for constant skid */ -+ precise++; - /* Support for IP fixup */ - if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2) - precise++; -@@ -569,13 +577,14 @@ int x86_pmu_max_precise(void) - if (x86_pmu.pebs_prec_dist) - precise++; - } -+ - return precise; - } - - int x86_pmu_hw_config(struct perf_event *event) - { - if (event->attr.precise_ip) { -- int precise = x86_pmu_max_precise(); -+ int precise = x86_pmu_max_precise(event->pmu); - - if (event->attr.precise_ip > precise) - return -EOPNOTSUPP; -@@ -2631,7 +2640,9 @@ static ssize_t max_precise_show(struct device *cdev, - struct device_attribute *attr, - char *buf) - { -- return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise()); -+ struct pmu *pmu = dev_get_drvdata(cdev); -+ -+ return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise(pmu)); - } - - static DEVICE_ATTR_RO(max_precise); -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 0b57a9146b82..dcc796d00f05 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5420,34 +5420,49 @@ static inline bool intel_pmu_broken_perf_cap(void) - - static void update_pmu_cap(struct pmu *pmu) - { -- unsigned int cntr, fixed_cntr, ecx, edx; -- union cpuid35_eax eax; -- union cpuid35_ebx ebx; -+ unsigned int eax, ebx, ecx, edx; -+ union cpuid35_eax eax_0; -+ union cpuid35_ebx ebx_0; - -- cpuid(ARCH_PERFMON_EXT_LEAF, &eax.full, &ebx.full, &ecx, &edx); -+ cpuid(ARCH_PERFMON_EXT_LEAF, &eax_0.full, &ebx_0.full, &ecx, &edx); - -- if (ebx.split.umask2) -+ if (ebx_0.split.umask2) - hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_UMASK2; -- if (ebx.split.eq) -+ if (ebx_0.split.eq) - hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_EQ; - -- if (eax.split.cntr_subleaf) { -+ if (eax_0.split.cntr_subleaf) { - cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_NUM_COUNTER_LEAF, -- &cntr, &fixed_cntr, &ecx, &edx); -- hybrid(pmu, cntr_mask64) = cntr; -- hybrid(pmu, fixed_cntr_mask64) = fixed_cntr; -+ &eax, &ebx, &ecx, &edx); -+ hybrid(pmu, cntr_mask64) = eax; -+ hybrid(pmu, fixed_cntr_mask64) = ebx; - } - -- if (eax.split.acr_subleaf) { -+ if (eax_0.split.acr_subleaf) { - cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_ACR_LEAF, -- &cntr, &fixed_cntr, &ecx, &edx); -+ &eax, &ebx, &ecx, &edx); - /* The mask of the counters which can be reloaded */ -- hybrid(pmu, acr_cntr_mask64) = cntr | ((u64)fixed_cntr << INTEL_PMC_IDX_FIXED); -+ hybrid(pmu, acr_cntr_mask64) = eax | ((u64)ebx << INTEL_PMC_IDX_FIXED); - - /* The mask of the counters which can cause a reload of reloadable counters */ - hybrid(pmu, acr_cause_mask64) = ecx | ((u64)edx << INTEL_PMC_IDX_FIXED); - } - -+ /* Bits[5:4] should be set simultaneously if arch-PEBS is supported */ -+ if (eax_0.split.pebs_caps_subleaf && eax_0.split.pebs_cnts_subleaf) { -+ cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_CAP_LEAF, -+ &eax, &ebx, &ecx, &edx); -+ hybrid(pmu, arch_pebs_cap).caps = (u64)ebx << 32; -+ -+ cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_COUNTER_LEAF, -+ &eax, &ebx, &ecx, &edx); -+ hybrid(pmu, arch_pebs_cap).counters = ((u64)ecx << 32) | eax; -+ hybrid(pmu, arch_pebs_cap).pdists = ((u64)edx << 32) | ebx; -+ } else { -+ WARN_ON(x86_pmu.arch_pebs == 1); -+ x86_pmu.arch_pebs = 0; -+ } -+ - if (!intel_pmu_broken_perf_cap()) { - /* Perf Metric (Bit 15) and PEBS via PT (Bit 16) are hybrid enumeration */ - rdmsrq(MSR_IA32_PERF_CAPABILITIES, hybrid(pmu, intel_cap).capabilities); -@@ -6396,7 +6411,7 @@ tsx_is_visible(struct kobject *kobj, struct attribute *attr, int i) - static umode_t - pebs_is_visible(struct kobject *kobj, struct attribute *attr, int i) - { -- return x86_pmu.ds_pebs ? attr->mode : 0; -+ return intel_pmu_has_pebs() ? attr->mode : 0; - } - - static umode_t -@@ -7872,6 +7887,9 @@ __init int intel_pmu_init(void) - if (!is_hybrid() && boot_cpu_has(X86_FEATURE_ARCH_PERFMON_EXT)) - update_pmu_cap(NULL); - -+ if (x86_pmu.arch_pebs) -+ pr_cont("Architectural PEBS, "); -+ - intel_pmu_check_counters_mask(&x86_pmu.cntr_mask64, - &x86_pmu.fixed_cntr_mask64, - &x86_pmu.intel_ctrl); -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index b23c49e2e06f..19d707933d61 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1531,6 +1531,15 @@ static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc) - intel_pmu_drain_pebs_buffer(); - } - -+static void __intel_pmu_pebs_enable(struct perf_event *event) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct hw_perf_event *hwc = &event->hw; -+ -+ hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; -+ cpuc->pebs_enabled |= 1ULL << hwc->idx; -+} -+ - void intel_pmu_pebs_enable(struct perf_event *event) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -@@ -1539,9 +1548,7 @@ void intel_pmu_pebs_enable(struct perf_event *event) - struct debug_store *ds = cpuc->ds; - unsigned int idx = hwc->idx; - -- hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; -- -- cpuc->pebs_enabled |= 1ULL << hwc->idx; -+ __intel_pmu_pebs_enable(event); - - if ((event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) && (x86_pmu.version < 5)) - cpuc->pebs_enabled |= 1ULL << (hwc->idx + 32); -@@ -1603,14 +1610,22 @@ void intel_pmu_pebs_del(struct perf_event *event) - pebs_update_state(needed_cb, cpuc, event, false); - } - --void intel_pmu_pebs_disable(struct perf_event *event) -+static void __intel_pmu_pebs_disable(struct perf_event *event) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - struct hw_perf_event *hwc = &event->hw; - - intel_pmu_drain_large_pebs(cpuc); -- - cpuc->pebs_enabled &= ~(1ULL << hwc->idx); -+ hwc->config |= ARCH_PERFMON_EVENTSEL_INT; -+} -+ -+void intel_pmu_pebs_disable(struct perf_event *event) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct hw_perf_event *hwc = &event->hw; -+ -+ __intel_pmu_pebs_disable(event); - - if ((event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) && - (x86_pmu.version < 5)) -@@ -1622,8 +1637,6 @@ void intel_pmu_pebs_disable(struct perf_event *event) - - if (cpuc->enabled) - wrmsrq(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled); -- -- hwc->config |= ARCH_PERFMON_EVENTSEL_INT; - } - - void intel_pmu_pebs_enable_all(void) -@@ -2668,11 +2681,26 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - } - } - -+static void __init intel_arch_pebs_init(void) -+{ -+ /* -+ * Current hybrid platforms always both support arch-PEBS or not -+ * on all kinds of cores. So directly set x86_pmu.arch_pebs flag -+ * if boot cpu supports arch-PEBS. -+ */ -+ x86_pmu.arch_pebs = 1; -+ x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; -+ x86_pmu.pebs_capable = ~0ULL; -+ -+ x86_pmu.pebs_enable = __intel_pmu_pebs_enable; -+ x86_pmu.pebs_disable = __intel_pmu_pebs_disable; -+} -+ - /* - * PEBS probe and setup - */ - --void __init intel_pebs_init(void) -+static void __init intel_ds_pebs_init(void) - { - /* - * No support for 32bit formats -@@ -2787,6 +2815,14 @@ void __init intel_pebs_init(void) - } - } - -+void __init intel_pebs_init(void) -+{ -+ if (x86_pmu.intel_cap.pebs_format == 0xf) -+ intel_arch_pebs_init(); -+ else -+ intel_ds_pebs_init(); -+} -+ - void perf_restore_debug_store(void) - { - struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds); -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index 2b969386dcdd..a5145e8f1ddb 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -708,6 +708,12 @@ enum hybrid_pmu_type { - hybrid_big_small_tiny = hybrid_big | hybrid_small_tiny, - }; - -+struct arch_pebs_cap { -+ u64 caps; -+ u64 counters; -+ u64 pdists; -+}; -+ - struct x86_hybrid_pmu { - struct pmu pmu; - const char *name; -@@ -752,6 +758,8 @@ struct x86_hybrid_pmu { - mid_ack :1, - enabled_ack :1; - -+ struct arch_pebs_cap arch_pebs_cap; -+ - u64 pebs_data_source[PERF_PEBS_DATA_SOURCE_MAX]; - }; - -@@ -906,7 +914,7 @@ struct x86_pmu { - union perf_capabilities intel_cap; - - /* -- * Intel DebugStore bits -+ * Intel DebugStore and PEBS bits - */ - unsigned int bts :1, - bts_active :1, -@@ -917,7 +925,8 @@ struct x86_pmu { - pebs_no_tlb :1, - pebs_no_isolation :1, - pebs_block :1, -- pebs_ept :1; -+ pebs_ept :1, -+ arch_pebs :1; - int pebs_record_size; - int pebs_buffer_size; - u64 pebs_events_mask; -@@ -929,6 +938,11 @@ struct x86_pmu { - u64 rtm_abort_event; - u64 pebs_capable; - -+ /* -+ * Intel Architectural PEBS -+ */ -+ struct arch_pebs_cap arch_pebs_cap; -+ - /* - * Intel LBR - */ -@@ -1217,7 +1231,7 @@ int x86_reserve_hardware(void); - - void x86_release_hardware(void); - --int x86_pmu_max_precise(void); -+int x86_pmu_max_precise(struct pmu *pmu); - - void hw_perf_lbr_event_destroy(struct perf_event *event); - -@@ -1792,6 +1806,11 @@ static inline int intel_pmu_max_num_pebs(struct pmu *pmu) - return fls((u32)hybrid(pmu, pebs_events_mask)); - } - -+static inline bool intel_pmu_has_pebs(void) -+{ -+ return x86_pmu.ds_pebs || x86_pmu.arch_pebs; -+} -+ - #else /* CONFIG_CPU_SUP_INTEL */ - - static inline void reserve_ds_buffers(void) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 1eb31cd9fceb..a81084f58de4 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -200,6 +200,8 @@ union cpuid10_edx { - #define ARCH_PERFMON_EXT_LEAF 0x00000023 - #define ARCH_PERFMON_NUM_COUNTER_LEAF 0x1 - #define ARCH_PERFMON_ACR_LEAF 0x2 -+#define ARCH_PERFMON_PEBS_CAP_LEAF 0x4 -+#define ARCH_PERFMON_PEBS_COUNTER_LEAF 0x5 - - union cpuid35_eax { - struct { -@@ -210,7 +212,10 @@ union cpuid35_eax { - unsigned int acr_subleaf:1; - /* Events Sub-Leaf */ - unsigned int events_subleaf:1; -- unsigned int reserved:28; -+ /* arch-PEBS Sub-Leaves */ -+ unsigned int pebs_caps_subleaf:1; -+ unsigned int pebs_cnts_subleaf:1; -+ unsigned int reserved:26; - } split; - unsigned int full; - }; --- -2.43.0 - diff --git a/SPECS/kernel/0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf b/SPECS/kernel/0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf new file mode 100644 index 000000000..a723eaeb8 --- /dev/null +++ b/SPECS/kernel/0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf @@ -0,0 +1,110 @@ +From 77f8e1175fdfcf2f6df88260a7a3cc7721c764f4 Mon Sep 17 00:00:00 2001 +From: Zide Chen +Date: Wed, 17 Dec 2025 13:22:20 -0800 +Subject: [PATCH 13/13] perf/x86/intel/uncore: Add missing PMON units for + Panther Lake + +Besides CBOX, Panther Lake includes several legacy uncore PMON units +not enumerated via discovery tables, including cNCU, SANTA, and +ia_core_bridge. + +The cNCU PMON is similar to Meteor Lake but has two boxes with two +counters each. SANTA and IA Core Bridge PMON units follow the legacy +model used on Lunar Lake, Meteor Lake, and others. + +Panther Lake implements the Global Control Register; the freeze_all bit +must be cleared before programming counters. + +Signed-off-by: Zide Chen +--- + arch/x86/events/intel/uncore.c | 1 + + arch/x86/events/intel/uncore_snb.c | 45 ++++++++++++++++++++++++++++++ + 2 files changed, 46 insertions(+) + +diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c +index e47483d1207d4..efdaa0d111d2a 100644 +--- a/arch/x86/events/intel/uncore.c ++++ b/arch/x86/events/intel/uncore.c +@@ -1814,6 +1814,7 @@ static const struct uncore_plat_init ptl_uncore_init __initconst = { + .cpu_init = ptl_uncore_cpu_init, + .mmio_init = ptl_uncore_mmio_init, + .domain[0].discovery_base = UNCORE_DISCOVERY_MSR, ++ .domain[0].global_init = uncore_mmio_global_init, + }; + + static const struct uncore_plat_init icx_uncore_init __initconst = { +diff --git a/arch/x86/events/intel/uncore_snb.c b/arch/x86/events/intel/uncore_snb.c +index 807e582b8f17d..c663b00b68fe6 100644 +--- a/arch/x86/events/intel/uncore_snb.c ++++ b/arch/x86/events/intel/uncore_snb.c +@@ -245,6 +245,17 @@ + #define MTL_UNC_HBO_CTR 0x2048 + #define MTL_UNC_HBO_CTRL 0x2042 + ++/* PTL Low Power Bridge register */ ++#define PTL_UNC_IA_CORE_BRIDGE_PER_CTR0 0x2028 ++#define PTL_UNC_IA_CORE_BRIDGE_PERFEVTSEL0 0x2022 ++ ++/* PTL Santa register */ ++#define PTL_UNC_SANTA_CTR0 0x2418 ++#define PTL_UNC_SANTA_CTRL0 0x2412 ++ ++/* PTL cNCU register */ ++#define PTL_UNC_CNCU_MSR_OFFSET 0x140 ++ + DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); + DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15"); + DEFINE_UNCORE_FORMAT_ATTR(chmask, chmask, "config:8-11"); +@@ -1921,8 +1932,36 @@ void ptl_uncore_mmio_init(void) + ptl_uncores); + } + ++static struct intel_uncore_type ptl_uncore_ia_core_bridge = { ++ .name = "ia_core_bridge", ++ .num_counters = 2, ++ .num_boxes = 1, ++ .perf_ctr_bits = 48, ++ .perf_ctr = PTL_UNC_IA_CORE_BRIDGE_PER_CTR0, ++ .event_ctl = PTL_UNC_IA_CORE_BRIDGE_PERFEVTSEL0, ++ .event_mask = ADL_UNC_RAW_EVENT_MASK, ++ .ops = &icl_uncore_msr_ops, ++ .format_group = &adl_uncore_format_group, ++}; ++ ++static struct intel_uncore_type ptl_uncore_santa = { ++ .name = "santa", ++ .num_counters = 2, ++ .num_boxes = 2, ++ .perf_ctr_bits = 48, ++ .perf_ctr = PTL_UNC_SANTA_CTR0, ++ .event_ctl = PTL_UNC_SANTA_CTRL0, ++ .event_mask = ADL_UNC_RAW_EVENT_MASK, ++ .msr_offset = SNB_UNC_CBO_MSR_OFFSET, ++ .ops = &icl_uncore_msr_ops, ++ .format_group = &adl_uncore_format_group, ++}; ++ + static struct intel_uncore_type *ptl_msr_uncores[] = { + &mtl_uncore_cbox, ++ &ptl_uncore_ia_core_bridge, ++ &ptl_uncore_santa, ++ &mtl_uncore_cncu, + NULL + }; + +@@ -1930,6 +1969,12 @@ void ptl_uncore_cpu_init(void) + { + mtl_uncore_cbox.num_boxes = 6; + mtl_uncore_cbox.ops = &lnl_uncore_msr_ops; ++ ++ mtl_uncore_cncu.num_counters = 2; ++ mtl_uncore_cncu.num_boxes = 2; ++ mtl_uncore_cncu.msr_offset = PTL_UNC_CNCU_MSR_OFFSET; ++ mtl_uncore_cncu.single_fixed = 0; ++ + uncore_msr_uncores = ptl_msr_uncores; + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl b/SPECS/kernel/0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl new file mode 100644 index 000000000..7370634a1 --- /dev/null +++ b/SPECS/kernel/0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl @@ -0,0 +1,222 @@ +From 89288056b82c56b3e09188f93a98d52319fda6bf Mon Sep 17 00:00:00 2001 +From: Kuppuswamy Sathyanarayanan +Date: Thu, 20 Nov 2025 16:05:38 -0800 +Subject: [PATCH 13/17] powercap: intel_rapl: Prepare read_raw() interface for + atomic-context callers + +The current read_raw() implementation of the TPMI, MMIO and MSR +interfaces does not distinguish between atomic and non-atomic callers. + +rapl_msr_read_raw() uses rdmsrq_safe_on_cpu(), which can sleep and +issue cross CPU calls. When MSR-based RAPL PMU support is enabled, PMU +event handlers can invoke this function from atomic context where +sleeping or rescheduling is not allowed. In atomic context, the caller +is already executing on the target CPU, so a direct rdmsrq() is +sufficient. + +To support such usage, introduce an atomic flag to the read_raw() +interface to allow callers pass the context information. Modify the +common RAPL code to propagate this flag, and set the flag to reflect +the calling contexts. + +Utilize the atomic flag in rapl_msr_read_raw() to perform direct MSR +read with rdmsrq() when running in atomic context, and a sanity check +to ensure target CPU matches the current CPU for such use cases. + +The TPMI and MMIO implementations do not require special atomic +handling, so the flag is ignored in those paths. + +This is a preparatory patch for adding MSR-based RAPL PMU support. + +Signed-off-by: Kuppuswamy Sathyanarayanan +Reviewed-by: Srinivas Pandruvada +[ rjw: Subject tweak ] +Link: https://patch.msgid.link/20251121000539.386069-2-sathyanarayanan.kuppuswamy@linux.intel.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/powercap/intel_rapl_common.c | 24 ++++++++++--------- + drivers/powercap/intel_rapl_msr.c | 16 ++++++++++++- + drivers/powercap/intel_rapl_tpmi.c | 2 +- + .../int340x_thermal/processor_thermal_rapl.c | 2 +- + include/linux/intel_rapl.h | 2 +- + 5 files changed, 31 insertions(+), 15 deletions(-) + +diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c +index 57bebd07c7d0d..47ec34d4c0997 100644 +--- a/drivers/powercap/intel_rapl_common.c ++++ b/drivers/powercap/intel_rapl_common.c +@@ -253,7 +253,8 @@ struct rapl_primitive_info { + static void rapl_init_domains(struct rapl_package *rp); + static int rapl_read_data_raw(struct rapl_domain *rd, + enum rapl_primitives prim, +- bool xlate, u64 *data); ++ bool xlate, u64 *data, ++ bool atomic); + static int rapl_write_data_raw(struct rapl_domain *rd, + enum rapl_primitives prim, + unsigned long long value); +@@ -289,7 +290,7 @@ static int get_energy_counter(struct powercap_zone *power_zone, + cpus_read_lock(); + rd = power_zone_to_rapl_domain(power_zone); + +- if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now)) { ++ if (!rapl_read_data_raw(rd, ENERGY_COUNTER, true, &energy_now, false)) { + *energy_raw = energy_now; + cpus_read_unlock(); + +@@ -830,7 +831,8 @@ prim_fixups(struct rapl_domain *rd, enum rapl_primitives prim) + * 63-------------------------- 31--------------------------- 0 + */ + static int rapl_read_data_raw(struct rapl_domain *rd, +- enum rapl_primitives prim, bool xlate, u64 *data) ++ enum rapl_primitives prim, bool xlate, u64 *data, ++ bool atomic) + { + u64 value; + enum rapl_primitives prim_fixed = prim_fixups(rd, prim); +@@ -852,7 +854,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, + + ra.mask = rpi->mask; + +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, atomic)) { + pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg.val, rd->rp->name, rd->name); + return -EIO; + } +@@ -904,7 +906,7 @@ static int rapl_read_pl_data(struct rapl_domain *rd, int pl, + if (!is_pl_valid(rd, pl)) + return -EINVAL; + +- return rapl_read_data_raw(rd, prim, xlate, data); ++ return rapl_read_data_raw(rd, prim, xlate, data, false); + } + + static int rapl_write_pl_data(struct rapl_domain *rd, int pl, +@@ -941,7 +943,7 @@ static int rapl_check_unit_core(struct rapl_domain *rd) + + ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; + ra.mask = ~0; +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, false)) { + pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", + ra.reg.val, rd->rp->name, rd->name); + return -ENODEV; +@@ -969,7 +971,7 @@ static int rapl_check_unit_atom(struct rapl_domain *rd) + + ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; + ra.mask = ~0; +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, false)) { + pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", + ra.reg.val, rd->rp->name, rd->name); + return -ENODEV; +@@ -1156,7 +1158,7 @@ static int rapl_check_unit_tpmi(struct rapl_domain *rd) + + ra.reg = rd->regs[RAPL_DOMAIN_REG_UNIT]; + ra.mask = ~0; +- if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra)) { ++ if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, false)) { + pr_err("Failed to read power unit REG 0x%llx on %s:%s, exit.\n", + ra.reg.val, rd->rp->name, rd->name); + return -ENODEV; +@@ -1328,7 +1330,7 @@ static void rapl_update_domain_data(struct rapl_package *rp) + struct rapl_primitive_info *rpi = get_rpi(rp, prim); + + if (!rapl_read_data_raw(&rp->domains[dmn], prim, +- rpi->unit, &val)) ++ rpi->unit, &val, false)) + rp->domains[dmn].rdd.primitives[prim] = val; + } + } +@@ -1428,7 +1430,7 @@ static int rapl_check_domain(int domain, struct rapl_package *rp) + */ + + ra.mask = ENERGY_STATUS_MASK; +- if (rp->priv->read_raw(get_rid(rp), &ra) || !ra.value) ++ if (rp->priv->read_raw(get_rid(rp), &ra, false) || !ra.value) + return -ENODEV; + + return 0; +@@ -1639,7 +1641,7 @@ static u64 event_read_counter(struct perf_event *event) + if (event->hw.idx < 0) + return 0; + +- ret = rapl_read_data_raw(&rp->domains[event->hw.idx], ENERGY_COUNTER, false, &val); ++ ret = rapl_read_data_raw(&rp->domains[event->hw.idx], ENERGY_COUNTER, false, &val, true); + + /* Return 0 for failed read */ + if (ret) +diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c +index c6b9a7debc354..6e3c50af09128 100644 +--- a/drivers/powercap/intel_rapl_msr.c ++++ b/drivers/powercap/intel_rapl_msr.c +@@ -102,12 +102,26 @@ static int rapl_cpu_down_prep(unsigned int cpu) + return 0; + } + +-static int rapl_msr_read_raw(int cpu, struct reg_action *ra) ++static int rapl_msr_read_raw(int cpu, struct reg_action *ra, bool atomic) + { ++ /* ++ * When called from atomic-context (eg PMU event handler) ++ * perform MSR read directly using rdmsrq(). ++ */ ++ if (atomic) { ++ if (unlikely(smp_processor_id() != cpu)) ++ return -EIO; ++ ++ rdmsrq(ra->reg.msr, ra->value); ++ goto out; ++ } ++ + if (rdmsrq_safe_on_cpu(cpu, ra->reg.msr, &ra->value)) { + pr_debug("failed to read msr 0x%x on cpu %d\n", ra->reg.msr, cpu); + return -EIO; + } ++ ++out: + ra->value &= ra->mask; + return 0; + } +diff --git a/drivers/powercap/intel_rapl_tpmi.c b/drivers/powercap/intel_rapl_tpmi.c +index 82201bf4685d4..0a0b85f4528b1 100644 +--- a/drivers/powercap/intel_rapl_tpmi.c ++++ b/drivers/powercap/intel_rapl_tpmi.c +@@ -60,7 +60,7 @@ static DEFINE_MUTEX(tpmi_rapl_lock); + + static struct powercap_control_type *tpmi_control_type; + +-static int tpmi_rapl_read_raw(int id, struct reg_action *ra) ++static int tpmi_rapl_read_raw(int id, struct reg_action *ra, bool atomic) + { + if (!ra->reg.mmio) + return -EINVAL; +diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +index bde2cc386afdd..bf51a17c5be61 100644 +--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c ++++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c +@@ -19,7 +19,7 @@ static const struct rapl_mmio_regs rapl_mmio_default = { + .limits[RAPL_DOMAIN_DRAM] = BIT(POWER_LIMIT2), + }; + +-static int rapl_mmio_read_raw(int cpu, struct reg_action *ra) ++static int rapl_mmio_read_raw(int cpu, struct reg_action *ra, bool atomic) + { + if (!ra->reg.mmio) + return -EINVAL; +diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h +index c0397423d3a89..e9ade2ff4af66 100644 +--- a/include/linux/intel_rapl.h ++++ b/include/linux/intel_rapl.h +@@ -152,7 +152,7 @@ struct rapl_if_priv { + union rapl_reg reg_unit; + union rapl_reg regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX]; + int limits[RAPL_DOMAIN_MAX]; +- int (*read_raw)(int id, struct reg_action *ra); ++ int (*read_raw)(int id, struct reg_action *ra, bool atomic); + int (*write_raw)(int id, struct reg_action *ra); + void *defaults; + void *rpi; +-- +2.43.0 + diff --git a/SPECS/kernel/0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo b/SPECS/kernel/0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo new file mode 100644 index 000000000..4a90d5a58 --- /dev/null +++ b/SPECS/kernel/0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo @@ -0,0 +1,35 @@ +From 18949ad92e8c5aef6f621b1ffe77d1fbc619685c Mon Sep 17 00:00:00 2001 +From: Malaya Kumar Rout +Date: Sat, 22 Nov 2025 19:14:54 +0530 +Subject: [PATCH 13/21] tools/power x86_energy_perf_policy: Fix format string + in error message + +The error message in validate_cpu_selected_set() uses an incomplete +format specifier "cpu%" instead of "cpu%d", resulting in the error +message printing "Requested cpu% is not present" rather than +showing the actual CPU number. + +Fix the format string to properly display the CPU number. + +Signed-off-by: Malaya Kumar Rout +Signed-off-by: Len Brown +--- + tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index e68eaa9f7cd4d..b2125275c69e0 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -372,7 +372,7 @@ void validate_cpu_selected_set(void) + for (cpu = 0; cpu <= max_cpu_num; ++cpu) { + if (CPU_ISSET_S(cpu, cpu_setsize, cpu_selected_set)) + if (!CPU_ISSET_S(cpu, cpu_setsize, cpu_present_set)) +- errx(1, "Requested cpu% is not present", cpu); ++ errx(1, "Requested cpu%d is not present", cpu); + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet b/SPECS/kernel/0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet deleted file mode 100644 index 70750dc1c..000000000 --- a/SPECS/kernel/0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet +++ /dev/null @@ -1,85 +0,0 @@ -From a77961bfab3a3364a7a63fb67d1ee0be53392b47 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:44 -0700 -Subject: [PATCH 14/24] KVM: x86: Enable guest SSP read/write interface with - new uAPIs - -Enable guest shadow stack pointer(SSP) access interface with new uAPIs. -CET guest SSP is HW register which has corresponding VMCS field to save -/restore guest values when VM-{Exit,Entry} happens. KVM handles SSP as -a synthetic MSR for userspace access. - -Use a translation helper to set up mapping for SSP synthetic index and -KVM-internal MSR index so that userspace doesn't need to take care of -KVM's management for synthetic MSRs and avoid conflicts. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/uapi/asm/kvm.h | 3 +++ - arch/x86/kvm/x86.c | 10 +++++++++- - arch/x86/kvm/x86.h | 10 ++++++++++ - 3 files changed, 22 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h -index e72d9e6c1739..a4870d9c9279 100644 ---- a/arch/x86/include/uapi/asm/kvm.h -+++ b/arch/x86/include/uapi/asm/kvm.h -@@ -421,6 +421,9 @@ struct kvm_x86_reg_id { - __u16 rsvd16; - }; - -+/* KVM synthetic MSR index staring from 0 */ -+#define KVM_SYNTHETIC_GUEST_SSP 0 -+ - #define KVM_SYNC_X86_REGS (1UL << 0) - #define KVM_SYNC_X86_SREGS (1UL << 1) - #define KVM_SYNC_X86_EVENTS (1UL << 2) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index cbefc0228ba5..1a437aad26f9 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -5953,7 +5953,15 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, - - static int kvm_translate_synthetic_msr(struct kvm_x86_reg_id *reg) - { -- return -EINVAL; -+ switch (reg->index) { -+ case KVM_SYNTHETIC_GUEST_SSP: -+ reg->type = KVM_X86_REG_MSR; -+ reg->index = MSR_KVM_INTERNAL_GUEST_SSP; -+ break; -+ default: -+ return -EINVAL; -+ } -+ return 0; - } - - long kvm_arch_vcpu_ioctl(struct file *filp, -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 97e258a3e88e..cf74d877737a 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -101,6 +101,16 @@ do { \ - #define KVM_SVM_DEFAULT_PLE_WINDOW_MAX USHRT_MAX - #define KVM_SVM_DEFAULT_PLE_WINDOW 3000 - -+/* -+ * KVM's internal, non-ABI indices for synthetic MSRs. The values themselves -+ * are arbitrary and have no meaning, the only requirement is that they don't -+ * conflict with "real" MSRs that KVM supports. Use values at the upper end -+ * of KVM's reserved paravirtual MSR range to minimize churn, i.e. these values -+ * will be usable until KVM exhausts its supply of paravirtual MSR indices. -+ */ -+ -+#define MSR_KVM_INTERNAL_GUEST_SSP 0x4b564dff -+ - static inline unsigned int __grow_ple_window(unsigned int val, - unsigned int base, unsigned int modifier, unsigned int max) - { --- -2.43.0 - diff --git a/SPECS/kernel/0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi b/SPECS/kernel/0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi deleted file mode 100644 index 180b99a87..000000000 --- a/SPECS/kernel/0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi +++ /dev/null @@ -1,61 +0,0 @@ -From b638a94b8dd192801ea2f76e1b7261caf2cbf927 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 13:26:01 -0700 -Subject: [PATCH 14/44] KVM: x86: Mark CR4.FRED as not reserved - -The CR4.FRED bit, i.e., CR4[32], is no longer a reserved bit when -guest cpu cap has FRED, i.e., - 1) All of FRED KVM support is in place. - 2) Guest enumerates FRED. - -Otherwise it is still a reserved bit. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change in v4: -* Rebase on top of "guest_cpu_cap". - -Change in v3: -* Don't allow CR4.FRED=1 before all of FRED KVM support is in place - (Sean Christopherson). ---- - arch/x86/include/asm/kvm_host.h | 2 +- - arch/x86/kvm/x86.h | 2 ++ - 2 files changed, 3 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 3b38c82b4c1c..2634c16b09c4 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -142,7 +142,7 @@ - | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ - | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ - | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ -- | X86_CR4_LAM_SUP | X86_CR4_CET)) -+ | X86_CR4_LAM_SUP | X86_CR4_CET | X86_CR4_FRED)) - - #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR) - -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 25c2d7173b89..5f2279e11f87 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -688,6 +688,8 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) - if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ - !__cpu_has(__c, X86_FEATURE_IBT)) \ - __reserved_bits |= X86_CR4_CET; \ -+ if (!__cpu_has(__c, X86_FEATURE_FRED)) \ -+ __reserved_bits |= X86_CR4_FRED; \ - __reserved_bits; \ - }) - --- -2.43.0 - diff --git a/SPECS/kernel/0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi b/SPECS/kernel/0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi new file mode 100644 index 000000000..c85247f89 --- /dev/null +++ b/SPECS/kernel/0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi @@ -0,0 +1,204 @@ +From 068fe8908d3439277254912358e677dd6f5e68d0 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Thu, 13 Jun 2024 09:48:43 -0700 +Subject: [PATCH 14/44] KVM: x86: Save/restore the nested flag of an exception + +Save/restore the nested flag of an exception during VM save/restore +and live migration to ensure a correct event stack level is chosen +when a nested exception is injected through FRED event delivery. + +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v8: +* Update KVM_CAP_EXCEPTION_NESTED_FLAG, as the number in v7 is used + by another new cap. + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Add live migration support for exception nested flag (Chao Gao). +--- + Documentation/virt/kvm/api.rst | 21 ++++++++++++++++++++- + arch/x86/include/asm/kvm_host.h | 1 + + arch/x86/include/uapi/asm/kvm.h | 4 +++- + arch/x86/kvm/x86.c | 19 ++++++++++++++++++- + include/uapi/linux/kvm.h | 1 + + 5 files changed, 43 insertions(+), 3 deletions(-) + +diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst +index 57061fa29e6a0..88dbd80dcd3c8 100644 +--- a/Documentation/virt/kvm/api.rst ++++ b/Documentation/virt/kvm/api.rst +@@ -1184,6 +1184,10 @@ The following bits are defined in the flags field: + fields contain a valid state. This bit will be set whenever + KVM_CAP_EXCEPTION_PAYLOAD is enabled. + ++- KVM_VCPUEVENT_VALID_NESTED_FLAG may be set to inform that the ++ exception is a nested exception. This bit will be set whenever ++ KVM_CAP_EXCEPTION_NESTED_FLAG is enabled. ++ + - KVM_VCPUEVENT_VALID_TRIPLE_FAULT may be set to signal that the + triple_fault_pending field contains a valid state. This bit will + be set whenever KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled. +@@ -1286,6 +1290,10 @@ can be set in the flags field to signal that the + exception_has_payload, exception_payload, and exception.pending fields + contain a valid state and shall be written into the VCPU. + ++If KVM_CAP_EXCEPTION_NESTED_FLAG is enabled, KVM_VCPUEVENT_VALID_NESTED_FLAG ++can be set in the flags field to inform that the exception is a nested ++exception and exception_is_nested shall be written into the VCPU. ++ + If KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled, KVM_VCPUEVENT_VALID_TRIPLE_FAULT + can be set in flags field to signal that the triple_fault field contains + a valid state and shall be written into the VCPU. +@@ -8692,7 +8700,7 @@ given VM. + When this capability is enabled, KVM resets the VCPU when setting + MP_STATE_INIT_RECEIVED through IOCTL. The original MP_STATE is preserved. + +-7.43 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED ++7.44 KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED + ------------------------------------------- + + :Architectures: arm64 +@@ -8703,6 +8711,17 @@ This capability indicate to the userspace whether a PFNMAP memory region + can be safely mapped as cacheable. This relies on the presence of + force write back (FWB) feature support on the hardware. + ++7.45 KVM_CAP_EXCEPTION_NESTED_FLAG ++---------------------------------- ++ ++:Architectures: x86 ++:Parameters: args[0] whether feature should be enabled or not ++ ++With this capability enabled, an exception is save/restored with the ++additional information of whether it was nested or not. FRED event ++delivery uses this information to ensure a correct event stack level ++is chosen when a VM entry injects a nested exception. ++ + 8. Other capabilities. + ====================== + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 3b6dadf368eb6..5fff22d837aa0 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -1491,6 +1491,7 @@ struct kvm_arch { + bool has_mapped_host_mmio; + bool guest_can_read_msr_platform_info; + bool exception_payload_enabled; ++ bool exception_nested_flag_enabled; + + bool triple_fault_event; + +diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h +index d420c9c066d48..fbeeea236fc21 100644 +--- a/arch/x86/include/uapi/asm/kvm.h ++++ b/arch/x86/include/uapi/asm/kvm.h +@@ -331,6 +331,7 @@ struct kvm_reinject_control { + #define KVM_VCPUEVENT_VALID_SMM 0x00000008 + #define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 + #define KVM_VCPUEVENT_VALID_TRIPLE_FAULT 0x00000020 ++#define KVM_VCPUEVENT_VALID_NESTED_FLAG 0x00000040 + + /* Interrupt shadow states */ + #define KVM_X86_SHADOW_INT_MOV_SS 0x01 +@@ -368,7 +369,8 @@ struct kvm_vcpu_events { + struct { + __u8 pending; + } triple_fault; +- __u8 reserved[26]; ++ __u8 reserved[25]; ++ __u8 exception_is_nested; + __u8 exception_has_payload; + __u64 exception_payload; + }; +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 881c5543a77a5..2f364c4e35879 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -4967,6 +4967,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) + case KVM_CAP_GET_MSR_FEATURES: + case KVM_CAP_MSR_PLATFORM_INFO: + case KVM_CAP_EXCEPTION_PAYLOAD: ++ case KVM_CAP_EXCEPTION_NESTED_FLAG: + case KVM_CAP_X86_TRIPLE_FAULT_EVENT: + case KVM_CAP_SET_GUEST_DEBUG: + case KVM_CAP_LAST_CPU: +@@ -5712,6 +5713,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, + events->exception.error_code = ex->error_code; + events->exception_has_payload = ex->has_payload; + events->exception_payload = ex->payload; ++ events->exception_is_nested = ex->nested; + + events->interrupt.injected = + vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft; +@@ -5737,6 +5739,8 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, + | KVM_VCPUEVENT_VALID_SMM); + if (vcpu->kvm->arch.exception_payload_enabled) + events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD; ++ if (vcpu->kvm->arch.exception_nested_flag_enabled) ++ events->flags |= KVM_VCPUEVENT_VALID_NESTED_FLAG; + if (vcpu->kvm->arch.triple_fault_event) { + events->triple_fault.pending = kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu); + events->flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; +@@ -5751,7 +5755,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, + | KVM_VCPUEVENT_VALID_SHADOW + | KVM_VCPUEVENT_VALID_SMM + | KVM_VCPUEVENT_VALID_PAYLOAD +- | KVM_VCPUEVENT_VALID_TRIPLE_FAULT)) ++ | KVM_VCPUEVENT_VALID_TRIPLE_FAULT ++ | KVM_VCPUEVENT_VALID_NESTED_FLAG)) + return -EINVAL; + + if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) { +@@ -5766,6 +5771,13 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, + events->exception_has_payload = 0; + } + ++ if (events->flags & KVM_VCPUEVENT_VALID_NESTED_FLAG) { ++ if (!vcpu->kvm->arch.exception_nested_flag_enabled) ++ return -EINVAL; ++ } else { ++ events->exception_is_nested = 0; ++ } ++ + if ((events->exception.injected || events->exception.pending) && + (events->exception.nr > 31 || events->exception.nr == NMI_VECTOR)) + return -EINVAL; +@@ -5791,6 +5803,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, + vcpu->arch.exception.error_code = events->exception.error_code; + vcpu->arch.exception.has_payload = events->exception_has_payload; + vcpu->arch.exception.payload = events->exception_payload; ++ vcpu->arch.exception.nested = events->exception_is_nested; + + vcpu->arch.interrupt.injected = events->interrupt.injected; + vcpu->arch.interrupt.nr = events->interrupt.nr; +@@ -6911,6 +6924,10 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, + kvm->arch.exception_payload_enabled = cap->args[0]; + r = 0; + break; ++ case KVM_CAP_EXCEPTION_NESTED_FLAG: ++ kvm->arch.exception_nested_flag_enabled = cap->args[0]; ++ r = 0; ++ break; + case KVM_CAP_X86_TRIPLE_FAULT_EVENT: + kvm->arch.triple_fault_event = cap->args[0]; + r = 0; +diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h +index 52f6000ab0208..ec3cc37b93739 100644 +--- a/include/uapi/linux/kvm.h ++++ b/include/uapi/linux/kvm.h +@@ -963,6 +963,7 @@ struct kvm_enable_cap { + #define KVM_CAP_RISCV_MP_STATE_RESET 242 + #define KVM_CAP_ARM_CACHEABLE_PFNMAP_SUPPORTED 243 + #define KVM_CAP_GUEST_MEMFD_FLAGS 244 ++#define KVM_CAP_EXCEPTION_NESTED_FLAG 245 + + struct kvm_irq_routing_irqchip { + __u32 irqchip; +-- +2.43.0 + diff --git a/SPECS/kernel/0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet b/SPECS/kernel/0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet deleted file mode 100644 index b339486cf..000000000 --- a/SPECS/kernel/0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet +++ /dev/null @@ -1,97 +0,0 @@ -From 235954e7641a45928f8038d9e0f8f6cbfb57d1cb Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Thu, 10 Jun 2021 12:44:46 +0800 -Subject: [PATCH 14/19] igc: Enable HW TX Timestamp for AF_XDP ZC - -Enable HW TX Timestamp per-packet using dma write back descriptor. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc.h | 1 + - drivers/net/ethernet/intel/igc/igc_main.c | 7 ++++++- - drivers/net/ethernet/intel/igc/igc_ptp.c | 24 +++++++++++++++++++++++ - 3 files changed, 31 insertions(+), 1 deletion(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h -index 64d22481be91..3f8969c73a6d 100644 ---- a/drivers/net/ethernet/intel/igc/igc.h -+++ b/drivers/net/ethernet/intel/igc/igc.h -@@ -788,6 +788,7 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, - void igc_ptp_tx_hang(struct igc_adapter *adapter); - void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); - void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); -+ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp); - - int igc_led_setup(struct igc_adapter *adapter); - void igc_led_free(struct igc_adapter *adapter); -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index e4a8dfaf1b02..8b5f747db38b 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -3160,6 +3160,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) - tx_desc->read.buffer_addr = cpu_to_le64(dma); - - bi->type = IGC_TX_BUFFER_TYPE_XSK; -+ bi->tx_flags |= IGC_TX_FLAGS_DMA_TSTAMP; - bi->protocol = 0; - bi->bytecount = xdp_desc.len; - bi->gso_segs = 1; -@@ -3200,6 +3201,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - unsigned int i = tx_ring->next_to_clean; - struct igc_tx_buffer *tx_buffer; - union igc_adv_tx_desc *tx_desc; -+ ktime_t timestamp = 0; - u32 xsk_frames = 0; - - if (test_bit(__IGC_DOWN, &adapter->state)) -@@ -3238,7 +3240,10 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - tx_buffer->tx_flags & IGC_TX_FLAGS_DMA_TSTAMP) { - u64 tstamp = le64_to_cpu(eop_desc->wb.dma_tstamp); - -- igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); -+ if (tx_ring->xsk_pool && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) -+ timestamp = igc_tx_dma_hw_tstamp(adapter, tstamp); -+ else -+ igc_ptp_tx_dma_tstamp(adapter, tx_buffer->skb, tstamp); - } - - /* clear next_to_watch to prevent false hangs */ -diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c -index 7c220a6f601f..4fef3f1abe29 100644 ---- a/drivers/net/ethernet/intel/igc/igc_ptp.c -+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c -@@ -909,6 +909,30 @@ void igc_ptp_tx_dma_tstamp(struct igc_adapter *adapter, - skb_tstamp_tx(skb, &shhwtstamps); - } - -+ktime_t igc_tx_dma_hw_tstamp(struct igc_adapter *adapter, u64 tstamp) -+{ -+ struct skb_shared_hwtstamps shhwtstamps; -+ int adjust = 0; -+ -+ igc_ptp_dma_time_to_hwtstamp(adapter, &shhwtstamps, tstamp); -+ -+ switch (adapter->link_speed) { -+ case SPEED_10: -+ adjust = IGC_I225_TX_LATENCY_10; -+ break; -+ case SPEED_100: -+ adjust = IGC_I225_TX_LATENCY_100; -+ break; -+ case SPEED_1000: -+ adjust = IGC_I225_TX_LATENCY_1000; -+ break; -+ case SPEED_2500: -+ adjust = IGC_I225_TX_LATENCY_2500; -+ break; -+ } -+ return ktime_add_ns(shhwtstamps.hwtstamp, adjust); -+} -+ - /** - * igc_ptp_tx_tstamp_event - * @adapter: board private structure --- -2.43.0 - diff --git a/SPECS/kernel/0014-igc-Remove-XDP-metadata-invalidation.ethernet b/SPECS/kernel/0014-igc-Remove-XDP-metadata-invalidation.ethernet new file mode 100644 index 000000000..85fc57f6f --- /dev/null +++ b/SPECS/kernel/0014-igc-Remove-XDP-metadata-invalidation.ethernet @@ -0,0 +1,32 @@ +From ed83e3fc4986d1af74f20d37b3fcd9f4f82edffa Mon Sep 17 00:00:00 2001 +From: KhaiWenTan +Date: Wed, 24 Dec 2025 15:13:12 +0800 +Subject: [PATCH 14/14] igc: Remove XDP metadata invalidation + +Delete the else branch that invalidates XDP metadata when RX +timestamping is off. Only set metadata when hardware RX timestamping +is enabled, simplifying the receive path logic. + +Fixes: c2697c3 ("igc: Enable HW RX Timestamp for AF_XDP ZC") +Signed-off-by: KhaiWenTan +Signed-off-by: Song Yoong Siang +--- + drivers/net/ethernet/intel/igc/igc_main.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c +index bfef9555a12ea..08d924f77e99e 100644 +--- a/drivers/net/ethernet/intel/igc/igc_main.c ++++ b/drivers/net/ethernet/intel/igc/igc_main.c +@@ -2868,8 +2868,6 @@ static int igc_clean_rx_irq_zc(struct igc_q_vector *q_vector, const int budget) + md = bi->xdp->data - sizeof(*md); + md->timestamp = igc_ptp_rx_pktstamp(adapter, ctx->rx_ts->timer0); + bi->xdp->data_meta = md; +- } else { +- xdp_set_data_meta_invalid(bi->xdp); + } + + res = __igc_xdp_run_prog(adapter, prog, bi->xdp); +-- +2.43.0 + diff --git a/SPECS/kernel/0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu b/SPECS/kernel/0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu deleted file mode 100644 index f2ac7adbb..000000000 --- a/SPECS/kernel/0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu +++ /dev/null @@ -1,43 +0,0 @@ -From 9cb2621f1ca767b50ed3aa7517aaed767301406d Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Sat, 25 Oct 2025 16:42:45 +0800 -Subject: [PATCH 14/21] media: ipu: Dma sync at buffer_prepare callback as DMA - is non-coherent - -Test Platform: -PTLRVP -LNLRVP - -Signed-off-by: Bingbu Cao -Signed-off-by: linya14x ---- - drivers/staging/media/ipu7/ipu7-isys-queue.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c -index 527fdfe2f11c..3ae832c1909c 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-queue.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c -@@ -91,7 +91,9 @@ static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, - static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - { - struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue); -+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue); - struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq); -+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0); - struct device *dev = &av->isys->adev->auxdev.dev; - u32 bytesperline = av->pix_fmt.bytesperline; - u32 height = av->pix_fmt.height; -@@ -106,6 +108,9 @@ static int ipu7_isys_buf_prepare(struct vb2_buffer *vb) - av->vdev.name, bytesperline, height); - vb2_set_plane_payload(vb, 0, bytesperline * height); - -+ /* assume IPU is not DMA coherent */ -+ ipu7_dma_sync_sgtable(isys->adev, sg); -+ - return 0; - } - --- -2.43.0 - diff --git a/SPECS/kernel/0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet b/SPECS/kernel/0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet new file mode 100644 index 000000000..2940af489 --- /dev/null +++ b/SPECS/kernel/0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet @@ -0,0 +1,69 @@ +From cb8806ed837b76bcbca199e1ec2d945d9fecbb8e Mon Sep 17 00:00:00 2001 +From: "Song, Yoong Siang" +Date: Sun, 11 Apr 2021 22:49:37 +0800 +Subject: [PATCH 14/18] net: stmmac: introduce AF_XDP ZC RX HW timestamps + +Users can requests for timestamps by requesting HWTSTAMP_FILTER_ALL. +The timestamp is passed up the stack via xdp_buff's data_meta field. +This is applicable to AF_XDP ZC only for now. + +Signed-off-by: Voon Weifeng +Signed-off-by: Song, Yoong Siang +--- + drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 + + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 13 ++++++++++++- + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +index 56acc67bd6044..48c5907c97ffb 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h +@@ -264,6 +264,7 @@ struct stmmac_priv { + struct mac_device_info *hw; + int (*hwif_quirks)(struct stmmac_priv *priv); + struct mutex lock; ++ int hwts_all; + + struct stmmac_dma_conf dma_conf; + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 641b011035087..056a91165df82 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -616,6 +616,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, + case HWTSTAMP_FILTER_ALL: + /* time stamp any incoming packet */ + config->rx_filter = HWTSTAMP_FILTER_ALL; ++ priv->hwts_all = HWTSTAMP_FILTER_ALL; + tstamp_all = PTP_TCR_TSENALL; + break; + +@@ -5357,7 +5358,7 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) + buf->xdp = NULL; + dirty++; + error = 1; +- if (!priv->hwts_rx_en) ++ if (!priv->hwts_rx_en || !priv->hwts_all) + rx_errors++; + } + +@@ -5382,6 +5383,16 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) + ctx->desc = p; + ctx->ndesc = np; + ++ if (unlikely(priv->hwts_all)) { ++ /* We use XDP meta data to store T/S */ ++ buf->xdp->data_meta = buf->xdp->data - sizeof(ktime_t); ++ ++ stmmac_get_rx_hwtstamp(priv, p, np, ++ (ktime_t *)buf->xdp->data_meta); ++ } else { ++ buf->xdp->data_meta = buf->xdp->data; ++ } ++ + /* XDP ZC Frame only support primary buffers for now */ + buf1_len = stmmac_rx_buf1_len(priv, p, status, len); + len += buf1_len; +-- +2.43.0 + diff --git a/SPECS/kernel/0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf b/SPECS/kernel/0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf deleted file mode 100644 index 873c674a8..000000000 --- a/SPECS/kernel/0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf +++ /dev/null @@ -1,136 +0,0 @@ -From af6b9db282d3fd4d7cb4bb6bf09810fcd3b39ec2 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 05:27:22 +0000 -Subject: [PATCH 014/100] perf/x86/intel/ds: Factor out PEBS record processing - code to functions - -Beside some PEBS record layout difference, arch-PEBS can share most of -PEBS record processing code with adaptive PEBS. Thus, factor out these -common processing code to independent inline functions, so they can be -reused by subsequent arch-PEBS handler. - -Suggested-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/ds.c | 81 ++++++++++++++++++++++++++------------ - 1 file changed, 56 insertions(+), 25 deletions(-) - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 19d707933d61..db40dc76eb20 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2613,6 +2613,55 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d - } - } - -+static inline void __intel_pmu_handle_pebs_record(struct pt_regs *iregs, -+ struct pt_regs *regs, -+ struct perf_sample_data *data, -+ void *at, u64 pebs_status, -+ short *counts, void **last, -+ setup_fn setup_sample) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct perf_event *event; -+ int bit; -+ -+ for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) { -+ event = cpuc->events[bit]; -+ -+ if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) -+ continue; -+ -+ if (counts[bit]++) -+ __intel_pmu_pebs_event(event, iregs, regs, data, -+ last[bit], setup_sample); -+ -+ last[bit] = at; -+ } -+} -+ -+static inline void -+__intel_pmu_handle_last_pebs_record(struct pt_regs *iregs, struct pt_regs *regs, -+ struct perf_sample_data *data, u64 mask, -+ short *counts, void **last, -+ setup_fn setup_sample) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct perf_event *event; -+ int bit; -+ -+ for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) { -+ if (!counts[bit]) -+ continue; -+ -+ event = cpuc->events[bit]; -+ if (!event) -+ continue; -+ -+ __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit], -+ counts[bit], setup_sample); -+ } -+ -+} -+ - static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_data *data) - { - short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {}; -@@ -2622,9 +2671,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - struct x86_perf_regs perf_regs; - struct pt_regs *regs = &perf_regs.regs; - struct pebs_basic *basic; -- struct perf_event *event; - void *base, *at, *top; -- int bit; - u64 mask; - - if (!x86_pmu.pebs_active) -@@ -2637,6 +2684,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - - mask = hybrid(cpuc->pmu, pebs_events_mask) | - (hybrid(cpuc->pmu, fixed_cntr_mask64) << INTEL_PMC_IDX_FIXED); -+ mask &= cpuc->pebs_enabled; - - if (unlikely(base >= top)) { - intel_pmu_pebs_event_update_no_drain(cpuc, mask); -@@ -2654,31 +2702,14 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - if (basic->format_size != cpuc->pebs_record_size) - continue; - -- pebs_status = basic->applicable_counters & cpuc->pebs_enabled & mask; -- for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) { -- event = cpuc->events[bit]; -- if (!event || WARN_ON_ONCE(!event->attr.precise_ip)) -- continue; -- -- if (counts[bit]++) { -- __intel_pmu_pebs_event(event, iregs, regs, data, last[bit], -- setup_pebs_adaptive_sample_data); -- } -- last[bit] = at; -- } -+ pebs_status = mask & basic->applicable_counters; -+ __intel_pmu_handle_pebs_record(iregs, regs, data, at, -+ pebs_status, counts, last, -+ setup_pebs_adaptive_sample_data); - } - -- for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) { -- if (!counts[bit]) -- continue; -- -- event = cpuc->events[bit]; -- if (!event) -- continue; -- -- __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit], -- counts[bit], setup_pebs_adaptive_sample_data); -- } -+ __intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, last, -+ setup_pebs_adaptive_sample_data); - } - - static void __init intel_arch_pebs_init(void) --- -2.43.0 - diff --git a/SPECS/kernel/0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl b/SPECS/kernel/0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl new file mode 100644 index 000000000..50077e267 --- /dev/null +++ b/SPECS/kernel/0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl @@ -0,0 +1,147 @@ +From 4bd64bb8656d5c02a470af121b196d928344e3cc Mon Sep 17 00:00:00 2001 +From: Kuppuswamy Sathyanarayanan +Date: Thu, 20 Nov 2025 16:05:39 -0800 +Subject: [PATCH 14/17] powercap: intel_rapl: Enable MSR-based RAPL PMU support + +Currently, RAPL PMU support requires adding CPU model entries to +arch/x86/events/rapl.c for each new generation. However, RAPL MSRs are +not architectural and require platform-specific customization, making +arch/x86 an inappropriate location for this functionality. + +The powercap subsystem already handles RAPL functionality and is the +natural place to consolidate all RAPL features. The powercap RAPL +driver already includes PMU support for TPMI-based RAPL interfaces, +making it straightforward to extend this support to MSR-based RAPL +interfaces as well. + +This consolidation eliminates the need to maintain RAPL support in +multiple subsystems and provides a unified approach for both TPMI and +MSR-based RAPL implementations. + +The MSR-based PMU support includes the following updates: + + 1. Register MSR-based PMU support for the supported platforms + and unregister it when no online CPUs remain in the package. + + 2. Remove existing checks that restrict RAPL PMU support to TPMI-based + interfaces and extend the logic to allow MSR-based RAPL interfaces. + + 3. Define a CPU model list to determine which processors should + register RAPL PMU interface through the powercap driver for + MSR-based RAPL, excluding those that support TPMI interface. + This list prevents conflicts with existing arch/x86 PMU code + that already registers RAPL PMU for some processors. Add + Panther Lake & Wildcat Lake to the CPU models list. + +Signed-off-by: Kuppuswamy Sathyanarayanan +Reviewed-by: Srinivas Pandruvada +[ rjw: Changelog edits ] +Link: https://patch.msgid.link/20251121000539.386069-3-sathyanarayanan.kuppuswamy@linux.intel.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/powercap/intel_rapl_common.c | 12 ++++++------ + drivers/powercap/intel_rapl_msr.c | 24 ++++++++++++++++++++++-- + 2 files changed, 28 insertions(+), 8 deletions(-) + +diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c +index 47ec34d4c0997..b9d87e56cbbc8 100644 +--- a/drivers/powercap/intel_rapl_common.c ++++ b/drivers/powercap/intel_rapl_common.c +@@ -1597,11 +1597,11 @@ static int get_pmu_cpu(struct rapl_package *rp) + if (!rp->has_pmu) + return nr_cpu_ids; + +- /* Only TPMI RAPL is supported for now */ +- if (rp->priv->type != RAPL_IF_TPMI) ++ /* Only TPMI & MSR RAPL are supported for now */ ++ if (rp->priv->type != RAPL_IF_TPMI && rp->priv->type != RAPL_IF_MSR) + return nr_cpu_ids; + +- /* TPMI RAPL uses any CPU in the package for PMU */ ++ /* TPMI/MSR RAPL uses any CPU in the package for PMU */ + for_each_online_cpu(cpu) + if (topology_physical_package_id(cpu) == rp->id) + return cpu; +@@ -1614,11 +1614,11 @@ static bool is_rp_pmu_cpu(struct rapl_package *rp, int cpu) + if (!rp->has_pmu) + return false; + +- /* Only TPMI RAPL is supported for now */ +- if (rp->priv->type != RAPL_IF_TPMI) ++ /* Only TPMI & MSR RAPL are supported for now */ ++ if (rp->priv->type != RAPL_IF_TPMI && rp->priv->type != RAPL_IF_MSR) + return false; + +- /* TPMI RAPL uses any CPU in the package for PMU */ ++ /* TPMI/MSR RAPL uses any CPU in the package for PMU */ + return topology_physical_package_id(cpu) == rp->id; + } + +diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c +index 6e3c50af09128..0ce1096b63145 100644 +--- a/drivers/powercap/intel_rapl_msr.c ++++ b/drivers/powercap/intel_rapl_msr.c +@@ -33,6 +33,8 @@ + /* private data for RAPL MSR Interface */ + static struct rapl_if_priv *rapl_msr_priv; + ++static bool rapl_msr_pmu __ro_after_init; ++ + static struct rapl_if_priv rapl_msr_priv_intel = { + .type = RAPL_IF_MSR, + .reg_unit.msr = MSR_RAPL_POWER_UNIT, +@@ -79,6 +81,8 @@ static int rapl_cpu_online(unsigned int cpu) + rp = rapl_add_package_cpuslocked(cpu, rapl_msr_priv, true); + if (IS_ERR(rp)) + return PTR_ERR(rp); ++ if (rapl_msr_pmu) ++ rapl_package_add_pmu(rp); + } + cpumask_set_cpu(cpu, &rp->cpumask); + return 0; +@@ -95,10 +99,14 @@ static int rapl_cpu_down_prep(unsigned int cpu) + + cpumask_clear_cpu(cpu, &rp->cpumask); + lead_cpu = cpumask_first(&rp->cpumask); +- if (lead_cpu >= nr_cpu_ids) ++ if (lead_cpu >= nr_cpu_ids) { ++ if (rapl_msr_pmu) ++ rapl_package_remove_pmu(rp); + rapl_remove_package_cpuslocked(rp); +- else if (rp->lead_cpu == cpu) ++ } else if (rp->lead_cpu == cpu) { + rp->lead_cpu = lead_cpu; ++ } ++ + return 0; + } + +@@ -171,6 +179,13 @@ static const struct x86_cpu_id pl4_support_ids[] = { + {} + }; + ++/* List of MSR-based RAPL PMU support CPUs */ ++static const struct x86_cpu_id pmu_support_ids[] = { ++ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, NULL), ++ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, NULL), ++ {} ++}; ++ + static int rapl_msr_probe(struct platform_device *pdev) + { + const struct x86_cpu_id *id = x86_match_cpu(pl4_support_ids); +@@ -198,6 +213,11 @@ static int rapl_msr_probe(struct platform_device *pdev) + pr_info("PL4 support detected.\n"); + } + ++ if (x86_match_cpu(pmu_support_ids)) { ++ rapl_msr_pmu = true; ++ pr_info("MSR-based RAPL PMU support enabled\n"); ++ } ++ + rapl_msr_priv->control_type = powercap_register_control_type(NULL, "intel-rapl", NULL); + if (IS_ERR(rapl_msr_priv->control_type)) { + pr_debug("failed to register powercap control_type.\n"); +-- +2.43.0 + diff --git a/SPECS/kernel/0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo b/SPECS/kernel/0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo new file mode 100644 index 000000000..b381aa377 --- /dev/null +++ b/SPECS/kernel/0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo @@ -0,0 +1,49 @@ +From 037577d50b92f1e879887a8736fdfa0af3c23b39 Mon Sep 17 00:00:00 2001 +From: Malaya Kumar Rout +Date: Sat, 22 Nov 2025 20:46:52 +0530 +Subject: [PATCH 14/21] tools/power x86_energy_perf_policy: Fix potential NULL + pointer dereference + +In err_on_hypervisor(), strstr() is called to search for "flags" in the +buffer, but the return value is not checked before being used in pointer +arithmetic (flags - buffer). If strstr() returns NULL because "flags" is +not found in /proc/cpuinfo, this will cause undefined behavior and likely +a crash. + +Add a NULL check after the strstr() call and handle the error appropriately +by cleaning up resources and reporting a meaningful error message. + +Signed-off-by: Malaya Kumar Rout +Signed-off-by: Len Brown +--- + .../x86/x86_energy_perf_policy/x86_energy_perf_policy.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +index b2125275c69e0..ac37132207a47 100644 +--- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c ++++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +@@ -520,7 +520,7 @@ void for_packages(unsigned long long pkg_set, int (func)(int)) + + void print_version(void) + { +- printf("x86_energy_perf_policy 2025.9.19 Len Brown \n"); ++ printf("x86_energy_perf_policy 2025.11.22 Len Brown \n"); + } + + void cmdline(int argc, char **argv) +@@ -662,6 +662,11 @@ void err_on_hypervisor(void) + } + + flags = strstr(buffer, "flags"); ++ if (!flags) { ++ fclose(cpuinfo); ++ free(buffer); ++ err(1, "Failed to find 'flags' in /proc/cpuinfo"); ++ } + rewind(cpuinfo); + fseek(cpuinfo, flags - buffer, SEEK_SET); + if (!fgets(buffer, 4096, cpuinfo)) { +-- +2.43.0 + diff --git a/SPECS/kernel/0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi b/SPECS/kernel/0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi deleted file mode 100644 index 5316044d8..000000000 --- a/SPECS/kernel/0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi +++ /dev/null @@ -1,133 +0,0 @@ -From b6f80792b264fea9e081fd691885e1915a1048b0 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 28 Feb 2023 13:06:07 -0800 -Subject: [PATCH 15/44] KVM: VMX: Dump FRED context in dump_vmcs() - -Add FRED related VMCS fields to dump_vmcs() to dump FRED context. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Read guest FRED RSP0 with vmx_read_guest_fred_rsp0() (Sean). -* Add TB from Xuelian Guo. - -Change in v3: -* Use (vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED) instead of is_fred_enabled() - (Chao Gao). - -Changes in v2: -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). -* Dump guest FRED states only if guest has FRED enabled (Nikolay Borisov). ---- - arch/x86/kvm/vmx/vmx.c | 43 +++++++++++++++++++++++++++++++++++------- - 1 file changed, 36 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index f225778115dc..96edf88dd924 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -1402,6 +1402,9 @@ static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) - vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, - &vmx->msr_guest_fred_rsp0); - } -+#else -+/* To make sure dump_vmcs() compile on 32-bit */ -+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) { return 0; } - #endif - - static void grow_ple_window(struct kvm_vcpu *vcpu) -@@ -6478,7 +6481,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - struct vcpu_vmx *vmx = to_vmx(vcpu); - u32 vmentry_ctl, vmexit_ctl; - u32 cpu_based_exec_ctrl, pin_based_exec_ctrl, secondary_exec_control; -- u64 tertiary_exec_control; -+ u64 tertiary_exec_control, secondary_vmexit_ctl; - unsigned long cr4; - int efer_slot; - -@@ -6489,6 +6492,8 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - - vmentry_ctl = vmcs_read32(VM_ENTRY_CONTROLS); - vmexit_ctl = vmcs_read32(VM_EXIT_CONTROLS); -+ secondary_vmexit_ctl = cpu_has_secondary_vmexit_ctrls() ? -+ vmcs_read64(SECONDARY_VM_EXIT_CONTROLS) : 0; - cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); - pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL); - cr4 = vmcs_readl(GUEST_CR4); -@@ -6535,6 +6540,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - vmx_dump_sel("LDTR:", GUEST_LDTR_SELECTOR); - vmx_dump_dtsel("IDTR:", GUEST_IDTR_LIMIT); - vmx_dump_sel("TR: ", GUEST_TR_SELECTOR); -+ if (vmentry_ctl & VM_ENTRY_LOAD_IA32_FRED) -+ pr_err("FRED guest: config=0x%016llx, stack_levels=0x%016llx\n" -+ "RSP0=0x%016llx, RSP1=0x%016llx\n" -+ "RSP2=0x%016llx, RSP3=0x%016llx\n", -+ vmcs_read64(GUEST_IA32_FRED_CONFIG), -+ vmcs_read64(GUEST_IA32_FRED_STKLVLS), -+ vmx_read_guest_fred_rsp0(vmx), -+ vmcs_read64(GUEST_IA32_FRED_RSP1), -+ vmcs_read64(GUEST_IA32_FRED_RSP2), -+ vmcs_read64(GUEST_IA32_FRED_RSP3)); - efer_slot = vmx_find_loadstore_msr_slot(&vmx->msr_autoload.guest, MSR_EFER); - if (vmentry_ctl & VM_ENTRY_LOAD_IA32_EFER) - pr_err("EFER= 0x%016llx\n", vmcs_read64(GUEST_IA32_EFER)); -@@ -6586,6 +6601,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - vmcs_readl(HOST_TR_BASE)); - pr_err("GDTBase=%016lx IDTBase=%016lx\n", - vmcs_readl(HOST_GDTR_BASE), vmcs_readl(HOST_IDTR_BASE)); -+ if (vmexit_ctl & SECONDARY_VM_EXIT_LOAD_IA32_FRED) -+ pr_err("FRED host: config=0x%016llx, stack_levels=0x%016llx\n" -+ "RSP0=0x%016lx, RSP1=0x%016llx\n" -+ "RSP2=0x%016llx, RSP3=0x%016llx\n", -+ vmcs_read64(HOST_IA32_FRED_CONFIG), -+ vmcs_read64(HOST_IA32_FRED_STKLVLS), -+ (unsigned long)task_stack_page(current) + THREAD_SIZE, -+ vmcs_read64(HOST_IA32_FRED_RSP1), -+ vmcs_read64(HOST_IA32_FRED_RSP2), -+ vmcs_read64(HOST_IA32_FRED_RSP3)); - pr_err("CR0=%016lx CR3=%016lx CR4=%016lx\n", - vmcs_readl(HOST_CR0), vmcs_readl(HOST_CR3), - vmcs_readl(HOST_CR4)); -@@ -6611,25 +6636,29 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - pr_err("*** Control State ***\n"); - pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", - cpu_based_exec_ctrl, secondary_exec_control, tertiary_exec_control); -- pr_err("PinBased=0x%08x EntryControls=%08x ExitControls=%08x\n", -- pin_based_exec_ctrl, vmentry_ctl, vmexit_ctl); -+ pr_err("PinBased=0x%08x EntryControls=0x%08x\n", -+ pin_based_exec_ctrl, vmentry_ctl); -+ pr_err("ExitControls=0x%08x SecondaryExitControls=0x%016llx\n", -+ vmexit_ctl, secondary_vmexit_ctl); - pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n", - vmcs_read32(EXCEPTION_BITMAP), - vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK), - vmcs_read32(PAGE_FAULT_ERROR_CODE_MATCH)); -- pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x\n", -+ pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x event_data=%016llx\n", - vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), - vmcs_read32(VM_ENTRY_EXCEPTION_ERROR_CODE), -- vmcs_read32(VM_ENTRY_INSTRUCTION_LEN)); -+ vmcs_read32(VM_ENTRY_INSTRUCTION_LEN), -+ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(INJECTED_EVENT_DATA) : 0); - pr_err("VMExit: intr_info=%08x errcode=%08x ilen=%08x\n", - vmcs_read32(VM_EXIT_INTR_INFO), - vmcs_read32(VM_EXIT_INTR_ERROR_CODE), - vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); - pr_err(" reason=%08x qualification=%016lx\n", - vmcs_read32(VM_EXIT_REASON), vmcs_readl(EXIT_QUALIFICATION)); -- pr_err("IDTVectoring: info=%08x errcode=%08x\n", -+ pr_err("IDTVectoring: info=%08x errcode=%08x event_data=%016llx\n", - vmcs_read32(IDT_VECTORING_INFO_FIELD), -- vmcs_read32(IDT_VECTORING_ERROR_CODE)); -+ vmcs_read32(IDT_VECTORING_ERROR_CODE), -+ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(ORIGINAL_EVENT_DATA) : 0); - pr_err("TSC Offset = 0x%016llx\n", vmcs_read64(TSC_OFFSET)); - if (secondary_exec_control & SECONDARY_EXEC_TSC_SCALING) - pr_err("TSC Multiplier = 0x%016llx\n", --- -2.43.0 - diff --git a/SPECS/kernel/0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet b/SPECS/kernel/0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet deleted file mode 100644 index 358f488dd..000000000 --- a/SPECS/kernel/0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet +++ /dev/null @@ -1,172 +0,0 @@ -From 0adbce1831ce117f956f1e8b1b28df73bd66e61a Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:45 -0700 -Subject: [PATCH 15/24] KVM: VMX: Emulate read and write to CET MSRs - -Add emulation interface for CET MSR access. The emulation code is split -into common part and vendor specific part. The former does common checks -for MSRs, e.g., accessibility, data validity etc., then passes operation -to either XSAVE-managed MSRs via the helpers or CET VMCS fields. - -SSP can only be read via RDSSP. Writing even requires destructive and -potentially faulting operations such as SAVEPREVSSP/RSTORSSP or -SETSSBSY/CLRSSBSY. Let the host use a pseudo-MSR that is just a wrapper -for the GUEST_SSP field of the VMCS. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/vmx.c | 18 ++++++++++++++++++ - arch/x86/kvm/x86.c | 43 ++++++++++++++++++++++++++++++++++++++++++ - arch/x86/kvm/x86.h | 23 ++++++++++++++++++++++ - 3 files changed, 84 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 4a4691beba55..9098d4ee4f79 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2095,6 +2095,15 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - else - msr_info->data = vmx->pt_desc.guest.addr_a[index / 2]; - break; -+ case MSR_IA32_S_CET: -+ msr_info->data = vmcs_readl(GUEST_S_CET); -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ msr_info->data = vmcs_readl(GUEST_SSP); -+ break; -+ case MSR_IA32_INT_SSP_TAB: -+ msr_info->data = vmcs_readl(GUEST_INTR_SSP_TABLE); -+ break; - case MSR_IA32_DEBUGCTLMSR: - msr_info->data = vmx_guest_debugctl_read(); - break; -@@ -2413,6 +2422,15 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - else - vmx->pt_desc.guest.addr_a[index / 2] = data; - break; -+ case MSR_IA32_S_CET: -+ vmcs_writel(GUEST_S_CET, data); -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ vmcs_writel(GUEST_SSP, data); -+ break; -+ case MSR_IA32_INT_SSP_TAB: -+ vmcs_writel(GUEST_INTR_SSP_TABLE, data); -+ break; - case MSR_IA32_PERF_CAPABILITIES: - if (data & PERF_CAP_LBR_FMT) { - if ((data & PERF_CAP_LBR_FMT) != -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 1a437aad26f9..42e4fac999de 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -1889,6 +1889,27 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, - - data = (u32)data; - break; -+ case MSR_IA32_U_CET: -+ case MSR_IA32_S_CET: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ if (!is_cet_msr_valid(vcpu, data)) -+ return 1; -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ if (!host_initiated) -+ return 1; -+ fallthrough; -+ case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ if (is_noncanonical_msr_address(data, vcpu)) -+ return 1; -+ /* All SSP MSRs except MSR_IA32_INT_SSP_TAB must be 4-byte aligned */ -+ if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4)) -+ return 1; -+ break; - } - - msr.data = data; -@@ -1933,6 +1954,20 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data, - !guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID)) - return 1; - break; -+ case MSR_IA32_U_CET: -+ case MSR_IA32_S_CET: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; -+ case MSR_KVM_INTERNAL_GUEST_SSP: -+ if (!host_initiated) -+ return 1; -+ fallthrough; -+ case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB: -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ return KVM_MSR_RET_UNSUPPORTED; -+ break; - } - - msr.index = index; -@@ -4185,6 +4220,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - vcpu->arch.guest_fpu.xfd_err = data; - break; - #endif -+ case MSR_IA32_U_CET: -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ kvm_set_xstate_msr(vcpu, msr_info); -+ break; - default: - if (kvm_pmu_is_valid_msr(vcpu, msr)) - return kvm_pmu_set_msr(vcpu, msr_info); -@@ -4534,6 +4573,10 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - msr_info->data = vcpu->arch.guest_fpu.xfd_err; - break; - #endif -+ case MSR_IA32_U_CET: -+ case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP: -+ kvm_get_xstate_msr(vcpu, msr_info); -+ break; - default: - if (kvm_pmu_is_valid_msr(vcpu, msr_info->index)) - return kvm_pmu_get_msr(vcpu, msr_info); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index cf74d877737a..b54ec7d37f1f 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -735,4 +735,27 @@ static inline void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, - kvm_fpu_put(); - } - -+#define CET_US_RESERVED_BITS GENMASK(9, 6) -+#define CET_US_SHSTK_MASK_BITS GENMASK(1, 0) -+#define CET_US_IBT_MASK_BITS (GENMASK_ULL(5, 2) | GENMASK_ULL(63, 10)) -+#define CET_US_LEGACY_BITMAP_BASE(data) ((data) >> 12) -+ -+static inline bool is_cet_msr_valid(struct kvm_vcpu *vcpu, u64 data) -+{ -+ if (data & CET_US_RESERVED_BITS) -+ return false; -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ (data & CET_US_SHSTK_MASK_BITS)) -+ return false; -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) && -+ (data & CET_US_IBT_MASK_BITS)) -+ return false; -+ if (!IS_ALIGNED(CET_US_LEGACY_BITMAP_BASE(data), 4)) -+ return false; -+ /* IBT can be suppressed iff the TRACKER isn't WAIT_ENDBR. */ -+ if ((data & CET_SUPPRESS) && (data & CET_WAIT_ENDBR)) -+ return false; -+ -+ return true; -+} - #endif --- -2.43.0 - diff --git a/SPECS/kernel/0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi b/SPECS/kernel/0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi new file mode 100644 index 000000000..b37f870d1 --- /dev/null +++ b/SPECS/kernel/0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi @@ -0,0 +1,61 @@ +From 3d0fa57e44432f0f0f1a92c1cef082c4a2c95410 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 13:26:01 -0700 +Subject: [PATCH 15/44] KVM: x86: Mark CR4.FRED as not reserved + +The CR4.FRED bit, i.e., CR4[32], is no longer a reserved bit when +guest cpu cap has FRED, i.e., + 1) All of FRED KVM support is in place. + 2) Guest enumerates FRED. + +Otherwise it is still a reserved bit. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change in v4: +* Rebase on top of "guest_cpu_cap". + +Change in v3: +* Don't allow CR4.FRED=1 before all of FRED KVM support is in place + (Sean Christopherson). +--- + arch/x86/include/asm/kvm_host.h | 2 +- + arch/x86/kvm/x86.h | 2 ++ + 2 files changed, 3 insertions(+), 1 deletion(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 5fff22d837aa0..558f260a1afd7 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -142,7 +142,7 @@ + | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ + | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ + | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ +- | X86_CR4_LAM_SUP | X86_CR4_CET)) ++ | X86_CR4_LAM_SUP | X86_CR4_CET | X86_CR4_FRED)) + + #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR) + +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index 4f5d12d7136ee..e9c6f304b02e4 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -687,6 +687,8 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) + if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ + !__cpu_has(__c, X86_FEATURE_IBT)) \ + __reserved_bits |= X86_CR4_CET; \ ++ if (!__cpu_has(__c, X86_FEATURE_FRED)) \ ++ __reserved_bits |= X86_CR4_FRED; \ + __reserved_bits; \ + }) + +-- +2.43.0 + diff --git a/SPECS/kernel/0015-comedi-c6xdigio-Fix-invalid-PNP-driver-unregistratio.patch b/SPECS/kernel/0015-comedi-c6xdigio-Fix-invalid-PNP-driver-unregistratio.patch deleted file mode 100644 index 32ea0bd90..000000000 --- a/SPECS/kernel/0015-comedi-c6xdigio-Fix-invalid-PNP-driver-unregistratio.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 2d5318767a2c0249af5485810fe8c162835db1bd Mon Sep 17 00:00:00 2001 -From: Ian Abbott -Date: Thu, 23 Oct 2025 13:31:41 +0100 -Subject: [PATCH 15/51] comedi: c6xdigio: Fix invalid PNP driver unregistration - -The Comedi low-level driver "c6xdigio" seems to be for a parallel port -connected device. When the Comedi core calls the driver's Comedi -"attach" handler `c6xdigio_attach()` to configure a Comedi to use this -driver, it tries to enable the parallel port PNP resources by -registering a PNP driver with `pnp_register_driver()`, but ignores the -return value. (The `struct pnp_driver` it uses has only the `name` and -`id_table` members filled in.) The driver's Comedi "detach" handler -`c6xdigio_detach()` unconditionally unregisters the PNP driver with -`pnp_unregister_driver()`. - -It is possible for `c6xdigio_attach()` to return an error before it -calls `pnp_register_driver()` and it is possible for the call to -`pnp_register_driver()` to return an error (that is ignored). In both -cases, the driver should not be calling `pnp_unregister_driver()` as it -does in `c6xdigio_detach()`. (Note that `c6xdigio_detach()` will be -called by the Comedi core if `c6xdigio_attach()` returns an error, or if -the Comedi core decides to detach the Comedi device from the driver for -some other reason.) - -The unconditional call to `pnp_unregister_driver()` without a previous -successful call to `pnp_register_driver()` will cause -`driver_unregister()` to issue a warning "Unexpected driver -unregister!". This was detected by Syzbot [1]. - -Also, the PNP driver registration and unregistration should be done at -module init and exit time, respectively, not when attaching or detaching -Comedi devices to the driver. (There might be more than one Comedi -device being attached to the driver, although that is unlikely.) - -Change the driver to do the PNP driver registration at module init time, -and the unregistration at module exit time. Since `c6xdigio_detach()` -now only calls `comedi_legacy_detach()`, remove the function and change -the Comedi driver "detach" handler to `comedi_legacy_detach`. - -------------------------------------------- -[1] Syzbot sample crash report: -Unexpected driver unregister! -WARNING: CPU: 0 PID: 5970 at drivers/base/driver.c:273 driver_unregister drivers/base/driver.c:273 [inline] -WARNING: CPU: 0 PID: 5970 at drivers/base/driver.c:273 driver_unregister+0x90/0xb0 drivers/base/driver.c:270 -Modules linked in: -CPU: 0 UID: 0 PID: 5970 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full) -Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 10/02/2025 -RIP: 0010:driver_unregister drivers/base/driver.c:273 [inline] -RIP: 0010:driver_unregister+0x90/0xb0 drivers/base/driver.c:270 -Code: 48 89 ef e8 c2 e6 82 fc 48 89 df e8 3a 93 ff ff 5b 5d e9 c3 6d d9 fb e8 be 6d d9 fb 90 48 c7 c7 e0 f8 1f 8c e8 51 a2 97 fb 90 <0f> 0b 90 90 5b 5d e9 a5 6d d9 fb e8 e0 f4 41 fc eb 94 e8 d9 f4 41 -RSP: 0018:ffffc9000373f9a0 EFLAGS: 00010282 -RAX: 0000000000000000 RBX: ffffffff8ff24720 RCX: ffffffff817b6ee8 -RDX: ffff88807c932480 RSI: ffffffff817b6ef5 RDI: 0000000000000001 -RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000000 -R10: 0000000000000001 R11: 0000000000000001 R12: ffffffff8ff24660 -R13: dffffc0000000000 R14: 0000000000000000 R15: ffff88814cca0000 -FS: 000055556dab1500(0000) GS:ffff8881249d9000(0000) knlGS:0000000000000000 -CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 -CR2: 000055f77f285cd0 CR3: 000000007d871000 CR4: 00000000003526f0 -Call Trace: - - comedi_device_detach_locked+0x12f/0xa50 drivers/comedi/drivers.c:207 - comedi_device_detach+0x67/0xb0 drivers/comedi/drivers.c:215 - comedi_device_attach+0x43d/0x900 drivers/comedi/drivers.c:1011 - do_devconfig_ioctl+0x1b1/0x710 drivers/comedi/comedi_fops.c:872 - comedi_unlocked_ioctl+0x165d/0x2f00 drivers/comedi/comedi_fops.c:2178 - vfs_ioctl fs/ioctl.c:51 [inline] - __do_sys_ioctl fs/ioctl.c:597 [inline] - __se_sys_ioctl fs/ioctl.c:583 [inline] - __x64_sys_ioctl+0x18e/0x210 fs/ioctl.c:583 - do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] - do_syscall_64+0xcd/0xfa0 arch/x86/entry/syscall_64.c:94 - entry_SYSCALL_64_after_hwframe+0x77/0x7f -RIP: 0033:0x7fc05798eec9 -Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 a8 ff ff ff f7 d8 64 89 01 48 -RSP: 002b:00007ffcf8184238 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 -RAX: ffffffffffffffda RBX: 00007fc057be5fa0 RCX: 00007fc05798eec9 -RDX: 0000200000000080 RSI: 0000000040946400 RDI: 0000000000000003 -RBP: 00007fc057a11f91 R08: 0000000000000000 R09: 0000000000000000 -R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 -R13: 00007fc057be5fa0 R14: 00007fc057be5fa0 R15: 0000000000000003 - -------------------------------------------- - -Reported-by: syzbot+6616bba359cec7a1def1@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=6616bba359cec7a1def1 -Fixes: 2c89e159cd2f ("Staging: comedi: add c6xdigio driver") -Cc: stable@kernel.org -Signed-off-by: Ian Abbott -Link: https://patch.msgid.link/20251023123141.6537-1-abbotti@mev.co.uk -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/drivers/c6xdigio.c | 46 +++++++++++++++++++++++-------- - 1 file changed, 35 insertions(+), 11 deletions(-) - -diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c -index 14b90d1c64dc..8da653c8c1ca 100644 ---- a/drivers/comedi/drivers/c6xdigio.c -+++ b/drivers/comedi/drivers/c6xdigio.c -@@ -249,9 +249,6 @@ static int c6xdigio_attach(struct comedi_device *dev, - if (ret) - return ret; - -- /* Make sure that PnP ports get activated */ -- pnp_register_driver(&c6xdigio_pnp_driver); -- - s = &dev->subdevices[0]; - /* pwm output subdevice */ - s->type = COMEDI_SUBD_PWM; -@@ -278,19 +275,46 @@ static int c6xdigio_attach(struct comedi_device *dev, - return 0; - } - --static void c6xdigio_detach(struct comedi_device *dev) --{ -- comedi_legacy_detach(dev); -- pnp_unregister_driver(&c6xdigio_pnp_driver); --} -- - static struct comedi_driver c6xdigio_driver = { - .driver_name = "c6xdigio", - .module = THIS_MODULE, - .attach = c6xdigio_attach, -- .detach = c6xdigio_detach, -+ .detach = comedi_legacy_detach, - }; --module_comedi_driver(c6xdigio_driver); -+ -+static bool c6xdigio_pnp_registered; -+ -+static int __init c6xdigio_module_init(void) -+{ -+ int ret; -+ -+ ret = comedi_driver_register(&c6xdigio_driver); -+ if (ret) -+ return ret; -+ -+ if (IS_ENABLED(CONFIG_PNP)) { -+ /* Try to activate the PnP ports */ -+ ret = pnp_register_driver(&c6xdigio_pnp_driver); -+ if (ret) { -+ pr_warn("failed to register pnp driver - err %d\n", -+ ret); -+ ret = 0; /* ignore the error. */ -+ } else { -+ c6xdigio_pnp_registered = true; -+ } -+ } -+ -+ return 0; -+} -+module_init(c6xdigio_module_init); -+ -+static void __exit c6xdigio_module_exit(void) -+{ -+ if (c6xdigio_pnp_registered) -+ pnp_unregister_driver(&c6xdigio_pnp_driver); -+ comedi_driver_unregister(&c6xdigio_driver); -+} -+module_exit(c6xdigio_module_exit); - - MODULE_AUTHOR("Comedi https://www.comedi.org"); - MODULE_DESCRIPTION("Comedi driver for the C6x_DIGIO DSP daughter card"); --- -2.43.0 - diff --git a/SPECS/kernel/0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl b/SPECS/kernel/0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl new file mode 100644 index 000000000..281fb0582 --- /dev/null +++ b/SPECS/kernel/0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl @@ -0,0 +1,32 @@ +From 108a0dab67c8cf7584162c87a656b75c192f4294 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Fri, 21 Nov 2025 21:11:16 +0100 +Subject: [PATCH 15/17] cpuidle: governors: teo: Add missing space to the + description + +There is a missing space in the governor description comment, so add it. + +No functional impact. + +Signed-off-by: Rafael J. Wysocki +Link: https://patch.msgid.link/5059034.31r3eYUQgx@rafael.j.wysocki +--- + drivers/cpuidle/governors/teo.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c +index bab186336bf4a..81ac5fd58a1c6 100644 +--- a/drivers/cpuidle/governors/teo.c ++++ b/drivers/cpuidle/governors/teo.c +@@ -76,7 +76,7 @@ + * likely woken up by a non-timer wakeup source). + * + * 2. If the second sum computed in step 1 is greater than a half of the sum of +- * both metrics for the candidate state bin and all subsequent bins(if any), ++ * both metrics for the candidate state bin and all subsequent bins (if any), + * a shallower idle state is likely to be more suitable, so look for it. + * + * - Traverse the enabled idle states shallower than the candidate one in the +-- +2.43.0 + diff --git a/SPECS/kernel/0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet b/SPECS/kernel/0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet deleted file mode 100644 index c051c20cb..000000000 --- a/SPECS/kernel/0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet +++ /dev/null @@ -1,36 +0,0 @@ -From cb41485c3b08e1a27d2ce3e25c6b75e6f416e418 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Fri, 23 Jul 2021 21:27:36 +0800 -Subject: [PATCH 15/19] igc: Enable trace for HW TX Timestamp AF_XDP ZC - -This is a temporary solution as it uses trace_printk as a means to -log tx timestamps. - -Future implementation should use xdp_frame's data_meta to let user -applications retrieve it directly. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc_main.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 8b5f747db38b..8fdeea791b1d 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -3255,6 +3255,11 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - - switch (tx_buffer->type) { - case IGC_TX_BUFFER_TYPE_XSK: -+#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) -+ /* Only use for RTCP KPI Measurement on Q2 */ -+ if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) -+ trace_printk("TX HW TS %lld\n", timestamp); -+#endif - xsk_frames++; - break; - case IGC_TX_BUFFER_TYPE_XDP: --- -2.43.0 - diff --git a/SPECS/kernel/0015-media-ipu7-update-CDPHY-register-settings.ipu b/SPECS/kernel/0015-media-ipu7-update-CDPHY-register-settings.ipu deleted file mode 100644 index c7c610e79..000000000 --- a/SPECS/kernel/0015-media-ipu7-update-CDPHY-register-settings.ipu +++ /dev/null @@ -1,81 +0,0 @@ -From 7612f6deb2488e07a6c38a73655a83b45762cb6c Mon Sep 17 00:00:00 2001 -From: hepengpx -Date: Fri, 19 Dec 2025 15:29:00 +0800 -Subject: [PATCH 15/21] media: ipu7: update CDPHY register settings - -Some CPHY settings are copied from Wins code, but some of -them aren't correct and need to be fixed. - -Program 45ohm for tuning resistance to fix CPHY problem and -update the ITMINRX and GMODE for CPHY high data rate. - -Test Platform: -PTLRVP -LNLRVP - -Signed-off-by: Bingbu Cao -Signed-off-by: hepengpx ---- - drivers/staging/media/ipu7/ipu7-isys-csi-phy.c | 15 +++++++++++---- - 1 file changed, 11 insertions(+), 4 deletions(-) - -diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c -index b8c5db7ae300..97cd47daf614 100644 ---- a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c -+++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c -@@ -124,6 +124,7 @@ static const struct cdr_fbk_cap_prog_params table7[] = { - { 1350, 1589, 4 }, - { 1590, 1949, 5 }, - { 1950, 2499, 6 }, -+ { 2500, 3500, 7 }, - { } - }; - -@@ -730,7 +731,7 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - u16 deass_thresh; - u16 delay_thresh; - u16 reset_thresh; -- u16 cap_prog = 6U; -+ u16 cap_prog; - u16 reg; - u16 val; - u32 i; -@@ -838,9 +839,10 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - dwc_phy_write_mask(isys, id, reg + 0x400 * i, - reset_thresh, 9, 11); - -+ /* Tuning ITMINRX to 2 for CPHY */ - reg = CORE_DIG_CLANE_0_RW_LP_0; - for (i = 0; i < trios; i++) -- dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15); -+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15); - - reg = CORE_DIG_CLANE_0_RW_LP_2; - for (i = 0; i < trios; i++) -@@ -860,7 +862,11 @@ static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - for (i = 0; i < (lanes + 1); i++) { - reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i; - dwc_phy_write_mask(isys, id, reg, 4U, 0, 2); -- dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); -+ /* Set GMODE to 2 when CPHY >= 1.5Gsps */ -+ if (mbps >= 1500) -+ dwc_phy_write_mask(isys, id, reg, 2U, 3, 4); -+ else -+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4); - - reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i; - dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12); -@@ -930,8 +936,9 @@ static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes, - 7, 12, 14); - dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7, - 0, 8, 10); -+ /* resistance tuning: 1 for 45ohm, 0 for 50ohm */ - dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5, -- 0, 8, 8); -+ 1, 8, 8); - - if (aggregation) - phy_mode = isys->csi2[0].phy_mode; --- -2.43.0 - diff --git a/SPECS/kernel/0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet b/SPECS/kernel/0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet new file mode 100644 index 000000000..5b27f5af7 --- /dev/null +++ b/SPECS/kernel/0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet @@ -0,0 +1,97 @@ +From ed293eeac928fcf3dcaf9b7e3481f4e5c346bd6f Mon Sep 17 00:00:00 2001 +From: Tan Tee Min +Date: Tue, 12 Apr 2022 15:14:07 +0800 +Subject: [PATCH 15/18] net: stmmac: add fsleep() in HW Rx timestamp checking + loop + +There is a possibility that the context descriptor still owned by the DMA +even the previous normal descriptor own bit is already cleared. Checking +the context descriptor readiness without delay might be not enough time +for the DMA to update the descriptor field, which causing failure in +getting HW Rx timestamp. + +This patch introduces a 1ms fsleep() in HW Rx timestamp checking loop +to give time for DMA to update/complete the context descriptor. + +ptp4l Timestamp log without this patch: +----------------------------------------------------------- +$ echo 10000 > /sys/class/net/enp0s30f4/gro_flush_timeout +$ echo 10000 > /sys/class/net/enp0s30f4/napi_defer_hard_irqs +$ ptp4l -P2Hi enp0s30f4 --step_threshold=1 -m +ptp4l: selected /dev/ptp2 as PTP clock +ptp4l: port 1: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l: selected local clock 901210.fffe.b57df7 as best master +ptp4l: port 1: new foreign master 22bb22.fffe.bb22bb-1 +ptp4l: selected best master clock 22bb22.fffe.bb22bb +ptp4l: port 1: LISTENING to UNCALIBRATED on RS_SLAVE +ptp4l: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED +ptp4l: port 1: received SYNC without timestamp +ptp4l: rms 49 max 63 freq -9573 +/- 34 delay 71 +/- 1 +ptp4l: rms 15 max 25 freq -9553 +/- 20 delay 72 +/- 0 +ptp4l: port 1: received SYNC without timestamp +ptp4l: rms 9 max 18 freq -9540 +/- 11 delay 70 +/- 0 +ptp4l: port 1: received PDELAY_REQ without timestamp +ptp4l: rms 16 max 29 freq -9519 +/- 12 delay 72 +/- 0 +ptp4l: port 1: received PDELAY_REQ without timestamp +ptp4l: rms 9 max 18 freq -9527 +/- 12 delay 72 +/- 0 +ptp4l: rms 5 max 9 freq -9530 +/- 7 delay 70 +/- 0 +ptp4l: rms 11 max 20 freq -9530 +/- 16 delay 72 +/- 0 +ptp4l: rms 5 max 11 freq -9530 +/- 7 delay 74 +/- 0 +ptp4l: rms 6 max 9 freq -9522 +/- 7 delay 72 +/- 0 +ptp4l: port 1: received PDELAY_REQ without timestamp +----------------------------------------------------------- + +ptp4l Timestamp log with this patch: +----------------------------------------------------------- +$ echo 10000 > /sys/class/net/enp0s30f4/gro_flush_timeout +$ echo 10000 > /sys/class/net/enp0s30f4/napi_defer_hard_irqs +$ ptp4l -P2Hi enp0s30f4 --step_threshold=1 -m +ptp4l: selected /dev/ptp2 as PTP clock +ptp4l: port 1: INITIALIZING to LISTENING on INIT_COMPLETE +ptp4l: selected local clock 901210.fffe.b57df7 as best master +ptp4l: port 1: new foreign master 22bb22.fffe.bb22bb-1 +ptp4l: selected best master clock 22bb22.fffe.bb22bb +ptp4l: port 1: LISTENING to UNCALIBRATED on RS_SLAVE +ptp4l: port 1: UNCALIBRATED to SLAVE on MASTER_CLOCK_SELECTED +ptp4l: rms 30 max 45 freq -9400 +/- 23 delay 72 +/- 0 +ptp4l: rms 7 max 16 freq -9414 +/- 10 delay 70 +/- 0 +ptp4l: rms 6 max 9 freq -9422 +/- 6 delay 72 +/- 0 +ptp4l: rms 13 max 20 freq -9436 +/- 13 delay 74 +/- 0 +ptp4l: rms 12 max 27 freq -9446 +/- 11 delay 72 +/- 0 +ptp4l: rms 9 max 12 freq -9453 +/- 6 delay 74 +/- 0 +ptp4l: rms 9 max 15 freq -9438 +/- 11 delay 74 +/- 0 +ptp4l: rms 10 max 16 freq -9435 +/- 12 delay 74 +/- 0 +ptp4l: rms 8 max 18 freq -9428 +/- 8 delay 72 +/- 0 +ptp4l: rms 8 max 18 freq -9423 +/- 8 delay 72 +/- 0 +ptp4l: rms 9 max 16 freq -9431 +/- 12 delay 70 +/- 0 +ptp4l: rms 9 max 18 freq -9441 +/- 9 delay 72 +/- 0 +----------------------------------------------------------- + +Fixes: ba1ffd74df74 ("stmmac: fix PTP support for GMAC4") +Cc: # 5.4.x +Signed-off-by: Song Yoong Siang +Signed-off-by: Tan Tee Min +--- + drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +index aac68dc28dc19..615d5764e52b3 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +@@ -292,10 +292,11 @@ static int dwmac4_wrback_get_rx_timestamp_status(void *desc, void *next_desc, + /* Check if timestamp is OK from context descriptor */ + do { + ret = dwmac4_rx_check_timestamp(next_desc); +- if (ret < 0) ++ if (ret <= 0) + goto exit; + i++; + ++ fsleep(1); + } while ((ret == 1) && (i < 10)); + + if (i == 10) +-- +2.43.0 + diff --git a/SPECS/kernel/0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf b/SPECS/kernel/0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf deleted file mode 100644 index 5de4385b1..000000000 --- a/SPECS/kernel/0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf +++ /dev/null @@ -1,233 +0,0 @@ -From 999573cf9a51cb35df1ecf5a3a014dd5fcb980f1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 28 Nov 2024 06:45:17 +0000 -Subject: [PATCH 015/100] perf/x86/intel/ds: Factor out PEBS group processing - code to functions - -Adaptive PEBS and arch-PEBS share lots of same code to process these -PEBS groups, like basic, GPR and meminfo groups. Extract these shared -code to generic functions to avoid duplicated code. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/ds.c | 170 +++++++++++++++++++++++-------------- - 1 file changed, 104 insertions(+), 66 deletions(-) - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index db40dc76eb20..f1d79b9e5f50 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2072,6 +2072,90 @@ static inline void __setup_pebs_counter_group(struct cpu_hw_events *cpuc, - - #define PEBS_LATENCY_MASK 0xffff - -+static inline void __setup_perf_sample_data(struct perf_event *event, -+ struct pt_regs *iregs, -+ struct perf_sample_data *data) -+{ -+ perf_sample_data_init(data, 0, event->hw.last_period); -+ -+ /* -+ * We must however always use iregs for the unwinder to stay sane; the -+ * record BP,SP,IP can point into thin air when the record is from a -+ * previous PMI context or an (I)RET happened between the record and -+ * PMI. -+ */ -+ perf_sample_save_callchain(data, event, iregs); -+} -+ -+static inline void __setup_pebs_basic_group(struct perf_event *event, -+ struct pt_regs *regs, -+ struct perf_sample_data *data, -+ u64 sample_type, u64 ip, -+ u64 tsc, u16 retire) -+{ -+ /* The ip in basic is EventingIP */ -+ set_linear_ip(regs, ip); -+ regs->flags = PERF_EFLAGS_EXACT; -+ setup_pebs_time(event, data, tsc); -+ -+ if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) -+ data->weight.var3_w = retire; -+} -+ -+static inline void __setup_pebs_gpr_group(struct perf_event *event, -+ struct pt_regs *regs, -+ struct pebs_gprs *gprs, -+ u64 sample_type) -+{ -+ if (event->attr.precise_ip < 2) { -+ set_linear_ip(regs, gprs->ip); -+ regs->flags &= ~PERF_EFLAGS_EXACT; -+ } -+ -+ if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) -+ adaptive_pebs_save_regs(regs, gprs); -+} -+ -+static inline void __setup_pebs_meminfo_group(struct perf_event *event, -+ struct perf_sample_data *data, -+ u64 sample_type, u64 latency, -+ u16 instr_latency, u64 address, -+ u64 aux, u64 tsx_tuning, u64 ax) -+{ -+ if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) { -+ u64 tsx_latency = intel_get_tsx_weight(tsx_tuning); -+ -+ data->weight.var2_w = instr_latency; -+ -+ /* -+ * Although meminfo::latency is defined as a u64, -+ * only the lower 32 bits include the valid data -+ * in practice on Ice Lake and earlier platforms. -+ */ -+ if (sample_type & PERF_SAMPLE_WEIGHT) -+ data->weight.full = latency ?: tsx_latency; -+ else -+ data->weight.var1_dw = (u32)latency ?: tsx_latency; -+ -+ data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE; -+ } -+ -+ if (sample_type & PERF_SAMPLE_DATA_SRC) { -+ data->data_src.val = get_data_src(event, aux); -+ data->sample_flags |= PERF_SAMPLE_DATA_SRC; -+ } -+ -+ if (sample_type & PERF_SAMPLE_ADDR_TYPE) { -+ data->addr = address; -+ data->sample_flags |= PERF_SAMPLE_ADDR; -+ } -+ -+ if (sample_type & PERF_SAMPLE_TRANSACTION) { -+ data->txn = intel_get_tsx_transaction(tsx_tuning, ax); -+ data->sample_flags |= PERF_SAMPLE_TRANSACTION; -+ } -+} -+ - /* - * With adaptive PEBS the layout depends on what fields are configured. - */ -@@ -2081,12 +2165,14 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - struct pt_regs *regs) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ u64 sample_type = event->attr.sample_type; - struct pebs_basic *basic = __pebs; - void *next_record = basic + 1; -- u64 sample_type, format_group; - struct pebs_meminfo *meminfo = NULL; - struct pebs_gprs *gprs = NULL; - struct x86_perf_regs *perf_regs; -+ u64 format_group; -+ u16 retire; - - if (basic == NULL) - return; -@@ -2094,31 +2180,17 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; - -- sample_type = event->attr.sample_type; - format_group = basic->format_group; -- perf_sample_data_init(data, 0, event->hw.last_period); - -- setup_pebs_time(event, data, basic->tsc); -- -- /* -- * We must however always use iregs for the unwinder to stay sane; the -- * record BP,SP,IP can point into thin air when the record is from a -- * previous PMI context or an (I)RET happened between the record and -- * PMI. -- */ -- perf_sample_save_callchain(data, event, iregs); -+ __setup_perf_sample_data(event, iregs, data); - - *regs = *iregs; -- /* The ip in basic is EventingIP */ -- set_linear_ip(regs, basic->ip); -- regs->flags = PERF_EFLAGS_EXACT; - -- if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) { -- if (x86_pmu.flags & PMU_FL_RETIRE_LATENCY) -- data->weight.var3_w = basic->retire_latency; -- else -- data->weight.var3_w = 0; -- } -+ /* basic group */ -+ retire = x86_pmu.flags & PMU_FL_RETIRE_LATENCY ? -+ basic->retire_latency : 0; -+ __setup_pebs_basic_group(event, regs, data, sample_type, -+ basic->ip, basic->tsc, retire); - - /* - * The record for MEMINFO is in front of GP -@@ -2134,54 +2206,20 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - gprs = next_record; - next_record = gprs + 1; - -- if (event->attr.precise_ip < 2) { -- set_linear_ip(regs, gprs->ip); -- regs->flags &= ~PERF_EFLAGS_EXACT; -- } -- -- if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER)) -- adaptive_pebs_save_regs(regs, gprs); -+ __setup_pebs_gpr_group(event, regs, gprs, sample_type); - } - - if (format_group & PEBS_DATACFG_MEMINFO) { -- if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) { -- u64 latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ? -- meminfo->cache_latency : meminfo->mem_latency; -- -- if (x86_pmu.flags & PMU_FL_INSTR_LATENCY) -- data->weight.var2_w = meminfo->instr_latency; -- -- /* -- * Although meminfo::latency is defined as a u64, -- * only the lower 32 bits include the valid data -- * in practice on Ice Lake and earlier platforms. -- */ -- if (sample_type & PERF_SAMPLE_WEIGHT) { -- data->weight.full = latency ?: -- intel_get_tsx_weight(meminfo->tsx_tuning); -- } else { -- data->weight.var1_dw = (u32)latency ?: -- intel_get_tsx_weight(meminfo->tsx_tuning); -- } -- -- data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE; -- } -- -- if (sample_type & PERF_SAMPLE_DATA_SRC) { -- data->data_src.val = get_data_src(event, meminfo->aux); -- data->sample_flags |= PERF_SAMPLE_DATA_SRC; -- } -- -- if (sample_type & PERF_SAMPLE_ADDR_TYPE) { -- data->addr = meminfo->address; -- data->sample_flags |= PERF_SAMPLE_ADDR; -- } -- -- if (sample_type & PERF_SAMPLE_TRANSACTION) { -- data->txn = intel_get_tsx_transaction(meminfo->tsx_tuning, -- gprs ? gprs->ax : 0); -- data->sample_flags |= PERF_SAMPLE_TRANSACTION; -- } -+ u64 latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ? -+ meminfo->cache_latency : meminfo->mem_latency; -+ u64 instr_latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ? -+ meminfo->instr_latency : 0; -+ u64 ax = gprs ? gprs->ax : 0; -+ -+ __setup_pebs_meminfo_group(event, data, sample_type, latency, -+ instr_latency, meminfo->address, -+ meminfo->aux, meminfo->tsx_tuning, -+ ax); - } - - if (format_group & PEBS_DATACFG_XMMS) { --- -2.43.0 - diff --git a/SPECS/kernel/0015-scsi-imm-Fix-use-after-free-bug-caused-by-unfinished.patch b/SPECS/kernel/0015-scsi-imm-Fix-use-after-free-bug-caused-by-unfinished.patch deleted file mode 100644 index da2964be8..000000000 --- a/SPECS/kernel/0015-scsi-imm-Fix-use-after-free-bug-caused-by-unfinished.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 3cb9169b624d34394440ce655a9b8bf5efc706e6 Mon Sep 17 00:00:00 2001 -From: Duoming Zhou -Date: Tue, 28 Oct 2025 18:01:49 +0800 -Subject: [PATCH 15/16] scsi: imm: Fix use-after-free bug caused by unfinished - delayed work - -The delayed work item 'imm_tq' is initialized in imm_attach() and -scheduled via imm_queuecommand() for processing SCSI commands. When the -IMM parallel port SCSI host adapter is detached through imm_detach(), -the imm_struct device instance is deallocated. - -However, the delayed work might still be pending or executing -when imm_detach() is called, leading to use-after-free bugs -when the work function imm_interrupt() accesses the already -freed imm_struct memory. - -The race condition can occur as follows: - -CPU 0(detach thread) | CPU 1 - | imm_queuecommand() - | imm_queuecommand_lck() -imm_detach() | schedule_delayed_work() - kfree(dev) //FREE | imm_interrupt() - | dev = container_of(...) //USE - dev-> //USE - -Add disable_delayed_work_sync() in imm_detach() to guarantee proper -cancellation of the delayed work item before imm_struct is deallocated. - -Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") -Signed-off-by: Duoming Zhou -Link: https://patch.msgid.link/20251028100149.40721-1-duoming@zju.edu.cn -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/imm.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c -index 0821cf994b98..8a099bc27e06 100644 ---- a/drivers/scsi/imm.c -+++ b/drivers/scsi/imm.c -@@ -1260,6 +1260,7 @@ static void imm_detach(struct parport *pb) - imm_struct *dev; - list_for_each_entry(dev, &imm_hosts, list) { - if (dev->dev->port == pb) { -+ disable_delayed_work_sync(&dev->imm_tq); - list_del_init(&dev->list); - scsi_remove_host(dev->host); - scsi_host_put(dev->host); --- -2.43.0 - diff --git a/SPECS/kernel/0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo b/SPECS/kernel/0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo new file mode 100644 index 000000000..97b682568 --- /dev/null +++ b/SPECS/kernel/0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo @@ -0,0 +1,554 @@ +From 1dd87cc0d4d7d7b0cb124ec1ee101dba9de3da8e Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Sun, 30 Nov 2025 00:11:22 -0500 +Subject: [PATCH 15/21] tools/power turbostat: Validate RAPL MSRs for AWS Nitro + Hypervisor + +Even though the platform->plat_rapl_msrs enumeration may be accurate, +a VM, such as AWS Nitro Hypervisor, may deny access to the underlying MSRs. + +Probe if PKG_ENERGY is readable and non-zero. +If no, ignore all RAPL MSRs. + +Reported-by: Emily Ehlert +Tested-by: Emily Ehlert +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 156 ++++++++++++++++---------- + 1 file changed, 98 insertions(+), 58 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index e85bdb00f24a5..4411ef44294f4 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -492,6 +492,7 @@ unsigned int quiet; + unsigned int shown; + unsigned int sums_need_wide_columns; + unsigned int rapl_joules; ++unsigned int valid_rapl_msrs; + unsigned int summary_only; + unsigned int list_header_only; + unsigned int dump_only; +@@ -588,7 +589,7 @@ struct platform_features { + bool has_cst_prewake_bit; /* Cstate prewake bit in MSR_IA32_POWER_CTL */ + int trl_msrs; /* MSR_TURBO_RATIO_LIMIT/LIMIT1/LIMIT2/SECONDARY, Atom TRL MSRs */ + int plr_msrs; /* MSR_CORE/GFX/RING_PERF_LIMIT_REASONS */ +- int rapl_msrs; /* RAPL PKG/DRAM/CORE/GFX MSRs, AMD RAPL MSRs */ ++ int plat_rapl_msrs; /* RAPL PKG/DRAM/CORE/GFX MSRs, AMD RAPL MSRs */ + bool has_per_core_rapl; /* Indicates cores energy collection is per-core, not per-package. AMD specific for now */ + bool has_rapl_divisor; /* Divisor for Energy unit raw value from MSR_RAPL_POWER_UNIT */ + bool has_fixed_rapl_unit; /* Fixed Energy Unit used for DRAM RAPL Domain */ +@@ -743,7 +744,7 @@ static const struct platform_features snb_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features snx_features = { +@@ -755,7 +756,7 @@ static const struct platform_features snx_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, + }; + + static const struct platform_features ivb_features = { +@@ -768,7 +769,7 @@ static const struct platform_features ivb_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features ivx_features = { +@@ -780,7 +781,7 @@ static const struct platform_features ivx_features = { + .cst_limit = CST_LIMIT_SNB, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_LIMIT1, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM_ALL, + }; + + static const struct platform_features hsw_features = { +@@ -794,7 +795,7 @@ static const struct platform_features hsw_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features hsx_features = { +@@ -808,7 +809,7 @@ static const struct platform_features hsx_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_LIMIT1 | TRL_LIMIT2, + .plr_msrs = PLR_CORE | PLR_RING, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + }; + +@@ -823,7 +824,7 @@ static const struct platform_features hswl_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features hswg_features = { +@@ -837,7 +838,7 @@ static const struct platform_features hswg_features = { + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, + .plr_msrs = PLR_CORE | PLR_GFX | PLR_RING, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features bdw_features = { +@@ -850,7 +851,7 @@ static const struct platform_features bdw_features = { + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features bdwg_features = { +@@ -863,7 +864,7 @@ static const struct platform_features bdwg_features = { + .cst_limit = CST_LIMIT_HSW, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE_ALL | RAPL_GFX | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features bdx_features = { +@@ -877,7 +878,7 @@ static const struct platform_features bdx_features = { + .has_irtl_msrs = 1, + .has_cst_auto_convension = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + }; + +@@ -894,7 +895,7 @@ static const struct platform_features skl_features = { + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, + .enable_tsc_tweak = 1, + }; + +@@ -911,7 +912,7 @@ static const struct platform_features cnl_features = { + .has_ext_cst_msrs = 1, + .trl_msrs = TRL_BASE, + .tcc_offset_bits = 6, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX | RAPL_PSYS, + .enable_tsc_tweak = 1, + }; + +@@ -929,7 +930,7 @@ static const struct platform_features adl_features = { + .has_ext_cst_msrs = cnl_features.has_ext_cst_msrs, + .trl_msrs = cnl_features.trl_msrs, + .tcc_offset_bits = cnl_features.tcc_offset_bits, +- .rapl_msrs = cnl_features.rapl_msrs, ++ .plat_rapl_msrs = cnl_features.plat_rapl_msrs, + .enable_tsc_tweak = cnl_features.enable_tsc_tweak, + }; + +@@ -947,7 +948,7 @@ static const struct platform_features lnl_features = { + .has_ext_cst_msrs = adl_features.has_ext_cst_msrs, + .trl_msrs = adl_features.trl_msrs, + .tcc_offset_bits = adl_features.tcc_offset_bits, +- .rapl_msrs = adl_features.rapl_msrs, ++ .plat_rapl_msrs = adl_features.plat_rapl_msrs, + .enable_tsc_tweak = adl_features.enable_tsc_tweak, + }; + +@@ -962,7 +963,7 @@ static const struct platform_features skx_features = { + .has_irtl_msrs = 1, + .has_cst_auto_convension = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + }; + +@@ -978,7 +979,7 @@ static const struct platform_features icx_features = { + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + .has_fixed_rapl_unit = 1, + }; + +@@ -995,7 +996,7 @@ static const struct platform_features spr_features = { + .has_cst_prewake_bit = 1, + .has_fixed_rapl_psys_unit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + }; + + static const struct platform_features dmr_features = { +@@ -1010,7 +1011,7 @@ static const struct platform_features dmr_features = { + .has_fixed_rapl_psys_unit = spr_features.has_fixed_rapl_psys_unit, + .trl_msrs = spr_features.trl_msrs, + .has_msr_module_c6_res_ms = 1, /* DMR has Dual-Core-Module and MC6 MSR */ +- .rapl_msrs = 0, /* DMR does not have RAPL MSRs */ ++ .plat_rapl_msrs = 0, /* DMR does not have RAPL MSRs */ + .plr_msrs = 0, /* DMR does not have PLR MSRs */ + .has_irtl_msrs = 0, /* DMR does not have IRTL MSRs */ + .has_config_tdp = 0, /* DMR does not have CTDP MSRs */ +@@ -1029,7 +1030,7 @@ static const struct platform_features srf_features = { + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + }; + + static const struct platform_features grr_features = { +@@ -1045,7 +1046,7 @@ static const struct platform_features grr_features = { + .has_irtl_msrs = 1, + .has_cst_prewake_bit = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_PSYS, + }; + + static const struct platform_features slv_features = { +@@ -1058,7 +1059,7 @@ static const struct platform_features slv_features = { + .has_msr_c6_demotion_policy_config = 1, + .has_msr_atom_pkg_c6_residency = 1, + .trl_msrs = TRL_ATOM, +- .rapl_msrs = RAPL_PKG | RAPL_CORE, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE, + .has_rapl_divisor = 1, + .rapl_quirk_tdp = 30, + }; +@@ -1071,7 +1072,7 @@ static const struct platform_features slvd_features = { + .cst_limit = CST_LIMIT_SLV, + .has_msr_atom_pkg_c6_residency = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_CORE, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_CORE, + .rapl_quirk_tdp = 30, + }; + +@@ -1092,7 +1093,7 @@ static const struct platform_features gmt_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features gmtd_features = { +@@ -1105,7 +1106,7 @@ static const struct platform_features gmtd_features = { + .has_irtl_msrs = 1, + .has_msr_core_c1_res = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_CORE_ENERGY_STATUS, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL | RAPL_CORE_ENERGY_STATUS, + }; + + static const struct platform_features gmtp_features = { +@@ -1117,7 +1118,7 @@ static const struct platform_features gmtp_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, ++ .plat_rapl_msrs = RAPL_PKG | RAPL_PKG_POWER_INFO, + }; + + static const struct platform_features tmt_features = { +@@ -1128,7 +1129,7 @@ static const struct platform_features tmt_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_CORE_ALL | RAPL_DRAM | RAPL_DRAM_PERF_STATUS | RAPL_GFX, + .enable_tsc_tweak = 1, + }; + +@@ -1140,7 +1141,7 @@ static const struct platform_features tmtd_features = { + .cst_limit = CST_LIMIT_GMT, + .has_irtl_msrs = 1, + .trl_msrs = TRL_BASE | TRL_CORECOUNT, +- .rapl_msrs = RAPL_PKG_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL, + }; + + static const struct platform_features knl_features = { +@@ -1152,7 +1153,7 @@ static const struct platform_features knl_features = { + .cst_limit = CST_LIMIT_KNL, + .has_msr_knl_core_c6_residency = 1, + .trl_msrs = TRL_KNL, +- .rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, ++ .plat_rapl_msrs = RAPL_PKG_ALL | RAPL_DRAM_ALL, + .has_fixed_rapl_unit = 1, + .need_perf_multiplier = 1, + }; +@@ -1161,7 +1162,7 @@ static const struct platform_features default_features = { + }; + + static const struct platform_features amd_features_with_rapl = { +- .rapl_msrs = RAPL_AMD_F17H, ++ .plat_rapl_msrs = RAPL_AMD_F17H, + .has_per_core_rapl = 1, + .rapl_quirk_tdp = 280, /* This is the max stock TDP of HEDT/Server Fam17h+ chips */ + }; +@@ -2136,7 +2137,7 @@ off_t idx_to_offset(int idx) + + switch (idx) { + case IDX_PKG_ENERGY: +- if (platform->rapl_msrs & RAPL_AMD_F17H) ++ if (valid_rapl_msrs & RAPL_AMD_F17H) + offset = MSR_PKG_ENERGY_STAT; + else + offset = MSR_PKG_ENERGY_STATUS; +@@ -2202,19 +2203,19 @@ int idx_valid(int idx) + { + switch (idx) { + case IDX_PKG_ENERGY: +- return platform->rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H); ++ return valid_rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H); + case IDX_DRAM_ENERGY: +- return platform->rapl_msrs & RAPL_DRAM; ++ return valid_rapl_msrs & RAPL_DRAM; + case IDX_PP0_ENERGY: +- return platform->rapl_msrs & RAPL_CORE_ENERGY_STATUS; ++ return valid_rapl_msrs & RAPL_CORE_ENERGY_STATUS; + case IDX_PP1_ENERGY: +- return platform->rapl_msrs & RAPL_GFX; ++ return valid_rapl_msrs & RAPL_GFX; + case IDX_PKG_PERF: +- return platform->rapl_msrs & RAPL_PKG_PERF_STATUS; ++ return valid_rapl_msrs & RAPL_PKG_PERF_STATUS; + case IDX_DRAM_PERF: +- return platform->rapl_msrs & RAPL_DRAM_PERF_STATUS; ++ return valid_rapl_msrs & RAPL_DRAM_PERF_STATUS; + case IDX_PSYS_ENERGY: +- return platform->rapl_msrs & RAPL_PSYS; ++ return valid_rapl_msrs & RAPL_PSYS; + default: + return 0; + } +@@ -2517,7 +2518,7 @@ int add_rapl_msr_counter(int cpu, const struct rapl_counter_arch_info *cai) + { + int ret; + +- if (!(platform->rapl_msrs & cai->feature_mask)) ++ if (!(valid_rapl_msrs & cai->feature_mask)) + return -1; + + ret = add_msr_counter(cpu, cai->msr); +@@ -2850,10 +2851,10 @@ void print_header(char *delim) + if (DO_BIC(BIC_CORE_THROT_CNT)) + outp += sprintf(outp, "%sCoreThr", (printed++ ? delim : "")); + +- if (platform->rapl_msrs && !rapl_joules) { ++ if (valid_rapl_msrs && !rapl_joules) { + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) + outp += sprintf(outp, "%sCorWatt", (printed++ ? delim : "")); +- } else if (platform->rapl_msrs && rapl_joules) { ++ } else if (valid_rapl_msrs && rapl_joules) { + if (DO_BIC(BIC_Cor_J) && platform->has_per_core_rapl) + outp += sprintf(outp, "%sCor_J", (printed++ ? delim : "")); + } +@@ -7572,7 +7573,7 @@ double get_tdp_intel(void) + { + unsigned long long msr; + +- if (platform->rapl_msrs & RAPL_PKG_POWER_INFO) ++ if (valid_rapl_msrs & RAPL_PKG_POWER_INFO) + if (!get_msr(base_cpu, MSR_PKG_POWER_INFO, &msr)) + return ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units; + return get_quirk_tdp(); +@@ -7603,12 +7604,12 @@ void rapl_probe_intel(void) + CLR_BIC(BIC_GFX_J, &bic_enabled); + } + +- if (!platform->rapl_msrs || no_msr) ++ if (!valid_rapl_msrs || no_msr) + return; + +- if (!(platform->rapl_msrs & RAPL_PKG_PERF_STATUS)) ++ if (!(valid_rapl_msrs & RAPL_PKG_PERF_STATUS)) + CLR_BIC(BIC_PKG__, &bic_enabled); +- if (!(platform->rapl_msrs & RAPL_DRAM_PERF_STATUS)) ++ if (!(valid_rapl_msrs & RAPL_DRAM_PERF_STATUS)) + CLR_BIC(BIC_RAM__, &bic_enabled); + + /* units on package 0, verify later other packages match */ +@@ -7657,7 +7658,7 @@ void rapl_probe_amd(void) + CLR_BIC(BIC_Cor_J, &bic_enabled); + } + +- if (!platform->rapl_msrs || no_msr) ++ if (!valid_rapl_msrs || no_msr) + return; + + if (get_msr(base_cpu, MSR_RAPL_PWR_UNIT, &msr)) +@@ -7847,7 +7848,7 @@ int print_rapl(PER_THREAD_PARAMS) + UNUSED(c); + UNUSED(p); + +- if (!platform->rapl_msrs) ++ if (!valid_rapl_msrs) + return 0; + + /* RAPL counters are per package, so print only for 1st thread/package */ +@@ -7860,7 +7861,7 @@ int print_rapl(PER_THREAD_PARAMS) + return -1; + } + +- if (platform->rapl_msrs & RAPL_AMD_F17H) { ++ if (valid_rapl_msrs & RAPL_AMD_F17H) { + msr_name = "MSR_RAPL_PWR_UNIT"; + if (get_msr(cpu, MSR_RAPL_PWR_UNIT, &msr)) + return -1; +@@ -7873,7 +7874,7 @@ int print_rapl(PER_THREAD_PARAMS) + fprintf(outf, "cpu%d: %s: 0x%08llx (%f Watts, %f Joules, %f sec.)\n", cpu, msr_name, msr, + rapl_power_units, rapl_energy_units, rapl_time_units); + +- if (platform->rapl_msrs & RAPL_PKG_POWER_INFO) { ++ if (valid_rapl_msrs & RAPL_PKG_POWER_INFO) { + + if (get_msr(cpu, MSR_PKG_POWER_INFO, &msr)) + return -5; +@@ -7886,7 +7887,7 @@ int print_rapl(PER_THREAD_PARAMS) + ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + + } +- if (platform->rapl_msrs & RAPL_PKG) { ++ if (valid_rapl_msrs & RAPL_PKG) { + + if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr)) + return -9; +@@ -7910,7 +7911,7 @@ int print_rapl(PER_THREAD_PARAMS) + cpu, ((msr >> 0) & 0x1FFF) * rapl_power_units, (msr >> 31) & 1 ? "" : "UN"); + } + +- if (platform->rapl_msrs & RAPL_DRAM_POWER_INFO) { ++ if (valid_rapl_msrs & RAPL_DRAM_POWER_INFO) { + if (get_msr(cpu, MSR_DRAM_POWER_INFO, &msr)) + return -6; + +@@ -7921,7 +7922,7 @@ int print_rapl(PER_THREAD_PARAMS) + ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, + ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + } +- if (platform->rapl_msrs & RAPL_DRAM) { ++ if (valid_rapl_msrs & RAPL_DRAM) { + if (get_msr(cpu, MSR_DRAM_POWER_LIMIT, &msr)) + return -9; + fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", +@@ -7929,20 +7930,20 @@ int print_rapl(PER_THREAD_PARAMS) + + print_power_limit_msr(cpu, msr, "DRAM Limit"); + } +- if (platform->rapl_msrs & RAPL_CORE_POLICY) { ++ if (valid_rapl_msrs & RAPL_CORE_POLICY) { + if (get_msr(cpu, MSR_PP0_POLICY, &msr)) + return -7; + + fprintf(outf, "cpu%d: MSR_PP0_POLICY: %lld\n", cpu, msr & 0xF); + } +- if (platform->rapl_msrs & RAPL_CORE_POWER_LIMIT) { ++ if (valid_rapl_msrs & RAPL_CORE_POWER_LIMIT) { + if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr)) + return -9; + fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", + cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + print_power_limit_msr(cpu, msr, "Cores Limit"); + } +- if (platform->rapl_msrs & RAPL_GFX) { ++ if (valid_rapl_msrs & RAPL_GFX) { + if (get_msr(cpu, MSR_PP1_POLICY, &msr)) + return -8; + +@@ -7957,6 +7958,43 @@ int print_rapl(PER_THREAD_PARAMS) + return 0; + } + ++/* ++ * probe_rapl_msrs ++ * ++ * initialize global valid_rapl_msrs to platform->plat_rapl_msrs ++ * only if PKG_ENERGY counter is enumerated and reads non-zero ++ */ ++void probe_rapl_msrs(void) ++{ ++ int ret; ++ off_t offset; ++ unsigned long long msr_value; ++ ++ if (no_msr) ++ return; ++ ++ if ((platform->plat_rapl_msrs & (RAPL_PKG | RAPL_AMD_F17H)) == 0) ++ return; ++ ++ offset = idx_to_offset(IDX_PKG_ENERGY); ++ if (offset < 0) ++ return; ++ ++ ret = get_msr(base_cpu, offset, &msr_value); ++ if (ret) { ++ if (debug) ++ fprintf(outf, "Can not read RAPL_PKG_ENERGY MSR(0x%llx)\n", (unsigned long long)offset); ++ return; ++ } ++ if (msr_value == 0) { ++ if (debug) ++ fprintf(outf, "RAPL_PKG_ENERGY MSR(0x%llx) == ZERO: disabling all RAPL MSRs\n", (unsigned long long)offset); ++ return; ++ } ++ ++ valid_rapl_msrs = platform->plat_rapl_msrs; /* success */ ++} ++ + /* + * probe_rapl() + * +@@ -7964,6 +8002,8 @@ int print_rapl(PER_THREAD_PARAMS) + */ + void probe_rapl(void) + { ++ probe_rapl_msrs(); ++ + if (genuine_intel) + rapl_probe_intel(); + if (authentic_amd || hygon_genuine) +@@ -7974,7 +8014,7 @@ void probe_rapl(void) + + print_rapl_sysfs(); + +- if (!platform->rapl_msrs || no_msr) ++ if (!valid_rapl_msrs || no_msr) + return; + + for_all_cpus(print_rapl, ODD_COUNTERS); +@@ -10157,7 +10197,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.11.29 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.12.01 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel/0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi b/SPECS/kernel/0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi new file mode 100644 index 000000000..5d0cb3931 --- /dev/null +++ b/SPECS/kernel/0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi @@ -0,0 +1,133 @@ +From 2a578d27be4741b1275fb893d00be8442ca0a48a Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 28 Feb 2023 13:06:07 -0800 +Subject: [PATCH 16/44] KVM: VMX: Dump FRED context in dump_vmcs() + +Add FRED related VMCS fields to dump_vmcs() to dump FRED context. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v5: +* Read guest FRED RSP0 with vmx_read_guest_fred_rsp0() (Sean). +* Add TB from Xuelian Guo. + +Change in v3: +* Use (vmentry_ctrl & VM_ENTRY_LOAD_IA32_FRED) instead of is_fred_enabled() + (Chao Gao). + +Changes in v2: +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). +* Dump guest FRED states only if guest has FRED enabled (Nikolay Borisov). +--- + arch/x86/kvm/vmx/vmx.c | 43 +++++++++++++++++++++++++++++++++++------- + 1 file changed, 36 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 9d12c4a01ab2f..4fdf80d02dadd 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -1398,6 +1398,9 @@ static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data) + vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data, + &vmx->msr_guest_fred_rsp0); + } ++#else ++/* Make sure it builds on 32-bit */ ++static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx) { return 0; } + #endif + + static void grow_ple_window(struct kvm_vcpu *vcpu) +@@ -6449,7 +6452,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + struct vcpu_vmx *vmx = to_vmx(vcpu); + u32 vmentry_ctl, vmexit_ctl; + u32 cpu_based_exec_ctrl, pin_based_exec_ctrl, secondary_exec_control; +- u64 tertiary_exec_control; ++ u64 tertiary_exec_control, secondary_vmexit_ctl; + unsigned long cr4; + int efer_slot; + +@@ -6460,6 +6463,8 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + + vmentry_ctl = vmcs_read32(VM_ENTRY_CONTROLS); + vmexit_ctl = vmcs_read32(VM_EXIT_CONTROLS); ++ secondary_vmexit_ctl = cpu_has_secondary_vmexit_ctrls() ? ++ vmcs_read64(SECONDARY_VM_EXIT_CONTROLS) : 0; + cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); + pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL); + cr4 = vmcs_readl(GUEST_CR4); +@@ -6506,6 +6511,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + vmx_dump_sel("LDTR:", GUEST_LDTR_SELECTOR); + vmx_dump_dtsel("IDTR:", GUEST_IDTR_LIMIT); + vmx_dump_sel("TR: ", GUEST_TR_SELECTOR); ++ if (vmentry_ctl & VM_ENTRY_LOAD_IA32_FRED) ++ pr_err("FRED guest: config=0x%016llx, stack_levels=0x%016llx\n" ++ "RSP0=0x%016llx, RSP1=0x%016llx\n" ++ "RSP2=0x%016llx, RSP3=0x%016llx\n", ++ vmcs_read64(GUEST_IA32_FRED_CONFIG), ++ vmcs_read64(GUEST_IA32_FRED_STKLVLS), ++ vmx_read_guest_fred_rsp0(vmx), ++ vmcs_read64(GUEST_IA32_FRED_RSP1), ++ vmcs_read64(GUEST_IA32_FRED_RSP2), ++ vmcs_read64(GUEST_IA32_FRED_RSP3)); + efer_slot = vmx_find_loadstore_msr_slot(&vmx->msr_autoload.guest, MSR_EFER); + if (vmentry_ctl & VM_ENTRY_LOAD_IA32_EFER) + pr_err("EFER= 0x%016llx\n", vmcs_read64(GUEST_IA32_EFER)); +@@ -6557,6 +6572,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + vmcs_readl(HOST_TR_BASE)); + pr_err("GDTBase=%016lx IDTBase=%016lx\n", + vmcs_readl(HOST_GDTR_BASE), vmcs_readl(HOST_IDTR_BASE)); ++ if (vmexit_ctl & SECONDARY_VM_EXIT_LOAD_IA32_FRED) ++ pr_err("FRED host: config=0x%016llx, stack_levels=0x%016llx\n" ++ "RSP0=0x%016lx, RSP1=0x%016llx\n" ++ "RSP2=0x%016llx, RSP3=0x%016llx\n", ++ vmcs_read64(HOST_IA32_FRED_CONFIG), ++ vmcs_read64(HOST_IA32_FRED_STKLVLS), ++ (unsigned long)task_stack_page(current) + THREAD_SIZE, ++ vmcs_read64(HOST_IA32_FRED_RSP1), ++ vmcs_read64(HOST_IA32_FRED_RSP2), ++ vmcs_read64(HOST_IA32_FRED_RSP3)); + pr_err("CR0=%016lx CR3=%016lx CR4=%016lx\n", + vmcs_readl(HOST_CR0), vmcs_readl(HOST_CR3), + vmcs_readl(HOST_CR4)); +@@ -6582,25 +6607,29 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + pr_err("*** Control State ***\n"); + pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", + cpu_based_exec_ctrl, secondary_exec_control, tertiary_exec_control); +- pr_err("PinBased=0x%08x EntryControls=%08x ExitControls=%08x\n", +- pin_based_exec_ctrl, vmentry_ctl, vmexit_ctl); ++ pr_err("PinBased=0x%08x EntryControls=0x%08x\n", ++ pin_based_exec_ctrl, vmentry_ctl); ++ pr_err("ExitControls=0x%08x SecondaryExitControls=0x%016llx\n", ++ vmexit_ctl, secondary_vmexit_ctl); + pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n", + vmcs_read32(EXCEPTION_BITMAP), + vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK), + vmcs_read32(PAGE_FAULT_ERROR_CODE_MATCH)); +- pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x\n", ++ pr_err("VMEntry: intr_info=%08x errcode=%08x ilen=%08x event_data=%016llx\n", + vmcs_read32(VM_ENTRY_INTR_INFO_FIELD), + vmcs_read32(VM_ENTRY_EXCEPTION_ERROR_CODE), +- vmcs_read32(VM_ENTRY_INSTRUCTION_LEN)); ++ vmcs_read32(VM_ENTRY_INSTRUCTION_LEN), ++ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(INJECTED_EVENT_DATA) : 0); + pr_err("VMExit: intr_info=%08x errcode=%08x ilen=%08x\n", + vmcs_read32(VM_EXIT_INTR_INFO), + vmcs_read32(VM_EXIT_INTR_ERROR_CODE), + vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); + pr_err(" reason=%08x qualification=%016lx\n", + vmcs_read32(VM_EXIT_REASON), vmcs_readl(EXIT_QUALIFICATION)); +- pr_err("IDTVectoring: info=%08x errcode=%08x\n", ++ pr_err("IDTVectoring: info=%08x errcode=%08x event_data=%016llx\n", + vmcs_read32(IDT_VECTORING_INFO_FIELD), +- vmcs_read32(IDT_VECTORING_ERROR_CODE)); ++ vmcs_read32(IDT_VECTORING_ERROR_CODE), ++ kvm_cpu_cap_has(X86_FEATURE_FRED) ? vmcs_read64(ORIGINAL_EVENT_DATA) : 0); + pr_err("TSC Offset = 0x%016llx\n", vmcs_read64(TSC_OFFSET)); + if (secondary_exec_control & SECONDARY_EXEC_TSC_SCALING) + pr_err("TSC Multiplier = 0x%016llx\n", +-- +2.43.0 + diff --git a/SPECS/kernel/0016-KVM-x86-Advertise-support-for-FRED.nmi b/SPECS/kernel/0016-KVM-x86-Advertise-support-for-FRED.nmi deleted file mode 100644 index 965f69f75..000000000 --- a/SPECS/kernel/0016-KVM-x86-Advertise-support-for-FRED.nmi +++ /dev/null @@ -1,37 +0,0 @@ -From d8890903db72a044940523acbf532c7807141723 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Wed, 11 May 2022 17:53:27 -0700 -Subject: [PATCH 16/44] KVM: x86: Advertise support for FRED - -Advertise support for FRED to userspace after changes required to enable -FRED in a KVM guest are in place. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Don't advertise FRED/LKGS together, LKGS can be advertised as an - independent feature (Sean). -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/cpuid.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index 5f76dd7a7620..15217805c007 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -996,6 +996,7 @@ void kvm_set_cpu_caps(void) - F(FSRS), - F(FSRC), - F(WRMSRNS), -+ X86_64_F(FRED), - X86_64_F(LKGS), - F(AMX_FP16), - F(AVX_IFMA), --- -2.43.0 - diff --git a/SPECS/kernel/0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet b/SPECS/kernel/0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet deleted file mode 100644 index ca07fbf34..000000000 --- a/SPECS/kernel/0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet +++ /dev/null @@ -1,63 +0,0 @@ -From a1ccfaa33793cad7f90e4523bc33708cf05e6111 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:46 -0700 -Subject: [PATCH 16/24] KVM: x86: Save and reload SSP to/from SMRAM - -Save CET SSP to SMRAM on SMI and reload it on RSM. KVM emulates HW arch -behavior when guest enters/leaves SMM mode,i.e., save registers to SMRAM -at the entry of SMM and reload them at the exit to SMM. Per SDM, SSP is -one of such registers on 64-bit Arch, and add the support for SSP. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/smm.c | 8 ++++++++ - arch/x86/kvm/smm.h | 2 +- - 2 files changed, 9 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/smm.c b/arch/x86/kvm/smm.c -index 5dd8a1646800..b0b14ba37f9a 100644 ---- a/arch/x86/kvm/smm.c -+++ b/arch/x86/kvm/smm.c -@@ -269,6 +269,10 @@ static void enter_smm_save_state_64(struct kvm_vcpu *vcpu, - enter_smm_save_seg_64(vcpu, &smram->gs, VCPU_SREG_GS); - - smram->int_shadow = kvm_x86_call(get_interrupt_shadow)(vcpu); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ kvm_msr_read(vcpu, MSR_KVM_INTERNAL_GUEST_SSP, &smram->ssp)) -+ kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); - } - #endif - -@@ -558,6 +562,10 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, - kvm_x86_call(set_interrupt_shadow)(vcpu, 0); - ctxt->interruptibility = (u8)smstate->int_shadow; - -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) && -+ kvm_msr_write(vcpu, MSR_KVM_INTERNAL_GUEST_SSP, smstate->ssp)) -+ return X86EMUL_UNHANDLEABLE; -+ - return X86EMUL_CONTINUE; - } - #endif -diff --git a/arch/x86/kvm/smm.h b/arch/x86/kvm/smm.h -index 551703fbe200..db3c88f16138 100644 ---- a/arch/x86/kvm/smm.h -+++ b/arch/x86/kvm/smm.h -@@ -116,8 +116,8 @@ struct kvm_smram_state_64 { - u32 smbase; - u32 reserved4[5]; - -- /* ssp and svm_* fields below are not implemented by KVM */ - u64 ssp; -+ /* svm_* fields below are not implemented by KVM */ - u64 svm_guest_pat; - u64 svm_host_efer; - u64 svm_host_cr4; --- -2.43.0 - diff --git a/SPECS/kernel/0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu b/SPECS/kernel/0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu deleted file mode 100644 index 955681991..000000000 --- a/SPECS/kernel/0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu +++ /dev/null @@ -1,806 +0,0 @@ -From c5b019c970132fd87f0d8c9477ec6620c1404c60 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 26 Jan 2026 16:00:15 +0800 -Subject: [PATCH 16/21] Port over IPU ACPI drivers changes from VTG github repo - -Removing Linux version macro from upstream code. -This macro removal is to align with innersource -kernel PR submission standard. - -These are the ACPI drivers copied over from ipu6-drivers repo: -- drivers/media/platform/intel/Kconfig -- drivers/media/platform/intel/Makefile -- drivers/media/platform/intel/ipu-acpi-common.c -- drivers/media/platform/intel/ipu-acpi-pdata.c -- drivers/media/platform/intel/ipu-acpi.c - -This is the ACPI drivers copied over from ipu7-drivers repo: -- drivers/media/platform/intel/ipu7-fpga-pdata.c - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/platform/intel/Kconfig | 3 +- - drivers/media/platform/intel/Makefile | 8 +- - .../media/platform/intel/ipu-acpi-common.c | 8 +- - drivers/media/platform/intel/ipu-acpi-pdata.c | 394 ++++++++++++++++-- - drivers/media/platform/intel/ipu-acpi.c | 68 ++- - .../media/platform/intel/ipu7-fpga-pdata.c | 3 + - 6 files changed, 414 insertions(+), 70 deletions(-) - -diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig -index f8affa636c5c..4ec9c70b80cf 100644 ---- a/drivers/media/platform/intel/Kconfig -+++ b/drivers/media/platform/intel/Kconfig -@@ -8,9 +8,8 @@ config INTEL_IPU7_FPGA_PDATA - development and mainly used for pixter or sensor enablement - without ACPI support. - --config INTEL_IPU7_ACPI -+config INTEL_IPU_ACPI - tristate "Enable IPU ACPI driver" -- default VIDEO_INTEL_IPU7 - depends on I2C - depends on ACPI - help -diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile -index d0c41b731794..64ecc8c1fff3 100644 ---- a/drivers/media/platform/intel/Makefile -+++ b/drivers/media/platform/intel/Makefile -@@ -1,5 +1,5 @@ - # SPDX-License-Identifier: GPL-2.0 --# Copyright (c) 2010 - 2022 Intel Corporation. -+# Copyright (c) 2010 - 2025 Intel Corporation. - - is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) - ifeq ($(is_kernel_lt_6_10), 1) -@@ -8,10 +8,10 @@ src := $(srctree)/$(src) - endif - endif - --ccflags-y += -I$(src)/../../pci/intel/ipu7/ -+ccflags-y += -I$(src)/../../../staging/media/ipu7/ - --ifneq ($(filter y m, $(CONFIG_INTEL_IPU7_ACPI)),) --obj-$(CONFIG_INTEL_IPU7_ACPI) += ipu-acpi.o \ -+ifneq ($(filter y m, $(CONFIG_INTEL_IPU_ACPI)),) -+obj-$(CONFIG_INTEL_IPU_ACPI) += ipu-acpi.o \ - ipu-acpi-pdata.o \ - ipu-acpi-common.o - else -diff --git a/drivers/media/platform/intel/ipu-acpi-common.c b/drivers/media/platform/intel/ipu-acpi-common.c -index ee7bd6c93f9a..f846cb574fca 100644 ---- a/drivers/media/platform/intel/ipu-acpi-common.c -+++ b/drivers/media/platform/intel/ipu-acpi-common.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0 - /* -- * Copyright (c) 2016-2024 Intel Corporation. -+ * Copyright (c) 2016-2025 Intel Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version -@@ -12,8 +12,12 @@ - * GNU General Public License for more details. - * - */ --#include -+ - #include -+#include -+#include -+#include -+ - #include - #include - -diff --git a/drivers/media/platform/intel/ipu-acpi-pdata.c b/drivers/media/platform/intel/ipu-acpi-pdata.c -index 0a84dccf980c..8488693f0068 100644 ---- a/drivers/media/platform/intel/ipu-acpi-pdata.c -+++ b/drivers/media/platform/intel/ipu-acpi-pdata.c -@@ -12,18 +12,32 @@ - * GNU General Public License for more details. - * - */ -- - #include - #include - - #define MIN_SENSOR_I2C 1 - #define MIN_SERDES_I2C 3 -+#define SENSOR_2X_I2C 5 - #define SUFFIX_BASE 97 -+#define MSG_LEN 128 - --struct ipu7_isys_subdev_pdata acpi_subdev_pdata = { -- .subdevs = (struct ipu7_isys_subdev_info *[]) { -- NULL, -- } -+static struct ipu_isys_subdev_pdata *ptr_built_in_pdata; -+ -+void set_built_in_pdata(struct ipu_isys_subdev_pdata *pdata) -+{ -+ ptr_built_in_pdata = pdata; -+}; -+EXPORT_SYMBOL(set_built_in_pdata); -+ -+static struct ipu_isys_clk_mapping clk_mapping[] = { -+ { CLKDEV_INIT(NULL, NULL, NULL), NULL } -+}; -+ -+struct ipu_isys_subdev_pdata acpi_subdev_pdata = { -+ .subdevs = (struct ipu_isys_subdev_info *[]) { -+ NULL, NULL, NULL, NULL, NULL, -+ }, -+ .clk_map = clk_mapping, - }; - - struct serdes_local serdes_info; -@@ -74,7 +88,8 @@ static int get_i2c_bus_id(int adapter_id, char *adapter_bdf, int bdf_len) - */ - static void update_i2c_bus_id(void) - { -- struct ipu7_isys_subdev_info **subdevs = acpi_subdev_pdata.subdevs; -+ struct ipu_isys_subdev_info **subdevs = acpi_subdev_pdata.subdevs; -+ - for (int i = 0; subdevs[i] != NULL; i++) { - subdevs[i]->i2c.i2c_adapter_id = - get_i2c_bus_id(subdevs[i]->i2c.i2c_adapter_id, -@@ -83,9 +98,9 @@ static void update_i2c_bus_id(void) - } - } - --struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void) -+struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void) - { -- struct ipu7_isys_subdev_pdata *ptr; -+ struct ipu_isys_subdev_pdata *ptr; - - update_i2c_bus_id(); - ptr = &acpi_subdev_pdata; -@@ -122,7 +137,7 @@ static void print_serdes_sdinfo(struct serdes_subdev_info *sdinfo) - (int)sd_mpdata->gpio_powerup_seq[i]); - } - --static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) -+static void print_serdes_subdev(struct ipu_isys_subdev_info *sd) - { - struct serdes_platform_data *sd_pdata = sd->i2c.board_info.platform_data; - int i; -@@ -153,6 +168,7 @@ static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) - pr_debug("\t\tlink_freq_mbps \t\t= %d", sd_pdata->link_freq_mbps); - pr_debug("\t\tdeser_nlanes \t\t= %d", sd_pdata->deser_nlanes); - pr_debug("\t\tser_nlanes \t\t= %d", sd_pdata->ser_nlanes); -+ pr_debug("\t\tser_name \t\t= %s", sd_pdata->ser_name); - - for (i = 0; i < serdes_info.rx_port; i++) { - sd_sdinfo = &sd_pdata->subdev_info[i]; -@@ -167,7 +183,7 @@ static void print_serdes_subdev(struct ipu7_isys_subdev_info *sd) - - } - --static void print_subdev(struct ipu7_isys_subdev_info *sd) -+static void print_subdev(struct ipu_isys_subdev_info *sd) - { - struct sensor_platform_data *spdata = sd->i2c.board_info.platform_data; - int i; -@@ -198,10 +214,316 @@ static void print_subdev(struct ipu7_isys_subdev_info *sd) - pr_debug("\t\treset_pin \t\t= %d", spdata->reset_pin); - pr_debug("\t\tdetect_pin \t\t= %d", spdata->detect_pin); - -- for (i = 0; i < IPU7_SPDATA_GPIO_NUM; i++) -+ for (i = 0; i < IPU_SPDATA_GPIO_NUM; i++) - pr_debug("\t\tgpios[%d] \t\t= %d", i, spdata->gpios[i]); - } - -+static void add_local_subdevs(struct ipu_isys_subdev_info *new_subdev_info) -+{ -+ struct ipu_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -+ int i = 0; -+ -+ while (i < MAX_ACPI_SENSOR_NUM) { -+ if (!ptr_acpi_subdev_pdata->subdevs[i]) { -+ ptr_acpi_subdev_pdata->subdevs[i] = new_subdev_info; -+ ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -+ break; -+ } -+ i++; -+ } -+} -+ -+static void update_short(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned short *old_short, -+ unsigned int new_short) -+{ -+ if (*old_short != new_short) { -+ dev_info(dev, "%s 0x%x -> 0x%x", msg, *old_short, new_short); -+ *old_short = new_short; -+ } -+} -+ -+static void update_hex(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned int *old_hex, -+ unsigned int new_hex) -+{ -+ if (*old_hex != new_hex) { -+ dev_info(dev, "%s 0x%x -> 0x%x", msg, *old_hex, new_hex); -+ *old_hex = new_hex; -+ } -+} -+ -+static void update_int(struct device *dev, -+ char msg[MSG_LEN], -+ unsigned int *old_int, -+ unsigned int new_int) -+{ -+ if (*old_int != new_int) { -+ dev_info(dev, "%s %d -> %d", msg, *old_int, new_int); -+ *old_int = new_int; -+ } -+} -+ -+static void update_inta(struct device *dev, -+ char msg[MSG_LEN], -+ int old_int[MSG_LEN], -+ int new_int[MSG_LEN], -+ size_t size) -+{ -+ int i; -+ -+ for (i = 0; i < size; i++) { -+ if (old_int[i] != new_int[i]) { -+ dev_info(dev, "%s %d -> %d", msg, old_int[i], new_int[i]); -+ old_int[i] = new_int[i]; -+ } -+ } -+} -+ -+static void update_str(struct device *dev, -+ char msg[MSG_LEN], -+ char old_str[MSG_LEN], -+ char new_str[MSG_LEN]) -+{ -+ if (strcmp(old_str, new_str) != 0) { -+ dev_info(dev, "%s %s -> %s", msg, old_str, new_str); -+ strscpy(old_str, new_str, strlen(new_str)+1); -+ } -+} -+ -+static void update_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_sd, -+ struct ipu_isys_subdev_info **old_sd) -+{ -+ struct sensor_platform_data *old_pdata = -+ (*old_sd)->i2c.board_info.platform_data; -+ -+ struct sensor_platform_data *new_pdata = -+ new_sd->i2c.board_info.platform_data; -+ -+ /* csi2 */ -+ update_int(dev, "CSI2 port", &(*old_sd)->csi2->port, new_sd->csi2->port); -+ update_int(dev, "CSI2 nlanes", &(*old_sd)->csi2->nlanes, new_sd->csi2->nlanes); -+ -+ /* i2c */ -+ update_short(dev, "I2C board_info addr", &(*old_sd)->i2c.board_info.addr, -+ new_sd->i2c.board_info.addr); -+ update_str(dev, "I2C i2c_adapter_bdf", (*old_sd)->i2c.i2c_adapter_bdf, -+ new_sd->i2c.i2c_adapter_bdf); -+ -+ /* platform data */ -+ update_int(dev, "pdata port", &(old_pdata)->port, new_pdata->port); -+ update_int(dev, "pdata lanes", &(old_pdata)->lanes, new_pdata->lanes); -+ update_hex(dev, "pdata I2C slave addr", &(old_pdata)->i2c_slave_address, -+ new_pdata->i2c_slave_address); -+ update_int(dev, "pdata irq_pin", &(old_pdata)->irq_pin, new_pdata->irq_pin); -+ update_str(dev, "pdata irq_pin_name", old_pdata->irq_pin_name, new_pdata->irq_pin_name); -+ update_int(dev, "pdata reset_pin", &(old_pdata)->reset_pin, new_pdata->reset_pin); -+ update_int(dev, "pdata detect_pin", &(old_pdata)->detect_pin, new_pdata->detect_pin); -+ update_inta(dev, "pdata gpios", old_pdata->gpios, new_pdata->gpios, IPU_SPDATA_GPIO_NUM); -+} -+ -+static void update_serdes_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_sd, -+ struct ipu_isys_subdev_info **old_sd) -+{ -+ struct serdes_platform_data *old_pdata = -+ (*old_sd)->i2c.board_info.platform_data; -+ -+ struct serdes_platform_data *new_pdata = -+ new_sd->i2c.board_info.platform_data; -+ -+ int i; -+ struct serdes_subdev_info *old_sdinfo, *new_sdinfo; -+ struct serdes_module_pdata *old_mpdata, *new_mpdata; -+ -+ /* csi2 */ -+ update_int(dev, "CSI2 port", &(*old_sd)->csi2->port, new_sd->csi2->port); -+ update_int(dev, "CSI2 nlanes", &(*old_sd)->csi2->nlanes, new_sd->csi2->nlanes); -+ -+ /* i2c */ -+ update_short(dev, "I2C board_info addr", &(*old_sd)->i2c.board_info.addr, -+ new_sd->i2c.board_info.addr); -+ update_str(dev, "I2C i2c_adapter_bdf", (*old_sd)->i2c.i2c_adapter_bdf, -+ new_sd->i2c.i2c_adapter_bdf); -+ -+ update_int(dev, "I2C Pdata reset_gpio", &old_pdata->reset_gpio, -+ new_pdata->reset_gpio); -+ update_int(dev, "I2C Pdata FPD_gpio", &old_pdata->FPD_gpio, new_pdata->FPD_gpio); -+ -+ /* platform data */ -+ for (i = 0; i < SERDES_MAX_PORT; i++) { -+ old_sdinfo = &old_pdata->subdev_info[i]; -+ old_mpdata = old_sdinfo->board_info.platform_data; -+ -+ new_sdinfo = &new_pdata->subdev_info[i]; -+ new_mpdata = new_sdinfo->board_info.platform_data; -+ -+ if (!strcmp(old_sdinfo->board_info.type, new_sdinfo->board_info.type) && -+ old_sdinfo->suffix == new_sdinfo->suffix) { -+ update_short(dev, "SdInfo port", &old_sdinfo->rx_port, -+ new_sdinfo->rx_port); -+ update_short(dev, "SdInfo ser_alias", &old_sdinfo->ser_alias, -+ new_sdinfo->ser_alias); -+ update_short(dev, "SdInfo board_info.addr", &old_sdinfo->board_info.addr, -+ new_sdinfo->board_info.addr); -+ -+ if (!strcmp(old_mpdata->module_name, new_mpdata->module_name)) { -+ update_int(dev, "mPdata lanes", &old_mpdata->lanes, -+ new_mpdata->lanes); -+ update_int(dev, "mPdata fsin", &old_mpdata->fsin, -+ new_mpdata->fsin); -+ update_inta(dev, "mPdata gpio_powerup_seq", -+ (int *)old_mpdata->gpio_powerup_seq, -+ (int *)new_mpdata->gpio_powerup_seq, -+ SERDES_MAX_GPIO_POWERUP_SEQ); -+ } -+ } -+ } -+} -+ -+static int compare_subdev(struct device *dev, -+ struct ipu_isys_subdev_info *new_subdev, -+ struct ipu_isys_subdev_info *old_subdev, -+ enum connection_type connect) -+{ -+ /* check for ACPI HID in existing pdata */ -+ if (old_subdev->acpi_hid) { -+ /* compare with HID for User Custom */ -+ if (!strcmp(old_subdev->acpi_hid, dev_name(dev))) { -+ dev_info(dev, "Found matching sensor : %s", dev_name(dev)); -+ return 0; -+ } -+ } -+ /* compare sensor type */ -+ if (!strcmp(old_subdev->i2c.board_info.type, -+ new_subdev->i2c.board_info.type)) { -+ -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *old_pdata, *new_pdata; -+ -+ old_pdata = (struct sensor_platform_data *) -+ old_subdev->i2c.board_info.platform_data; -+ -+ new_pdata = (struct sensor_platform_data *) -+ new_subdev->i2c.board_info.platform_data; -+ -+ if (old_pdata->suffix == new_pdata->suffix) { -+ dev_info(dev, "Found matching sensor : %s %c", -+ old_subdev->i2c.board_info.type, -+ old_pdata->suffix); -+ return 0; -+ } -+ } else if (connect == TYPE_SERDES) { -+ struct serdes_platform_data *old_pdata, *new_pdata; -+ -+ old_pdata = (struct serdes_platform_data *) -+ old_subdev->i2c.board_info.platform_data; -+ -+ new_pdata = (struct serdes_platform_data *) -+ new_subdev->i2c.board_info.platform_data; -+ -+ if (old_pdata->suffix == new_pdata->suffix) { -+ dev_info(dev, "Found matching sensor : %s %c", -+ old_subdev->i2c.board_info.type, -+ old_pdata->suffix); -+ return 0; -+ } -+ } -+ } -+ return -1; -+} -+ -+static void update_pdata(struct device *dev, -+ struct ipu_isys_subdev_info *new_subdev, -+ enum connection_type connect) -+{ -+ struct ipu_isys_subdev_info *acpi_subdev; -+ bool found = false; -+ -+ acpi_subdev = new_subdev; -+ -+ /* update local ipu_isys_subdev_pdata */ -+ add_local_subdevs(acpi_subdev); -+ -+ /* found existing pdata */ -+ if (ptr_built_in_pdata) { -+ struct ipu_isys_subdev_info **subdevs, *sd_info; -+ -+ for (subdevs = ptr_built_in_pdata->subdevs; *subdevs; subdevs++) { -+ sd_info = *subdevs; -+ -+ /* found similar subdev in existing pdata*/ -+ if (!compare_subdev(dev, acpi_subdev, sd_info, connect)) { -+ /* print and update old subdev */ -+ if (connect == TYPE_DIRECT) { -+ dev_dbg(dev, "Old sensor subdev\n"); -+ print_subdev(sd_info); -+ update_subdev(dev, acpi_subdev, &sd_info); -+ dev_dbg(dev, "Updated subdev\n"); -+ print_subdev(sd_info); -+ } else if (connect == TYPE_SERDES) { -+ dev_dbg(dev, "Old serdes subdev\n"); -+ print_serdes_subdev(sd_info); -+ update_serdes_subdev(dev, acpi_subdev, &sd_info); -+ dev_dbg(dev, "Updated subdev\n"); -+ print_serdes_subdev(sd_info); -+ } -+ -+ /* stop once similar subdev updated */ -+ found = true; -+ break; -+ } -+ } -+ -+ /* no similar subdev found */ -+ if (!found) { -+ if (connect == TYPE_DIRECT) { -+ struct sensor_platform_data *acpi_pdata; -+ -+ acpi_pdata = (struct sensor_platform_data *) -+ acpi_subdev->i2c.board_info.platform_data; -+ -+ dev_err(dev, "Pdata does not contain %s %c\n", -+ acpi_subdev->i2c.board_info.type, -+ acpi_pdata->suffix); -+ -+ /* print new subdev */ -+ print_subdev(acpi_subdev); -+ -+ } else { -+ struct serdes_platform_data *acpi_pdata; -+ -+ acpi_pdata = (struct serdes_platform_data *) -+ acpi_subdev->i2c.board_info.platform_data; -+ -+ dev_err(dev, "Pdata does not contain %s %c\n", -+ acpi_subdev->i2c.board_info.type, -+ acpi_pdata->suffix); -+ -+ print_serdes_subdev(acpi_subdev); -+ } -+ } -+ } -+ /* does not have existing pdata */ -+ else { -+ /* print new subdev */ -+ if (connect == TYPE_DIRECT) { -+ pr_debug("New sensor subdev\n"); -+ print_subdev(acpi_subdev); -+ } else { -+ pr_debug("New serdes subdev\n"); -+ print_serdes_subdev(acpi_subdev); -+ } -+ } -+ -+ /* update total num of sensor connected */ -+ if (connect == TYPE_SERDES) -+ serdes_info.deser_num++; -+} -+ - static void set_common_gpio(struct control_logic_data *ctl_data, - struct sensor_platform_data **pdata) - { -@@ -226,11 +548,11 @@ static void set_common_gpio(struct control_logic_data *ctl_data, - ctl_data->gpio[i].func); - } - --static int set_csi2(struct ipu7_isys_subdev_info **sensor_sd, -+static int set_csi2(struct ipu_isys_subdev_info **sensor_sd, - unsigned int lanes, unsigned int port, - unsigned int bus_type) - { -- struct ipu7_isys_csi2_config *csi2_config; -+ struct ipu_isys_csi2_config *csi2_config; - - csi2_config = kzalloc(sizeof(*csi2_config), GFP_KERNEL); - if (!csi2_config) -@@ -250,7 +572,7 @@ static int set_csi2(struct ipu7_isys_subdev_info **sensor_sd, - return 0; - } - --static void set_i2c(struct ipu7_isys_subdev_info **sensor_sd, -+static void set_i2c(struct ipu_isys_subdev_info **sensor_sd, - struct device *dev, - const char *sensor_name, - unsigned int addr, -@@ -274,7 +596,7 @@ static void set_serdes_sd_pdata(struct serdes_module_pdata **module_pdata, - - #define PORT_NR 8 - --static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, -+static int set_serdes_subdev(struct ipu_isys_subdev_info **serdes_sd, - struct device *dev, - struct serdes_platform_data **pdata, - const char *sensor_name, -@@ -304,12 +626,13 @@ static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, - - /* board info */ - strscpy(serdes_sdinfo[i].board_info.type, sensor_name, I2C_NAME_SIZE); -- serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ serdes_sdinfo[i].board_info.addr = serdes_info.sensor_map_addr + i; -+ - serdes_sdinfo[i].board_info.platform_data = module_pdata[i]; - - /* serdes_subdev_info */ - serdes_sdinfo[i].rx_port = i; -- serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; -+ serdes_sdinfo[i].ser_alias = serdes_info.ser_map_addr + i; - - serdes_sdinfo[i].phy_i2c_addr = serdes_info.phy_i2c_addr; - snprintf(serdes_sdinfo[i].suffix, sizeof(serdes_sdinfo[i].suffix), "%c-%d", -@@ -326,7 +649,7 @@ static int set_serdes_subdev(struct ipu7_isys_subdev_info **serdes_sd, - return 0; - } - --static int set_pdata(struct ipu7_isys_subdev_info **sensor_sd, -+static int set_pdata(struct ipu_isys_subdev_info **sensor_sd, - struct device *dev, - const char *sensor_name, - const char *hid_name, -@@ -363,7 +686,7 @@ static int set_pdata(struct ipu7_isys_subdev_info **sensor_sd, - pdata->i2c_slave_address = addr; - - /* gpio */ -- set_common_gpio(ctl_data, &pdata); -+ set_common_gpio(ctl_data, &pdata); - - (*sensor_sd)->i2c.board_info.platform_data = pdata; - } else if (connect == TYPE_SERDES) { -@@ -423,7 +746,7 @@ static void set_serdes_info(struct device *dev, const char *sensor_name, - } - - static int populate_sensor_pdata(struct device *dev, -- struct ipu7_isys_subdev_info **sensor_sd, -+ struct ipu_isys_subdev_info **sensor_sd, - struct sensor_bios_data *cam_data, - struct control_logic_data *ctl_data, - enum connection_type connect, -@@ -433,8 +756,6 @@ static int populate_sensor_pdata(struct device *dev, - int sensor_physical_addr, - int link_freq) - { -- struct ipu7_isys_subdev_pdata *ptr_acpi_subdev_pdata = &acpi_subdev_pdata; -- int i = 0; - int ret; - - if (connect == TYPE_DIRECT) { -@@ -453,7 +774,7 @@ static int populate_sensor_pdata(struct device *dev, - cam_data->i2c_num); - return -1; - } -- /* Others use DISCRETE Control Logic */ -+ - if (ctl_data->type != CL_DISCRETE) { - dev_err(dev, "IPU ACPI: Control Logic Type\n"); - dev_err(dev, "for %s: %d is Incorrect\n", -@@ -495,28 +816,9 @@ static int populate_sensor_pdata(struct device *dev, - if (ret) - return ret; - -- /* update local ipu7_isys_subdev_pdata */ -- while (i <= MAX_ACPI_SENSOR_NUM) { -- if (!ptr_acpi_subdev_pdata->subdevs[i]) { -- ptr_acpi_subdev_pdata->subdevs[i] = *sensor_sd; -- ptr_acpi_subdev_pdata->subdevs[i+1] = NULL; -- break; -- } -- i++; -- } -- -- /* print new subdev */ -- if (connect == TYPE_DIRECT) { -- pr_debug("New sensor subdev\n"); -- print_subdev(*sensor_sd); -- } else { -- pr_debug("New serdes subdev\n"); -- print_serdes_subdev(*sensor_sd); -- } -+ update_pdata(dev, *sensor_sd, connect); - -- /* update total num of sensor connected */ -- if (connect == TYPE_SERDES) -- serdes_info.deser_num++; -+ /* Lontium specific */ - - return 0; - } -@@ -530,12 +832,13 @@ int get_sensor_pdata(struct device *dev, - { - struct sensor_bios_data *cam_data; - struct control_logic_data *ctl_data; -- struct ipu7_isys_subdev_info *sensor_sd; -+ struct ipu_isys_subdev_info *sensor_sd; - int rval; - - cam_data = kzalloc(sizeof(*cam_data), GFP_KERNEL); - if (!cam_data) - return -ENOMEM; -+ - cam_data->dev = dev; - - ctl_data = kzalloc(sizeof(*ctl_data), GFP_KERNEL); -@@ -543,6 +846,7 @@ int get_sensor_pdata(struct device *dev, - kfree(cam_data); - return -ENOMEM; - } -+ - ctl_data->dev = dev; - - sensor_sd = kzalloc(sizeof(*sensor_sd), GFP_KERNEL); -diff --git a/drivers/media/platform/intel/ipu-acpi.c b/drivers/media/platform/intel/ipu-acpi.c -index c254c8299930..8b94dc307735 100644 ---- a/drivers/media/platform/intel/ipu-acpi.c -+++ b/drivers/media/platform/intel/ipu-acpi.c -@@ -32,15 +32,10 @@ - #include - #include - #include -+ - #include - #include -- --#if IS_ENABLED(CONFIG_VIDEO_ISX031) --#include --#endif -- --#include "ipu7.h" --#include "ipu7-isys.h" -+#include - - static LIST_HEAD(devices); - -@@ -73,7 +68,7 @@ static const struct ipu_acpi_devices supported_devices[] = { - - static int get_table_index(const char *acpi_name) - { -- unsigned int i; -+ int i; - - for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { - if (!strncmp(supported_devices[i].hid_name, acpi_name, -@@ -143,8 +138,8 @@ static int ipu_acpi_test(struct device *dev, void *priv) - - if (acpi_idx < 0) - return 0; -- else -- dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); -+ -+ dev_info(dev, "IPU6 ACPI: ACPI device %s\n", dev_name(dev)); - - const char *target_hid = supported_devices[acpi_idx].hid_name; - -@@ -161,10 +156,10 @@ static int ipu_acpi_test(struct device *dev, void *priv) - if (!adev) { - dev_dbg(dev, "No ACPI device found for %s\n", target_hid); - return 0; -- } else { -- set_primary_fwnode(dev, &adev->fwnode); -- dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); - } -+ -+ set_primary_fwnode(dev, &adev->fwnode); -+ dev_dbg(dev, "Assigned fwnode to %s\n", dev_name(dev)); - } - - if (ACPI_COMPANION(dev) != adev) { -@@ -185,14 +180,52 @@ static int ipu_acpi_test(struct device *dev, void *priv) - return 0; /* Continue iteration */ - } - -+/* Scan all i2c devices and pick ones which we can handle */ -+ - /* Try to get all IPU related devices mentioned in BIOS and all related information -- * return a new generated existing pdata -+ * If there is existing ipu_isys_subdev_pdata, update the existing pdata -+ * If not, return a new generated existing pdata - */ - --int ipu_get_acpi_devices(void **spdata) -+int ipu_get_acpi_devices(void *driver_data, -+ struct device *dev, -+ void **spdata, -+ void **built_in_pdata, -+ int (*fn) -+ (struct device *, void *, -+ void *csi2, -+ bool reprobe)) - { - int rval; -- struct ipu7_isys_subdev_pdata *ptr = NULL; -+ -+ if (!built_in_pdata) -+ dev_dbg(dev, "Built-in pdata not found"); -+ else { -+ dev_dbg(dev, "Built-in pdata found"); -+ set_built_in_pdata(*built_in_pdata); -+ } -+ -+ if ((!fn) || (!driver_data)) -+ return -ENODEV; -+ -+ rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); -+ if (rval < 0) -+ return rval; -+ -+ if (!built_in_pdata) { -+ dev_dbg(dev, "Return ACPI generated pdata"); -+ *spdata = get_acpi_subdev_pdata(); -+ } else -+ dev_dbg(dev, "Return updated built-in pdata"); -+ -+ return 0; -+} -+EXPORT_SYMBOL(ipu_get_acpi_devices); -+ -+int ipu_get_acpi_devices_new(void **spdata) -+{ -+ int rval; -+ struct ipu_isys_subdev_pdata *ptr = NULL; - - rval = acpi_bus_for_each_dev(ipu_acpi_test, NULL); - if (rval < 0) -@@ -204,10 +237,11 @@ int ipu_get_acpi_devices(void **spdata) - - return 0; - } --EXPORT_SYMBOL(ipu_get_acpi_devices); -+EXPORT_SYMBOL(ipu_get_acpi_devices_new); - - static int __init ipu_acpi_init(void) - { -+ set_built_in_pdata(NULL); - return 0; - } - -diff --git a/drivers/media/platform/intel/ipu7-fpga-pdata.c b/drivers/media/platform/intel/ipu7-fpga-pdata.c -index 9f60a38d1536..753e2f5c23ad 100644 ---- a/drivers/media/platform/intel/ipu7-fpga-pdata.c -+++ b/drivers/media/platform/intel/ipu7-fpga-pdata.c -@@ -3,6 +3,7 @@ - #include - #include - #include -+#include - - #include "ipu7.h" - #include "ipu7-isys.h" -@@ -14,6 +15,7 @@ - static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_0 = { - .nlanes = OV13B10_LANES, - .port = 0, -+ .bus_type = V4L2_MBUS_CSI2_DPHY, - }; - - static struct ipu7_isys_subdev_info ov13b10_sd_0 = { -@@ -29,6 +31,7 @@ static struct ipu7_isys_subdev_info ov13b10_sd_0 = { - static struct ipu7_isys_csi2_config ov13b10_csi2_cfg_1 = { - .nlanes = OV13B10_2LANES, - .port = 2, -+ .bus_type = V4L2_MBUS_CSI2_DPHY, - }; - - static struct ipu7_isys_subdev_info ov13b10_sd_1 = { --- -2.43.0 - diff --git a/SPECS/kernel/0016-cpuidle-Update-header-inclusion.rapl b/SPECS/kernel/0016-cpuidle-Update-header-inclusion.rapl new file mode 100644 index 000000000..f4e0a168f --- /dev/null +++ b/SPECS/kernel/0016-cpuidle-Update-header-inclusion.rapl @@ -0,0 +1,40 @@ +From 135d26f20b612def7d998378fce999adfa9df715 Mon Sep 17 00:00:00 2001 +From: Andy Shevchenko +Date: Mon, 24 Nov 2025 21:57:52 +0100 +Subject: [PATCH 16/17] cpuidle: Update header inclusion + +While cleaning up some headers, I got a build error on this file: + +drivers/cpuidle/poll_state.c:52:2: error: call to undeclared library function 'snprintf' with type 'int (char *restrict, unsigned long, const char *restrict, ...)'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration] + +Update header inclusions to follow IWYU (Include What You Use) +principle. + +Signed-off-by: Andy Shevchenko +Link: https://patch.msgid.link/20251124205752.1328701-1-andriy.shevchenko@linux.intel.com +Signed-off-by: Rafael J. Wysocki +--- + drivers/cpuidle/poll_state.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c +index 9b6d90a726019..c7524e4c522a2 100644 +--- a/drivers/cpuidle/poll_state.c ++++ b/drivers/cpuidle/poll_state.c +@@ -4,9 +4,13 @@ + */ + + #include ++#include ++#include + #include + #include + #include ++#include ++#include + + #define POLL_IDLE_RELAX_COUNT 200 + +-- +2.43.0 + diff --git a/SPECS/kernel/0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet b/SPECS/kernel/0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet deleted file mode 100644 index 6b689a7a6..000000000 --- a/SPECS/kernel/0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet +++ /dev/null @@ -1,31 +0,0 @@ -From be0eb7643ae076c4c70fdf2cb0f75fb053345394 Mon Sep 17 00:00:00 2001 -From: Muhammad Husaini Zulkifli -Date: Fri, 30 Jul 2021 10:17:52 +0800 -Subject: [PATCH 16/19] igc: Remove the CONFIG_DEBUG_MISC condition for trace - -This patch is to remove the CONFIG_DEBUG_MISC for trace_printk. -CONFIG_DEBUG_MISC was enabled in ER89 config but not enable in -latest config. - -Signed-off-by: Muhammad Husaini Zulkifli -Signed-off-by: Aravindhan Gunasekaran ---- - drivers/net/ethernet/intel/igc/igc_main.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c -index 8fdeea791b1d..ace39bc52667 100644 ---- a/drivers/net/ethernet/intel/igc/igc_main.c -+++ b/drivers/net/ethernet/intel/igc/igc_main.c -@@ -3255,7 +3255,7 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) - - switch (tx_buffer->type) { - case IGC_TX_BUFFER_TYPE_XSK: --#if defined(CONFIG_TRACING) && defined(CONFIG_DEBUG_MISC) -+#if defined(CONFIG_TRACING) - /* Only use for RTCP KPI Measurement on Q2 */ - if (tx_ring->queue_index == 2 && adapter->tstamp_config.tx_type == HWTSTAMP_TX_ON) - trace_printk("TX HW TS %lld\n", timestamp); --- -2.43.0 - diff --git a/SPECS/kernel/0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet b/SPECS/kernel/0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet new file mode 100644 index 000000000..db7550541 --- /dev/null +++ b/SPECS/kernel/0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet @@ -0,0 +1,50 @@ +From e691b36dc2dbe1ef38095372c13bd7aa4c9ff256 Mon Sep 17 00:00:00 2001 +From: "Song, Yoong Siang" +Date: Sun, 11 Apr 2021 23:19:13 +0800 +Subject: [PATCH 16/18] net: stmmac: introduce AF_XDP ZC TX HW timestamps + +This is a temporary solution as it uses trace_printk as a means to +log tx timestamps. Future implementation should use xdp_frame's +data_meta to let user applications retrieve it directly. + +Signed-off-by: Voon Weifeng +Signed-off-by: Tan, Tee Min +Signed-off-by: Song, Yoong Siang +--- + drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +index 056a91165df82..5397996dfb261 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c ++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +@@ -2582,7 +2582,10 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) + + tx_q->tx_count_frames++; + +- if (!priv->tx_coal_frames[queue]) ++ if (unlikely(priv->hwts_all)) { ++ stmmac_enable_tx_timestamp(priv, tx_desc); ++ set_ic = true; ++ } else if (!priv->tx_coal_frames[queue]) + set_ic = false; + else if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0) + set_ic = true; +@@ -2738,6 +2741,14 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue, + xsk_tx_metadata_complete(&tx_q->tx_skbuff_dma[entry].xsk_meta, + &stmmac_xsk_tx_metadata_ops, + &tx_compl); ++ } else if (unlikely(priv->hwts_all) && ++ tx_q->tx_skbuff_dma[entry].buf_type == ++ STMMAC_TXBUF_T_XSK_TX) { ++ ktime_t tx_hwtstamp = { 0 }; ++ ++ stmmac_get_tx_hwtstamp(priv, p, &tx_hwtstamp); ++ trace_printk("XDP TX HW TS %llu\n", ++ tx_hwtstamp); + } + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf b/SPECS/kernel/0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf deleted file mode 100644 index c85ba3007..000000000 --- a/SPECS/kernel/0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf +++ /dev/null @@ -1,401 +0,0 @@ -From ad152a9e55f0029be873b8571880c7e558e02c28 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 05:43:33 +0000 -Subject: [PATCH 016/100] perf/x86/intel: Process arch-PEBS records or record - fragments - -A significant difference with adaptive PEBS is that arch-PEBS record -supports fragments which means an arch-PEBS record could be split into -several independent fragments which have its own arch-PEBS header in -each fragment. - -This patch defines architectural PEBS record layout structures and add -helpers to process arch-PEBS records or fragments. Only legacy PEBS -groups like basic, GPR, XMM and LBR groups are supported in this patch, -the new added YMM/ZMM/OPMASK vector registers capturing would be -supported in subsequent patches. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 15 ++- - arch/x86/events/intel/ds.c | 180 ++++++++++++++++++++++++++++++ - arch/x86/include/asm/msr-index.h | 6 + - arch/x86/include/asm/perf_event.h | 100 +++++++++++++++++ - 4 files changed, 300 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index dcc796d00f05..a1e91cc4bfdc 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3219,6 +3219,19 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) - status &= ~GLOBAL_STATUS_PERF_METRICS_OVF_BIT; - } - -+ /* -+ * Arch PEBS sets bit 54 in the global status register -+ */ -+ if (__test_and_clear_bit(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT, -+ (unsigned long *)&status)) { -+ handled++; -+ static_call(x86_pmu_drain_pebs)(regs, &data); -+ -+ if (cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS] && -+ is_pebs_counter_event_group(cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS])) -+ status &= ~GLOBAL_STATUS_PERF_METRICS_OVF_BIT; -+ } -+ - /* - * Intel PT - */ -@@ -3273,7 +3286,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) - * The PEBS buffer has to be drained before handling the A-PMI - */ - if (is_pebs_counter_event_group(event)) -- x86_pmu.drain_pebs(regs, &data); -+ static_call(x86_pmu_drain_pebs)(regs, &data); - - last_period = event->hw.last_period; - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index f1d79b9e5f50..e5f88c48bbd6 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2270,6 +2270,114 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - format_group); - } - -+static inline bool arch_pebs_record_continued(struct arch_pebs_header *header) -+{ -+ /* Continue bit or null PEBS record indicates fragment follows. */ -+ return header->cont || !(header->format & GENMASK_ULL(63, 16)); -+} -+ -+static void setup_arch_pebs_sample_data(struct perf_event *event, -+ struct pt_regs *iregs, void *__pebs, -+ struct perf_sample_data *data, -+ struct pt_regs *regs) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ u64 sample_type = event->attr.sample_type; -+ struct arch_pebs_header *header = NULL; -+ struct arch_pebs_aux *meminfo = NULL; -+ struct arch_pebs_gprs *gprs = NULL; -+ struct x86_perf_regs *perf_regs; -+ void *next_record; -+ void *at = __pebs; -+ -+ if (at == NULL) -+ return; -+ -+ perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ perf_regs->xmm_regs = NULL; -+ -+ __setup_perf_sample_data(event, iregs, data); -+ -+ *regs = *iregs; -+ -+again: -+ header = at; -+ next_record = at + sizeof(struct arch_pebs_header); -+ if (header->basic) { -+ struct arch_pebs_basic *basic = next_record; -+ u16 retire = 0; -+ -+ next_record = basic + 1; -+ -+ if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) -+ retire = basic->valid ? basic->retire : 0; -+ __setup_pebs_basic_group(event, regs, data, sample_type, -+ basic->ip, basic->tsc, retire); -+ } -+ -+ /* -+ * The record for MEMINFO is in front of GP -+ * But PERF_SAMPLE_TRANSACTION needs gprs->ax. -+ * Save the pointer here but process later. -+ */ -+ if (header->aux) { -+ meminfo = next_record; -+ next_record = meminfo + 1; -+ } -+ -+ if (header->gpr) { -+ gprs = next_record; -+ next_record = gprs + 1; -+ -+ __setup_pebs_gpr_group(event, regs, (struct pebs_gprs *)gprs, -+ sample_type); -+ } -+ -+ if (header->aux) { -+ u64 ax = gprs ? gprs->ax : 0; -+ -+ __setup_pebs_meminfo_group(event, data, sample_type, -+ meminfo->cache_latency, -+ meminfo->instr_latency, -+ meminfo->address, meminfo->aux, -+ meminfo->tsx_tuning, ax); -+ } -+ -+ if (header->xmm) { -+ struct arch_pebs_xmm *xmm; -+ -+ next_record += sizeof(struct arch_pebs_xer_header); -+ -+ xmm = next_record; -+ perf_regs->xmm_regs = xmm->xmm; -+ next_record = xmm + 1; -+ } -+ -+ if (header->lbr) { -+ struct arch_pebs_lbr_header *lbr_header = next_record; -+ struct lbr_entry *lbr; -+ int num_lbr; -+ -+ next_record = lbr_header + 1; -+ lbr = next_record; -+ -+ num_lbr = header->lbr == ARCH_PEBS_LBR_NUM_VAR ? lbr_header->depth : -+ header->lbr * ARCH_PEBS_BASE_LBR_ENTRIES; -+ next_record += num_lbr * sizeof(struct lbr_entry); -+ -+ if (has_branch_stack(event)) { -+ intel_pmu_store_pebs_lbrs(lbr); -+ intel_pmu_lbr_save_brstack(data, cpuc, event); -+ } -+ } -+ -+ /* Parse followed fragments if there are. */ -+ if (arch_pebs_record_continued(header)) { -+ at = at + header->size; -+ goto again; -+ } -+} -+ - static inline void * - get_next_pebs_record_by_bit(void *base, void *top, int bit) - { -@@ -2750,6 +2858,77 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d - setup_pebs_adaptive_sample_data); - } - -+static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs, -+ struct perf_sample_data *data) -+{ -+ short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {}; -+ void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS]; -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ union arch_pebs_index index; -+ struct x86_perf_regs perf_regs; -+ struct pt_regs *regs = &perf_regs.regs; -+ void *base, *at, *top; -+ u64 mask; -+ -+ rdmsrq(MSR_IA32_PEBS_INDEX, index.full); -+ -+ if (unlikely(!index.split.wr)) { -+ intel_pmu_pebs_event_update_no_drain(cpuc, X86_PMC_IDX_MAX); -+ return; -+ } -+ -+ base = cpuc->ds_pebs_vaddr; -+ top = (void *)((u64)cpuc->ds_pebs_vaddr + -+ (index.split.wr << ARCH_PEBS_INDEX_WR_SHIFT)); -+ -+ mask = hybrid(cpuc->pmu, arch_pebs_cap).counters & cpuc->pebs_enabled; -+ -+ if (!iregs) -+ iregs = &dummy_iregs; -+ -+ /* Process all but the last event for each counter. */ -+ for (at = base; at < top;) { -+ struct arch_pebs_header *header; -+ struct arch_pebs_basic *basic; -+ u64 pebs_status; -+ -+ header = at; -+ -+ if (WARN_ON_ONCE(!header->size)) -+ break; -+ -+ /* 1st fragment or single record must have basic group */ -+ if (!header->basic) { -+ at += header->size; -+ continue; -+ } -+ -+ basic = at + sizeof(struct arch_pebs_header); -+ pebs_status = mask & basic->applicable_counters; -+ __intel_pmu_handle_pebs_record(iregs, regs, data, at, -+ pebs_status, counts, last, -+ setup_arch_pebs_sample_data); -+ -+ /* Skip non-last fragments */ -+ while (arch_pebs_record_continued(header)) { -+ if (!header->size) -+ break; -+ at += header->size; -+ header = at; -+ } -+ -+ /* Skip last fragment or the single record */ -+ at += header->size; -+ } -+ -+ __intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, -+ last, setup_arch_pebs_sample_data); -+ -+ index.split.wr = 0; -+ index.split.full = 0; -+ wrmsrq(MSR_IA32_PEBS_INDEX, index.full); -+} -+ - static void __init intel_arch_pebs_init(void) - { - /* -@@ -2759,6 +2938,7 @@ static void __init intel_arch_pebs_init(void) - */ - x86_pmu.arch_pebs = 1; - x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; -+ x86_pmu.drain_pebs = intel_pmu_drain_arch_pebs; - x86_pmu.pebs_capable = ~0ULL; - - x86_pmu.pebs_enable = __intel_pmu_pebs_enable; -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index f627196eb796..7c306dd0713c 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -324,6 +324,12 @@ - PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \ - PERF_CAP_PEBS_TIMING_INFO) - -+/* Arch PEBS */ -+#define MSR_IA32_PEBS_BASE 0x000003f4 -+#define MSR_IA32_PEBS_INDEX 0x000003f5 -+#define ARCH_PEBS_OFFSET_MASK 0x7fffff -+#define ARCH_PEBS_INDEX_WR_SHIFT 4 -+ - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) - #define RTIT_CTL_CYCLEACC BIT(1) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index a81084f58de4..1f62efe3fce2 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -437,6 +437,8 @@ static inline bool is_topdown_idx(int idx) - #define GLOBAL_STATUS_LBRS_FROZEN BIT_ULL(GLOBAL_STATUS_LBRS_FROZEN_BIT) - #define GLOBAL_STATUS_TRACE_TOPAPMI_BIT 55 - #define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(GLOBAL_STATUS_TRACE_TOPAPMI_BIT) -+#define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT 54 -+#define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD BIT_ULL(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT) - #define GLOBAL_STATUS_PERF_METRICS_OVF_BIT 48 - - #define GLOBAL_CTRL_EN_PERF_METRICS BIT_ULL(48) -@@ -507,6 +509,104 @@ struct pebs_cntr_header { - - #define INTEL_CNTR_METRICS 0x3 - -+/* -+ * Arch PEBS -+ */ -+union arch_pebs_index { -+ struct { -+ u64 rsvd:4, -+ wr:23, -+ rsvd2:4, -+ full:1, -+ en:1, -+ rsvd3:3, -+ thresh:23, -+ rsvd4:5; -+ } split; -+ u64 full; -+}; -+ -+struct arch_pebs_header { -+ union { -+ u64 format; -+ struct { -+ u64 size:16, /* Record size */ -+ rsvd:14, -+ mode:1, /* 64BIT_MODE */ -+ cont:1, -+ rsvd2:3, -+ cntr:5, -+ lbr:2, -+ rsvd3:7, -+ xmm:1, -+ ymmh:1, -+ rsvd4:2, -+ opmask:1, -+ zmmh:1, -+ h16zmm:1, -+ rsvd5:5, -+ gpr:1, -+ aux:1, -+ basic:1; -+ }; -+ }; -+ u64 rsvd6; -+}; -+ -+struct arch_pebs_basic { -+ u64 ip; -+ u64 applicable_counters; -+ u64 tsc; -+ u64 retire :16, /* Retire Latency */ -+ valid :1, -+ rsvd :47; -+ u64 rsvd2; -+ u64 rsvd3; -+}; -+ -+struct arch_pebs_aux { -+ u64 address; -+ u64 rsvd; -+ u64 rsvd2; -+ u64 rsvd3; -+ u64 rsvd4; -+ u64 aux; -+ u64 instr_latency :16, -+ pad2 :16, -+ cache_latency :16, -+ pad3 :16; -+ u64 tsx_tuning; -+}; -+ -+struct arch_pebs_gprs { -+ u64 flags, ip, ax, cx, dx, bx, sp, bp, si, di; -+ u64 r8, r9, r10, r11, r12, r13, r14, r15, ssp; -+ u64 rsvd; -+}; -+ -+struct arch_pebs_xer_header { -+ u64 xstate; -+ u64 rsvd; -+}; -+ -+struct arch_pebs_xmm { -+ u64 xmm[16*2]; /* two entries for each register */ -+}; -+ -+#define ARCH_PEBS_LBR_NAN 0x0 -+#define ARCH_PEBS_LBR_NUM_8 0x1 -+#define ARCH_PEBS_LBR_NUM_16 0x2 -+#define ARCH_PEBS_LBR_NUM_VAR 0x3 -+#define ARCH_PEBS_BASE_LBR_ENTRIES 8 -+struct arch_pebs_lbr_header { -+ u64 rsvd; -+ u64 ctl; -+ u64 depth; -+ u64 ler_from; -+ u64 ler_to; -+ u64 ler_info; -+}; -+ - /* - * AMD Extended Performance Monitoring and Debug cpuid feature detection - */ --- -2.43.0 - diff --git a/SPECS/kernel/0016-tools-power-turbostat-Enhance-perf-probe.turbo b/SPECS/kernel/0016-tools-power-turbostat-Enhance-perf-probe.turbo new file mode 100644 index 000000000..1a99f351a --- /dev/null +++ b/SPECS/kernel/0016-tools-power-turbostat-Enhance-perf-probe.turbo @@ -0,0 +1,115 @@ +From bdd2267861f4539de190f281ce2202f5da1865ed Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 15:15:32 -0500 +Subject: [PATCH 16/21] tools/power turbostat: Enhance perf probe + +check_perf_access() will now check both IPC and LLC perf counters +if they are enabled. If any fail, it now disables perf +and all perf counters. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 54 ++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 10 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 4411ef44294f4..ab28ac6e74b63 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2438,6 +2438,13 @@ static void bic_disable_msr_access(void) + free_sys_msr_counters(); + } + ++static void bic_disable_perf_access(void) ++{ ++ CLR_BIC(BIC_IPC, &bic_enabled); ++ CLR_BIC(BIC_LLC_RPS, &bic_enabled); ++ CLR_BIC(BIC_LLC_HIT, &bic_enabled); ++} ++ + static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) + { + assert(!no_perf); +@@ -8327,26 +8334,23 @@ void print_dev_latency(void) + close(fd); + } + +-static int has_instr_count_access(void) ++static int has_perf_instr_count_access(void) + { + int fd; +- int has_access; + + if (no_perf) + return 0; + + fd = open_perf_counter(base_cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS, -1, 0); +- has_access = fd != -1; +- + if (fd != -1) + close(fd); + +- if (!has_access) ++ if (fd == -1) + warnx("Failed to access %s. Some of the counters may not be available\n" +- "\tRun as root to enable them or use %s to disable the access explicitly", +- "instructions retired perf counter", "--no-perf"); ++ "\tRun as root to enable them or use %s to disable the access explicitly", "perf instructions retired counter", ++ "'--hide IPC' or '--no-perf'"); + +- return has_access; ++ return (fd != -1); + } + + int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai, +@@ -9080,6 +9084,28 @@ void probe_pm_features(void) + decode_misc_feature_control(); + } + ++/* perf_llc_probe ++ * ++ * return 1 on success, else 0 ++ */ ++int has_perf_llc_access(void) ++{ ++ int fd; ++ ++ if (no_perf) ++ return 0; ++ ++ fd = open_perf_counter(base_cpu, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, -1, PERF_FORMAT_GROUP); ++ if (fd != -1) ++ close(fd); ++ ++ if (fd == -1) ++ warnx("Failed to access %s. Some of the counters may not be available\n" ++ "\tRun as root to enable them or use %s to disable the access explicitly", "perf LLC counters", "'--hide LLC' or '--no-perf'"); ++ ++ return (fd != -1); ++} ++ + void perf_llc_init(void) + { + int cpu; +@@ -9535,8 +9561,16 @@ void check_msr_access(void) + + void check_perf_access(void) + { +- if (no_perf || !BIC_IS_ENABLED(BIC_IPC) || !has_instr_count_access()) +- CLR_BIC(BIC_IPC, &bic_enabled); ++ if (BIC_IS_ENABLED(BIC_IPC)) ++ if (!has_perf_instr_count_access()) ++ no_perf = 1; ++ ++ if (BIC_IS_ENABLED(BIC_LLC_RPS) || BIC_IS_ENABLED(BIC_LLC_HIT)) ++ if (!has_perf_llc_access()) ++ no_perf = 1; ++ ++ if (no_perf) ++ bic_disable_perf_access(); + } + + bool perf_has_hybrid_devices(void) +-- +2.43.0 + diff --git a/SPECS/kernel/0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu b/SPECS/kernel/0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu deleted file mode 100644 index aa8871213..000000000 --- a/SPECS/kernel/0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu +++ /dev/null @@ -1,295 +0,0 @@ -From 58cb4ba66d58380ce189e67303d6b908e11e1567 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Thu, 15 Jan 2026 15:22:42 +0800 -Subject: [PATCH 17/21] Copy ACPI header files from VTG IPU7 & IPU6 repo - -These are the header files copied over from ipu6-drivers: -- include/media/ipu-acpi-pdata.h -- include/media/ipu-acpi.h -- include/media/ipu-get-acpi.h -- include/media/serdes-pdata.h - -Linux kernel macro need to be removed in ipu-acpi.h -to meet innersource kernel standards. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - include/media/ipu-acpi-pdata.h | 38 +++--------------- - include/media/ipu-acpi.h | 73 +++++++++++++++++++++++++++++----- - include/media/ipu-get-acpi.h | 30 ++++++++++++++ - include/media/serdes-pdata.h | 37 +++++++++++++++++ - 4 files changed, 137 insertions(+), 41 deletions(-) - create mode 100644 include/media/ipu-get-acpi.h - create mode 100644 include/media/serdes-pdata.h - -diff --git a/include/media/ipu-acpi-pdata.h b/include/media/ipu-acpi-pdata.h -index e2074419dd88..4c754e98ded6 100644 ---- a/include/media/ipu-acpi-pdata.h -+++ b/include/media/ipu-acpi-pdata.h -@@ -3,12 +3,12 @@ - #include - #include - #include --#if IS_ENABLED(CONFIG_VIDEO_ISX031) --#include --#endif -+#include - - #define CL_EMPTY 0 - #define CL_DISCRETE 1 -+#define CL_LT 5 -+#define SERDES_MAX_PORT 4 - #define SERDES_MAX_GPIO_POWERUP_SEQ 4 - #define LOOP_SIZE 10 - -@@ -26,7 +26,7 @@ int get_sensor_pdata(struct device *dev, - int sensor_physical_addr, - int link_freq); - --struct ipu7_isys_subdev_pdata *get_acpi_subdev_pdata(void); -+struct ipu_isys_subdev_pdata *get_acpi_subdev_pdata(void); - - struct sensor_platform_data { - unsigned int port; -@@ -34,37 +34,11 @@ struct sensor_platform_data { - uint32_t i2c_slave_address; - int irq_pin; - unsigned int irq_pin_flags; -- char irq_pin_name[IPU7_SPDATA_IRQ_PIN_NAME_LEN]; -+ char irq_pin_name[IPU_SPDATA_IRQ_PIN_NAME_LEN]; - int reset_pin; - int detect_pin; - char suffix; -- int gpios[IPU7_SPDATA_GPIO_NUM]; --}; -- --struct serdes_platform_data { -- unsigned int subdev_num; -- struct serdes_subdev_info *subdev_info; -- unsigned int reset_gpio; -- unsigned int FPD_gpio; -- char suffix; -- unsigned int link_freq_mbps; -- enum v4l2_mbus_type bus_type; -- unsigned int deser_nlanes; -- unsigned int ser_nlanes; -- unsigned int des_port; -- char ser_name[I2C_NAME_SIZE]; -- struct i2c_board_info *deser_board_info; --}; -- --struct serdes_subdev_info { -- struct i2c_board_info board_info; -- int i2c_adapter_id; -- unsigned short rx_port; -- unsigned short phy_i2c_addr; -- unsigned short ser_alias; -- char suffix[5]; /* suffix for subdevs */ -- unsigned short ser_phys_addr; -- unsigned int sensor_dt; -+ int gpios[IPU_SPDATA_GPIO_NUM]; - }; - - struct serdes_module_pdata { -diff --git a/include/media/ipu-acpi.h b/include/media/ipu-acpi.h -index bc63240d7c24..f06b6861eb24 100644 ---- a/include/media/ipu-acpi.h -+++ b/include/media/ipu-acpi.h -@@ -16,16 +16,56 @@ - #ifndef MEDIA_INTEL_IPU_ACPI_H - #define MEDIA_INTEL_IPU_ACPI_H - --#include "ipu7-isys.h" -+#include -+ -+#include -+#include -+ -+#include -+ -+struct ipu_isys_csi2_config { -+ unsigned int nlanes; -+ unsigned int port; -+ enum v4l2_mbus_type bus_type; -+}; -+ -+struct ipu_isys_subdev_i2c_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ char i2c_adapter_bdf[32]; -+}; -+ -+struct ipu_isys_subdev_info { -+ struct ipu_isys_csi2_config *csi2; -+ struct ipu_isys_subdev_i2c_info i2c; -+#if IS_ENABLED(CONFIG_INTEL_IPU_ACPI) -+ char *acpi_hid; -+#endif -+}; -+ -+struct ipu_isys_clk_mapping { -+ struct clk_lookup clkdev_data; -+ char *platform_clock_name; -+}; -+ -+struct ipu_isys_subdev_pdata { -+ struct ipu_isys_subdev_info **subdevs; -+ struct ipu_isys_clk_mapping *clk_map; -+}; - - #define MAX_ACPI_SENSOR_NUM 4 - #define MAX_ACPI_I2C_NUM 12 - #define MAX_ACPI_GPIO_NUM 12 - - #define GPIO_RESET 0x0 -+#define GPIO_POWER_EN 0xb -+#define GPIO_READY_STAT 0x13 -+#define GPIO_HDMI_DETECT 0x14 -+ -+#define IPU_SPDATA_GPIO_NUM 4 -+#define IPU_SPDATA_IRQ_PIN_NAME_LEN 16 - --#define IPU7_SPDATA_GPIO_NUM 4 --#define IPU7_SPDATA_IRQ_PIN_NAME_LEN 16 -+void set_built_in_pdata(struct ipu_isys_subdev_pdata *pdata); - - enum connection_type { - TYPE_DIRECT, -@@ -122,6 +162,11 @@ struct ipu_gpio_info { - bool valid; - }; - -+struct ipu_irq_info { -+ int irq_pin; -+ char irq_pin_name[IPU_SPDATA_IRQ_PIN_NAME_LEN]; -+}; -+ - /* Each I2C client linked to 1 set of CTL Logic */ - struct control_logic_data { - struct device *dev; -@@ -133,9 +178,7 @@ struct control_logic_data { - bool completed; - }; - --int ipu_get_acpi_devices(void **spdata); -- --struct ipu7_isys_subdev_pdata *get_built_in_pdata(void); -+struct ipu_isys_subdev_pdata *get_built_in_pdata(void); - - int ipu_acpi_get_cam_data(struct device *dev, - struct sensor_bios_data *sensor); -@@ -146,17 +189,29 @@ int ipu_acpi_get_dep_data(struct device *dev, - int ipu_acpi_get_control_logic_data(struct device *dev, - struct control_logic_data **ctl_data); - -+struct intel_ipu6_regulator { -+ char *src_dev_name; -+ char *src_rail; -+ char *dest_rail; -+}; -+ - struct ipu_i2c_helper { - int (*fn)(struct device *dev, void *priv, -- struct ipu7_isys_csi2_config *csi2, -+ struct ipu_isys_csi2_config *csi2, - bool reprobe); - void *driver_data; - }; - -+struct ipu_i2c_new_dev { -+ struct list_head list; -+ struct i2c_board_info info; -+ unsigned short int bus; -+}; -+ - struct ipu_camera_module_data { - struct list_head list; -- struct ipu7_isys_subdev_info sd; -- struct ipu7_isys_csi2_config csi2; -+ struct ipu_isys_subdev_info sd; -+ struct ipu_isys_csi2_config csi2; - unsigned int ext_clk; - void *pdata; /* Ptr to generated platform data*/ - void *priv; /* Private for specific subdevice */ -diff --git a/include/media/ipu-get-acpi.h b/include/media/ipu-get-acpi.h -new file mode 100644 -index 000000000000..da2ed5117c53 ---- /dev/null -+++ b/include/media/ipu-get-acpi.h -@@ -0,0 +1,30 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* -+ * Copyright (c) 2016-2025 Intel Corporation. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License version -+ * 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ */ -+ -+#ifndef MEDIA_INTEL_IPU_GET_ACPI_H -+#define MEDIA_INTEL_IPU_GET_ACPI_H -+ -+int ipu_get_acpi_devices(void *driver_data, -+ struct device *dev, -+ void **spdata, -+ void **built_in_pdata, -+ int (*fn) -+ (struct device *, void *, -+ void *csi2, -+ bool reprobe)); -+ -+int ipu_get_acpi_devices_new(void **spdata); -+ -+#endif /* MEDIA_INTEL_IPU_GET_ACPI_H */ -diff --git a/include/media/serdes-pdata.h b/include/media/serdes-pdata.h -new file mode 100644 -index 000000000000..829f730140b1 ---- /dev/null -+++ b/include/media/serdes-pdata.h -@@ -0,0 +1,37 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2023-2025 Intel Corporation */ -+ -+#ifndef MEDIA_SERDES_PDATA_H -+#define MEDIA_SERDES_PDATA_H -+ -+#if IS_ENABLED(CONFIG_VIDEO_ISX031) -+#include -+#endif -+ -+struct serdes_subdev_info { -+ struct i2c_board_info board_info; -+ int i2c_adapter_id; -+ unsigned short rx_port; -+ unsigned short phy_i2c_addr; -+ unsigned short ser_alias; -+ char suffix[5]; /* suffix for subdevs */ -+ unsigned short ser_phys_addr; -+ unsigned int sensor_dt; -+}; -+ -+struct serdes_platform_data { -+ unsigned int subdev_num; -+ struct serdes_subdev_info *subdev_info; -+ unsigned int reset_gpio; -+ unsigned int FPD_gpio; -+ char suffix; -+ unsigned int link_freq_mbps; -+ enum v4l2_mbus_type bus_type; -+ unsigned int deser_nlanes; -+ unsigned int ser_nlanes; -+ unsigned int des_port; -+ char ser_name[I2C_NAME_SIZE]; -+ struct i2c_board_info *deser_board_info; -+}; -+ -+#endif --- -2.43.0 - diff --git a/SPECS/kernel/0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet b/SPECS/kernel/0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet deleted file mode 100644 index 42cc2018f..000000000 --- a/SPECS/kernel/0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet +++ /dev/null @@ -1,65 +0,0 @@ -From e6c2937b1b1e7c00a0057b02c6468ac7c901e5d7 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:47 -0700 -Subject: [PATCH 17/24] KVM: VMX: Set up interception for CET MSRs - -Enable/disable CET MSRs interception per associated feature configuration. - -Shadow Stack feature requires all CET MSRs passed through to guest to make -it supported in user and supervisor mode while IBT feature only depends on -MSR_IA32_{U,S}_CETS_CET to enable user and supervisor IBT. - -Note, this MSR design introduced an architectural limitation of SHSTK and -IBT control for guest, i.e., when SHSTK is exposed, IBT is also available -to guest from architectural perspective since IBT relies on subset of SHSTK -relevant MSRs. - -Suggested-by: Sean Christopherson -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/vmx.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 9098d4ee4f79..ca10c1b142dc 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4137,6 +4137,8 @@ static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) - - static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { -+ bool set; -+ - if (!cpu_has_vmx_msr_bitmap()) - return; - -@@ -4184,6 +4186,23 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - - vmx_recalc_pmu_msr_intercepts(vcpu); - -+ if (kvm_cpu_cap_has(X86_FEATURE_SHSTK)) { -+ set = !guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK); -+ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL0_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL1_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL2_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PL3_SSP, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_INT_SSP_TAB, MSR_TYPE_RW, set); -+ } -+ -+ if (kvm_cpu_cap_has(X86_FEATURE_SHSTK) || kvm_cpu_cap_has(X86_FEATURE_IBT)) { -+ set = !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) && -+ !guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK); -+ -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_U_CET, MSR_TYPE_RW, set); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_S_CET, MSR_TYPE_RW, set); -+ } - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be - * filtered by userspace. --- -2.43.0 - diff --git a/SPECS/kernel/0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi b/SPECS/kernel/0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi deleted file mode 100644 index 92d1408ca..000000000 --- a/SPECS/kernel/0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi +++ /dev/null @@ -1,169 +0,0 @@ -From 273e1c51bb3131051f1e896847368d164ee3cabf Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Aug 2022 09:58:24 -0700 -Subject: [PATCH 17/44] KVM: nVMX: Add support for the secondary VM exit - controls - -Enable the secondary VM exit controls to prepare for nested FRED. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Changes in v5: -* Allow writing MSR_IA32_VMX_EXIT_CTLS2 (Sean). -* Add TB from Xuelian Guo. - -Change in v3: -* Read secondary VM exit controls from vmcs_conf insteasd of the hardware - MSR MSR_IA32_VMX_EXIT_CTLS2 to avoid advertising features to L1 that KVM - itself doesn't support, e.g. because the expected entry+exit pairs aren't - supported. (Sean Christopherson) ---- - Documentation/virt/kvm/x86/nested-vmx.rst | 1 + - arch/x86/kvm/vmx/capabilities.h | 1 + - arch/x86/kvm/vmx/nested.c | 26 ++++++++++++++++++++++- - arch/x86/kvm/vmx/vmcs12.c | 1 + - arch/x86/kvm/vmx/vmcs12.h | 2 ++ - arch/x86/kvm/x86.h | 2 +- - 6 files changed, 31 insertions(+), 2 deletions(-) - -diff --git a/Documentation/virt/kvm/x86/nested-vmx.rst b/Documentation/virt/kvm/x86/nested-vmx.rst -index ac2095d41f02..e64ef231f310 100644 ---- a/Documentation/virt/kvm/x86/nested-vmx.rst -+++ b/Documentation/virt/kvm/x86/nested-vmx.rst -@@ -217,6 +217,7 @@ struct shadow_vmcs is ever changed. - u16 host_fs_selector; - u16 host_gs_selector; - u16 host_tr_selector; -+ u64 secondary_vm_exit_controls; - }; - - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 1100291d42ea..ebf4cbd2df70 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -34,6 +34,7 @@ struct nested_vmx_msrs { - u32 pinbased_ctls_high; - u32 exit_ctls_low; - u32 exit_ctls_high; -+ u64 secondary_exit_ctls; - u32 entry_ctls_low; - u32 entry_ctls_high; - u32 misc_low; -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index a6a3e0cbbe89..611ccff1dc90 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -1567,6 +1567,11 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) - return -EINVAL; - vmx->nested.msrs.vmfunc_controls = data; - return 0; -+ case MSR_IA32_VMX_EXIT_CTLS2: -+ if (data & ~vmcs_config.nested.secondary_exit_ctls) -+ return -EINVAL; -+ vmx->nested.msrs.secondary_exit_ctls = data; -+ return 0; - default: - /* - * The rest of the VMX capability MSRs do not support restore. -@@ -1606,6 +1611,9 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) - if (msr_index == MSR_IA32_VMX_EXIT_CTLS) - *pdata |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR; - break; -+ case MSR_IA32_VMX_EXIT_CTLS2: -+ *pdata = msrs->secondary_exit_ctls; -+ break; - case MSR_IA32_VMX_TRUE_ENTRY_CTLS: - case MSR_IA32_VMX_ENTRY_CTLS: - *pdata = vmx_control_msr( -@@ -2556,6 +2564,11 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 - exec_control &= ~VM_EXIT_LOAD_IA32_EFER; - vm_exit_controls_set(vmx, exec_control); - -+ if (exec_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { -+ exec_control = __secondary_vm_exit_controls_get(vmcs01); -+ secondary_vm_exit_controls_set(vmx, exec_control); -+ } -+ - /* - * Interrupt/Exception Fields - */ -@@ -7198,7 +7211,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - VM_EXIT_HOST_ADDR_SPACE_SIZE | - #endif - VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT | -- VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE; -+ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE | -+ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; - msrs->exit_ctls_high |= - VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | - VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | -@@ -7207,6 +7221,16 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - - /* We support free control of debug control saving. */ - msrs->exit_ctls_low &= ~VM_EXIT_SAVE_DEBUG_CONTROLS; -+ -+ if (msrs->exit_ctls_high & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { -+ msrs->secondary_exit_ctls = vmcs_conf->vmexit_2nd_ctrl; -+ /* -+ * As the secondary VM exit control is always loaded, do not -+ * advertise any feature in it to nVMX until its nVMX support -+ * is ready. -+ */ -+ msrs->secondary_exit_ctls &= 0; -+ } - } - - static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, -diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c -index 4233b5ca9461..3b01175f392a 100644 ---- a/arch/x86/kvm/vmx/vmcs12.c -+++ b/arch/x86/kvm/vmx/vmcs12.c -@@ -66,6 +66,7 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD64(HOST_IA32_PAT, host_ia32_pat), - FIELD64(HOST_IA32_EFER, host_ia32_efer), - FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), -+ FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), - FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), - FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), - FIELD(EXCEPTION_BITMAP, exception_bitmap), -diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h -index 4ad6b16525b9..7866fdce7a23 100644 ---- a/arch/x86/kvm/vmx/vmcs12.h -+++ b/arch/x86/kvm/vmx/vmcs12.h -@@ -191,6 +191,7 @@ struct __packed vmcs12 { - u16 host_gs_selector; - u16 host_tr_selector; - u16 guest_pml_index; -+ u64 secondary_vm_exit_controls; - }; - - /* -@@ -372,6 +373,7 @@ static inline void vmx_check_vmcs12_offsets(void) - CHECK_OFFSET(host_gs_selector, 992); - CHECK_OFFSET(host_tr_selector, 994); - CHECK_OFFSET(guest_pml_index, 996); -+ CHECK_OFFSET(secondary_vm_exit_controls, 998); - } - - extern const unsigned short vmcs12_field_offsets[]; -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 5f2279e11f87..c7f9e719c9b0 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -95,7 +95,7 @@ do { \ - * associated feature that KVM supports for nested virtualization. - */ - #define KVM_FIRST_EMULATED_VMX_MSR MSR_IA32_VMX_BASIC --#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_VMFUNC -+#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_EXIT_CTLS2 - - #define KVM_DEFAULT_PLE_GAP 128 - #define KVM_VMX_DEFAULT_PLE_WINDOW 4096 --- -2.43.0 - diff --git a/SPECS/kernel/0017-KVM-x86-Advertise-support-for-FRED.nmi b/SPECS/kernel/0017-KVM-x86-Advertise-support-for-FRED.nmi new file mode 100644 index 000000000..227c59fc1 --- /dev/null +++ b/SPECS/kernel/0017-KVM-x86-Advertise-support-for-FRED.nmi @@ -0,0 +1,37 @@ +From a4a595380145401de86d399e5e87227fe0a63a74 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Wed, 11 May 2022 17:53:27 -0700 +Subject: [PATCH 17/44] KVM: x86: Advertise support for FRED + +Advertise support for FRED to userspace after changes required to enable +FRED in a KVM guest are in place. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Don't advertise FRED/LKGS together, LKGS can be advertised as an + independent feature (Sean). +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/cpuid.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c +index 52524e0ca97f7..f4ff5ccbcf1e4 100644 +--- a/arch/x86/kvm/cpuid.c ++++ b/arch/x86/kvm/cpuid.c +@@ -1014,6 +1014,7 @@ void kvm_set_cpu_caps(void) + F(FSRS), + F(FSRC), + F(WRMSRNS), ++ X86_64_F(FRED), + X86_64_F(LKGS), + F(AMX_FP16), + F(AVX_IFMA), +-- +2.43.0 + diff --git a/SPECS/kernel/0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet b/SPECS/kernel/0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet deleted file mode 100644 index 37829ac3b..000000000 --- a/SPECS/kernel/0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet +++ /dev/null @@ -1,85 +0,0 @@ -From 8483034a48f6b9549b5db703eb01e59b7d08f327 Mon Sep 17 00:00:00 2001 -From: Vinicius Costa Gomes -Date: Wed, 19 Mar 2025 11:17:06 +0500 -Subject: [PATCH 17/19] af_packet: Fix wrong timestamps in tcpdump - -Since commit '97dc7cd ("ptp: Support late timestamp -determination")', timestamps may need to be retrieved with input from -the driver (.ndo_get_tstamp()). Support was missing from mmap'ed -AF_PACKET sockets. - -The problem was noticed using the igc driver, that since commit -'069b142 ("igc: Add support for PTP .getcyclesx64()")' has migrated -to use .ndo_get_tstamp() that timestamps were wrong on the receiving -side, when using tcpdump. - -Signed-off-by: Vinicius Costa Gomes ---- - net/packet/af_packet.c | 29 ++++++++++++++++++++--------- - 1 file changed, 20 insertions(+), 9 deletions(-) - -diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c -index a7017d7f0927..64d5b197f732 100644 ---- a/net/packet/af_packet.c -+++ b/net/packet/af_packet.c -@@ -451,15 +451,20 @@ static int __packet_get_status(const struct packet_sock *po, void *frame) - } - } - --static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, -- unsigned int flags) -+static __u32 tpacket_get_timestamp(struct net_device *dev, struct sk_buff *skb, -+ struct timespec64 *ts, unsigned int flags, -+ unsigned int sk_tsflags) - { - struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); - -- if (shhwtstamps && -- (flags & SOF_TIMESTAMPING_RAW_HARDWARE) && -- ktime_to_timespec64_cond(shhwtstamps->hwtstamp, ts)) -- return TP_STATUS_TS_RAW_HARDWARE; -+ bool cycles = sk_tsflags & SOF_TIMESTAMPING_BIND_PHC; -+ -+ if (shhwtstamps && shhwtstamps->hwtstamp && -+ (flags & SOF_TIMESTAMPING_RAW_HARDWARE)) { -+ ktime_t tstamp = netdev_get_tstamp(dev, shhwtstamps, cycles); -+ -+ return ktime_to_timespec64_cond(tstamp, ts) ? TP_STATUS_TS_RAW_HARDWARE : 0; -+ } - - if ((flags & SOF_TIMESTAMPING_SOFTWARE) && - ktime_to_timespec64_cond(skb_tstamp(skb), ts)) -@@ -471,11 +476,16 @@ static __u32 tpacket_get_timestamp(struct sk_buff *skb, struct timespec64 *ts, - static __u32 __packet_set_timestamp(struct packet_sock *po, void *frame, - struct sk_buff *skb) - { -+ struct net_device *dev = skb->dev; -+ unsigned int sk_tsflags; - union tpacket_uhdr h; - struct timespec64 ts; - __u32 ts_status; - -- if (!(ts_status = tpacket_get_timestamp(skb, &ts, READ_ONCE(po->tp_tstamp)))) -+ sk_tsflags = READ_ONCE(po->sk.sk_tsflags); -+ ts_status = tpacket_get_timestamp(dev, skb, &ts, READ_ONCE(po->tp_tstamp), sk_tsflags); -+ -+ if (!(ts_status)) - return 0; - - h.raw = frame; -@@ -2446,9 +2456,10 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, - /* Always timestamp; prefer an existing software timestamp taken - * closer to the time of capture. - */ -- ts_status = tpacket_get_timestamp(skb, &ts, -+ ts_status = tpacket_get_timestamp(dev, skb, &ts, - READ_ONCE(po->tp_tstamp) | -- SOF_TIMESTAMPING_SOFTWARE); -+ SOF_TIMESTAMPING_SOFTWARE, -+ READ_ONCE(sk->sk_tsflags)); - if (!ts_status) - ktime_get_real_ts64(&ts); - --- -2.43.0 - diff --git a/SPECS/kernel/0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl b/SPECS/kernel/0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl new file mode 100644 index 000000000..46cbb4c5f --- /dev/null +++ b/SPECS/kernel/0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl @@ -0,0 +1,81 @@ +From 741735b8bbc82f09f79cd78d0705862ec5ab0ac9 Mon Sep 17 00:00:00 2001 +From: "Rafael J. Wysocki" +Date: Tue, 25 Nov 2025 17:23:12 +0100 +Subject: [PATCH 17/17] cpuidle: Warn instead of bailing out if target + residency check fails + +It turns out that the change in commit 76934e495cdc ("cpuidle: Add +sanity check for exit latency and target residency") goes too far +because there are systems in the field on which the check introduced +by that commit does not pass. + +For this reason, change __cpuidle_driver_init() return type back to void +and make it print a warning when the check mentioned above does not +pass. + +Fixes: 76934e495cdc ("cpuidle: Add sanity check for exit latency and target residency") +Reported-by: Val Packett +Closes: https://lore.kernel.org/linux-pm/20251121010756.6687-1-val@packett.cool/ +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Christian Loehle +Link: https://patch.msgid.link/2808566.mvXUDI8C0e@rafael.j.wysocki +--- + drivers/cpuidle/driver.c | 18 ++++++++---------- + 1 file changed, 8 insertions(+), 10 deletions(-) + +diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c +index 1c295a93d5829..370664c47e659 100644 +--- a/drivers/cpuidle/driver.c ++++ b/drivers/cpuidle/driver.c +@@ -8,6 +8,8 @@ + * This code is licenced under the GPL. + */ + ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ + #include + #include + #include +@@ -152,7 +154,7 @@ static void cpuidle_setup_broadcast_timer(void *arg) + * __cpuidle_driver_init - initialize the driver's internal data + * @drv: a valid pointer to a struct cpuidle_driver + */ +-static int __cpuidle_driver_init(struct cpuidle_driver *drv) ++static void __cpuidle_driver_init(struct cpuidle_driver *drv) + { + int i; + +@@ -195,15 +197,13 @@ static int __cpuidle_driver_init(struct cpuidle_driver *drv) + s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); + + /* +- * Ensure that the exit latency of a CPU idle state does not +- * exceed its target residency which is assumed in cpuidle in +- * multiple places. ++ * Warn if the exit latency of a CPU idle state exceeds its ++ * target residency which is assumed to never happen in cpuidle ++ * in multiple places. + */ + if (s->exit_latency_ns > s->target_residency_ns) +- return -EINVAL; ++ pr_warn("Idle state %d target residency too low\n", i); + } +- +- return 0; + } + + /** +@@ -233,9 +233,7 @@ static int __cpuidle_register_driver(struct cpuidle_driver *drv) + if (cpuidle_disabled()) + return -ENODEV; + +- ret = __cpuidle_driver_init(drv); +- if (ret) +- return ret; ++ __cpuidle_driver_init(drv); + + ret = __cpuidle_set_driver(drv); + if (ret) +-- +2.43.0 + diff --git a/SPECS/kernel/0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet b/SPECS/kernel/0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet new file mode 100644 index 000000000..78034c8b2 --- /dev/null +++ b/SPECS/kernel/0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet @@ -0,0 +1,35 @@ +From 207e0a018d608eb9b472c2781aa2c6316e2778e8 Mon Sep 17 00:00:00 2001 +From: KhaiWenTan +Date: Thu, 23 Jan 2025 11:14:34 +0800 +Subject: [PATCH 17/18] net: phy: Set eee_cfg.eee_enabled according to PHY + +During the phy_probe function, the default value for eee_cfg.eee_enabled +was configured based on the PHY hardware. However, forcing +eee_cfg.eee_enabled to be true would cause the interface to be unable to +ping and set speed. + +This patch removes the implementation that forces eee_cfg.eee_enabled +to be true for EEE. This will make the eee_cfg.eee_enabled for EEE +follow the PHY hardware's default behavior. + +Signed-off-by: Choong Yong Liang +Signed-off-by: KhaiWenTan +--- + drivers/net/phy/phy_device.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c +index 7a67c900e79a5..72a140275c4eb 100644 +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -2825,7 +2825,6 @@ void phy_support_eee(struct phy_device *phydev) + { + linkmode_copy(phydev->advertising_eee, phydev->supported_eee); + phydev->eee_cfg.tx_lpi_enabled = true; +- phydev->eee_cfg.eee_enabled = true; + } + EXPORT_SYMBOL(phy_support_eee); + +-- +2.43.0 + diff --git a/SPECS/kernel/0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf b/SPECS/kernel/0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf deleted file mode 100644 index cac3a3502..000000000 --- a/SPECS/kernel/0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf +++ /dev/null @@ -1,199 +0,0 @@ -From 1dd4e6038b365981501006c931a9b6fe6fa2e115 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 14:16:58 +0000 -Subject: [PATCH 017/100] perf/x86/intel: Allocate arch-PEBS buffer and - initialize PEBS_BASE MSR - -Arch-PEBS introduces a new MSR IA32_PEBS_BASE to store the arch-PEBS -buffer physical address. This patch allocates arch-PEBS buffer and then -initialize IA32_PEBS_BASE MSR with the buffer physical address. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 2 + - arch/x86/events/intel/ds.c | 69 ++++++++++++++++++++++++++------- - arch/x86/events/perf_event.h | 7 +++- - arch/x86/include/asm/intel_ds.h | 3 +- - 4 files changed, 66 insertions(+), 15 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index a1e91cc4bfdc..d4eaaeff0614 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5592,6 +5592,7 @@ static void intel_pmu_cpu_starting(int cpu) - return; - - init_debug_store_on_cpu(cpu); -+ init_arch_pebs_buf_on_cpu(cpu); - /* - * Deal with CPUs that don't clear their LBRs on power-up, and that may - * even boot with LBRs enabled. -@@ -5689,6 +5690,7 @@ static void free_excl_cntrs(struct cpu_hw_events *cpuc) - static void intel_pmu_cpu_dying(int cpu) - { - fini_debug_store_on_cpu(cpu); -+ fini_arch_pebs_buf_on_cpu(cpu); - } - - void intel_cpuc_finish(struct cpu_hw_events *cpuc) -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index e5f88c48bbd6..3f9ee8dbe6b7 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -625,13 +625,18 @@ static int alloc_pebs_buffer(int cpu) - int max, node = cpu_to_node(cpu); - void *buffer, *insn_buff, *cea; - -- if (!x86_pmu.ds_pebs) -+ if (!intel_pmu_has_pebs()) - return 0; - -- buffer = dsalloc_pages(bsiz, GFP_KERNEL, cpu); -+ buffer = dsalloc_pages(bsiz, preemptible() ? GFP_KERNEL : GFP_ATOMIC, cpu); - if (unlikely(!buffer)) - return -ENOMEM; - -+ if (x86_pmu.arch_pebs) { -+ hwev->pebs_vaddr = buffer; -+ return 0; -+ } -+ - /* - * HSW+ already provides us the eventing ip; no need to allocate this - * buffer then. -@@ -644,7 +649,7 @@ static int alloc_pebs_buffer(int cpu) - } - per_cpu(insn_buffer, cpu) = insn_buff; - } -- hwev->ds_pebs_vaddr = buffer; -+ hwev->pebs_vaddr = buffer; - /* Update the cpu entry area mapping */ - cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer; - ds->pebs_buffer_base = (unsigned long) cea; -@@ -660,17 +665,20 @@ static void release_pebs_buffer(int cpu) - struct cpu_hw_events *hwev = per_cpu_ptr(&cpu_hw_events, cpu); - void *cea; - -- if (!x86_pmu.ds_pebs) -+ if (!intel_pmu_has_pebs()) - return; - -- kfree(per_cpu(insn_buffer, cpu)); -- per_cpu(insn_buffer, cpu) = NULL; -+ if (x86_pmu.ds_pebs) { -+ kfree(per_cpu(insn_buffer, cpu)); -+ per_cpu(insn_buffer, cpu) = NULL; - -- /* Clear the fixmap */ -- cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer; -- ds_clear_cea(cea, x86_pmu.pebs_buffer_size); -- dsfree_pages(hwev->ds_pebs_vaddr, x86_pmu.pebs_buffer_size); -- hwev->ds_pebs_vaddr = NULL; -+ /* Clear the fixmap */ -+ cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer; -+ ds_clear_cea(cea, x86_pmu.pebs_buffer_size); -+ } -+ -+ dsfree_pages(hwev->pebs_vaddr, x86_pmu.pebs_buffer_size); -+ hwev->pebs_vaddr = NULL; - } - - static int alloc_bts_buffer(int cpu) -@@ -823,6 +831,41 @@ void reserve_ds_buffers(void) - } - } - -+void init_arch_pebs_buf_on_cpu(int cpu) -+{ -+ struct cpu_hw_events *cpuc = per_cpu_ptr(&cpu_hw_events, cpu); -+ u64 arch_pebs_base; -+ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ if (alloc_pebs_buffer(cpu) < 0 || !cpuc->pebs_vaddr) { -+ WARN(1, "Fail to allocate PEBS buffer on CPU %d\n", cpu); -+ x86_pmu.pebs_active = 0; -+ return; -+ } -+ -+ /* -+ * 4KB-aligned pointer of the output buffer -+ * (__alloc_pages_node() return page aligned address) -+ * Buffer Size = 4KB * 2^SIZE -+ * contiguous physical buffer (__alloc_pages_node() with order) -+ */ -+ arch_pebs_base = virt_to_phys(cpuc->pebs_vaddr) | PEBS_BUFFER_SHIFT; -+ wrmsr_on_cpu(cpu, MSR_IA32_PEBS_BASE, (u32)arch_pebs_base, -+ (u32)(arch_pebs_base >> 32)); -+ x86_pmu.pebs_active = 1; -+} -+ -+void fini_arch_pebs_buf_on_cpu(int cpu) -+{ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ release_pebs_buffer(cpu); -+ wrmsr_on_cpu(cpu, MSR_IA32_PEBS_BASE, 0, 0); -+} -+ - /* - * BTS - */ -@@ -2877,8 +2920,8 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs, - return; - } - -- base = cpuc->ds_pebs_vaddr; -- top = (void *)((u64)cpuc->ds_pebs_vaddr + -+ base = cpuc->pebs_vaddr; -+ top = (void *)((u64)cpuc->pebs_vaddr + - (index.split.wr << ARCH_PEBS_INDEX_WR_SHIFT)); - - mask = hybrid(cpuc->pmu, arch_pebs_cap).counters & cpuc->pebs_enabled; -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index a5145e8f1ddb..82e8c20611b9 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -283,8 +283,9 @@ struct cpu_hw_events { - * Intel DebugStore bits - */ - struct debug_store *ds; -- void *ds_pebs_vaddr; - void *ds_bts_vaddr; -+ /* DS based PEBS or arch-PEBS buffer address */ -+ void *pebs_vaddr; - u64 pebs_enabled; - int n_pebs; - int n_large_pebs; -@@ -1618,6 +1619,10 @@ extern void intel_cpuc_finish(struct cpu_hw_events *cpuc); - - int intel_pmu_init(void); - -+void init_arch_pebs_buf_on_cpu(int cpu); -+ -+void fini_arch_pebs_buf_on_cpu(int cpu); -+ - void init_debug_store_on_cpu(int cpu); - - void fini_debug_store_on_cpu(int cpu); -diff --git a/arch/x86/include/asm/intel_ds.h b/arch/x86/include/asm/intel_ds.h -index 5dbeac48a5b9..023c2883f9f3 100644 ---- a/arch/x86/include/asm/intel_ds.h -+++ b/arch/x86/include/asm/intel_ds.h -@@ -4,7 +4,8 @@ - #include - - #define BTS_BUFFER_SIZE (PAGE_SIZE << 4) --#define PEBS_BUFFER_SIZE (PAGE_SIZE << 4) -+#define PEBS_BUFFER_SHIFT 4 -+#define PEBS_BUFFER_SIZE (PAGE_SIZE << PEBS_BUFFER_SHIFT) - - /* The maximal number of PEBS events: */ - #define MAX_PEBS_EVENTS_FMT4 8 --- -2.43.0 - diff --git a/SPECS/kernel/0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo b/SPECS/kernel/0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo new file mode 100644 index 000000000..2230ffa56 --- /dev/null +++ b/SPECS/kernel/0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo @@ -0,0 +1,91 @@ +From 22e27c5561fe77b109c96d7cf87004c2db14ace0 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 11:29:27 -0500 +Subject: [PATCH 17/21] tools/power turbostat: Validate APERF access for VMWARE + +VMWARE correctly enumerates lack of APERF and MPERF in CPUID, +but turbostat didn't consult that before attempting to access them. + +Since VMWARE allows access, but always returns 0, turbostat +got confusd into an infinite reset loop. + +Head this off by listening to CPUID.6.APERF_MPERF +(and rename the existing variable to make this more clear) + +Reported-by: David Arcari +Tested-by: David Arcari +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index ab28ac6e74b63..0064f9091c7f0 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -497,7 +497,7 @@ unsigned int summary_only; + unsigned int list_header_only; + unsigned int dump_only; + unsigned int force_load; +-unsigned int has_aperf; ++unsigned int cpuid_has_aperf_mperf; + unsigned int has_aperf_access; + unsigned int has_epb; + unsigned int has_turbo; +@@ -8404,7 +8404,7 @@ void linux_perf_init(void) + if (access("/proc/sys/kernel/perf_event_paranoid", F_OK)) + return; + +- if (BIC_IS_ENABLED(BIC_IPC) && has_aperf) { ++ if (BIC_IS_ENABLED(BIC_IPC) && cpuid_has_aperf_mperf) { + fd_instr_count_percpu = calloc(topo.max_cpu_num + 1, sizeof(int)); + if (fd_instr_count_percpu == NULL) + err(-1, "calloc fd_instr_count_percpu"); +@@ -8524,7 +8524,7 @@ void rapl_perf_init(void) + /* Assumes msr_counter_info is populated */ + static int has_amperf_access(void) + { +- return msr_counter_arch_infos[MSR_ARCH_INFO_APERF_INDEX].present && ++ return cpuid_has_aperf_mperf && msr_counter_arch_infos[MSR_ARCH_INFO_APERF_INDEX].present && + msr_counter_arch_infos[MSR_ARCH_INFO_MPERF_INDEX].present; + } + +@@ -8936,7 +8936,7 @@ void process_cpuid() + */ + + __cpuid(0x6, eax, ebx, ecx, edx); +- has_aperf = ecx & (1 << 0); ++ cpuid_has_aperf_mperf = ecx & (1 << 0); + do_dts = eax & (1 << 0); + if (do_dts) + BIC_PRESENT(BIC_CoreTmp); +@@ -8954,7 +8954,7 @@ void process_cpuid() + if (!quiet) + fprintf(outf, "CPUID(6): %sAPERF, %sTURBO, %sDTS, %sPTM, %sHWP, " + "%sHWPnotify, %sHWPwindow, %sHWPepp, %sHWPpkg, %sEPB\n", +- has_aperf ? "" : "No-", ++ cpuid_has_aperf_mperf ? "" : "No-", + has_turbo ? "" : "No-", + do_dts ? "" : "No-", + do_ptm ? "" : "No-", +@@ -9032,7 +9032,7 @@ void process_cpuid() + base_mhz, max_mhz, bus_mhz); + } + +- if (has_aperf) ++ if (cpuid_has_aperf_mperf) + aperf_mperf_multiplier = platform->need_perf_multiplier ? 1024 : 1; + + BIC_PRESENT(BIC_IRQ); +@@ -10231,7 +10231,7 @@ int get_and_dump_counters(void) + + void print_version() + { +- fprintf(outf, "turbostat version 2025.12.01 - Len Brown \n"); ++ fprintf(outf, "turbostat version 2025.12.02 - Len Brown \n"); + } + + #define COMMAND_LINE_SIZE 2048 +-- +2.43.0 + diff --git a/SPECS/kernel/0018-IPU7-PSYS-driver-addition.ipu b/SPECS/kernel/0018-IPU7-PSYS-driver-addition.ipu deleted file mode 100644 index 7233d6bd8..000000000 --- a/SPECS/kernel/0018-IPU7-PSYS-driver-addition.ipu +++ /dev/null @@ -1,2862 +0,0 @@ -From 4bce4e3dd4b320221103c2e9c83b07e875e2ef80 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Wed, 21 Jan 2026 10:55:42 +0800 -Subject: [PATCH 18/21] IPU7 PSYS driver addition - -Include IPU7 psys drivers into staging folder. -These drivers are from ipu7-drivers github repo for PTL PV release. - -Remove Linux version code macro to match with kernel version to merge -as recommended from checkpatch. - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/staging/media/ipu7/psys/Makefile | 18 + - drivers/staging/media/ipu7/psys/ipu-psys.c | 1551 +++++++++++++++++ - .../staging/media/ipu7/psys/ipu7-fw-psys.c | 603 +++++++ - .../staging/media/ipu7/psys/ipu7-fw-psys.h | 42 + - drivers/staging/media/ipu7/psys/ipu7-psys.c | 398 +++++ - drivers/staging/media/ipu7/psys/ipu7-psys.h | 184 ++ - 6 files changed, 2796 insertions(+) - create mode 100644 drivers/staging/media/ipu7/psys/Makefile - create mode 100644 drivers/staging/media/ipu7/psys/ipu-psys.c - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-fw-psys.c - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-fw-psys.h - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-psys.c - create mode 100644 drivers/staging/media/ipu7/psys/ipu7-psys.h - -diff --git a/drivers/staging/media/ipu7/psys/Makefile b/drivers/staging/media/ipu7/psys/Makefile -new file mode 100644 -index 000000000000..33eb383a14bc ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/Makefile -@@ -0,0 +1,18 @@ -+# SPDX-License-Identifier: GPL-2.0 -+# Copyright (c) 2017 - 2024 Intel Corporation. -+ -+is_kernel_lt_6_10 = $(shell if [ $$(printf "6.10\n$(KERNELVERSION)" | sort -V | head -n1) != "6.10" ]; then echo 1; fi) -+ifeq ($(is_kernel_lt_6_10), 1) -+ifneq ($(EXTERNAL_BUILD), 1) -+src := $(srctree)/$(src) -+endif -+endif -+ -+intel-ipu7-psys-objs += ipu-psys.o \ -+ ipu7-psys.o \ -+ ipu7-fw-psys.o -+ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-psys.o -+ -+ccflags-y += -I$(src)/ -+ccflags-y += -I$(src)/../ -diff --git a/drivers/staging/media/ipu7/psys/ipu-psys.c b/drivers/staging/media/ipu7/psys/ipu-psys.c -new file mode 100644 -index 000000000000..d6f3cea52f8a ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu-psys.c -@@ -0,0 +1,1551 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2013 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#include "ipu7.h" -+#include "ipu7-mmu.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress.h" -+#include "ipu7-cpd.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-syscom.h" -+#include "ipu7-boot.h" -+ -+#define IPU_PSYS_NUM_DEVICES 4U -+ -+static int psys_runtime_pm_resume(struct device *dev); -+static int psys_runtime_pm_suspend(struct device *dev); -+ -+#define IPU_FW_CALL_TIMEOUT_JIFFIES \ -+ msecs_to_jiffies(IPU_PSYS_CMD_TIMEOUT_MS) -+ -+static dev_t ipu7_psys_dev_t; -+static DECLARE_BITMAP(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES); -+static DEFINE_MUTEX(ipu7_psys_mutex); -+ -+static int ipu7_psys_get_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ struct vm_area_struct *vma; -+ unsigned long start, end; -+ int npages, array_size; -+ struct page **pages; -+ struct sg_table *sgt; -+ int ret = -ENOMEM; -+ int nr = 0; -+ u32 flags; -+ -+ start = (unsigned long)attach->userptr; -+ end = PAGE_ALIGN(start + attach->len); -+ npages = PHYS_PFN(end - (start & PAGE_MASK)); -+ array_size = npages * sizeof(struct page *); -+ -+ sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); -+ if (!sgt) -+ return -ENOMEM; -+ -+ WARN_ON_ONCE(attach->npages); -+ -+ pages = kvzalloc(array_size, GFP_KERNEL); -+ if (!pages) -+ goto free_sgt; -+ -+ mmap_read_lock(current->mm); -+ vma = vma_lookup(current->mm, start); -+ if (unlikely(!vma)) { -+ ret = -EFAULT; -+ goto error_up_read; -+ } -+ mmap_read_unlock(current->mm); -+ -+ flags = FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM; -+ nr = pin_user_pages_fast(start & PAGE_MASK, npages, -+ flags, pages); -+ if (nr < npages) -+ goto error; -+ -+ attach->pages = pages; -+ attach->npages = npages; -+ -+ ret = sg_alloc_table_from_pages(sgt, pages, npages, -+ start & ~PAGE_MASK, attach->len, -+ GFP_KERNEL); -+ if (ret < 0) -+ goto error; -+ -+ attach->sgt = sgt; -+ -+ return 0; -+ -+error_up_read: -+ mmap_read_unlock(current->mm); -+error: -+ if (nr) -+ unpin_user_pages(pages, nr); -+ -+ kvfree(pages); -+free_sgt: -+ kfree(sgt); -+ -+ pr_err("failed to get userpages:%d\n", ret); -+ -+ return ret; -+} -+ -+static void ipu7_psys_put_userpages(struct ipu7_dma_buf_attach *attach) -+{ -+ if (!attach || !attach->userptr || !attach->sgt) -+ return; -+ -+ unpin_user_pages(attach->pages, attach->npages); -+ -+ kvfree(attach->pages); -+ -+ sg_free_table(attach->sgt); -+ kfree(attach->sgt); -+ attach->sgt = NULL; -+} -+ -+static int ipu7_dma_buf_attach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_psys_kbuffer *kbuf = dbuf->priv; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ int ret; -+ -+ ipu7_attach = kzalloc(sizeof(*ipu7_attach), GFP_KERNEL); -+ if (!ipu7_attach) -+ return -ENOMEM; -+ -+ ipu7_attach->len = kbuf->len; -+ ipu7_attach->userptr = kbuf->userptr; -+ -+ attach->priv = ipu7_attach; -+ -+ ret = ipu7_psys_get_userpages(ipu7_attach); -+ if (ret) { -+ kfree(ipu7_attach); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_detach(struct dma_buf *dbuf, -+ struct dma_buf_attachment *attach) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ -+ ipu7_psys_put_userpages(ipu7_attach); -+ kfree(ipu7_attach); -+ attach->priv = NULL; -+} -+ -+static struct sg_table *ipu7_dma_buf_map(struct dma_buf_attachment *attach, -+ enum dma_data_direction dir) -+{ -+ struct ipu7_dma_buf_attach *ipu7_attach = attach->priv; -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ unsigned long attrs; -+ int ret; -+ -+ attrs = DMA_ATTR_SKIP_CPU_SYNC; -+ ret = dma_map_sgtable(&pdev->dev, ipu7_attach->sgt, DMA_BIDIRECTIONAL, -+ attrs); -+ if (ret) { -+ dev_err(attach->dev, "pci buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ dma_sync_sgtable_for_device(&pdev->dev, ipu7_attach->sgt, -+ DMA_BIDIRECTIONAL); -+ -+ ret = ipu7_dma_map_sgtable(adev, ipu7_attach->sgt, dir, 0); -+ if (ret) { -+ dev_err(attach->dev, "ipu7 buf map failed\n"); -+ return ERR_PTR(-EIO); -+ } -+ -+ ipu7_dma_sync_sgtable(adev, ipu7_attach->sgt); -+ -+ return ipu7_attach->sgt; -+} -+ -+static void ipu7_dma_buf_unmap(struct dma_buf_attachment *attach, -+ struct sg_table *sgt, -+ enum dma_data_direction dir) -+{ -+ struct pci_dev *pdev = to_pci_dev(attach->dev); -+ struct ipu7_device *isp = pci_get_drvdata(pdev); -+ struct ipu7_bus_device *adev = isp->psys; -+ -+ ipu7_dma_unmap_sgtable(adev, sgt, dir, DMA_ATTR_SKIP_CPU_SYNC); -+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0); -+} -+ -+static int ipu7_dma_buf_mmap(struct dma_buf *dbuf, struct vm_area_struct *vma) -+{ -+ return -ENOTTY; -+} -+ -+static void ipu7_dma_buf_release(struct dma_buf *buf) -+{ -+ struct ipu7_psys_kbuffer *kbuf = buf->priv; -+ -+ if (!kbuf) -+ return; -+ -+ if (kbuf->db_attach) -+ ipu7_psys_put_userpages(kbuf->db_attach->priv); -+ -+ kfree(kbuf); -+} -+ -+static int ipu7_dma_buf_begin_cpu_access(struct dma_buf *dma_buf, -+ enum dma_data_direction dir) -+{ -+ return -ENOTTY; -+} -+ -+static int ipu7_dma_buf_vmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (list_empty(&dmabuf->attachments)) -+ return -EINVAL; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (!ipu7_attach || !ipu7_attach->pages || !ipu7_attach->npages) -+ return -EINVAL; -+ -+ map->vaddr = vm_map_ram(ipu7_attach->pages, ipu7_attach->npages, 0); -+ map->is_iomem = false; -+ if (!map->vaddr) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+static void ipu7_dma_buf_vunmap(struct dma_buf *dmabuf, struct iosys_map *map) -+{ -+ struct dma_buf_attachment *attach; -+ struct ipu7_dma_buf_attach *ipu7_attach; -+ -+ if (WARN_ON(list_empty(&dmabuf->attachments))) -+ return; -+ -+ attach = list_last_entry(&dmabuf->attachments, -+ struct dma_buf_attachment, node); -+ ipu7_attach = attach->priv; -+ -+ if (WARN_ON(!ipu7_attach || !ipu7_attach->pages || -+ !ipu7_attach->npages)) -+ return; -+ -+ vm_unmap_ram(map->vaddr, ipu7_attach->npages); -+} -+ -+static struct dma_buf_ops ipu7_dma_buf_ops = { -+ .attach = ipu7_dma_buf_attach, -+ .detach = ipu7_dma_buf_detach, -+ .map_dma_buf = ipu7_dma_buf_map, -+ .unmap_dma_buf = ipu7_dma_buf_unmap, -+ .release = ipu7_dma_buf_release, -+ .begin_cpu_access = ipu7_dma_buf_begin_cpu_access, -+ .mmap = ipu7_dma_buf_mmap, -+ .vmap = ipu7_dma_buf_vmap, -+ .vunmap = ipu7_dma_buf_vunmap, -+}; -+ -+static int ipu7_psys_get_graph_id(struct ipu7_psys_fh *fh) -+{ -+ u8 graph_id = 0; -+ -+ for (graph_id = 0; graph_id < IPU_PSYS_NUM_STREAMS; graph_id++) { -+ if (fh->psys->graph_id[graph_id] == INVALID_STREAM_ID) -+ break; -+ } -+ -+ if (graph_id == IPU_PSYS_NUM_STREAMS) -+ return -EBUSY; -+ -+ fh->psys->graph_id[graph_id] = graph_id; -+ return graph_id; -+} -+ -+static void ipu7_psys_put_graph_id(struct ipu7_psys_fh *fh) -+{ -+ fh->psys->graph_id[fh->ip->graph_id] = INVALID_STREAM_ID; -+} -+ -+static void ipu7_psys_free_msg_task(struct ipu_psys_task_queue *tq, -+ struct ipu7_bus_device *adev) -+{ -+ if (tq->msg_task) -+ ipu7_dma_free(adev, sizeof(struct ipu7_msg_task), -+ tq->msg_task, tq->task_dma_addr, 0); -+ -+ list_del(&tq->list); -+ kfree(tq); -+} -+ -+static void ipu7_psys_stream_deinit(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct ipu_psys_task_ack *ack; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ -+ mutex_destroy(&ip->event_mutex); -+ mutex_destroy(&ip->task_mutex); -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_running_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ list_for_each_entry_safe(ack, tmp, &ip->ack_list, list) { -+ list_del(&ack->list); -+ kfree(ack); -+ } -+} -+ -+static int ipu7_psys_stream_init(struct ipu7_psys_stream *ip, -+ struct ipu7_bus_device *adev) -+{ -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu_psys_task_ack *event; -+ struct ipu_psys_task_ack *tmp; -+ struct ipu_psys_task_queue *tq; -+ struct ipu_psys_task_queue *tq_tmp; -+ u8 i; -+ -+ INIT_LIST_HEAD(&ip->event_list); -+ INIT_LIST_HEAD(&ip->ack_list); -+ -+ INIT_LIST_HEAD(&ip->tq_running_list); -+ INIT_LIST_HEAD(&ip->tq_list); -+ -+ for (i = 0; i < TASK_EVENT_QUEUE_SIZE; i++) { -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ if (!event) -+ goto event_cleanup; -+ -+ list_add(&event->list, &ip->event_list); -+ } -+ -+ for (i = 0; i < TASK_REQUEST_QUEUE_SIZE; i++) { -+ tq = kzalloc(sizeof(*tq), GFP_KERNEL); -+ if (!tq) -+ goto tq_cleanup; -+ -+ list_add(&tq->list, &ip->tq_list); -+ -+ tq->msg_task = -+ ipu7_dma_alloc(adev, sizeof(struct ipu7_msg_task), -+ &tq->task_dma_addr, GFP_KERNEL, 0); -+ -+ if (!tq->msg_task) { -+ dev_err(dev, "Failed to allocate msg task.\n"); -+ goto tq_cleanup; -+ } -+ } -+ -+ init_completion(&ip->graph_open); -+ init_completion(&ip->graph_close); -+ -+ return 0; -+ -+tq_cleanup: -+ list_for_each_entry_safe(tq, tq_tmp, &ip->tq_list, list) { -+ ipu7_psys_free_msg_task(tq, adev); -+ } -+ -+event_cleanup: -+ list_for_each_entry_safe(event, tmp, &ip->event_list, list) { -+ list_del(&event->list); -+ kfree(event); -+ } -+ -+ return -ENOMEM; -+} -+ -+static int ipu7_psys_open(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_psys_fh *fh; -+ struct ipu7_psys_stream *ip; -+ int ret; -+ -+ fh = kzalloc(sizeof(*fh), GFP_KERNEL); -+ if (!fh) -+ return -ENOMEM; -+ -+ ip = kzalloc(sizeof(*ip), GFP_KERNEL); -+ if (!ip) { -+ ret = -ENOMEM; -+ goto alloc_failed; -+ } -+ -+ ret = ipu7_psys_stream_init(ip, psys->adev); -+ if (ret) -+ goto stream_init_failed; -+ -+ fh->ip = ip; -+ ip->fh = fh; -+ -+ fh->psys = psys; -+ -+ file->private_data = fh; -+ -+ mutex_init(&fh->mutex); -+ INIT_LIST_HEAD(&fh->bufmap); -+ init_waitqueue_head(&fh->wait); -+ -+ mutex_init(&ip->task_mutex); -+ mutex_init(&ip->event_mutex); -+ -+ mutex_lock(&psys->mutex); -+ -+ ret = ipu7_psys_get_graph_id(fh); -+ if (ret < 0) -+ goto open_failed; -+ -+ fh->ip->graph_id = ret; -+ -+ ret = pm_runtime_get_sync(dev); -+ if (ret < 0) { -+ dev_err(dev, "Runtime PM failed (%d)\n", ret); -+ goto rpm_put; -+ } -+ -+ list_add_tail(&fh->list, &psys->fhs); -+ -+ mutex_unlock(&psys->mutex); -+ -+ return 0; -+ -+rpm_put: -+ pm_runtime_put(dev); -+ ipu7_psys_put_graph_id(fh); -+ -+open_failed: -+ ipu7_psys_stream_deinit(ip, psys->adev); -+ -+ mutex_destroy(&fh->mutex); -+ -+ mutex_unlock(&psys->mutex); -+ -+stream_init_failed: -+ kfree(ip); -+ -+alloc_failed: -+ kfree(fh); -+ -+ return ret; -+} -+ -+static inline void ipu7_psys_kbuf_unmap(struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ if (!kbuf) -+ return; -+ -+ kbuf->valid = false; -+ if (kbuf->kaddr) { -+ struct iosys_map dmap; -+ -+ iosys_map_set_vaddr(&dmap, kbuf->kaddr); -+ dma_buf_vunmap_unlocked(kbuf->dbuf, &dmap); -+ } -+ -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(fh->psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ -+ if (kbuf->sgt) -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ if (kbuf->db_attach) -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ dma_buf_put(kbuf->dbuf); -+ -+ kbuf->db_attach = NULL; -+ kbuf->dbuf = NULL; -+ kbuf->sgt = NULL; -+} -+ -+static int ipu7_psys_release(struct inode *inode, struct file *file) -+{ -+ struct ipu7_psys *psys = inode_to_ipu_psys(inode); -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct ipu7_psys_kbuffer *kbuf, *kbuf0; -+ struct dma_buf_attachment *dba; -+ -+ mutex_lock(&fh->mutex); -+ /* clean up buffers */ -+ if (!list_empty(&fh->bufmap)) { -+ list_for_each_entry_safe(kbuf, kbuf0, &fh->bufmap, list) { -+ list_del(&kbuf->list); -+ dba = kbuf->db_attach; -+ -+ /* Unmap and release buffers */ -+ if (kbuf->dbuf && dba) { -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ } else { -+ if (dba) -+ ipu7_psys_put_userpages(dba->priv); -+ kfree(kbuf); -+ } -+ } -+ } -+ mutex_unlock(&fh->mutex); -+ -+ ipu7_psys_stream_deinit(fh->ip, psys->adev); -+ -+ mutex_lock(&psys->mutex); -+ list_del(&fh->list); -+ -+ ipu7_psys_put_graph_id(fh); -+ kfree(fh->ip); -+ -+ mutex_unlock(&psys->mutex); -+ mutex_destroy(&fh->mutex); -+ kfree(fh); -+ -+ pm_runtime_put_sync(&psys->adev->auxdev.dev); -+ -+ return 0; -+} -+ -+static int ipu7_psys_getbuf(struct ipu_psys_buffer *buf, -+ struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info); -+ struct dma_buf *dbuf; -+ int ret; -+ -+ if (!buf->base.userptr) { -+ dev_err(dev, "Buffer allocation not supported\n"); -+ return -EINVAL; -+ } -+ -+ if (!PAGE_ALIGNED(buf->base.userptr)) { -+ dev_err(dev, "Not page-aligned userptr is not supported\n"); -+ return -EINVAL; -+ } -+ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) -+ return -ENOMEM; -+ -+ kbuf->len = buf->len; -+ kbuf->userptr = buf->base.userptr; -+ kbuf->flags = buf->flags; -+ -+ exp_info.ops = &ipu7_dma_buf_ops; -+ exp_info.size = kbuf->len; -+ exp_info.flags = O_RDWR; -+ exp_info.priv = kbuf; -+ -+ dbuf = dma_buf_export(&exp_info); -+ if (IS_ERR(dbuf)) { -+ kfree(kbuf); -+ return PTR_ERR(dbuf); -+ } -+ -+ ret = dma_buf_fd(dbuf, 0); -+ if (ret < 0) { -+ dma_buf_put(dbuf); -+ return ret; -+ } -+ -+ kbuf->fd = ret; -+ buf->base.fd = ret; -+ buf->flags &= ~IPU_BUFFER_FLAG_USERPTR; -+ buf->flags |= IPU_BUFFER_FLAG_DMA_HANDLE; -+ kbuf->flags = buf->flags; -+ -+ mutex_lock(&fh->mutex); -+ list_add(&kbuf->list, &fh->bufmap); -+ mutex_unlock(&fh->mutex); -+ -+ dev_dbg(dev, "IOC_GETBUF: userptr %p size %llu to fd %d", -+ buf->base.userptr, buf->len, buf->base.fd); -+ -+ return 0; -+} -+ -+static int -+ipu7_psys_putbuf(struct ipu_psys_buffer *buf, struct ipu7_psys_fh *fh) -+{ -+ return 0; -+} -+ -+static struct ipu7_psys_kbuffer * -+ipu7_psys_lookup_kbuffer(struct ipu7_psys_fh *fh, int fd) -+{ -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ list_for_each_entry(kbuf, &fh->bufmap, list) { -+ if (kbuf->fd == fd) -+ return kbuf; -+ } -+ -+ return NULL; -+} -+ -+static int ipu7_psys_unmapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ -+ if (!kbuf || fd != kbuf->fd) { -+ dev_err(dev, "invalid kbuffer\n"); -+ return -EINVAL; -+ } -+ -+ /* From now on it is not safe to use this kbuffer */ -+ ipu7_psys_kbuf_unmap(fh, kbuf); -+ -+ list_del(&kbuf->list); -+ -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+ dev_dbg(dev, "%s fd %d unmapped\n", __func__, fd); -+ -+ return 0; -+} -+ -+static int ipu7_psys_mapbuf_locked(int fd, struct ipu7_psys_fh *fh, -+ struct ipu7_psys_kbuffer *kbuf) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct device *dev = &psys->adev->isp->pdev->dev; -+ struct dma_buf *dbuf; -+ struct iosys_map dmap; -+ int ret; -+ -+ dbuf = dma_buf_get(fd); -+ if (IS_ERR(dbuf)) -+ return -EINVAL; -+ -+ if (!kbuf) { -+ /* This fd isn't generated by ipu7_psys_getbuf, it -+ * is a new fd. Create a new kbuf item for this fd, and -+ * add this kbuf to bufmap list. -+ */ -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ -+ /* fd valid and found, need remap */ -+ if (kbuf->dbuf && (kbuf->dbuf != dbuf || kbuf->len != dbuf->size)) { -+ dev_dbg(dev, "dmabuf fd %d with kbuf %p changed, need remap.\n", -+ fd, kbuf); -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ if (ret) -+ goto mapbuf_fail; -+ -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ /* changed external dmabuf */ -+ if (!kbuf) { -+ kbuf = kzalloc(sizeof(*kbuf), GFP_KERNEL); -+ if (!kbuf) { -+ ret = -ENOMEM; -+ goto mapbuf_fail; -+ } -+ list_add(&kbuf->list, &fh->bufmap); -+ } -+ } -+ -+ if (kbuf->sgt) { -+ dev_dbg(dev, "fd %d has been mapped!\n", fd); -+ dma_buf_put(dbuf); -+ goto mapbuf_end; -+ } -+ -+ kbuf->dbuf = dbuf; -+ -+ if (kbuf->len == 0) -+ kbuf->len = kbuf->dbuf->size; -+ -+ kbuf->fd = fd; -+ -+ kbuf->db_attach = dma_buf_attach(kbuf->dbuf, dev); -+ if (IS_ERR(kbuf->db_attach)) { -+ ret = PTR_ERR(kbuf->db_attach); -+ dev_err(dev, "dma buf attach failed\n"); -+ goto attach_fail; -+ } -+ -+ kbuf->sgt = dma_buf_map_attachment_unlocked(kbuf->db_attach, -+ DMA_BIDIRECTIONAL); -+ if (IS_ERR_OR_NULL(kbuf->sgt)) { -+ ret = -EINVAL; -+ kbuf->sgt = NULL; -+ dev_err(dev, "dma buf map attachment failed\n"); -+ goto kbuf_map_fail; -+ } -+ -+ if (!kbuf->userptr) { -+ ret = ipu7_dma_map_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ if (ret) { -+ dev_dbg(dev, "ipu7 buf map failed\n"); -+ goto kbuf_map_fail; -+ } -+ } -+ -+ kbuf->dma_addr = sg_dma_address(kbuf->sgt->sgl); -+ -+ /* no need vmap for imported dmabufs */ -+ if (!kbuf->userptr) -+ goto mapbuf_end; -+ -+ dmap.is_iomem = false; -+ ret = dma_buf_vmap_unlocked(kbuf->dbuf, &dmap); -+ if (ret) { -+ dev_err(dev, "dma buf vmap failed\n"); -+ goto kbuf_map_fail; -+ } -+ kbuf->kaddr = dmap.vaddr; -+ -+mapbuf_end: -+ dev_dbg(dev, "%s %s kbuf %p fd %d with len %llu mapped\n", -+ __func__, kbuf->kaddr ? "private" : "imported", kbuf, fd, -+ kbuf->len); -+ kbuf->valid = true; -+ -+ return 0; -+ -+kbuf_map_fail: -+ if (!IS_ERR_OR_NULL(kbuf->sgt)) { -+ if (!kbuf->userptr) -+ ipu7_dma_unmap_sgtable(psys->adev, kbuf->sgt, -+ DMA_BIDIRECTIONAL, 0); -+ dma_buf_unmap_attachment_unlocked(kbuf->db_attach, -+ kbuf->sgt, -+ DMA_BIDIRECTIONAL); -+ } -+ dma_buf_detach(kbuf->dbuf, kbuf->db_attach); -+ -+attach_fail: -+ list_del(&kbuf->list); -+ if (!kbuf->userptr) -+ kfree(kbuf); -+ -+mapbuf_fail: -+ dma_buf_put(dbuf); -+ -+ dev_err(dev, "%s failed for fd %d\n", __func__, fd); -+ return ret; -+} -+ -+static long ipu7_psys_mapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ long ret; -+ struct ipu7_psys_kbuffer *kbuf; -+ -+ dev_dbg(&fh->psys->adev->auxdev.dev, "IOC_MAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ ret = ipu7_psys_mapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu7_psys_unmapbuf(int fd, struct ipu7_psys_fh *fh) -+{ -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_kbuffer *kbuf; -+ long ret; -+ -+ dev_dbg(dev, "IOC_UNMAPBUF\n"); -+ -+ mutex_lock(&fh->mutex); -+ kbuf = ipu7_psys_lookup_kbuffer(fh, fd); -+ if (!kbuf) { -+ dev_err(dev, -+ "buffer with fd %d not found\n", fd); -+ mutex_unlock(&fh->mutex); -+ return -EINVAL; -+ } -+ ret = ipu7_psys_unmapbuf_locked(fd, fh, kbuf); -+ mutex_unlock(&fh->mutex); -+ -+ return ret; -+} -+ -+static long ipu_psys_graph_open(struct ipu_psys_graph_info *graph, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ if (!graph->nodes || graph->num_nodes > MAX_GRAPH_NODES) { -+ dev_err(&psys->dev, "nodes is wrong\n"); -+ return -EINVAL; -+ } -+ -+ if (copy_from_user(fh->ip->nodes, graph->nodes, -+ graph->num_nodes * sizeof(*graph->nodes))) { -+ dev_err(&psys->dev, "Failed to copy nodes\n"); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_open); -+ -+ ret = ipu7_fw_psys_graph_open(graph, psys, fh->ip); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to open graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_open, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Open graph %d timeout\n", -+ fh->ip->graph_id); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Failed to set graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ graph->graph_id = fh->ip->graph_id; -+ -+ return 0; -+} -+ -+static long ipu_psys_graph_close(int graph_id, struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN) { -+ dev_err(&psys->dev, "Wrong state %d to open graph %d\n", -+ fh->ip->graph_state, fh->ip->graph_id); -+ return -EINVAL; -+ } -+ -+ reinit_completion(&fh->ip->graph_close); -+ -+ ret = ipu7_fw_psys_graph_close(fh->ip->graph_id, fh->psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to close graph %d\n", -+ fh->ip->graph_id); -+ return ret; -+ } -+ -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSE_WAIT; -+ -+ ret = wait_for_completion_timeout(&fh->ip->graph_close, -+ IPU_FW_CALL_TIMEOUT_JIFFIES); -+ if (!ret) { -+ dev_err(&psys->dev, "Close graph %d timeout\n", -+ fh->ip->graph_id); -+ return -ETIMEDOUT; -+ } -+ -+ if (fh->ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSED) { -+ dev_err(&psys->dev, "Failed to close graph\n"); -+ fh->ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static struct ipu_psys_task_queue * -+ipu7_psys_get_task_queue(struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_request *task) -+{ -+ struct device *dev = &ip->fh->psys->dev; -+ struct ipu7_psys_kbuffer *kbuf = NULL; -+ struct ipu_psys_task_queue *tq; -+ int fd, prevfd = -1; -+ u32 i; -+ -+ if (task->term_buf_count > MAX_GRAPH_TERMINALS) { -+ dev_err(dev, "num_teminal_buffer is too large\n"); -+ return NULL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (list_empty(&ip->tq_list)) { -+ dev_err(dev, "No available take queue for stream %p\n", ip); -+ goto unlock; -+ } -+ -+ tq = list_first_entry(&ip->tq_list, struct ipu_psys_task_queue, -+ list); -+ -+ if (copy_from_user(tq->task_buffers, -+ task->task_buffers, -+ task->term_buf_count * -+ sizeof(*task->task_buffers))) { -+ dev_err(dev, "failed to copy task buffers\n"); -+ goto unlock; -+ } -+ -+ for (i = 0; i < task->term_buf_count; i++) { -+ fd = tq->task_buffers[i].term_buf.base.fd; -+ kbuf = ipu7_psys_lookup_kbuffer(ip->fh, fd); -+ if (!kbuf) { -+ dev_err(dev, "fd %d not found\n", fd); -+ goto unlock; -+ } -+ tq->ipu7_addr[i] = kbuf->dma_addr -+ + tq->task_buffers[i].term_buf.data_offset; -+ -+ if (prevfd == fd || (tq->task_buffers[i].term_buf.flags & -+ IPU_BUFFER_FLAG_NO_FLUSH)) -+ continue; -+ -+ prevfd = fd; -+ -+ if (kbuf->kaddr) -+ clflush_cache_range(kbuf->kaddr, kbuf->len); -+ } -+ -+ dev_dbg(dev, "frame %d to task queue %p\n", task->frame_id, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_running_list); -+ -+ mutex_unlock(&ip->task_mutex); -+ return tq; -+ -+unlock: -+ mutex_unlock(&ip->task_mutex); -+ return NULL; -+} -+ -+static long ipu_psys_task_request(struct ipu_psys_task_request *task, -+ struct ipu7_psys_fh *fh) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ struct ipu_psys_task_queue *tq; -+ int ret = 0; -+ -+ if (task->term_buf_count == 0 || !task->task_buffers) { -+ dev_err(&psys->dev, "task_buffer is NULL\n"); -+ return -EINVAL; -+ } -+ -+ tq = ipu7_psys_get_task_queue(fh->ip, task); -+ if (!tq) { -+ dev_err(&psys->dev, "Failed to get task queue\n"); -+ return -EINVAL; -+ } -+ -+ ret = ipu7_fw_psys_task_request(task, fh->ip, tq, psys); -+ if (ret) { -+ dev_err(&psys->dev, "Failed to request task %d\n", -+ fh->ip->graph_id); -+ mutex_lock(&fh->ip->task_mutex); -+ list_move_tail(&tq->list, &fh->ip->tq_list); -+ mutex_unlock(&fh->ip->task_mutex); -+ return ret; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_WAIT_DONE; -+ -+ return 0; -+} -+ -+static unsigned int ipu7_psys_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ struct ipu7_psys_fh *fh = file->private_data; -+ struct device *dev = &fh->psys->adev->auxdev.dev; -+ struct ipu7_psys_stream *ip = fh->ip; -+ unsigned int res = 0; -+ -+ dev_dbg(dev, "ipu psys poll\n"); -+ -+ poll_wait(file, &fh->wait, wait); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->ack_list)) -+ res = POLLIN; -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(dev, "ipu psys poll res %u\n", res); -+ -+ return res; -+} -+ -+static long ipu7_psys_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ union { -+ struct ipu_psys_graph_info graph; -+ struct ipu_psys_task_request task; -+ struct ipu_psys_buffer buf; -+ struct ipu_psys_event ev; -+ } karg; -+ struct ipu7_psys_fh *fh = file->private_data; -+ long err = 0; -+ void __user *up = (void __user *)arg; -+ bool copy = (cmd != IPU_IOC_MAPBUF && cmd != IPU_IOC_UNMAPBUF && -+ cmd != IPU_IOC_GRAPH_CLOSE); -+ -+ if (copy) { -+ if (_IOC_SIZE(cmd) > sizeof(karg)) -+ return -ENOTTY; -+ -+ if (_IOC_DIR(cmd) & _IOC_WRITE) { -+ err = copy_from_user(&karg, up, _IOC_SIZE(cmd)); -+ if (err) -+ return -EFAULT; -+ } -+ } -+ -+ switch (cmd) { -+ case IPU_IOC_MAPBUF: -+ err = ipu7_psys_mapbuf(arg, fh); -+ break; -+ case IPU_IOC_UNMAPBUF: -+ err = ipu7_psys_unmapbuf(arg, fh); -+ break; -+ case IPU_IOC_GETBUF: -+ err = ipu7_psys_getbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_PUTBUF: -+ err = ipu7_psys_putbuf(&karg.buf, fh); -+ break; -+ case IPU_IOC_GRAPH_OPEN: -+ err = ipu_psys_graph_open(&karg.graph, fh); -+ break; -+ case IPU_IOC_GRAPH_CLOSE: -+ err = ipu_psys_graph_close(arg, fh); -+ break; -+ case IPU_IOC_TASK_REQUEST: -+ err = ipu_psys_task_request(&karg.task, fh); -+ break; -+ case IPU_IOC_DQEVENT: -+ err = ipu7_ioctl_dqevent(&karg.ev, fh, file->f_flags); -+ break; -+ default: -+ err = -ENOTTY; -+ break; -+ } -+ -+ if (err) -+ return err; -+ -+ if (copy && _IOC_DIR(cmd) & _IOC_READ) -+ if (copy_to_user(up, &karg, _IOC_SIZE(cmd))) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static const struct file_operations ipu7_psys_fops = { -+ .open = ipu7_psys_open, -+ .release = ipu7_psys_release, -+ .unlocked_ioctl = ipu7_psys_ioctl, -+ .poll = ipu7_psys_poll, -+ .owner = THIS_MODULE, -+}; -+ -+static void ipu7_psys_dev_release(struct device *dev) -+{ -+} -+ -+static int psys_runtime_pm_resume(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ int ret; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ return ret; -+ -+ if (!ipu_buttress_auth_done(adev->isp)) { -+ dev_dbg(dev, "%s: not yet authenticated, skipping\n", __func__); -+ return 0; -+ } -+ -+ ipu7_psys_setup_hw(psys); -+ -+ ipu7_psys_subdomains_power(psys, 1); -+ -+ ret = ipu7_boot_start_fw(psys->adev); -+ if (ret) { -+ dev_err(&psys->dev, "failed to start psys fw. ret: %d\n", ret); -+ return ret; -+ } -+ -+ ret = ipu7_fw_psys_open(psys); -+ if (ret) { -+ dev_err(&psys->adev->auxdev.dev, "Failed to open abi.\n"); -+ return ret; -+ } -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ psys->ready = 1; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return 0; -+} -+ -+static int psys_runtime_pm_suspend(struct device *dev) -+{ -+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev); -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ unsigned long flags; -+ -+ if (!psys) -+ return 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (!psys->ready) { -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ return 0; -+ } -+ psys->ready = 0; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ ipu7_fw_psys_close(psys); -+ -+ ipu7_boot_stop_fw(psys->adev); -+ -+ ipu7_psys_subdomains_power(psys, 0); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+ return 0; -+} -+ -+/* The following PM callbacks are needed to enable runtime PM in IPU PCI -+ * device resume, otherwise, runtime PM can't work in PCI resume from -+ * S3 state. -+ */ -+static int psys_resume(struct device *dev) -+{ -+ return 0; -+} -+ -+static int psys_suspend(struct device *dev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(dev); -+ unsigned long flags; -+ int ret = 0; -+ -+ spin_lock_irqsave(&psys->ready_lock, flags); -+ if (psys->ready) -+ ret = -EBUSY; -+ spin_unlock_irqrestore(&psys->ready_lock, flags); -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops psys_pm_ops = { -+ .runtime_suspend = psys_runtime_pm_suspend, -+ .runtime_resume = psys_runtime_pm_resume, -+ .suspend = psys_suspend, -+ .resume = psys_resume, -+}; -+ -+#ifdef CONFIG_DEBUG_FS -+static int psys_fw_log_init(struct ipu7_psys *psys) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log; -+ void *log_buf; -+ -+ if (psys->fw_log) -+ return 0; -+ -+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL); -+ if (!fw_log) -+ return -ENOMEM; -+ -+ mutex_init(&fw_log->mutex); -+ -+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!log_buf) -+ return -ENOMEM; -+ -+ fw_log->head = log_buf; -+ fw_log->addr = log_buf; -+ fw_log->count = 0; -+ fw_log->size = 0; -+ -+ psys->fw_log = fw_log; -+ -+ return 0; -+} -+ -+static ssize_t fwlog_read(struct file *file, char __user *userbuf, size_t size, -+ loff_t *pos) -+{ -+ struct ipu7_psys *psys = file->private_data; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ struct device *dev = &psys->adev->auxdev.dev; -+ u32 log_size; -+ void *buf; -+ int ret = 0; -+ -+ if (!fw_log) -+ return 0; -+ -+ buf = kvzalloc(FW_LOG_BUF_SIZE, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ mutex_lock(&fw_log->mutex); -+ if (!fw_log->size) { -+ dev_warn(dev, "no available fw log\n"); -+ mutex_unlock(&fw_log->mutex); -+ goto free_and_return; -+ } -+ -+ if (fw_log->size > FW_LOG_BUF_SIZE) -+ log_size = FW_LOG_BUF_SIZE; -+ else -+ log_size = fw_log->size; -+ -+ memcpy(buf, fw_log->addr, log_size); -+ dev_dbg(dev, "copy %d bytes fw log to user\n", log_size); -+ mutex_unlock(&fw_log->mutex); -+ -+ ret = simple_read_from_buffer(userbuf, size, pos, buf, -+ log_size); -+free_and_return: -+ kvfree(buf); -+ -+ return ret; -+} -+ -+static const struct file_operations psys_fw_log_fops = { -+ .open = simple_open, -+ .owner = THIS_MODULE, -+ .read = fwlog_read, -+ .llseek = default_llseek, -+}; -+ -+static int ipu7_psys_init_debugfs(struct ipu7_psys *psys) -+{ -+ struct dentry *file; -+ struct dentry *dir; -+ -+ dir = debugfs_create_dir("psys", psys->adev->isp->ipu7_dir); -+ if (IS_ERR(dir)) -+ return -ENOMEM; -+ -+ file = debugfs_create_file("fwlog", 0400, -+ dir, psys, &psys_fw_log_fops); -+ if (IS_ERR(file)) -+ goto err; -+ -+ psys->debugfsdir = dir; -+ -+ return 0; -+err: -+ debugfs_remove_recursive(dir); -+ return -ENOMEM; -+} -+#endif -+ -+static const struct bus_type ipu7_psys_bus = { -+ .name = "intel-ipu7-psys", -+}; -+ -+static int ipu7_psys_probe(struct auxiliary_device *auxdev, -+ const struct auxiliary_device_id *auxdev_id) -+{ -+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev); -+ struct device *dev = &auxdev->dev; -+ struct ipu7_psys *psys; -+ unsigned int minor; -+ unsigned int i; -+ int ret; -+ -+ if (!adev->isp->ipu7_bus_ready_to_probe) -+ return -EPROBE_DEFER; -+ -+ ret = alloc_chrdev_region(&ipu7_psys_dev_t, 0, -+ IPU_PSYS_NUM_DEVICES, IPU_PSYS_NAME); -+ if (ret) { -+ dev_err(dev, "can't alloc psys chrdev region (%d)\n", -+ ret); -+ return ret; -+ } -+ -+ ret = pm_runtime_resume_and_get(&auxdev->dev); -+ if (ret < 0) -+ return ret; -+ -+ ret = ipu7_mmu_hw_init(adev->mmu); -+ if (ret) -+ goto out_unregister_chr_region; -+ -+ mutex_lock(&ipu7_psys_mutex); -+ -+ minor = find_next_zero_bit(ipu7_psys_devices, IPU_PSYS_NUM_DEVICES, 0); -+ if (minor == IPU_PSYS_NUM_DEVICES) { -+ dev_err(dev, "too many devices\n"); -+ goto out_unlock; -+ } -+ -+ psys = devm_kzalloc(dev, sizeof(*psys), GFP_KERNEL); -+ if (!psys) { -+ ret = -ENOMEM; -+ goto out_unlock; -+ } -+ -+ for (i = 0 ; i < IPU_PSYS_NUM_STREAMS; i++) -+ psys->graph_id[i] = INVALID_STREAM_ID; -+ -+ adev->auxdrv_data = -+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data; -+ adev->auxdrv = to_auxiliary_drv(dev->driver); -+ -+ psys->adev = adev; -+ psys->pdata = adev->pdata; -+ -+ cdev_init(&psys->cdev, &ipu7_psys_fops); -+ psys->cdev.owner = ipu7_psys_fops.owner; -+ -+ ret = cdev_add(&psys->cdev, MKDEV(MAJOR(ipu7_psys_dev_t), minor), 1); -+ if (ret) { -+ dev_err(dev, "cdev_add failed (%d)\n", ret); -+ goto out_unlock; -+ } -+ -+ set_bit(minor, ipu7_psys_devices); -+ -+ spin_lock_init(&psys->ready_lock); -+ -+ psys->ready = 0; -+ psys->timeout = IPU_PSYS_CMD_TIMEOUT_MS; -+ -+ mutex_init(&psys->mutex); -+ INIT_LIST_HEAD(&psys->fhs); -+ -+ ret = ipu7_fw_psys_init(psys); -+ if (ret) { -+ dev_err(dev, "FW init failed(%d)\n", ret); -+ goto out_mutex_destroy; -+ } -+ -+ psys->dev.bus = &ipu7_psys_bus; -+ psys->dev.parent = dev; -+ psys->dev.devt = MKDEV(MAJOR(ipu7_psys_dev_t), minor); -+ psys->dev.release = ipu7_psys_dev_release; -+ dev_set_name(&psys->dev, "ipu7-psys%d", minor); -+ ret = device_register(&psys->dev); -+ if (ret < 0) { -+ dev_err(&psys->dev, "psys device_register failed\n"); -+ goto out_fw_release; -+ } -+ -+ dev_set_drvdata(dev, psys); -+ mutex_unlock(&ipu7_psys_mutex); -+#ifdef CONFIG_DEBUG_FS -+ psys_fw_log_init(psys); -+ ipu7_psys_init_debugfs(psys); -+#endif -+ dev_info(dev, "IPU psys probe done.\n"); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ pm_runtime_put(&auxdev->dev); -+ -+ return 0; -+ -+out_fw_release: -+ ipu7_fw_psys_release(psys); -+out_mutex_destroy: -+ mutex_destroy(&psys->mutex); -+ cdev_del(&psys->cdev); -+out_unlock: -+ /* Safe to call even if the init is not called */ -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ ipu7_mmu_hw_cleanup(adev->mmu); -+ -+out_unregister_chr_region: -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ pm_runtime_put(&auxdev->dev); -+ -+ return ret; -+} -+ -+static void ipu7_psys_remove(struct auxiliary_device *auxdev) -+{ -+ struct ipu7_psys *psys = dev_get_drvdata(&auxdev->dev); -+ struct device *dev = &auxdev->dev; -+#ifdef CONFIG_DEBUG_FS -+ struct ipu7_device *isp = psys->adev->isp; -+ -+ if (isp->ipu7_dir) -+ debugfs_remove_recursive(psys->debugfsdir); -+#endif -+ -+ mutex_lock(&ipu7_psys_mutex); -+ ipu7_fw_psys_release(psys); -+ device_unregister(&psys->dev); -+ clear_bit(MINOR(psys->cdev.dev), ipu7_psys_devices); -+ cdev_del(&psys->cdev); -+ mutex_unlock(&ipu7_psys_mutex); -+ -+ mutex_destroy(&psys->mutex); -+ -+ unregister_chrdev_region(ipu7_psys_dev_t, IPU_PSYS_NUM_DEVICES); -+ -+ dev_info(dev, "removed\n"); -+} -+ -+static irqreturn_t psys_isr_threaded(struct ipu7_bus_device *adev) -+{ -+ struct ipu7_psys *psys = ipu7_bus_get_drvdata(adev); -+ struct device *dev = &psys->adev->auxdev.dev; -+ void __iomem *base = psys->pdata->base; -+ u32 status, state; -+ int r; -+ -+ mutex_lock(&psys->mutex); -+ r = pm_runtime_get_if_in_use(dev); -+ if (!r || WARN_ON_ONCE(r < 0)) { -+ mutex_unlock(&psys->mutex); -+ return IRQ_NONE; -+ } -+ -+ state = ipu7_boot_get_boot_state(adev); -+ if (IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(state)) { -+ dev_warn(&psys->dev, "error state %u\n", state); -+ } else { -+ /* Disable irq before clear irq status */ -+ status = readl(base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS); -+ writel(0, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ writel(status, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ -+ if (status & IRQ_FROM_LOCAL_FW) -+ ipu7_psys_handle_events(psys); -+ -+ writel(IRQ_FROM_LOCAL_FW, -+ base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ } -+ -+ pm_runtime_put(dev); -+ mutex_unlock(&psys->mutex); -+ -+ return IRQ_HANDLED; -+} -+ -+static const struct ipu7_auxdrv_data ipu7_psys_auxdrv_data = { -+ .isr_threaded = psys_isr_threaded, -+ .wake_isr_thread = true, -+}; -+ -+static const struct auxiliary_device_id ipu7_psys_id_table[] = { -+ { -+ .name = "intel_ipu7.psys", -+ .driver_data = (kernel_ulong_t)&ipu7_psys_auxdrv_data, -+ }, -+ { } -+}; -+ -+MODULE_DEVICE_TABLE(auxiliary, ipu7_psys_id_table); -+ -+static struct auxiliary_driver ipu7_psys_driver = { -+ .name = IPU_PSYS_NAME, -+ .probe = ipu7_psys_probe, -+ .remove = ipu7_psys_remove, -+ .id_table = ipu7_psys_id_table, -+ .driver = { -+ .pm = &psys_pm_ops, -+ }, -+}; -+ -+module_auxiliary_driver(ipu7_psys_driver); -+ -+MODULE_AUTHOR("Bingbu Cao "); -+MODULE_AUTHOR("Qingwu Zhang "); -+MODULE_AUTHOR("Tianshu Qiu "); -+ -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("Intel ipu7 processing system driver"); -+MODULE_IMPORT_NS("INTEL_IPU7"); -+MODULE_IMPORT_NS("DMA_BUF"); -diff --git a/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c -new file mode 100644 -index 000000000000..15ba548ecd83 ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.c -@@ -0,0 +1,603 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2016 - 2024 Intel Corporation -+ -+#include -+#include -+ -+#include -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+#include "abi/ipu7_fw_psys_config_abi.h" -+ -+#include "ipu7-boot.h" -+#include "ipu7-bus.h" -+#include "ipu7-dma.h" -+#include "ipu7-fw-psys.h" -+#include "ipu7-syscom.h" -+#include "ipu7-psys.h" -+ -+#define TLV_TYPE(type) ((u32)(type) & 0x3FU) -+#define TLV_SIZE(buf_size) (((buf_size) / TLV_ITEM_ALIGNMENT) & 0xFFFFU) -+ -+/* -+ * Node resource ID of INSYS, required when there is a link from INSYS to PSYS. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_IS (0xFEU) -+ -+/* -+ * Special node resource ID to identify a generic external node. -+ * Required when there is a link to/from IPU and that node. -+ */ -+#define IPU_PSYS_NODE_RSRC_ID_EXT_IP (0xFFU) -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ struct device *dev = &adev->auxdev.dev; -+ struct ipu7_syscom_context *syscom; -+ struct ipu7_psys_config *psys_config; -+ struct syscom_queue_config *queue_configs; -+ dma_addr_t psys_config_dma_addr; -+ u32 freq; -+ int i, num_queues, ret; -+ -+ /* Allocate and init syscom context. */ -+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context), -+ GFP_KERNEL); -+ if (!syscom) -+ return -ENOMEM; -+ -+ adev->syscom = syscom; -+ syscom->num_input_queues = FWPS_MSG_ABI_MAX_INPUT_QUEUES; -+ syscom->num_output_queues = FWPS_MSG_ABI_MAX_OUTPUT_QUEUES; -+ num_queues = syscom->num_input_queues + syscom->num_output_queues; -+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues), -+ GFP_KERNEL); -+ if (!queue_configs) { -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ syscom->queue_configs = queue_configs; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].max_capacity = -+ IPU_PSYS_ACK_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_ACK_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].max_capacity = -+ IPU_PSYS_LOG_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_OUT_LOG_QUEUE_ID].token_size_in_bytes = -+ IPU_PSYS_OUT_MSG_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].max_capacity = -+ IPU_PSYS_CMD_QUEUE_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_DEV_QUEUE_ID].token_size_in_bytes = -+ FWPS_MSG_HOST2FW_MAX_SIZE; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].max_capacity = 0; -+ queue_configs[FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID].token_size_in_bytes = -+ 0; -+ -+ for (i = FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID; i < num_queues; i++) { -+ queue_configs[i].max_capacity = IPU_PSYS_TASK_QUEUE_SIZE; -+ queue_configs[i].token_size_in_bytes = -+ sizeof(struct ia_gofo_msg_indirect); -+ } -+ -+ /* Allocate PSYS subsys config. */ -+ psys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_psys_config), -+ &psys_config_dma_addr, GFP_KERNEL, 0); -+ if (!psys_config) { -+ dev_err(dev, "Failed to allocate psys subsys config.\n"); -+ ipu7_fw_psys_release(psys); -+ return -ENOMEM; -+ } -+ psys->subsys_config = psys_config; -+ psys->subsys_config_dma_addr = psys_config_dma_addr; -+ memset(psys_config, 0, sizeof(struct ipu7_psys_config)); -+ ret = ipu_buttress_get_psys_freq(adev->isp, &freq); -+ if (ret) { -+ dev_err(dev, "Failed to get PSYS frequency.\n"); -+ ipu7_fw_psys_release(psys); -+ return ret; -+ } -+ -+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues, -+ freq, psys_config_dma_addr, 1U); -+ if (ret) -+ ipu7_fw_psys_release(psys); -+ return ret; -+} -+ -+void ipu7_fw_psys_release(struct ipu7_psys *psys) -+{ -+ struct ipu7_bus_device *adev = psys->adev; -+ -+ ipu7_boot_release_boot_config(adev); -+ if (psys->subsys_config) { -+ ipu7_dma_free(adev, sizeof(struct ipu7_psys_config), -+ psys->subsys_config, -+ psys->subsys_config_dma_addr, 0); -+ psys->subsys_config = NULL; -+ psys->subsys_config_dma_addr = 0; -+ } -+} -+ -+static int ipu7_fw_dev_ready(struct ipu7_psys *psys, u16 type) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ int ret; -+ -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ return ret; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ if (ack_header->header.tlv_header.tlv_type == type) -+ return 0; -+ -+ return -EAGAIN; -+} -+ -+static int ipu7_fw_dev_open(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_open *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys open\n"); -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_OPEN); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->max_graphs = IPU_PSYS_MAX_GRAPH_NUMS; -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_OPEN_SEND_RESP | -+ IPU_MSG_DEVICE_OPEN_SEND_IRQ); -+ token->enable_power_gating = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_open(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_open(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to open PSYS dev.\n"); -+ return ret; -+ } -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_OPEN_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev open done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_OPEN; -+ return 0; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev open timeout!\n"); -+ -+ return ret; -+} -+ -+static int ipu7_fw_dev_close(struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_dev_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_DEV_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->dev_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP | -+ IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+void ipu7_fw_psys_close(struct ipu7_psys *psys) -+{ -+ u32 retry = IPU_PSYS_OPEN_CLOSE_RETRY; -+ int ret; -+ -+ ret = ipu7_fw_dev_close(psys); -+ if (ret) { -+ dev_err(&psys->dev, "failed to close PSYS dev.\n"); -+ return; -+ } -+ -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSE_WAIT; -+ -+ do { -+ usleep_range(IPU_PSYS_OPEN_CLOSE_TIMEOUT_US, -+ IPU_PSYS_OPEN_CLOSE_TIMEOUT_US + 10); -+ ret = ipu7_fw_dev_ready(psys, IPU_MSG_TYPE_DEV_CLOSE_ACK); -+ if (!ret) { -+ dev_dbg(&psys->dev, "dev close done.\n"); -+ psys->dev_state = IPU_MSG_DEV_STATE_CLOSED; -+ return; -+ } -+ } while (retry--); -+ -+ if (!retry) -+ dev_err(&psys->dev, "wait dev close timeout!\n"); -+} -+ -+static void -+ipu7_fw_psys_build_node_profile(const struct node_profile *profile, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_cb_profile *cb_profile = -+ (struct ipu7_msg_cb_profile *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*cb_profile); -+ -+ memcpy(cb_profile->profile_base.teb, profile->teb, -+ sizeof(cb_profile->profile_base.teb)); -+ -+ memcpy(cb_profile->rbm, profile->rbm, sizeof(cb_profile->rbm)); -+ memcpy(cb_profile->deb, profile->deb, sizeof(cb_profile->deb)); -+ memcpy(cb_profile->reb, profile->reb, sizeof(cb_profile->reb)); -+ -+ cb_profile->profile_base.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_NODE_PROFILE_TYPE_CB); -+ cb_profile->profile_base.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+} -+ -+/* skip term, return false */ -+static bool ipu7_fw_psys_build_node_term(const struct node_ternimal *term, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_term *msg_term = (struct ipu7_msg_term *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_term); -+ -+ if (!term->term_id && !term->buf_size) -+ return false; -+ -+ memset(msg_term, 0, sizeof(*msg_term)); -+ msg_term->term_id = term->term_id; -+ /* Disable progress message on connect terminals */ -+ msg_term->event_req_bm = 0U; -+ msg_term->payload_size = term->buf_size; -+ -+ msg_term->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TERM_TYPE_BASE); -+ msg_term->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ *buf_ptr_ptr += buf_size; -+ return true; -+} -+ -+/* When skip processing node, just return false */ -+static bool ipu7_fw_psys_build_node(const struct graph_node *node, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_node *msg_node = (struct ipu7_msg_node *)*buf_ptr_ptr; -+ u16 buf_size = sizeof(*msg_node); -+ bool ret = false; -+ u8 i = 0; -+ u8 max_terms = 0; -+ -+ memset(msg_node, 0, sizeof(*msg_node)); -+ /** -+ * Pass node info to FW, do not check for external IP and ISYS -+ * As FW expects a external node -+ */ -+ if (node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_IS && -+ node->node_rsrc_id != IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] == 0U) -+ return false; -+ } -+ -+ /** -+ * Sanity check for dummy node, TEB should set to required one -+ */ -+ if (node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_IS || -+ node->node_rsrc_id == IPU_PSYS_NODE_RSRC_ID_EXT_IP) { -+ if (node->profiles[0].teb[0] != IPU_MSG_NODE_DONT_CARE_TEB_LO || -+ node->profiles[0].teb[1] != IPU_MSG_NODE_DONT_CARE_TEB_HI) -+ return false; -+ } -+ -+ msg_node->node_rsrc_id = node->node_rsrc_id; -+ msg_node->node_ctx_id = node->node_ctx_id; -+ msg_node->num_frags = 1; -+ -+ *buf_ptr_ptr += buf_size; -+ -+ msg_node->profiles_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr -+ - (uintptr_t)&msg_node->profiles_list); -+ for (i = 0; i < ARRAY_SIZE(node->profiles); i++) { -+ ipu7_fw_psys_build_node_profile(&node->profiles[i], -+ buf_ptr_ptr); -+ msg_node->profiles_list.num_elems++; -+ } -+ -+ msg_node->terms_list.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_node->terms_list); -+ max_terms = ARRAY_SIZE(node->terminals); -+ for (i = 0; i < max_terms && i < node->num_terms; i++) { -+ ret = ipu7_fw_psys_build_node_term(&node->terminals[i], -+ buf_ptr_ptr); -+ if (ret) -+ msg_node->terms_list.num_elems++; -+ } -+ -+ buf_size = (u32)(uintptr_t)*buf_ptr_ptr - (uintptr_t)msg_node; -+ msg_node->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_NODE_TYPE_BASE); -+ msg_node->tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ -+ return true; -+} -+ -+static bool ipu7_fw_psys_build_link(const struct graph_link *link, -+ void **buf_ptr_ptr) -+{ -+ struct ipu7_msg_link *msg_link = (struct ipu7_msg_link *)*buf_ptr_ptr; -+ -+ if (!link->ep_src.node_ctx_id && !link->ep_dst.node_ctx_id && -+ !link->ep_src.term_id && !link->ep_dst.term_id) -+ return false; -+ -+ msg_link->endpoints.ep_src.node_ctx_id = link->ep_src.node_ctx_id; -+ msg_link->endpoints.ep_src.term_id = link->ep_src.term_id; -+ -+ msg_link->endpoints.ep_dst.node_ctx_id = link->ep_dst.node_ctx_id; -+ msg_link->endpoints.ep_dst.term_id = link->ep_dst.term_id; -+ -+ msg_link->foreign_key = link->foreign_key; -+ msg_link->streaming_mode = link->streaming_mode; -+ msg_link->pbk_id = link->pbk_id; -+ msg_link->pbk_slot_id = link->pbk_slot_id; -+ msg_link->delayed_link = link->delayed_link; -+ -+ *buf_ptr_ptr += sizeof(*msg_link); -+ -+ msg_link->link_options.num_elems = 0; -+ msg_link->link_options.head_offset = -+ (u16)((uintptr_t)*buf_ptr_ptr - -+ (uintptr_t)&msg_link->link_options); -+ msg_link->tlv_header.tlv_type = TLV_TYPE(IPU_MSG_LINK_TYPE_GENERIC); -+ msg_link->tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg_link)); -+ -+ return true; -+} -+ -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *buf_ptr; -+ struct ipu7_msg_graph_open *graph_open; -+ u32 buf_size = 0; -+ bool ret = false; -+ u8 i = 0; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph open\n"); -+ buf_ptr = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!buf_ptr) -+ return -ENODATA; -+ -+ graph_open = (struct ipu7_msg_graph_open *)buf_ptr; -+ -+ memset(graph_open, 0, sizeof(*graph_open)); -+ graph_open->graph_id = ip->graph_id; -+ graph_open->graph_msg_map = (u8)(IPU_MSG_GRAPH_OPEN_SEND_RESP -+ | IPU_MSG_GRAPH_OPEN_SEND_IRQ); -+ -+ buf_ptr += sizeof(*graph_open); -+ graph_open->nodes.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->nodes); -+ for (i = 0; i < ARRAY_SIZE(ip->nodes); i++) { -+ ret = ipu7_fw_psys_build_node(&ip->nodes[i], &buf_ptr); -+ if (ret) -+ graph_open->nodes.num_elems++; -+ } -+ -+ graph_open->links.head_offset = (u16)((uintptr_t)buf_ptr -+ - (uintptr_t)&graph_open->links); -+ for (i = 0; i < ARRAY_SIZE(graph->links); i++) { -+ ret = ipu7_fw_psys_build_link(&graph->links[i], &buf_ptr); -+ if (ret) -+ graph_open->links.num_elems++; -+ } -+ -+ buf_size = (u32)((uintptr_t)buf_ptr - (uintptr_t)graph_open); -+ graph_open->header.tlv_header.tlv_type = -+ TLV_TYPE(IPU_MSG_TYPE_GRAPH_OPEN); -+ graph_open->header.tlv_header.tlv_len32 = TLV_SIZE(buf_size); -+ graph_open->header.user_token = 0; -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_graph_close *token; -+ -+ dev_dbg(&psys->dev, "send_token: fw psys graph close\n"); -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ token->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_GRAPH_CLOSE); -+ token->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*token)); -+ token->header.user_token = 0; -+ -+ token->graph_id = graph_id; -+ token->graph_msg_map = (u8)(IPU_MSG_DEVICE_CLOSE_SEND_RESP -+ | IPU_MSG_DEVICE_CLOSE_SEND_IRQ); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_IN_DEV_QUEUE_ID); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ struct ipu7_msg_task *msg = tq->msg_task; -+ struct ia_gofo_msg_indirect *ind; -+ u32 node_q_id = ip->q_id[task->node_ctx_id]; -+ u32 teb_hi, teb_lo; -+ u64 teb; -+ u8 i, term_id; -+ u8 num_terms; -+ -+ ind = ipu7_syscom_get_token(ctx, node_q_id); -+ if (!ind) -+ return -ENODATA; -+ -+ memset(msg, 0, sizeof(*msg)); -+ msg->graph_id = task->graph_id; -+ msg->node_ctx_id = task->node_ctx_id; -+ msg->profile_idx = 0U; /* Only one profile on HKR */ -+ msg->frame_id = task->frame_id; -+ msg->frag_id = 0U; /* No frag, set to 0 */ -+ /* -+ * Each task has a flag indicating if ack needed, it may be used to -+ * reduce interrupts if multiple CBs supported. -+ */ -+ msg->req_done_msg = 1; -+ msg->req_done_irq = 1; -+ -+ memcpy(msg->payload_reuse_bm, task->payload_reuse_bm, -+ sizeof(task->payload_reuse_bm)); -+ -+ teb_hi = ip->nodes[msg->node_ctx_id].profiles[0].teb[1]; -+ teb_lo = ip->nodes[msg->node_ctx_id].profiles[0].teb[0]; -+ teb = (teb_lo | (((u64)teb_hi) << 32)); -+ -+ num_terms = ip->nodes[msg->node_ctx_id].num_terms; -+ for (i = 0U; i < num_terms; i++) { -+ term_id = tq->task_buffers[i].term_id; -+ if ((1U << term_id) & teb) -+ msg->term_buffers[term_id] = tq->ipu7_addr[i]; -+ } -+ -+ msg->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_TASK_REQ); -+ msg->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*msg)); -+ msg->header.user_token = (uintptr_t)tq; -+ -+ ind->header.tlv_header.tlv_type = TLV_TYPE(IPU_MSG_TYPE_INDIRECT); -+ ind->header.tlv_header.tlv_len32 = TLV_SIZE(sizeof(*ind)); -+ ind->header.msg_options.num_elems = 0; -+ ind->header.msg_options.head_offset = 0; -+ ind->ref_header = msg->header.tlv_header; -+ ind->ref_msg_ptr = tq->task_dma_addr; -+ -+ ipu7_syscom_put_token(ctx, node_q_id); -+ -+ ipu_buttress_wakeup_ps_uc(psys->adev->isp); -+ -+ return 0; -+} -+ -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr) -+{ -+ struct ipu7_syscom_context *ctx = psys->adev->syscom; -+ void *token; -+ -+ token = ipu7_syscom_get_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ memcpy(buf_ptr, token, sizeof(u8) * FWPS_MSG_FW2HOST_MAX_SIZE); -+ -+ ipu7_syscom_put_token(ctx, FWPS_MSG_ABI_OUT_ACK_QUEUE_ID); -+ return 0; -+} -+ -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys) -+{ -+ void *token; -+ struct ia_gofo_msg_log *log_msg; -+ u8 msg_type, msg_len; -+ u32 count, fmt_id; -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct psys_fw_log *fw_log = psys->fw_log; -+ u32 log_size = sizeof(struct ia_gofo_msg_log_info_ts); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ if (!token) -+ return -ENODATA; -+ -+ while (token) { -+ log_msg = (struct ia_gofo_msg_log *)token; -+ -+ msg_type = log_msg->header.tlv_header.tlv_type; -+ msg_len = log_msg->header.tlv_header.tlv_len32; -+ if (msg_type != IPU_MSG_TYPE_DEV_LOG || !msg_len) -+ dev_warn(dev, "Invalid msg data from Log queue!\n"); -+ -+ count = log_msg->log_info_ts.log_info.log_counter; -+ fmt_id = log_msg->log_info_ts.log_info.fmt_id; -+ if (count > fw_log->count + 1) -+ dev_warn(dev, "log msg lost, count %u+1 != %u!\n", -+ count, fw_log->count); -+ -+ if (fmt_id == IA_GOFO_MSG_LOG_FMT_ID_INVALID) { -+ dev_err(dev, "invalid log msg fmt_id 0x%x!\n", fmt_id); -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ return -EIO; -+ } -+ -+ if (log_size + fw_log->head - fw_log->addr > -+ FW_LOG_BUF_SIZE) -+ fw_log->head = fw_log->addr; -+ -+ memcpy(fw_log->head, (void *)&log_msg->log_info_ts, -+ sizeof(struct ia_gofo_msg_log_info_ts)); -+ -+ fw_log->count = count; -+ fw_log->head += log_size; -+ fw_log->size += log_size; -+ -+ ipu7_syscom_put_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ -+ token = ipu7_syscom_get_token(psys->adev->syscom, -+ FWPS_MSG_ABI_OUT_LOG_QUEUE_ID); -+ }; -+ -+ return 0; -+} -+ -diff --git a/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h -new file mode 100644 -index 000000000000..c43f235386d1 ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-fw-psys.h -@@ -0,0 +1,42 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2016 - 2024 Intel Corporation -+ */ -+ -+#ifndef IPU7_FW_PSYS_H -+#define IPU7_FW_PSYS_H -+ -+#include "abi/ipu7_fw_common_abi.h" -+#include "abi/ipu7_fw_msg_abi.h" -+ -+#include "ipu7-syscom.h" -+ -+#include -+ -+#define IPU_PSYS_MAX_GRAPH_NUMS (8U) -+#define IPU_PSYS_NUM_STREAMS IPU_PSYS_MAX_GRAPH_NUMS -+ -+struct ipu7_msg_to_str { -+ const enum ipu7_msg_type type; -+ const char *msg; -+}; -+ -+struct ipu7_psys; -+struct ipu7_psys_stream; -+struct ipu_psys_task_queue; -+ -+int ipu7_fw_psys_init(struct ipu7_psys *psys); -+void ipu7_fw_psys_release(struct ipu7_psys *psys); -+int ipu7_fw_psys_open(struct ipu7_psys *psys); -+void ipu7_fw_psys_close(struct ipu7_psys *psys); -+int ipu7_fw_psys_graph_open(const struct ipu_psys_graph_info *graph, -+ struct ipu7_psys *psys, -+ struct ipu7_psys_stream *ip); -+int ipu7_fw_psys_graph_close(u8 graph_id, struct ipu7_psys *psys); -+int ipu7_fw_psys_task_request(const struct ipu_psys_task_request *task, -+ struct ipu7_psys_stream *ip, -+ struct ipu_psys_task_queue *tq, -+ struct ipu7_psys *psys); -+int ipu7_fw_psys_event_handle(struct ipu7_psys *psys, u8 *buf_ptr); -+int ipu7_fw_psys_get_log(struct ipu7_psys *psys); -+#endif /* IPU7_FW_PSYS_H */ -diff --git a/drivers/staging/media/ipu7/psys/ipu7-psys.c b/drivers/staging/media/ipu7/psys/ipu7-psys.c -new file mode 100644 -index 000000000000..1ce52ae75a4c ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-psys.c -@@ -0,0 +1,398 @@ -+// SPDX-License-Identifier: GPL-2.0 -+// Copyright (C) 2020 - 2024 Intel Corporation -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-bus.h" -+#include "ipu7-buttress-regs.h" -+#include "ipu7-psys.h" -+#include "ipu7-platform-regs.h" -+#include "ipu7-fw-psys.h" -+ -+#define DOMAIN_POWE_TIMEOUT_US (200 * USEC_PER_MSEC) -+ -+static const struct ipu7_msg_to_str ps_fw_msg[] = { -+ {IPU_MSG_TYPE_RESERVED, "IPU_MSG_TYPE_RESERVED"}, -+ {IPU_MSG_TYPE_INDIRECT, "IPU_MSG_TYPE_INDIRECT"}, -+ {IPU_MSG_TYPE_DEV_LOG, "IPU_MSG_TYPE_DEV_LOG"}, -+ {IPU_MSG_TYPE_GENERAL_ERR, "IPU_MSG_TYPE_GENERAL_ERR"}, -+ {IPU_MSG_TYPE_DEV_OPEN, "IPU_MSG_TYPE_DEV_OPEN"}, -+ {IPU_MSG_TYPE_DEV_OPEN_ACK, "IPU_MSG_TYPE_DEV_OPEN_ACK"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN, "IPU_MSG_TYPE_GRAPH_OPEN"}, -+ {IPU_MSG_TYPE_GRAPH_OPEN_ACK, "IPU_MSG_TYPE_GRAPH_OPEN_ACK"}, -+ {IPU_MSG_TYPE_TASK_REQ, "IPU_MSG_TYPE_TASK_REQ"}, -+ {IPU_MSG_TYPE_TASK_DONE, "IPU_MSG_TYPE_TASK_DONE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE, "IPU_MSG_TYPE_GRAPH_CLOSE"}, -+ {IPU_MSG_TYPE_GRAPH_CLOSE_ACK, "IPU_MSG_TYPE_GRAPH_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_DEV_CLOSE, "IPU_MSG_TYPE_DEV_CLOSE"}, -+ {IPU_MSG_TYPE_DEV_CLOSE_ACK, "IPU_MSG_TYPE_DEV_CLOSE_ACK"}, -+ {IPU_MSG_TYPE_N, "IPU_MSG_TYPE_N"}, -+}; -+ -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on) -+{ -+ struct device *dev = &psys->adev->auxdev.dev; -+ struct ipu7_device *isp = psys->adev->isp; -+ void __iomem *base = isp->base; -+ u32 mask; -+ u32 val; -+ u32 req; -+ int ret; -+ -+ /* power domain req */ -+ mask = is_ipu8(isp->hw_ver) ? IPU8_PSYS_DOMAIN_POWER_MASK : -+ IPU7_PSYS_DOMAIN_POWER_MASK; -+ req = on ? mask : 0; -+ val = readl(base + BUTTRESS_REG_PS_DOMAINS_STATUS); -+ -+ dev_dbg(dev, "power %s psys sub-domains. status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+ if ((val & mask) == req) { -+ dev_warn(dev, -+ "psys sub-domains power already in request state.\n"); -+ return; -+ } -+ writel(req, base + BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ); -+ ret = readl_poll_timeout(base + BUTTRESS_REG_PS_DOMAINS_STATUS, -+ val, -+ !(val & IPU_PSYS_DOMAIN_POWER_IN_PROGRESS) && -+ ((val & mask) == req), -+ 100, DOMAIN_POWE_TIMEOUT_US); -+ if (ret) -+ dev_err(dev, -+ "Psys sub-domains power %s timeout! status: 0x%x\n", -+ on ? "UP" : "DOWN", val); -+} -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys) -+{ -+ void __iomem *base = psys->pdata->base; -+ u32 val = IRQ_FROM_LOCAL_FW; -+ -+ /* Enable TO_SW IRQ from FW */ -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK); -+ writel(val, base + IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE); -+ -+ /* correct the initial printf configuration */ -+ writel(0x2, base + PS_UC_CTRL_BASE + PRINTF_AXI_CNTL); -+} -+ -+static struct ipu7_psys_stream* -+ipu7_psys_get_ip_from_fh(struct ipu7_psys *psys, u8 graph_id) -+{ -+ struct ipu7_psys_fh *fh; -+ -+ list_for_each_entry(fh, &psys->fhs, list) { -+ if (fh->ip->graph_id == graph_id) -+ return fh->ip; -+ } -+ -+ return NULL; -+} -+ -+static void ipu7_psys_handle_graph_open_ack(struct ipu7_psys *psys, -+ const void *buffer) -+{ -+ const struct ipu7_msg_graph_open_ack *ack_msg = -+ (const struct ipu7_msg_graph_open_ack *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ struct ipu7_psys_stream *ip; -+ struct device *dev = &psys->dev; -+ const struct ia_gofo_tlv_header *msg_tlv_item; -+ u16 num_items; -+ u16 head_offset; -+ u32 i; -+ -+ dev_dbg(dev, "[ACK]%s: graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_OPEN_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto open_graph_exit; -+ } -+ -+ num_items = ack_header->header.msg_options.num_elems; -+ if (!num_items) { -+ dev_err(dev, "%s, num_items is 0\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ head_offset = ack_header->header.msg_options.head_offset; -+ msg_tlv_item = (const struct ia_gofo_tlv_header *) -+ ((u8 *)&ack_header->header.msg_options + head_offset); -+ -+ if (!msg_tlv_item) { -+ dev_err(dev, "%s: failed to get tlv item\n", __func__); -+ goto open_graph_exit; -+ } -+ -+ for (i = 0U; i < num_items; i++) { -+ u32 option_type = msg_tlv_item->tlv_type; -+ u32 option_bytes = msg_tlv_item->tlv_len32 * -+ TLV_ITEM_ALIGNMENT; -+ struct ipu7_msg_graph_open_ack_task_q_info *msg_option = -+ (void *)msg_tlv_item; -+ -+ switch (option_type) { -+ case IPU_MSG_GRAPH_ACK_TASK_Q_INFO: -+ /* -+ * Should do check that: -+ * - Each managed node has a queue ID -+ * - Queue ID's are sane -+ */ -+ dev_dbg(dev, "[ACK]set node_ctx_id %d q_id %d\n", -+ msg_option->node_ctx_id, msg_option->q_id); -+ ip->q_id[msg_option->node_ctx_id] = msg_option->q_id; -+ break; -+ default: -+ /* -+ * Only one option supported -+ */ -+ dev_err(dev, "not supported %u\n", option_type); -+ break; -+ } -+ -+ msg_tlv_item = (struct ia_gofo_tlv_header *) -+ (((u8 *)msg_tlv_item) + option_bytes); -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_OPEN; -+ -+open_graph_exit: -+ complete(&ip->graph_open); -+} -+ -+static int ipu7_psys_handle_task_done(struct ipu7_psys *psys, -+ void *buffer, u32 error) -+{ -+ const struct ipu7_msg_task_done *ack_msg = -+ (const struct ipu7_msg_task_done *)buffer; -+ const struct ia_gofo_msg_header_ack *ack_header = &ack_msg->header; -+ const struct ia_gofo_msg_header *msg_header = &ack_header->header; -+ struct ipu_psys_task_queue *tq; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ struct ipu_psys_task_ack *event; -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return -EINVAL; -+ } -+ -+ tq = (void *)(uintptr_t)msg_header->user_token; -+ if (!tq) { -+ dev_err(dev, "%s: task_token is NULL\n", __func__); -+ return -EINVAL; -+ } -+ -+ mutex_lock(&ip->task_mutex); -+ if (tq->task_state != IPU_MSG_TASK_STATE_WAIT_DONE) { -+ dev_err(dev, "%s: graph %d node %d error %d\n", __func__, -+ ack_msg->graph_id, ack_msg->node_ctx_id, -+ tq->task_state); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ return -ENOENT; -+ } -+ -+ tq->task_state = IPU_MSG_TASK_STATE_DONE; -+ dev_dbg(dev, "%s: task_token(%p)\n", __func__, tq); -+ -+ list_move_tail(&tq->list, &ip->tq_list); -+ mutex_unlock(&ip->task_mutex); -+ -+ mutex_lock(&ip->event_mutex); -+ if (!list_empty(&ip->event_list)) { -+ event = list_first_entry(&ip->event_list, -+ struct ipu_psys_task_ack, list); -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_move_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_dbg(dev, "event queue is full, add new one\n"); -+ event = kzalloc(sizeof(*event), GFP_KERNEL); -+ -+ if (event) { -+ event->graph_id = ack_msg->graph_id; -+ event->node_ctx_id = ack_msg->node_ctx_id; -+ event->frame_id = ack_msg->frame_id; -+ event->err_code = error; -+ -+ list_add_tail(&event->list, &ip->ack_list); -+ } else { -+ dev_err(dev, "failed to alloc event buf\n"); -+ } -+ } -+ mutex_unlock(&ip->event_mutex); -+ -+ wake_up_interruptible(&ip->fh->wait); -+ -+ return 0; -+} -+ -+static void ipu7_psys_handle_graph_close_ack(struct ipu7_psys *psys, -+ void *buffer) -+{ -+ struct ipu7_msg_graph_close_ack *ack_msg = -+ (struct ipu7_msg_graph_close_ack *)buffer; -+ struct device *dev = &psys->dev; -+ struct ipu7_psys_stream *ip; -+ -+ dev_dbg(dev, "[ACK]%s:graph_id: %d\n", __func__, ack_msg->graph_id); -+ -+ if (ack_msg->graph_id > (u8)IPU_PSYS_MAX_GRAPH_NUMS) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ ip = ipu7_psys_get_ip_from_fh(psys, ack_msg->graph_id); -+ if (!ip) { -+ dev_err(dev, "%s: graph id %d\n", __func__, ack_msg->graph_id); -+ return; -+ } -+ -+ if (ip->graph_state != IPU_MSG_GRAPH_STATE_CLOSE_WAIT) { -+ dev_err(dev, "%s state %d\n", __func__, ip->graph_state); -+ goto graph_close_exit; -+ } -+ -+ ip->graph_state = IPU_MSG_GRAPH_STATE_CLOSED; -+ -+graph_close_exit: -+ complete(&ip->graph_close); -+} -+ -+void ipu7_psys_handle_events(struct ipu7_psys *psys) -+{ -+ const struct ia_gofo_msg_header_ack *ack_header; -+ u8 buffer[FWPS_MSG_FW2HOST_MAX_SIZE]; -+ struct device *dev = &psys->dev; -+ u32 error = 0; -+ int ret = 0; -+ -+ do { -+#ifdef ENABLE_FW_OFFLINE_LOGGER -+ ipu7_fw_psys_get_log(psys); -+#endif -+ ret = ipu7_fw_psys_event_handle(psys, buffer); -+ if (ret) -+ break; -+ -+ ack_header = (const struct ia_gofo_msg_header_ack *)buffer; -+ -+ dev_dbg(dev, "[ACK]%s: ack msg %s received\n", __func__, -+ ps_fw_msg[ack_header->header.tlv_header.tlv_type].msg); -+ -+ if (!IA_GOFO_MSG_ERR_IS_OK(ack_header->err)) { -+ dev_err(dev, "group %d, code %d, detail: %d, %d\n", -+ ack_header->err.err_group, -+ ack_header->err.err_code, -+ ack_header->err.err_detail[0], -+ ack_header->err.err_detail[1]); -+ error = (ack_header->err.err_group == -+ IPU_MSG_ERR_GROUP_TASK) ? -+ IPU_PSYS_EVT_ERROR_FRAME : -+ IPU_PSYS_EVT_ERROR_INTERNAL; -+ } -+ -+ switch (ack_header->header.tlv_header.tlv_type) { -+ case IPU_MSG_TYPE_GRAPH_OPEN_ACK: -+ ipu7_psys_handle_graph_open_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_TASK_DONE: -+ ipu7_psys_handle_task_done(psys, buffer, error); -+ break; -+ case IPU_MSG_TYPE_GRAPH_CLOSE_ACK: -+ ipu7_psys_handle_graph_close_ack(psys, buffer); -+ break; -+ case IPU_MSG_TYPE_GENERAL_ERR: -+ /* already printed the log above for general error */ -+ break; -+ default: -+ dev_err(&psys->dev, "Unknown type %d\n", -+ ack_header->header.tlv_header.tlv_type); -+ } -+ } while (1); -+} -+ -+static int ipu7_psys_get_event(struct ipu7_psys_stream *ip, -+ struct ipu_psys_event *event) -+{ -+ struct ipu_psys_task_ack *ack; -+ -+ mutex_lock(&ip->event_mutex); -+ /* Check if there is already an event in the list */ -+ if (list_empty(&ip->ack_list)) { -+ mutex_unlock(&ip->event_mutex); -+ return -EAGAIN; -+ } -+ -+ ack = list_first_entry(&ip->ack_list, struct ipu_psys_task_ack, list); -+ -+ event->graph_id = ack->graph_id; -+ event->node_ctx_id = ack->node_ctx_id; -+ event->frame_id = ack->frame_id; -+ event->error = ack->err_code; -+ -+ list_move_tail(&ack->list, &ip->event_list); -+ mutex_unlock(&ip->event_mutex); -+ -+ dev_dbg(&ip->fh->psys->dev, "event graph %d cb %d frame %d dequeued", -+ event->graph_id, event->node_ctx_id, event->frame_id); -+ -+ return 0; -+} -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags) -+{ -+ struct ipu7_psys *psys = fh->psys; -+ int ret = 0; -+ -+ dev_dbg(&psys->adev->auxdev.dev, "IOC_DQEVENT\n"); -+ -+ if (!(f_flags & O_NONBLOCK)) { -+ ret = wait_event_interruptible(fh->wait, -+ !ipu7_psys_get_event(fh->ip, -+ event)); -+ if (ret == -ERESTARTSYS) -+ return ret; -+ } else { -+ ret = ipu7_psys_get_event(fh->ip, event); -+ } -+ -+ return ret; -+} -diff --git a/drivers/staging/media/ipu7/psys/ipu7-psys.h b/drivers/staging/media/ipu7/psys/ipu7-psys.h -new file mode 100644 -index 000000000000..a72ce4a7c5b9 ---- /dev/null -+++ b/drivers/staging/media/ipu7/psys/ipu7-psys.h -@@ -0,0 +1,184 @@ -+/* SPDX-License-Identifier: GPL-2.0 */ -+/* Copyright (C) 2013 - 2024 Intel Corporation */ -+ -+#ifndef IPU7_PSYS_H -+#define IPU7_PSYS_H -+ -+#include -+#include -+#include -+ -+#include "ipu7.h" -+#include "ipu7-fw-psys.h" -+ -+#define IPU_PSYS_WORK_QUEUE system_power_efficient_wq -+ -+#define IPU_PSYS_CMD_QUEUE_SIZE 0x20 -+#define IPU_PSYS_TASK_QUEUE_SIZE 0x20 -+#define IPU_PSYS_ACK_QUEUE_SIZE 0x40 -+#define IPU_PSYS_LOG_QUEUE_SIZE 256 -+#define IPU_PSYS_OUT_MSG_SIZE 256 -+ -+/** -+ * Each event from FW will be first queued into a -+ * event queue, define the queue depth here -+ */ -+#define TASK_EVENT_QUEUE_SIZE 3U -+/** -+ * Each task queue from user will be first queued into -+ * a task queue, define the queue depth here -+ */ -+#define TASK_REQUEST_QUEUE_SIZE 8U -+ -+#define INVALID_STREAM_ID 0xFF -+/** -+ * Task request queues per stream -+ * -+ * Each task will first assigned a task queue buffer here, -+ * all the nodes will share the same task queue, maximum -+ * queue will be full there. -+ */ -+struct ipu_psys_task_queue { -+ struct ipu_psys_term_buffers task_buffers[MAX_GRAPH_TERMINALS]; -+ dma_addr_t ipu7_addr[MAX_GRAPH_TERMINALS]; -+ -+ struct ipu7_msg_task *msg_task; -+ dma_addr_t task_dma_addr; -+ -+ struct list_head list; -+ -+ /* task state of each task input, represent ipu7_msg_task_state */ -+ enum ipu7_msg_task_state task_state; -+}; -+ -+struct psys_fw_log { -+ struct mutex mutex; /* protect whole struct */ -+ void *head; -+ void *addr; -+ u32 count; /* running counter of log */ -+ u32 size; /* actual size of log content, in bits */ -+}; -+ -+/** -+ * Task quest event context -+ * -+ * Each task request should get its event ack from FW and save -+ * to this structure and for user dequeue purpose. -+ */ -+struct ipu_psys_task_ack { -+ u8 graph_id; /* graph id of the task request */ -+ u8 node_ctx_id; /* logical node id */ -+ u8 frame_id; /* frame id of the original task request */ -+ -+ struct list_head list; -+ -+ u32 err_code; /* error indication to user */ -+}; -+ -+/** -+ * stream here is equal to pipe, each stream has -+ * its dedicated graph_id, and task request queue. -+ * -+ * For multiple stream supported design. -+ */ -+struct ipu7_psys_stream { -+ struct ipu7_psys_fh *fh; -+ -+ u8 graph_id; /* graph_id on this stream */ -+ -+ /* Handle events from FW */ -+ struct mutex event_mutex; -+ struct list_head event_list; /* Reserved event list */ -+ struct list_head ack_list; /* Received ack from FW */ -+ -+ /* Serialize task queue */ -+ struct mutex task_mutex; -+ struct list_head tq_list; /* Reserved task queue list */ -+ struct list_head tq_running_list; /* Running task sent to FW */ -+ -+ u8 num_nodes; /* Number of enabled nodes */ -+ struct graph_node nodes[MAX_GRAPH_NODES]; -+ u8 q_id[MAX_GRAPH_NODES]; /* syscom input queue id assigned by fw */ -+ -+ struct completion graph_open; -+ struct completion graph_close; -+ -+ /* Graph state, represent enum ipu7_msg_graph_state */ -+ enum ipu7_msg_graph_state graph_state; -+}; -+ -+struct task_struct; -+struct ipu7_psys { -+ struct cdev cdev; -+ struct device dev; -+ -+ struct mutex mutex; /* Psys various */ -+ int ready; /* psys fw status */ -+ spinlock_t ready_lock; /* protect psys firmware state */ -+ -+ struct list_head fhs; -+ -+ struct ipu7_psys_pdata *pdata; -+ struct ipu7_bus_device *adev; -+#ifdef CONFIG_DEBUG_FS -+ struct dentry *debugfsdir; -+#endif -+ -+ unsigned long timeout; -+ -+ struct psys_fw_log *fw_log; -+ -+ /* available graph_id range is 0 ~ IPU_PSYS_NUM_STREAMS - 1 */ -+ u8 graph_id[IPU_PSYS_NUM_STREAMS]; -+ -+ /* Device state, represent enum ipu7_msg_dev_state */ -+ enum ipu7_msg_dev_state dev_state; -+ -+ struct ipu7_psys_config *subsys_config; -+ dma_addr_t subsys_config_dma_addr; -+}; -+ -+struct ipu7_psys_fh { -+ struct ipu7_psys *psys; -+ struct mutex mutex; /* Protects bufmap & kcmds fields */ -+ struct list_head list; -+ struct list_head bufmap; -+ wait_queue_head_t wait; -+ -+ struct ipu7_psys_stream *ip; -+}; -+ -+struct ipu7_dma_buf_attach { -+ struct device *dev; -+ u64 len; -+ uintptr_t *userptr; -+ struct sg_table *sgt; -+ struct page **pages; -+ size_t npages; -+}; -+ -+struct ipu7_psys_kbuffer { -+ u64 len; -+ uintptr_t *userptr; -+ u32 flags; -+ int fd; -+ void *kaddr; -+ struct list_head list; -+ dma_addr_t dma_addr; -+ struct sg_table *sgt; -+ struct dma_buf_attachment *db_attach; -+ struct dma_buf *dbuf; -+ bool valid; /* True when buffer is usable */ -+}; -+ -+#define inode_to_ipu_psys(inode) \ -+ container_of((inode)->i_cdev, struct ipu7_psys, cdev) -+ -+void ipu7_psys_setup_hw(struct ipu7_psys *psys); -+void ipu7_psys_subdomains_power(struct ipu7_psys *psys, bool on); -+void ipu7_psys_handle_events(struct ipu7_psys *psys); -+ -+long ipu7_ioctl_dqevent(struct ipu_psys_event *event, -+ struct ipu7_psys_fh *fh, unsigned int f_flags); -+ -+#endif /* IPU7_PSYS_H */ --- -2.43.0 - diff --git a/SPECS/kernel/0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet b/SPECS/kernel/0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet deleted file mode 100644 index 9195d874f..000000000 --- a/SPECS/kernel/0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet +++ /dev/null @@ -1,116 +0,0 @@ -From 251c9e433daa663f7f6014c26cc4a1bf2ed1f272 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:48 -0700 -Subject: [PATCH 18/24] KVM: VMX: Set host constant supervisor states to VMCS - fields - -Save constant values to HOST_{S_CET,SSP,INTR_SSP_TABLE} field explicitly. -Kernel IBT is supported and the setting in MSR_IA32_S_CET is static after -post-boot(The exception is BIOS call case but vCPU thread never across it) -and KVM doesn't need to refresh HOST_S_CET field before every VM-Enter/ -VM-Exit sequence. - -Host supervisor shadow stack is not enabled now and SSP is not accessible -to kernel mode, thus it's safe to set host IA32_INT_SSP_TAB/SSP VMCS field -to 0s. When shadow stack is enabled for CPL3, SSP is reloaded from PL3_SSP -before it exits to userspace. Check SDM Vol 2A/B Chapter 3/4 for SYSCALL/ -SYSRET/SYSENTER SYSEXIT/RDSSP/CALL etc. - -Prevent KVM module loading if host supervisor shadow stack SHSTK_EN is set -in MSR_IA32_S_CET as KVM cannot co-exit with it correctly. - -Suggested-by: Sean Christopherson -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/capabilities.h | 5 +++++ - arch/x86/kvm/vmx/vmx.c | 15 +++++++++++++++ - arch/x86/kvm/x86.c | 12 ++++++++++++ - arch/x86/kvm/x86.h | 1 + - 4 files changed, 33 insertions(+) - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 874c6dd34665..fd1d43ebe1df 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -105,6 +105,11 @@ static inline bool cpu_has_save_perf_global_ctrl(void) - return vmcs_config.vmexit_ctrl & VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL; - } - -+static inline bool cpu_has_load_cet_ctrl(void) -+{ -+ return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_CET_STATE); -+} -+ - static inline bool cpu_has_vmx_mpx(void) - { - return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index ca10c1b142dc..9393abf9f183 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4371,6 +4371,21 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) - if (cpu_has_load_ia32_efer()) - vmcs_write64(HOST_IA32_EFER, kvm_host.efer); - -+ /* -+ * Supervisor shadow stack is not enabled on host side, i.e., -+ * host IA32_S_CET.SHSTK_EN bit is guaranteed to 0 now, per SDM -+ * description(RDSSP instruction), SSP is not readable in CPL0, -+ * so resetting the two registers to 0s at VM-Exit does no harm -+ * to kernel execution. When execution flow exits to userspace, -+ * SSP is reloaded from IA32_PL3_SSP. Check SDM Vol.2A/B Chapter -+ * 3 and 4 for details. -+ */ -+ if (cpu_has_load_cet_ctrl()) { -+ vmcs_writel(HOST_S_CET, kvm_host.s_cet); -+ vmcs_writel(HOST_SSP, 0); -+ vmcs_writel(HOST_INTR_SSP_TABLE, 0); -+ } -+ - /* - * When running a guest with a mediated PMU, guest state is resident in - * hardware after VM-Exit. Zero PERF_GLOBAL_CTRL on exit so that host -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 42e4fac999de..83fd91494a4b 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9827,6 +9827,18 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - return -EIO; - } - -+ if (boot_cpu_has(X86_FEATURE_SHSTK)) { -+ rdmsrl(MSR_IA32_S_CET, kvm_host.s_cet); -+ /* -+ * Linux doesn't yet support supervisor shadow stacks (SSS), so -+ * KVM doesn't save/restore the associated MSRs, i.e. KVM may -+ * clobber the host values. Yell and refuse to load if SSS is -+ * unexpectedly enabled, e.g. to avoid crashing the host. -+ */ -+ if (WARN_ON_ONCE(kvm_host.s_cet & CET_SHSTK_EN)) -+ return -EIO; -+ } -+ - memset(&kvm_caps, 0, sizeof(kvm_caps)); - - x86_emulator_cache = kvm_alloc_emulator_cache(); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index b54ec7d37f1f..1f631b3459e8 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -50,6 +50,7 @@ struct kvm_host_values { - u64 efer; - u64 xcr0; - u64 xss; -+ u64 s_cet; - u64 arch_capabilities; - }; - --- -2.43.0 - diff --git a/SPECS/kernel/0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi b/SPECS/kernel/0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi deleted file mode 100644 index 4dd7e492d..000000000 --- a/SPECS/kernel/0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi +++ /dev/null @@ -1,516 +0,0 @@ -From 3961eaba21b1ab7dc2bf9e82c577d5b62a44bf5c Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 15 Sep 2022 22:45:34 -0700 -Subject: [PATCH 18/44] KVM: nVMX: Add FRED VMCS fields to nested VMX context - handling - -Extend nested VMX context management to include FRED-related VMCS fields. -This enables proper handling of FRED state during nested virtualization. - -Because KVM always sets SECONDARY_VM_EXIT_SAVE_IA32_FRED, FRED MSRs are -always saved to vmcs02. However an L1 VMM may choose to clear this bit, -i.e., not to save FRED MSRs to vmcs12. This is not a problem when the L1 -VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, as KVM then immediately loads -host FRED MSRs of vmcs12 to guest FRED MSRs of vmcs01. However if the L1 -VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should retain FRED MSRs -to run the L1 VMM. - -To propagate guest FRED MSRs from vmcs02 to vmcs01, save them in -sync_vmcs02_to_vmcs12() regardless of whether -SECONDARY_VM_EXIT_SAVE_IA32_FRED is set in vmcs12. Then, use the saved -values to set guest FRED MSRs in vmcs01 within load_vmcs12_host_state() -when !nested_cpu_load_host_fred_state(). - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v6: -* Handle FRED MSR pre-vmenter save/restore (Chao Gao). -* Save FRED MSRs of vmcs02 at VM-Exit even an L1 VMM clears - SECONDARY_VM_EXIT_SAVE_IA32_FRED. -* Save FRED MSRs in sync_vmcs02_to_vmcs12() instead of its rare version. - -Change in v5: -* Add TB from Xuelian Guo. - -Changes in v4: -* Advertise VMX nested exception as if the CPU supports it (Chao Gao). -* Split FRED state management controls (Chao Gao). - -Changes in v3: -* Add and use nested_cpu_has_fred(vmcs12) because vmcs02 should be set - from vmcs12 if and only if the field is enabled in L1's VMX config - (Sean Christopherson). -* Fix coding style issues (Sean Christopherson). - -Changes in v2: -* Remove hyperv TLFS related changes (Jeremi Piotrowski). -* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). ---- - Documentation/virt/kvm/x86/nested-vmx.rst | 18 ++++ - arch/x86/kvm/vmx/capabilities.h | 5 + - arch/x86/kvm/vmx/nested.c | 113 +++++++++++++++++++++- - arch/x86/kvm/vmx/nested.h | 22 +++++ - arch/x86/kvm/vmx/vmcs12.c | 18 ++++ - arch/x86/kvm/vmx/vmcs12.h | 36 +++++++ - arch/x86/kvm/vmx/vmcs_shadow_fields.h | 4 + - arch/x86/kvm/vmx/vmx.h | 41 ++++++++ - 8 files changed, 255 insertions(+), 2 deletions(-) - -diff --git a/Documentation/virt/kvm/x86/nested-vmx.rst b/Documentation/virt/kvm/x86/nested-vmx.rst -index e64ef231f310..87fa9f3877ab 100644 ---- a/Documentation/virt/kvm/x86/nested-vmx.rst -+++ b/Documentation/virt/kvm/x86/nested-vmx.rst -@@ -218,6 +218,24 @@ struct shadow_vmcs is ever changed. - u16 host_gs_selector; - u16 host_tr_selector; - u64 secondary_vm_exit_controls; -+ u64 guest_ia32_fred_config; -+ u64 guest_ia32_fred_rsp1; -+ u64 guest_ia32_fred_rsp2; -+ u64 guest_ia32_fred_rsp3; -+ u64 guest_ia32_fred_stklvls; -+ u64 guest_ia32_fred_ssp1; -+ u64 guest_ia32_fred_ssp2; -+ u64 guest_ia32_fred_ssp3; -+ u64 host_ia32_fred_config; -+ u64 host_ia32_fred_rsp1; -+ u64 host_ia32_fred_rsp2; -+ u64 host_ia32_fred_rsp3; -+ u64 host_ia32_fred_stklvls; -+ u64 host_ia32_fred_ssp1; -+ u64 host_ia32_fred_ssp2; -+ u64 host_ia32_fred_ssp3; -+ u64 injected_event_data; -+ u64 original_event_data; - }; - - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index ebf4cbd2df70..3cd66020c926 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -80,6 +80,11 @@ static inline bool cpu_has_vmx_basic_no_hw_errcode(void) - return vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; - } - -+static inline bool cpu_has_vmx_nested_exception(void) -+{ -+ return vmcs_config.basic & VMX_BASIC_NESTED_EXCEPTION; -+} -+ - static inline bool cpu_has_virtual_nmis(void) - { - return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS && -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 611ccff1dc90..7b3cd9b6f7c0 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -741,6 +741,9 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - nested_vmx_merge_msr_bitmaps_rw(MSR_FS_BASE); - nested_vmx_merge_msr_bitmaps_rw(MSR_GS_BASE); - nested_vmx_merge_msr_bitmaps_rw(MSR_KERNEL_GS_BASE); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_FRED_RSP0, MSR_TYPE_RW); - #endif - nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_SPEC_CTRL); - nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD); -@@ -1327,9 +1330,11 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) - const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT | - VMX_BASIC_INOUT | - VMX_BASIC_TRUE_CTLS | -- VMX_BASIC_NO_HW_ERROR_CODE_CC; -+ VMX_BASIC_NO_HW_ERROR_CODE_CC | -+ VMX_BASIC_NESTED_EXCEPTION; - -- const u64 reserved_bits = GENMASK_ULL(63, 57) | -+ const u64 reserved_bits = GENMASK_ULL(63, 59) | -+ BIT_ULL(57) | - GENMASK_ULL(47, 45) | - BIT_ULL(31); - -@@ -2581,6 +2586,8 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 - vmcs12->vm_entry_instruction_len); - vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, - vmcs12->guest_interruptibility_info); -+ if (cpu_has_vmx_fred()) -+ vmcs_write64(INJECTED_EVENT_DATA, vmcs12->injected_event_data); - vmx->loaded_vmcs->nmi_known_unmasked = - !(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_NMI); - } else { -@@ -2735,6 +2742,17 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) - vmcs12->guest_ssp, vmcs12->guest_ssp_tbl); - - set_cr4_guest_host_mask(vmx); -+ -+ if (nested_cpu_load_guest_fred_state(vmcs12)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->guest_ia32_fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->guest_ia32_fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->guest_ia32_fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->guest_ia32_fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->guest_ia32_fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->guest_ia32_fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->guest_ia32_fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->guest_ia32_fred_ssp3); -+ } - } - - /* -@@ -2801,6 +2819,18 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat); - } - -+ if (!vmx->nested.nested_run_pending || -+ !nested_cpu_load_guest_fred_state(vmcs12)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.pre_vmenter_fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.pre_vmenter_fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.pre_vmenter_fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.pre_vmenter_fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.pre_vmenter_fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.pre_vmenter_fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.pre_vmenter_fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.pre_vmenter_fred_ssp3); -+ } -+ - vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset( - vcpu->arch.l1_tsc_offset, - vmx_get_l2_tsc_offset(vcpu), -@@ -3715,6 +3745,18 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, - &vmx->nested.pre_vmenter_ssp, - &vmx->nested.pre_vmenter_ssp_tbl); - -+ if (!vmx->nested.nested_run_pending || -+ !nested_cpu_load_guest_fred_state(vmcs12)) { -+ vmx->nested.pre_vmenter_fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); -+ vmx->nested.pre_vmenter_fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); -+ vmx->nested.pre_vmenter_fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); -+ vmx->nested.pre_vmenter_fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); -+ vmx->nested.pre_vmenter_fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); -+ vmx->nested.pre_vmenter_fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); -+ vmx->nested.pre_vmenter_fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); -+ vmx->nested.pre_vmenter_fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); -+ } -+ - /* - * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* - * nested early checks are disabled. In the event of a "late" VM-Fail, -@@ -4022,6 +4064,8 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, - u32 idt_vectoring; - unsigned int nr; - -+ vmcs12->original_event_data = 0; -+ - /* - * Per the SDM, VM-Exits due to double and triple faults are never - * considered to occur during event delivery, even if the double/triple -@@ -4060,6 +4104,13 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, - vcpu->arch.exception.error_code; - } - -+ if ((vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && -+ (vmcs12->guest_cr4 & X86_CR4_FRED) && -+ (vcpu->arch.exception.nested)) -+ idt_vectoring |= VECTORING_INFO_NESTED_EXCEPTION_MASK; -+ -+ vmcs12->original_event_data = vcpu->arch.exception.event_data; -+ - vmcs12->idt_vectoring_info_field = idt_vectoring; - } else if (vcpu->arch.nmi_injected) { - vmcs12->idt_vectoring_info_field = -@@ -4805,6 +4856,26 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) - - if (vmcs12->vm_exit_controls & VM_EXIT_SAVE_IA32_EFER) - vmcs12->guest_ia32_efer = vcpu->arch.efer; -+ -+ vmx->nested.fred_msr_at_vmexit.fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); -+ vmx->nested.fred_msr_at_vmexit.fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); -+ vmx->nested.fred_msr_at_vmexit.fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); -+ vmx->nested.fred_msr_at_vmexit.fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); -+ vmx->nested.fred_msr_at_vmexit.fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); -+ vmx->nested.fred_msr_at_vmexit.fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); -+ vmx->nested.fred_msr_at_vmexit.fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); -+ vmx->nested.fred_msr_at_vmexit.fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); -+ -+ if (nested_cpu_save_guest_fred_state(vmcs12)) { -+ vmcs12->guest_ia32_fred_config = vmx->nested.fred_msr_at_vmexit.fred_config; -+ vmcs12->guest_ia32_fred_rsp1 = vmx->nested.fred_msr_at_vmexit.fred_rsp1; -+ vmcs12->guest_ia32_fred_rsp2 = vmx->nested.fred_msr_at_vmexit.fred_rsp2; -+ vmcs12->guest_ia32_fred_rsp3 = vmx->nested.fred_msr_at_vmexit.fred_rsp3; -+ vmcs12->guest_ia32_fred_stklvls = vmx->nested.fred_msr_at_vmexit.fred_stklvls; -+ vmcs12->guest_ia32_fred_ssp1 = vmx->nested.fred_msr_at_vmexit.fred_ssp1; -+ vmcs12->guest_ia32_fred_ssp2 = vmx->nested.fred_msr_at_vmexit.fred_ssp2; -+ vmcs12->guest_ia32_fred_ssp3 = vmx->nested.fred_msr_at_vmexit.fred_ssp3; -+ } - } - - /* -@@ -4849,6 +4920,21 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - - vmcs12->vm_exit_intr_info = exit_intr_info; - vmcs12->vm_exit_instruction_len = exit_insn_len; -+ -+ /* -+ * When there is a valid original event, the exiting event is a nested -+ * event during delivery of the earlier original event. -+ * -+ * FRED event delivery reflects this relationship by setting the value -+ * of the nested exception bit of VM-exit interruption information -+ * (aka exiting-event identification) to that of the valid bit of the -+ * IDT-vectoring information (aka original-event identification). -+ */ -+ if ((vmcs12->idt_vectoring_info_field & VECTORING_INFO_VALID_MASK) && -+ (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && -+ (vmcs12->guest_cr4 & X86_CR4_FRED)) -+ vmcs12->vm_exit_intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; -+ - vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO); - - /* -@@ -4877,6 +4963,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) - { -+ struct vcpu_vmx *vmx = to_vmx(vcpu); - enum vm_entry_failure_code ignored; - struct kvm_segment seg; - -@@ -4943,6 +5030,26 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, - vmcs12->host_ia32_perf_global_ctrl)); - -+ if (nested_cpu_load_host_fred_state(vmcs12)) { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->host_ia32_fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->host_ia32_fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->host_ia32_fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->host_ia32_fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->host_ia32_fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->host_ia32_fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->host_ia32_fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->host_ia32_fred_ssp3); -+ } else { -+ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.fred_msr_at_vmexit.fred_config); -+ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.fred_msr_at_vmexit.fred_rsp1); -+ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.fred_msr_at_vmexit.fred_rsp2); -+ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.fred_msr_at_vmexit.fred_rsp3); -+ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.fred_msr_at_vmexit.fred_stklvls); -+ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.fred_msr_at_vmexit.fred_ssp1); -+ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.fred_msr_at_vmexit.fred_ssp2); -+ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.fred_msr_at_vmexit.fred_ssp3); -+ } -+ - /* Set L1 segment info according to Intel SDM - 27.5.2 Loading Host Segment and Descriptor-Table Registers */ - seg = (struct kvm_segment) { -@@ -7401,6 +7508,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs) - msrs->basic |= VMX_BASIC_INOUT; - if (cpu_has_vmx_basic_no_hw_errcode()) - msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC; -+ if (cpu_has_vmx_nested_exception()) -+ msrs->basic |= VMX_BASIC_NESTED_EXCEPTION; - } - - static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs) -diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h -index 983484d42ebf..a99d3d83d58e 100644 ---- a/arch/x86/kvm/vmx/nested.h -+++ b/arch/x86/kvm/vmx/nested.h -@@ -249,6 +249,11 @@ static inline bool nested_cpu_has_save_preemption_timer(struct vmcs12 *vmcs12) - VM_EXIT_SAVE_VMX_PREEMPTION_TIMER; - } - -+static inline bool nested_cpu_has_secondary_vm_exit_controls(struct vmcs12 *vmcs12) -+{ -+ return vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; -+} -+ - static inline bool nested_exit_on_nmi(struct kvm_vcpu *vcpu) - { - return nested_cpu_has_nmi_exiting(get_vmcs12(vcpu)); -@@ -269,6 +274,23 @@ static inline bool nested_cpu_has_encls_exit(struct vmcs12 *vmcs12) - return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENCLS_EXITING); - } - -+static inline bool nested_cpu_load_guest_fred_state(struct vmcs12 *vmcs12) -+{ -+ return vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED; -+} -+ -+static inline bool nested_cpu_save_guest_fred_state(struct vmcs12 *vmcs12) -+{ -+ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && -+ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_SAVE_IA32_FRED; -+} -+ -+static inline bool nested_cpu_load_host_fred_state(struct vmcs12 *vmcs12) -+{ -+ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && -+ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED; -+} -+ - /* - * if fixed0[i] == 1: val[i] must be 1 - * if fixed1[i] == 0: val[i] must be 0 -diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c -index 3b01175f392a..9691e709061f 100644 ---- a/arch/x86/kvm/vmx/vmcs12.c -+++ b/arch/x86/kvm/vmx/vmcs12.c -@@ -67,6 +67,24 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD64(HOST_IA32_EFER, host_ia32_efer), - FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), - FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), -+ FIELD64(INJECTED_EVENT_DATA, injected_event_data), -+ FIELD64(ORIGINAL_EVENT_DATA, original_event_data), -+ FIELD64(GUEST_IA32_FRED_CONFIG, guest_ia32_fred_config), -+ FIELD64(GUEST_IA32_FRED_RSP1, guest_ia32_fred_rsp1), -+ FIELD64(GUEST_IA32_FRED_RSP2, guest_ia32_fred_rsp2), -+ FIELD64(GUEST_IA32_FRED_RSP3, guest_ia32_fred_rsp3), -+ FIELD64(GUEST_IA32_FRED_STKLVLS, guest_ia32_fred_stklvls), -+ FIELD64(GUEST_IA32_FRED_SSP1, guest_ia32_fred_ssp1), -+ FIELD64(GUEST_IA32_FRED_SSP2, guest_ia32_fred_ssp2), -+ FIELD64(GUEST_IA32_FRED_SSP3, guest_ia32_fred_ssp3), -+ FIELD64(HOST_IA32_FRED_CONFIG, host_ia32_fred_config), -+ FIELD64(HOST_IA32_FRED_RSP1, host_ia32_fred_rsp1), -+ FIELD64(HOST_IA32_FRED_RSP2, host_ia32_fred_rsp2), -+ FIELD64(HOST_IA32_FRED_RSP3, host_ia32_fred_rsp3), -+ FIELD64(HOST_IA32_FRED_STKLVLS, host_ia32_fred_stklvls), -+ FIELD64(HOST_IA32_FRED_SSP1, host_ia32_fred_ssp1), -+ FIELD64(HOST_IA32_FRED_SSP2, host_ia32_fred_ssp2), -+ FIELD64(HOST_IA32_FRED_SSP3, host_ia32_fred_ssp3), - FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), - FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), - FIELD(EXCEPTION_BITMAP, exception_bitmap), -diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h -index 7866fdce7a23..a3853536a575 100644 ---- a/arch/x86/kvm/vmx/vmcs12.h -+++ b/arch/x86/kvm/vmx/vmcs12.h -@@ -192,6 +192,24 @@ struct __packed vmcs12 { - u16 host_tr_selector; - u16 guest_pml_index; - u64 secondary_vm_exit_controls; -+ u64 guest_ia32_fred_config; -+ u64 guest_ia32_fred_rsp1; -+ u64 guest_ia32_fred_rsp2; -+ u64 guest_ia32_fred_rsp3; -+ u64 guest_ia32_fred_stklvls; -+ u64 guest_ia32_fred_ssp1; -+ u64 guest_ia32_fred_ssp2; -+ u64 guest_ia32_fred_ssp3; -+ u64 host_ia32_fred_config; -+ u64 host_ia32_fred_rsp1; -+ u64 host_ia32_fred_rsp2; -+ u64 host_ia32_fred_rsp3; -+ u64 host_ia32_fred_stklvls; -+ u64 host_ia32_fred_ssp1; -+ u64 host_ia32_fred_ssp2; -+ u64 host_ia32_fred_ssp3; -+ u64 injected_event_data; -+ u64 original_event_data; - }; - - /* -@@ -374,6 +392,24 @@ static inline void vmx_check_vmcs12_offsets(void) - CHECK_OFFSET(host_tr_selector, 994); - CHECK_OFFSET(guest_pml_index, 996); - CHECK_OFFSET(secondary_vm_exit_controls, 998); -+ CHECK_OFFSET(guest_ia32_fred_config, 1006); -+ CHECK_OFFSET(guest_ia32_fred_rsp1, 1014); -+ CHECK_OFFSET(guest_ia32_fred_rsp2, 1022); -+ CHECK_OFFSET(guest_ia32_fred_rsp3, 1030); -+ CHECK_OFFSET(guest_ia32_fred_stklvls, 1038); -+ CHECK_OFFSET(guest_ia32_fred_ssp1, 1046); -+ CHECK_OFFSET(guest_ia32_fred_ssp2, 1054); -+ CHECK_OFFSET(guest_ia32_fred_ssp3, 1062); -+ CHECK_OFFSET(host_ia32_fred_config, 1070); -+ CHECK_OFFSET(host_ia32_fred_rsp1, 1078); -+ CHECK_OFFSET(host_ia32_fred_rsp2, 1086); -+ CHECK_OFFSET(host_ia32_fred_rsp3, 1094); -+ CHECK_OFFSET(host_ia32_fred_stklvls, 1102); -+ CHECK_OFFSET(host_ia32_fred_ssp1, 1110); -+ CHECK_OFFSET(host_ia32_fred_ssp2, 1118); -+ CHECK_OFFSET(host_ia32_fred_ssp3, 1126); -+ CHECK_OFFSET(injected_event_data, 1134); -+ CHECK_OFFSET(original_event_data, 1142); - } - - extern const unsigned short vmcs12_field_offsets[]; -diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -index cad128d1657b..da338327c2b3 100644 ---- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h -+++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -@@ -74,6 +74,10 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) - /* 64-bit */ - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) -+SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) -+SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) -+SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) -+SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) - - #undef SHADOW_FIELD_RO - #undef SHADOW_FIELD_RW -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 05fa52a7581c..d0adb0f31d03 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -67,6 +67,37 @@ struct pt_desc { - struct pt_ctx guest; - }; - -+/* -+ * Used to snapshot FRED MSRs that may NOT be saved to vmcs12 as specified -+ * in the VM-Exit controls of vmcs12 configured by L1 VMM. -+ * -+ * FRED MSRs are *always* saved into vmcs02 because KVM always sets -+ * SECONDARY_VM_EXIT_SAVE_IA32_FRED. However an L1 VMM may choose to clear -+ * this bit, resulting in FRED MSRs not being propagated to vmcs12 from -+ * vmcs02. When the L1 VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, this is -+ * not a problem, since KVM then immediately loads the host FRED MSRs of -+ * vmcs12 to the guest FRED MSRs of vmcs01. -+ * -+ * But if the L1 VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should -+ * retain the FRED MSRs, i.e., propagate the guest FRED MSRs of vmcs02 to -+ * the guest FRED MSRs of vmcs01. -+ * -+ * This structure stores guest FRED MSRs that an L1 VMM opts not to save -+ * during VM-Exits from L2 to L1. These MSRs may still be retained for -+ * running the L1 VMM if SECONDARY_VM_EXIT_LOAD_IA32_FRED is cleared in -+ * vmcs12. -+ */ -+struct fred_msr_at_vmexit { -+ u64 fred_config; -+ u64 fred_rsp1; -+ u64 fred_rsp2; -+ u64 fred_rsp3; -+ u64 fred_stklvls; -+ u64 fred_ssp1; -+ u64 fred_ssp2; -+ u64 fred_ssp3; -+}; -+ - /* - * The nested_vmx structure is part of vcpu_vmx, and holds information we need - * for correct emulation of VMX (i.e., nested VMX) on this vcpu. -@@ -184,6 +215,16 @@ struct nested_vmx { - u64 pre_vmenter_s_cet; - u64 pre_vmenter_ssp; - u64 pre_vmenter_ssp_tbl; -+ u64 pre_vmenter_fred_config; -+ u64 pre_vmenter_fred_rsp1; -+ u64 pre_vmenter_fred_rsp2; -+ u64 pre_vmenter_fred_rsp3; -+ u64 pre_vmenter_fred_stklvls; -+ u64 pre_vmenter_fred_ssp1; -+ u64 pre_vmenter_fred_ssp2; -+ u64 pre_vmenter_fred_ssp3; -+ -+ struct fred_msr_at_vmexit fred_msr_at_vmexit; - - /* to migrate it to L1 if L2 writes to L1's CR8 directly */ - int l1_tpr_threshold; --- -2.43.0 - diff --git a/SPECS/kernel/0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi b/SPECS/kernel/0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi new file mode 100644 index 000000000..bff5ccb9f --- /dev/null +++ b/SPECS/kernel/0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi @@ -0,0 +1,162 @@ +From 9796175f56299f2cec3b9677aa71f4bd82f30ad4 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Aug 2022 09:58:24 -0700 +Subject: [PATCH 18/44] KVM: nVMX: Enable support for secondary VM exit + controls + +Add support for secondary VM exit controls in nested VMX to facilitate +future FRED integration. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v8: +* Relocate secondary_vm_exit_controls to the last u64 padding field. +* Remove the change to Documentation/virt/kvm/x86/nested-vmx.rst. + +Changes in v5: +* Allow writing MSR_IA32_VMX_EXIT_CTLS2 (Sean). +* Add TB from Xuelian Guo. + +Change in v3: +* Read secondary VM exit controls from vmcs_conf insteasd of the hardware + MSR MSR_IA32_VMX_EXIT_CTLS2 to avoid advertising features to L1 that KVM + itself doesn't support, e.g. because the expected entry+exit pairs aren't + supported. (Sean Christopherson) +--- + arch/x86/kvm/vmx/capabilities.h | 1 + + arch/x86/kvm/vmx/nested.c | 26 +++++++++++++++++++++++++- + arch/x86/kvm/vmx/vmcs12.c | 1 + + arch/x86/kvm/vmx/vmcs12.h | 3 ++- + arch/x86/kvm/x86.h | 2 +- + 5 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index 651507627ef32..f390f9f883c30 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -34,6 +34,7 @@ struct nested_vmx_msrs { + u32 pinbased_ctls_high; + u32 exit_ctls_low; + u32 exit_ctls_high; ++ u64 secondary_exit_ctls; + u32 entry_ctls_low; + u32 entry_ctls_high; + u32 misc_low; +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index bcea087b642fd..b994f264acc0a 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -1531,6 +1531,11 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) + return -EINVAL; + vmx->nested.msrs.vmfunc_controls = data; + return 0; ++ case MSR_IA32_VMX_EXIT_CTLS2: ++ if (data & ~vmcs_config.nested.secondary_exit_ctls) ++ return -EINVAL; ++ vmx->nested.msrs.secondary_exit_ctls = data; ++ return 0; + default: + /* + * The rest of the VMX capability MSRs do not support restore. +@@ -1570,6 +1575,9 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) + if (msr_index == MSR_IA32_VMX_EXIT_CTLS) + *pdata |= VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR; + break; ++ case MSR_IA32_VMX_EXIT_CTLS2: ++ *pdata = msrs->secondary_exit_ctls; ++ break; + case MSR_IA32_VMX_TRUE_ENTRY_CTLS: + case MSR_IA32_VMX_ENTRY_CTLS: + *pdata = vmx_control_msr( +@@ -2520,6 +2528,11 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 + exec_control &= ~VM_EXIT_LOAD_IA32_EFER; + vm_exit_controls_set(vmx, exec_control); + ++ if (exec_control & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { ++ exec_control = __secondary_vm_exit_controls_get(vmcs01); ++ secondary_vm_exit_controls_set(vmx, exec_control); ++ } ++ + /* + * Interrupt/Exception Fields + */ +@@ -7187,7 +7200,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, + VM_EXIT_HOST_ADDR_SPACE_SIZE | + #endif + VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT | +- VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE; ++ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE | ++ VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; + msrs->exit_ctls_high |= + VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | + VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | +@@ -7200,6 +7214,16 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, + + /* We support free control of debug control saving. */ + msrs->exit_ctls_low &= ~VM_EXIT_SAVE_DEBUG_CONTROLS; ++ ++ if (msrs->exit_ctls_high & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS) { ++ msrs->secondary_exit_ctls = vmcs_conf->vmexit_2nd_ctrl; ++ /* ++ * As the secondary VM exit control is always loaded, do not ++ * advertise any feature in it to nVMX until its nVMX support ++ * is ready. ++ */ ++ msrs->secondary_exit_ctls &= 0; ++ } + } + + static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, +diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c +index 4233b5ca9461a..3b01175f392ae 100644 +--- a/arch/x86/kvm/vmx/vmcs12.c ++++ b/arch/x86/kvm/vmx/vmcs12.c +@@ -66,6 +66,7 @@ const unsigned short vmcs12_field_offsets[] = { + FIELD64(HOST_IA32_PAT, host_ia32_pat), + FIELD64(HOST_IA32_EFER, host_ia32_efer), + FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), ++ FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), + FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), + FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), + FIELD(EXCEPTION_BITMAP, exception_bitmap), +diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h +index 4ad6b16525b93..fa5306dc0311f 100644 +--- a/arch/x86/kvm/vmx/vmcs12.h ++++ b/arch/x86/kvm/vmx/vmcs12.h +@@ -71,7 +71,7 @@ struct __packed vmcs12 { + u64 pml_address; + u64 encls_exiting_bitmap; + u64 tsc_multiplier; +- u64 padding64[1]; /* room for future expansion */ ++ u64 secondary_vm_exit_controls; + /* + * To allow migration of L1 (complete with its L2 guests) between + * machines of different natural widths (32 or 64 bit), we cannot have +@@ -261,6 +261,7 @@ static inline void vmx_check_vmcs12_offsets(void) + CHECK_OFFSET(pml_address, 312); + CHECK_OFFSET(encls_exiting_bitmap, 320); + CHECK_OFFSET(tsc_multiplier, 328); ++ CHECK_OFFSET(secondary_vm_exit_controls, 336); + CHECK_OFFSET(cr0_guest_host_mask, 344); + CHECK_OFFSET(cr4_guest_host_mask, 352); + CHECK_OFFSET(cr0_read_shadow, 360); +diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h +index e9c6f304b02e4..1576f192a647b 100644 +--- a/arch/x86/kvm/x86.h ++++ b/arch/x86/kvm/x86.h +@@ -95,7 +95,7 @@ do { \ + * associated feature that KVM supports for nested virtualization. + */ + #define KVM_FIRST_EMULATED_VMX_MSR MSR_IA32_VMX_BASIC +-#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_VMFUNC ++#define KVM_LAST_EMULATED_VMX_MSR MSR_IA32_VMX_EXIT_CTLS2 + + #define KVM_DEFAULT_PLE_GAP 128 + #define KVM_VMX_DEFAULT_PLE_WINDOW 4096 +-- +2.43.0 + diff --git a/SPECS/kernel/0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet b/SPECS/kernel/0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet new file mode 100644 index 000000000..778350603 --- /dev/null +++ b/SPECS/kernel/0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet @@ -0,0 +1,38 @@ +From 1ae1abe63f9eb8907fd4d0805b5759983b4d707f Mon Sep 17 00:00:00 2001 +From: KhaiWenTan +Date: Tue, 13 Jan 2026 22:52:50 +0800 +Subject: [PATCH 18/18] net: stmmac: intel: Initialize plat->phy_interfaces in + tgl_common_data() + +Commit d3836052fe09 ("net: stmmac: intel: convert speed_mode_2500() +to get_interfaces()") has made the initialization of phy_interfaces +to be done after Intel mgbe xpcs setup. + +The pcs-xpcs setup rely on checking the phy_interfaces is whether +SGMII or 1000BASEX. Without the phy_interfaces initialized first, +pcs-xpcs setup will be skipped no matter the condition, causing ping +issue for 100Mbps and 10Mbps. + +Adding the initialization of plat->phy_interfaces to tgl_common_data() +ensure the pcs-xpcs setup will be executed for SGMII and 1000BASEX. + +Signed-off-by: KhaiWenTan +--- + drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +index 932432846800d..0bd4ae619289e 100644 +--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c ++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c +@@ -940,6 +940,7 @@ static int tgl_common_data(struct pci_dev *pdev, + plat->rx_queues_to_use = 6; + plat->tx_queues_to_use = 4; + plat->clk_ptp_rate = 204800000; ++ plat->phy_interface = PHY_INTERFACE_MODE_SGMII; + plat->get_interfaces = tgl_get_interfaces; + + plat->safety_feat_cfg->tsoee = 1; +-- +2.43.0 + diff --git a/SPECS/kernel/0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf b/SPECS/kernel/0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf deleted file mode 100644 index e5a1b412c..000000000 --- a/SPECS/kernel/0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf +++ /dev/null @@ -1,61 +0,0 @@ -From d1e990d74ea175d2e98eaf8c156f941eb190897e Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 05:36:55 +0000 -Subject: [PATCH 018/100] perf/x86/intel: Update dyn_constranit base on PEBS - event precise level - -arch-PEBS provides CPUIDs to enumerate which counters support PEBS -sampling and precise distribution PEBS sampling. Thus PEBS constraints -should be dynamically configured base on these counter and precise -distribution bitmap instead of defining them statically. - -Update event dyn_constraint base on PEBS event precise level. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 9 +++++++++ - arch/x86/events/intel/ds.c | 1 + - 2 files changed, 10 insertions(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index d4eaaeff0614..b91c8f24c8f6 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -4254,6 +4254,8 @@ static int intel_pmu_hw_config(struct perf_event *event) - } - - if (event->attr.precise_ip) { -+ struct arch_pebs_cap pebs_cap = hybrid(event->pmu, arch_pebs_cap); -+ - if ((event->attr.config & INTEL_ARCH_EVENT_MASK) == INTEL_FIXED_VLBR_EVENT) - return -EINVAL; - -@@ -4267,6 +4269,13 @@ static int intel_pmu_hw_config(struct perf_event *event) - } - if (x86_pmu.pebs_aliases) - x86_pmu.pebs_aliases(event); -+ -+ if (x86_pmu.arch_pebs) { -+ u64 cntr_mask = event->attr.precise_ip >= 3 ? -+ pebs_cap.pdists : pebs_cap.counters; -+ if (cntr_mask != hybrid(event->pmu, intel_ctrl)) -+ event->hw.dyn_constraint &= cntr_mask; -+ } - } - - if (needs_branch_stack(event)) { -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 3f9ee8dbe6b7..f8b5275409d6 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -2983,6 +2983,7 @@ static void __init intel_arch_pebs_init(void) - x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE; - x86_pmu.drain_pebs = intel_pmu_drain_arch_pebs; - x86_pmu.pebs_capable = ~0ULL; -+ x86_pmu.flags |= PMU_FL_PEBS_ALL; - - x86_pmu.pebs_enable = __intel_pmu_pebs_enable; - x86_pmu.pebs_disable = __intel_pmu_pebs_disable; --- -2.43.0 - diff --git a/SPECS/kernel/0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo b/SPECS/kernel/0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo new file mode 100644 index 000000000..e99bd5e27 --- /dev/null +++ b/SPECS/kernel/0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo @@ -0,0 +1,261 @@ +From cacfc54ae3d9f400fc328bb39a44a13d4c60a387 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 15:30:36 -0500 +Subject: [PATCH 18/21] tools/power turbostat: Print "nan" for out of range + percentages + +Sometimes counters return junk. +For the cases where values > 100% is invalid, print "nan". + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 92 +++++++++++++++------------ + 1 file changed, 53 insertions(+), 39 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 0064f9091c7f0..f59dcee3c816e 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2999,6 +2999,25 @@ void print_header(char *delim) + outp += sprintf(outp, "\n"); + } + ++/* ++ * pct() ++ * ++ * If absolute value is < 1.1, return percentage ++ * otherwise, return nan ++ * ++ * return value is appropriate for printing percentages with %f ++ * while flagging some obvious erroneous values. ++ */ ++double pct(double d) ++{ ++ ++ double abs = fabs(d); ++ ++ if (abs < 1.10) ++ return (100.0 * d); ++ return nan(""); ++} ++ + int dump_counters(PER_THREAD_PARAMS) + { + int i; +@@ -3026,7 +3045,7 @@ int dump_counters(PER_THREAD_PARAMS) + + outp += sprintf(outp, "LLC refs: %lld", t->llc.references); + outp += sprintf(outp, "LLC miss: %lld", t->llc.misses); +- outp += sprintf(outp, "LLC Hit%%: %.2f", 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ outp += sprintf(outp, "LLC Hit%%: %.2f", pct((t->llc.references - t->llc.misses) / t->llc.references)); + + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + outp += +@@ -3248,7 +3267,7 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), 1.0 / units * t->aperf / interval_float); + + if (DO_BIC(BIC_Busy)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->mperf / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(t->mperf / tsc)); + + if (DO_BIC(BIC_Bzy_MHz)) { + if (has_base_hz) +@@ -3291,7 +3310,7 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), t->llc.references / interval_float / 1000); + + if (DO_BIC(BIC_LLC_HIT)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), 100.0 * (t->llc.references - t->llc.misses) / t->llc.references); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), pct((t->llc.references - t->llc.misses) / t->llc.references)); + } + + +@@ -3305,7 +3324,7 @@ int format_counters(PER_THREAD_PARAMS) + if (mp->type == COUNTER_USEC) + outp += print_float_value(&printed, delim, t->counter[i] / interval_float / 10000); + else +- outp += print_float_value(&printed, delim, 100.0 * t->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(t->counter[i] / tsc)); + } + } + +@@ -3319,7 +3338,7 @@ int format_counters(PER_THREAD_PARAMS) + if (pp->type == COUNTER_USEC) + outp += print_float_value(&printed, delim, t->perf_counter[i] / interval_float / 10000); + else +- outp += print_float_value(&printed, delim, 100.0 * t->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(t->perf_counter[i] / tsc)); + } + } + +@@ -3333,34 +3352,34 @@ int format_counters(PER_THREAD_PARAMS) + break; + + case PMT_TYPE_XTAL_TIME: +- value_converted = 100.0 * value_raw / crystal_hz / interval_float; ++ value_converted = pct(value_raw / crystal_hz / interval_float); + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: +- value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; ++ value_converted = pct(value_raw / tcore_clock_freq_hz / interval_float); + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), value_converted); + } + } + + /* C1 */ + if (DO_BIC(BIC_CPU_c1)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * t->c1 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(t->c1 / tsc)); + + /* print per-core data only for 1st thread in core */ + if (!is_cpu_first_thread_in_core(t, c)) + goto done; + + if (DO_BIC(BIC_CPU_c3)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->c3 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->c3 / tsc)); + if (DO_BIC(BIC_CPU_c6)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->c6 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->c6 / tsc)); + if (DO_BIC(BIC_CPU_c7)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->c7 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->c7 / tsc)); + + /* Mod%c6 */ + if (DO_BIC(BIC_Mod_c6)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * c->mc6_us / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(c->mc6_us / tsc)); + + if (DO_BIC(BIC_CoreTmp)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), c->core_temp_c); +@@ -3376,7 +3395,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(mp->width, &printed, delim, c->counter[i]); + else if (mp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * c->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(c->counter[i] / tsc)); + } + + /* Added perf Core counters */ +@@ -3386,7 +3405,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, c->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * c->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(c->perf_counter[i] / tsc)); + } + + /* Added PMT Core counters */ +@@ -3399,12 +3418,12 @@ int format_counters(PER_THREAD_PARAMS) + break; + + case PMT_TYPE_XTAL_TIME: +- value_converted = 100.0 * value_raw / crystal_hz / interval_float; ++ value_converted = pct(value_raw / crystal_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: +- value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; ++ value_converted = pct(value_raw / tcore_clock_freq_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + } + } +@@ -3463,46 +3482,41 @@ int format_counters(PER_THREAD_PARAMS) + + /* Totl%C0, Any%C0 GFX%C0 CPUGFX% */ + if (DO_BIC(BIC_Totl_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_wtd_core_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100 * p->pkg_wtd_core_c0 / tsc); /* can exceed 100% */ + if (DO_BIC(BIC_Any_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_any_core_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_any_core_c0 / tsc)); + if (DO_BIC(BIC_GFX_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_any_gfxe_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_any_gfxe_c0 / tsc)); + if (DO_BIC(BIC_CPUGFX)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pkg_both_core_gfxe_c0 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_both_core_gfxe_c0 / tsc)); + + if (DO_BIC(BIC_Pkgpc2)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc2 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc2 / tsc)); + if (DO_BIC(BIC_Pkgpc3)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc3 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc3 / tsc)); + if (DO_BIC(BIC_Pkgpc6)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc6 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc6 / tsc)); + if (DO_BIC(BIC_Pkgpc7)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc7 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc7 / tsc)); + if (DO_BIC(BIC_Pkgpc8)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc8 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc8 / tsc)); + if (DO_BIC(BIC_Pkgpc9)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc9 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc9 / tsc)); + if (DO_BIC(BIC_Pkgpc10)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->pc10 / tsc); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pc10 / tsc)); + + if (DO_BIC(BIC_Diec6)) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100.0 * p->die_c6 / crystal_hz / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->die_c6 / crystal_hz / interval_float)); + + if (DO_BIC(BIC_CPU_LPI)) { + if (p->cpu_lpi >= 0) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- 100.0 * p->cpu_lpi / 1000000.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->cpu_lpi / 1000000.0 / interval_float)); + else + outp += sprintf(outp, "%s(neg)", (printed++ ? delim : "")); + } + if (DO_BIC(BIC_SYS_LPI)) { + if (p->sys_lpi >= 0) +- outp += +- sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- 100.0 * p->sys_lpi / 1000000.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->sys_lpi / 1000000.0 / interval_float)); + else + outp += sprintf(outp, "%s(neg)", (printed++ ? delim : "")); + } +@@ -3556,7 +3570,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (mp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(mp->width, &printed, delim, p->counter[i]); + else if (mp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * p->counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(p->counter[i] / tsc)); + } + + /* Added perf Package Counters */ +@@ -3569,7 +3583,7 @@ int format_counters(PER_THREAD_PARAMS) + else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) +- outp += print_float_value(&printed, delim, 100.0 * p->perf_counter[i] / tsc); ++ outp += print_float_value(&printed, delim, pct(p->perf_counter[i] / tsc)); + } + + /* Added PMT Package Counters */ +@@ -3582,12 +3596,12 @@ int format_counters(PER_THREAD_PARAMS) + break; + + case PMT_TYPE_XTAL_TIME: +- value_converted = 100.0 * value_raw / crystal_hz / interval_float; ++ value_converted = pct(value_raw / crystal_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + break; + + case PMT_TYPE_TCORE_CLOCK: +- value_converted = 100.0 * value_raw / tcore_clock_freq_hz / interval_float; ++ value_converted = pct(value_raw / tcore_clock_freq_hz / interval_float); + outp += print_float_value(&printed, delim, value_converted); + } + } +-- +2.43.0 + diff --git a/SPECS/kernel/0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi b/SPECS/kernel/0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi deleted file mode 100644 index bd2984c16..000000000 --- a/SPECS/kernel/0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi +++ /dev/null @@ -1,195 +0,0 @@ -From b52a89c6eff252b16233c77be9157a29ab770079 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Sun, 4 Jun 2023 23:58:33 -0700 -Subject: [PATCH 19/44] KVM: nVMX: Add FRED-related VMCS field checks - -As with real hardware, nested VMX validates various VMCS fields, including -control and guest/host state fields. This patch adds checks for FRED-related -VMCS fields to support nested FRED functionality. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/vmx/nested.c | 117 +++++++++++++++++++++++++++++++++----- - 1 file changed, 104 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 7b3cd9b6f7c0..7b1a45f06ae6 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3067,6 +3067,8 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) - { - struct vcpu_vmx *vmx = to_vmx(vcpu); -+ bool fred_enabled = (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && -+ (vmcs12->guest_cr4 & X86_CR4_FRED); - - if (CC(!vmx_control_verify(vmcs12->vm_entry_controls, - vmx->nested.msrs.entry_ctls_low, -@@ -3084,22 +3086,11 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - u8 vector = intr_info & INTR_INFO_VECTOR_MASK; - u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; - bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; -+ bool has_nested_exception = vmx->nested.msrs.basic & VMX_BASIC_NESTED_EXCEPTION; - bool urg = nested_cpu_has2(vmcs12, - SECONDARY_EXEC_UNRESTRICTED_GUEST); - bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; - -- /* VM-entry interruption-info field: interruption type */ -- if (CC(intr_type == INTR_TYPE_RESERVED) || -- CC(intr_type == INTR_TYPE_OTHER_EVENT && -- !nested_cpu_supports_monitor_trap_flag(vcpu))) -- return -EINVAL; -- -- /* VM-entry interruption-info field: vector */ -- if (CC(intr_type == INTR_TYPE_NMI_INTR && vector != NMI_VECTOR) || -- CC(intr_type == INTR_TYPE_HARD_EXCEPTION && vector > 31) || -- CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) -- return -EINVAL; -- - /* - * Cannot deliver error code in real mode or if the interrupt - * type is not hardware exception. For other cases, do the -@@ -3124,8 +3115,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - if (CC(intr_info & INTR_INFO_RESVD_BITS_MASK)) - return -EINVAL; - -- /* VM-entry instruction length */ -+ /* -+ * When the CPU enumerates VMX nested-exception support, bit 13 -+ * (set to indicate a nested exception) of the intr info field -+ * may have value 1. Otherwise bit 13 is reserved. -+ */ -+ if (CC(!(has_nested_exception && intr_type == INTR_TYPE_HARD_EXCEPTION) && -+ intr_info & INTR_INFO_NESTED_EXCEPTION_MASK)) -+ return -EINVAL; -+ - switch (intr_type) { -+ case INTR_TYPE_EXT_INTR: -+ break; -+ case INTR_TYPE_RESERVED: -+ return -EINVAL; -+ case INTR_TYPE_NMI_INTR: -+ if (CC(vector != NMI_VECTOR)) -+ return -EINVAL; -+ break; -+ case INTR_TYPE_HARD_EXCEPTION: -+ if (CC(vector > 31)) -+ return -EINVAL; -+ break; - case INTR_TYPE_SOFT_EXCEPTION: - case INTR_TYPE_SOFT_INTR: - case INTR_TYPE_PRIV_SW_EXCEPTION: -@@ -3133,6 +3144,24 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - CC(vmcs12->vm_entry_instruction_len == 0 && - CC(!nested_cpu_has_zero_length_injection(vcpu)))) - return -EINVAL; -+ break; -+ case INTR_TYPE_OTHER_EVENT: -+ switch (vector) { -+ case 0: -+ if (CC(!nested_cpu_supports_monitor_trap_flag(vcpu))) -+ return -EINVAL; -+ break; -+ case 1: -+ case 2: -+ if (CC(!fred_enabled)) -+ return -EINVAL; -+ if (CC(vmcs12->vm_entry_instruction_len > X86_MAX_INSTRUCTION_LENGTH)) -+ return -EINVAL; -+ break; -+ default: -+ return -EINVAL; -+ } -+ break; - } - } - -@@ -3220,9 +3249,29 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - if (ia32e) { - if (CC(!(vmcs12->host_cr4 & X86_CR4_PAE))) - return -EINVAL; -+ if (vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS && -+ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED) { -+ if (CC(vmcs12->host_ia32_fred_config & -+ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || -+ CC(vmcs12->host_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->host_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->host_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->host_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->host_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->host_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_config & PAGE_MASK, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp3, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp3, vcpu))) -+ return -EINVAL; -+ } - } else { - if (CC(vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) || - CC(vmcs12->host_cr4 & X86_CR4_PCIDE) || -+ CC(vmcs12->host_cr4 & X86_CR4_FRED) || - CC((vmcs12->host_rip) >> 32)) - return -EINVAL; - } -@@ -3390,6 +3439,48 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, - CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) - return -EINVAL; - -+ if (ia32e) { -+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED) { -+ if (CC(vmcs12->guest_ia32_fred_config & -+ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || -+ CC(vmcs12->guest_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->guest_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->guest_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || -+ CC(vmcs12->guest_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->guest_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || -+ CC(vmcs12->guest_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_config & PAGE_MASK, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp3, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp1, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp2, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp3, vcpu))) -+ return -EINVAL; -+ } -+ if (vmcs12->guest_cr4 & X86_CR4_FRED) { -+ unsigned int ss_dpl = VMX_AR_DPL(vmcs12->guest_ss_ar_bytes); -+ switch (ss_dpl) { -+ case 0: -+ if (CC(!(vmcs12->guest_cs_ar_bytes & VMX_AR_L_MASK))) -+ return -EINVAL; -+ break; -+ case 1: -+ case 2: -+ return -EINVAL; -+ case 3: -+ if (CC(vmcs12->guest_rflags & X86_EFLAGS_IOPL)) -+ return -EINVAL; -+ if (CC(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_STI)) -+ return -EINVAL; -+ break; -+ } -+ } -+ } else { -+ if (CC(vmcs12->guest_cr4 & X86_CR4_FRED)) -+ return -EINVAL; -+ } -+ - if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { - if (CC(!is_valid_cet_state(vcpu, vmcs12->guest_s_cet, vmcs12->guest_ssp, - vmcs12->guest_ssp_tbl))) --- -2.43.0 - diff --git a/SPECS/kernel/0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi b/SPECS/kernel/0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi new file mode 100644 index 000000000..5bc80d105 --- /dev/null +++ b/SPECS/kernel/0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi @@ -0,0 +1,501 @@ +From a008f98d01a9c82935b67bbe85548ca3a6c2cce5 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 15 Sep 2022 22:45:34 -0700 +Subject: [PATCH 19/44] KVM: nVMX: Handle FRED VMCS fields in nested VMX + context + +Extend nested VMX context management to include FRED-related VMCS fields, +enabling proper handling of FRED state during nested virtualization. + +Because KVM always sets SECONDARY_VM_EXIT_SAVE_IA32_FRED, FRED MSRs are +always saved to vmcs02. However an L1 VMM may choose to clear this bit, +i.e., not to save FRED MSRs to vmcs12. This is not a problem when the L1 +VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, as KVM then immediately loads +host FRED MSRs of vmcs12 to guest FRED MSRs of vmcs01. However if the L1 +VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should retain FRED MSRs +to run the L1 VMM. + +To propagate guest FRED MSRs from vmcs02 to vmcs01, save them in +sync_vmcs02_to_vmcs12() regardless of whether +SECONDARY_VM_EXIT_SAVE_IA32_FRED is set in vmcs12. Then, use the saved +values to set guest FRED MSRs in vmcs01 within load_vmcs12_host_state() +when !nested_cpu_load_host_fred_state(). + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Changes in v9: +* Rebase to kvm-x86/next. +* Guard FRED state save/restore with guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) + (syzbot & Chao). + +Changes in v8: +* Make the newly added FRED fields 64-bit aligned in vmcs12 (Isaku). +* Remove the change to Documentation/virt/kvm/x86/nested-vmx.rst. + +Change in v6: +* Handle FRED MSR pre-vmenter save/restore (Chao Gao). +* Save FRED MSRs of vmcs02 at VM-Exit even an L1 VMM clears + SECONDARY_VM_EXIT_SAVE_IA32_FRED. +* Save FRED MSRs in sync_vmcs02_to_vmcs12() instead of its rare version. + +Change in v5: +* Add TB from Xuelian Guo. + +Changes in v4: +* Advertise VMX nested exception as if the CPU supports it (Chao Gao). +* Split FRED state management controls (Chao Gao). + +Changes in v3: +* Add and use nested_cpu_has_fred(vmcs12) because vmcs02 should be set + from vmcs12 if and only if the field is enabled in L1's VMX config + (Sean Christopherson). +* Fix coding style issues (Sean Christopherson). + +Changes in v2: +* Remove hyperv TLFS related changes (Jeremi Piotrowski). +* Use kvm_cpu_cap_has() instead of cpu_feature_enabled() (Chao Gao). +--- + arch/x86/kvm/vmx/capabilities.h | 5 ++ + arch/x86/kvm/vmx/nested.c | 118 +++++++++++++++++++++++++- + arch/x86/kvm/vmx/nested.h | 22 +++++ + arch/x86/kvm/vmx/vmcs12.c | 18 ++++ + arch/x86/kvm/vmx/vmcs12.h | 37 ++++++++ + arch/x86/kvm/vmx/vmcs_shadow_fields.h | 4 + + arch/x86/kvm/vmx/vmx.h | 41 +++++++++ + 7 files changed, 243 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h +index f390f9f883c30..5eba2530ffb40 100644 +--- a/arch/x86/kvm/vmx/capabilities.h ++++ b/arch/x86/kvm/vmx/capabilities.h +@@ -80,6 +80,11 @@ static inline bool cpu_has_vmx_basic_no_hw_errcode_cc(void) + return vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; + } + ++static inline bool cpu_has_vmx_nested_exception(void) ++{ ++ return vmcs_config.basic & VMX_BASIC_NESTED_EXCEPTION; ++} ++ + static inline bool cpu_has_virtual_nmis(void) + { + return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS && +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index b994f264acc0a..da49de1800609 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -705,6 +705,9 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, + + nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, + MSR_KERNEL_GS_BASE, MSR_TYPE_RW); ++ ++ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, ++ MSR_IA32_FRED_RSP0, MSR_TYPE_RW); + #endif + nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, + MSR_IA32_SPEC_CTRL, MSR_TYPE_RW); +@@ -1291,9 +1294,11 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) + const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT | + VMX_BASIC_INOUT | + VMX_BASIC_TRUE_CTLS | +- VMX_BASIC_NO_HW_ERROR_CODE_CC; ++ VMX_BASIC_NO_HW_ERROR_CODE_CC | ++ VMX_BASIC_NESTED_EXCEPTION; + +- const u64 reserved_bits = GENMASK_ULL(63, 57) | ++ const u64 reserved_bits = GENMASK_ULL(63, 59) | ++ BIT_ULL(57) | + GENMASK_ULL(47, 45) | + BIT_ULL(31); + +@@ -2545,6 +2550,8 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 + vmcs12->vm_entry_instruction_len); + vmcs_write32(GUEST_INTERRUPTIBILITY_INFO, + vmcs12->guest_interruptibility_info); ++ if (cpu_has_vmx_fred()) ++ vmcs_write64(INJECTED_EVENT_DATA, vmcs12->injected_event_data); + vmx->loaded_vmcs->nmi_known_unmasked = + !(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_NMI); + } else { +@@ -2699,6 +2706,18 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) + vmcs12->guest_ssp, vmcs12->guest_ssp_tbl); + + set_cr4_guest_host_mask(vmx); ++ ++ if (guest_cpu_cap_has(&vmx->vcpu, X86_FEATURE_FRED) && ++ nested_cpu_load_guest_fred_state(vmcs12)) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->guest_ia32_fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->guest_ia32_fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->guest_ia32_fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->guest_ia32_fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->guest_ia32_fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->guest_ia32_fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->guest_ia32_fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->guest_ia32_fred_ssp3); ++ } + } + + /* +@@ -2765,6 +2784,18 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, + vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat); + } + ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) && ++ (!vmx->nested.nested_run_pending || !nested_cpu_load_guest_fred_state(vmcs12))) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.pre_vmenter_fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.pre_vmenter_fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.pre_vmenter_fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.pre_vmenter_fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.pre_vmenter_fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.pre_vmenter_fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.pre_vmenter_fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.pre_vmenter_fred_ssp3); ++ } ++ + vcpu->arch.tsc_offset = kvm_calc_nested_tsc_offset( + vcpu->arch.l1_tsc_offset, + vmx_get_l2_tsc_offset(vcpu), +@@ -3679,6 +3710,18 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, + &vmx->nested.pre_vmenter_ssp, + &vmx->nested.pre_vmenter_ssp_tbl); + ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED) && ++ (!vmx->nested.nested_run_pending || !nested_cpu_load_guest_fred_state(vmcs12))) { ++ vmx->nested.pre_vmenter_fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); ++ vmx->nested.pre_vmenter_fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); ++ vmx->nested.pre_vmenter_fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); ++ vmx->nested.pre_vmenter_fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); ++ vmx->nested.pre_vmenter_fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); ++ vmx->nested.pre_vmenter_fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); ++ vmx->nested.pre_vmenter_fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); ++ vmx->nested.pre_vmenter_fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); ++ } ++ + /* + * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* + * nested early checks are disabled. In the event of a "late" VM-Fail, +@@ -3986,6 +4029,8 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, + u32 idt_vectoring; + unsigned int nr; + ++ vmcs12->original_event_data = 0; ++ + /* + * Per the SDM, VM-Exits due to double and triple faults are never + * considered to occur during event delivery, even if the double/triple +@@ -4024,6 +4069,13 @@ static void vmcs12_save_pending_event(struct kvm_vcpu *vcpu, + vcpu->arch.exception.error_code; + } + ++ if ((vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && ++ (vmcs12->guest_cr4 & X86_CR4_FRED) && ++ (vcpu->arch.exception.nested)) ++ idt_vectoring |= VECTORING_INFO_NESTED_EXCEPTION_MASK; ++ ++ vmcs12->original_event_data = vcpu->arch.exception.event_data; ++ + vmcs12->idt_vectoring_info_field = idt_vectoring; + } else if (vcpu->arch.nmi_injected) { + vmcs12->idt_vectoring_info_field = +@@ -4766,6 +4818,28 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12) + vmcs_read_cet_state(&vmx->vcpu, &vmcs12->guest_s_cet, + &vmcs12->guest_ssp, + &vmcs12->guest_ssp_tbl); ++ ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) { ++ vmx->nested.fred_msr_at_vmexit.fred_config = vmcs_read64(GUEST_IA32_FRED_CONFIG); ++ vmx->nested.fred_msr_at_vmexit.fred_rsp1 = vmcs_read64(GUEST_IA32_FRED_RSP1); ++ vmx->nested.fred_msr_at_vmexit.fred_rsp2 = vmcs_read64(GUEST_IA32_FRED_RSP2); ++ vmx->nested.fred_msr_at_vmexit.fred_rsp3 = vmcs_read64(GUEST_IA32_FRED_RSP3); ++ vmx->nested.fred_msr_at_vmexit.fred_stklvls = vmcs_read64(GUEST_IA32_FRED_STKLVLS); ++ vmx->nested.fred_msr_at_vmexit.fred_ssp1 = vmcs_read64(GUEST_IA32_FRED_SSP1); ++ vmx->nested.fred_msr_at_vmexit.fred_ssp2 = vmcs_read64(GUEST_IA32_FRED_SSP2); ++ vmx->nested.fred_msr_at_vmexit.fred_ssp3 = vmcs_read64(GUEST_IA32_FRED_SSP3); ++ ++ if (nested_cpu_save_guest_fred_state(vmcs12)) { ++ vmcs12->guest_ia32_fred_config = vmx->nested.fred_msr_at_vmexit.fred_config; ++ vmcs12->guest_ia32_fred_rsp1 = vmx->nested.fred_msr_at_vmexit.fred_rsp1; ++ vmcs12->guest_ia32_fred_rsp2 = vmx->nested.fred_msr_at_vmexit.fred_rsp2; ++ vmcs12->guest_ia32_fred_rsp3 = vmx->nested.fred_msr_at_vmexit.fred_rsp3; ++ vmcs12->guest_ia32_fred_stklvls = vmx->nested.fred_msr_at_vmexit.fred_stklvls; ++ vmcs12->guest_ia32_fred_ssp1 = vmx->nested.fred_msr_at_vmexit.fred_ssp1; ++ vmcs12->guest_ia32_fred_ssp2 = vmx->nested.fred_msr_at_vmexit.fred_ssp2; ++ vmcs12->guest_ia32_fred_ssp3 = vmx->nested.fred_msr_at_vmexit.fred_ssp3; ++ } ++ } + } + + /* +@@ -4810,6 +4884,21 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, + + vmcs12->vm_exit_intr_info = exit_intr_info; + vmcs12->vm_exit_instruction_len = exit_insn_len; ++ ++ /* ++ * When there is a valid original event, the exiting event is a nested ++ * event during delivery of the earlier original event. ++ * ++ * FRED event delivery reflects this relationship by setting the value ++ * of the nested exception bit of VM-exit interruption information ++ * (aka exiting-event identification) to that of the valid bit of the ++ * IDT-vectoring information (aka original-event identification). ++ */ ++ if ((vmcs12->idt_vectoring_info_field & VECTORING_INFO_VALID_MASK) && ++ (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && ++ (vmcs12->guest_cr4 & X86_CR4_FRED)) ++ vmcs12->vm_exit_intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK; ++ + vmcs12->vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO); + + /* +@@ -4838,6 +4927,7 @@ static void prepare_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, + static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12) + { ++ struct vcpu_vmx *vmx = to_vmx(vcpu); + enum vm_entry_failure_code ignored; + struct kvm_segment seg; + +@@ -4912,6 +5002,28 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, + WARN_ON_ONCE(__kvm_emulate_msr_write(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, + vmcs12->host_ia32_perf_global_ctrl)); + ++ if (guest_cpu_cap_has(vcpu, X86_FEATURE_FRED)) { ++ if (nested_cpu_load_host_fred_state(vmcs12)) { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmcs12->host_ia32_fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmcs12->host_ia32_fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmcs12->host_ia32_fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmcs12->host_ia32_fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmcs12->host_ia32_fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmcs12->host_ia32_fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmcs12->host_ia32_fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmcs12->host_ia32_fred_ssp3); ++ } else { ++ vmcs_write64(GUEST_IA32_FRED_CONFIG, vmx->nested.fred_msr_at_vmexit.fred_config); ++ vmcs_write64(GUEST_IA32_FRED_RSP1, vmx->nested.fred_msr_at_vmexit.fred_rsp1); ++ vmcs_write64(GUEST_IA32_FRED_RSP2, vmx->nested.fred_msr_at_vmexit.fred_rsp2); ++ vmcs_write64(GUEST_IA32_FRED_RSP3, vmx->nested.fred_msr_at_vmexit.fred_rsp3); ++ vmcs_write64(GUEST_IA32_FRED_STKLVLS, vmx->nested.fred_msr_at_vmexit.fred_stklvls); ++ vmcs_write64(GUEST_IA32_FRED_SSP1, vmx->nested.fred_msr_at_vmexit.fred_ssp1); ++ vmcs_write64(GUEST_IA32_FRED_SSP2, vmx->nested.fred_msr_at_vmexit.fred_ssp2); ++ vmcs_write64(GUEST_IA32_FRED_SSP3, vmx->nested.fred_msr_at_vmexit.fred_ssp3); ++ } ++ } ++ + /* Set L1 segment info according to Intel SDM + 27.5.2 Loading Host Segment and Descriptor-Table Registers */ + seg = (struct kvm_segment) { +@@ -7398,6 +7510,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs) + msrs->basic |= VMX_BASIC_INOUT; + if (cpu_has_vmx_basic_no_hw_errcode_cc()) + msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC; ++ if (cpu_has_vmx_nested_exception()) ++ msrs->basic |= VMX_BASIC_NESTED_EXCEPTION; + } + + static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs) +diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h +index 983484d42ebf9..a99d3d83d58e2 100644 +--- a/arch/x86/kvm/vmx/nested.h ++++ b/arch/x86/kvm/vmx/nested.h +@@ -249,6 +249,11 @@ static inline bool nested_cpu_has_save_preemption_timer(struct vmcs12 *vmcs12) + VM_EXIT_SAVE_VMX_PREEMPTION_TIMER; + } + ++static inline bool nested_cpu_has_secondary_vm_exit_controls(struct vmcs12 *vmcs12) ++{ ++ return vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS; ++} ++ + static inline bool nested_exit_on_nmi(struct kvm_vcpu *vcpu) + { + return nested_cpu_has_nmi_exiting(get_vmcs12(vcpu)); +@@ -269,6 +274,23 @@ static inline bool nested_cpu_has_encls_exit(struct vmcs12 *vmcs12) + return nested_cpu_has2(vmcs12, SECONDARY_EXEC_ENCLS_EXITING); + } + ++static inline bool nested_cpu_load_guest_fred_state(struct vmcs12 *vmcs12) ++{ ++ return vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED; ++} ++ ++static inline bool nested_cpu_save_guest_fred_state(struct vmcs12 *vmcs12) ++{ ++ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && ++ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_SAVE_IA32_FRED; ++} ++ ++static inline bool nested_cpu_load_host_fred_state(struct vmcs12 *vmcs12) ++{ ++ return nested_cpu_has_secondary_vm_exit_controls(vmcs12) && ++ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED; ++} ++ + /* + * if fixed0[i] == 1: val[i] must be 1 + * if fixed1[i] == 0: val[i] must be 0 +diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c +index 3b01175f392ae..9691e709061ff 100644 +--- a/arch/x86/kvm/vmx/vmcs12.c ++++ b/arch/x86/kvm/vmx/vmcs12.c +@@ -67,6 +67,24 @@ const unsigned short vmcs12_field_offsets[] = { + FIELD64(HOST_IA32_EFER, host_ia32_efer), + FIELD64(HOST_IA32_PERF_GLOBAL_CTRL, host_ia32_perf_global_ctrl), + FIELD64(SECONDARY_VM_EXIT_CONTROLS, secondary_vm_exit_controls), ++ FIELD64(INJECTED_EVENT_DATA, injected_event_data), ++ FIELD64(ORIGINAL_EVENT_DATA, original_event_data), ++ FIELD64(GUEST_IA32_FRED_CONFIG, guest_ia32_fred_config), ++ FIELD64(GUEST_IA32_FRED_RSP1, guest_ia32_fred_rsp1), ++ FIELD64(GUEST_IA32_FRED_RSP2, guest_ia32_fred_rsp2), ++ FIELD64(GUEST_IA32_FRED_RSP3, guest_ia32_fred_rsp3), ++ FIELD64(GUEST_IA32_FRED_STKLVLS, guest_ia32_fred_stklvls), ++ FIELD64(GUEST_IA32_FRED_SSP1, guest_ia32_fred_ssp1), ++ FIELD64(GUEST_IA32_FRED_SSP2, guest_ia32_fred_ssp2), ++ FIELD64(GUEST_IA32_FRED_SSP3, guest_ia32_fred_ssp3), ++ FIELD64(HOST_IA32_FRED_CONFIG, host_ia32_fred_config), ++ FIELD64(HOST_IA32_FRED_RSP1, host_ia32_fred_rsp1), ++ FIELD64(HOST_IA32_FRED_RSP2, host_ia32_fred_rsp2), ++ FIELD64(HOST_IA32_FRED_RSP3, host_ia32_fred_rsp3), ++ FIELD64(HOST_IA32_FRED_STKLVLS, host_ia32_fred_stklvls), ++ FIELD64(HOST_IA32_FRED_SSP1, host_ia32_fred_ssp1), ++ FIELD64(HOST_IA32_FRED_SSP2, host_ia32_fred_ssp2), ++ FIELD64(HOST_IA32_FRED_SSP3, host_ia32_fred_ssp3), + FIELD(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control), + FIELD(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control), + FIELD(EXCEPTION_BITMAP, exception_bitmap), +diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h +index fa5306dc0311f..051016a3afba4 100644 +--- a/arch/x86/kvm/vmx/vmcs12.h ++++ b/arch/x86/kvm/vmx/vmcs12.h +@@ -191,6 +191,25 @@ struct __packed vmcs12 { + u16 host_gs_selector; + u16 host_tr_selector; + u16 guest_pml_index; ++ u16 padding16[1]; /* align to 64-bit boundary */ ++ u64 guest_ia32_fred_config; ++ u64 guest_ia32_fred_rsp1; ++ u64 guest_ia32_fred_rsp2; ++ u64 guest_ia32_fred_rsp3; ++ u64 guest_ia32_fred_stklvls; ++ u64 guest_ia32_fred_ssp1; ++ u64 guest_ia32_fred_ssp2; ++ u64 guest_ia32_fred_ssp3; ++ u64 host_ia32_fred_config; ++ u64 host_ia32_fred_rsp1; ++ u64 host_ia32_fred_rsp2; ++ u64 host_ia32_fred_rsp3; ++ u64 host_ia32_fred_stklvls; ++ u64 host_ia32_fred_ssp1; ++ u64 host_ia32_fred_ssp2; ++ u64 host_ia32_fred_ssp3; ++ u64 injected_event_data; ++ u64 original_event_data; + }; + + /* +@@ -373,6 +392,24 @@ static inline void vmx_check_vmcs12_offsets(void) + CHECK_OFFSET(host_gs_selector, 992); + CHECK_OFFSET(host_tr_selector, 994); + CHECK_OFFSET(guest_pml_index, 996); ++ CHECK_OFFSET(guest_ia32_fred_config, 1000); ++ CHECK_OFFSET(guest_ia32_fred_rsp1, 1008); ++ CHECK_OFFSET(guest_ia32_fred_rsp2, 1016); ++ CHECK_OFFSET(guest_ia32_fred_rsp3, 1024); ++ CHECK_OFFSET(guest_ia32_fred_stklvls, 1032); ++ CHECK_OFFSET(guest_ia32_fred_ssp1, 1040); ++ CHECK_OFFSET(guest_ia32_fred_ssp2, 1048); ++ CHECK_OFFSET(guest_ia32_fred_ssp3, 1056); ++ CHECK_OFFSET(host_ia32_fred_config, 1064); ++ CHECK_OFFSET(host_ia32_fred_rsp1, 1072); ++ CHECK_OFFSET(host_ia32_fred_rsp2, 1080); ++ CHECK_OFFSET(host_ia32_fred_rsp3, 1088); ++ CHECK_OFFSET(host_ia32_fred_stklvls, 1096); ++ CHECK_OFFSET(host_ia32_fred_ssp1, 1104); ++ CHECK_OFFSET(host_ia32_fred_ssp2, 1112); ++ CHECK_OFFSET(host_ia32_fred_ssp3, 1120); ++ CHECK_OFFSET(injected_event_data, 1128); ++ CHECK_OFFSET(original_event_data, 1136); + } + + extern const unsigned short vmcs12_field_offsets[]; +diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +index cad128d1657be..da338327c2b34 100644 +--- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h ++++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +@@ -74,6 +74,10 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) + /* 64-bit */ + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) ++SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) ++SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) ++SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) ++SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) + + #undef SHADOW_FIELD_RO + #undef SHADOW_FIELD_RW +diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h +index c4a3b28553fbd..36dcc888e5c63 100644 +--- a/arch/x86/kvm/vmx/vmx.h ++++ b/arch/x86/kvm/vmx/vmx.h +@@ -67,6 +67,37 @@ struct pt_desc { + struct pt_ctx guest; + }; + ++/* ++ * Used to snapshot FRED MSRs that may NOT be saved to vmcs12 as specified ++ * in the VM-Exit controls of vmcs12 configured by L1 VMM. ++ * ++ * FRED MSRs are *always* saved into vmcs02 because KVM always sets ++ * SECONDARY_VM_EXIT_SAVE_IA32_FRED. However an L1 VMM may choose to clear ++ * this bit, resulting in FRED MSRs not being propagated to vmcs12 from ++ * vmcs02. When the L1 VMM sets SECONDARY_VM_EXIT_LOAD_IA32_FRED, this is ++ * not a problem, since KVM then immediately loads the host FRED MSRs of ++ * vmcs12 to the guest FRED MSRs of vmcs01. ++ * ++ * But if the L1 VMM clears SECONDARY_VM_EXIT_LOAD_IA32_FRED, KVM should ++ * retain the FRED MSRs, i.e., propagate the guest FRED MSRs of vmcs02 to ++ * the guest FRED MSRs of vmcs01. ++ * ++ * This structure stores guest FRED MSRs that an L1 VMM opts not to save ++ * during VM-Exits from L2 to L1. These MSRs may still be retained for ++ * running the L1 VMM if SECONDARY_VM_EXIT_LOAD_IA32_FRED is cleared in ++ * vmcs12. ++ */ ++struct fred_msr_at_vmexit { ++ u64 fred_config; ++ u64 fred_rsp1; ++ u64 fred_rsp2; ++ u64 fred_rsp3; ++ u64 fred_stklvls; ++ u64 fred_ssp1; ++ u64 fred_ssp2; ++ u64 fred_ssp3; ++}; ++ + /* + * The nested_vmx structure is part of vcpu_vmx, and holds information we need + * for correct emulation of VMX (i.e., nested VMX) on this vcpu. +@@ -184,6 +215,16 @@ struct nested_vmx { + u64 pre_vmenter_s_cet; + u64 pre_vmenter_ssp; + u64 pre_vmenter_ssp_tbl; ++ u64 pre_vmenter_fred_config; ++ u64 pre_vmenter_fred_rsp1; ++ u64 pre_vmenter_fred_rsp2; ++ u64 pre_vmenter_fred_rsp3; ++ u64 pre_vmenter_fred_stklvls; ++ u64 pre_vmenter_fred_ssp1; ++ u64 pre_vmenter_fred_ssp2; ++ u64 pre_vmenter_fred_ssp3; ++ ++ struct fred_msr_at_vmexit fred_msr_at_vmexit; + + /* to migrate it to L1 if L2 writes to L1's CR8 directly */ + int l1_tpr_threshold; +-- +2.43.0 + diff --git a/SPECS/kernel/0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet b/SPECS/kernel/0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet deleted file mode 100644 index 849bd5e02..000000000 --- a/SPECS/kernel/0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet +++ /dev/null @@ -1,127 +0,0 @@ -From 4b93c7a331f8b3a34bffa76517f950f60ef3b401 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:49 -0700 -Subject: [PATCH 19/24] KVM: x86: Don't emulate instructions guarded by CET - -Don't emulate the branch instructions, e.g., CALL/RET/JMP etc., when CET -is active in guest, return KVM_INTERNAL_ERROR_EMULATION to userspace to -handle it. - -KVM doesn't emulate CPU behaviors to check CET protected stuffs while -emulating guest instructions, instead it stops emulation on detecting -the instructions in process are CET protected. By doing so, it can avoid -generating bogus #CP in guest and preventing CET protected execution flow -subversion from guest side. - -Suggested-by: Chao Gao -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/emulate.c | 46 ++++++++++++++++++++++++++++++++---------- - 1 file changed, 35 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c -index 1349e278cd2a..80b9d1e4a50a 100644 ---- a/arch/x86/kvm/emulate.c -+++ b/arch/x86/kvm/emulate.c -@@ -178,6 +178,8 @@ - #define IncSP ((u64)1 << 54) /* SP is incremented before ModRM calc */ - #define TwoMemOp ((u64)1 << 55) /* Instruction has two memory operand */ - #define IsBranch ((u64)1 << 56) /* Instruction is considered a branch. */ -+#define ShadowStack ((u64)1 << 57) /* Instruction protected by Shadow Stack. */ -+#define IndirBrnTrk ((u64)1 << 58) /* Instruction protected by IBT. */ - - #define DstXacc (DstAccLo | SrcAccHi | SrcWrite) - -@@ -4068,9 +4070,11 @@ static const struct opcode group4[] = { - static const struct opcode group5[] = { - F(DstMem | SrcNone | Lock, em_inc), - F(DstMem | SrcNone | Lock, em_dec), -- I(SrcMem | NearBranch | IsBranch, em_call_near_abs), -- I(SrcMemFAddr | ImplicitOps | IsBranch, em_call_far), -- I(SrcMem | NearBranch | IsBranch, em_jmp_abs), -+ I(SrcMem | NearBranch | IsBranch | ShadowStack | IndirBrnTrk, -+ em_call_near_abs), -+ I(SrcMemFAddr | ImplicitOps | IsBranch | ShadowStack | IndirBrnTrk, -+ em_call_far), -+ I(SrcMem | NearBranch | IsBranch | IndirBrnTrk, em_jmp_abs), - I(SrcMemFAddr | ImplicitOps | IsBranch, em_jmp_far), - I(SrcMem | Stack | TwoMemOp, em_push), D(Undefined), - }; -@@ -4332,11 +4336,11 @@ static const struct opcode opcode_table[256] = { - /* 0xC8 - 0xCF */ - I(Stack | SrcImmU16 | Src2ImmByte | IsBranch, em_enter), - I(Stack | IsBranch, em_leave), -- I(ImplicitOps | SrcImmU16 | IsBranch, em_ret_far_imm), -- I(ImplicitOps | IsBranch, em_ret_far), -- D(ImplicitOps | IsBranch), DI(SrcImmByte | IsBranch, intn), -+ I(ImplicitOps | SrcImmU16 | IsBranch | ShadowStack, em_ret_far_imm), -+ I(ImplicitOps | IsBranch | ShadowStack, em_ret_far), -+ D(ImplicitOps | IsBranch), DI(SrcImmByte | IsBranch | ShadowStack, intn), - D(ImplicitOps | No64 | IsBranch), -- II(ImplicitOps | IsBranch, em_iret, iret), -+ II(ImplicitOps | IsBranch | ShadowStack, em_iret, iret), - /* 0xD0 - 0xD7 */ - G(Src2One | ByteOp, group2), G(Src2One, group2), - G(Src2CL | ByteOp, group2), G(Src2CL, group2), -@@ -4352,7 +4356,7 @@ static const struct opcode opcode_table[256] = { - I2bvIP(SrcImmUByte | DstAcc, em_in, in, check_perm_in), - I2bvIP(SrcAcc | DstImmUByte, em_out, out, check_perm_out), - /* 0xE8 - 0xEF */ -- I(SrcImm | NearBranch | IsBranch, em_call), -+ I(SrcImm | NearBranch | IsBranch | ShadowStack, em_call), - D(SrcImm | ImplicitOps | NearBranch | IsBranch), - I(SrcImmFAddr | No64 | IsBranch, em_jmp_far), - D(SrcImmByte | ImplicitOps | NearBranch | IsBranch), -@@ -4371,7 +4375,8 @@ static const struct opcode opcode_table[256] = { - static const struct opcode twobyte_table[256] = { - /* 0x00 - 0x0F */ - G(0, group6), GD(0, &group7), N, N, -- N, I(ImplicitOps | EmulateOnUD | IsBranch, em_syscall), -+ N, I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack | IndirBrnTrk, -+ em_syscall), - II(ImplicitOps | Priv, em_clts, clts), N, - DI(ImplicitOps | Priv, invd), DI(ImplicitOps | Priv, wbinvd), N, N, - N, D(ImplicitOps | ModRM | SrcMem | NoAccess), N, N, -@@ -4402,8 +4407,9 @@ static const struct opcode twobyte_table[256] = { - IIP(ImplicitOps, em_rdtsc, rdtsc, check_rdtsc), - II(ImplicitOps | Priv, em_rdmsr, rdmsr), - IIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc), -- I(ImplicitOps | EmulateOnUD | IsBranch, em_sysenter), -- I(ImplicitOps | Priv | EmulateOnUD | IsBranch, em_sysexit), -+ I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack | IndirBrnTrk, -+ em_sysenter), -+ I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack, em_sysexit), - N, N, - N, N, N, N, N, N, N, N, - /* 0x40 - 0x4F */ -@@ -4941,6 +4947,24 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int - if (ctxt->d == 0) - return EMULATION_FAILED; - -+ if (ctxt->ops->get_cr(ctxt, 4) & X86_CR4_CET) { -+ u64 u_cet, s_cet; -+ bool stop_em; -+ -+ if (ctxt->ops->get_msr(ctxt, MSR_IA32_U_CET, &u_cet) || -+ ctxt->ops->get_msr(ctxt, MSR_IA32_S_CET, &s_cet)) -+ return EMULATION_FAILED; -+ -+ stop_em = ((u_cet & CET_SHSTK_EN) || (s_cet & CET_SHSTK_EN)) && -+ (opcode.flags & ShadowStack); -+ -+ stop_em |= ((u_cet & CET_ENDBR_EN) || (s_cet & CET_ENDBR_EN)) && -+ (opcode.flags & IndirBrnTrk); -+ -+ if (stop_em) -+ return EMULATION_FAILED; -+ } -+ - ctxt->execute = opcode.u.execute; - - if (unlikely(emulation_type & EMULTYPE_TRAP_UD) && --- -2.43.0 - diff --git a/SPECS/kernel/0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf b/SPECS/kernel/0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf deleted file mode 100644 index c1a646005..000000000 --- a/SPECS/kernel/0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf +++ /dev/null @@ -1,323 +0,0 @@ -From 6ef22e5aced242e831b90e56ad3aaf15d611b8a7 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 05:53:23 +0000 -Subject: [PATCH 019/100] perf/x86/intel: Setup PEBS data configuration and - enable legacy groups - -Different with legacy PEBS, arch-PEBS provides per-counter PEBS data -configuration by programing MSR IA32_PMC_GPx/FXx_CFG_C MSRs. - -This patch obtains PEBS data configuration from event attribute and then -writes the PEBS data configuration to MSR IA32_PMC_GPx/FXx_CFG_C and -enable corresponding PEBS groups. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 127 +++++++++++++++++++++++++++++++ - arch/x86/events/intel/ds.c | 17 +++++ - arch/x86/events/perf_event.h | 12 +++ - arch/x86/include/asm/intel_ds.h | 7 ++ - arch/x86/include/asm/msr-index.h | 8 ++ - 5 files changed, 171 insertions(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index b91c8f24c8f6..fabe2e750a0b 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -2563,6 +2563,39 @@ static void intel_pmu_disable_fixed(struct perf_event *event) - cpuc->fixed_ctrl_val &= ~mask; - } - -+static inline void __intel_pmu_update_event_ext(int idx, u64 ext) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ u32 msr = idx < INTEL_PMC_IDX_FIXED ? -+ x86_pmu_cfg_c_addr(idx, true) : -+ x86_pmu_cfg_c_addr(idx - INTEL_PMC_IDX_FIXED, false); -+ -+ cpuc->cfg_c_val[idx] = ext; -+ wrmsrq(msr, ext); -+} -+ -+static void intel_pmu_disable_event_ext(struct perf_event *event) -+{ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ /* -+ * Only clear CFG_C MSR for PEBS counter group events, -+ * it avoids the HW counter's value to be added into -+ * other PEBS records incorrectly after PEBS counter -+ * group events are disabled. -+ * -+ * For other events, it's unnecessary to clear CFG_C MSRs -+ * since CFG_C doesn't take effect if counter is in -+ * disabled state. That helps to reduce the WRMSR overhead -+ * in context switches. -+ */ -+ if (!is_pebs_counter_event_group(event)) -+ return; -+ -+ __intel_pmu_update_event_ext(event->hw.idx, 0); -+} -+ - static void intel_pmu_disable_event(struct perf_event *event) - { - struct hw_perf_event *hwc = &event->hw; -@@ -2571,9 +2604,12 @@ static void intel_pmu_disable_event(struct perf_event *event) - switch (idx) { - case 0 ... INTEL_PMC_IDX_FIXED - 1: - intel_clear_masks(event, idx); -+ intel_pmu_disable_event_ext(event); - x86_pmu_disable_event(event); - break; - case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1: -+ intel_pmu_disable_event_ext(event); -+ fallthrough; - case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END: - intel_pmu_disable_fixed(event); - break; -@@ -2944,6 +2980,67 @@ static void intel_pmu_enable_acr(struct perf_event *event) - - DEFINE_STATIC_CALL_NULL(intel_pmu_enable_acr_event, intel_pmu_enable_acr); - -+static void intel_pmu_enable_event_ext(struct perf_event *event) -+{ -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -+ struct hw_perf_event *hwc = &event->hw; -+ union arch_pebs_index cached, index; -+ struct arch_pebs_cap cap; -+ u64 ext = 0; -+ -+ if (!x86_pmu.arch_pebs) -+ return; -+ -+ cap = hybrid(cpuc->pmu, arch_pebs_cap); -+ -+ if (event->attr.precise_ip) { -+ u64 pebs_data_cfg = intel_get_arch_pebs_data_config(event); -+ -+ ext |= ARCH_PEBS_EN; -+ if (hwc->flags & PERF_X86_EVENT_AUTO_RELOAD) -+ ext |= (-hwc->sample_period) & ARCH_PEBS_RELOAD; -+ -+ if (pebs_data_cfg && cap.caps) { -+ if (pebs_data_cfg & PEBS_DATACFG_MEMINFO) -+ ext |= ARCH_PEBS_AUX & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_GP) -+ ext |= ARCH_PEBS_GPR & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_XMMS) -+ ext |= ARCH_PEBS_VECR_XMM & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_LBRS) -+ ext |= ARCH_PEBS_LBR & cap.caps; -+ } -+ -+ if (cpuc->n_pebs == cpuc->n_large_pebs) -+ index.split.thresh = ARCH_PEBS_THRESH_MUL; -+ else -+ index.split.thresh = ARCH_PEBS_THRESH_SINGLE; -+ -+ rdmsrq(MSR_IA32_PEBS_INDEX, cached.full); -+ if (index.split.thresh != cached.split.thresh || !cached.split.en) { -+ if (cached.split.thresh == ARCH_PEBS_THRESH_MUL && -+ cached.split.wr > 0) { -+ /* -+ * Large PEBS was enabled. -+ * Drain PEBS buffer before applying the single PEBS. -+ */ -+ intel_pmu_drain_pebs_buffer(); -+ } else { -+ index.split.wr = 0; -+ index.split.full = 0; -+ index.split.en = 1; -+ wrmsrq(MSR_IA32_PEBS_INDEX, index.full); -+ } -+ } -+ } -+ -+ if (cpuc->cfg_c_val[hwc->idx] != ext) -+ __intel_pmu_update_event_ext(hwc->idx, ext); -+} -+ - static void intel_pmu_enable_event(struct perf_event *event) - { - u64 enable_mask = ARCH_PERFMON_EVENTSEL_ENABLE; -@@ -2959,10 +3056,12 @@ static void intel_pmu_enable_event(struct perf_event *event) - enable_mask |= ARCH_PERFMON_EVENTSEL_BR_CNTR; - intel_set_masks(event, idx); - static_call_cond(intel_pmu_enable_acr_event)(event); -+ intel_pmu_enable_event_ext(event); - __x86_pmu_enable_event(hwc, enable_mask); - break; - case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1: - static_call_cond(intel_pmu_enable_acr_event)(event); -+ intel_pmu_enable_event_ext(event); - fallthrough; - case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END: - intel_pmu_enable_fixed(event); -@@ -5440,6 +5539,29 @@ static inline bool intel_pmu_broken_perf_cap(void) - return false; - } - -+static inline void __intel_update_pmu_caps(struct pmu *pmu) -+{ -+ struct pmu *dest_pmu = pmu ? pmu : x86_get_pmu(smp_processor_id()); -+ -+ if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_XMM) -+ dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS; -+} -+ -+static inline void __intel_update_large_pebs_flags(struct pmu *pmu) -+{ -+ u64 caps = hybrid(pmu, arch_pebs_cap).caps; -+ -+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_TIME; -+ if (caps & ARCH_PEBS_LBR) -+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_BRANCH_STACK; -+ -+ if (!(caps & ARCH_PEBS_AUX)) -+ x86_pmu.large_pebs_flags &= ~PERF_SAMPLE_DATA_SRC; -+ if (!(caps & ARCH_PEBS_GPR)) -+ x86_pmu.large_pebs_flags &= -+ ~(PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER); -+} -+ - static void update_pmu_cap(struct pmu *pmu) - { - unsigned int eax, ebx, ecx, edx; -@@ -5480,6 +5602,9 @@ static void update_pmu_cap(struct pmu *pmu) - &eax, &ebx, &ecx, &edx); - hybrid(pmu, arch_pebs_cap).counters = ((u64)ecx << 32) | eax; - hybrid(pmu, arch_pebs_cap).pdists = ((u64)edx << 32) | ebx; -+ -+ __intel_update_pmu_caps(pmu); -+ __intel_update_large_pebs_flags(pmu); - } else { - WARN_ON(x86_pmu.arch_pebs == 1); - x86_pmu.arch_pebs = 0; -@@ -5640,6 +5765,8 @@ static void intel_pmu_cpu_starting(int cpu) - } - } - -+ __intel_update_pmu_caps(cpuc->pmu); -+ - if (!cpuc->shared_regs) - return; - -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index f8b5275409d6..530f5a8bc9d2 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1513,6 +1513,18 @@ pebs_update_state(bool needed_cb, struct cpu_hw_events *cpuc, - } - } - -+u64 intel_get_arch_pebs_data_config(struct perf_event *event) -+{ -+ u64 pebs_data_cfg = 0; -+ -+ if (WARN_ON(event->hw.idx < 0 || event->hw.idx >= X86_PMC_IDX_MAX)) -+ return 0; -+ -+ pebs_data_cfg |= pebs_update_adaptive_cfg(event); -+ -+ return pebs_data_cfg; -+} -+ - void intel_pmu_pebs_add(struct perf_event *event) - { - struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); -@@ -2969,6 +2981,11 @@ static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs, - - index.split.wr = 0; - index.split.full = 0; -+ index.split.en = 1; -+ if (cpuc->n_pebs == cpuc->n_large_pebs) -+ index.split.thresh = ARCH_PEBS_THRESH_MUL; -+ else -+ index.split.thresh = ARCH_PEBS_THRESH_SINGLE; - wrmsrq(MSR_IA32_PEBS_INDEX, index.full); - } - -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index 82e8c20611b9..db4ec2975de4 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -304,6 +304,8 @@ struct cpu_hw_events { - /* Intel ACR configuration */ - u64 acr_cfg_b[X86_PMC_IDX_MAX]; - u64 acr_cfg_c[X86_PMC_IDX_MAX]; -+ /* Cached CFG_C values */ -+ u64 cfg_c_val[X86_PMC_IDX_MAX]; - - /* - * Intel LBR bits -@@ -1216,6 +1218,14 @@ static inline unsigned int x86_pmu_fixed_ctr_addr(int index) - x86_pmu.addr_offset(index, false) : index); - } - -+static inline unsigned int x86_pmu_cfg_c_addr(int index, bool gp) -+{ -+ u32 base = gp ? MSR_IA32_PMC_V6_GP0_CFG_C : MSR_IA32_PMC_V6_FX0_CFG_C; -+ -+ return base + (x86_pmu.addr_offset ? x86_pmu.addr_offset(index, false) : -+ index * MSR_IA32_PMC_V6_STEP); -+} -+ - static inline int x86_pmu_rdpmc_index(int index) - { - return x86_pmu.rdpmc_index ? x86_pmu.rdpmc_index(index) : index; -@@ -1779,6 +1789,8 @@ void intel_pmu_pebs_data_source_cmt(void); - - void intel_pmu_pebs_data_source_lnl(void); - -+u64 intel_get_arch_pebs_data_config(struct perf_event *event); -+ - int intel_pmu_setup_lbr_filter(struct perf_event *event); - - void intel_pt_interrupt(void); -diff --git a/arch/x86/include/asm/intel_ds.h b/arch/x86/include/asm/intel_ds.h -index 023c2883f9f3..7bb80c993bef 100644 ---- a/arch/x86/include/asm/intel_ds.h -+++ b/arch/x86/include/asm/intel_ds.h -@@ -7,6 +7,13 @@ - #define PEBS_BUFFER_SHIFT 4 - #define PEBS_BUFFER_SIZE (PAGE_SIZE << PEBS_BUFFER_SHIFT) - -+/* -+ * The largest PEBS record could consume a page, ensure -+ * a record at least can be written after triggering PMI. -+ */ -+#define ARCH_PEBS_THRESH_MUL ((PEBS_BUFFER_SIZE - PAGE_SIZE) >> PEBS_BUFFER_SHIFT) -+#define ARCH_PEBS_THRESH_SINGLE 1 -+ - /* The maximal number of PEBS events: */ - #define MAX_PEBS_EVENTS_FMT4 8 - #define MAX_PEBS_EVENTS 32 -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 7c306dd0713c..737d51629c03 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -330,6 +330,14 @@ - #define ARCH_PEBS_OFFSET_MASK 0x7fffff - #define ARCH_PEBS_INDEX_WR_SHIFT 4 - -+#define ARCH_PEBS_RELOAD 0xffffffff -+#define ARCH_PEBS_LBR_SHIFT 40 -+#define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT) -+#define ARCH_PEBS_VECR_XMM BIT_ULL(49) -+#define ARCH_PEBS_GPR BIT_ULL(61) -+#define ARCH_PEBS_AUX BIT_ULL(62) -+#define ARCH_PEBS_EN BIT_ULL(63) -+ - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) - #define RTIT_CTL_CYCLEACC BIT(1) --- -2.43.0 - diff --git a/SPECS/kernel/0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu b/SPECS/kernel/0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu deleted file mode 100644 index f25fcb7d1..000000000 --- a/SPECS/kernel/0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu +++ /dev/null @@ -1,1937 +0,0 @@ -From 31b97b296aa51821c160494b464e2619cd24ac2e Mon Sep 17 00:00:00 2001 -From: linya14x -Date: Sun, 21 Sep 2025 11:51:07 +0800 -Subject: [PATCH 19/21] porting gmsl isx031 code between PTL IPU7 beta release - and PV release - -Update max9x deserializer driver and isx031 sensor driver. -Recent sensor driver changes are pushed in ipu6-drivers repo -under drivers/media/i2c path. - -Strip code is only needed for internal use to strip out -kernel version macro unrelated to the target kernel version. - -Signed-off-by: linya14x -Signed-off-by: hepengpx ---- - drivers/media/i2c/isx031.c | 410 +++++++++++++++++++------ - drivers/media/i2c/max9x/Makefile | 10 +- - drivers/media/i2c/max9x/max9295.c | 38 +-- - drivers/media/i2c/max9x/max9296.c | 46 +-- - drivers/media/i2c/max9x/max9296.h | 2 +- - drivers/media/i2c/max9x/max96724.c | 55 ++-- - drivers/media/i2c/max9x/max9x_pdata.h | 1 + - drivers/media/i2c/max9x/regmap-retry.c | 52 ++++ - drivers/media/i2c/max9x/regmap-retry.h | 18 ++ - drivers/media/i2c/max9x/serdes.c | 216 ++++++------- - drivers/media/i2c/max9x/serdes.h | 6 +- - 11 files changed, 551 insertions(+), 303 deletions(-) - create mode 100644 drivers/media/i2c/max9x/regmap-retry.c - create mode 100644 drivers/media/i2c/max9x/regmap-retry.h - -diff --git a/drivers/media/i2c/isx031.c b/drivers/media/i2c/isx031.c -index ddc3e512efa4..3cf638c86438 100644 ---- a/drivers/media/i2c/isx031.c -+++ b/drivers/media/i2c/isx031.c -@@ -17,6 +17,11 @@ - #include - #define to_isx031(_sd) container_of(_sd, struct isx031, sd) - -+#define ISX031_OTP_TYPE_NAME_L 0x7E8A -+#define ISX031_OTP_TYPE_NAME_H 0x7E8B -+#define ISX031_OTP_TYPE_NAME_H_FIELD 0x0F -+#define ISX031_OTP_MODULE_ID_L 0x031 -+ - #define ISX031_REG_MODE_SET_F 0x8A01 - #define ISX031_MODE_STANDBY 0x00 - #define ISX031_MODE_STREAMING 0x80 -@@ -33,6 +38,9 @@ - #define ISX031_MODE_4LANES_30FPS 0x17 - #define ISX031_MODE_2LANES_30FPS 0x18 - -+/* To serialize asynchronous callbacks */ -+static DEFINE_MUTEX(isx031_mutex); -+ - struct isx031_reg { - enum { - ISX031_REG_LEN_DELAY = 0, -@@ -52,13 +60,17 @@ struct isx031_link_freq_config { - const struct isx031_reg_list reg_list; - }; - --struct isx031_driver_mode { -+struct isx031_drive_mode { - int lanes; - int fps; - int mode; - }; - --static const struct isx031_driver_mode isx031_driver_modes[] = { -+struct isx031_info { -+ bool is_direct; -+}; -+ -+static const struct isx031_drive_mode isx031_drive_modes[] = { - { 4, 60, ISX031_MODE_4LANES_60FPS }, - { 4, 30, ISX031_MODE_4LANES_30FPS }, - { 2, 30, ISX031_MODE_2LANES_30FPS }, -@@ -94,9 +106,6 @@ struct isx031 { - const struct isx031_mode *pre_mode; - u8 lanes; - -- /* To serialize asynchronus callbacks */ -- struct mutex mutex; -- - /* i2c client */ - struct i2c_client *client; - -@@ -105,6 +114,15 @@ struct isx031 { - - /* Streaming on/off */ - bool streaming; -+ -+ struct v4l2_ctrl_handler ctrls; -+ -+ /* MIPI direct connection */ -+ bool is_direct; -+}; -+ -+static const s64 isx031_link_frequencies[] = { -+ 300000000ULL - }; - - static const struct isx031_reg isx031_init_reg[] = { -@@ -116,7 +134,7 @@ static const struct isx031_reg isx031_init_reg[] = { - static const struct isx031_reg isx031_framesync_reg[] = { - /* External sync */ - {ISX031_REG_LEN_08BIT, 0xBF14, 0x01}, /* SG_MODE_APL */ -- {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ -+ {ISX031_REG_LEN_08BIT, 0x8AFF, 0x0c}, /* Hi-Z (input setting or output disabled) */ - {ISX031_REG_LEN_08BIT, 0x0153, 0x00}, - {ISX031_REG_LEN_08BIT, 0x8AF0, 0x01}, /* external pulse-based sync */ - {ISX031_REG_LEN_08BIT, 0x0144, 0x00}, -@@ -242,24 +260,8 @@ static const struct isx031_mode supported_modes[] = { - }, - }; - --static int isx031_reset(struct gpio_desc *reset_gpio) -+static int isx031_read_reg(struct i2c_client *client, u16 reg, u16 len, u32 *val) - { -- if (!IS_ERR_OR_NULL(reset_gpio)) { -- gpiod_set_value_cansleep(reset_gpio, 0); -- usleep_range(500, 1000); -- gpiod_set_value_cansleep(reset_gpio, 1); -- /*Needs to sleep for quite a while before register writes*/ -- usleep_range(200 * 1000, 200 * 1000 + 500); -- -- return 0; -- } -- -- return -EINVAL; --} -- --static int isx031_read_reg(struct isx031 *isx031, u16 reg, u16 len, u32 *val) --{ -- struct i2c_client *client = isx031->client; - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = {0}; -@@ -306,8 +308,24 @@ static int isx031_write_reg(struct isx031 *isx031, u16 reg, u16 len, u32 val) - return 0; - } - -+static int isx031_write_reg_retry(struct isx031 *isx031, u16 reg, u16 len, u32 val) -+{ -+ int ret; -+ int retry = 100; -+ -+ while (retry--) { -+ ret = isx031_write_reg(isx031, reg, len, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ - static int isx031_write_reg_list(struct isx031 *isx031, -- const struct isx031_reg_list *r_list) -+ const struct isx031_reg_list *r_list, -+ bool isRetry) - { - struct i2c_client *client = v4l2_get_subdevdata(&isx031->sd); - unsigned int i; -@@ -318,9 +336,14 @@ static int isx031_write_reg_list(struct isx031 *isx031, - msleep(r_list->regs[i].val); - continue; - } -- ret = isx031_write_reg(isx031, r_list->regs[i].address, -- ISX031_REG_LEN_08BIT, -- r_list->regs[i].val); -+ ret = isRetry ? -+ isx031_write_reg_retry(isx031, -+ r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val) : -+ isx031_write_reg(isx031, r_list->regs[i].address, -+ ISX031_REG_LEN_08BIT, -+ r_list->regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", -@@ -332,28 +355,29 @@ static int isx031_write_reg_list(struct isx031 *isx031, - return 0; - } - --static int isx031_find_driver_mode(int lanes, int fps) -+static int isx031_find_drive_mode(int lanes, int fps) - { - int i; - -- for (i = 0; i < ARRAY_SIZE(isx031_driver_modes); i++) { -- if (isx031_driver_modes[i].lanes == lanes && isx031_driver_modes[i].fps == fps) -- return isx031_driver_modes[i].mode; -+ for (i = 0; i < ARRAY_SIZE(isx031_drive_modes); i++) { -+ if (isx031_drive_modes[i].lanes == lanes && isx031_drive_modes[i].fps == fps) -+ return isx031_drive_modes[i].mode; - } - - return -EINVAL; - } - --static int isx031_set_driver_mode(struct isx031 *isx031) -+static int isx031_set_drive_mode(struct isx031 *isx031) - { - int ret; - int mode; - -- mode = isx031_find_driver_mode(isx031->lanes, isx031->cur_mode->fps); -+ mode = isx031_find_drive_mode(isx031->lanes, isx031->cur_mode->fps); - if (mode < 0) - return mode; - -- ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, mode); -+ ret = isx031_write_reg(isx031, ISX031_REG_MODE_SELECT, 1, (u32)mode); -+ - return ret; - } - -@@ -372,7 +396,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - - retry = 50; - while (retry--) { -- ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, &val); - if (ret == 0) - break; -@@ -380,10 +404,13 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - } - cur_mode = val; - -- //TODO: only set if isx031->lanes != 0, means get lanes from pdata -- ret = isx031_set_driver_mode(isx031); -+ /* Note: Ideally, drive mode should only be set if isx031->lanes != 0, -+ * which would mean the number of lanes is obtained from platform data. -+ * Currently, drive mode is always set. -+ */ -+ ret = isx031_set_drive_mode(isx031); - if (ret) { -- dev_err(&client->dev, "failed to set driver mode"); -+ dev_err(&client->dev, "failed to set drive mode"); - return ret; - } - -@@ -394,17 +421,17 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return ret; - } - ret = isx031_write_reg(isx031, ISX031_REG_MODE_SET_F, 1, -- mode); -+ (u32)mode); - if (ret) { - dev_err(&client->dev, "failed to transit mode from 0x%x to 0x%x", - cur_mode, mode); - return ret; - } - -- /*streaming transit to standby need 1 frame+5ms*/ -+ /* streaming transit to standby need 1 frame+5ms */ - retry = 50; - while (retry--) { -- ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, &val); - if (ret == 0 && val == state) - break; -@@ -414,7 +441,7 @@ static int isx031_mode_transit(struct isx031 *isx031, int state) - return 0; - } - --static int isx031_identify_module(struct isx031 *isx031) -+static int isx031_initialize_module(struct isx031 *isx031) - { - struct i2c_client *client = isx031->client; - int ret; -@@ -422,7 +449,7 @@ static int isx031_identify_module(struct isx031 *isx031) - u32 val; - - while (retry--) { -- ret = isx031_read_reg(isx031, ISX031_REG_SENSOR_STATE, -+ ret = isx031_read_reg(client, ISX031_REG_SENSOR_STATE, - ISX031_REG_LEN_08BIT, &val); - if (ret == 0) - break; -@@ -434,17 +461,17 @@ static int isx031_identify_module(struct isx031 *isx031) - - dev_dbg(&client->dev, "sensor in mode 0x%x", val); - -- /* if sensor alreay in ISX031_STATE_STARTUP, can access i2c write directly*/ -+ /* if sensor already in ISX031_STATE_STARTUP, can access i2c write directly */ - if (val == ISX031_STATE_STREAMING) { - if (isx031_mode_transit(isx031, ISX031_STATE_STARTUP)) - return ret; - } - -- ret = isx031_write_reg_list(isx031, &isx031_init_reg_list); -+ ret = isx031_write_reg_list(isx031, &isx031_init_reg_list, true); - if (ret) - return ret; - if (isx031->platform_data != NULL && !isx031->platform_data->irq_pin_flags) { -- ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list); -+ ret = isx031_write_reg_list(isx031, &isx031_framesync_reg_list, false); - if (ret) { - dev_err(&client->dev, "failed in set framesync."); - return ret; -@@ -454,6 +481,49 @@ static int isx031_identify_module(struct isx031 *isx031) - return 0; - } - -+static int isx031_identify_module(struct i2c_client *client) -+{ -+ u32 NAME_L = 0; -+ u32 NAME_H = 0; -+ int ret = 0; -+ int i = 0; -+ int retry = 50; -+ -+ for (i = 0; i < retry; i++) { -+ ret = isx031_read_reg(client, ISX031_OTP_TYPE_NAME_L, -+ ISX031_REG_LEN_08BIT, &NAME_L); -+ if (!ret) -+ break; -+ } -+ -+ if (i == retry) { -+ dev_err(&client->dev, "isx031 read NAME_L failed"); -+ return ret; -+ } -+ -+ for (i = 0; i < retry; i++) { -+ ret = isx031_read_reg(client, ISX031_OTP_TYPE_NAME_H, -+ ISX031_REG_LEN_08BIT, &NAME_H); -+ if (!ret) -+ break; -+ } -+ -+ if (i == retry) { -+ dev_err(&client->dev, "isx031 read NAME_H failed"); -+ return ret; -+ } -+ -+ if (((NAME_H & ISX031_OTP_TYPE_NAME_H_FIELD) << 8 | NAME_L) != -+ ISX031_OTP_MODULE_ID_L) { -+ dev_err(&client->dev, "isx031 module id mismatch: 0x%4.4x\n", -+ ((NAME_H & ISX031_OTP_TYPE_NAME_H_FIELD) << 8 | -+ NAME_L)); -+ return -ENODEV; -+ } -+ -+ return ret; -+} -+ - static void isx031_update_pad_format(const struct isx031_mode *mode, - struct v4l2_mbus_framefmt *fmt) - { -@@ -463,6 +533,45 @@ static void isx031_update_pad_format(const struct isx031_mode *mode, - fmt->field = V4L2_FIELD_NONE; - } - -+static int isx031_get_mipi_lane(struct isx031 *isx031, struct device *dev) -+{ -+ struct fwnode_handle *endpoint; -+ struct v4l2_fwnode_endpoint bus_cfg = { -+ .bus_type = V4L2_MBUS_CSI2_DPHY, -+ }; -+ -+ int ret; -+ -+ endpoint = -+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, -+ FWNODE_GRAPH_ENDPOINT_NEXT); -+ if (!endpoint) { -+ dev_err(dev, "endpoint node not found"); -+ return -EPROBE_DEFER; -+ } -+ -+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); -+ if (ret) { -+ dev_err(dev, "parsing endpoint node fail"); -+ goto out_err; -+ } -+ -+ /* Check the number of MIPI CSI2 data lanes */ -+ if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && -+ bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { -+ dev_err(dev, "only 2 or 4 data lanes are currently supported"); -+ goto out_err; -+ } -+ -+ isx031->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; -+ -+out_err: -+ v4l2_fwnode_endpoint_free(&bus_cfg); -+ fwnode_handle_put(endpoint); -+ -+ return ret; -+} -+ - static int isx031_start_streaming(struct isx031 *isx031) - { - int ret; -@@ -471,7 +580,7 @@ static int isx031_start_streaming(struct isx031 *isx031) - - if (isx031->cur_mode != isx031->pre_mode) { - reg_list = &isx031->cur_mode->reg_list; -- ret = isx031_write_reg_list(isx031, reg_list); -+ ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "failed to set stream mode"); - return ret; -@@ -481,6 +590,14 @@ static int isx031_start_streaming(struct isx031 *isx031) - dev_dbg(&client->dev, "same mode, skip write reg list"); - } - -+ if (isx031->is_direct) { -+ ret = __v4l2_ctrl_handler_setup(&isx031->ctrls); -+ if (ret) { -+ dev_err(&client->dev, "failed to setup ctrls"); -+ return ret; -+ } -+ } -+ - ret = isx031_mode_transit(isx031, ISX031_STATE_STREAMING); - if (ret) { - dev_err(&client->dev, "failed to start streaming"); -@@ -506,13 +623,12 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - if (isx031->streaming == enable) - return 0; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - - ret = isx031_start_streaming(isx031); -@@ -528,7 +644,8 @@ static int isx031_set_stream(struct v4l2_subdev *sd, int enable) - - isx031->streaming = enable; - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(&isx031_mutex); - - return ret; - } -@@ -553,11 +670,14 @@ static int __maybe_unused isx031_suspend(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - if (isx031->streaming) - isx031_stop_streaming(isx031); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); -+ -+ /* Active low gpio reset, set 1 to power off sensor */ -+ gpiod_set_value_cansleep(isx031->reset_gpio, 1); - - return 0; - } -@@ -568,38 +688,60 @@ static int __maybe_unused isx031_resume(struct device *dev) - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct isx031 *isx031 = to_isx031(sd); - const struct isx031_reg_list *reg_list; -- int ret; -+ int count = 0; -+ int ret = 0; -+ -+ mutex_lock(&isx031_mutex); -+ -+ /* Active low gpio reset, set 0 to power on sensor, -+ * sensor must on back before start resume -+ */ -+ if (isx031->reset_gpio != NULL) { -+ do { -+ gpiod_set_value_cansleep(isx031->reset_gpio, 0); -+ ret = gpiod_get_value_cansleep(isx031->reset_gpio); -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ if (++count >= 20) { -+ dev_err(&client->dev, -+ "%s: failed to power on reset gpio, reset gpio is %d", -+ __func__, ret); -+ break; -+ } -+ } while (ret != 0); -+ } - -- if (isx031->reset_gpio != NULL) -- isx031_reset(isx031->reset_gpio); -+ ret = isx031_identify_module(isx031->client); -+ if (ret) { -+ dev_err(&client->dev, "isx031 identify module failed"); -+ goto err_unlock; -+ } - -- ret = isx031_identify_module(isx031); -+ ret = isx031_initialize_module(isx031); - if (ret == 0) { - reg_list = &isx031->cur_mode->reg_list; -- ret = isx031_write_reg_list(isx031, reg_list); -+ ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "resume: failed to apply cur mode"); -- return ret; -+ goto err_unlock; - } - } else { -- dev_err(&client->dev, "isx031 resume failed"); -- return ret; -+ dev_err(&client->dev, "isx031 resume initialization failed"); -+ goto err_unlock; - } -- -- mutex_lock(&isx031->mutex); - if (isx031->streaming) { - ret = isx031_start_streaming(isx031); - if (ret) { - isx031->streaming = false; - isx031_stop_streaming(isx031); -- mutex_unlock(&isx031->mutex); -- return ret; -+ goto err_unlock; - } - } - -- mutex_unlock(&isx031->mutex); -+err_unlock: -+ mutex_unlock(&isx031_mutex); - -- return 0; -+ return ret; - } - - static int isx031_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, -@@ -638,7 +780,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - if (i >= ARRAY_SIZE(supported_modes)) - mode = &supported_modes[0]; - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - - isx031_update_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { -@@ -647,7 +789,7 @@ static int isx031_set_format(struct v4l2_subdev *sd, - isx031->cur_mode = mode; - } - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); - - return 0; - } -@@ -658,26 +800,24 @@ static int isx031_get_format(struct v4l2_subdev *sd, - { - struct isx031 *isx031 = to_isx031(sd); - -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_state_get_format(sd_state, - fmt->pad); - else - isx031_update_pad_format(isx031->cur_mode, &fmt->format); - -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); - - return 0; - } - - static int isx031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) - { -- struct isx031 *isx031 = to_isx031(sd); -- -- mutex_lock(&isx031->mutex); -+ mutex_lock(&isx031_mutex); - isx031_update_pad_format(&supported_modes[0], - v4l2_subdev_state_get_format(fh->state, 0)); -- mutex_unlock(&isx031->mutex); -+ mutex_unlock(&isx031_mutex); - - return 0; - } -@@ -707,15 +847,42 @@ static const struct v4l2_subdev_internal_ops isx031_internal_ops = { - .open = isx031_open, - }; - -+static int isx031_set_ctrl(struct v4l2_ctrl *ctrl) -+{ -+ return 0; -+}; -+ -+static const struct v4l2_ctrl_ops isx031_ctrl_ops = { -+ .s_ctrl = isx031_set_ctrl, -+}; -+ -+static int isx031_ctrls_init(struct isx031 *sensor) -+{ -+ int ret = 0; -+ struct v4l2_ctrl *ctrl; -+ -+ v4l2_ctrl_handler_init(&sensor->ctrls, 10); -+ -+ /* There's a need to set the link frequency because IPU6 dictates it. */ -+ ctrl = v4l2_ctrl_new_int_menu(&sensor->ctrls, &isx031_ctrl_ops, -+ V4L2_CID_LINK_FREQ, -+ ARRAY_SIZE(isx031_link_frequencies) - 1, 0, -+ isx031_link_frequencies); -+ -+ if (ctrl) -+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; -+ -+ sensor->sd.ctrl_handler = &sensor->ctrls; -+ return ret; -+} -+ - static void isx031_remove(struct i2c_client *client) - { - struct v4l2_subdev *sd = i2c_get_clientdata(client); -- struct isx031 *isx031 = to_isx031(sd); - - v4l2_async_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); - - } - -@@ -723,6 +890,7 @@ static int isx031_probe(struct i2c_client *client) - { - struct v4l2_subdev *sd; - struct isx031 *isx031; -+ const struct isx031_info *info; - const struct isx031_reg_list *reg_list; - int ret; - -@@ -736,19 +904,37 @@ static int isx031_probe(struct i2c_client *client) - dev_warn(&client->dev, "no platform data provided\n"); - - isx031->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", -- GPIOD_OUT_HIGH); -+ GPIOD_OUT_LOW); -+ - if (IS_ERR(isx031->reset_gpio)) - return -EPROBE_DEFER; - else if (isx031->reset_gpio == NULL) - dev_warn(&client->dev, "Reset GPIO not found"); -- else { -- dev_dbg(&client->dev, "Found reset GPIO"); -- isx031_reset(isx031->reset_gpio); -+ else -+ dev_dbg(&client->dev, "Reset GPIO found"); -+ -+ ret = isx031_identify_module(client); -+ if (ret) { -+ dev_err(&client->dev, "isx031 identify module failed"); -+ return ret; - } - -+ info = device_get_match_data(&client->dev); -+ if (info) -+ isx031->is_direct = info->is_direct; -+ else -+ isx031->is_direct = false; -+ - /* initialize subdevice */ - sd = &isx031->sd; - v4l2_i2c_subdev_init(sd, client, &isx031_subdev_ops); -+ if (isx031->is_direct) { -+ ret = isx031_ctrls_init(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to init sensor ctrls: %d", ret); -+ return ret; -+ } -+ } - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - sd->internal_ops = &isx031_internal_ops; - sd->entity.ops = &isx031_subdev_entity_ops; -@@ -758,36 +944,47 @@ static int isx031_probe(struct i2c_client *client) - isx031->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&sd->entity, 1, &isx031->pad); - if (ret < 0) { -- dev_err(&client->dev, -- "%s : media entity init Failed %d\n", __func__, ret); -- return ret; -+ dev_err(&client->dev, "failed to init entity pads: %d", ret); -+ goto probe_error_v4l2_ctrl_handler_free; - } - -- ret = isx031_identify_module(isx031); -- if (ret) { -- dev_err(&client->dev, "failed to find sensor: %d", ret); -- return ret; -+ if (isx031->is_direct) { -+ isx031->sd.state_lock = isx031->sd.ctrl_handler->lock; -+ v4l2_subdev_init_finalize(&isx031->sd); - } - -- if (isx031->platform_data) -+ if (isx031->platform_data && isx031->platform_data->suffix[0]) - snprintf(isx031->sd.name, sizeof(isx031->sd.name), "isx031 %s", - isx031->platform_data->suffix); - - if (isx031->platform_data) - isx031->lanes = isx031->platform_data->lanes; - -- mutex_init(&isx031->mutex); -+ if (isx031->is_direct) { -+ /* mipi sensor read info from fwnode entrypoint bus cfg */ -+ ret = isx031_get_mipi_lane(isx031, &client->dev); -+ if (ret) { -+ dev_err(&client->dev, "failed to get MIPI lane configuration"); -+ return ret; -+ } -+ } - - /* 1920x1536 default */ -- isx031->cur_mode = NULL; -- isx031->pre_mode = &supported_modes[0]; -- reg_list = &isx031->pre_mode->reg_list; -- ret = isx031_write_reg_list(isx031, reg_list); -+ isx031->pre_mode = NULL; -+ isx031->cur_mode = &supported_modes[0]; -+ ret = isx031_initialize_module(isx031); -+ if (ret) { -+ dev_err(&client->dev, "failed to initialize sensor: %d", ret); -+ return ret; -+ } -+ reg_list = &isx031->cur_mode->reg_list; -+ ret = isx031_write_reg_list(isx031, reg_list, true); - if (ret) { - dev_err(&client->dev, "failed to apply preset mode"); - goto probe_error_media_entity_cleanup; - } -- isx031->cur_mode = isx031->pre_mode; -+ isx031->pre_mode = isx031->cur_mode; -+ - ret = v4l2_async_register_subdev_sensor(&isx031->sd); - if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", -@@ -807,8 +1004,9 @@ static int isx031_probe(struct i2c_client *client) - - probe_error_media_entity_cleanup: - media_entity_cleanup(&isx031->sd.entity); -- pm_runtime_disable(&client->dev); -- mutex_destroy(&isx031->mutex); -+ -+probe_error_v4l2_ctrl_handler_free: -+ v4l2_ctrl_handler_free(isx031->sd.ctrl_handler); - - return ret; - } -@@ -819,13 +1017,25 @@ static const struct dev_pm_ops isx031_pm_ops = { - - static const struct i2c_device_id isx031_id_table[] = { - { "isx031", 0 }, -- { /* sentinel */ }, -+ {}, - }; - MODULE_DEVICE_TABLE(i2c, isx031_id_table); - -+static const struct isx031_info isx031_mipi_info = { -+ .is_direct = true, -+}; -+ -+static const struct acpi_device_id isx031_acpi_ids[] = { -+ { "INTC3031", (kernel_ulong_t)&isx031_mipi_info }, -+ {} -+}; -+ -+MODULE_DEVICE_TABLE(acpi, isx031_acpi_ids); -+ - static struct i2c_driver isx031_i2c_driver = { - .driver = { - .name = "isx031", -+ .acpi_match_table = ACPI_PTR(isx031_acpi_ids), - .pm = &isx031_pm_ops, - }, - .probe = isx031_probe, -@@ -835,6 +1045,8 @@ static struct i2c_driver isx031_i2c_driver = { - - module_i2c_driver(isx031_i2c_driver); - --MODULE_AUTHOR("Hao Yao "); - MODULE_DESCRIPTION("isx031 sensor driver"); -+MODULE_AUTHOR("Hao Yao "); -+MODULE_AUTHOR("Jonathan Lui "); -+MODULE_AUTHOR("Wei Khang, Goh "); - MODULE_LICENSE("GPL v2"); -diff --git a/drivers/media/i2c/max9x/Makefile b/drivers/media/i2c/max9x/Makefile -index ab533b790f72..fce29b31ed68 100644 ---- a/drivers/media/i2c/max9x/Makefile -+++ b/drivers/media/i2c/max9x/Makefile -@@ -1,10 +1,2 @@ --ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU7)),) --ccflags-y += -I$(src)/../../pci/intel/ipu7/ --endif -- --ifneq ($(filter y m, $(CONFIG_VIDEO_INTEL_IPU6)),) --ccflags-y += -I$(src)/../../pci/intel/ipu6/ --endif -- - obj-m += max9x.o --max9x-y += serdes.o max9296.o max96724.o max9295.o max96717.o -+max9x-y += regmap-retry.o serdes.o max9296.o max96724.o max9295.o max96717.o -diff --git a/drivers/media/i2c/max9x/max9295.c b/drivers/media/i2c/max9x/max9295.c -index cc1ee2f5ff92..b83b344cdc6b 100644 ---- a/drivers/media/i2c/max9x/max9295.c -+++ b/drivers/media/i2c/max9x/max9295.c -@@ -30,6 +30,7 @@ - #include - - #include "max9295.h" -+#include "regmap-retry.h" - - static const char *const max9295_gpio_chip_names[] = { - "MFP1", -@@ -176,7 +177,7 @@ static int max9295_setup_gpio(struct max9x_common *common) - { - struct device *dev = common->dev; - int ret; -- struct max9x_gpio_pdata *gpio_pdata; -+ struct max9x_gpio_pdata *gpio_pdata = NULL; - - if (dev->platform_data) { - struct max9x_pdata *pdata = dev->platform_data; -@@ -261,14 +262,14 @@ static int max9295_set_pipe_data_types_enabled(struct max9x_common *common, - struct device *dev = common->dev; - struct regmap *map = common->map; - int data_type_slot, dt; -- int ret; -+ int ret = 0; - - for (data_type_slot = 0; data_type_slot < common->video_pipe[pipe_id].config.num_data_types; data_type_slot++) { - dt = common->video_pipe[pipe_id].config.data_type[data_type_slot]; - dev_dbg(dev, "Video-pipe %d, data type %d: (%#.2x: %s)", - pipe_id, data_type_slot, dt, (enable ? "enable" : "disable")); - -- TRY(ret, regmap_update_bits(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_MEM_DT_SEL(pipe_id, data_type_slot), - MAX9295_MEM_DT_SEL_DT_FIELD | MAX9295_MEM_DT_SEL_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_DT_FIELD, dt) | - MAX9X_FIELD_PREP(MAX9295_MEM_DT_SEL_EN_FIELD, enable ? 1U : 0U)) -@@ -522,13 +523,13 @@ static int max9295_enable_rclk(struct max9x_common *common) - ); - - // Configure reference generation output on MFP4 -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_GPIO_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_GPIO_FIELD, 4U)) - ); - - // Enable output -- TRY(ret, regmap_update_bits(map, MAX9295_REF_VTG1, -+ TRY(ret, regmap_update_bits_retry(map, MAX9295_REF_VTG1, - MAX9295_PCLK_EN_FIELD, - MAX9X_FIELD_PREP(MAX9295_PCLK_EN_FIELD, 1U)) - ); -@@ -553,7 +554,7 @@ static int max9295_set_local_control_channel_enabled(struct max9x_common *common - - dev_dbg(dev, "set rem cc %s", (enabled ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, -+ return regmap_update_bits_retry(map, MAX9295_PHY_REM_CTRL, MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, - MAX9X_FIELD_PREP(MAX9295_PHY_LOCAL_CTRL_DIS_FIELD, (enabled ? 0U : 1U))); - } - -@@ -575,8 +576,8 @@ static int max9295_verify_devid(struct max9x_common *common) - int ret; - - // Fetch and output chip name + revision -- TRY(ret, regmap_read(map, MAX9295_DEV_ID, &dev_id)); -- TRY(ret, regmap_read(map, MAX9295_DEV_REV, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_ID, &dev_id)); -+ TRY(ret, regmap_read_retry(map, MAX9295_DEV_REV, &dev_rev)); - - switch (dev_id) { - case MAX9295A: -@@ -617,10 +618,10 @@ static int max9295_enable(struct max9x_common *common) - TRY(ret, max9295_set_local_control_channel_enabled(common, false)); - - /* Clear the pipe maps */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_9, 0)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_9, 0)); - - /* Clear the csi port selections */ -- TRY(ret, regmap_write(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); -+ TRY(ret, regmap_write_retry(map, MAX9295_FRONTTOP_0, MAX9295_FRONTTOP_0_LINE_INFO)); - - return 0; - } -@@ -694,15 +695,14 @@ static int max9295_remap_reset(struct max9x_common *common) - struct device *dev = common->dev; - struct max9x_pdata *pdata = dev->platform_data; - u32 phys_addr = pdata->phys_addr ? pdata->phys_addr : -- common->client->addr; -+ common->client->addr; - u32 virt_addr = common->client->addr; - - dev_info(dev, "Remap reset address from 0x%02x to 0x%02x", virt_addr, - phys_addr); - -- TRY(ret, regmap_update_bits( -- common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -- FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); -+ TRY(ret, regmap_update_bits(common->map, MAX9295_REG0, MAX9295_REG0_DEV_ADDR_FIELD, -+ FIELD_PREP(MAX9295_REG0_DEV_ADDR_FIELD, phys_addr))); - - return 0; - } -@@ -717,18 +717,18 @@ static int max9295_add_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr || src == 0) { - dev_dbg(dev, "SRC %02x = %02x, DST %02x = %02x", - MAX9295_I2C_SRC(i2c_id, alias), virt_addr, - MAX9295_I2C_DST(i2c_id, alias), phys_addr); -- TRY(ret, regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, phys_addr)) - ); - -- TRY(ret, regmap_write(map, MAX9295_I2C_SRC(i2c_id, alias), -+ TRY(ret, regmap_write_retry(map, MAX9295_I2C_SRC(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_SRC_FIELD, virt_addr)) - ); - } -@@ -746,10 +746,10 @@ static int max9295_remove_translate_addr(struct max9x_common *common, - int ret; - - for (alias = 0; alias < MAX9295_NUM_ALIASES; alias++) { -- TRY(ret, regmap_read(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); -+ TRY(ret, regmap_read_retry(map, MAX9295_I2C_SRC(i2c_id, alias), &src)); - src = FIELD_GET(MAX9295_I2C_SRC_FIELD, src); - if (src == virt_addr) { -- return regmap_write(map, MAX9295_I2C_DST(i2c_id, alias), -+ return regmap_write_retry(map, MAX9295_I2C_DST(i2c_id, alias), - MAX9X_FIELD_PREP(MAX9295_I2C_DST_FIELD, 0)); - } - } -diff --git a/drivers/media/i2c/max9x/max9296.c b/drivers/media/i2c/max9x/max9296.c -index 41074d60cc01..be144f188de0 100644 ---- a/drivers/media/i2c/max9x/max9296.c -+++ b/drivers/media/i2c/max9x/max9296.c -@@ -215,14 +215,14 @@ static int max9296_conf_phy_maps(struct max9x_common *common, unsigned int csi_i - * For second pair of PHYs, first lanes are on the master PHY too. - * - * PHY 0 + 1 -- * CLK = PHY 1 -+ * CLK is on PHY 1 - * PHY1 Lane 0 = D0 - * PHY1 Lane 1 = D1 - * PHY0 Lane 0 = D2 - * PHY0 Lane 1 = D3 - * - * PHY 2 + 3 -- * CLK = PHY 2 -+ * CLK is on PHY 2 - * PHY2 Lane 0 = D0 - * PHY2 Lane 1 = D1 - * PHY3 Lane 0 = D2 -@@ -500,7 +500,7 @@ static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned in - { - struct device *dev = common->dev; - struct max9x_serdes_csi_link *csi_link; -- int ret; -+ int ret = 0; - - if (csi_id > common->num_csi_links) - return -EINVAL; -@@ -513,45 +513,49 @@ static int max9296_set_csi_link_enabled(struct max9x_common *common, unsigned in - if (WARN_ONCE(enable && csi_link->config.num_lanes == 0, "Tried to enable CSI port with no lanes???")) - return -EINVAL; - -- if (enable) -- csi_link->usecount++; -- else if (csi_link->usecount > 0) -- csi_link->usecount--; -+ mutex_lock(&csi_link->csi_mutex); - - dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, (enable ? "enable" : "disable"), csi_link->usecount); - -- if (enable && csi_link->usecount == 1) { -+ if (enable && csi_link->usecount == 0) { - // Enable && first user - ret = max9296_set_initial_deskew(common, csi_id, csi_link->config.auto_init_deskew_enabled, - csi_link->config.initial_deskew_width); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_dpll_freq(common, csi_id, csi_link->config.freq_mhz); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_dpll_enabled(common, csi_id, true); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_enabled(common, csi_id, true); - if (ret) -- return ret; -+ goto err_unlock; - -- } else if (!enable && csi_link->usecount == 0) { -+ } else if (!enable && csi_link->usecount == 1) { - // Disable && no more users - ret = max9296_set_phy_enabled(common, csi_id, false); - if (ret) -- return ret; -+ goto err_unlock; - - ret = max9296_set_phy_dpll_enabled(common, csi_id, false); - if (ret) -- return ret; -- -+ goto err_unlock; - } - -- return 0; -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+err_unlock: -+ mutex_unlock(&csi_link->csi_mutex); -+ -+ return ret; - } - - static int max9296_set_video_pipe_enabled(struct max9x_common *common, unsigned int pipe_id, bool enable) -@@ -624,10 +628,11 @@ static int max9296_set_serial_link_routing(struct max9x_common *common, unsigned - if (ret) - return ret; - -- if (common->csi_link[config->map[map_id].dst_csi].config.auto_start) { -+ if (!config->map[map_id].is_csi_enabled && common->csi_link[config->map[map_id].dst_csi].config.auto_start) { - ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, true); - if (ret) - return ret; -+ config->map[map_id].is_csi_enabled = true; - } - } - -@@ -730,7 +735,7 @@ static int max9296_deisolate_serial_link(struct max9x_common *common, unsigned i - link_cfg = MAX9296_LINK_B; - else { - dev_err(dev, "No link was detected"); -- return -1; -+ return -EINVAL; - } - - dev_dbg(dev, "Deisolate link %d (link_cfg=%d)", link, link_cfg); -@@ -807,9 +812,12 @@ static int max9296_disable_serial_link(struct max9x_common *common, unsigned int - return ret; - - for (map_id = 0; map_id < config->num_maps; map_id++) { -+ if (!config->map[map_id].is_csi_enabled) -+ continue; - ret = max9296_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); - if (ret) - return ret; -+ config->map[map_id].is_csi_enabled = false; - } - } - -diff --git a/drivers/media/i2c/max9x/max9296.h b/drivers/media/i2c/max9x/max9296.h -index 38c344669df4..acc6bb03405b 100644 ---- a/drivers/media/i2c/max9x/max9296.h -+++ b/drivers/media/i2c/max9x/max9296.h -@@ -49,7 +49,7 @@ enum max9296_link_mode { - #define MAX9296_NUM_CSI_LINKS 4 /* Total Number of PHYs */ - /* 2 CSI controllers, 2 PHYs per controller, and 2 lanes per PHY */ - --#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 250 -+#define MAX9296_DEFAULT_SERIAL_LINK_TIMEOUT_MS 350 - - #define MAX9296_DPLL_FREQ_MHZ_MULTIPLE 100 - -diff --git a/drivers/media/i2c/max9x/max96724.c b/drivers/media/i2c/max9x/max96724.c -index b214e1176963..1d253a6faf71 100644 ---- a/drivers/media/i2c/max9x/max96724.c -+++ b/drivers/media/i2c/max9x/max96724.c -@@ -30,6 +30,7 @@ - #include - - #include "max96724.h" -+#include "regmap-retry.h" - - // Params - int max96724_serial_link_timeout_ms = MAX96724_DEFAULT_SERIAL_LINK_TIMEOUT_MS; -@@ -337,7 +338,7 @@ static int max96724_set_all_reset(struct max9x_common *common, bool enable) - - dev_dbg(dev, "Reset %s", (enable ? "enable" : "disable")); - -- return regmap_update_bits(map, MAX96724_RESET_ALL, -+ return regmap_update_bits_retry(map, MAX96724_RESET_ALL, - MAX96724_RESET_ALL_FIELD, - MAX9X_FIELD_PREP(MAX96724_RESET_ALL_FIELD, enable ? 1U : 0U)); - } -@@ -354,8 +355,8 @@ static int max96724_soft_reset(struct max9x_common *common) - return ret; - - /* Wait for hardware available after soft reset */ -- /* TODO: Optimize sleep time 45 ms */ -- msleep(45); -+ /* TODO: Optimize sleep time 20 ms */ -+ msleep(20); - - ret = max96724_set_all_reset(common, 0); - if (ret) -@@ -454,7 +455,7 @@ static int max96724_set_serial_link_rate(struct max9x_common *common, unsigned i - struct device *dev = common->dev; - struct regmap *map = common->map; - struct max9x_serdes_serial_config *config = &common->serial_link[link_id].config; -- unsigned int tx_rate, rx_rate; -+ int tx_rate, rx_rate; - - tx_rate = max9x_serdes_mhz_to_rate(max96724_tx_rates, ARRAY_SIZE(max96724_tx_rates), config->tx_freq_mhz); - if (tx_rate < 0) -@@ -570,7 +571,7 @@ static int max96724_set_csi_link_enabled(struct max9x_common *common, - { - struct device *dev = common->dev; - struct max9x_serdes_csi_link *csi_link; -- int ret; -+ int ret = 0; - - if (csi_id > common->num_csi_links) - return -EINVAL; -@@ -585,32 +586,37 @@ static int max96724_set_csi_link_enabled(struct max9x_common *common, - "Tried to enable CSI port with no lanes???")) - return -EINVAL; - -- // Keep track of number of enabled maps using this CSI link -- if (enable) -- csi_link->usecount++; -- else if (csi_link->usecount > 0) -- csi_link->usecount--; -+ mutex_lock(&csi_link->csi_mutex); - - dev_dbg(dev, "CSI link %d: %s (%d users)", csi_id, - (enable ? "enable" : "disable"), csi_link->usecount); - -- if (enable && csi_link->usecount == 1) { -+ if (enable && csi_link->usecount == 0) { - // Enable && first user - - ret = max96724_set_phy_enabled(common, csi_id, true); - if (ret) -- return ret; -+ goto err_unlock; - -- } else if (!enable && csi_link->usecount == 0) { -+ } else if (!enable && csi_link->usecount == 1) { - // Disable && no more users - - ret = max96724_set_phy_enabled(common, csi_id, false); - if (ret) -- return ret; -+ goto err_unlock; - - } - -- return 0; -+ // Keep track of number of enabled maps using this CSI link -+ if (enable) -+ csi_link->usecount++; -+ else if (csi_link->usecount > 0) -+ csi_link->usecount--; -+ -+err_unlock: -+ mutex_unlock(&csi_link->csi_mutex); -+ -+ return ret; - } - - static int max96724_csi_double_pixel(struct max9x_common *common, -@@ -719,11 +725,14 @@ static int max96724_set_serial_link_routing(struct max9x_common *common, - if (ret) - return ret; - -- ret = max96724_set_csi_link_enabled(common, -+ if (!config->map[map_id].is_csi_enabled) { -+ ret = max96724_set_csi_link_enabled(common, - config->map[map_id].dst_csi, - true); -- if (ret) -- return ret; -+ if (ret) -+ return ret; -+ config->map[map_id].is_csi_enabled = true; -+ } - - ret = max96724_csi_double_pixel(common, - config->map[map_id].dst_csi, -@@ -766,16 +775,18 @@ static int max96724_disable_serial_link(struct max9x_common *common, - return ret; - - for (map_id = 0; map_id < config->num_maps; map_id++) { -+ if (!config->map[map_id].is_csi_enabled) -+ continue; - ret = max96724_set_csi_link_enabled(common, config->map[map_id].dst_csi, false); - if (ret) - return ret; -+ config->map[map_id].is_csi_enabled = false; - } - } - -- /* TODO: if disabling serial link, serializer can't perform i2c communication. */ -- // ret = max96724_set_serial_link_state(common, link_id, false); -- // if (ret) -- // return ret; -+ ret = max96724_set_serial_link_state(common, link_id, false); -+ if (ret) -+ return ret; - - return 0; - } -diff --git a/drivers/media/i2c/max9x/max9x_pdata.h b/drivers/media/i2c/max9x/max9x_pdata.h -index 874e7412315f..04da75fc7d54 100644 ---- a/drivers/media/i2c/max9x/max9x_pdata.h -+++ b/drivers/media/i2c/max9x/max9x_pdata.h -@@ -39,6 +39,7 @@ struct max9x_serdes_mipi_map { - u16 dst_vc; - u16 dst_dt; - u16 dst_csi; -+ bool is_csi_enabled; - }; - - struct max9x_video_pipe_pdata { -diff --git a/drivers/media/i2c/max9x/regmap-retry.c b/drivers/media/i2c/max9x/regmap-retry.c -new file mode 100644 -index 000000000000..9d9ebc33b7a6 ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.c -@@ -0,0 +1,52 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * Copyright (C) 2025 Intel Corporation -+ */ -+ -+#include "regmap-retry.h" -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_read(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_write(map, reg, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val) -+{ -+ int ret = 0; -+ int retry = 50; -+ -+ while (retry--) { -+ ret = regmap_update_bits(map, reg, mask, val); -+ if (!ret) -+ break; -+ msleep(20); -+ } -+ -+ return ret; -+} -diff --git a/drivers/media/i2c/max9x/regmap-retry.h b/drivers/media/i2c/max9x/regmap-retry.h -new file mode 100644 -index 000000000000..e4347c47d2af ---- /dev/null -+++ b/drivers/media/i2c/max9x/regmap-retry.h -@@ -0,0 +1,18 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+/* -+ * Copyright (C) 2025 Intel Corporation -+ */ -+ -+#ifndef _REGMAP_RETRY_H -+#define _REGMAP_RETRY_H -+ -+#include -+ -+int regmap_read_retry(struct regmap *map, unsigned int reg, unsigned int *val); -+ -+int regmap_write_retry(struct regmap *map, unsigned int reg, unsigned int val); -+ -+int regmap_update_bits_retry(struct regmap *map, unsigned int reg, -+ unsigned int mask, unsigned int val); -+ -+#endif -diff --git a/drivers/media/i2c/max9x/serdes.c b/drivers/media/i2c/max9x/serdes.c -index 633c55504273..08c44a28bb8c 100644 ---- a/drivers/media/i2c/max9x/serdes.c -+++ b/drivers/media/i2c/max9x/serdes.c -@@ -31,6 +31,7 @@ - #include - - #include "serdes.h" -+#include "regmap-retry.h" - - static const s64 max9x_op_sys_clock[] = { - MAX9X_LINK_FREQ_MBPS_TO_HZ(2500), -@@ -132,7 +133,7 @@ static const struct of_device_id max9x_of_match[] = { - MODULE_DEVICE_TABLE(of, max9x_of_match); - - static const struct i2c_device_id max9x_id[] = { -- { "max9x", MAX9296 }, -+ { "max9x", 0 }, - { "max9296", MAX9296 }, - { "max96724", MAX96724 }, - { "max9295", MAX9295 }, -@@ -314,7 +315,7 @@ static void *parse_serdes_pdata(struct device *dev) - */ - struct serdes_platform_data *serdes_pdata = dev->platform_data; - unsigned int num_ports = serdes_pdata->subdev_num; -- unsigned int csi_port = (serdes_pdata->des_port / 90); -+ unsigned int csi_port = serdes_pdata->des_port / 90; - struct max9x_pdata *des_pdata = devm_kzalloc(dev, sizeof(*des_pdata), GFP_KERNEL); - - snprintf(des_pdata->suffix, sizeof(des_pdata->suffix), "%c", serdes_pdata->suffix); -@@ -383,36 +384,28 @@ static void *parse_serdes_pdata(struct device *dev) - csi_link->auto_initial_deskew = true; - csi_link->initial_deskew_width = 7; - csi_link->auto_start = false; -- csi_link->num_maps = 2; -+ csi_link->num_maps = serdes_pdata->deser_nlanes; - csi_link->maps = devm_kzalloc(dev, csi_link->num_maps * sizeof(*csi_link->maps), GFP_KERNEL); - if (csi_port == 1) { - SET_PHY_MAP(csi_link->maps, 0, 0, 1, 0); /* 0 (DA0) -> PHY1.0 */ - SET_PHY_MAP(csi_link->maps, 1, 1, 1, 1); /* 1 (DA1) -> PHY1.1 */ -+ if (csi_link->num_maps == 4) { -+ SET_PHY_MAP(csi_link->maps, 2, 2, 0, 0); /* 2 (DA2) -> PHY0.0 */ -+ SET_PHY_MAP(csi_link->maps, 3, 3, 0, 1); /* 3 (DA3) -> PHY0.1 */ -+ } - } else if (csi_port == 2) { -- SET_PHY_MAP(csi_link->maps, 0, 2, 2, 0); /* 0 (DA0) -> PHY2.0 */ -- SET_PHY_MAP(csi_link->maps, 1, 2, 2, 1); /* 1 (DA1) -> PHY2.1 */ -+ SET_PHY_MAP(csi_link->maps, 0, 0, 2, 0); /* 0 (DA0) -> PHY2.0 */ -+ SET_PHY_MAP(csi_link->maps, 1, 1, 2, 1); /* 1 (DA1) -> PHY2.1 */ -+ if (csi_link->num_maps == 4) { -+ SET_PHY_MAP(csi_link->maps, 2, 2, 3, 0); /* 2 (DA2) -> PHY3.0 */ -+ SET_PHY_MAP(csi_link->maps, 3, 3, 3, 1); /* 3 (DA3) -> PHY3.1 */ -+ } - } - } while (0); - - return des_pdata; - } - --static int regmap_read_retry(struct regmap *map, unsigned int reg, -- unsigned int *val) --{ -- int ret = 0; -- int i = 0; -- -- for (i = 0; i < 50; i++) { -- ret = regmap_read(map, reg, val); -- if (!ret) -- break; -- msleep(20); -- } -- -- return ret; --} -- - static int max9x_enable_resume(struct max9x_common *common) - { - struct device *dev = common->dev; -@@ -457,6 +450,7 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - unsigned int phys_addr, virt_addr; - struct i2c_client *phys_client; - struct regmap *phys_map, *virt_map; -+ struct device *dev = &serial_link->remote.client->dev; - unsigned int val; - const struct regmap_config regmap_config = { - .reg_bits = 16, -@@ -466,27 +460,23 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - if (!serial_link->remote.pdata) - return 0; - -- ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; -- - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; - if (phys_addr == virt_addr) - return 0; - -- dev_info(common->dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); -+ dev_info(dev, "Remap serializer from 0x%02x to 0x%02x", phys_addr, virt_addr); - - phys_client = i2c_new_dummy_device(serial_link->remote.client->adapter, phys_addr); - if (IS_ERR_OR_NULL(phys_client)) { -- dev_err(common->dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy client for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_client); - return ret; - } - - phys_map = regmap_init_i2c(phys_client, ®map_config); - if (IS_ERR_OR_NULL(phys_map)) { -- dev_err(common->dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); -+ dev_err(dev, "Failed to create dummy map for phys_addr 0x%x", phys_addr); - ret = PTR_ERR(phys_map); - goto err_client; - } -@@ -494,36 +484,36 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - struct max9x_common *ser_common = max9x_client_to_common(serial_link->remote.client); - - virt_map = ser_common->map; -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { -- dev_info(common->dev, "Remap not necessary"); -+ dev_info(dev, "Remap not necessary"); - ret = 0; - goto err_regmap; - } - - ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present at 0x%02x", phys_addr); -+ dev_err(dev, "Device not present at 0x%02x", phys_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID before: 0x%02x", val); -+ dev_info(dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { -- dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", -+ dev_err(dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); - goto err_regmap; - } - -- msleep(100); -+ usleep_range(1000, 1050); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { -- dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); -+ dev_err(dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_regmap; - } else { -- dev_info(common->dev, "DEV_ID after: 0x%02x", val); -+ dev_info(dev, "DEV_ID after: 0x%02x", val); - } - - err_regmap: -@@ -531,8 +521,6 @@ static int max9x_remap_serializers_resume(struct max9x_common *common, unsigned - err_client: - i2c_unregister_device(phys_client); - -- max9x_des_deisolate_serial_link(common, link_id); -- - return ret; - } - -@@ -582,42 +570,31 @@ int max9x_common_resume(struct max9x_common *common) - struct max9x_common *des_common = NULL; - struct device *dev = common->dev; - u32 des_link; -- u32 phys_addr, virt_addr; - int ret = 0; -- int retry = 50; - -- if (dev->platform_data) { -+ if (dev->platform_data && common->type == MAX9X_SERIALIZER) { - struct max9x_pdata *pdata = dev->platform_data; -+ WARN_ON(pdata->num_serial_links < 1); - -- virt_addr = common->client->addr; -- phys_addr = pdata->phys_addr ? pdata->phys_addr : virt_addr; -- -- if (common->type == MAX9X_SERIALIZER) { -- WARN_ON(pdata->num_serial_links < 1); -- -- des_common = max9x_client_to_common(pdata->serial_links[0].des_client); -- if (des_common) { -- des_link = pdata->serial_links[0].des_link_id; -- ret = max9x_remap_serializers_resume(des_common, des_link); -- if (ret) -- return ret; -- ret = max9x_des_isolate_serial_link(des_common, des_link); -- if (ret) -- goto err_reset_serializer; -- } -+ des_common = max9x_client_to_common( -+ pdata->serial_links[0].des_client); -+ if (des_common) { -+ des_link = pdata->serial_links[0].des_link_id; -+ ret = max9x_des_isolate_serial_link(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ ret = max9x_remap_serializers_resume(des_common, des_link); -+ if (ret) -+ goto err_deisolate; -+ } else { -+ return ret; - } - } - -- while (retry--) { -- ret = max9x_verify_devid(common); -- if (ret) -- msleep(100); -- else -- break; -- } -+ ret = max9x_verify_devid(common); - if (ret) { - dev_err(dev, "can't get devid after resume"); -- goto err_deisolate; -+ goto err_reset_serializer; - } - - ret = max9x_enable_resume(common); -@@ -632,20 +609,14 @@ int max9x_common_resume(struct max9x_common *common) - goto err_disable; - } - -- if (common->type == MAX9X_SERIALIZER && des_common) { -+ if (common->type == MAX9X_SERIALIZER && des_common) - max9x_des_deisolate_serial_link(des_common, des_link); -- } - - return 0; - - err_disable: - max9x_disable(common); - --err_deisolate: -- if (common->type == MAX9X_SERIALIZER && des_common) { -- max9x_des_deisolate_serial_link(des_common, des_link); -- } -- - err_reset_serializer: - if (common->type == MAX9X_SERIALIZER) { - if (common->common_ops && common->common_ops->remap_reset) { -@@ -655,6 +626,10 @@ int max9x_common_resume(struct max9x_common *common) - } - } - -+err_deisolate: -+ if (common->type == MAX9X_SERIALIZER && des_common) -+ max9x_des_deisolate_serial_link(des_common, des_link); -+ - return ret; - } - -@@ -664,26 +639,11 @@ int max9x_common_suspend(struct max9x_common *common) - - dev_dbg(common->dev, "try to suspend"); - -- max9x_disable_translations(common); -- - for (link_id = 0; link_id < common->num_serial_links; link_id++) - max9x_disable_serial_link(common, link_id); - - max9x_disable(common); - -- if (common->type == MAX9X_SERIALIZER) { -- struct device *dev = common->dev; -- int ret; -- -- if (dev->platform_data) { -- if (common->common_ops && common->common_ops->remap_reset) { -- ret = common->common_ops->remap_reset(common); -- if (ret) -- return ret; -- } -- } -- } -- - return 0; - } - -@@ -886,11 +846,15 @@ void max9x_destroy(struct max9x_common *common) - /* unregister devices? */ - - v4l2_async_unregister_subdev(&common->v4l.sd); -+ v4l2_subdev_cleanup(&common->v4l.sd); - media_entity_cleanup(&common->v4l.sd.entity); - - i2c_mux_del_adapters(common->muxc); - mutex_destroy(&common->link_mutex); - mutex_destroy(&common->isolate_mutex); -+ for (int i = 0; i < common->num_csi_links; i++) { -+ mutex_destroy(&common->csi_link[i].csi_mutex); -+ } - } - EXPORT_SYMBOL(max9x_destroy); - -@@ -1018,6 +982,7 @@ int max9x_enable(struct max9x_common *common) - ret = max9x_remap_addr(common); - if (ret) - goto err; -+ msleep(20); - } - - if (common->common_ops && common->common_ops->enable) { -@@ -1099,7 +1064,7 @@ int max9x_verify_devid(struct max9x_common *common) - * Fetch and output chip name + revision - * try both virtual address and physical address - */ -- ret = regmap_read(map, MAX9X_DEV_ID, &dev_id); -+ ret = regmap_read_retry(map, MAX9X_DEV_ID, &dev_id); - if (ret) { - dev_warn(dev, "Failed to read chip ID from virtual address"); - if (phys_map) { -@@ -1119,7 +1084,7 @@ int max9x_verify_devid(struct max9x_common *common) - } - common->des = &max9x_chips[chip_type]; - common->type = common->des->serdes_type; -- TRY(ret, regmap_read(map, common->des->rev_reg, &dev_rev)); -+ TRY(ret, regmap_read_retry(map, common->des->rev_reg, &dev_rev)); - dev_rev = FIELD_GET(MAX9X_DEV_REV_FIELD, dev_rev); - - dev_info(dev, "Detected MAX9x chip ID 0x%x revision 0x%x", dev_id, dev_rev); -@@ -1149,7 +1114,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - ret = max9x_des_isolate_serial_link(common, link_id); - if (ret) -- return ret; -+ goto err_deisolate; - - phys_addr = serial_link->remote.pdata->phys_addr; - virt_addr = serial_link->remote.pdata->board_info.addr; -@@ -1176,14 +1141,14 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - if (IS_ERR_OR_NULL(virt_map)) - goto err_virt_client; - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (!ret) { - dev_info(common->dev, "Remap not necessary"); - ret = 0; - goto err_virt_regmap; - } - -- ret = regmap_read(phys_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(phys_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present at 0x%02x", phys_addr); - goto err_virt_regmap; -@@ -1191,7 +1156,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - dev_info(common->dev, "DEV_ID before: 0x%02x", val); - } - -- ret = regmap_write(phys_map, 0x00, (virt_addr & 0x7f) << 1); -+ ret = regmap_write_retry(phys_map, 0x00, (virt_addr & 0x7f) << 1); - if (ret) { - dev_err(common->dev, "Failed to remap serialzier from 0x%02x to 0x%02x (%d)", - phys_addr, virt_addr, ret); -@@ -1200,7 +1165,7 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - - usleep_range(1000, 1050); - -- ret = regmap_read(virt_map, MAX9X_DEV_ID, &val); -+ ret = regmap_read_retry(virt_map, MAX9X_DEV_ID, &val); - if (ret) { - dev_err(common->dev, "Device not present after remap to 0x%02x", virt_addr); - goto err_virt_regmap; -@@ -1218,10 +1183,11 @@ int max9x_remap_serializers(struct max9x_common *common, unsigned int link_id) - err_client: - i2c_unregister_device(phys_client); - -- max9x_deselect_i2c_chan(common->muxc, link_id); -- -+err_deisolate: - max9x_des_deisolate_serial_link(common, link_id); - -+ max9x_deselect_i2c_chan(common->muxc, link_id); -+ - return ret; - } - -@@ -1320,7 +1286,7 @@ static void max9x_des_s_csi_link(struct max9x_common *common, - if (common->csi_link[csi_link_id].config.auto_start) - continue; /* Already started at probe */ - -- if (enable) { -+ if (enable && !video_pipe->config.map[map_id].is_csi_enabled) { - if (common->csi_link_ops->enable) { - err = common->csi_link_ops->enable( - common, csi_link_id); -@@ -1329,8 +1295,9 @@ static void max9x_des_s_csi_link(struct max9x_common *common, - common->dev, - "csi_link_ops->enable CSI %d failed: %d", - csi_link_id, err); -+ video_pipe->config.map[map_id].is_csi_enabled = true; - } -- } else { -+ } else if (!enable && video_pipe->config.map[map_id].is_csi_enabled) { - if (common->csi_link_ops->disable) { - err = common->csi_link_ops->disable( - common, csi_link_id); -@@ -1339,6 +1306,7 @@ static void max9x_des_s_csi_link(struct max9x_common *common, - common->dev, - "csi_link_ops->disable CSI %d failed: %d", - csi_link_id, err); -+ video_pipe->config.map[map_id].is_csi_enabled = false; - } - } - } -@@ -1352,7 +1320,7 @@ static int _max9x_s_remote_stream(struct max9x_common *common, u32 sink_pad, - struct v4l2_subdev *remote_sd; - int ret = 0; - -- if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ if (sink_pad >= common->v4l.num_pads) - return -EINVAL; - - remote_pad = media_pad_remote_pad_first(&common->v4l.pads[sink_pad]); -@@ -1393,10 +1361,10 @@ static int _max9x_s_remote_stream(struct max9x_common *common, u32 sink_pad, - static int _max9x_des_set_stream(struct max9x_common *common, u32 sink_pad, - u32 sink_stream, int enable) - { -- u32 rxport; -+ int rxport = 0; - int ret = 0; - -- if (sink_pad < 0 || sink_pad >= common->v4l.num_pads) -+ if (sink_pad >= common->v4l.num_pads) - return -EINVAL; - - rxport = sink_pad - common->num_csi_links; -@@ -1524,7 +1492,7 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - return ERR_PTR(-EINVAL); - } - -- if (fmt->pad < 0 || fmt->pad >= common->v4l.num_pads) { -+ if (fmt->pad >= common->v4l.num_pads) { - dev_err(sd->dev, "%s invalid pad %d", __func__, fmt->pad); - return ERR_PTR(-EINVAL); - } -@@ -1532,10 +1500,7 @@ static struct v4l2_mbus_framefmt *__max9x_get_ffmt(struct v4l2_subdev *sd, - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_state_get_format(v4l2_state, fmt->pad, fmt->stream); - -- if (fmt->pad >= 0 && fmt->pad < common->v4l.num_pads) -- return &common->v4l.ffmts[fmt->pad]; -- -- return ERR_PTR(-EINVAL); -+ return &common->v4l.ffmts[fmt->pad]; - } - - static int max9x_get_fmt(struct v4l2_subdev *sd, -@@ -1599,7 +1564,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - - if (((common->type != MAX9X_DESERIALIZER) && - (common->type != MAX9X_SERIALIZER)) || -- pad < 0 || pad >= common->v4l.num_pads) -+ pad >= common->v4l.num_pads) - return -EINVAL; - - desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2; -@@ -1611,7 +1576,7 @@ static int max9x_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - for_each_active_route(&state->routing, route) { - if (route->source_pad != pad) - continue; -- if (route->sink_pad >= common->v4l.num_pads) { -+ if (unlikely(route->sink_pad >= common->v4l.num_pads)) { - ret = -EINVAL; - dev_err(common->dev, "Found invalid route sink_pad!"); - goto out_unlock; -@@ -1786,6 +1751,7 @@ static int max9x_registered(struct v4l2_subdev *sd) - if (subdev_pdata) { - struct max9x_pdata *ser_pdata = - subdev_pdata->board_info.platform_data; -+ struct v4l2_subdev *subdev = NULL; - - WARN_ON(ser_pdata->num_serial_links < 1); - -@@ -1797,13 +1763,12 @@ static int max9x_registered(struct v4l2_subdev *sd) - * physical i2c at the same time - */ - ret = max9x_des_isolate_serial_link(common, link_id); -- if (ret) -- return ret; -- -- struct v4l2_subdev *subdev = -- v4l2_i2c_new_subdev_board(sd->v4l2_dev, -- common->muxc->adapter[link_id], -- &subdev_pdata->board_info, NULL); -+ if (!ret) -+ subdev = v4l2_i2c_new_subdev_board( -+ sd->v4l2_dev, -+ common->muxc->adapter[link_id], -+ &subdev_pdata->board_info, -+ NULL); - - ret = max9x_des_deisolate_serial_link(common, link_id); - if (ret) -@@ -1858,7 +1823,7 @@ static int max9x_registered(struct v4l2_subdev *sd) - .dev_id = "", - .table = { - GPIO_LOOKUP("", 0, "reset", -- GPIO_ACTIVE_HIGH), -+ GPIO_ACTIVE_LOW), - {} - }, - }; -@@ -2140,7 +2105,7 @@ int max9x_disable_serial_link(struct max9x_common *common, unsigned int link_id) - struct device *dev = common->dev; - int ret; - -- if (link_id >= common->num_serial_links) -+ if (unlikely(link_id >= common->num_serial_links)) - return 0; - - serial_link = &common->serial_link[link_id]; -@@ -2423,6 +2388,7 @@ static int max9x_parse_csi_link_pdata(struct max9x_common *common, - - struct max9x_serdes_csi_link *csi_link = &common->csi_link[csi_link_id]; - -+ mutex_init(&csi_link->csi_mutex); - csi_link->enabled = true; - - csi_link->config.num_maps = csi_link_pdata->num_maps; -@@ -2461,13 +2427,9 @@ static int max9x_parse_subdev_pdata(struct max9x_common *common, - int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - unsigned long timeout = jiffies + msecs_to_jiffies(10000); - -- dev_dbg(common->dev, "try to select %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2481,7 +2443,7 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - usleep_range(1000, 1050); - - if (time_is_before_jiffies(timeout)) { -- dev_dbg(common->dev, "select %d TIMEOUT", chan_id); -+ dev_warn(common->dev, "select %d TIMEOUT", chan_id); - return -ETIMEDOUT; - } - } while (1); -@@ -2499,12 +2461,8 @@ int max9x_select_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - int max9x_deselect_i2c_chan(struct i2c_mux_core *muxc, u32 chan_id) - { - struct max9x_common *common = i2c_mux_priv(muxc); -- struct i2c_client *client = common->serial_link[chan_id].remote.client; - int ret = 0; - -- dev_dbg(common->dev, "try to deselect %d for %s", chan_id, -- client ? dev_name(&client->dev) : ""); -- - if (unlikely(chan_id > common->num_serial_links)) - return -EINVAL; - -@@ -2633,8 +2591,6 @@ int max9x_setup_translations(struct max9x_common *common) - break; - } - -- msleep(10); -- - return err; - } - -diff --git a/drivers/media/i2c/max9x/serdes.h b/drivers/media/i2c/max9x/serdes.h -index 227f61e03229..3ea4ba4d7e66 100644 ---- a/drivers/media/i2c/max9x/serdes.h -+++ b/drivers/media/i2c/max9x/serdes.h -@@ -41,10 +41,7 @@ - #include - #include - --#if IS_ENABLED(CONFIG_VIDEO_INTEL_IPU6) --#include "ipu6-isys.h" --#endif --#include -+#include - #include "max9x_pdata.h" - - #define MAX9X_VDD_REGULATOR_NAME "vdd" -@@ -219,6 +216,7 @@ struct max9x_serdes_csi_link { - bool enabled; - unsigned int usecount; - struct max9x_serdes_csi_config config; -+ struct mutex csi_mutex; - }; - - struct max9x_serdes_video_pipe { --- -2.43.0 - diff --git a/SPECS/kernel/0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo b/SPECS/kernel/0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo new file mode 100644 index 000000000..85abd949f --- /dev/null +++ b/SPECS/kernel/0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo @@ -0,0 +1,40 @@ +From 19d42f94450222eb8ccd902bee736c5ad133461c Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 12:55:26 -0500 +Subject: [PATCH 19/21] tools/power turbostat: Print percentages in 8-columns + +Added counters that are FORMAT_PERCENT +do not need to be 64-bits -- 32 is plenty. +This allows the output code to fit them, +and their header, into 8-columns. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index f59dcee3c816e..28625143a1b99 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -3482,7 +3482,7 @@ int format_counters(PER_THREAD_PARAMS) + + /* Totl%C0, Any%C0 GFX%C0 CPUGFX% */ + if (DO_BIC(BIC_Totl_c0)) +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100 * p->pkg_wtd_core_c0 / tsc); /* can exceed 100% */ ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), 100 * p->pkg_wtd_core_c0 / tsc); /* can exceed 100% */ + if (DO_BIC(BIC_Any_c0)) + outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), pct(p->pkg_any_core_c0 / tsc)); + if (DO_BIC(BIC_GFX_c0)) +@@ -10997,7 +10997,7 @@ void probe_cpuidle_residency(void) + if (is_deferred_skip(name_buf)) + continue; + +- add_counter(0, path, name_buf, 64, SCOPE_CPU, COUNTER_USEC, FORMAT_PERCENT, SYSFS_PERCPU, 0); ++ add_counter(0, path, name_buf, 32, SCOPE_CPU, COUNTER_USEC, FORMAT_PERCENT, SYSFS_PERCPU, 0); + + if (state > max_state) + max_state = state; +-- +2.43.0 + diff --git a/SPECS/kernel/0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi b/SPECS/kernel/0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi deleted file mode 100644 index 4b63d8cb7..000000000 --- a/SPECS/kernel/0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi +++ /dev/null @@ -1,267 +0,0 @@ -From aaa541b31cb3920955b836a68e85863e70726970 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 2 Jan 2024 23:51:33 -0800 -Subject: [PATCH 20/44] KVM: nVMX: Add prerequisites to SHADOW_FIELD_R[OW] - macros - -Add VMX feature checks before accessing VMCS fields via SHADOW_FIELD_R[OW] -macros, as some fields may not be supported on all CPUs. - -Functions like copy_shadow_to_vmcs12() and copy_vmcs12_to_shadow() access -VMCS fields that may not exist on certain hardware, such as -INJECTED_EVENT_DATA. To avoid VMREAD/VMWRITE warnings, skip syncing fields -tied to unsupported VMX features. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. - -Change since v2: -* Add __SHADOW_FIELD_R[OW] for better readability or maintability (Sean). ---- - arch/x86/kvm/vmx/nested.c | 79 +++++++++++++++++++-------- - arch/x86/kvm/vmx/vmcs_shadow_fields.h | 41 +++++++++----- - 2 files changed, 83 insertions(+), 37 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 7b1a45f06ae6..9374560fc91e 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -55,14 +55,14 @@ struct shadow_vmcs_field { - u16 offset; - }; - static struct shadow_vmcs_field shadow_read_only_fields[] = { --#define SHADOW_FIELD_RO(x, y) { x, offsetof(struct vmcs12, y) }, -+#define __SHADOW_FIELD_RO(x, y, c) { x, offsetof(struct vmcs12, y) }, - #include "vmcs_shadow_fields.h" - }; - static int max_shadow_read_only_fields = - ARRAY_SIZE(shadow_read_only_fields); - - static struct shadow_vmcs_field shadow_read_write_fields[] = { --#define SHADOW_FIELD_RW(x, y) { x, offsetof(struct vmcs12, y) }, -+#define __SHADOW_FIELD_RW(x, y, c) { x, offsetof(struct vmcs12, y) }, - #include "vmcs_shadow_fields.h" - }; - static int max_shadow_read_write_fields = -@@ -85,6 +85,17 @@ static void init_vmcs_shadow_fields(void) - pr_err("Missing field from shadow_read_only_field %x\n", - field + 1); - -+ switch (field) { -+#define __SHADOW_FIELD_RO(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#include "vmcs_shadow_fields.h" -+ default: -+ break; -+ } -+ - clear_bit(field, vmx_vmread_bitmap); - if (field & 1) - #ifdef CONFIG_X86_64 -@@ -110,24 +121,13 @@ static void init_vmcs_shadow_fields(void) - field <= GUEST_TR_AR_BYTES, - "Update vmcs12_write_any() to drop reserved bits from AR_BYTES"); - -- /* -- * PML and the preemption timer can be emulated, but the -- * processor cannot vmwrite to fields that don't exist -- * on bare metal. -- */ - switch (field) { -- case GUEST_PML_INDEX: -- if (!cpu_has_vmx_pml()) -- continue; -- break; -- case VMX_PREEMPTION_TIMER_VALUE: -- if (!cpu_has_vmx_preemption_timer()) -- continue; -- break; -- case GUEST_INTR_STATUS: -- if (!cpu_has_vmx_apicv()) -- continue; -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ - break; -+#include "vmcs_shadow_fields.h" - default: - break; - } -@@ -1669,8 +1669,8 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) - /* - * Copy the writable VMCS shadow fields back to the VMCS12, in case they have - * been modified by the L1 guest. Note, "writable" in this context means -- * "writable by the guest", i.e. tagged SHADOW_FIELD_RW; the set of -- * fields tagged SHADOW_FIELD_RO may or may not align with the "read-only" -+ * "writable by the guest", i.e. tagged __SHADOW_FIELD_RW; the set of -+ * fields tagged __SHADOW_FIELD_RO may or may not align with the "read-only" - * VM-exit information fields (which are actually writable if the vCPU is - * configured to support "VMWRITE to any supported field in the VMCS"). - */ -@@ -1691,6 +1691,18 @@ static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx) - - for (i = 0; i < max_shadow_read_write_fields; i++) { - field = shadow_read_write_fields[i]; -+ -+ switch (field.encoding) { -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#include "vmcs_shadow_fields.h" -+ default: -+ break; -+ } -+ - val = __vmcs_readl(field.encoding); - vmcs12_write_any(vmcs12, field.encoding, field.offset, val); - } -@@ -1725,6 +1737,23 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx) - for (q = 0; q < ARRAY_SIZE(fields); q++) { - for (i = 0; i < max_fields[q]; i++) { - field = fields[q][i]; -+ -+ switch (field.encoding) { -+#define __SHADOW_FIELD_RO(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ if (!(c)) \ -+ continue; \ -+ break; -+#include "vmcs_shadow_fields.h" -+ default: -+ break; -+ } -+ - val = vmcs12_read_any(vmcs12, field.encoding, - field.offset); - __vmcs_writel(field.encoding, val); -@@ -6028,9 +6057,10 @@ static int handle_vmread(struct kvm_vcpu *vcpu) - static bool is_shadow_field_rw(unsigned long field) - { - switch (field) { --#define SHADOW_FIELD_RW(x, y) case x: -+#define __SHADOW_FIELD_RW(x, y, c) \ -+ case x: \ -+ return c; - #include "vmcs_shadow_fields.h" -- return true; - default: - break; - } -@@ -6040,9 +6070,10 @@ static bool is_shadow_field_rw(unsigned long field) - static bool is_shadow_field_ro(unsigned long field) - { - switch (field) { --#define SHADOW_FIELD_RO(x, y) case x: -+#define __SHADOW_FIELD_RO(x, y, c) \ -+ case x: \ -+ return c; - #include "vmcs_shadow_fields.h" -- return true; - default: - break; - } -diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -index da338327c2b3..607945ada35f 100644 ---- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h -+++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h -@@ -1,14 +1,17 @@ --#if !defined(SHADOW_FIELD_RO) && !defined(SHADOW_FIELD_RW) -+#if !defined(__SHADOW_FIELD_RO) && !defined(__SHADOW_FIELD_RW) - BUILD_BUG_ON(1) - #endif - --#ifndef SHADOW_FIELD_RO --#define SHADOW_FIELD_RO(x, y) -+#ifndef __SHADOW_FIELD_RO -+#define __SHADOW_FIELD_RO(x, y, c) - #endif --#ifndef SHADOW_FIELD_RW --#define SHADOW_FIELD_RW(x, y) -+#ifndef __SHADOW_FIELD_RW -+#define __SHADOW_FIELD_RW(x, y, c) - #endif - -+#define SHADOW_FIELD_RO(x, y) __SHADOW_FIELD_RO(x, y, true) -+#define SHADOW_FIELD_RW(x, y) __SHADOW_FIELD_RW(x, y, true) -+ - /* - * We do NOT shadow fields that are modified when L0 - * traps and emulates any vmx instruction (e.g. VMPTRLD, -@@ -32,8 +35,12 @@ BUILD_BUG_ON(1) - */ - - /* 16-bits */ --SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status) --SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index) -+__SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status, cpu_has_vmx_apicv()) -+/* -+ * PML can be emulated, but the processor cannot vmwrite to the VMCS field -+ * GUEST_PML_INDEX that doesn't exist on bare metal. -+ */ -+__SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index, cpu_has_vmx_pml()) - SHADOW_FIELD_RW(HOST_FS_SELECTOR, host_fs_selector) - SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) - -@@ -41,9 +48,9 @@ SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) - SHADOW_FIELD_RO(VM_EXIT_REASON, vm_exit_reason) - SHADOW_FIELD_RO(VM_EXIT_INTR_INFO, vm_exit_intr_info) - SHADOW_FIELD_RO(VM_EXIT_INSTRUCTION_LEN, vm_exit_instruction_len) -+SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) - SHADOW_FIELD_RO(IDT_VECTORING_INFO_FIELD, idt_vectoring_info_field) - SHADOW_FIELD_RO(IDT_VECTORING_ERROR_CODE, idt_vectoring_error_code) --SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) - SHADOW_FIELD_RO(GUEST_CS_AR_BYTES, guest_cs_ar_bytes) - SHADOW_FIELD_RO(GUEST_SS_AR_BYTES, guest_ss_ar_bytes) - SHADOW_FIELD_RW(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control) -@@ -54,7 +61,12 @@ SHADOW_FIELD_RW(VM_ENTRY_INTR_INFO_FIELD, vm_entry_intr_info_field) - SHADOW_FIELD_RW(VM_ENTRY_INSTRUCTION_LEN, vm_entry_instruction_len) - SHADOW_FIELD_RW(TPR_THRESHOLD, tpr_threshold) - SHADOW_FIELD_RW(GUEST_INTERRUPTIBILITY_INFO, guest_interruptibility_info) --SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value) -+/* -+ * The preemption timer can be emulated, but the processor cannot vmwrite to -+ * the VMCS field VMX_PREEMPTION_TIMER_VALUE that doesn't exist on bare metal. -+ */ -+__SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value, -+ cpu_has_vmx_preemption_timer()) - - /* Natural width */ - SHADOW_FIELD_RO(EXIT_QUALIFICATION, exit_qualification) -@@ -74,10 +86,13 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) - /* 64-bit */ - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) - SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) --SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) --SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) --SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) --SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) -+__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data, cpu_has_vmx_fred()) -+__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data, cpu_has_vmx_fred()) -+__SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data, cpu_has_vmx_fred()) -+__SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data, cpu_has_vmx_fred()) - - #undef SHADOW_FIELD_RO - #undef SHADOW_FIELD_RW -+ -+#undef __SHADOW_FIELD_RO -+#undef __SHADOW_FIELD_RW --- -2.43.0 - diff --git a/SPECS/kernel/0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi b/SPECS/kernel/0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi new file mode 100644 index 000000000..43c43185e --- /dev/null +++ b/SPECS/kernel/0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi @@ -0,0 +1,197 @@ +From f8cc3a950037a639a22008ed75f78a41af8a879f Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Sun, 4 Jun 2023 23:58:33 -0700 +Subject: [PATCH 20/44] KVM: nVMX: Validate FRED-related VMCS fields + +Extend nested VMX field validation to include FRED-specific VMCS fields, +mirroring hardware behavior. + +This enables support for nested FRED by ensuring control and guest/host +state fields are properly checked. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/vmx/nested.c | 117 +++++++++++++++++++++++++++++++++----- + 1 file changed, 104 insertions(+), 13 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index da49de1800609..a6ebe3f2fdd22 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -3032,6 +3032,8 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + struct vmcs12 *vmcs12) + { + struct vcpu_vmx *vmx = to_vmx(vcpu); ++ bool fred_enabled = (vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) && ++ (vmcs12->guest_cr4 & X86_CR4_FRED); + + if (CC(!vmx_control_verify(vmcs12->vm_entry_controls, + vmx->nested.msrs.entry_ctls_low, +@@ -3049,22 +3051,11 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + u8 vector = intr_info & INTR_INFO_VECTOR_MASK; + u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; + bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; ++ bool has_nested_exception = vmx->nested.msrs.basic & VMX_BASIC_NESTED_EXCEPTION; + bool urg = nested_cpu_has2(vmcs12, + SECONDARY_EXEC_UNRESTRICTED_GUEST); + bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; + +- /* VM-entry interruption-info field: interruption type */ +- if (CC(intr_type == INTR_TYPE_RESERVED) || +- CC(intr_type == INTR_TYPE_OTHER_EVENT && +- !nested_cpu_supports_monitor_trap_flag(vcpu))) +- return -EINVAL; +- +- /* VM-entry interruption-info field: vector */ +- if (CC(intr_type == INTR_TYPE_NMI_INTR && vector != NMI_VECTOR) || +- CC(intr_type == INTR_TYPE_HARD_EXCEPTION && vector > 31) || +- CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) +- return -EINVAL; +- + /* + * Cannot deliver error code in real mode or if the interrupt + * type is not hardware exception. For other cases, do the +@@ -3088,8 +3079,28 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + if (CC(intr_info & INTR_INFO_RESVD_BITS_MASK)) + return -EINVAL; + +- /* VM-entry instruction length */ ++ /* ++ * When the CPU enumerates VMX nested-exception support, bit 13 ++ * (set to indicate a nested exception) of the intr info field ++ * may have value 1. Otherwise bit 13 is reserved. ++ */ ++ if (CC(!(has_nested_exception && intr_type == INTR_TYPE_HARD_EXCEPTION) && ++ intr_info & INTR_INFO_NESTED_EXCEPTION_MASK)) ++ return -EINVAL; ++ + switch (intr_type) { ++ case INTR_TYPE_EXT_INTR: ++ break; ++ case INTR_TYPE_RESERVED: ++ return -EINVAL; ++ case INTR_TYPE_NMI_INTR: ++ if (CC(vector != NMI_VECTOR)) ++ return -EINVAL; ++ break; ++ case INTR_TYPE_HARD_EXCEPTION: ++ if (CC(vector > 31)) ++ return -EINVAL; ++ break; + case INTR_TYPE_SOFT_EXCEPTION: + case INTR_TYPE_SOFT_INTR: + case INTR_TYPE_PRIV_SW_EXCEPTION: +@@ -3097,6 +3108,24 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, + CC(vmcs12->vm_entry_instruction_len == 0 && + CC(!nested_cpu_has_zero_length_injection(vcpu)))) + return -EINVAL; ++ break; ++ case INTR_TYPE_OTHER_EVENT: ++ switch (vector) { ++ case 0: ++ if (CC(!nested_cpu_supports_monitor_trap_flag(vcpu))) ++ return -EINVAL; ++ break; ++ case 1: ++ case 2: ++ if (CC(!fred_enabled)) ++ return -EINVAL; ++ if (CC(vmcs12->vm_entry_instruction_len > X86_MAX_INSTRUCTION_LENGTH)) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; + } + } + +@@ -3183,9 +3212,29 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, + if (ia32e) { + if (CC(!(vmcs12->host_cr4 & X86_CR4_PAE))) + return -EINVAL; ++ if (vmcs12->vm_exit_controls & VM_EXIT_ACTIVATE_SECONDARY_CONTROLS && ++ vmcs12->secondary_vm_exit_controls & SECONDARY_VM_EXIT_LOAD_IA32_FRED) { ++ if (CC(vmcs12->host_ia32_fred_config & ++ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || ++ CC(vmcs12->host_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->host_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->host_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->host_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->host_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->host_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_config & PAGE_MASK, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_rsp3, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->host_ia32_fred_ssp3, vcpu))) ++ return -EINVAL; ++ } + } else { + if (CC(vmcs12->vm_entry_controls & VM_ENTRY_IA32E_MODE) || + CC(vmcs12->host_cr4 & X86_CR4_PCIDE) || ++ CC(vmcs12->host_cr4 & X86_CR4_FRED) || + CC((vmcs12->host_rip) >> 32)) + return -EINVAL; + } +@@ -3354,6 +3403,48 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, + CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) + return -EINVAL; + ++ if (ia32e) { ++ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_FRED) { ++ if (CC(vmcs12->guest_ia32_fred_config & ++ (BIT_ULL(11) | GENMASK_ULL(5, 4) | BIT_ULL(2))) || ++ CC(vmcs12->guest_ia32_fred_rsp1 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->guest_ia32_fred_rsp2 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->guest_ia32_fred_rsp3 & GENMASK_ULL(5, 0)) || ++ CC(vmcs12->guest_ia32_fred_ssp1 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->guest_ia32_fred_ssp2 & GENMASK_ULL(2, 0)) || ++ CC(vmcs12->guest_ia32_fred_ssp3 & GENMASK_ULL(2, 0)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_config & PAGE_MASK, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_rsp3, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp1, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp2, vcpu)) || ++ CC(is_noncanonical_msr_address(vmcs12->guest_ia32_fred_ssp3, vcpu))) ++ return -EINVAL; ++ } ++ if (vmcs12->guest_cr4 & X86_CR4_FRED) { ++ unsigned int ss_dpl = VMX_AR_DPL(vmcs12->guest_ss_ar_bytes); ++ switch (ss_dpl) { ++ case 0: ++ if (CC(!(vmcs12->guest_cs_ar_bytes & VMX_AR_L_MASK))) ++ return -EINVAL; ++ break; ++ case 1: ++ case 2: ++ return -EINVAL; ++ case 3: ++ if (CC(vmcs12->guest_rflags & X86_EFLAGS_IOPL)) ++ return -EINVAL; ++ if (CC(vmcs12->guest_interruptibility_info & GUEST_INTR_STATE_STI)) ++ return -EINVAL; ++ break; ++ } ++ } ++ } else { ++ if (CC(vmcs12->guest_cr4 & X86_CR4_FRED)) ++ return -EINVAL; ++ } ++ + if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { + if (nested_vmx_check_cet_state_common(vcpu, vmcs12->guest_s_cet, + vmcs12->guest_ssp, +-- +2.43.0 + diff --git a/SPECS/kernel/0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet b/SPECS/kernel/0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet deleted file mode 100644 index 774e57be5..000000000 --- a/SPECS/kernel/0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet +++ /dev/null @@ -1,290 +0,0 @@ -From fa7418f912e0d36ec4f998dfb6d458ec23dee047 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:50 -0700 -Subject: [PATCH 20/24] KVM: x86: Enable CET virtualization for VMX and - advertise to userspace - -Expose CET features to guest if KVM/host can support them, clear CPUID -feature bits if KVM/host cannot support. - -Set CPUID feature bits so that CET features are available in guest CPUID. -Add CR4.CET bit support in order to allow guest set CET master control -bit. - -Disable KVM CET feature if unrestricted_guest is unsupported/disabled as -KVM does not support emulating CET. - -The CET load-bits in VM_ENTRY/VM_EXIT control fields should be set to make -guest CET xstates isolated from host's. - -On platforms with VMX_BASIC[bit56] == 0, inject #CP at VMX entry with error -code will fail, and if VMX_BASIC[bit56] == 1, #CP injection with or without -error code is allowed. Disable CET feature bits if the MSR bit is cleared -so that nested VMM can inject #CP if and only if VMX_BASIC[bit56] == 1. - -Don't expose CET feature if either of {U,S}_CET xstate bits is cleared -in host XSS or if XSAVES isn't supported. - -CET MSRs are reset to 0s after RESET, power-up and INIT, clear guest CET -xsave-area fields so that guest CET MSRs are reset to 0s after the events. - -Meanwhile explicitly disable SHSTK and IBT for SVM because CET KVM enabling -for SVM is not ready. - -Signed-off-by: Yang Weijiang -Signed-off-by: Mathias Krause -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/include/asm/kvm_host.h | 2 +- - arch/x86/include/asm/vmx.h | 1 + - arch/x86/kvm/cpuid.c | 2 ++ - arch/x86/kvm/svm/svm.c | 4 ++++ - arch/x86/kvm/vmx/capabilities.h | 5 +++++ - arch/x86/kvm/vmx/vmx.c | 30 +++++++++++++++++++++++++++++- - arch/x86/kvm/vmx/vmx.h | 4 +++- - arch/x86/kvm/x86.c | 22 +++++++++++++++++++--- - arch/x86/kvm/x86.h | 3 +++ - 9 files changed, 67 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 6b567512984a..3b64414a6730 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -142,7 +142,7 @@ - | X86_CR4_OSXSAVE | X86_CR4_SMEP | X86_CR4_FSGSBASE \ - | X86_CR4_OSXMMEXCPT | X86_CR4_LA57 | X86_CR4_VMXE \ - | X86_CR4_SMAP | X86_CR4_PKE | X86_CR4_UMIP \ -- | X86_CR4_LAM_SUP)) -+ | X86_CR4_LAM_SUP | X86_CR4_CET)) - - #define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR) - -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index 6a3fb81e852a..b92ff87e3560 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -135,6 +135,7 @@ - #define VMX_BASIC_DUAL_MONITOR_TREATMENT BIT_ULL(49) - #define VMX_BASIC_INOUT BIT_ULL(54) - #define VMX_BASIC_TRUE_CTLS BIT_ULL(55) -+#define VMX_BASIC_NO_HW_ERROR_CODE_CC BIT_ULL(56) - - static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic) - { -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index b9f278efb907..5f76dd7a7620 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -946,6 +946,7 @@ void kvm_set_cpu_caps(void) - VENDOR_F(WAITPKG), - F(SGX_LC), - F(BUS_LOCK_DETECT), -+ F(SHSTK), - ); - - /* -@@ -972,6 +973,7 @@ void kvm_set_cpu_caps(void) - F(AMX_INT8), - F(AMX_BF16), - F(FLUSH_L1D), -+ F(IBT), - ); - - if (boot_cpu_has(X86_FEATURE_AMD_IBPB_RET) && -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 2797c3ab7854..239436ae074b 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -5285,6 +5285,10 @@ static __init void svm_set_cpu_caps(void) - kvm_caps.supported_perf_cap = 0; - kvm_caps.supported_xss = 0; - -+ /* KVM doesn't yet support CET virtualization for SVM. */ -+ kvm_cpu_cap_clear(X86_FEATURE_SHSTK); -+ kvm_cpu_cap_clear(X86_FEATURE_IBT); -+ - /* CPUID 0x80000001 and 0x8000000A (SVM features) */ - if (nested) { - kvm_cpu_cap_set(X86_FEATURE_SVM); -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index fd1d43ebe1df..79cd9d316ba4 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -73,6 +73,11 @@ static inline bool cpu_has_vmx_basic_inout(void) - return vmcs_config.basic & VMX_BASIC_INOUT; - } - -+static inline bool cpu_has_vmx_basic_no_hw_errcode(void) -+{ -+ return vmcs_config.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; -+} -+ - static inline bool cpu_has_virtual_nmis(void) - { - return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS && -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 9393abf9f183..8a9fb9a02080 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2604,6 +2604,7 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf, - { VM_ENTRY_LOAD_IA32_EFER, VM_EXIT_LOAD_IA32_EFER }, - { VM_ENTRY_LOAD_BNDCFGS, VM_EXIT_CLEAR_BNDCFGS }, - { VM_ENTRY_LOAD_IA32_RTIT_CTL, VM_EXIT_CLEAR_IA32_RTIT_CTL }, -+ { VM_ENTRY_LOAD_CET_STATE, VM_EXIT_LOAD_CET_STATE }, - }; - - memset(vmcs_conf, 0, sizeof(*vmcs_conf)); -@@ -4937,6 +4938,14 @@ void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, 0); /* 22.2.1 */ - -+ if (kvm_cpu_cap_has(X86_FEATURE_SHSTK)) { -+ vmcs_writel(GUEST_SSP, 0); -+ vmcs_writel(GUEST_INTR_SSP_TABLE, 0); -+ } -+ if (kvm_cpu_cap_has(X86_FEATURE_IBT) || -+ kvm_cpu_cap_has(X86_FEATURE_SHSTK)) -+ vmcs_writel(GUEST_S_CET, 0); -+ - kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu); - - vpid_sync_context(vmx->vpid); -@@ -6385,6 +6394,10 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - if (vmcs_read32(VM_EXIT_MSR_STORE_COUNT) > 0) - vmx_dump_msrs("guest autostore", &vmx->msr_autostore.guest); - -+ if (vmentry_ctl & VM_ENTRY_LOAD_CET_STATE) -+ pr_err("S_CET = 0x%016lx, SSP = 0x%016lx, SSP TABLE = 0x%016lx\n", -+ vmcs_readl(GUEST_S_CET), vmcs_readl(GUEST_SSP), -+ vmcs_readl(GUEST_INTR_SSP_TABLE)); - pr_err("*** Host State ***\n"); - pr_err("RIP = 0x%016lx RSP = 0x%016lx\n", - vmcs_readl(HOST_RIP), vmcs_readl(HOST_RSP)); -@@ -6415,6 +6428,10 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - vmcs_read64(HOST_IA32_PERF_GLOBAL_CTRL)); - if (vmcs_read32(VM_EXIT_MSR_LOAD_COUNT) > 0) - vmx_dump_msrs("host autoload", &vmx->msr_autoload.host); -+ if (vmexit_ctl & VM_EXIT_LOAD_CET_STATE) -+ pr_err("S_CET = 0x%016lx, SSP = 0x%016lx, SSP TABLE = 0x%016lx\n", -+ vmcs_readl(HOST_S_CET), vmcs_readl(HOST_SSP), -+ vmcs_readl(HOST_INTR_SSP_TABLE)); - - pr_err("*** Control State ***\n"); - pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", -@@ -7998,7 +8015,6 @@ static __init void vmx_set_cpu_caps(void) - kvm_cpu_cap_set(X86_FEATURE_UMIP); - - /* CPUID 0xD.1 */ -- kvm_caps.supported_xss = 0; - if (!cpu_has_vmx_xsaves()) - kvm_cpu_cap_clear(X86_FEATURE_XSAVES); - -@@ -8010,6 +8026,18 @@ static __init void vmx_set_cpu_caps(void) - - if (cpu_has_vmx_waitpkg()) - kvm_cpu_cap_check_and_set(X86_FEATURE_WAITPKG); -+ -+ /* -+ * Disable CET if unrestricted_guest is unsupported as KVM doesn't -+ * enforce CET HW behaviors in emulator. On platforms with -+ * VMX_BASIC[bit56] == 0, inject #CP at VMX entry with error code -+ * fails, so disable CET in this case too. -+ */ -+ if (!cpu_has_load_cet_ctrl() || !enable_unrestricted_guest || -+ !cpu_has_vmx_basic_no_hw_errcode()) { -+ kvm_cpu_cap_clear(X86_FEATURE_SHSTK); -+ kvm_cpu_cap_clear(X86_FEATURE_IBT); -+ } - } - - static bool vmx_is_io_intercepted(struct kvm_vcpu *vcpu, -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index 7eb57f5cb975..b8bf296eb9a4 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -484,7 +484,8 @@ static inline u8 vmx_get_rvi(void) - VM_ENTRY_LOAD_IA32_EFER | \ - VM_ENTRY_LOAD_BNDCFGS | \ - VM_ENTRY_PT_CONCEAL_PIP | \ -- VM_ENTRY_LOAD_IA32_RTIT_CTL) -+ VM_ENTRY_LOAD_IA32_RTIT_CTL | \ -+ VM_ENTRY_LOAD_CET_STATE) - - #define __KVM_REQUIRED_VMX_VM_EXIT_CONTROLS \ - (VM_EXIT_SAVE_DEBUG_CONTROLS | \ -@@ -507,6 +508,7 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_CLEAR_BNDCFGS | \ - VM_EXIT_PT_CONCEAL_PIP | \ - VM_EXIT_CLEAR_IA32_RTIT_CTL | \ -+ VM_EXIT_LOAD_CET_STATE | \ - VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 83fd91494a4b..da04331a4200 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -227,7 +227,8 @@ static struct kvm_user_return_msrs __percpu *user_return_msrs; - | XFEATURE_MASK_BNDCSR | XFEATURE_MASK_AVX512 \ - | XFEATURE_MASK_PKRU | XFEATURE_MASK_XTILE) - --#define KVM_SUPPORTED_XSS 0 -+#define KVM_SUPPORTED_XSS (XFEATURE_MASK_CET_USER | \ -+ XFEATURE_MASK_CET_KERNEL) - - bool __read_mostly allow_smaller_maxphyaddr = 0; - EXPORT_SYMBOL_GPL(allow_smaller_maxphyaddr); -@@ -9926,6 +9927,20 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - if (!kvm_cpu_cap_has(X86_FEATURE_XSAVES)) - kvm_caps.supported_xss = 0; - -+ if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) && -+ !kvm_cpu_cap_has(X86_FEATURE_IBT)) -+ kvm_caps.supported_xss &= ~(XFEATURE_MASK_CET_USER | -+ XFEATURE_MASK_CET_KERNEL); -+ -+ if ((kvm_caps.supported_xss & (XFEATURE_MASK_CET_USER | -+ XFEATURE_MASK_CET_KERNEL)) != -+ (XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL)) { -+ kvm_cpu_cap_clear(X86_FEATURE_SHSTK); -+ kvm_cpu_cap_clear(X86_FEATURE_IBT); -+ kvm_caps.supported_xss &= ~(XFEATURE_MASK_CET_USER | -+ XFEATURE_MASK_CET_KERNEL); -+ } -+ - if (kvm_caps.has_tsc_control) { - /* - * Make sure the user can only configure tsc_khz values that -@@ -12607,10 +12622,11 @@ static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event) - /* - * On INIT, only select XSTATE components are zeroed, most components - * are unchanged. Currently, the only components that are zeroed and -- * supported by KVM are MPX related. -+ * supported by KVM are MPX and CET related. - */ - xfeatures_mask = (kvm_caps.supported_xcr0 | kvm_caps.supported_xss) & -- (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); -+ (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR | -+ XFEATURE_MASK_CET_USER | XFEATURE_MASK_CET_KERNEL); - if (!xfeatures_mask) - return; - -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 1f631b3459e8..41ab002f6471 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -681,6 +681,9 @@ static inline bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) - __reserved_bits |= X86_CR4_PCIDE; \ - if (!__cpu_has(__c, X86_FEATURE_LAM)) \ - __reserved_bits |= X86_CR4_LAM_SUP; \ -+ if (!__cpu_has(__c, X86_FEATURE_SHSTK) && \ -+ !__cpu_has(__c, X86_FEATURE_IBT)) \ -+ __reserved_bits |= X86_CR4_CET; \ - __reserved_bits; \ - }) - --- -2.43.0 - diff --git a/SPECS/kernel/0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu b/SPECS/kernel/0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu deleted file mode 100644 index 67f5eaca1..000000000 --- a/SPECS/kernel/0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu +++ /dev/null @@ -1,81 +0,0 @@ -From db8fcde6e507256c5260568ab213221c90783e1c Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Thu, 22 Jan 2026 18:10:31 +0800 -Subject: [PATCH 20/21] Update lt6911gxd sensor driver to fix timeout issue - after S3 - -Suspend function and resume function is added -to address Lontium issue unable to stream after -system wakes up from S3 state. - -JIRA ID: NEXIMAGING-50 - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/i2c/lt6911gxd.c | 43 +++++++++++++++++++++++++++++++++++ - 1 file changed, 43 insertions(+) - -diff --git a/drivers/media/i2c/lt6911gxd.c b/drivers/media/i2c/lt6911gxd.c -index d791560ce1a3..1cdc22652955 100644 ---- a/drivers/media/i2c/lt6911gxd.c -+++ b/drivers/media/i2c/lt6911gxd.c -@@ -622,6 +622,48 @@ static int lt6911gxd_probe(struct i2c_client *client) - return ret; - } - -+static int lt6911gxd_suspend(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ -+ /* Active low gpio reset, set 1 to power off sensor */ -+ gpiod_set_value_cansleep(lt6911gxd->reset_gpio, 1); -+ -+ return 0; -+} -+ -+static int lt6911gxd_resume(struct device *dev) -+{ -+ struct i2c_client *client = to_i2c_client(dev); -+ struct v4l2_subdev *sd = i2c_get_clientdata(client); -+ struct lt6911gxd *lt6911gxd = to_lt6911gxd(sd); -+ int ret = 0; -+ int count = 0; -+ -+ if (lt6911gxd->reset_gpio != NULL) { -+ do { -+ gpiod_set_value_cansleep(lt6911gxd->reset_gpio, 0); -+ ret = gpiod_get_value_cansleep(lt6911gxd->reset_gpio); -+ usleep_range(200 * 1000, 200 * 1000 + 500); -+ -+ if (++count >= 20) { -+ dev_err(&client->dev, -+ "%s: failed to power on reset gpio, reset gpio is %d", -+ __func__, ret); -+ break; -+ } -+ } while (ret != 0); -+ } -+ -+ return ret; -+} -+ -+static const struct dev_pm_ops lt6911gxd_pm_ops = { -+ SET_SYSTEM_SLEEP_PM_OPS(lt6911gxd_suspend, lt6911gxd_resume) -+}; -+ - static const struct acpi_device_id lt6911gxd_acpi_ids[] = { - { "INTC1124" }, - {} -@@ -632,6 +674,7 @@ static struct i2c_driver lt6911gxd_i2c_driver = { - .driver = { - .name = "lt6911gxd", - .acpi_match_table = ACPI_PTR(lt6911gxd_acpi_ids), -+ .pm = <6911gxd_pm_ops, - }, - .probe = lt6911gxd_probe, - .remove = lt6911gxd_remove, --- -2.43.0 - diff --git a/SPECS/kernel/0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf b/SPECS/kernel/0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf deleted file mode 100644 index 9fc055119..000000000 --- a/SPECS/kernel/0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf +++ /dev/null @@ -1,230 +0,0 @@ -From c3c3be9b3e43a7a7c0d1e797bad3b93a1b5a0b49 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 12 Dec 2024 02:04:34 +0000 -Subject: [PATCH 020/100] perf/x86/intel: Add counter group support for - arch-PEBS - -Base on previous adaptive PEBS counter snapshot support, add counter -group support for architectural PEBS. Since arch-PEBS shares same -counter group layout with adaptive PEBS, directly reuse -__setup_pebs_counter_group() helper to process arch-PEBS counter group. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 38 ++++++++++++++++++++++++++++--- - arch/x86/events/intel/ds.c | 29 ++++++++++++++++++++--- - arch/x86/include/asm/msr-index.h | 6 +++++ - arch/x86/include/asm/perf_event.h | 13 ++++++++--- - 4 files changed, 77 insertions(+), 9 deletions(-) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index fabe2e750a0b..d77bbc1d43fa 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3012,6 +3012,17 @@ static void intel_pmu_enable_event_ext(struct perf_event *event) - - if (pebs_data_cfg & PEBS_DATACFG_LBRS) - ext |= ARCH_PEBS_LBR & cap.caps; -+ -+ if (pebs_data_cfg & -+ (PEBS_DATACFG_CNTR_MASK << PEBS_DATACFG_CNTR_SHIFT)) -+ ext |= ARCH_PEBS_CNTR_GP & cap.caps; -+ -+ if (pebs_data_cfg & -+ (PEBS_DATACFG_FIX_MASK << PEBS_DATACFG_FIX_SHIFT)) -+ ext |= ARCH_PEBS_CNTR_FIXED & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_METRICS) -+ ext |= ARCH_PEBS_CNTR_METRICS & cap.caps; - } - - if (cpuc->n_pebs == cpuc->n_large_pebs) -@@ -3037,6 +3048,9 @@ static void intel_pmu_enable_event_ext(struct perf_event *event) - } - } - -+ if (is_pebs_counter_event_group(event)) -+ ext |= ARCH_PEBS_CNTR_ALLOW; -+ - if (cpuc->cfg_c_val[hwc->idx] != ext) - __intel_pmu_update_event_ext(hwc->idx, ext); - } -@@ -4320,6 +4334,20 @@ static bool intel_pmu_is_acr_group(struct perf_event *event) - return false; - } - -+static inline bool intel_pmu_has_pebs_counter_group(struct pmu *pmu) -+{ -+ u64 caps; -+ -+ if (x86_pmu.intel_cap.pebs_format >= 6 && x86_pmu.intel_cap.pebs_baseline) -+ return true; -+ -+ caps = hybrid(pmu, arch_pebs_cap).caps; -+ if (x86_pmu.arch_pebs && (caps & ARCH_PEBS_CNTR_MASK)) -+ return true; -+ -+ return false; -+} -+ - static inline void intel_pmu_set_acr_cntr_constr(struct perf_event *event, - u64 *cause_mask, int *num) - { -@@ -4466,8 +4494,7 @@ static int intel_pmu_hw_config(struct perf_event *event) - } - - if ((event->attr.sample_type & PERF_SAMPLE_READ) && -- (x86_pmu.intel_cap.pebs_format >= 6) && -- x86_pmu.intel_cap.pebs_baseline && -+ intel_pmu_has_pebs_counter_group(event->pmu) && - is_sampling_event(event) && - event->attr.precise_ip) - event->group_leader->hw.flags |= PERF_X86_EVENT_PEBS_CNTR; -@@ -5554,6 +5581,8 @@ static inline void __intel_update_large_pebs_flags(struct pmu *pmu) - x86_pmu.large_pebs_flags |= PERF_SAMPLE_TIME; - if (caps & ARCH_PEBS_LBR) - x86_pmu.large_pebs_flags |= PERF_SAMPLE_BRANCH_STACK; -+ if (caps & ARCH_PEBS_CNTR_MASK) -+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_READ; - - if (!(caps & ARCH_PEBS_AUX)) - x86_pmu.large_pebs_flags &= ~PERF_SAMPLE_DATA_SRC; -@@ -7252,8 +7281,11 @@ __init int intel_pmu_init(void) - * Many features on and after V6 require dynamic constraint, - * e.g., Arch PEBS, ACR. - */ -- if (version >= 6) -+ if (version >= 6) { - x86_pmu.flags |= PMU_FL_DYN_CONSTRAINT; -+ x86_pmu.late_setup = intel_pmu_late_setup; -+ } -+ - /* - * Install the hw-cache-events table: - */ -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 530f5a8bc9d2..9876e367174f 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1515,13 +1515,20 @@ pebs_update_state(bool needed_cb, struct cpu_hw_events *cpuc, - - u64 intel_get_arch_pebs_data_config(struct perf_event *event) - { -+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); - u64 pebs_data_cfg = 0; -+ u64 cntr_mask; - - if (WARN_ON(event->hw.idx < 0 || event->hw.idx >= X86_PMC_IDX_MAX)) - return 0; - - pebs_data_cfg |= pebs_update_adaptive_cfg(event); - -+ cntr_mask = (PEBS_DATACFG_CNTR_MASK << PEBS_DATACFG_CNTR_SHIFT) | -+ (PEBS_DATACFG_FIX_MASK << PEBS_DATACFG_FIX_SHIFT) | -+ PEBS_DATACFG_CNTR | PEBS_DATACFG_METRICS; -+ pebs_data_cfg |= cpuc->pebs_data_cfg & cntr_mask; -+ - return pebs_data_cfg; - } - -@@ -2426,6 +2433,24 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - } - } - -+ if (header->cntr) { -+ struct arch_pebs_cntr_header *cntr = next_record; -+ unsigned int nr; -+ -+ next_record += sizeof(struct arch_pebs_cntr_header); -+ -+ if (is_pebs_counter_event_group(event)) { -+ __setup_pebs_counter_group(cpuc, event, -+ (struct pebs_cntr_header *)cntr, next_record); -+ data->sample_flags |= PERF_SAMPLE_READ; -+ } -+ -+ nr = hweight32(cntr->cntr) + hweight32(cntr->fixed); -+ if (cntr->metrics == INTEL_CNTR_METRICS) -+ nr += 2; -+ next_record += nr * sizeof(u64); -+ } -+ - /* Parse followed fragments if there are. */ - if (arch_pebs_record_continued(header)) { - at = at + header->size; -@@ -3072,10 +3097,8 @@ static void __init intel_ds_pebs_init(void) - break; - - case 6: -- if (x86_pmu.intel_cap.pebs_baseline) { -+ if (x86_pmu.intel_cap.pebs_baseline) - x86_pmu.large_pebs_flags |= PERF_SAMPLE_READ; -- x86_pmu.late_setup = intel_pmu_late_setup; -- } - fallthrough; - case 5: - x86_pmu.pebs_ept = 1; -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 737d51629c03..41852e8690d7 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -331,12 +331,18 @@ - #define ARCH_PEBS_INDEX_WR_SHIFT 4 - - #define ARCH_PEBS_RELOAD 0xffffffff -+#define ARCH_PEBS_CNTR_ALLOW BIT_ULL(35) -+#define ARCH_PEBS_CNTR_GP BIT_ULL(36) -+#define ARCH_PEBS_CNTR_FIXED BIT_ULL(37) -+#define ARCH_PEBS_CNTR_METRICS BIT_ULL(38) - #define ARCH_PEBS_LBR_SHIFT 40 - #define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT) - #define ARCH_PEBS_VECR_XMM BIT_ULL(49) - #define ARCH_PEBS_GPR BIT_ULL(61) - #define ARCH_PEBS_AUX BIT_ULL(62) - #define ARCH_PEBS_EN BIT_ULL(63) -+#define ARCH_PEBS_CNTR_MASK (ARCH_PEBS_CNTR_GP | ARCH_PEBS_CNTR_FIXED | \ -+ ARCH_PEBS_CNTR_METRICS) - - #define MSR_IA32_RTIT_CTL 0x00000570 - #define RTIT_CTL_TRACEEN BIT(0) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 1f62efe3fce2..d6e90c8fa87c 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -141,16 +141,16 @@ - #define ARCH_PERFMON_EVENTS_COUNT 7 - - #define PEBS_DATACFG_MEMINFO BIT_ULL(0) --#define PEBS_DATACFG_GP BIT_ULL(1) -+#define PEBS_DATACFG_GP BIT_ULL(1) - #define PEBS_DATACFG_XMMS BIT_ULL(2) - #define PEBS_DATACFG_LBRS BIT_ULL(3) --#define PEBS_DATACFG_LBR_SHIFT 24 - #define PEBS_DATACFG_CNTR BIT_ULL(4) -+#define PEBS_DATACFG_METRICS BIT_ULL(5) -+#define PEBS_DATACFG_LBR_SHIFT 24 - #define PEBS_DATACFG_CNTR_SHIFT 32 - #define PEBS_DATACFG_CNTR_MASK GENMASK_ULL(15, 0) - #define PEBS_DATACFG_FIX_SHIFT 48 - #define PEBS_DATACFG_FIX_MASK GENMASK_ULL(7, 0) --#define PEBS_DATACFG_METRICS BIT_ULL(5) - - /* Steal the highest bit of pebs_data_cfg for SW usage */ - #define PEBS_UPDATE_DS_SW BIT_ULL(63) -@@ -607,6 +607,13 @@ struct arch_pebs_lbr_header { - u64 ler_info; - }; - -+struct arch_pebs_cntr_header { -+ u32 cntr; -+ u32 fixed; -+ u32 metrics; -+ u32 reserved; -+}; -+ - /* - * AMD Extended Performance Monitoring and Debug cpuid feature detection - */ --- -2.43.0 - diff --git a/SPECS/kernel/0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo b/SPECS/kernel/0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo new file mode 100644 index 000000000..9fe7f583a --- /dev/null +++ b/SPECS/kernel/0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo @@ -0,0 +1,131 @@ +From 022168cfacd440abb4a1b9efc4f9929c5dd32dd8 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 13:32:34 -0500 +Subject: [PATCH 20/21] tools/power turbostat: Print wide names only for RAW + 64-bit columns + +Print a wide column header only for the case of a 64-bit RAW counter. + +It turns out that wide column headers otherwise are more harm than good. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 40 ++++++++++++++------------- + 1 file changed, 21 insertions(+), 19 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 28625143a1b99..9329a503464ab 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -2723,20 +2723,22 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + + /* + * print_name() +- * Print column header name for 64-bit counter in 16 columns (at least 8-char plus a tab) ++ * Print column header name for raw 64-bit counter in 16 columns (at least 8-char plus a tab) + * Otherwise, allow the name + tab to fit within 8-coumn tab-stop. + * In both cases, left justififed, just like other turbostat columns, + * to allow the column values to consume the tab. + * +- * Yes, 32-bit counters can overflow 8-columns, but then they are usually 64-bit counters. +- * 64-bit counters can overflow 16-columns, but rarely do. ++ * Yes, 32-bit counters can overflow 8-columns, and ++ * 64-bit counters can overflow 16-columns, but that is uncommon. + */ +-static inline int print_name(int width, int *printed, char *delim, char *name) ++static inline int print_name(int width, int *printed, char *delim, char *name, enum counter_type type, enum counter_format format) + { +- if (width <= 32) +- return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); +- else ++ UNUSED(type); ++ ++ if (format == FORMAT_RAW && width >= 64) + return (sprintf(outp, "%s%-8s", (*printed++ ? delim : ""), name)); ++ else ++ return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); + } + static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) + { +@@ -2819,21 +2821,21 @@ void print_header(char *delim) + outp += sprintf(outp, "%sLLC%%hit", (printed++ ? delim : "")); + + for (mp = sys.tp; mp; mp = mp->next) +- outp += print_name(mp->width, &printed, delim, mp->name); ++ outp += print_name(mp->width, &printed, delim, mp->name, mp->type, mp->format); + + for (pp = sys.perf_tp; pp; pp = pp->next) +- outp += print_name(pp->width, &printed, delim, pp->name); ++ outp += print_name(pp->width, &printed, delim, pp->name, pp->type, pp->format); + + ppmt = sys.pmt_tp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += print_name(32, &printed, delim, ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + } + +@@ -2867,22 +2869,22 @@ void print_header(char *delim) + } + + for (mp = sys.cp; mp; mp = mp->next) +- outp += print_name(mp->width, &printed, delim, mp->name); ++ outp += print_name(mp->width, &printed, delim, mp->name, mp->type, mp->format); + + for (pp = sys.perf_cp; pp; pp = pp->next) +- outp += print_name(pp->width, &printed, delim, pp->name); ++ outp += print_name(pp->width, &printed, delim, pp->name, pp->type, pp->format); + + ppmt = sys.pmt_cp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += print_name(32, &printed, delim, ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + } + +@@ -2970,21 +2972,21 @@ void print_header(char *delim) + outp += sprintf(outp, "%sUncMHz", (printed++ ? delim : "")); + + for (mp = sys.pp; mp; mp = mp->next) +- outp += print_name(mp->width, &printed, delim, mp->name); ++ outp += print_name(mp->width, &printed, delim, mp->name, mp->type, mp->format); + + for (pp = sys.perf_pp; pp; pp = pp->next) +- outp += print_name(pp->width, &printed, delim, pp->name); ++ outp += print_name(pp->width, &printed, delim, pp->name, pp->type, pp->format); + + ppmt = sys.pmt_pp; + while (ppmt) { + switch (ppmt->type) { + case PMT_TYPE_RAW: +- outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name); ++ outp += print_name(pmt_counter_get_width(ppmt), &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + + case PMT_TYPE_XTAL_TIME: + case PMT_TYPE_TCORE_CLOCK: +- outp += print_name(32, &printed, delim, ppmt->name); ++ outp += print_name(32, &printed, delim, ppmt->name, COUNTER_ITEMS, ppmt->format); + break; + } + +-- +2.43.0 + diff --git a/SPECS/kernel/0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi b/SPECS/kernel/0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi deleted file mode 100644 index 016ab967c..000000000 --- a/SPECS/kernel/0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi +++ /dev/null @@ -1,58 +0,0 @@ -From 5360d6c11aca43d55397c6080c1a94cb5ff95f6c Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Tue, 9 Aug 2022 10:03:32 -0700 -Subject: [PATCH 21/44] KVM: nVMX: Allow VMX FRED controls - -Allow nVMX FRED controls as nested FRED support is in place. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Shan Kang -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kvm/vmx/nested.c | 5 +++-- - arch/x86/kvm/vmx/vmx.c | 1 + - 2 files changed, 4 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 9374560fc91e..9ac36ddf3f05 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -7458,7 +7458,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - * advertise any feature in it to nVMX until its nVMX support - * is ready. - */ -- msrs->secondary_exit_ctls &= 0; -+ msrs->secondary_exit_ctls &= SECONDARY_VM_EXIT_SAVE_IA32_FRED | -+ SECONDARY_VM_EXIT_LOAD_IA32_FRED; - } - } - -@@ -7474,7 +7475,7 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, - VM_ENTRY_IA32E_MODE | - #endif - VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS | -- VM_ENTRY_LOAD_CET_STATE; -+ VM_ENTRY_LOAD_CET_STATE | VM_ENTRY_LOAD_IA32_FRED; - msrs->entry_ctls_high |= - (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER | - VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 96edf88dd924..d04085f2d4a2 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -8007,6 +8007,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) - - entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1); - cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM)); -+ cr4_fixed1_update(X86_CR4_FRED, eax, feature_bit(FRED)); - - #undef cr4_fixed1_update - } --- -2.43.0 - diff --git a/SPECS/kernel/0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi b/SPECS/kernel/0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi new file mode 100644 index 000000000..899391407 --- /dev/null +++ b/SPECS/kernel/0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi @@ -0,0 +1,267 @@ +From d6bcc838ff53f463231b16ad441bc4b5e27d9a46 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 2 Jan 2024 23:51:33 -0800 +Subject: [PATCH 21/44] KVM: nVMX: Guard SHADOW_FIELD_R[OW] macros with VMX + feature checks + +Add VMX feature checks to the SHADOW_FIELD_R[OW] macros to prevent access +to VMCS fields that may be unsupported on some CPUs. + +Functions like copy_shadow_to_vmcs12() and copy_vmcs12_to_shadow() access +VMCS fields that may not exist on certain hardware, such as +INJECTED_EVENT_DATA. To avoid VMREAD/VMWRITE warnings, skip syncing fields +tied to unsupported VMX features. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. + +Change since v2: +* Add __SHADOW_FIELD_R[OW] for better readability or maintability (Sean). +--- + arch/x86/kvm/vmx/nested.c | 79 +++++++++++++++++++-------- + arch/x86/kvm/vmx/vmcs_shadow_fields.h | 41 +++++++++----- + 2 files changed, 83 insertions(+), 37 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index a6ebe3f2fdd22..361152678aead 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -55,14 +55,14 @@ struct shadow_vmcs_field { + u16 offset; + }; + static struct shadow_vmcs_field shadow_read_only_fields[] = { +-#define SHADOW_FIELD_RO(x, y) { x, offsetof(struct vmcs12, y) }, ++#define __SHADOW_FIELD_RO(x, y, c) { x, offsetof(struct vmcs12, y) }, + #include "vmcs_shadow_fields.h" + }; + static int max_shadow_read_only_fields = + ARRAY_SIZE(shadow_read_only_fields); + + static struct shadow_vmcs_field shadow_read_write_fields[] = { +-#define SHADOW_FIELD_RW(x, y) { x, offsetof(struct vmcs12, y) }, ++#define __SHADOW_FIELD_RW(x, y, c) { x, offsetof(struct vmcs12, y) }, + #include "vmcs_shadow_fields.h" + }; + static int max_shadow_read_write_fields = +@@ -85,6 +85,17 @@ static void init_vmcs_shadow_fields(void) + pr_err("Missing field from shadow_read_only_field %x\n", + field + 1); + ++ switch (field) { ++#define __SHADOW_FIELD_RO(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#include "vmcs_shadow_fields.h" ++ default: ++ break; ++ } ++ + clear_bit(field, vmx_vmread_bitmap); + if (field & 1) + #ifdef CONFIG_X86_64 +@@ -110,24 +121,13 @@ static void init_vmcs_shadow_fields(void) + field <= GUEST_TR_AR_BYTES, + "Update vmcs12_write_any() to drop reserved bits from AR_BYTES"); + +- /* +- * PML and the preemption timer can be emulated, but the +- * processor cannot vmwrite to fields that don't exist +- * on bare metal. +- */ + switch (field) { +- case GUEST_PML_INDEX: +- if (!cpu_has_vmx_pml()) +- continue; +- break; +- case VMX_PREEMPTION_TIMER_VALUE: +- if (!cpu_has_vmx_preemption_timer()) +- continue; +- break; +- case GUEST_INTR_STATUS: +- if (!cpu_has_vmx_apicv()) +- continue; ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ + break; ++#include "vmcs_shadow_fields.h" + default: + break; + } +@@ -1633,8 +1633,8 @@ int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata) + /* + * Copy the writable VMCS shadow fields back to the VMCS12, in case they have + * been modified by the L1 guest. Note, "writable" in this context means +- * "writable by the guest", i.e. tagged SHADOW_FIELD_RW; the set of +- * fields tagged SHADOW_FIELD_RO may or may not align with the "read-only" ++ * "writable by the guest", i.e. tagged __SHADOW_FIELD_RW; the set of ++ * fields tagged __SHADOW_FIELD_RO may or may not align with the "read-only" + * VM-exit information fields (which are actually writable if the vCPU is + * configured to support "VMWRITE to any supported field in the VMCS"). + */ +@@ -1655,6 +1655,18 @@ static void copy_shadow_to_vmcs12(struct vcpu_vmx *vmx) + + for (i = 0; i < max_shadow_read_write_fields; i++) { + field = shadow_read_write_fields[i]; ++ ++ switch (field.encoding) { ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#include "vmcs_shadow_fields.h" ++ default: ++ break; ++ } ++ + val = __vmcs_readl(field.encoding); + vmcs12_write_any(vmcs12, field.encoding, field.offset, val); + } +@@ -1689,6 +1701,23 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx) + for (q = 0; q < ARRAY_SIZE(fields); q++) { + for (i = 0; i < max_fields[q]; i++) { + field = fields[q][i]; ++ ++ switch (field.encoding) { ++#define __SHADOW_FIELD_RO(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ if (!(c)) \ ++ continue; \ ++ break; ++#include "vmcs_shadow_fields.h" ++ default: ++ break; ++ } ++ + val = vmcs12_read_any(vmcs12, field.encoding, + field.offset); + __vmcs_writel(field.encoding, val); +@@ -6002,9 +6031,10 @@ static int handle_vmread(struct kvm_vcpu *vcpu) + static bool is_shadow_field_rw(unsigned long field) + { + switch (field) { +-#define SHADOW_FIELD_RW(x, y) case x: ++#define __SHADOW_FIELD_RW(x, y, c) \ ++ case x: \ ++ return c; + #include "vmcs_shadow_fields.h" +- return true; + default: + break; + } +@@ -6014,9 +6044,10 @@ static bool is_shadow_field_rw(unsigned long field) + static bool is_shadow_field_ro(unsigned long field) + { + switch (field) { +-#define SHADOW_FIELD_RO(x, y) case x: ++#define __SHADOW_FIELD_RO(x, y, c) \ ++ case x: \ ++ return c; + #include "vmcs_shadow_fields.h" +- return true; + default: + break; + } +diff --git a/arch/x86/kvm/vmx/vmcs_shadow_fields.h b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +index da338327c2b34..607945ada35fb 100644 +--- a/arch/x86/kvm/vmx/vmcs_shadow_fields.h ++++ b/arch/x86/kvm/vmx/vmcs_shadow_fields.h +@@ -1,14 +1,17 @@ +-#if !defined(SHADOW_FIELD_RO) && !defined(SHADOW_FIELD_RW) ++#if !defined(__SHADOW_FIELD_RO) && !defined(__SHADOW_FIELD_RW) + BUILD_BUG_ON(1) + #endif + +-#ifndef SHADOW_FIELD_RO +-#define SHADOW_FIELD_RO(x, y) ++#ifndef __SHADOW_FIELD_RO ++#define __SHADOW_FIELD_RO(x, y, c) + #endif +-#ifndef SHADOW_FIELD_RW +-#define SHADOW_FIELD_RW(x, y) ++#ifndef __SHADOW_FIELD_RW ++#define __SHADOW_FIELD_RW(x, y, c) + #endif + ++#define SHADOW_FIELD_RO(x, y) __SHADOW_FIELD_RO(x, y, true) ++#define SHADOW_FIELD_RW(x, y) __SHADOW_FIELD_RW(x, y, true) ++ + /* + * We do NOT shadow fields that are modified when L0 + * traps and emulates any vmx instruction (e.g. VMPTRLD, +@@ -32,8 +35,12 @@ BUILD_BUG_ON(1) + */ + + /* 16-bits */ +-SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status) +-SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index) ++__SHADOW_FIELD_RW(GUEST_INTR_STATUS, guest_intr_status, cpu_has_vmx_apicv()) ++/* ++ * PML can be emulated, but the processor cannot vmwrite to the VMCS field ++ * GUEST_PML_INDEX that doesn't exist on bare metal. ++ */ ++__SHADOW_FIELD_RW(GUEST_PML_INDEX, guest_pml_index, cpu_has_vmx_pml()) + SHADOW_FIELD_RW(HOST_FS_SELECTOR, host_fs_selector) + SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) + +@@ -41,9 +48,9 @@ SHADOW_FIELD_RW(HOST_GS_SELECTOR, host_gs_selector) + SHADOW_FIELD_RO(VM_EXIT_REASON, vm_exit_reason) + SHADOW_FIELD_RO(VM_EXIT_INTR_INFO, vm_exit_intr_info) + SHADOW_FIELD_RO(VM_EXIT_INSTRUCTION_LEN, vm_exit_instruction_len) ++SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) + SHADOW_FIELD_RO(IDT_VECTORING_INFO_FIELD, idt_vectoring_info_field) + SHADOW_FIELD_RO(IDT_VECTORING_ERROR_CODE, idt_vectoring_error_code) +-SHADOW_FIELD_RO(VM_EXIT_INTR_ERROR_CODE, vm_exit_intr_error_code) + SHADOW_FIELD_RO(GUEST_CS_AR_BYTES, guest_cs_ar_bytes) + SHADOW_FIELD_RO(GUEST_SS_AR_BYTES, guest_ss_ar_bytes) + SHADOW_FIELD_RW(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control) +@@ -54,7 +61,12 @@ SHADOW_FIELD_RW(VM_ENTRY_INTR_INFO_FIELD, vm_entry_intr_info_field) + SHADOW_FIELD_RW(VM_ENTRY_INSTRUCTION_LEN, vm_entry_instruction_len) + SHADOW_FIELD_RW(TPR_THRESHOLD, tpr_threshold) + SHADOW_FIELD_RW(GUEST_INTERRUPTIBILITY_INFO, guest_interruptibility_info) +-SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value) ++/* ++ * The preemption timer can be emulated, but the processor cannot vmwrite to ++ * the VMCS field VMX_PREEMPTION_TIMER_VALUE that doesn't exist on bare metal. ++ */ ++__SHADOW_FIELD_RW(VMX_PREEMPTION_TIMER_VALUE, vmx_preemption_timer_value, ++ cpu_has_vmx_preemption_timer()) + + /* Natural width */ + SHADOW_FIELD_RO(EXIT_QUALIFICATION, exit_qualification) +@@ -74,10 +86,13 @@ SHADOW_FIELD_RW(HOST_GS_BASE, host_gs_base) + /* 64-bit */ + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS, guest_physical_address) + SHADOW_FIELD_RO(GUEST_PHYSICAL_ADDRESS_HIGH, guest_physical_address) +-SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data) +-SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data) +-SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data) +-SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data) ++__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA, original_event_data, cpu_has_vmx_fred()) ++__SHADOW_FIELD_RO(ORIGINAL_EVENT_DATA_HIGH, original_event_data, cpu_has_vmx_fred()) ++__SHADOW_FIELD_RW(INJECTED_EVENT_DATA, injected_event_data, cpu_has_vmx_fred()) ++__SHADOW_FIELD_RW(INJECTED_EVENT_DATA_HIGH, injected_event_data, cpu_has_vmx_fred()) + + #undef SHADOW_FIELD_RO + #undef SHADOW_FIELD_RW ++ ++#undef __SHADOW_FIELD_RO ++#undef __SHADOW_FIELD_RW +-- +2.43.0 + diff --git a/SPECS/kernel/0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet b/SPECS/kernel/0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet deleted file mode 100644 index 822478a18..000000000 --- a/SPECS/kernel/0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet +++ /dev/null @@ -1,106 +0,0 @@ -From 45a9614af06267963709a963e2dc0ca2d516b6e1 Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:51 -0700 -Subject: [PATCH 21/24] KVM: nVMX: Virtualize NO_HW_ERROR_CODE_CC for L1 event - injection to L2 - -Per SDM description(Vol.3D, Appendix A.1): -"If bit 56 is read as 1, software can use VM entry to deliver a hardware -exception with or without an error code, regardless of vector" - -Modify has_error_code check before inject events to nested guest. Only -enforce the check when guest is in real mode, the exception is not hard -exception and the platform doesn't enumerate bit56 in VMX_BASIC, in all -other case ignore the check to make the logic consistent with SDM. - -Signed-off-by: Yang Weijiang -Reviewed-by: Maxim Levitsky -Reviewed-by: Chao Gao -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 28 +++++++++++++++++++--------- - arch/x86/kvm/vmx/nested.h | 5 +++++ - 2 files changed, 24 insertions(+), 9 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 36405c80dc15..f40ba4adb5ee 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -1305,9 +1305,10 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) - { - const u64 feature_bits = VMX_BASIC_DUAL_MONITOR_TREATMENT | - VMX_BASIC_INOUT | -- VMX_BASIC_TRUE_CTLS; -+ VMX_BASIC_TRUE_CTLS | -+ VMX_BASIC_NO_HW_ERROR_CODE_CC; - -- const u64 reserved_bits = GENMASK_ULL(63, 56) | -+ const u64 reserved_bits = GENMASK_ULL(63, 57) | - GENMASK_ULL(47, 45) | - BIT_ULL(31); - -@@ -2982,7 +2983,6 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - u8 vector = intr_info & INTR_INFO_VECTOR_MASK; - u32 intr_type = intr_info & INTR_INFO_INTR_TYPE_MASK; - bool has_error_code = intr_info & INTR_INFO_DELIVER_CODE_MASK; -- bool should_have_error_code; - bool urg = nested_cpu_has2(vmcs12, - SECONDARY_EXEC_UNRESTRICTED_GUEST); - bool prot_mode = !urg || vmcs12->guest_cr0 & X86_CR0_PE; -@@ -2999,12 +2999,20 @@ static int nested_check_vm_entry_controls(struct kvm_vcpu *vcpu, - CC(intr_type == INTR_TYPE_OTHER_EVENT && vector != 0)) - return -EINVAL; - -- /* VM-entry interruption-info field: deliver error code */ -- should_have_error_code = -- intr_type == INTR_TYPE_HARD_EXCEPTION && prot_mode && -- x86_exception_has_error_code(vector); -- if (CC(has_error_code != should_have_error_code)) -- return -EINVAL; -+ /* -+ * Cannot deliver error code in real mode or if the interrupt -+ * type is not hardware exception. For other cases, do the -+ * consistency check only if the vCPU doesn't enumerate -+ * VMX_BASIC_NO_HW_ERROR_CODE_CC. -+ */ -+ if (!prot_mode || intr_type != INTR_TYPE_HARD_EXCEPTION) { -+ if (CC(has_error_code)) -+ return -EINVAL; -+ } else if (!nested_cpu_has_no_hw_errcode_cc(vcpu)) { -+ if (CC(has_error_code != -+ x86_exception_has_error_code(vector))) -+ return -EINVAL; -+ } - - /* VM-entry exception error code */ - if (CC(has_error_code && -@@ -7238,6 +7246,8 @@ static void nested_vmx_setup_basic(struct nested_vmx_msrs *msrs) - msrs->basic |= VMX_BASIC_TRUE_CTLS; - if (cpu_has_vmx_basic_inout()) - msrs->basic |= VMX_BASIC_INOUT; -+ if (cpu_has_vmx_basic_no_hw_errcode()) -+ msrs->basic |= VMX_BASIC_NO_HW_ERROR_CODE_CC; - } - - static void nested_vmx_setup_cr_fixed(struct nested_vmx_msrs *msrs) -diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h -index 6eedcfc91070..983484d42ebf 100644 ---- a/arch/x86/kvm/vmx/nested.h -+++ b/arch/x86/kvm/vmx/nested.h -@@ -309,6 +309,11 @@ static inline bool nested_cr4_valid(struct kvm_vcpu *vcpu, unsigned long val) - __kvm_is_valid_cr4(vcpu, val); - } - -+static inline bool nested_cpu_has_no_hw_errcode_cc(struct kvm_vcpu *vcpu) -+{ -+ return to_vmx(vcpu)->nested.msrs.basic & VMX_BASIC_NO_HW_ERROR_CODE_CC; -+} -+ - /* No difference in the restrictions on guest and host CR4 in VMX operation. */ - #define nested_guest_cr4_valid nested_cr4_valid - #define nested_host_cr4_valid nested_cr4_valid --- -2.43.0 - diff --git a/SPECS/kernel/0021-Update-compilation-path-for-IPU7-drivers.ipu b/SPECS/kernel/0021-Update-compilation-path-for-IPU7-drivers.ipu deleted file mode 100644 index 5290c8c9c..000000000 --- a/SPECS/kernel/0021-Update-compilation-path-for-IPU7-drivers.ipu +++ /dev/null @@ -1,66 +0,0 @@ -From 8ea0697fea1ac028582ad9c479804e1414c288b2 Mon Sep 17 00:00:00 2001 -From: "Shahidan, Muhammad Shahmil" -Date: Mon, 26 Jan 2026 17:52:11 +0800 -Subject: [PATCH 21/21] Update compilation path for IPU7 drivers - -The new compilation path for IPU7 drivers is located -under drivers/staging/media/ipu7 - -Signed-off-by: Shahidan, Muhammad Shahmil ---- - drivers/media/pci/intel/Kconfig | 2 +- - drivers/media/pci/intel/Makefile | 2 +- - drivers/staging/media/Kconfig | 2 +- - drivers/staging/media/Makefile | 2 +- - 4 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/drivers/media/pci/intel/Kconfig b/drivers/media/pci/intel/Kconfig -index f6b1d7915f83..f2960393622a 100644 ---- a/drivers/media/pci/intel/Kconfig -+++ b/drivers/media/pci/intel/Kconfig -@@ -1,7 +1,7 @@ - # SPDX-License-Identifier: GPL-2.0-only - - source "drivers/media/pci/intel/ipu3/Kconfig" --source "drivers/media/pci/intel/ipu7/Kconfig" -+#source "drivers/media/pci/intel/ipu7/Kconfig" - source "drivers/media/pci/intel/ivsc/Kconfig" - - config IPU_BRIDGE -diff --git a/drivers/media/pci/intel/Makefile b/drivers/media/pci/intel/Makefile -index b022340db8ba..b58608f4ebb6 100644 ---- a/drivers/media/pci/intel/Makefile -+++ b/drivers/media/pci/intel/Makefile -@@ -5,4 +5,4 @@ - obj-$(CONFIG_IPU_BRIDGE) += ipu-bridge.o - obj-y += ipu3/ - obj-y += ivsc/ --obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -+#obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig -index 6da885037207..ab250c89cd4d 100644 ---- a/drivers/staging/media/Kconfig -+++ b/drivers/staging/media/Kconfig -@@ -28,7 +28,7 @@ source "drivers/staging/media/imx/Kconfig" - - source "drivers/staging/media/ipu3/Kconfig" - --#source "drivers/staging/media/ipu7/Kconfig" -+source "drivers/staging/media/ipu7/Kconfig" - - source "drivers/staging/media/max96712/Kconfig" - -diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile -index 22d19580e91f..4a073938b2b2 100644 ---- a/drivers/staging/media/Makefile -+++ b/drivers/staging/media/Makefile -@@ -8,5 +8,5 @@ obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/ - obj-$(CONFIG_VIDEO_SUNXI) += sunxi/ - obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/ - obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/ --#obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ -+obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/ - obj-$(CONFIG_DVB_AV7110) += av7110/ --- -2.43.0 - diff --git a/SPECS/kernel/0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf b/SPECS/kernel/0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf deleted file mode 100644 index 60e4a5e30..000000000 --- a/SPECS/kernel/0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf +++ /dev/null @@ -1,190 +0,0 @@ -From 5ee14fc8d425c3bba966478d12f600eddf7632e2 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 12 Dec 2024 01:33:27 +0000 -Subject: [PATCH 021/100] perf/x86/intel: Support SSP register capturing for - arch-PEBS - -Arch-PEBS supports to capture shadow stack pointer (SSP) register in GPR -group. This patch supports to capture and output SSP register at -interrupt or user space, but capturing SSP at user space requires -'exclude_kernel' attribute must be set. That avoids kernel space SSP -register is captured unintentionally. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 15 +++++++++++++++ - arch/x86/events/intel/core.c | 3 ++- - arch/x86/events/intel/ds.c | 9 +++++++-- - arch/x86/events/perf_event.h | 4 ++++ - arch/x86/include/asm/perf_event.h | 1 + - arch/x86/include/uapi/asm/perf_regs.h | 4 +++- - arch/x86/kernel/perf_regs.c | 7 +++++++ - 7 files changed, 39 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 94a8ea4389e4..ad919f980389 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -651,6 +651,21 @@ int x86_pmu_hw_config(struct perf_event *event) - return -EINVAL; - } - -+ if (unlikely(event->attr.sample_regs_user & BIT_ULL(PERF_REG_X86_SSP))) { -+ /* Only arch-PEBS supports to capture SSP register. */ -+ if (!x86_pmu.arch_pebs || !event->attr.precise_ip) -+ return -EINVAL; -+ /* Only user space is allowed to capture. */ -+ if (!event->attr.exclude_kernel) -+ return -EINVAL; -+ } -+ -+ if (unlikely(event->attr.sample_regs_intr & BIT_ULL(PERF_REG_X86_SSP))) { -+ /* Only arch-PEBS supports to capture SSP register. */ -+ if (!x86_pmu.arch_pebs || !event->attr.precise_ip) -+ return -EINVAL; -+ } -+ - /* sample_regs_user never support XMM registers */ - if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)) - return -EINVAL; -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index d77bbc1d43fa..007414c37ad1 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -4153,12 +4153,13 @@ static void intel_pebs_aliases_skl(struct perf_event *event) - static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event) - { - unsigned long flags = x86_pmu.large_pebs_flags; -+ u64 gprs_mask = x86_pmu.arch_pebs ? ARCH_PEBS_GP_REGS : PEBS_GP_REGS; - - if (event->attr.use_clockid) - flags &= ~PERF_SAMPLE_TIME; - if (!event->attr.exclude_kernel) - flags &= ~PERF_SAMPLE_REGS_USER; -- if (event->attr.sample_regs_user & ~PEBS_GP_REGS) -+ if (event->attr.sample_regs_user & ~gprs_mask) - flags &= ~(PERF_SAMPLE_REGS_USER | PERF_SAMPLE_REGS_INTR); - return flags; - } -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 9876e367174f..3a88f62ae127 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1432,6 +1432,7 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event) - u64 sample_type = attr->sample_type; - u64 pebs_data_cfg = 0; - bool gprs, tsx_weight; -+ u64 gprs_mask; - - if (!(sample_type & ~(PERF_SAMPLE_IP|PERF_SAMPLE_TIME)) && - attr->precise_ip > 1) -@@ -1446,10 +1447,11 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event) - * + precise_ip < 2 for the non event IP - * + For RTM TSX weight we need GPRs for the abort code. - */ -+ gprs_mask = x86_pmu.arch_pebs ? ARCH_PEBS_GP_REGS : PEBS_GP_REGS; - gprs = ((sample_type & PERF_SAMPLE_REGS_INTR) && -- (attr->sample_regs_intr & PEBS_GP_REGS)) || -+ (attr->sample_regs_intr & gprs_mask)) || - ((sample_type & PERF_SAMPLE_REGS_USER) && -- (attr->sample_regs_user & PEBS_GP_REGS)); -+ (attr->sample_regs_user & gprs_mask)); - - tsx_weight = (sample_type & PERF_SAMPLE_WEIGHT_TYPE) && - ((attr->config & INTEL_ARCH_EVENT_MASK) == -@@ -2241,6 +2243,7 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ssp = 0; - - format_group = basic->format_group; - -@@ -2357,6 +2360,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ssp = 0; - - __setup_perf_sample_data(event, iregs, data); - -@@ -2393,6 +2397,7 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - - __setup_pebs_gpr_group(event, regs, (struct pebs_gprs *)gprs, - sample_type); -+ perf_regs->ssp = gprs->ssp; - } - - if (header->aux) { -diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h -index db4ec2975de4..95779d8e6cf5 100644 ---- a/arch/x86/events/perf_event.h -+++ b/arch/x86/events/perf_event.h -@@ -183,6 +183,10 @@ struct amd_nb { - (1ULL << PERF_REG_X86_R14) | \ - (1ULL << PERF_REG_X86_R15)) - -+#define ARCH_PEBS_GP_REGS \ -+ (PEBS_GP_REGS | \ -+ (1ULL << PERF_REG_X86_SSP)) -+ - /* - * Per register state. - */ -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index d6e90c8fa87c..452a2222ca6b 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -708,6 +708,7 @@ extern void perf_events_lapic_init(void); - struct pt_regs; - struct x86_perf_regs { - struct pt_regs regs; -+ u64 ssp; - u64 *xmm_regs; - }; - -diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h -index 7c9d2bb3833b..f9c5b16b1882 100644 ---- a/arch/x86/include/uapi/asm/perf_regs.h -+++ b/arch/x86/include/uapi/asm/perf_regs.h -@@ -27,9 +27,11 @@ enum perf_event_x86_regs { - PERF_REG_X86_R13, - PERF_REG_X86_R14, - PERF_REG_X86_R15, -+ /* arch-PEBS supports to capture shadow stack pointer (SSP) */ -+ PERF_REG_X86_SSP, - /* These are the limits for the GPRs. */ - PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, -- PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1, -+ PERF_REG_X86_64_MAX = PERF_REG_X86_SSP + 1, - - /* These all need two bits set because they are 128bit */ - PERF_REG_X86_XMM0 = 32, -diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c -index 624703af80a1..985bd616200e 100644 ---- a/arch/x86/kernel/perf_regs.c -+++ b/arch/x86/kernel/perf_regs.c -@@ -54,6 +54,8 @@ static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = { - PT_REGS_OFFSET(PERF_REG_X86_R13, r13), - PT_REGS_OFFSET(PERF_REG_X86_R14, r14), - PT_REGS_OFFSET(PERF_REG_X86_R15, r15), -+ /* The pt_regs struct does not store Shadow stack pointer. */ -+ (unsigned int) -1, - #endif - }; - -@@ -68,6 +70,11 @@ u64 perf_reg_value(struct pt_regs *regs, int idx) - return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0]; - } - -+ if (idx == PERF_REG_X86_SSP) { -+ perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ return perf_regs->ssp; -+ } -+ - if (WARN_ON_ONCE(idx >= ARRAY_SIZE(pt_regs_offset))) - return 0; - --- -2.43.0 - diff --git a/SPECS/kernel/0021-tools-power-turbostat-version-2025.12.02.turbo b/SPECS/kernel/0021-tools-power-turbostat-version-2025.12.02.turbo new file mode 100644 index 000000000..b6ce41633 --- /dev/null +++ b/SPECS/kernel/0021-tools-power-turbostat-version-2025.12.02.turbo @@ -0,0 +1,984 @@ +From 19ae0bfb527358cc88946183174f8e0d16e2dea1 Mon Sep 17 00:00:00 2001 +From: Len Brown +Date: Tue, 2 Dec 2025 11:45:42 -0500 +Subject: [PATCH 21/21] tools/power turbostat: version 2025.12.02 + +Since release 2025.09.09: + +Add LLC statistics columns: + LLCkRPS = Last Level Cache Thousands of References Per Second + LLC%hit = Last Level Cache Hit % +Recognize Wildcat Lake and Nova Lake platforms +Add MSR check for Android +Add APERF check for VMWARE +Add RAPL check for AWS +minor fixes + +This patch: + +White-space only, resulting from running Lindent +on everything except the tab-justified data-tables, +and using -l150 instead of -l80 to allow long lines. + +Signed-off-by: Len Brown +--- + tools/power/x86/turbostat/turbostat.c | 326 +++++++++----------------- + 1 file changed, 108 insertions(+), 218 deletions(-) + +diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c +index 9329a503464ab..5ad45c2ac5bd8 100644 +--- a/tools/power/x86/turbostat/turbostat.c ++++ b/tools/power/x86/turbostat/turbostat.c +@@ -563,8 +563,7 @@ static struct gfx_sysfs_info gfx_info[GFX_MAX]; + + int get_msr(int cpu, off_t offset, unsigned long long *msr); + int add_counter(unsigned int msr_num, char *path, char *name, +- unsigned int width, enum counter_scope scope, +- enum counter_type type, enum counter_format format, int flags, int package_num); ++ unsigned int width, enum counter_scope scope, enum counter_type type, enum counter_format format, int flags, int package_num); + + /* Model specific support Start */ + +@@ -1308,8 +1307,7 @@ char *progname; + + #define CPU_SUBSET_MAXCPUS 8192 /* need to use before probe... */ + cpu_set_t *cpu_present_set, *cpu_possible_set, *cpu_effective_set, *cpu_allowed_set, *cpu_affinity_set, *cpu_subset; +-size_t cpu_present_setsize, cpu_possible_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, +- cpu_subset_size; ++size_t cpu_present_setsize, cpu_possible_setsize, cpu_effective_setsize, cpu_allowed_setsize, cpu_affinity_setsize, cpu_subset_size; + #define MAX_ADDED_THREAD_COUNTERS 24 + #define MAX_ADDED_CORE_COUNTERS 8 + #define MAX_ADDED_PACKAGE_COUNTERS 16 +@@ -2696,8 +2694,7 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + if (mode == SHOW_LIST) { + deferred_add_names[deferred_add_index++] = name_list; + if (deferred_add_index >= MAX_DEFERRED) { +- fprintf(stderr, "More than max %d un-recognized --add options '%s'\n", +- MAX_DEFERRED, name_list); ++ fprintf(stderr, "More than max %d un-recognized --add options '%s'\n", MAX_DEFERRED, name_list); + help(); + exit(1); + } +@@ -2706,8 +2703,7 @@ void bic_lookup(cpu_set_t *ret_set, char *name_list, enum show_hide_mode mode) + if (debug) + fprintf(stderr, "deferred \"%s\"\n", name_list); + if (deferred_skip_index >= MAX_DEFERRED) { +- fprintf(stderr, "More than max %d un-recognized --skip options '%s'\n", +- MAX_DEFERRED, name_list); ++ fprintf(stderr, "More than max %d un-recognized --skip options '%s'\n", MAX_DEFERRED, name_list); + help(); + exit(1); + } +@@ -2740,6 +2736,7 @@ static inline int print_name(int width, int *printed, char *delim, char *name, e + else + return (sprintf(outp, "%s%s", (*printed++ ? delim : ""), name)); + } ++ + static inline int print_hex_value(int width, int *printed, char *delim, unsigned long long value) + { + if (width <= 32) +@@ -2747,6 +2744,7 @@ static inline int print_hex_value(int width, int *printed, char *delim, unsigned + else + return (sprintf(outp, "%s%016llx", (*printed++ ? delim : ""), value)); + } ++ + static inline int print_decimal_value(int width, int *printed, char *delim, unsigned long long value) + { + if (width <= 32) +@@ -2754,6 +2752,7 @@ static inline int print_decimal_value(int width, int *printed, char *delim, unsi + else + return (sprintf(outp, "%s%-8lld", (*printed++ ? delim : ""), value)); + } ++ + static inline int print_float_value(int *printed, char *delim, double value) + { + return (sprintf(outp, "%s%0.2f", (*printed++ ? delim : ""), value)); +@@ -3050,9 +3049,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "LLC Hit%%: %.2f", pct((t->llc.references - t->llc.misses) / t->llc.references)); + + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { +- outp += +- sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +- t->counter[i], mp->sp->path); ++ outp += sprintf(outp, "tADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, t->counter[i], mp->sp->path); + } + } + +@@ -3071,9 +3068,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "Joules: %0llX (scale: %lf)\n", energy_value, energy_scale); + + for (i = 0, mp = sys.cp; mp; i++, mp = mp->next) { +- outp += +- sprintf(outp, "cADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +- c->counter[i], mp->sp->path); ++ outp += sprintf(outp, "cADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, c->counter[i], mp->sp->path); + } + outp += sprintf(outp, "mc6_us: %016llX\n", c->mc6_us); + } +@@ -3108,9 +3103,7 @@ int dump_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, "PTM: %dC\n", p->pkg_temp_c); + + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { +- outp += +- sprintf(outp, "pADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, +- p->counter[i], mp->sp->path); ++ outp += sprintf(outp, "pADDED [%d] %8s msr0x%x: %08llX %s\n", i, mp->name, mp->msr_num, p->counter[i], mp->sp->path); + } + } + +@@ -3246,8 +3239,7 @@ int format_counters(PER_THREAD_PARAMS) + } + if (DO_BIC(BIC_Node)) { + if (t) +- outp += sprintf(outp, "%s%d", +- (printed++ ? delim : ""), cpus[t->cpu_id].physical_node_id); ++ outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), cpus[t->cpu_id].physical_node_id); + else + outp += sprintf(outp, "%s-", (printed++ ? delim : "")); + } +@@ -3273,11 +3265,9 @@ int format_counters(PER_THREAD_PARAMS) + + if (DO_BIC(BIC_Bzy_MHz)) { + if (has_base_hz) +- outp += +- sprintf(outp, "%s%.0f", (printed++ ? delim : ""), base_hz / units * t->aperf / t->mperf); ++ outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), base_hz / units * t->aperf / t->mperf); + else +- outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), +- tsc / units * t->aperf / t->mperf / interval_float); ++ outp += sprintf(outp, "%s%.0f", (printed++ ? delim : ""), tsc / units * t->aperf / t->mperf / interval_float); + } + + if (DO_BIC(BIC_TSC_MHz)) +@@ -3315,7 +3305,6 @@ int format_counters(PER_THREAD_PARAMS) + outp += sprintf(outp, fmt8, (printed++ ? delim : ""), pct((t->llc.references - t->llc.misses) / t->llc.references)); + } + +- + /* Added Thread Counters */ + for (i = 0, mp = sys.tp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) +@@ -3431,12 +3420,9 @@ int format_counters(PER_THREAD_PARAMS) + } + + if (DO_BIC(BIC_CorWatt) && platform->has_per_core_rapl) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&c->core_energy, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&c->core_energy, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_Cor_J) && platform->has_per_core_rapl) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&c->core_energy, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&c->core_energy, RAPL_UNIT_JOULES, interval_float)); + + /* print per-package data only for 1st core in package */ + if (!is_cpu_first_core_in_package(t, p)) +@@ -3451,8 +3437,7 @@ int format_counters(PER_THREAD_PARAMS) + if (p->gfx_rc6_ms == -1) { /* detect GFX counter reset */ + outp += sprintf(outp, "%s**.**", (printed++ ? delim : "")); + } else { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- p->gfx_rc6_ms / 10.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), p->gfx_rc6_ms / 10.0 / interval_float); + } + } + +@@ -3469,8 +3454,7 @@ int format_counters(PER_THREAD_PARAMS) + if (p->sam_mc6_ms == -1) { /* detect GFX counter reset */ + outp += sprintf(outp, "%s**.**", (printed++ ? delim : "")); + } else { +- outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), +- p->sam_mc6_ms / 10.0 / interval_float); ++ outp += sprintf(outp, "%s%.2f", (printed++ ? delim : ""), p->sam_mc6_ms / 10.0 / interval_float); + } + } + +@@ -3524,41 +3508,27 @@ int format_counters(PER_THREAD_PARAMS) + } + + if (DO_BIC(BIC_PkgWatt)) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_CorWatt) && !platform->has_per_core_rapl) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_GFXWatt)) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_RAMWatt)) +- outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_WATTS, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_Pkg_J)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_pkg, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_Cor_J) && !platform->has_per_core_rapl) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_cores, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_GFX_J)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_gfx, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_RAM_J)) +- outp += sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_JOULES, interval_float)); ++ outp += sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->energy_dram, RAPL_UNIT_JOULES, interval_float)); + if (DO_BIC(BIC_PKG__)) + outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->rapl_pkg_perf_status, RAPL_UNIT_WATTS, interval_float)); ++ sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->rapl_pkg_perf_status, RAPL_UNIT_WATTS, interval_float)); + if (DO_BIC(BIC_RAM__)) + outp += +- sprintf(outp, fmt8, (printed++ ? delim : ""), +- rapl_counter_get_value(&p->rapl_dram_perf_status, RAPL_UNIT_WATTS, interval_float)); ++ sprintf(outp, fmt8, (printed++ ? delim : ""), rapl_counter_get_value(&p->rapl_dram_perf_status, RAPL_UNIT_WATTS, interval_float)); + /* UncMHz */ + if (DO_BIC(BIC_UNCORE_MHZ)) + outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), p->uncore_mhz); +@@ -3580,8 +3550,7 @@ int format_counters(PER_THREAD_PARAMS) + if (pp->format == FORMAT_RAW) + outp += print_hex_value(pp->width, &printed, delim, p->perf_counter[i]); + else if (pp->type == COUNTER_K2M) +- outp += +- sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); ++ outp += sprintf(outp, "%s%d", (printed++ ? delim : ""), (unsigned int)p->perf_counter[i] / 1000); + else if (pp->format == FORMAT_DELTA || mp->format == FORMAT_AVERAGE) + outp += print_decimal_value(pp->width, &printed, delim, p->perf_counter[i]); + else if (pp->format == FORMAT_PERCENT) +@@ -3719,8 +3688,7 @@ int delta_package(struct pkg_data *new, struct pkg_data *old) + old->energy_gfx.raw_value = new->energy_gfx.raw_value - old->energy_gfx.raw_value; + old->energy_dram.raw_value = new->energy_dram.raw_value - old->energy_dram.raw_value; + old->rapl_pkg_perf_status.raw_value = new->rapl_pkg_perf_status.raw_value - old->rapl_pkg_perf_status.raw_value; +- old->rapl_dram_perf_status.raw_value = +- new->rapl_dram_perf_status.raw_value - old->rapl_dram_perf_status.raw_value; ++ old->rapl_dram_perf_status.raw_value = new->rapl_dram_perf_status.raw_value - old->rapl_dram_perf_status.raw_value; + + for (i = 0, mp = sys.pp; mp; i++, mp = mp->next) { + if (mp->format == FORMAT_RAW) +@@ -3827,8 +3795,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + /* check for TSC < 1 Mcycles over interval */ + if (old->tsc < (1000 * 1000)) + errx(-3, "Insanely slow TSC rate, TSC stops in idle?\n" +- "You can disable all c-states by booting with \"idle=poll\"\n" +- "or just the deep ones with \"processor.max_cstate=1\""); ++ "You can disable all c-states by booting with \"idle=poll\"\n" "or just the deep ones with \"processor.max_cstate=1\""); + + old->c1 = new->c1 - old->c1; + +@@ -3857,8 +3824,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + old->c1 = 0; + else { + /* normal case, derive c1 */ +- old->c1 = (old->tsc * tsc_tweak) - old->mperf - core_delta->c3 +- - core_delta->c6 - core_delta->c7; ++ old->c1 = (old->tsc * tsc_tweak) - old->mperf - core_delta->c3 - core_delta->c6 - core_delta->c7; + } + } + +@@ -3910,8 +3876,7 @@ int delta_thread(struct thread_data *new, struct thread_data *old, struct core_d + return 0; + } + +-int delta_cpu(struct thread_data *t, struct core_data *c, +- struct pkg_data *p, struct thread_data *t2, struct core_data *c2, struct pkg_data *p2) ++int delta_cpu(struct thread_data *t, struct core_data *c, struct pkg_data *p, struct thread_data *t2, struct core_data *c2, struct pkg_data *p2) + { + int retval = 0; + +@@ -4391,8 +4356,7 @@ unsigned long long get_legacy_uncore_mhz(int package) + */ + for (die = 0; die <= topo.max_die_id; ++die) { + +- sprintf(path, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/current_freq_khz", +- package, die); ++ sprintf(path, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d/current_freq_khz", package, die); + + if (access(path, R_OK) == 0) + return (snapshot_sysfs_counter(path) / 1000); +@@ -4702,8 +4666,7 @@ int get_rapl_counters(int cpu, unsigned int domain, struct core_data *c, struct + const ssize_t actual_read_size = read(rci->fd_perf, &perf_data[0], sizeof(perf_data)); + + if (actual_read_size != expected_read_size) +- err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, +- actual_read_size); ++ err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, actual_read_size); + } + + for (unsigned int i = 0, pi = 1; i < NUM_RAPL_COUNTERS; ++i) { +@@ -4941,8 +4904,7 @@ int get_smi_aperf_mperf(unsigned int cpu, struct thread_data *t) + const ssize_t actual_read_size = read(mci->fd_perf, &perf_data[0], sizeof(perf_data)); + + if (actual_read_size != expected_read_size) +- err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, +- actual_read_size); ++ err(-1, "%s: failed to read perf_data (%zu %zu)", __func__, expected_read_size, actual_read_size); + } + + for (unsigned int i = 0, pi = 1; i < NUM_MSR_COUNTERS; ++i) { +@@ -5255,48 +5217,39 @@ char *pkg_cstate_limit_strings[] = { "unknown", "reserved", "pc0", "pc1", "pc2", + "pc3", "pc4", "pc6", "pc6n", "pc6r", "pc7", "pc7s", "pc8", "pc9", "pc10", "unlimited" + }; + +-int nhm_pkg_cstate_limits[16] = +- { PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int nhm_pkg_cstate_limits[16] = { PCL__0, PCL__1, PCL__3, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int snb_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int snb_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL_6N, PCL_6R, PCL__7, PCL_7S, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int hsw_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int hsw_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int slv_pkg_cstate_limits[16] = +- { PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int slv_pkg_cstate_limits[16] = { PCL__0, PCL__1, PCLRSV, PCLRSV, PCL__4, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCL__6, PCL__7 + }; + +-int amt_pkg_cstate_limits[16] = +- { PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int amt_pkg_cstate_limits[16] = { PCLUNL, PCL__1, PCL__2, PCLRSV, PCLRSV, PCLRSV, PCL__6, PCL__7, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int phi_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int phi_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int glm_pkg_cstate_limits[16] = +- { PCLUNL, PCL__1, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCL_10, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int glm_pkg_cstate_limits[16] = { PCLUNL, PCL__1, PCL__3, PCL__6, PCL__7, PCL_7S, PCL__8, PCL__9, PCL_10, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int skx_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int skx_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL_6N, PCL_6R, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +-int icx_pkg_cstate_limits[16] = +- { PCL__0, PCL__2, PCL__6, PCL__6, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, ++int icx_pkg_cstate_limits[16] = { PCL__0, PCL__2, PCL__6, PCL__6, PCLRSV, PCLRSV, PCLRSV, PCLUNL, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, PCLRSV, + PCLRSV, PCLRSV + }; + +@@ -5371,8 +5324,7 @@ static void dump_power_ctl(void) + return; + + get_msr(base_cpu, MSR_IA32_POWER_CTL, &msr); +- fprintf(outf, "cpu%d: MSR_IA32_POWER_CTL: 0x%08llx (C1E auto-promotion: %sabled)\n", +- base_cpu, msr, msr & 0x2 ? "EN" : "DIS"); ++ fprintf(outf, "cpu%d: MSR_IA32_POWER_CTL: 0x%08llx (C1E auto-promotion: %sabled)\n", base_cpu, msr, msr & 0x2 ? "EN" : "DIS"); + + /* C-state Pre-wake Disable (CSTATE_PREWAKE_DISABLE) */ + if (platform->has_cst_prewake_bit) +@@ -5465,8 +5417,7 @@ static void dump_turbo_ratio_limits(int trl_msr_offset) + ratio = (msr >> shift) & 0xFF; + group_size = (core_counts >> shift) & 0xFF; + if (ratio) +- fprintf(outf, "%d * %.1f = %.1f MHz max turbo %d active cores\n", +- ratio, bclk, ratio * bclk, group_size); ++ fprintf(outf, "%d * %.1f = %.1f MHz max turbo %d active cores\n", ratio, bclk, ratio * bclk, group_size); + } + + return; +@@ -5564,9 +5515,7 @@ static void dump_knl_turbo_ratio_limits(void) + + for (i = buckets_no - 1; i >= 0; i--) + if (i > 0 ? ratio[i] != ratio[i - 1] : 1) +- fprintf(outf, +- "%d * %.1f = %.1f MHz max turbo %d active cores\n", +- ratio[i], bclk, ratio[i] * bclk, cores[i]); ++ fprintf(outf, "%d * %.1f = %.1f MHz max turbo %d active cores\n", ratio[i], bclk, ratio[i] * bclk, cores[i]); + } + + static void dump_cst_cfg(void) +@@ -5651,43 +5600,37 @@ void print_irtl(void) + if (platform->supported_cstates & PC3) { + get_msr(base_cpu, MSR_PKGC3_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC3_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC6) { + get_msr(base_cpu, MSR_PKGC6_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC6_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC7) { + get_msr(base_cpu, MSR_PKGC7_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC7_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC8) { + get_msr(base_cpu, MSR_PKGC8_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC8_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC9) { + get_msr(base_cpu, MSR_PKGC9_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC9_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + + if (platform->supported_cstates & PC10) { + get_msr(base_cpu, MSR_PKGC10_IRTL, &msr); + fprintf(outf, "cpu%d: MSR_PKGC10_IRTL: 0x%08llx (", base_cpu, msr); +- fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", +- (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); ++ fprintf(outf, "%svalid, %lld ns)\n", msr & (1 << 15) ? "" : "NOT", (msr & 0x3FF) * irtl_time_units[(msr >> 10) & 0x3]); + } + } + +@@ -6221,8 +6164,7 @@ void re_initialize(void) + perf_llc_init(); + added_perf_counters_init(); + pmt_init(); +- fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, +- topo.allowed_cpus); ++ fprintf(outf, "turbostat: re-initialized with num_cpus %d, allowed_cpus %d\n", topo.num_cpus, topo.allowed_cpus); + } + + void set_max_cpu_num(void) +@@ -6798,8 +6740,8 @@ int probe_dev_msr(void) + struct stat sb; + char pathname[32]; + +- sprintf(pathname, "/dev/msr%d", base_cpu); +- return !stat(pathname, &sb); ++ sprintf(pathname, "/dev/msr%d", base_cpu); ++ return !stat(pathname, &sb); + } + + int probe_dev_cpu_msr(void) +@@ -7013,8 +6955,7 @@ static void probe_intel_uncore_frequency_legacy(void) + int k, l; + char path_base[128]; + +- sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d", i, +- j); ++ sprintf(path_base, "/sys/devices/system/cpu/intel_uncore_frequency/package_%02d_die_%02d", i, j); + + sprintf(path, "%s/current_freq_khz", path_base); + if (access(path, R_OK)) +@@ -7097,8 +7038,7 @@ static void probe_intel_uncore_frequency_cluster(void) + */ + if BIC_IS_ENABLED + (BIC_UNCORE_MHZ) +- add_counter(0, path, name_buf, 0, SCOPE_PACKAGE, COUNTER_K2M, FORMAT_AVERAGE, 0, +- package_id); ++ add_counter(0, path, name_buf, 0, SCOPE_PACKAGE, COUNTER_K2M, FORMAT_AVERAGE, 0, package_id); + + if (quiet) + continue; +@@ -7107,8 +7047,7 @@ static void probe_intel_uncore_frequency_cluster(void) + k = read_sysfs_int(path); + sprintf(path, "%s/max_freq_khz", path_base); + l = read_sysfs_int(path); +- fprintf(outf, "Uncore Frequency package%d domain%d cluster%d: %d - %d MHz ", package_id, domain_id, +- cluster_id, k / 1000, l / 1000); ++ fprintf(outf, "Uncore Frequency package%d domain%d cluster%d: %d - %d MHz ", package_id, domain_id, cluster_id, k / 1000, l / 1000); + + sprintf(path, "%s/initial_min_freq_khz", path_base); + k = read_sysfs_int(path); +@@ -7170,21 +7109,17 @@ static void probe_graphics(void) + else + goto next; + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms", +- gt0_is_gt ? GFX_rc6 : SAM_mc6); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/gtidle/idle_residency_ms", gt0_is_gt ? GFX_rc6 : SAM_mc6); + + set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/cur_freq", gt0_is_gt ? GFX_MHz : SAM_MHz); + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq", +- gt0_is_gt ? GFX_ACTMHz : SAM_ACTMHz); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt0/freq0/act_freq", gt0_is_gt ? GFX_ACTMHz : SAM_ACTMHz); + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms", +- gt0_is_gt ? SAM_mc6 : GFX_rc6); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/gtidle/idle_residency_ms", gt0_is_gt ? SAM_mc6 : GFX_rc6); + + set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/cur_freq", gt0_is_gt ? SAM_MHz : GFX_MHz); + +- set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq", +- gt0_is_gt ? SAM_ACTMHz : GFX_ACTMHz); ++ set_graphics_fp("/sys/class/drm/card0/device/tile0/gt1/freq0/act_freq", gt0_is_gt ? SAM_ACTMHz : GFX_ACTMHz); + + goto end; + } +@@ -7439,8 +7374,7 @@ int print_hwp(PER_THREAD_PARAMS) + "(high %d guar %d eff %d low %d)\n", + cpu, msr, + (unsigned int)HWP_HIGHEST_PERF(msr), +- (unsigned int)HWP_GUARANTEED_PERF(msr), +- (unsigned int)HWP_MOSTEFFICIENT_PERF(msr), (unsigned int)HWP_LOWEST_PERF(msr)); ++ (unsigned int)HWP_GUARANTEED_PERF(msr), (unsigned int)HWP_MOSTEFFICIENT_PERF(msr), (unsigned int)HWP_LOWEST_PERF(msr)); + + if (get_msr(cpu, MSR_HWP_REQUEST, &msr)) + return 0; +@@ -7451,8 +7385,7 @@ int print_hwp(PER_THREAD_PARAMS) + (unsigned int)(((msr) >> 0) & 0xff), + (unsigned int)(((msr) >> 8) & 0xff), + (unsigned int)(((msr) >> 16) & 0xff), +- (unsigned int)(((msr) >> 24) & 0xff), +- (unsigned int)(((msr) >> 32) & 0xff3), (unsigned int)(((msr) >> 42) & 0x1)); ++ (unsigned int)(((msr) >> 24) & 0xff), (unsigned int)(((msr) >> 32) & 0xff3), (unsigned int)(((msr) >> 42) & 0x1)); + + if (has_hwp_pkg) { + if (get_msr(cpu, MSR_HWP_REQUEST_PKG, &msr)) +@@ -7463,23 +7396,20 @@ int print_hwp(PER_THREAD_PARAMS) + cpu, msr, + (unsigned int)(((msr) >> 0) & 0xff), + (unsigned int)(((msr) >> 8) & 0xff), +- (unsigned int)(((msr) >> 16) & 0xff), +- (unsigned int)(((msr) >> 24) & 0xff), (unsigned int)(((msr) >> 32) & 0xff3)); ++ (unsigned int)(((msr) >> 16) & 0xff), (unsigned int)(((msr) >> 24) & 0xff), (unsigned int)(((msr) >> 32) & 0xff3)); + } + if (has_hwp_notify) { + if (get_msr(cpu, MSR_HWP_INTERRUPT, &msr)) + return 0; + + fprintf(outf, "cpu%d: MSR_HWP_INTERRUPT: 0x%08llx " +- "(%s_Guaranteed_Perf_Change, %s_Excursion_Min)\n", +- cpu, msr, ((msr) & 0x1) ? "EN" : "Dis", ((msr) & 0x2) ? "EN" : "Dis"); ++ "(%s_Guaranteed_Perf_Change, %s_Excursion_Min)\n", cpu, msr, ((msr) & 0x1) ? "EN" : "Dis", ((msr) & 0x2) ? "EN" : "Dis"); + } + if (get_msr(cpu, MSR_HWP_STATUS, &msr)) + return 0; + + fprintf(outf, "cpu%d: MSR_HWP_STATUS: 0x%08llx " +- "(%sGuaranteed_Perf_Change, %sExcursion_Min)\n", +- cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x4) ? "" : "No-"); ++ "(%sGuaranteed_Perf_Change, %sExcursion_Min)\n", cpu, msr, ((msr) & 0x1) ? "" : "No-", ((msr) & 0x4) ? "" : "No-"); + + return 0; + } +@@ -7524,8 +7454,7 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 6) ? "VR-Therm, " : "", + (msr & 1 << 5) ? "Auto-HWP, " : "", + (msr & 1 << 4) ? "Graphics, " : "", +- (msr & 1 << 2) ? "bit2, " : "", +- (msr & 1 << 1) ? "ThermStatus, " : "", (msr & 1 << 0) ? "PROCHOT, " : ""); ++ (msr & 1 << 2) ? "bit2, " : "", (msr & 1 << 1) ? "ThermStatus, " : "", (msr & 1 << 0) ? "PROCHOT, " : ""); + fprintf(outf, " (Logged: %s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", + (msr & 1 << 31) ? "bit31, " : "", + (msr & 1 << 30) ? "bit30, " : "", +@@ -7538,8 +7467,7 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 22) ? "VR-Therm, " : "", + (msr & 1 << 21) ? "Auto-HWP, " : "", + (msr & 1 << 20) ? "Graphics, " : "", +- (msr & 1 << 18) ? "bit18, " : "", +- (msr & 1 << 17) ? "ThermStatus, " : "", (msr & 1 << 16) ? "PROCHOT, " : ""); ++ (msr & 1 << 18) ? "bit18, " : "", (msr & 1 << 17) ? "ThermStatus, " : "", (msr & 1 << 16) ? "PROCHOT, " : ""); + + } + if (platform->plr_msrs & PLR_GFX) { +@@ -7551,16 +7479,14 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 4) ? "Graphics, " : "", + (msr & 1 << 6) ? "VR-Therm, " : "", + (msr & 1 << 8) ? "Amps, " : "", +- (msr & 1 << 9) ? "GFXPwr, " : "", +- (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 9) ? "GFXPwr, " : "", (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); + fprintf(outf, " (Logged: %s%s%s%s%s%s%s%s)\n", + (msr & 1 << 16) ? "PROCHOT, " : "", + (msr & 1 << 17) ? "ThermStatus, " : "", + (msr & 1 << 20) ? "Graphics, " : "", + (msr & 1 << 22) ? "VR-Therm, " : "", + (msr & 1 << 24) ? "Amps, " : "", +- (msr & 1 << 25) ? "GFXPwr, " : "", +- (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 25) ? "GFXPwr, " : "", (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); + } + if (platform->plr_msrs & PLR_RING) { + get_msr(cpu, MSR_RING_PERF_LIMIT_REASONS, &msr); +@@ -7569,14 +7495,12 @@ int print_perf_limit(PER_THREAD_PARAMS) + (msr & 1 << 0) ? "PROCHOT, " : "", + (msr & 1 << 1) ? "ThermStatus, " : "", + (msr & 1 << 6) ? "VR-Therm, " : "", +- (msr & 1 << 8) ? "Amps, " : "", +- (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 8) ? "Amps, " : "", (msr & 1 << 10) ? "PkgPwrL1, " : "", (msr & 1 << 11) ? "PkgPwrL2, " : ""); + fprintf(outf, " (Logged: %s%s%s%s%s%s)\n", + (msr & 1 << 16) ? "PROCHOT, " : "", + (msr & 1 << 17) ? "ThermStatus, " : "", + (msr & 1 << 22) ? "VR-Therm, " : "", +- (msr & 1 << 24) ? "Amps, " : "", +- (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); ++ (msr & 1 << 24) ? "Amps, " : "", (msr & 1 << 26) ? "PkgPwrL1, " : "", (msr & 1 << 27) ? "PkgPwrL2, " : ""); + } + return 0; + } +@@ -7704,8 +7628,7 @@ void print_power_limit_msr(int cpu, unsigned long long msr, char *label) + cpu, label, + ((msr >> 15) & 1) ? "EN" : "DIS", + ((msr >> 0) & 0x7FFF) * rapl_power_units, +- (1.0 + (((msr >> 22) & 0x3) / 4.0)) * (1 << ((msr >> 17) & 0x1F)) * rapl_time_units, +- (((msr >> 16) & 1) ? "EN" : "DIS")); ++ (1.0 + (((msr >> 22) & 0x3) / 4.0)) * (1 << ((msr >> 17) & 0x1F)) * rapl_time_units, (((msr >> 16) & 1) ? "EN" : "DIS")); + + return; + } +@@ -7906,8 +7829,7 @@ int print_rapl(PER_THREAD_PARAMS) + cpu, msr, + ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units, + ((msr >> 16) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); ++ ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + + } + if (valid_rapl_msrs & RAPL_PKG) { +@@ -7915,16 +7837,14 @@ int print_rapl(PER_THREAD_PARAMS) + if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr)) + return -9; + +- fprintf(outf, "cpu%d: MSR_PKG_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 63) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_PKG_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 63) & 1 ? "" : "UN"); + + print_power_limit_msr(cpu, msr, "PKG Limit #1"); + fprintf(outf, "cpu%d: PKG Limit #2: %sabled (%0.3f Watts, %f* sec, clamp %sabled)\n", + cpu, + ((msr >> 47) & 1) ? "EN" : "DIS", + ((msr >> 32) & 0x7FFF) * rapl_power_units, +- (1.0 + (((msr >> 54) & 0x3) / 4.0)) * (1 << ((msr >> 49) & 0x1F)) * rapl_time_units, +- ((msr >> 48) & 1) ? "EN" : "DIS"); ++ (1.0 + (((msr >> 54) & 0x3) / 4.0)) * (1 << ((msr >> 49) & 0x1F)) * rapl_time_units, ((msr >> 48) & 1) ? "EN" : "DIS"); + + if (get_msr(cpu, MSR_VR_CURRENT_CONFIG, &msr)) + return -9; +@@ -7942,14 +7862,12 @@ int print_rapl(PER_THREAD_PARAMS) + cpu, msr, + ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units, + ((msr >> 16) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, +- ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); ++ ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); + } + if (valid_rapl_msrs & RAPL_DRAM) { + if (get_msr(cpu, MSR_DRAM_POWER_LIMIT, &msr)) + return -9; +- fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 31) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + + print_power_limit_msr(cpu, msr, "DRAM Limit"); + } +@@ -7962,8 +7880,7 @@ int print_rapl(PER_THREAD_PARAMS) + if (valid_rapl_msrs & RAPL_CORE_POWER_LIMIT) { + if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr)) + return -9; +- fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 31) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + print_power_limit_msr(cpu, msr, "Cores Limit"); + } + if (valid_rapl_msrs & RAPL_GFX) { +@@ -7974,8 +7891,7 @@ int print_rapl(PER_THREAD_PARAMS) + + if (get_msr(cpu, MSR_PP1_POWER_LIMIT, &msr)) + return -9; +- fprintf(outf, "cpu%d: MSR_PP1_POWER_LIMIT: 0x%08llx (%slocked)\n", +- cpu, msr, (msr >> 31) & 1 ? "" : "UN"); ++ fprintf(outf, "cpu%d: MSR_PP1_POWER_LIMIT: 0x%08llx (%slocked)\n", cpu, msr, (msr >> 31) & 1 ? "" : "UN"); + print_power_limit_msr(cpu, msr, "GFX Limit"); + } + return 0; +@@ -8015,7 +7931,7 @@ void probe_rapl_msrs(void) + return; + } + +- valid_rapl_msrs = platform->plat_rapl_msrs; /* success */ ++ valid_rapl_msrs = platform->plat_rapl_msrs; /* success */ + } + + /* +@@ -8161,8 +8077,7 @@ int print_thermal(PER_THREAD_PARAMS) + + dts = (msr >> 16) & 0x7F; + dts2 = (msr >> 8) & 0x7F; +- fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", +- cpu, msr, tj_max - dts, tj_max - dts2); ++ fprintf(outf, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", cpu, msr, tj_max - dts, tj_max - dts2); + } + + if (do_dts && debug) { +@@ -8173,16 +8088,14 @@ int print_thermal(PER_THREAD_PARAMS) + + dts = (msr >> 16) & 0x7F; + resolution = (msr >> 27) & 0xF; +- fprintf(outf, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", +- cpu, msr, tj_max - dts, resolution); ++ fprintf(outf, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", cpu, msr, tj_max - dts, resolution); + + if (get_msr(cpu, MSR_IA32_THERM_INTERRUPT, &msr)) + return 0; + + dts = (msr >> 16) & 0x7F; + dts2 = (msr >> 8) & 0x7F; +- fprintf(outf, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", +- cpu, msr, tj_max - dts, tj_max - dts2); ++ fprintf(outf, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", cpu, msr, tj_max - dts, tj_max - dts2); + } + + return 0; +@@ -8256,8 +8169,7 @@ void decode_misc_enable_msr(void) + msr & MSR_IA32_MISC_ENABLE_TM1 ? "" : "No-", + msr & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP ? "" : "No-", + msr & MSR_IA32_MISC_ENABLE_MWAIT ? "" : "No-", +- msr & MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE ? "No-" : "", +- msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE ? "No-" : ""); ++ msr & MSR_IA32_MISC_ENABLE_PREFETCH_DISABLE ? "No-" : "", msr & MSR_IA32_MISC_ENABLE_TURBO_DISABLE ? "No-" : ""); + } + + void decode_misc_feature_control(void) +@@ -8296,8 +8208,7 @@ void decode_misc_pwr_mgmt_msr(void) + + if (!get_msr(base_cpu, MSR_MISC_PWR_MGMT, &msr)) + fprintf(outf, "cpu%d: MSR_MISC_PWR_MGMT: 0x%08llx (%sable-EIST_Coordination %sable-EPB %sable-OOB)\n", +- base_cpu, msr, +- msr & (1 << 0) ? "DIS" : "EN", msr & (1 << 1) ? "EN" : "DIS", msr & (1 << 8) ? "EN" : "DIS"); ++ base_cpu, msr, msr & (1 << 0) ? "DIS" : "EN", msr & (1 << 1) ? "EN" : "DIS", msr & (1 << 8) ? "EN" : "DIS"); + } + + /* +@@ -8369,8 +8280,7 @@ static int has_perf_instr_count_access(void) + return (fd != -1); + } + +-int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai, +- double *scale_, enum rapl_unit *unit_) ++int add_rapl_perf_counter(int cpu, struct rapl_counter_info_t *rci, const struct rapl_counter_arch_info *cai, double *scale_, enum rapl_unit *unit_) + { + int ret = -1; + +@@ -8763,8 +8673,7 @@ void cstate_perf_init_(bool soft_c1) + cci->source[cai->rci_index] = COUNTER_SOURCE_PERF; + + /* User MSR for this counter */ +- } else if (pkg_cstate_limit >= cai->pkg_cstate_limit +- && add_msr_counter(cpu, cai->msr) >= 0) { ++ } else if (pkg_cstate_limit >= cai->pkg_cstate_limit && add_msr_counter(cpu, cai->msr) >= 0) { + cci->source[cai->rci_index] = COUNTER_SOURCE_MSR; + cci->msr[cai->rci_index] = cai->msr; + } +@@ -8882,8 +8791,7 @@ void process_cpuid() + hygon_genuine = 1; + + if (!quiet) +- fprintf(outf, "CPUID(0): %.4s%.4s%.4s 0x%x CPUID levels\n", +- (char *)&ebx, (char *)&edx, (char *)&ecx, max_level); ++ fprintf(outf, "CPUID(0): %.4s%.4s%.4s 0x%x CPUID levels\n", (char *)&ebx, (char *)&edx, (char *)&ecx, max_level); + + __cpuid(1, fms, ebx, ecx, edx); + family = (fms >> 8) & 0xf; +@@ -8912,8 +8820,7 @@ void process_cpuid() + __cpuid(0x80000000, max_extended_level, ebx, ecx, edx); + + if (!quiet) { +- fprintf(outf, "CPUID(1): family:model:stepping 0x%x:%x:%x (%d:%d:%d)", +- family, model, stepping, family, model, stepping); ++ fprintf(outf, "CPUID(1): family:model:stepping 0x%x:%x:%x (%d:%d:%d)", family, model, stepping, family, model, stepping); + if (ucode_patch_valid) + fprintf(outf, " microcode 0x%x", (unsigned int)((ucode_patch >> 32) & 0xFFFFFFFF)); + fputc('\n', outf); +@@ -8927,8 +8834,7 @@ void process_cpuid() + ecx_flags & (1 << 8) ? "TM2" : "-", + edx_flags & (1 << 4) ? "TSC" : "-", + edx_flags & (1 << 5) ? "MSR" : "-", +- edx_flags & (1 << 22) ? "ACPI-TM" : "-", +- edx_flags & (1 << 28) ? "HT" : "-", edx_flags & (1 << 29) ? "TM" : "-"); ++ edx_flags & (1 << 22) ? "ACPI-TM" : "-", edx_flags & (1 << 28) ? "HT" : "-", edx_flags & (1 << 29) ? "TM" : "-"); + } + + probe_platform_features(family, model); +@@ -8976,8 +8882,7 @@ void process_cpuid() + do_ptm ? "" : "No-", + has_hwp ? "" : "No-", + has_hwp_notify ? "" : "No-", +- has_hwp_activity_window ? "" : "No-", +- has_hwp_epp ? "" : "No-", has_hwp_pkg ? "" : "No-", has_epb ? "" : "No-"); ++ has_hwp_activity_window ? "" : "No-", has_hwp_epp ? "" : "No-", has_hwp_pkg ? "" : "No-", has_epb ? "" : "No-"); + + if (!quiet) + decode_misc_enable_msr(); +@@ -9011,8 +8916,7 @@ void process_cpuid() + + if (ebx_tsc != 0) { + if (!quiet && (ebx != 0)) +- fprintf(outf, "CPUID(0x15): eax_crystal: %d ebx_tsc: %d ecx_crystal_hz: %d\n", +- eax_crystal, ebx_tsc, crystal_hz); ++ fprintf(outf, "CPUID(0x15): eax_crystal: %d ebx_tsc: %d ecx_crystal_hz: %d\n", eax_crystal, ebx_tsc, crystal_hz); + + if (crystal_hz == 0) + crystal_hz = platform->crystal_freq; +@@ -9044,8 +8948,7 @@ void process_cpuid() + tsc_tweak = base_hz / tsc_hz; + + if (!quiet) +- fprintf(outf, "CPUID(0x16): base_mhz: %d max_mhz: %d bus_mhz: %d\n", +- base_mhz, max_mhz, bus_mhz); ++ fprintf(outf, "CPUID(0x16): base_mhz: %d max_mhz: %d bus_mhz: %d\n", base_mhz, max_mhz, bus_mhz); + } + + if (cpuid_has_aperf_mperf) +@@ -9709,8 +9612,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo) + + perf_config = read_perf_config(perf_device, pinfo->event); + if (perf_config == (unsigned int)-1) { +- warnx("%s: perf/%s/%s: failed to read %s", +- __func__, perf_device, pinfo->event, "config"); ++ warnx("%s: perf/%s/%s: failed to read %s", __func__, perf_device, pinfo->event, "config"); + continue; + } + +@@ -9721,8 +9623,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo) + + fd_perf = open_perf_counter(cpu, perf_type, perf_config, -1, 0); + if (fd_perf == -1) { +- warnx("%s: perf/%s/%s: failed to open counter on cpu%d", +- __func__, perf_device, pinfo->event, cpu); ++ warnx("%s: perf/%s/%s: failed to open counter on cpu%d", __func__, perf_device, pinfo->event, cpu); + continue; + } + +@@ -9731,8 +9632,7 @@ int added_perf_counters_init_(struct perf_counter_info *pinfo) + pinfo->scale = perf_scale; + + if (debug) +- fprintf(stderr, "Add perf/%s/%s cpu%d: %d\n", +- perf_device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]); ++ fprintf(stderr, "Add perf/%s/%s cpu%d: %d\n", perf_device, pinfo->event, cpu, pinfo->fd_perf_per_domain[next_domain]); + } + + pinfo = pinfo->next; +@@ -10046,8 +9946,7 @@ int pmt_add_counter(unsigned int guid, unsigned int seq, const char *name, enum + } + + if (conflict) { +- fprintf(stderr, "%s: conflicting parameters for the PMT counter with the same name %s\n", +- __func__, name); ++ fprintf(stderr, "%s: conflicting parameters for the PMT counter with the same name %s\n", __func__, name); + exit(1); + } + +@@ -10090,8 +9989,7 @@ void pmt_init(void) + * CWF with newer firmware might require a PMT_TYPE_XTAL_TIME intead of PMT_TYPE_TCORE_CLOCK. + */ + pmt_add_counter(PMT_CWF_MC1E_GUID, seq, "CPU%c1e", PMT_TYPE_TCORE_CLOCK, +- PMT_COUNTER_CWF_MC1E_LSB, PMT_COUNTER_CWF_MC1E_MSB, offset, SCOPE_CPU, +- FORMAT_DELTA, cpu_num, PMT_OPEN_TRY); ++ PMT_COUNTER_CWF_MC1E_LSB, PMT_COUNTER_CWF_MC1E_MSB, offset, SCOPE_CPU, FORMAT_DELTA, cpu_num, PMT_OPEN_TRY); + + /* + * Rather complex logic for each time we go to the next loop iteration, +@@ -10287,8 +10185,7 @@ struct msr_counter *find_msrp_by_name(struct msr_counter *head, char *name) + } + + int add_counter(unsigned int msr_num, char *path, char *name, +- unsigned int width, enum counter_scope scope, +- enum counter_type type, enum counter_format format, int flags, int id) ++ unsigned int width, enum counter_scope scope, enum counter_type type, enum counter_format format, int flags, int id) + { + struct msr_counter *msrp; + +@@ -10397,9 +10294,7 @@ int add_counter(unsigned int msr_num, char *path, char *name, + struct perf_counter_info *make_perf_counter_info(const char *perf_device, + const char *perf_event, + const char *name, +- unsigned int width, +- enum counter_scope scope, +- enum counter_type type, enum counter_format format) ++ unsigned int width, enum counter_scope scope, enum counter_type type, enum counter_format format) + { + struct perf_counter_info *pinfo; + +@@ -10474,8 +10369,7 @@ int add_perf_counter(const char *perf_device, const char *perf_event, const char + + // FIXME: we might not have debug here yet + if (debug) +- fprintf(stderr, "%s: %s/%s, name: %s, scope%d\n", +- __func__, pinfo->device, pinfo->event, pinfo->name, pinfo->scope); ++ fprintf(stderr, "%s: %s/%s, name: %s, scope%d\n", __func__, pinfo->device, pinfo->event, pinfo->name, pinfo->scope); + + return 0; + } +@@ -10644,8 +10538,7 @@ int pmt_parse_from_path(const char *target_path, unsigned int *out_guid, unsigne + + pmt_diriter_init(&pmt_iter); + +- for (dirname = pmt_diriter_begin(&pmt_iter, SYSFS_TELEM_PATH); dirname != NULL; +- dirname = pmt_diriter_next(&pmt_iter)) { ++ for (dirname = pmt_diriter_begin(&pmt_iter, SYSFS_TELEM_PATH); dirname != NULL; dirname = pmt_diriter_next(&pmt_iter)) { + + fd_telem_dir = openat(dirfd(pmt_iter.dir), dirname->d_name, O_RDONLY | O_DIRECTORY); + if (fd_telem_dir == -1) +@@ -10657,8 +10550,7 @@ int pmt_parse_from_path(const char *target_path, unsigned int *out_guid, unsigne + } + + if (fstat(fd_telem_dir, &stat) == -1) { +- fprintf(stderr, "%s: Failed to stat %s directory: %s", __func__, +- dirname->d_name, strerror(errno)); ++ fprintf(stderr, "%s: Failed to stat %s directory: %s", __func__, dirname->d_name, strerror(errno)); + continue; + } + +@@ -10754,8 +10646,7 @@ void parse_add_command_pmt(char *add_command) + } + + if (!has_scope) { +- printf("%s: invalid value for scope. Expected cpu%%u, core%%u or package%%u.\n", +- __func__); ++ printf("%s: invalid value for scope. Expected cpu%%u, core%%u or package%%u.\n", __func__); + exit(1); + } + +@@ -10831,8 +10722,7 @@ void parse_add_command_pmt(char *add_command) + } + + if (!has_format) { +- fprintf(stderr, "%s: Invalid format %s. Expected raw, average or delta\n", +- __func__, format_name); ++ fprintf(stderr, "%s: Invalid format %s. Expected raw, average or delta\n", __func__, format_name); + exit(1); + } + } +-- +2.43.0 + diff --git a/SPECS/kernel/0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet b/SPECS/kernel/0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet deleted file mode 100644 index 0e9d7e949..000000000 --- a/SPECS/kernel/0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet +++ /dev/null @@ -1,262 +0,0 @@ -From e943f26ae8b4682211ca37fbca3994ef999f600a Mon Sep 17 00:00:00 2001 -From: Yang Weijiang -Date: Fri, 4 Jul 2025 01:49:52 -0700 -Subject: [PATCH 22/24] KVM: nVMX: Enable CET support for nested guest - -Set up CET MSRs, related VM_ENTRY/EXIT control bits and fixed CR4 setting -to enable CET for nested VM. - -vmcs12 and vmcs02 needs to be synced when L2 exits to L1 or when L1 wants -to resume L2, that way correct CET states can be observed by one another. - -Signed-off-by: Yang Weijiang -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 80 ++++++++++++++++++++++++++++++++++++++- - arch/x86/kvm/vmx/vmcs12.c | 6 +++ - arch/x86/kvm/vmx/vmcs12.h | 14 ++++++- - arch/x86/kvm/vmx/vmx.c | 2 + - arch/x86/kvm/vmx/vmx.h | 3 ++ - 5 files changed, 102 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index f40ba4adb5ee..0f8dbf3cc6c3 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -746,6 +746,27 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD); - nested_vmx_merge_msr_bitmaps_write(MSR_IA32_FLUSH_CMD); - -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_U_CET, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_S_CET, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL0_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL1_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL2_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_PL3_SSP, MSR_TYPE_RW); -+ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -+ MSR_IA32_INT_SSP_TAB, MSR_TYPE_RW); -+ - nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, - MSR_IA32_APERF, MSR_TYPE_R); - -@@ -2554,6 +2575,32 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0 - } - } - -+static inline void cet_vmcs_fields_get(struct kvm_vcpu *vcpu, u64 *s_cet, -+ u64 *ssp, u64 *ssp_tbl) -+{ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) || -+ guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ *s_cet = vmcs_readl(GUEST_S_CET); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { -+ *ssp = vmcs_readl(GUEST_SSP); -+ *ssp_tbl = vmcs_readl(GUEST_INTR_SSP_TABLE); -+ } -+} -+ -+static inline void cet_vmcs_fields_set(struct kvm_vcpu *vcpu, u64 s_cet, -+ u64 ssp, u64 ssp_tbl) -+{ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) || -+ guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) -+ vmcs_writel(GUEST_S_CET, s_cet); -+ -+ if (guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) { -+ vmcs_writel(GUEST_SSP, ssp); -+ vmcs_writel(GUEST_INTR_SSP_TABLE, ssp_tbl); -+ } -+} -+ - static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) - { - struct hv_enlightened_vmcs *hv_evmcs = nested_vmx_evmcs(vmx); -@@ -2670,6 +2717,10 @@ static void prepare_vmcs02_rare(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) - vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr); - vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr); - -+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) -+ cet_vmcs_fields_set(&vmx->vcpu, vmcs12->guest_s_cet, -+ vmcs12->guest_ssp, vmcs12->guest_ssp_tbl); -+ - set_cr4_guest_host_mask(vmx); - } - -@@ -2709,6 +2760,13 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, - kvm_set_dr(vcpu, 7, vcpu->arch.dr7); - vmx_guest_debugctl_write(vcpu, vmx->nested.pre_vmenter_debugctl); - } -+ -+ if (!vmx->nested.nested_run_pending || -+ !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE)) -+ cet_vmcs_fields_set(vcpu, vmx->nested.pre_vmenter_s_cet, -+ vmx->nested.pre_vmenter_ssp, -+ vmx->nested.pre_vmenter_ssp_tbl); -+ - if (kvm_mpx_supported() && (!vmx->nested.nested_run_pending || - !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) - vmcs_write64(GUEST_BNDCFGS, vmx->nested.pre_vmenter_bndcfgs); -@@ -3585,6 +3643,12 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, - !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) - vmx->nested.pre_vmenter_bndcfgs = vmcs_read64(GUEST_BNDCFGS); - -+ if (!vmx->nested.nested_run_pending || -+ !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE)) -+ cet_vmcs_fields_get(vcpu, &vmx->nested.pre_vmenter_s_cet, -+ &vmx->nested.pre_vmenter_ssp, -+ &vmx->nested.pre_vmenter_ssp_tbl); -+ - /* - * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* - * nested early checks are disabled. In the event of a "late" VM-Fail, -@@ -4512,6 +4576,9 @@ static bool is_vmcs12_ext_field(unsigned long field) - case GUEST_IDTR_BASE: - case GUEST_PENDING_DBG_EXCEPTIONS: - case GUEST_BNDCFGS: -+ case GUEST_S_CET: -+ case GUEST_SSP: -+ case GUEST_INTR_SSP_TABLE: - return true; - default: - break; -@@ -4562,6 +4629,10 @@ static void sync_vmcs02_to_vmcs12_rare(struct kvm_vcpu *vcpu, - vmcs12->guest_pending_dbg_exceptions = - vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS); - -+ cet_vmcs_fields_get(&vmx->vcpu, &vmcs12->guest_s_cet, -+ &vmcs12->guest_ssp, -+ &vmcs12->guest_ssp_tbl); -+ - vmx->nested.need_sync_vmcs02_to_vmcs12_rare = false; - } - -@@ -4793,6 +4864,10 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, - if (vmcs12->vm_exit_controls & VM_EXIT_CLEAR_BNDCFGS) - vmcs_write64(GUEST_BNDCFGS, 0); - -+ if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_CET_STATE) -+ cet_vmcs_fields_set(vcpu, vmcs12->host_s_cet, vmcs12->host_ssp, -+ vmcs12->host_ssp_tbl); -+ - if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PAT) { - vmcs_write64(GUEST_IA32_PAT, vmcs12->host_ia32_pat); - vcpu->arch.pat = vmcs12->host_ia32_pat; -@@ -7070,7 +7145,7 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, - VM_EXIT_HOST_ADDR_SPACE_SIZE | - #endif - VM_EXIT_LOAD_IA32_PAT | VM_EXIT_SAVE_IA32_PAT | -- VM_EXIT_CLEAR_BNDCFGS; -+ VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_LOAD_CET_STATE; - msrs->exit_ctls_high |= - VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR | - VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_IA32_EFER | -@@ -7092,7 +7167,8 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, - #ifdef CONFIG_X86_64 - VM_ENTRY_IA32E_MODE | - #endif -- VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS; -+ VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS | -+ VM_ENTRY_LOAD_CET_STATE; - msrs->entry_ctls_high |= - (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER | - VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); -diff --git a/arch/x86/kvm/vmx/vmcs12.c b/arch/x86/kvm/vmx/vmcs12.c -index 106a72c923ca..4233b5ca9461 100644 ---- a/arch/x86/kvm/vmx/vmcs12.c -+++ b/arch/x86/kvm/vmx/vmcs12.c -@@ -139,6 +139,9 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD(GUEST_PENDING_DBG_EXCEPTIONS, guest_pending_dbg_exceptions), - FIELD(GUEST_SYSENTER_ESP, guest_sysenter_esp), - FIELD(GUEST_SYSENTER_EIP, guest_sysenter_eip), -+ FIELD(GUEST_S_CET, guest_s_cet), -+ FIELD(GUEST_SSP, guest_ssp), -+ FIELD(GUEST_INTR_SSP_TABLE, guest_ssp_tbl), - FIELD(HOST_CR0, host_cr0), - FIELD(HOST_CR3, host_cr3), - FIELD(HOST_CR4, host_cr4), -@@ -151,5 +154,8 @@ const unsigned short vmcs12_field_offsets[] = { - FIELD(HOST_IA32_SYSENTER_EIP, host_ia32_sysenter_eip), - FIELD(HOST_RSP, host_rsp), - FIELD(HOST_RIP, host_rip), -+ FIELD(HOST_S_CET, host_s_cet), -+ FIELD(HOST_SSP, host_ssp), -+ FIELD(HOST_INTR_SSP_TABLE, host_ssp_tbl), - }; - const unsigned int nr_vmcs12_fields = ARRAY_SIZE(vmcs12_field_offsets); -diff --git a/arch/x86/kvm/vmx/vmcs12.h b/arch/x86/kvm/vmx/vmcs12.h -index 56fd150a6f24..4ad6b16525b9 100644 ---- a/arch/x86/kvm/vmx/vmcs12.h -+++ b/arch/x86/kvm/vmx/vmcs12.h -@@ -117,7 +117,13 @@ struct __packed vmcs12 { - natural_width host_ia32_sysenter_eip; - natural_width host_rsp; - natural_width host_rip; -- natural_width paddingl[8]; /* room for future expansion */ -+ natural_width host_s_cet; -+ natural_width host_ssp; -+ natural_width host_ssp_tbl; -+ natural_width guest_s_cet; -+ natural_width guest_ssp; -+ natural_width guest_ssp_tbl; -+ natural_width paddingl[2]; /* room for future expansion */ - u32 pin_based_vm_exec_control; - u32 cpu_based_vm_exec_control; - u32 exception_bitmap; -@@ -294,6 +300,12 @@ static inline void vmx_check_vmcs12_offsets(void) - CHECK_OFFSET(host_ia32_sysenter_eip, 656); - CHECK_OFFSET(host_rsp, 664); - CHECK_OFFSET(host_rip, 672); -+ CHECK_OFFSET(host_s_cet, 680); -+ CHECK_OFFSET(host_ssp, 688); -+ CHECK_OFFSET(host_ssp_tbl, 696); -+ CHECK_OFFSET(guest_s_cet, 704); -+ CHECK_OFFSET(guest_ssp, 712); -+ CHECK_OFFSET(guest_ssp_tbl, 720); - CHECK_OFFSET(pin_based_vm_exec_control, 744); - CHECK_OFFSET(cpu_based_vm_exec_control, 748); - CHECK_OFFSET(exception_bitmap, 752); -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 8a9fb9a02080..779f73d09e68 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7786,6 +7786,8 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) - cr4_fixed1_update(X86_CR4_PKE, ecx, feature_bit(PKU)); - cr4_fixed1_update(X86_CR4_UMIP, ecx, feature_bit(UMIP)); - cr4_fixed1_update(X86_CR4_LA57, ecx, feature_bit(LA57)); -+ cr4_fixed1_update(X86_CR4_CET, ecx, feature_bit(SHSTK)); -+ cr4_fixed1_update(X86_CR4_CET, edx, feature_bit(IBT)); - - entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1); - cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM)); -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index b8bf296eb9a4..ef8fd345f1ae 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -181,6 +181,9 @@ struct nested_vmx { - */ - u64 pre_vmenter_debugctl; - u64 pre_vmenter_bndcfgs; -+ u64 pre_vmenter_s_cet; -+ u64 pre_vmenter_ssp; -+ u64 pre_vmenter_ssp_tbl; - - /* to migrate it to L1 if L2 writes to L1's CR8 directly */ - int l1_tpr_threshold; --- -2.43.0 - diff --git a/SPECS/kernel/0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi b/SPECS/kernel/0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi new file mode 100644 index 000000000..22b8ef8a4 --- /dev/null +++ b/SPECS/kernel/0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi @@ -0,0 +1,59 @@ +From f99e13d619df7fd1fa38f90971e329c4d524e863 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Tue, 9 Aug 2022 10:03:32 -0700 +Subject: [PATCH 22/44] KVM: nVMX: Enable VMX FRED controls + +Permit use of VMX FRED controls in nested VMX now that support for nested +FRED is implemented. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Shan Kang +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kvm/vmx/nested.c | 5 +++-- + arch/x86/kvm/vmx/vmx.c | 1 + + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c +index 361152678aead..4d61549588457 100644 +--- a/arch/x86/kvm/vmx/nested.c ++++ b/arch/x86/kvm/vmx/nested.c +@@ -7456,7 +7456,8 @@ static void nested_vmx_setup_exit_ctls(struct vmcs_config *vmcs_conf, + * advertise any feature in it to nVMX until its nVMX support + * is ready. + */ +- msrs->secondary_exit_ctls &= 0; ++ msrs->secondary_exit_ctls &= SECONDARY_VM_EXIT_SAVE_IA32_FRED | ++ SECONDARY_VM_EXIT_LOAD_IA32_FRED; + } + } + +@@ -7472,7 +7473,7 @@ static void nested_vmx_setup_entry_ctls(struct vmcs_config *vmcs_conf, + VM_ENTRY_IA32E_MODE | + #endif + VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_BNDCFGS | +- VM_ENTRY_LOAD_CET_STATE; ++ VM_ENTRY_LOAD_CET_STATE | VM_ENTRY_LOAD_IA32_FRED; + msrs->entry_ctls_high |= + (VM_ENTRY_ALWAYSON_WITHOUT_TRUE_MSR | VM_ENTRY_LOAD_IA32_EFER | + VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL); +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 4fdf80d02dadd..823ad3300f656 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7983,6 +7983,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) + + entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 1); + cr4_fixed1_update(X86_CR4_LAM_SUP, eax, feature_bit(LAM)); ++ cr4_fixed1_update(X86_CR4_FRED, eax, feature_bit(FRED)); + + #undef cr4_fixed1_update + } +-- +2.43.0 + diff --git a/SPECS/kernel/0022-perf-core-Support-to-capture-higher-width-vector-regi.perf b/SPECS/kernel/0022-perf-core-Support-to-capture-higher-width-vector-regi.perf deleted file mode 100644 index 1b0985236..000000000 --- a/SPECS/kernel/0022-perf-core-Support-to-capture-higher-width-vector-regi.perf +++ /dev/null @@ -1,625 +0,0 @@ -From cfe395a25e116bb796eb55e94adb894304aca978 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 09:05:25 +0000 -Subject: [PATCH 022/100] perf/core: Support to capture higher width vector - registers - -Arch-PEBS supports to capture more vector registers like OPMASK/YMM/ZMM -registers besides XMM registers. This patch extends PERF_SAMPLE_REGS_INTR -and PERF_SAMPLE_REGS_USER attributes to support these new vector registers -capturing at interrupt and user space. - -The arrays sample_regs_intr/user__ext[] is added into perf_event_attr -structure to record user configured extended register bitmap and a helper -perf_reg_ext_validate() is added to validate if these registers are -supported on some specific PMUs. Furthermore considering to leave enough -space to support more GPRs like R16 ~ R31 introduced by APX in the future, -directly extend the array size to 7. - -This patch just adds the common perf/core support, the x86/intel specific -support would be added in next patch. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/arm/kernel/perf_regs.c | 6 ++ - arch/arm64/kernel/perf_regs.c | 6 ++ - arch/csky/kernel/perf_regs.c | 5 ++ - arch/loongarch/kernel/perf_regs.c | 5 ++ - arch/mips/kernel/perf_regs.c | 5 ++ - arch/powerpc/perf/perf_regs.c | 5 ++ - arch/riscv/kernel/perf_regs.c | 5 ++ - arch/s390/kernel/perf_regs.c | 5 ++ - arch/x86/include/asm/perf_event.h | 4 ++ - arch/x86/include/uapi/asm/perf_regs.h | 79 ++++++++++++++++++++- - arch/x86/kernel/perf_regs.c | 64 ++++++++++++++++- - include/linux/perf_event.h | 4 ++ - include/linux/perf_regs.h | 10 +++ - include/uapi/linux/perf_event.h | 11 +++ - kernel/events/core.c | 98 +++++++++++++++++++++++++-- - 15 files changed, 304 insertions(+), 8 deletions(-) - -diff --git a/arch/arm/kernel/perf_regs.c b/arch/arm/kernel/perf_regs.c -index 0529f90395c9..86b2002d0846 100644 ---- a/arch/arm/kernel/perf_regs.c -+++ b/arch/arm/kernel/perf_regs.c -@@ -37,3 +37,9 @@ void perf_get_regs_user(struct perf_regs *regs_user, - regs_user->regs = task_pt_regs(current); - regs_user->abi = perf_reg_abi(current); - } -+ -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ -diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c -index b4eece3eb17d..1c91fd3530d5 100644 ---- a/arch/arm64/kernel/perf_regs.c -+++ b/arch/arm64/kernel/perf_regs.c -@@ -104,3 +104,9 @@ void perf_get_regs_user(struct perf_regs *regs_user, - regs_user->regs = task_pt_regs(current); - regs_user->abi = perf_reg_abi(current); - } -+ -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ -diff --git a/arch/csky/kernel/perf_regs.c b/arch/csky/kernel/perf_regs.c -index 09b7f88a2d6a..d2e2af0bf1ad 100644 ---- a/arch/csky/kernel/perf_regs.c -+++ b/arch/csky/kernel/perf_regs.c -@@ -26,6 +26,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - return PERF_SAMPLE_REGS_ABI_32; -diff --git a/arch/loongarch/kernel/perf_regs.c b/arch/loongarch/kernel/perf_regs.c -index 263ac4ab5af6..e1df67e3fab4 100644 ---- a/arch/loongarch/kernel/perf_regs.c -+++ b/arch/loongarch/kernel/perf_regs.c -@@ -34,6 +34,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_value(struct pt_regs *regs, int idx) - { - if (WARN_ON_ONCE((u32)idx >= PERF_REG_LOONGARCH_MAX)) -diff --git a/arch/mips/kernel/perf_regs.c b/arch/mips/kernel/perf_regs.c -index e686780d1647..bbb5f25b9191 100644 ---- a/arch/mips/kernel/perf_regs.c -+++ b/arch/mips/kernel/perf_regs.c -@@ -37,6 +37,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_value(struct pt_regs *regs, int idx) - { - long v; -diff --git a/arch/powerpc/perf/perf_regs.c b/arch/powerpc/perf/perf_regs.c -index 350dccb0143c..d919c628aee3 100644 ---- a/arch/powerpc/perf/perf_regs.c -+++ b/arch/powerpc/perf/perf_regs.c -@@ -132,6 +132,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - if (is_tsk_32bit_task(task)) -diff --git a/arch/riscv/kernel/perf_regs.c b/arch/riscv/kernel/perf_regs.c -index fd304a248de6..5beb60544c9a 100644 ---- a/arch/riscv/kernel/perf_regs.c -+++ b/arch/riscv/kernel/perf_regs.c -@@ -26,6 +26,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - #if __riscv_xlen == 64 -diff --git a/arch/s390/kernel/perf_regs.c b/arch/s390/kernel/perf_regs.c -index a6b058ee4a36..9247573229b0 100644 ---- a/arch/s390/kernel/perf_regs.c -+++ b/arch/s390/kernel/perf_regs.c -@@ -42,6 +42,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - if (test_tsk_thread_flag(task, TIF_31BIT)) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index 452a2222ca6b..a4f7cfc3fddb 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -710,6 +710,10 @@ struct x86_perf_regs { - struct pt_regs regs; - u64 ssp; - u64 *xmm_regs; -+ u64 *opmask_regs; -+ u64 *ymmh_regs; -+ u64 *zmmh_regs; -+ u64 *h16zmm_regs; - }; - - extern unsigned long perf_arch_instruction_pointer(struct pt_regs *regs); -diff --git a/arch/x86/include/uapi/asm/perf_regs.h b/arch/x86/include/uapi/asm/perf_regs.h -index f9c5b16b1882..5e2d9796b2cc 100644 ---- a/arch/x86/include/uapi/asm/perf_regs.h -+++ b/arch/x86/include/uapi/asm/perf_regs.h -@@ -33,7 +33,7 @@ enum perf_event_x86_regs { - PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, - PERF_REG_X86_64_MAX = PERF_REG_X86_SSP + 1, - -- /* These all need two bits set because they are 128bit */ -+ /* These all need two bits set because they are 128 bits */ - PERF_REG_X86_XMM0 = 32, - PERF_REG_X86_XMM1 = 34, - PERF_REG_X86_XMM2 = 36, -@@ -53,6 +53,83 @@ enum perf_event_x86_regs { - - /* These include both GPRs and XMMX registers */ - PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2, -+ -+ /* Leave bits[127:64] for other GP registers, like R16 ~ R31.*/ -+ -+ /* -+ * Each YMM register need 4 bits to represent because they are 256 bits. -+ * PERF_REG_X86_YMMH0 = 128 -+ */ -+ PERF_REG_X86_YMM0 = 128, -+ PERF_REG_X86_YMM1 = PERF_REG_X86_YMM0 + 4, -+ PERF_REG_X86_YMM2 = PERF_REG_X86_YMM1 + 4, -+ PERF_REG_X86_YMM3 = PERF_REG_X86_YMM2 + 4, -+ PERF_REG_X86_YMM4 = PERF_REG_X86_YMM3 + 4, -+ PERF_REG_X86_YMM5 = PERF_REG_X86_YMM4 + 4, -+ PERF_REG_X86_YMM6 = PERF_REG_X86_YMM5 + 4, -+ PERF_REG_X86_YMM7 = PERF_REG_X86_YMM6 + 4, -+ PERF_REG_X86_YMM8 = PERF_REG_X86_YMM7 + 4, -+ PERF_REG_X86_YMM9 = PERF_REG_X86_YMM8 + 4, -+ PERF_REG_X86_YMM10 = PERF_REG_X86_YMM9 + 4, -+ PERF_REG_X86_YMM11 = PERF_REG_X86_YMM10 + 4, -+ PERF_REG_X86_YMM12 = PERF_REG_X86_YMM11 + 4, -+ PERF_REG_X86_YMM13 = PERF_REG_X86_YMM12 + 4, -+ PERF_REG_X86_YMM14 = PERF_REG_X86_YMM13 + 4, -+ PERF_REG_X86_YMM15 = PERF_REG_X86_YMM14 + 4, -+ PERF_REG_X86_YMM_MAX = PERF_REG_X86_YMM15 + 4, -+ -+ /* -+ * Each ZMM register needs 8 bits to represent because they are 512 bits -+ * PERF_REG_X86_ZMMH0 = 192 -+ */ -+ PERF_REG_X86_ZMM0 = PERF_REG_X86_YMM_MAX, -+ PERF_REG_X86_ZMM1 = PERF_REG_X86_ZMM0 + 8, -+ PERF_REG_X86_ZMM2 = PERF_REG_X86_ZMM1 + 8, -+ PERF_REG_X86_ZMM3 = PERF_REG_X86_ZMM2 + 8, -+ PERF_REG_X86_ZMM4 = PERF_REG_X86_ZMM3 + 8, -+ PERF_REG_X86_ZMM5 = PERF_REG_X86_ZMM4 + 8, -+ PERF_REG_X86_ZMM6 = PERF_REG_X86_ZMM5 + 8, -+ PERF_REG_X86_ZMM7 = PERF_REG_X86_ZMM6 + 8, -+ PERF_REG_X86_ZMM8 = PERF_REG_X86_ZMM7 + 8, -+ PERF_REG_X86_ZMM9 = PERF_REG_X86_ZMM8 + 8, -+ PERF_REG_X86_ZMM10 = PERF_REG_X86_ZMM9 + 8, -+ PERF_REG_X86_ZMM11 = PERF_REG_X86_ZMM10 + 8, -+ PERF_REG_X86_ZMM12 = PERF_REG_X86_ZMM11 + 8, -+ PERF_REG_X86_ZMM13 = PERF_REG_X86_ZMM12 + 8, -+ PERF_REG_X86_ZMM14 = PERF_REG_X86_ZMM13 + 8, -+ PERF_REG_X86_ZMM15 = PERF_REG_X86_ZMM14 + 8, -+ PERF_REG_X86_ZMM16 = PERF_REG_X86_ZMM15 + 8, -+ PERF_REG_X86_ZMM17 = PERF_REG_X86_ZMM16 + 8, -+ PERF_REG_X86_ZMM18 = PERF_REG_X86_ZMM17 + 8, -+ PERF_REG_X86_ZMM19 = PERF_REG_X86_ZMM18 + 8, -+ PERF_REG_X86_ZMM20 = PERF_REG_X86_ZMM19 + 8, -+ PERF_REG_X86_ZMM21 = PERF_REG_X86_ZMM20 + 8, -+ PERF_REG_X86_ZMM22 = PERF_REG_X86_ZMM21 + 8, -+ PERF_REG_X86_ZMM23 = PERF_REG_X86_ZMM22 + 8, -+ PERF_REG_X86_ZMM24 = PERF_REG_X86_ZMM23 + 8, -+ PERF_REG_X86_ZMM25 = PERF_REG_X86_ZMM24 + 8, -+ PERF_REG_X86_ZMM26 = PERF_REG_X86_ZMM25 + 8, -+ PERF_REG_X86_ZMM27 = PERF_REG_X86_ZMM26 + 8, -+ PERF_REG_X86_ZMM28 = PERF_REG_X86_ZMM27 + 8, -+ PERF_REG_X86_ZMM29 = PERF_REG_X86_ZMM28 + 8, -+ PERF_REG_X86_ZMM30 = PERF_REG_X86_ZMM29 + 8, -+ PERF_REG_X86_ZMM31 = PERF_REG_X86_ZMM30 + 8, -+ PERF_REG_X86_ZMM_MAX = PERF_REG_X86_ZMM31 + 8, -+ -+ /* -+ * OPMASK Registers -+ * PERF_REG_X86_OPMASK0 = 448 -+ */ -+ PERF_REG_X86_OPMASK0 = PERF_REG_X86_ZMM_MAX, -+ PERF_REG_X86_OPMASK1 = PERF_REG_X86_OPMASK0 + 1, -+ PERF_REG_X86_OPMASK2 = PERF_REG_X86_OPMASK1 + 1, -+ PERF_REG_X86_OPMASK3 = PERF_REG_X86_OPMASK2 + 1, -+ PERF_REG_X86_OPMASK4 = PERF_REG_X86_OPMASK3 + 1, -+ PERF_REG_X86_OPMASK5 = PERF_REG_X86_OPMASK4 + 1, -+ PERF_REG_X86_OPMASK6 = PERF_REG_X86_OPMASK5 + 1, -+ PERF_REG_X86_OPMASK7 = PERF_REG_X86_OPMASK6 + 1, -+ -+ PERF_REG_X86_VEC_MAX = PERF_REG_X86_OPMASK7 + 1, - }; - - #define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1)) -diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c -index 985bd616200e..466ccd67ea99 100644 ---- a/arch/x86/kernel/perf_regs.c -+++ b/arch/x86/kernel/perf_regs.c -@@ -59,12 +59,55 @@ static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = { - #endif - }; - -+static u64 perf_reg_ext_value(struct pt_regs *regs, int idx) -+{ -+ struct x86_perf_regs *perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ u64 data; -+ int mod; -+ -+ switch (idx) { -+ case PERF_REG_X86_YMM0 ... PERF_REG_X86_YMM_MAX - 1: -+ idx -= PERF_REG_X86_YMM0; -+ mod = idx % 4; -+ if (mod < 2) -+ data = !perf_regs->xmm_regs ? 0 : perf_regs->xmm_regs[idx / 4 + mod]; -+ else -+ data = !perf_regs->ymmh_regs ? 0 : perf_regs->ymmh_regs[idx / 4 + mod - 2]; -+ return data; -+ case PERF_REG_X86_ZMM0 ... PERF_REG_X86_ZMM16 - 1: -+ idx -= PERF_REG_X86_ZMM0; -+ mod = idx % 8; -+ if (mod < 4) { -+ if (mod < 2) -+ data = !perf_regs->xmm_regs ? 0 : perf_regs->xmm_regs[idx / 8 + mod]; -+ else -+ data = !perf_regs->ymmh_regs ? 0 : perf_regs->ymmh_regs[idx / 8 + mod - 2]; -+ } else { -+ data = !perf_regs->zmmh_regs ? 0 : perf_regs->zmmh_regs[idx / 8 + mod - 4]; -+ } -+ return data; -+ case PERF_REG_X86_ZMM16 ... PERF_REG_X86_ZMM_MAX - 1: -+ idx -= PERF_REG_X86_ZMM16; -+ return !perf_regs->h16zmm_regs ? 0 : perf_regs->h16zmm_regs[idx]; -+ case PERF_REG_X86_OPMASK0 ... PERF_REG_X86_OPMASK7: -+ idx -= PERF_REG_X86_OPMASK0; -+ return !perf_regs->opmask_regs ? 0 : perf_regs->opmask_regs[idx]; -+ default: -+ WARN_ON_ONCE(1); -+ break; -+ } -+ -+ return 0; -+} -+ - u64 perf_reg_value(struct pt_regs *regs, int idx) - { -- struct x86_perf_regs *perf_regs; -+ struct x86_perf_regs *perf_regs = container_of(regs, struct x86_perf_regs, regs); -+ -+ if (idx >= PERF_REG_EXTENDED_OFFSET) -+ return perf_reg_ext_value(regs, idx); - - if (idx >= PERF_REG_X86_XMM0 && idx < PERF_REG_X86_XMM_MAX) { -- perf_regs = container_of(regs, struct x86_perf_regs, regs); - if (!perf_regs->xmm_regs) - return 0; - return perf_regs->xmm_regs[idx - PERF_REG_X86_XMM0]; -@@ -102,6 +145,11 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ return -EINVAL; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - return PERF_SAMPLE_REGS_ABI_32; -@@ -127,6 +175,18 @@ int perf_reg_validate(u64 mask) - return 0; - } - -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size) -+{ -+ if (!mask || !size || size > PERF_NUM_EXT_REGS) -+ return -EINVAL; -+ -+ if (find_last_bit(mask, size) > -+ (PERF_REG_X86_VEC_MAX - PERF_REG_EXTENDED_OFFSET)) -+ return -EINVAL; -+ -+ return 0; -+} -+ - u64 perf_reg_abi(struct task_struct *task) - { - if (!user_64bit_mode(task_pt_regs(task))) -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index ec9d96025683..dd9bfe42c011 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -305,6 +305,7 @@ struct perf_event_pmu_context; - #define PERF_PMU_CAP_EXTENDED_HW_TYPE 0x0100 - #define PERF_PMU_CAP_AUX_PAUSE 0x0200 - #define PERF_PMU_CAP_AUX_PREFER_LARGE 0x0400 -+#define PERF_PMU_CAP_MORE_EXT_REGS 0x0800 - - /** - * pmu::scope -@@ -1476,6 +1477,9 @@ static inline void perf_clear_branch_entry_bitfields(struct perf_branch_entry *b - br->reserved = 0; - } - -+extern bool has_more_extended_intr_regs(struct perf_event *event); -+extern bool has_more_extended_user_regs(struct perf_event *event); -+extern bool has_more_extended_regs(struct perf_event *event); - extern void perf_output_sample(struct perf_output_handle *handle, - struct perf_event_header *header, - struct perf_sample_data *data, -diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h -index f632c5725f16..aa4dfb5af552 100644 ---- a/include/linux/perf_regs.h -+++ b/include/linux/perf_regs.h -@@ -9,6 +9,8 @@ struct perf_regs { - struct pt_regs *regs; - }; - -+#define PERF_REG_EXTENDED_OFFSET 64 -+ - #ifdef CONFIG_HAVE_PERF_REGS - #include - -@@ -21,6 +23,8 @@ int perf_reg_validate(u64 mask); - u64 perf_reg_abi(struct task_struct *task); - void perf_get_regs_user(struct perf_regs *regs_user, - struct pt_regs *regs); -+int perf_reg_ext_validate(unsigned long *mask, unsigned int size); -+ - #else - - #define PERF_REG_EXTENDED_MASK 0 -@@ -35,6 +39,12 @@ static inline int perf_reg_validate(u64 mask) - return mask ? -ENOSYS : 0; - } - -+static inline int perf_reg_ext_validate(unsigned long *mask, -+ unsigned int size) -+{ -+ return -EINVAL; -+} -+ - static inline u64 perf_reg_abi(struct task_struct *task) - { - return PERF_SAMPLE_REGS_ABI_NONE; -diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h -index 78a362b80027..8de3c287096f 100644 ---- a/include/uapi/linux/perf_event.h -+++ b/include/uapi/linux/perf_event.h -@@ -382,6 +382,10 @@ enum perf_event_read_format { - #define PERF_ATTR_SIZE_VER6 120 /* Add: aux_sample_size */ - #define PERF_ATTR_SIZE_VER7 128 /* Add: sig_data */ - #define PERF_ATTR_SIZE_VER8 136 /* Add: config3 */ -+#define PERF_ATTR_SIZE_VER9 168 /* add: sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE] */ -+ -+#define PERF_EXT_REGS_ARRAY_SIZE 7 -+#define PERF_NUM_EXT_REGS (PERF_EXT_REGS_ARRAY_SIZE * 64) - - /* - * 'struct perf_event_attr' contains various attributes that define -@@ -543,6 +547,13 @@ struct perf_event_attr { - __u64 sig_data; - - __u64 config3; /* extension of config2 */ -+ -+ /* -+ * Extension sets of regs to dump for each sample. -+ * See asm/perf_regs.h for details. -+ */ -+ __u64 sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE]; -+ __u64 sample_regs_user_ext[PERF_EXT_REGS_ARRAY_SIZE]; - }; - - /* -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 872122e074e5..0ff00d0b59ea 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -7434,6 +7434,21 @@ perf_output_sample_regs(struct perf_output_handle *handle, - } - } - -+static void -+perf_output_sample_regs_ext(struct perf_output_handle *handle, -+ struct pt_regs *regs, -+ unsigned long *mask, -+ unsigned int size) -+{ -+ int bit; -+ u64 val; -+ -+ for_each_set_bit(bit, mask, size) { -+ val = perf_reg_value(regs, bit + PERF_REG_EXTENDED_OFFSET); -+ perf_output_put(handle, val); -+ } -+} -+ - static void perf_sample_regs_user(struct perf_regs *regs_user, - struct pt_regs *regs) - { -@@ -7866,6 +7881,26 @@ static void perf_output_read(struct perf_output_handle *handle, - perf_output_read_one(handle, event, enabled, running); - } - -+inline bool has_more_extended_intr_regs(struct perf_event *event) -+{ -+ return !!bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS); -+} -+ -+inline bool has_more_extended_user_regs(struct perf_event *event) -+{ -+ return !!bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_user_ext, -+ PERF_NUM_EXT_REGS); -+} -+ -+inline bool has_more_extended_regs(struct perf_event *event) -+{ -+ return has_more_extended_intr_regs(event) || -+ has_more_extended_user_regs(event); -+} -+ - void perf_output_sample(struct perf_output_handle *handle, - struct perf_event_header *header, - struct perf_sample_data *data, -@@ -7991,6 +8026,12 @@ void perf_output_sample(struct perf_output_handle *handle, - perf_output_sample_regs(handle, - data->regs_user.regs, - mask); -+ if (has_more_extended_user_regs(event)) { -+ perf_output_sample_regs_ext( -+ handle, data->regs_user.regs, -+ (unsigned long *)event->attr.sample_regs_user_ext, -+ PERF_NUM_EXT_REGS); -+ } - } - } - -@@ -8023,6 +8064,12 @@ void perf_output_sample(struct perf_output_handle *handle, - perf_output_sample_regs(handle, - data->regs_intr.regs, - mask); -+ if (has_more_extended_intr_regs(event)) { -+ perf_output_sample_regs_ext( -+ handle, data->regs_intr.regs, -+ (unsigned long *)event->attr.sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS); -+ } - } - } - -@@ -8277,6 +8324,12 @@ void perf_prepare_sample(struct perf_sample_data *data, - if (data->regs_user.regs) { - u64 mask = event->attr.sample_regs_user; - size += hweight64(mask) * sizeof(u64); -+ -+ if (has_more_extended_user_regs(event)) { -+ size += bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_user_ext, -+ PERF_NUM_EXT_REGS) * sizeof(u64); -+ } - } - - data->dyn_size += size; -@@ -8340,6 +8393,12 @@ void perf_prepare_sample(struct perf_sample_data *data, - u64 mask = event->attr.sample_regs_intr; - - size += hweight64(mask) * sizeof(u64); -+ -+ if (has_more_extended_intr_regs(event)) { -+ size += bitmap_weight( -+ (unsigned long *)event->attr.sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS) * sizeof(u64); -+ } - } - - data->dyn_size += size; -@@ -12598,6 +12657,12 @@ static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) - goto err_destroy; - } - -+ if (!(pmu->capabilities & PERF_PMU_CAP_MORE_EXT_REGS) && -+ has_more_extended_regs(event)) { -+ ret = -EOPNOTSUPP; -+ goto err_destroy; -+ } -+ - if (pmu->capabilities & PERF_PMU_CAP_NO_EXCLUDE && - event_has_any_exclude_flag(event)) { - ret = -EINVAL; -@@ -13130,9 +13195,19 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, - } - - if (attr->sample_type & PERF_SAMPLE_REGS_USER) { -- ret = perf_reg_validate(attr->sample_regs_user); -- if (ret) -- return ret; -+ if (attr->sample_regs_user != 0) { -+ ret = perf_reg_validate(attr->sample_regs_user); -+ if (ret) -+ return ret; -+ } -+ if (!!bitmap_weight((unsigned long *)attr->sample_regs_user_ext, -+ PERF_NUM_EXT_REGS)) { -+ ret = perf_reg_ext_validate( -+ (unsigned long *)attr->sample_regs_user_ext, -+ PERF_NUM_EXT_REGS); -+ if (ret) -+ return ret; -+ } - } - - if (attr->sample_type & PERF_SAMPLE_STACK_USER) { -@@ -13153,8 +13228,21 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, - if (!attr->sample_max_stack) - attr->sample_max_stack = sysctl_perf_event_max_stack; - -- if (attr->sample_type & PERF_SAMPLE_REGS_INTR) -- ret = perf_reg_validate(attr->sample_regs_intr); -+ if (attr->sample_type & PERF_SAMPLE_REGS_INTR) { -+ if (attr->sample_regs_intr != 0) { -+ ret = perf_reg_validate(attr->sample_regs_intr); -+ if (ret) -+ return ret; -+ } -+ if (!!bitmap_weight((unsigned long *)attr->sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS)) { -+ ret = perf_reg_ext_validate( -+ (unsigned long *)attr->sample_regs_intr_ext, -+ PERF_NUM_EXT_REGS); -+ if (ret) -+ return ret; -+ } -+ } - - #ifndef CONFIG_CGROUP_PERF - if (attr->sample_type & PERF_SAMPLE_CGROUP) --- -2.43.0 - diff --git a/SPECS/kernel/0022-x86-fred-Enable-FRED-by-default.nmi b/SPECS/kernel/0022-x86-fred-Enable-FRED-by-default.nmi deleted file mode 100644 index 9b93befbf..000000000 --- a/SPECS/kernel/0022-x86-fred-Enable-FRED-by-default.nmi +++ /dev/null @@ -1,31 +0,0 @@ -From 993fec078b97b8a3ebea1efbaa821d7f61f1a8d7 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Fri, 14 Feb 2025 10:31:18 -0800 -Subject: [PATCH 22/44] x86/fred: Enable FRED by default - -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - arch/x86/kernel/cpu/common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c -index cb14919f92da..96f28657072e 100644 ---- a/arch/x86/kernel/cpu/common.c -+++ b/arch/x86/kernel/cpu/common.c -@@ -1693,7 +1693,7 @@ static void __init cpu_parse_early_param(void) - - /* Minimize the gap between FRED is available and available but disabled. */ - arglen = cmdline_find_option(boot_command_line, "fred", arg, sizeof(arg)); -- if (arglen != 2 || strncmp(arg, "on", 2)) -+ if (arglen == 3 && !strncmp(arg, "off", 3)) - setup_clear_cpu_cap(X86_FEATURE_FRED); - - arglen = cmdline_find_option(boot_command_line, "clearcpuid", arg, sizeof(arg)); --- -2.43.0 - diff --git a/SPECS/kernel/0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet b/SPECS/kernel/0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet deleted file mode 100644 index 5439d6537..000000000 --- a/SPECS/kernel/0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet +++ /dev/null @@ -1,45 +0,0 @@ -From 825f0784fbb7768bf0c047882f3cf25b0c532717 Mon Sep 17 00:00:00 2001 -From: Chao Gao -Date: Fri, 4 Jul 2025 01:49:53 -0700 -Subject: [PATCH 23/24] KVM: nVMX: Add consistency checks for CR0.WP and - CR4.CET - -Add consistency checks for CR4.CET and CR0.WP in guest-state or host-state -area in the VMCS12. This ensures that configurations with CR4.CET set and -CR0.WP not set result in VM-entry failure, aligning with architectural -behavior. - -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 0f8dbf3cc6c3..bdaca6ada244 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3147,6 +3147,9 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - CC(!kvm_vcpu_is_legal_cr3(vcpu, vmcs12->host_cr3))) - return -EINVAL; - -+ if (CC(vmcs12->host_cr4 & X86_CR4_CET && !(vmcs12->host_cr0 & X86_CR0_WP))) -+ return -EINVAL; -+ - if (CC(is_noncanonical_msr_address(vmcs12->host_ia32_sysenter_esp, vcpu)) || - CC(is_noncanonical_msr_address(vmcs12->host_ia32_sysenter_eip, vcpu))) - return -EINVAL; -@@ -3261,6 +3264,9 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, - CC(!nested_guest_cr4_valid(vcpu, vmcs12->guest_cr4))) - return -EINVAL; - -+ if (CC(vmcs12->guest_cr4 & X86_CR4_CET && !(vmcs12->guest_cr0 & X86_CR0_WP))) -+ return -EINVAL; -+ - if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS) && - (CC(!kvm_dr7_valid(vmcs12->guest_dr7)) || - CC(!vmx_is_valid_debugctl(vcpu, vmcs12->guest_ia32_debugctl, false)))) --- -2.43.0 - diff --git a/SPECS/kernel/0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi b/SPECS/kernel/0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi new file mode 100644 index 000000000..270e29e79 --- /dev/null +++ b/SPECS/kernel/0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi @@ -0,0 +1,140 @@ +From e21ba60640193c49ee8eafd79f948fa519dccca9 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Fri, 27 Oct 2023 00:00:09 -0700 +Subject: [PATCH 23/44] KVM: selftests: Run debug_regs test with FRED enabled + +Run another round of debug_regs test with FRED enabled if FRED is +available. + +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + .../selftests/kvm/include/x86/processor.h | 4 ++ + tools/testing/selftests/kvm/x86/debug_regs.c | 50 ++++++++++++++----- + 2 files changed, 41 insertions(+), 13 deletions(-) + +diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h +index 51cd84b9ca664..1b07685533a1c 100644 +--- a/tools/testing/selftests/kvm/include/x86/processor.h ++++ b/tools/testing/selftests/kvm/include/x86/processor.h +@@ -59,6 +59,7 @@ const char *ex_str(int vector); + #define X86_CR4_SMEP (1ul << 20) + #define X86_CR4_SMAP (1ul << 21) + #define X86_CR4_PKE (1ul << 22) ++#define X86_CR4_FRED (1ul << 32) + + struct xstate_header { + u64 xstate_bv; +@@ -175,6 +176,9 @@ struct kvm_x86_cpu_feature { + #define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26) + #define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29) + #define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31) ++#define X86_FEATURE_FRED KVM_X86_CPU_FEATURE(0x7, 1, EAX, 17) ++#define X86_FEATURE_LKGS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 18) ++#define X86_FEATURE_WRMSRNS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 19) + #define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17) + #define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18) + #define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3) +diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c +index 2d814c1d1dc44..80cef67010ea2 100644 +--- a/tools/testing/selftests/kvm/x86/debug_regs.c ++++ b/tools/testing/selftests/kvm/x86/debug_regs.c +@@ -20,7 +20,7 @@ uint32_t guest_value; + + extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; + +-static void guest_code(void) ++static noinline void guest_test_code(void) + { + /* Create a pending interrupt on current vCPU */ + x2apic_enable(); +@@ -64,6 +64,15 @@ static void guest_code(void) + + /* DR6.BD test */ + asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); ++} ++ ++static void guest_code(void) ++{ ++ guest_test_code(); ++ ++ if (get_cr4() & X86_CR4_FRED) ++ guest_test_code(); ++ + GUEST_DONE(); + } + +@@ -78,19 +87,15 @@ static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) + vcpu_regs_set(vcpu, ®s); + } + +-int main(void) ++void run_test(struct kvm_vcpu *vcpu) + { + struct kvm_guest_debug debug; ++ struct kvm_run *run = vcpu->run; + unsigned long long target_dr6, target_rip; +- struct kvm_vcpu *vcpu; +- struct kvm_run *run; +- struct kvm_vm *vm; +- struct ucall uc; +- uint64_t cmd; + int i; + /* Instruction lengths starting at ss_start */ + int ss_size[6] = { +- 1, /* sti*/ ++ 1, /* sti */ + 2, /* xor */ + 2, /* cpuid */ + 5, /* mov */ +@@ -98,11 +103,6 @@ int main(void) + 1, /* cli */ + }; + +- TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); +- +- vm = vm_create_with_one_vcpu(&vcpu, guest_code); +- run = vcpu->run; +- + /* Test software BPs - int3 */ + memset(&debug, 0, sizeof(debug)); + debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; +@@ -205,6 +205,30 @@ int main(void) + /* Disable all debug controls, run to the end */ + memset(&debug, 0, sizeof(debug)); + vcpu_guest_debug_set(vcpu, &debug); ++} ++ ++int main(void) ++{ ++ struct kvm_vcpu *vcpu; ++ struct kvm_vm *vm; ++ struct ucall uc; ++ uint64_t cmd; ++ ++ TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); ++ ++ vm = vm_create_with_one_vcpu(&vcpu, guest_code); ++ ++ run_test(vcpu); ++ ++ if (kvm_cpu_has(X86_FEATURE_FRED)) { ++ struct kvm_sregs sregs; ++ ++ vcpu_sregs_get(vcpu, &sregs); ++ sregs.cr4 |= X86_CR4_FRED; ++ vcpu_sregs_set(vcpu, &sregs); ++ ++ run_test(vcpu); ++ } + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); +-- +2.43.0 + diff --git a/SPECS/kernel/0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf b/SPECS/kernel/0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf deleted file mode 100644 index be9b339cf..000000000 --- a/SPECS/kernel/0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf +++ /dev/null @@ -1,368 +0,0 @@ -From 448cd074e716108e97a5fea5027f7cac653a5ade Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 09:39:33 +0000 -Subject: [PATCH 023/100] perf/x86/intel: Support arch-PEBS vector registers - group capturing - -Add x86/intel specific vector register (VECR) group capturing for -arch-PEBS. Enable corresponding VECR group bits in -GPx_CFG_C/FX0_CFG_C MSRs if users configures these vector registers -bitmap in perf_event_attr and parse VECR group in arch-PEBS record. - -Currently vector registers capturing is only supported by PEBS based -sampling, PMU driver would return error if PMI based sampling tries to -capture these vector registers. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - arch/x86/events/core.c | 90 +++++++++++++++++++++++++++++- - arch/x86/events/intel/core.c | 15 +++++ - arch/x86/events/intel/ds.c | 93 ++++++++++++++++++++++++++++--- - arch/x86/include/asm/msr-index.h | 6 ++ - arch/x86/include/asm/perf_event.h | 20 +++++++ - 5 files changed, 214 insertions(+), 10 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index ad919f980389..56d197a56d59 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -581,6 +581,73 @@ int x86_pmu_max_precise(struct pmu *pmu) - return precise; - } - -+static bool has_vec_regs(struct perf_event *event, bool user, -+ int start, int end) -+{ -+ int idx = (start - PERF_REG_EXTENDED_OFFSET) / 64; -+ int s = start % 64; -+ int e = end % 64; -+ u64 regs_mask; -+ -+ if (user) -+ regs_mask = event->attr.sample_regs_user_ext[idx]; -+ else -+ regs_mask = event->attr.sample_regs_intr_ext[idx]; -+ -+ return regs_mask & GENMASK_ULL(e, s); -+} -+ -+static inline bool has_ymm_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_YMM0, PERF_REG_X86_YMM_MAX - 1); -+} -+ -+static inline bool has_zmm_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_ZMM0, PERF_REG_X86_ZMM8 - 1) || -+ has_vec_regs(event, user, PERF_REG_X86_ZMM8, PERF_REG_X86_ZMM16 - 1); -+} -+ -+static inline bool has_h16zmm_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_ZMM16, PERF_REG_X86_ZMM24 - 1) || -+ has_vec_regs(event, user, PERF_REG_X86_ZMM24, PERF_REG_X86_ZMM_MAX - 1); -+} -+ -+static inline bool has_opmask_regs(struct perf_event *event, bool user) -+{ -+ return has_vec_regs(event, user, PERF_REG_X86_OPMASK0, PERF_REG_X86_OPMASK7); -+} -+ -+static bool ext_vec_regs_supported(struct perf_event *event, bool user) -+{ -+ u64 caps = hybrid(event->pmu, arch_pebs_cap).caps; -+ -+ if (!(event->pmu->capabilities & PERF_PMU_CAP_MORE_EXT_REGS)) -+ return false; -+ -+ if (has_opmask_regs(event, user) && !(caps & ARCH_PEBS_VECR_OPMASK)) -+ return false; -+ -+ if (has_ymm_regs(event, user) && !(caps & ARCH_PEBS_VECR_YMMH)) -+ return false; -+ -+ if (has_zmm_regs(event, user) && !(caps & ARCH_PEBS_VECR_ZMMH)) -+ return false; -+ -+ if (has_h16zmm_regs(event, user) && !(caps & ARCH_PEBS_VECR_H16ZMM)) -+ return false; -+ -+ if (!event->attr.precise_ip) -+ return false; -+ -+ /* Only user space sampling is allowed for extended vector registers. */ -+ if (user && !event->attr.exclude_kernel) -+ return false; -+ -+ return true; -+} -+ - int x86_pmu_hw_config(struct perf_event *event) - { - if (event->attr.precise_ip) { -@@ -666,9 +733,12 @@ int x86_pmu_hw_config(struct perf_event *event) - return -EINVAL; - } - -- /* sample_regs_user never support XMM registers */ -- if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)) -- return -EINVAL; -+ if (unlikely(event->attr.sample_regs_user & PERF_REG_EXTENDED_MASK)) { -+ /* Only user space sampling is allowed for XMM registers. */ -+ if (!event->attr.exclude_kernel) -+ return -EINVAL; -+ } -+ - /* - * Besides the general purpose registers, XMM registers may - * be collected in PEBS on some platforms, e.g. Icelake -@@ -681,6 +751,20 @@ int x86_pmu_hw_config(struct perf_event *event) - return -EINVAL; - } - -+ /* -+ * Architectural PEBS supports to capture more vector registers besides -+ * XMM registers, like YMM, OPMASK and ZMM registers. -+ */ -+ if (unlikely(has_more_extended_user_regs(event))) { -+ if (!ext_vec_regs_supported(event, true)) -+ return -EINVAL; -+ } -+ -+ if (unlikely(has_more_extended_intr_regs(event))) { -+ if (!ext_vec_regs_supported(event, false)) -+ return -EINVAL; -+ } -+ - return x86_setup_perfctr(event); - } - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 007414c37ad1..0ed9cb66b49e 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3010,6 +3010,18 @@ static void intel_pmu_enable_event_ext(struct perf_event *event) - if (pebs_data_cfg & PEBS_DATACFG_XMMS) - ext |= ARCH_PEBS_VECR_XMM & cap.caps; - -+ if (pebs_data_cfg & PEBS_DATACFG_YMMHS) -+ ext |= ARCH_PEBS_VECR_YMMH & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_OPMASKS) -+ ext |= ARCH_PEBS_VECR_OPMASK & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_ZMMHS) -+ ext |= ARCH_PEBS_VECR_ZMMH & cap.caps; -+ -+ if (pebs_data_cfg & PEBS_DATACFG_H16ZMMS) -+ ext |= ARCH_PEBS_VECR_H16ZMM & cap.caps; -+ - if (pebs_data_cfg & PEBS_DATACFG_LBRS) - ext |= ARCH_PEBS_LBR & cap.caps; - -@@ -5573,6 +5585,9 @@ static inline void __intel_update_pmu_caps(struct pmu *pmu) - - if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_XMM) - dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS; -+ -+ if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_EXT) -+ dest_pmu->capabilities |= PERF_PMU_CAP_MORE_EXT_REGS; - } - - static inline void __intel_update_large_pebs_flags(struct pmu *pmu) -diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c -index 3a88f62ae127..5c4500d95174 100644 ---- a/arch/x86/events/intel/ds.c -+++ b/arch/x86/events/intel/ds.c -@@ -1426,6 +1426,34 @@ void intel_pmu_pebs_late_setup(struct cpu_hw_events *cpuc) - PERF_SAMPLE_TRANSACTION | \ - PERF_SAMPLE_DATA_PAGE_SIZE) - -+static u64 pebs_get_ext_reg_data_cfg(unsigned long *ext_reg) -+{ -+ u64 pebs_data_cfg = 0; -+ int bit; -+ -+ for_each_set_bit(bit, ext_reg, PERF_NUM_EXT_REGS) { -+ switch (bit + PERF_REG_EXTENDED_OFFSET) { -+ case PERF_REG_X86_OPMASK0 ... PERF_REG_X86_OPMASK7: -+ pebs_data_cfg |= PEBS_DATACFG_OPMASKS; -+ break; -+ case PERF_REG_X86_YMM0 ... PERF_REG_X86_YMM_MAX - 1: -+ pebs_data_cfg |= PEBS_DATACFG_YMMHS | PEBS_DATACFG_XMMS; -+ break; -+ case PERF_REG_X86_ZMM0 ... PERF_REG_X86_ZMM16 - 1: -+ pebs_data_cfg |= PEBS_DATACFG_ZMMHS | PEBS_DATACFG_YMMHS | -+ PEBS_DATACFG_XMMS; -+ break; -+ case PERF_REG_X86_ZMM16 ... PERF_REG_X86_ZMM_MAX - 1: -+ pebs_data_cfg |= PEBS_DATACFG_H16ZMMS; -+ break; -+ default: -+ break; -+ } -+ } -+ -+ return pebs_data_cfg; -+} -+ - static u64 pebs_update_adaptive_cfg(struct perf_event *event) - { - struct perf_event_attr *attr = &event->attr; -@@ -1460,9 +1488,21 @@ static u64 pebs_update_adaptive_cfg(struct perf_event *event) - if (gprs || (attr->precise_ip < 2) || tsx_weight) - pebs_data_cfg |= PEBS_DATACFG_GP; - -- if ((sample_type & PERF_SAMPLE_REGS_INTR) && -- (attr->sample_regs_intr & PERF_REG_EXTENDED_MASK)) -- pebs_data_cfg |= PEBS_DATACFG_XMMS; -+ if (sample_type & PERF_SAMPLE_REGS_INTR) { -+ if (attr->sample_regs_intr & PERF_REG_EXTENDED_MASK) -+ pebs_data_cfg |= PEBS_DATACFG_XMMS; -+ -+ pebs_data_cfg |= pebs_get_ext_reg_data_cfg( -+ (unsigned long *)event->attr.sample_regs_intr_ext); -+ } -+ -+ if (sample_type & PERF_SAMPLE_REGS_USER) { -+ if (attr->sample_regs_user & PERF_REG_EXTENDED_MASK) -+ pebs_data_cfg |= PEBS_DATACFG_XMMS; -+ -+ pebs_data_cfg |= pebs_get_ext_reg_data_cfg( -+ (unsigned long *)event->attr.sample_regs_user_ext); -+ } - - if (sample_type & PERF_SAMPLE_BRANCH_STACK) { - /* -@@ -2243,6 +2283,10 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ymmh_regs = NULL; -+ perf_regs->opmask_regs = NULL; -+ perf_regs->zmmh_regs = NULL; -+ perf_regs->h16zmm_regs = NULL; - perf_regs->ssp = 0; - - format_group = basic->format_group; -@@ -2360,6 +2404,10 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - - perf_regs = container_of(regs, struct x86_perf_regs, regs); - perf_regs->xmm_regs = NULL; -+ perf_regs->ymmh_regs = NULL; -+ perf_regs->opmask_regs = NULL; -+ perf_regs->zmmh_regs = NULL; -+ perf_regs->h16zmm_regs = NULL; - perf_regs->ssp = 0; - - __setup_perf_sample_data(event, iregs, data); -@@ -2410,14 +2458,45 @@ static void setup_arch_pebs_sample_data(struct perf_event *event, - meminfo->tsx_tuning, ax); - } - -- if (header->xmm) { -+ if (header->xmm || header->ymmh || header->opmask || -+ header->zmmh || header->h16zmm) { - struct arch_pebs_xmm *xmm; -+ struct arch_pebs_ymmh *ymmh; -+ struct arch_pebs_zmmh *zmmh; -+ struct arch_pebs_h16zmm *h16zmm; -+ struct arch_pebs_opmask *opmask; - - next_record += sizeof(struct arch_pebs_xer_header); - -- xmm = next_record; -- perf_regs->xmm_regs = xmm->xmm; -- next_record = xmm + 1; -+ if (header->xmm) { -+ xmm = next_record; -+ perf_regs->xmm_regs = xmm->xmm; -+ next_record = xmm + 1; -+ } -+ -+ if (header->ymmh) { -+ ymmh = next_record; -+ perf_regs->ymmh_regs = ymmh->ymmh; -+ next_record = ymmh + 1; -+ } -+ -+ if (header->opmask) { -+ opmask = next_record; -+ perf_regs->opmask_regs = opmask->opmask; -+ next_record = opmask + 1; -+ } -+ -+ if (header->zmmh) { -+ zmmh = next_record; -+ perf_regs->zmmh_regs = zmmh->zmmh; -+ next_record = zmmh + 1; -+ } -+ -+ if (header->h16zmm) { -+ h16zmm = next_record; -+ perf_regs->h16zmm_regs = h16zmm->h16zmm; -+ next_record = h16zmm + 1; -+ } - } - - if (header->lbr) { -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 41852e8690d7..84a1cbe6ab30 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -338,6 +338,12 @@ - #define ARCH_PEBS_LBR_SHIFT 40 - #define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT) - #define ARCH_PEBS_VECR_XMM BIT_ULL(49) -+#define ARCH_PEBS_VECR_YMMH BIT_ULL(50) -+#define ARCH_PEBS_VECR_OPMASK BIT_ULL(53) -+#define ARCH_PEBS_VECR_ZMMH BIT_ULL(54) -+#define ARCH_PEBS_VECR_H16ZMM BIT_ULL(55) -+#define ARCH_PEBS_VECR_EXT_SHIFT 50 -+#define ARCH_PEBS_VECR_EXT (0x3full << ARCH_PEBS_VECR_EXT_SHIFT) - #define ARCH_PEBS_GPR BIT_ULL(61) - #define ARCH_PEBS_AUX BIT_ULL(62) - #define ARCH_PEBS_EN BIT_ULL(63) -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index a4f7cfc3fddb..e723666c1de2 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -146,6 +146,10 @@ - #define PEBS_DATACFG_LBRS BIT_ULL(3) - #define PEBS_DATACFG_CNTR BIT_ULL(4) - #define PEBS_DATACFG_METRICS BIT_ULL(5) -+#define PEBS_DATACFG_YMMHS BIT_ULL(6) -+#define PEBS_DATACFG_OPMASKS BIT_ULL(7) -+#define PEBS_DATACFG_ZMMHS BIT_ULL(8) -+#define PEBS_DATACFG_H16ZMMS BIT_ULL(9) - #define PEBS_DATACFG_LBR_SHIFT 24 - #define PEBS_DATACFG_CNTR_SHIFT 32 - #define PEBS_DATACFG_CNTR_MASK GENMASK_ULL(15, 0) -@@ -593,6 +597,22 @@ struct arch_pebs_xmm { - u64 xmm[16*2]; /* two entries for each register */ - }; - -+struct arch_pebs_ymmh { -+ u64 ymmh[16*2]; /* two entries for each register */ -+}; -+ -+struct arch_pebs_opmask { -+ u64 opmask[8]; -+}; -+ -+struct arch_pebs_zmmh { -+ u64 zmmh[16*4]; /* four entries for each register */ -+}; -+ -+struct arch_pebs_h16zmm { -+ u64 h16zmm[16*8]; /* eight entries for each register */ -+}; -+ - #define ARCH_PEBS_LBR_NAN 0x0 - #define ARCH_PEBS_LBR_NUM_8 0x1 - #define ARCH_PEBS_LBR_NUM_16 0x2 --- -2.43.0 - diff --git a/SPECS/kernel/0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi b/SPECS/kernel/0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi deleted file mode 100644 index 9e7b527a4..000000000 --- a/SPECS/kernel/0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi +++ /dev/null @@ -1,31 +0,0 @@ -From 7589f16ab5ffcf24895f0e3d91e09f8d8d9750dd Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Fri, 18 Jul 2025 19:46:29 -0700 -Subject: [PATCH 23/44] x86/entry/fred: Simply push __KERNEL_CS - -Simply push __KERNEL_CS directly, rather than moving it into RAX and -then pushing RAX. - -Suggested-by: H. Peter Anvin (Intel) -Signed-off-by: Xin Li (Intel) ---- - arch/x86/entry/entry_64_fred.S | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S -index 29c5c32c16c3..cb5ff2b1f6e7 100644 ---- a/arch/x86/entry/entry_64_fred.S -+++ b/arch/x86/entry/entry_64_fred.S -@@ -97,8 +97,7 @@ SYM_FUNC_START(asm_fred_entry_from_kvm) - push %rdi /* fred_ss handed in by the caller */ - push %rbp - pushf -- mov $__KERNEL_CS, %rax -- push %rax -+ push $__KERNEL_CS - - /* - * Unlike the IDT event delivery, FRED _always_ pushes an error code --- -2.43.0 - diff --git a/SPECS/kernel/0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet b/SPECS/kernel/0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet deleted file mode 100644 index 1ea739e5c..000000000 --- a/SPECS/kernel/0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet +++ /dev/null @@ -1,117 +0,0 @@ -From cb08dfc62db2381f55172c749199bf2a24bfcc85 Mon Sep 17 00:00:00 2001 -From: Chao Gao -Date: Fri, 4 Jul 2025 01:49:54 -0700 -Subject: [PATCH 24/24] KVM: nVMX: Add consistency checks for CET states - -Introduce consistency checks for CET states during nested VM-entry. - -A VMCS contains both guest and host CET states, each comprising the -IA32_S_CET MSR, SSP, and IA32_INTERRUPT_SSP_TABLE_ADDR MSR. Various -checks are applied to CET states during VM-entry as documented in SDM -Vol3 Chapter "VM ENTRIES". Implement all these checks during nested -VM-entry to emulate the architectural behavior. - -In summary, there are three kinds of checks on guest/host CET states -during VM-entry: - -A. Checks applied to both guest states and host states: - - * The IA32_S_CET field must not set any reserved bits; bits 10 (SUPPRESS) - and 11 (TRACKER) cannot both be set. - * SSP should not have bits 1:0 set. - * The IA32_INTERRUPT_SSP_TABLE_ADDR field must be canonical. - -B. Checks applied to host states only - - * IA32_S_CET MSR and SSP must be canonical if the CPU enters 64-bit mode - after VM-exit. Otherwise, IA32_S_CET and SSP must have their higher 32 - bits cleared. - -C. Checks applied to guest states only: - - * IA32_S_CET MSR and SSP are not required to be canonical (i.e., 63:N-1 - are identical, where N is the CPU's maximum linear-address width). But, - bits 63:N of SSP must be identical. - -Tested-by: Mathias Krause -Tested-by: John Allen -Signed-off-by: Chao Gao ---- - arch/x86/kvm/vmx/nested.c | 47 +++++++++++++++++++++++++++++++++++++++ - 1 file changed, 47 insertions(+) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index bdaca6ada244..a6a3e0cbbe89 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3137,6 +3137,17 @@ static bool is_l1_noncanonical_address_on_vmexit(u64 la, struct vmcs12 *vmcs12) - return !__is_canonical_address(la, l1_address_bits_on_exit); - } - -+static bool is_valid_cet_state(struct kvm_vcpu *vcpu, u64 s_cet, u64 ssp, u64 ssp_tbl) -+{ -+ if (!is_cet_msr_valid(vcpu, s_cet) || !IS_ALIGNED(ssp, 4)) -+ return false; -+ -+ if (is_noncanonical_msr_address(ssp_tbl, vcpu)) -+ return false; -+ -+ return true; -+} -+ - static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - struct vmcs12 *vmcs12) - { -@@ -3206,6 +3217,26 @@ static int nested_vmx_check_host_state(struct kvm_vcpu *vcpu, - return -EINVAL; - } - -+ if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_CET_STATE) { -+ if (CC(!is_valid_cet_state(vcpu, vmcs12->host_s_cet, vmcs12->host_ssp, -+ vmcs12->host_ssp_tbl))) -+ return -EINVAL; -+ -+ /* -+ * IA32_S_CET and SSP must be canonical if the host will -+ * enter 64-bit mode after VM-exit; otherwise, higher -+ * 32-bits must be all 0s. -+ */ -+ if (ia32e) { -+ if (CC(is_noncanonical_msr_address(vmcs12->host_s_cet, vcpu)) || -+ CC(is_noncanonical_msr_address(vmcs12->host_ssp, vcpu))) -+ return -EINVAL; -+ } else { -+ if (CC(vmcs12->host_s_cet >> 32) || CC(vmcs12->host_ssp >> 32)) -+ return -EINVAL; -+ } -+ } -+ - return 0; - } - -@@ -3316,6 +3347,22 @@ static int nested_vmx_check_guest_state(struct kvm_vcpu *vcpu, - CC((vmcs12->guest_bndcfgs & MSR_IA32_BNDCFGS_RSVD)))) - return -EINVAL; - -+ if (vmcs12->vm_entry_controls & VM_ENTRY_LOAD_CET_STATE) { -+ if (CC(!is_valid_cet_state(vcpu, vmcs12->guest_s_cet, vmcs12->guest_ssp, -+ vmcs12->guest_ssp_tbl))) -+ return -EINVAL; -+ -+ /* -+ * Guest SSP must have 63:N bits identical, rather than -+ * be canonical (i.e., 63:N-1 bits identical), where N is -+ * the CPU's maximum linear-address width. Similar to -+ * is_noncanonical_msr_address(), use the host's -+ * linear-address width. -+ */ -+ if (CC(!__is_canonical_address(vmcs12->guest_ssp, max_host_virt_addr_bits() + 1))) -+ return -EINVAL; -+ } -+ - if (nested_check_guest_non_reg_state(vmcs12)) - return -EINVAL; - --- -2.43.0 - diff --git a/SPECS/kernel/0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi b/SPECS/kernel/0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi new file mode 100644 index 000000000..69b069a5c --- /dev/null +++ b/SPECS/kernel/0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi @@ -0,0 +1,161 @@ +From fedae904881c69f1c3d84867c5f2b7e2ee5f8e80 Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Mon, 6 Nov 2023 23:06:15 -0800 +Subject: [PATCH 24/44] KVM: selftests: Add a new VM guest mode to run user + level code + +Add a new VM guest mode VM_MODE_PXXV48_4K_USER to set the user bit of +guest page table entries, thus allow user level code to run in guests. + +Suggested-by: Sean Christopherson +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + .../testing/selftests/kvm/include/kvm_util.h | 1 + + tools/testing/selftests/kvm/lib/kvm_util.c | 5 +++- + .../testing/selftests/kvm/lib/x86/processor.c | 24 ++++++++++++++----- + tools/testing/selftests/kvm/lib/x86/vmx.c | 4 ++-- + 4 files changed, 25 insertions(+), 9 deletions(-) + +diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h +index d3f3e455c0310..9ab8968972eea 100644 +--- a/tools/testing/selftests/kvm/include/kvm_util.h ++++ b/tools/testing/selftests/kvm/include/kvm_util.h +@@ -185,6 +185,7 @@ enum vm_guest_mode { + VM_MODE_P36V48_64K, + VM_MODE_P47V47_16K, + VM_MODE_P36V47_16K, ++ VM_MODE_PXXV48_4K_USER, /* For 48bits VA but ANY bits PA with USER bit set */ + NUM_VM_MODES, + }; + +diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c +index 1a93d63616714..e64c23a9dad05 100644 +--- a/tools/testing/selftests/kvm/lib/kvm_util.c ++++ b/tools/testing/selftests/kvm/lib/kvm_util.c +@@ -209,6 +209,7 @@ const char *vm_guest_mode_string(uint32_t i) + [VM_MODE_P36V48_64K] = "PA-bits:36, VA-bits:48, 64K pages", + [VM_MODE_P47V47_16K] = "PA-bits:47, VA-bits:47, 16K pages", + [VM_MODE_P36V47_16K] = "PA-bits:36, VA-bits:47, 16K pages", ++ [VM_MODE_PXXV48_4K_USER] = "PA-bits:ANY, VA-bits:48, 4K user pages", + }; + _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, + "Missing new mode strings?"); +@@ -236,6 +237,7 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = { + [VM_MODE_P36V48_64K] = { 36, 48, 0x10000, 16 }, + [VM_MODE_P47V47_16K] = { 47, 47, 0x4000, 14 }, + [VM_MODE_P36V47_16K] = { 36, 47, 0x4000, 14 }, ++ [VM_MODE_PXXV48_4K_USER] = { 0, 0, 0x1000, 12 }, + }; + _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, + "Missing new mode params?"); +@@ -311,6 +313,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) + vm->pgtable_levels = 3; + break; + case VM_MODE_PXXV48_4K: ++ case VM_MODE_PXXV48_4K_USER: + #ifdef __x86_64__ + kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits); + kvm_init_vm_address_properties(vm); +@@ -327,7 +330,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) + vm->pgtable_levels = 4; + vm->va_bits = 48; + #else +- TEST_FAIL("VM_MODE_PXXV48_4K not supported on non-x86 platforms"); ++ TEST_FAIL("VM_MODE_PXXV48_4K(_USER) not supported on non-x86 platforms"); + #endif + break; + case VM_MODE_P47V64_4K: +diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c +index b418502c5ecc6..529997765fc63 100644 +--- a/tools/testing/selftests/kvm/lib/x86/processor.c ++++ b/tools/testing/selftests/kvm/lib/x86/processor.c +@@ -158,8 +158,8 @@ bool kvm_is_tdp_enabled(void) + + void virt_arch_pgd_alloc(struct kvm_vm *vm) + { +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " +- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), ++ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + /* If needed, create page map l4 table. */ + if (!vm->pgd_created) { +@@ -195,6 +195,8 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, + + if (!(*pte & PTE_PRESENT_MASK)) { + *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK; ++ if (vm->mode == VM_MODE_PXXV48_4K_USER) ++ *pte |= PTE_USER_MASK; + if (current_level == target_level) + *pte |= PTE_LARGE_MASK | (paddr & PHYSICAL_PAGE_MASK); + else +@@ -221,7 +223,7 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) + uint64_t *pml4e, *pdpe, *pde; + uint64_t *pte; + +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), + "Unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + TEST_ASSERT((vaddr % pg_size) == 0, +@@ -269,6 +271,9 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) + *pte |= vm->arch.c_bit; + else + *pte |= vm->arch.s_bit; ++ ++ if (vm->mode == VM_MODE_PXXV48_4K_USER) ++ *pte |= PTE_USER_MASK; + } + + void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +@@ -318,8 +323,8 @@ uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, + TEST_ASSERT(*level >= PG_LEVEL_NONE && *level < PG_LEVEL_NUM, + "Invalid PG_LEVEL_* '%d'", *level); + +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " +- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), ++ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); + TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, + (vaddr >> vm->page_shift)), + "Invalid virtual address, vaddr: 0x%lx", +@@ -526,7 +531,14 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu) + { + struct kvm_sregs sregs; + +- TEST_ASSERT_EQ(vm->mode, VM_MODE_PXXV48_4K); ++ switch (vm->mode) { ++ case VM_MODE_PXXV48_4K: ++ case VM_MODE_PXXV48_4K_USER: ++ break; ++ ++ default: ++ TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); ++ } + + /* Set mode specific system register values. */ + vcpu_sregs_get(vcpu, &sregs); +diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c +index d4d1208dd0238..4f7655b0830dd 100644 +--- a/tools/testing/selftests/kvm/lib/x86/vmx.c ++++ b/tools/testing/selftests/kvm/lib/x86/vmx.c +@@ -401,8 +401,8 @@ void __nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, + struct eptPageTableEntry *pt = vmx->eptp_hva, *pte; + uint16_t index; + +- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " +- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); ++ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), ++ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); + + TEST_ASSERT((nested_paddr >> 48) == 0, + "Nested physical address 0x%lx requires 5-level paging", +-- +2.43.0 + diff --git a/SPECS/kernel/0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi b/SPECS/kernel/0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi deleted file mode 100644 index 851b2ac42..000000000 --- a/SPECS/kernel/0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi +++ /dev/null @@ -1,140 +0,0 @@ -From a53bdf01705c28fe51beb4d2c1cfcc76d5dee00f Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Fri, 27 Oct 2023 00:00:09 -0700 -Subject: [PATCH 24/44] KVM: selftests: Run debug_regs test with FRED enabled - -Run another round of debug_regs test with FRED enabled if FRED is -available. - -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - .../selftests/kvm/include/x86/processor.h | 4 ++ - tools/testing/selftests/kvm/x86/debug_regs.c | 50 ++++++++++++++----- - 2 files changed, 41 insertions(+), 13 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h -index 232964f2a687..bcda4856180f 100644 ---- a/tools/testing/selftests/kvm/include/x86/processor.h -+++ b/tools/testing/selftests/kvm/include/x86/processor.h -@@ -57,6 +57,7 @@ extern uint64_t guest_tsc_khz; - #define X86_CR4_SMEP (1ul << 20) - #define X86_CR4_SMAP (1ul << 21) - #define X86_CR4_PKE (1ul << 22) -+#define X86_CR4_FRED (1ul << 32) - - struct xstate_header { - u64 xstate_bv; -@@ -173,6 +174,9 @@ struct kvm_x86_cpu_feature { - #define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26) - #define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29) - #define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31) -+#define X86_FEATURE_FRED KVM_X86_CPU_FEATURE(0x7, 1, EAX, 17) -+#define X86_FEATURE_LKGS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 18) -+#define X86_FEATURE_WRMSRNS KVM_X86_CPU_FEATURE(0x7, 1, EAX, 19) - #define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17) - #define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18) - #define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3) -diff --git a/tools/testing/selftests/kvm/x86/debug_regs.c b/tools/testing/selftests/kvm/x86/debug_regs.c -index 2d814c1d1dc4..80cef67010ea 100644 ---- a/tools/testing/selftests/kvm/x86/debug_regs.c -+++ b/tools/testing/selftests/kvm/x86/debug_regs.c -@@ -20,7 +20,7 @@ uint32_t guest_value; - - extern unsigned char sw_bp, hw_bp, write_data, ss_start, bd_start; - --static void guest_code(void) -+static noinline void guest_test_code(void) - { - /* Create a pending interrupt on current vCPU */ - x2apic_enable(); -@@ -64,6 +64,15 @@ static void guest_code(void) - - /* DR6.BD test */ - asm volatile("bd_start: mov %%dr0, %%rax" : : : "rax"); -+} -+ -+static void guest_code(void) -+{ -+ guest_test_code(); -+ -+ if (get_cr4() & X86_CR4_FRED) -+ guest_test_code(); -+ - GUEST_DONE(); - } - -@@ -78,19 +87,15 @@ static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) - vcpu_regs_set(vcpu, ®s); - } - --int main(void) -+void run_test(struct kvm_vcpu *vcpu) - { - struct kvm_guest_debug debug; -+ struct kvm_run *run = vcpu->run; - unsigned long long target_dr6, target_rip; -- struct kvm_vcpu *vcpu; -- struct kvm_run *run; -- struct kvm_vm *vm; -- struct ucall uc; -- uint64_t cmd; - int i; - /* Instruction lengths starting at ss_start */ - int ss_size[6] = { -- 1, /* sti*/ -+ 1, /* sti */ - 2, /* xor */ - 2, /* cpuid */ - 5, /* mov */ -@@ -98,11 +103,6 @@ int main(void) - 1, /* cli */ - }; - -- TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); -- -- vm = vm_create_with_one_vcpu(&vcpu, guest_code); -- run = vcpu->run; -- - /* Test software BPs - int3 */ - memset(&debug, 0, sizeof(debug)); - debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; -@@ -205,6 +205,30 @@ int main(void) - /* Disable all debug controls, run to the end */ - memset(&debug, 0, sizeof(debug)); - vcpu_guest_debug_set(vcpu, &debug); -+} -+ -+int main(void) -+{ -+ struct kvm_vcpu *vcpu; -+ struct kvm_vm *vm; -+ struct ucall uc; -+ uint64_t cmd; -+ -+ TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); -+ -+ vm = vm_create_with_one_vcpu(&vcpu, guest_code); -+ -+ run_test(vcpu); -+ -+ if (kvm_cpu_has(X86_FEATURE_FRED)) { -+ struct kvm_sregs sregs; -+ -+ vcpu_sregs_get(vcpu, &sregs); -+ sregs.cr4 |= X86_CR4_FRED; -+ vcpu_sregs_set(vcpu, &sregs); -+ -+ run_test(vcpu); -+ } - - vcpu_run(vcpu); - TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); --- -2.43.0 - diff --git a/SPECS/kernel/0024-gfs2-Prevent-recursive-memory-reclaim.patch b/SPECS/kernel/0024-gfs2-Prevent-recursive-memory-reclaim.patch deleted file mode 100644 index 573bbfe0e..000000000 --- a/SPECS/kernel/0024-gfs2-Prevent-recursive-memory-reclaim.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 625f186d2f968ecdbe3d5482f69aba6a07d388eb Mon Sep 17 00:00:00 2001 -From: Andreas Gruenbacher -Date: Thu, 13 Nov 2025 12:05:37 +0000 -Subject: [PATCH 24/51] gfs2: Prevent recursive memory reclaim - -Function new_inode() returns a new inode with inode->i_mapping->gfp_mask -set to GFP_HIGHUSER_MOVABLE. This value includes the __GFP_FS flag, so -allocations in that address space can recurse into filesystem memory -reclaim. We don't want that to happen because it can consume a -significant amount of stack memory. - -Worse than that is that it can also deadlock: for example, in several -places, gfs2_unstuff_dinode() is called inside filesystem transactions. -This calls filemap_grab_folio(), which can allocate a new folio, which -can trigger memory reclaim. If memory reclaim recurses into the -filesystem and starts another transaction, a deadlock will ensue. - -To fix these kinds of problems, prevent memory reclaim from recursing -into filesystem code by making sure that the gfp_mask of inode address -spaces doesn't include __GFP_FS. - -The "meta" and resource group address spaces were already using GFP_NOFS -as their gfp_mask (which doesn't include __GFP_FS). The default value -of GFP_HIGHUSER_MOVABLE is less restrictive than GFP_NOFS, though. To -avoid being overly limiting, use the default value and only knock off -the __GFP_FS flag. I'm not sure if this will actually make a -difference, but it also shouldn't hurt. - -This patch is loosely based on commit ad22c7a043c2 ("xfs: prevent stack -overflows from page cache allocation"). - -Fixes xfstest generic/273. - -Fixes: dc0b9435238c ("gfs: Don't use GFP_NOFS in gfs2_unstuff_dinode") -Reviewed-by: Andrew Price -Signed-off-by: Andreas Gruenbacher ---- - fs/gfs2/glock.c | 5 ++++- - fs/gfs2/inode.c | 15 +++++++++++++++ - fs/gfs2/inode.h | 1 + - fs/gfs2/ops_fstype.c | 2 +- - 4 files changed, 21 insertions(+), 2 deletions(-) - -diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c -index a6535413a0b4..d00cedf4460c 100644 ---- a/fs/gfs2/glock.c -+++ b/fs/gfs2/glock.c -@@ -1205,10 +1205,13 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, - - mapping = gfs2_glock2aspace(gl); - if (mapping) { -+ gfp_t gfp_mask; -+ - mapping->a_ops = &gfs2_meta_aops; - mapping->host = sdp->sd_inode; - mapping->flags = 0; -- mapping_set_gfp_mask(mapping, GFP_NOFS); -+ gfp_mask = mapping_gfp_mask(sdp->sd_inode->i_mapping); -+ mapping_set_gfp_mask(mapping, gfp_mask); - mapping->i_private_data = NULL; - mapping->writeback_index = 0; - } -diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c -index 8760e7e20c9d..35c1ccc1747f 100644 ---- a/fs/gfs2/inode.c -+++ b/fs/gfs2/inode.c -@@ -89,6 +89,19 @@ static int iget_set(struct inode *inode, void *opaque) - return 0; - } - -+void gfs2_setup_inode(struct inode *inode) -+{ -+ gfp_t gfp_mask; -+ -+ /* -+ * Ensure all page cache allocations are done from GFP_NOFS context to -+ * prevent direct reclaim recursion back into the filesystem and blowing -+ * stacks or deadlocking. -+ */ -+ gfp_mask = mapping_gfp_mask(inode->i_mapping); -+ mapping_set_gfp_mask(inode->i_mapping, gfp_mask & ~__GFP_FS); -+} -+ - /** - * gfs2_inode_lookup - Lookup an inode - * @sb: The super block -@@ -132,6 +145,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type, - struct gfs2_glock *io_gl; - int extra_flags = 0; - -+ gfs2_setup_inode(inode); - error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, - &ip->i_gl); - if (unlikely(error)) -@@ -752,6 +766,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry, - error = -ENOMEM; - if (!inode) - goto fail_gunlock; -+ gfs2_setup_inode(inode); - ip = GFS2_I(inode); - - error = posix_acl_create(dir, &mode, &default_acl, &acl); -diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h -index e43f08eb26e7..2fcd96dd1361 100644 ---- a/fs/gfs2/inode.h -+++ b/fs/gfs2/inode.h -@@ -86,6 +86,7 @@ static inline int gfs2_check_internal_file_size(struct inode *inode, - return -EIO; - } - -+void gfs2_setup_inode(struct inode *inode); - struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, - u64 no_addr, u64 no_formal_ino, - unsigned int blktype); -diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c -index efe99b732551..468ff57386dc 100644 ---- a/fs/gfs2/ops_fstype.c -+++ b/fs/gfs2/ops_fstype.c -@@ -1183,7 +1183,7 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) - - mapping = gfs2_aspace(sdp); - mapping->a_ops = &gfs2_rgrp_aops; -- mapping_set_gfp_mask(mapping, GFP_NOFS); -+ gfs2_setup_inode(sdp->sd_inode); - - error = init_names(sdp, silent); - if (error) --- -2.43.0 - diff --git a/SPECS/kernel/0024-perf-tools-Support-to-show-SSP-register.perf b/SPECS/kernel/0024-perf-tools-Support-to-show-SSP-register.perf deleted file mode 100644 index 4a5fc993a..000000000 --- a/SPECS/kernel/0024-perf-tools-Support-to-show-SSP-register.perf +++ /dev/null @@ -1,78 +0,0 @@ -From da2cc6441f6077d5e279ea137ee3b53562090746 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 25 Nov 2024 10:10:56 +0000 -Subject: [PATCH 024/100] perf tools: Support to show SSP register - -Add SSP register support. - -Reviewed-by: Ian Rogers -Signed-off-by: Dapeng Mi ---- - tools/arch/x86/include/uapi/asm/perf_regs.h | 7 ++++++- - tools/perf/arch/x86/util/perf_regs.c | 2 ++ - tools/perf/util/intel-pt.c | 2 +- - tools/perf/util/perf-regs-arch/perf_regs_x86.c | 2 ++ - 4 files changed, 11 insertions(+), 2 deletions(-) - -diff --git a/tools/arch/x86/include/uapi/asm/perf_regs.h b/tools/arch/x86/include/uapi/asm/perf_regs.h -index 7c9d2bb3833b..1c7ab5af5cc1 100644 ---- a/tools/arch/x86/include/uapi/asm/perf_regs.h -+++ b/tools/arch/x86/include/uapi/asm/perf_regs.h -@@ -27,9 +27,14 @@ enum perf_event_x86_regs { - PERF_REG_X86_R13, - PERF_REG_X86_R14, - PERF_REG_X86_R15, -+ /* arch-PEBS supports to capture shadow stack pointer (SSP) */ -+ PERF_REG_X86_SSP, - /* These are the limits for the GPRs. */ - PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1, -- PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1, -+ /* PERF_REG_X86_64_MAX used generally, for PEBS, etc. */ -+ PERF_REG_X86_64_MAX = PERF_REG_X86_SSP + 1, -+ /* PERF_REG_INTEL_PT_MAX ignores the SSP register. */ -+ PERF_REG_INTEL_PT_MAX = PERF_REG_X86_R15 + 1, - - /* These all need two bits set because they are 128bit */ - PERF_REG_X86_XMM0 = 32, -diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c -index 12fd93f04802..9f492568f3b4 100644 ---- a/tools/perf/arch/x86/util/perf_regs.c -+++ b/tools/perf/arch/x86/util/perf_regs.c -@@ -36,6 +36,8 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG(R14, PERF_REG_X86_R14), - SMPL_REG(R15, PERF_REG_X86_R15), - #endif -+ SMPL_REG(SSP, PERF_REG_X86_SSP), -+ - SMPL_REG2(XMM0, PERF_REG_X86_XMM0), - SMPL_REG2(XMM1, PERF_REG_X86_XMM1), - SMPL_REG2(XMM2, PERF_REG_X86_XMM2), -diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c -index 9b1011fe4826..a6b53718be7d 100644 ---- a/tools/perf/util/intel-pt.c -+++ b/tools/perf/util/intel-pt.c -@@ -2181,7 +2181,7 @@ static u64 *intel_pt_add_gp_regs(struct regs_dump *intr_regs, u64 *pos, - u32 bit; - int i; - -- for (i = 0, bit = 1; i < PERF_REG_X86_64_MAX; i++, bit <<= 1) { -+ for (i = 0, bit = 1; i < PERF_REG_INTEL_PT_MAX; i++, bit <<= 1) { - /* Get the PEBS gp_regs array index */ - int n = pebs_gp_regs[i] - 1; - -diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -index 708954a9d35d..c0e95215b577 100644 ---- a/tools/perf/util/perf-regs-arch/perf_regs_x86.c -+++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -@@ -54,6 +54,8 @@ const char *__perf_reg_name_x86(int id) - return "R14"; - case PERF_REG_X86_R15: - return "R15"; -+ case PERF_REG_X86_SSP: -+ return "SSP"; - - #define XMM(x) \ - case PERF_REG_X86_XMM ## x: \ --- -2.43.0 - diff --git a/SPECS/kernel/0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi b/SPECS/kernel/0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi deleted file mode 100644 index 607ec9dc1..000000000 --- a/SPECS/kernel/0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi +++ /dev/null @@ -1,161 +0,0 @@ -From 73d356ce29f00c03cc2008285fbfd6dbc6b0c0bf Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Mon, 6 Nov 2023 23:06:15 -0800 -Subject: [PATCH 25/44] KVM: selftests: Add a new VM guest mode to run user - level code - -Add a new VM guest mode VM_MODE_PXXV48_4K_USER to set the user bit of -guest page table entries, thus allow user level code to run in guests. - -Suggested-by: Sean Christopherson -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - .../testing/selftests/kvm/include/kvm_util.h | 1 + - tools/testing/selftests/kvm/lib/kvm_util.c | 5 +++- - .../testing/selftests/kvm/lib/x86/processor.c | 24 ++++++++++++++----- - tools/testing/selftests/kvm/lib/x86/vmx.c | 4 ++-- - 4 files changed, 25 insertions(+), 9 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h -index 23a506d7eca3..e5ea563fff94 100644 ---- a/tools/testing/selftests/kvm/include/kvm_util.h -+++ b/tools/testing/selftests/kvm/include/kvm_util.h -@@ -182,6 +182,7 @@ enum vm_guest_mode { - VM_MODE_P36V48_64K, - VM_MODE_P47V47_16K, - VM_MODE_P36V47_16K, -+ VM_MODE_PXXV48_4K_USER, /* For 48bits VA but ANY bits PA with USER bit set */ - NUM_VM_MODES, - }; - -diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c -index c3f5142b0a54..fc2bdf43ee23 100644 ---- a/tools/testing/selftests/kvm/lib/kvm_util.c -+++ b/tools/testing/selftests/kvm/lib/kvm_util.c -@@ -239,6 +239,7 @@ const char *vm_guest_mode_string(uint32_t i) - [VM_MODE_P36V48_64K] = "PA-bits:36, VA-bits:48, 64K pages", - [VM_MODE_P47V47_16K] = "PA-bits:47, VA-bits:47, 16K pages", - [VM_MODE_P36V47_16K] = "PA-bits:36, VA-bits:47, 16K pages", -+ [VM_MODE_PXXV48_4K_USER] = "PA-bits:ANY, VA-bits:48, 4K user pages", - }; - _Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES, - "Missing new mode strings?"); -@@ -266,6 +267,7 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = { - [VM_MODE_P36V48_64K] = { 36, 48, 0x10000, 16 }, - [VM_MODE_P47V47_16K] = { 47, 47, 0x4000, 14 }, - [VM_MODE_P36V47_16K] = { 36, 47, 0x4000, 14 }, -+ [VM_MODE_PXXV48_4K_USER] = { 0, 0, 0x1000, 12 }, - }; - _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, - "Missing new mode params?"); -@@ -341,6 +343,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) - vm->pgtable_levels = 3; - break; - case VM_MODE_PXXV48_4K: -+ case VM_MODE_PXXV48_4K_USER: - #ifdef __x86_64__ - kvm_get_cpu_address_width(&vm->pa_bits, &vm->va_bits); - kvm_init_vm_address_properties(vm); -@@ -357,7 +360,7 @@ struct kvm_vm *____vm_create(struct vm_shape shape) - vm->pgtable_levels = 4; - vm->va_bits = 48; - #else -- TEST_FAIL("VM_MODE_PXXV48_4K not supported on non-x86 platforms"); -+ TEST_FAIL("VM_MODE_PXXV48_4K(_USER) not supported on non-x86 platforms"); - #endif - break; - case VM_MODE_P47V64_4K: -diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c -index d4c19ac885a9..3e30732f4c3d 100644 ---- a/tools/testing/selftests/kvm/lib/x86/processor.c -+++ b/tools/testing/selftests/kvm/lib/x86/processor.c -@@ -124,8 +124,8 @@ bool kvm_is_tdp_enabled(void) - - void virt_arch_pgd_alloc(struct kvm_vm *vm) - { -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " -- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), -+ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); - - /* If needed, create page map l4 table. */ - if (!vm->pgd_created) { -@@ -161,6 +161,8 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm, - - if (!(*pte & PTE_PRESENT_MASK)) { - *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK; -+ if (vm->mode == VM_MODE_PXXV48_4K_USER) -+ *pte |= PTE_USER_MASK; - if (current_level == target_level) - *pte |= PTE_LARGE_MASK | (paddr & PHYSICAL_PAGE_MASK); - else -@@ -187,7 +189,7 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) - uint64_t *pml4e, *pdpe, *pde; - uint64_t *pte; - -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), - "Unknown or unsupported guest mode, mode: 0x%x", vm->mode); - - TEST_ASSERT((vaddr % pg_size) == 0, -@@ -235,6 +237,9 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) - *pte |= vm->arch.c_bit; - else - *pte |= vm->arch.s_bit; -+ -+ if (vm->mode == VM_MODE_PXXV48_4K_USER) -+ *pte |= PTE_USER_MASK; - } - - void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) -@@ -284,8 +289,8 @@ uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr, - TEST_ASSERT(*level >= PG_LEVEL_NONE && *level < PG_LEVEL_NUM, - "Invalid PG_LEVEL_* '%d'", *level); - -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " -- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), -+ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); - TEST_ASSERT(sparsebit_is_set(vm->vpages_valid, - (vaddr >> vm->page_shift)), - "Invalid virtual address, vaddr: 0x%lx", -@@ -492,7 +497,14 @@ static void vcpu_init_sregs(struct kvm_vm *vm, struct kvm_vcpu *vcpu) - { - struct kvm_sregs sregs; - -- TEST_ASSERT_EQ(vm->mode, VM_MODE_PXXV48_4K); -+ switch (vm->mode) { -+ case VM_MODE_PXXV48_4K: -+ case VM_MODE_PXXV48_4K_USER: -+ break; -+ -+ default: -+ TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode); -+ } - - /* Set mode specific system register values. */ - vcpu_sregs_get(vcpu, &sregs); -diff --git a/tools/testing/selftests/kvm/lib/x86/vmx.c b/tools/testing/selftests/kvm/lib/x86/vmx.c -index d4d1208dd023..4f7655b0830d 100644 ---- a/tools/testing/selftests/kvm/lib/x86/vmx.c -+++ b/tools/testing/selftests/kvm/lib/x86/vmx.c -@@ -401,8 +401,8 @@ void __nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, - struct eptPageTableEntry *pt = vmx->eptp_hva, *pte; - uint16_t index; - -- TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " -- "unknown or unsupported guest mode, mode: 0x%x", vm->mode); -+ TEST_ASSERT((vm->mode == VM_MODE_PXXV48_4K) || (vm->mode == VM_MODE_PXXV48_4K_USER), -+ "Attempt to use unknown or unsupported guest mode, mode: 0x%x", vm->mode); - - TEST_ASSERT((nested_paddr >> 48) == 0, - "Nested physical address 0x%lx requires 5-level paging", --- -2.43.0 - diff --git a/SPECS/kernel/0025-KVM-selftests-Add-fred-exception-tests.nmi b/SPECS/kernel/0025-KVM-selftests-Add-fred-exception-tests.nmi new file mode 100644 index 000000000..923aec8d9 --- /dev/null +++ b/SPECS/kernel/0025-KVM-selftests-Add-fred-exception-tests.nmi @@ -0,0 +1,391 @@ +From 81a70fda214064510d227cd886577e3f2e97079d Mon Sep 17 00:00:00 2001 +From: Xin Li +Date: Thu, 11 May 2023 08:17:30 +0000 +Subject: [PATCH 25/44] KVM: selftests: Add fred exception tests + +Add tests for FRED event data and VMX nested-exception. + +FRED is designed to save a complete event context in its stack frame, +e.g., FRED saves the faulting linear address of a #PF into a 64-bit +event data field defined in FRED stack frame. As such, FRED VMX adds +event data handling during VMX transitions. + +Besides, FRED introduces event stack levels to dispatch an event handler +onto a stack based on current stack level and stack levels defined in +IA32_FRED_STKLVLS MSR for each exception vector. VMX nested-exception +support ensures a correct event stack level is chosen when a VM entry +injects a nested exception, which is regarded as occurred in ring 0. + +To fully test the underlying FRED VMX code, this test should be run one +more round with EPT disabled to inject page faults as nested exceptions. + +Originally-by: Shan Kang +Signed-off-by: Xin Li +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + tools/testing/selftests/kvm/Makefile.kvm | 1 + + .../selftests/kvm/include/x86/processor.h | 32 ++ + tools/testing/selftests/kvm/x86/fred_test.c | 293 ++++++++++++++++++ + 3 files changed, 326 insertions(+) + create mode 100644 tools/testing/selftests/kvm/x86/fred_test.c + +diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm +index 148d427ff24be..5d84b8fcbaad4 100644 +--- a/tools/testing/selftests/kvm/Makefile.kvm ++++ b/tools/testing/selftests/kvm/Makefile.kvm +@@ -73,6 +73,7 @@ TEST_GEN_PROGS_x86 += x86/dirty_log_page_splitting_test + TEST_GEN_PROGS_x86 += x86/feature_msrs_test + TEST_GEN_PROGS_x86 += x86/exit_on_emulation_failure_test + TEST_GEN_PROGS_x86 += x86/fastops_test ++TEST_GEN_PROGS_x86 += x86/fred_test + TEST_GEN_PROGS_x86 += x86/fix_hypercall_test + TEST_GEN_PROGS_x86 += x86/hwcr_msr_test + TEST_GEN_PROGS_x86 += x86/hyperv_clock +diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h +index 1b07685533a1c..7f2b971a7a9f2 100644 +--- a/tools/testing/selftests/kvm/include/x86/processor.h ++++ b/tools/testing/selftests/kvm/include/x86/processor.h +@@ -1498,4 +1498,36 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, + + bool sys_clocksource_is_based_on_tsc(void); + ++/* ++ * FRED related data structures and functions ++ */ ++ ++#define FRED_SSX_NMI BIT_ULL(18) ++ ++struct fred_stack { ++ u64 r15; ++ u64 r14; ++ u64 r13; ++ u64 r12; ++ u64 bp; ++ u64 bx; ++ u64 r11; ++ u64 r10; ++ u64 r9; ++ u64 r8; ++ u64 ax; ++ u64 cx; ++ u64 dx; ++ u64 si; ++ u64 di; ++ u64 error_code; ++ u64 ip; ++ u64 csx; ++ u64 flags; ++ u64 sp; ++ u64 ssx; ++ u64 event_data; ++ u64 reserved; ++}; ++ + #endif /* SELFTEST_KVM_PROCESSOR_H */ +diff --git a/tools/testing/selftests/kvm/x86/fred_test.c b/tools/testing/selftests/kvm/x86/fred_test.c +new file mode 100644 +index 0000000000000..3d0703f6eba7c +--- /dev/null ++++ b/tools/testing/selftests/kvm/x86/fred_test.c +@@ -0,0 +1,293 @@ ++// SPDX-License-Identifier: GPL-2.0-only ++/* ++ * FRED nested exception tests ++ * ++ * Copyright (C) 2023, Intel, Inc. ++ */ ++#define _GNU_SOURCE /* for program_invocation_short_name */ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "apic.h" ++#include "kvm_util.h" ++#include "test_util.h" ++#include "guest_modes.h" ++#include "processor.h" ++ ++#define IRQ_VECTOR 0xAA ++ ++#define FRED_STKLVL(v, l) (_AT(unsigned long, l) << (2 * (v))) ++#define FRED_CONFIG_ENTRYPOINT(p) _AT(unsigned long, (p)) ++ ++/* This address is already mapped in guest page table. */ ++#define FRED_VALID_RSP 0x8000 ++ ++/* ++ * The following addresses are not yet mapped in both EPT and guest page ++ * tables at the beginning. As a result, it causes an EPT violation VM ++ * exit with an original guest #PF to access any of them for the first ++ * time. ++ * ++ * Use these addresses as guest FRED RSP0 to generate nested #PFs to test ++ * if event data are properly virtualized. ++ */ ++static unsigned long fred_invalid_rsp[4] = { ++ 0x0, ++ 0xf0000000, ++ 0xe0000000, ++ 0xd0000000, ++}; ++ ++extern char asm_user_nop[]; ++extern char asm_user_ud[]; ++extern char asm_done_fault[]; ++ ++extern void asm_test_fault(int test); ++ ++/* ++ * user level code for triggering faults. ++ */ ++asm(".pushsection .text\n" ++ ".align 4096\n" ++ ++ ".type asm_user_nop, @function\n" ++ "asm_user_nop:\n" ++ "1: .byte 0x90\n" ++ "jmp 1b\n" ++ ++ ".org asm_user_nop + 16, 0xcc\n" ++ ".type asm_user_ud, @function\n" ++ "asm_user_ud:\n" ++ /* Trigger a #UD */ ++ "ud2\n" ++ ++ ".align 4096, 0xcc\n" ++ ".popsection"); ++ ++/* Send current stack level and #PF address */ ++#define GUEST_SYNC_CSL_FA(__stage, __pf_address) \ ++ GUEST_SYNC_ARGS(__stage, __pf_address, 0, 0, 0) ++ ++void fred_entry_from_user(struct fred_stack *stack) ++{ ++ u32 current_stack_level = rdmsr(MSR_IA32_FRED_CONFIG) & 0x3; ++ ++ GUEST_SYNC_CSL_FA(current_stack_level, stack->event_data); ++ ++ /* Do NOT go back to user level, continue the next test instead */ ++ stack->ssx = 0x18; ++ stack->csx = 0x10; ++ stack->ip = (u64)&asm_done_fault; ++} ++ ++void fred_entry_from_kernel(struct fred_stack *stack) ++{ ++ /* ++ * Keep NMI blocked to delay the delivery of the next NMI until ++ * returning to user level. ++ * */ ++ stack->ssx &= ~FRED_SSX_NMI; ++} ++ ++#define PUSH_REGS \ ++ "push %rdi\n" \ ++ "push %rsi\n" \ ++ "push %rdx\n" \ ++ "push %rcx\n" \ ++ "push %rax\n" \ ++ "push %r8\n" \ ++ "push %r9\n" \ ++ "push %r10\n" \ ++ "push %r11\n" \ ++ "push %rbx\n" \ ++ "push %rbp\n" \ ++ "push %r12\n" \ ++ "push %r13\n" \ ++ "push %r14\n" \ ++ "push %r15\n" ++ ++#define POP_REGS \ ++ "pop %r15\n" \ ++ "pop %r14\n" \ ++ "pop %r13\n" \ ++ "pop %r12\n" \ ++ "pop %rbp\n" \ ++ "pop %rbx\n" \ ++ "pop %r11\n" \ ++ "pop %r10\n" \ ++ "pop %r9\n" \ ++ "pop %r8\n" \ ++ "pop %rax\n" \ ++ "pop %rcx\n" \ ++ "pop %rdx\n" \ ++ "pop %rsi\n" \ ++ "pop %rdi\n" ++ ++/* ++ * FRED entry points. ++ */ ++asm(".pushsection .text\n" ++ ".type asm_fred_entrypoint_user, @function\n" ++ ".align 4096\n" ++ "asm_fred_entrypoint_user:\n" ++ "endbr64\n" ++ PUSH_REGS ++ "movq %rsp, %rdi\n" ++ "call fred_entry_from_user\n" ++ POP_REGS ++ /* Do NOT go back to user level, continue the next test instead */ ++ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ ++ ++ ".org asm_fred_entrypoint_user + 256, 0xcc\n" ++ ".type asm_fred_entrypoint_kernel, @function\n" ++ "asm_fred_entrypoint_kernel:\n" ++ "endbr64\n" ++ PUSH_REGS ++ "movq %rsp, %rdi\n" ++ "call fred_entry_from_kernel\n" ++ POP_REGS ++ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ ++ ".align 4096, 0xcc\n" ++ ".popsection"); ++ ++extern char asm_fred_entrypoint_user[]; ++ ++/* ++ * Prepare a FRED stack frame for ERETU to return to user level code, ++ * nop or ud2. ++ * ++ * Because FRED RSP0 is deliberately not mapped in guest page table, ++ * the delivery of interrupt/NMI or #UD from ring 3 causes a nested ++ * #PF, which is then delivered on FRED RSPx (x is 1, 2 or 3, ++ * determinated by MSR FRED_STKLVL[PF_VECTOR]). ++ */ ++asm(".pushsection .text\n" ++ ".type asm_test_fault, @function\n" ++ ".align 4096\n" ++ "asm_test_fault:\n" ++ "endbr64\n" ++ "push %rbp\n" ++ "mov %rsp, %rbp\n" ++ "and $(~0x3f), %rsp\n" ++ "push $0\n" ++ "push $0\n" ++ "mov $0x2b, %rax\n" ++ /* Unblock NMI */ ++ "bts $18, %rax\n" ++ /* Set long mode bit */ ++ "bts $57, %rax\n" ++ "push %rax\n" ++ /* No stack required for the FRED user level test code */ ++ "push $0\n" ++ "pushf\n" ++ "pop %rax\n" ++ /* Allow external interrupts */ ++ "bts $9, %rax\n" ++ "push %rax\n" ++ "mov $0x33, %rax\n" ++ "push %rax\n" ++ "cmp $0, %edi\n" ++ "jne 1f\n" ++ "lea asm_user_nop(%rip), %rax\n" ++ "jmp 2f\n" ++ "1: lea asm_user_ud(%rip), %rax\n" ++ "2: push %rax\n" ++ "push $0\n" ++ /* ERETU to user level code to allow event delivery immediately */ ++ ".byte 0xf3,0x0f,0x01,0xca\n" ++ "asm_done_fault:\n" ++ "mov %rbp, %rsp\n" ++ "pop %rbp\n" ++ "ret\n" ++ ".align 4096, 0xcc\n" ++ ".popsection"); ++ ++/* ++ * To fully test the underlying FRED VMX code, this test should be run one ++ * more round with EPT disabled to inject page faults as nested exceptions. ++ */ ++static void guest_code(void) ++{ ++ wrmsr(MSR_IA32_FRED_CONFIG, ++ FRED_CONFIG_ENTRYPOINT(asm_fred_entrypoint_user)); ++ ++ wrmsr(MSR_IA32_FRED_RSP1, FRED_VALID_RSP); ++ wrmsr(MSR_IA32_FRED_RSP2, FRED_VALID_RSP); ++ wrmsr(MSR_IA32_FRED_RSP3, FRED_VALID_RSP); ++ ++ /* Enable FRED */ ++ set_cr4(get_cr4() | X86_CR4_FRED); ++ ++ x2apic_enable(); ++ ++ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 1)); ++ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[1]); ++ /* 1: ud2 to generate #UD */ ++ asm_test_fault(1); ++ ++ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 2)); ++ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[2]); ++ asm volatile("cli"); ++ /* Create a pending interrupt on current vCPU */ ++ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | ++ APIC_DM_FIXED | IRQ_VECTOR); ++ /* Return to ring 3 */ ++ asm_test_fault(0); ++ x2apic_write_reg(APIC_EOI, 0); ++ ++ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 3)); ++ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[3]); ++ /* ++ * The first NMI is just to have NMI blocked in ring 0, because ++ * fred_entry_from_kernel() deliberately clears the NMI bit in ++ * FRED stack frame. ++ */ ++ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | ++ APIC_DM_NMI | NMI_VECTOR); ++ /* The second NMI will be delivered after returning to ring 3 */ ++ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | ++ APIC_DM_NMI | NMI_VECTOR); ++ /* Return to ring 3 */ ++ asm_test_fault(0); ++ ++ GUEST_DONE(); ++} ++ ++int main(int argc, char *argv[]) ++{ ++ struct kvm_vcpu *vcpu; ++ struct kvm_vm *vm; ++ struct ucall uc; ++ uint64_t expected_current_stack_level = 1; ++ ++ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_FRED)); ++ ++ vm = __vm_create_with_vcpus(VM_SHAPE(VM_MODE_PXXV48_4K_USER), 1, 0, ++ guest_code, &vcpu); ++ ++ while (true) { ++ uint64_t r; ++ ++ vcpu_run(vcpu); ++ ++ r = get_ucall(vcpu, &uc); ++ ++ if (r == UCALL_DONE) ++ break; ++ ++ if (r == UCALL_SYNC) { ++ TEST_ASSERT((uc.args[1] == expected_current_stack_level) && ++ (uc.args[2] == fred_invalid_rsp[expected_current_stack_level] - 8), ++ "Incorrect stack level %lx and #PF address %lx\n", ++ uc.args[1], uc.args[2]); ++ expected_current_stack_level++; ++ } ++ } ++ ++ kvm_vm_free(vm); ++ return 0; ++} +-- +2.43.0 + diff --git a/SPECS/kernel/0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf b/SPECS/kernel/0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf deleted file mode 100644 index 030b85e23..000000000 --- a/SPECS/kernel/0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf +++ /dev/null @@ -1,390 +0,0 @@ -From 843251f6ff33766dde028d65f727675e035a8ee1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 17 Feb 2025 15:47:06 +0000 -Subject: [PATCH 025/100] perf tools: Enhance arch__intr/user_reg_mask() - helpers - -Arch-PEBS supports to capture more higher-width vector registers, like -YMM/ZMM registers, while the return value "uint64_t" of these 2 helpers -is not enough to represent these new added registors. Thus enhance these -two helpers by passing a "unsigned long" pointer, so these two helpers -can return more bits via this pointer. - -Currently only sample_intr_regs supports these new added vector -registers, but change arch__user_reg_mask() for the sake of consistency -as well. - -Signed-off-by: Dapeng Mi ---- - tools/perf/arch/arm/util/perf_regs.c | 8 ++++---- - tools/perf/arch/arm64/util/perf_regs.c | 11 ++++++----- - tools/perf/arch/csky/util/perf_regs.c | 8 ++++---- - tools/perf/arch/loongarch/util/perf_regs.c | 8 ++++---- - tools/perf/arch/mips/util/perf_regs.c | 8 ++++---- - tools/perf/arch/powerpc/util/perf_regs.c | 17 +++++++++-------- - tools/perf/arch/riscv/util/perf_regs.c | 8 ++++---- - tools/perf/arch/s390/util/perf_regs.c | 8 ++++---- - tools/perf/arch/x86/util/perf_regs.c | 13 +++++++------ - tools/perf/util/evsel.c | 6 ++++-- - tools/perf/util/parse-regs-options.c | 6 +++--- - tools/perf/util/perf_regs.c | 8 ++++---- - tools/perf/util/perf_regs.h | 4 ++-- - 13 files changed, 59 insertions(+), 54 deletions(-) - -diff --git a/tools/perf/arch/arm/util/perf_regs.c b/tools/perf/arch/arm/util/perf_regs.c -index f94a0210c7b7..14f18d518c96 100644 ---- a/tools/perf/arch/arm/util/perf_regs.c -+++ b/tools/perf/arch/arm/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/arm64/util/perf_regs.c b/tools/perf/arch/arm64/util/perf_regs.c -index 09308665e28a..9bcf4755290c 100644 ---- a/tools/perf/arch/arm64/util/perf_regs.c -+++ b/tools/perf/arch/arm64/util/perf_regs.c -@@ -140,12 +140,12 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op) - return SDT_ARG_VALID; - } - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, -@@ -170,10 +170,11 @@ uint64_t arch__user_reg_mask(void) - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); - if (fd != -1) { - close(fd); -- return attr.sample_regs_user; -+ *(uint64_t *)mask = attr.sample_regs_user; -+ return; - } - } -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/csky/util/perf_regs.c b/tools/perf/arch/csky/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/csky/util/perf_regs.c -+++ b/tools/perf/arch/csky/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/loongarch/util/perf_regs.c b/tools/perf/arch/loongarch/util/perf_regs.c -index f94a0210c7b7..14f18d518c96 100644 ---- a/tools/perf/arch/loongarch/util/perf_regs.c -+++ b/tools/perf/arch/loongarch/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/mips/util/perf_regs.c b/tools/perf/arch/mips/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/mips/util/perf_regs.c -+++ b/tools/perf/arch/mips/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/powerpc/util/perf_regs.c b/tools/perf/arch/powerpc/util/perf_regs.c -index bd36cfd420a2..e5d042305030 100644 ---- a/tools/perf/arch/powerpc/util/perf_regs.c -+++ b/tools/perf/arch/powerpc/util/perf_regs.c -@@ -187,7 +187,7 @@ int arch_sdt_arg_parse_op(char *old_op, char **new_op) - return SDT_ARG_VALID; - } - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, -@@ -199,7 +199,7 @@ uint64_t arch__intr_reg_mask(void) - }; - int fd; - u32 version; -- u64 extended_mask = 0, mask = PERF_REGS_MASK; -+ u64 extended_mask = 0; - - /* - * Get the PVR value to set the extended -@@ -210,8 +210,10 @@ uint64_t arch__intr_reg_mask(void) - extended_mask = PERF_REG_PMU_MASK_300; - else if ((version == PVR_POWER10) || (version == PVR_POWER11)) - extended_mask = PERF_REG_PMU_MASK_31; -- else -- return mask; -+ else { -+ *(u64 *)mask = PERF_REGS_MASK; -+ return; -+ } - - attr.sample_regs_intr = extended_mask; - attr.sample_period = 1; -@@ -224,14 +226,13 @@ uint64_t arch__intr_reg_mask(void) - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); - if (fd != -1) { - close(fd); -- mask |= extended_mask; -+ *(u64 *)mask = PERF_REGS_MASK | extended_mask; - } -- return mask; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/riscv/util/perf_regs.c b/tools/perf/arch/riscv/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/riscv/util/perf_regs.c -+++ b/tools/perf/arch/riscv/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/s390/util/perf_regs.c b/tools/perf/arch/s390/util/perf_regs.c -index 6b1665f41180..56c84fc91aff 100644 ---- a/tools/perf/arch/s390/util/perf_regs.c -+++ b/tools/perf/arch/s390/util/perf_regs.c -@@ -6,14 +6,14 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG_END - }; - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } - - const struct sample_reg *arch__sample_reg_masks(void) -diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c -index 9f492568f3b4..5b163f0a651a 100644 ---- a/tools/perf/arch/x86/util/perf_regs.c -+++ b/tools/perf/arch/x86/util/perf_regs.c -@@ -283,7 +283,7 @@ const struct sample_reg *arch__sample_reg_masks(void) - return sample_reg_masks; - } - --uint64_t arch__intr_reg_mask(void) -+void arch__intr_reg_mask(unsigned long *mask) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, -@@ -295,6 +295,9 @@ uint64_t arch__intr_reg_mask(void) - .exclude_kernel = 1, - }; - int fd; -+ -+ *(u64 *)mask = PERF_REGS_MASK; -+ - /* - * In an unnamed union, init it here to build on older gcc versions - */ -@@ -320,13 +323,11 @@ uint64_t arch__intr_reg_mask(void) - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); - if (fd != -1) { - close(fd); -- return (PERF_REG_EXTENDED_MASK | PERF_REGS_MASK); -+ *(u64 *)mask = PERF_REG_EXTENDED_MASK | PERF_REGS_MASK; - } -- -- return PERF_REGS_MASK; - } - --uint64_t arch__user_reg_mask(void) -+void arch__user_reg_mask(unsigned long *mask) - { -- return PERF_REGS_MASK; -+ *(uint64_t *)mask = PERF_REGS_MASK; - } -diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c -index d264c143b592..f9e79b86b794 100644 ---- a/tools/perf/util/evsel.c -+++ b/tools/perf/util/evsel.c -@@ -1041,17 +1041,19 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o - if (param->record_mode == CALLCHAIN_DWARF) { - if (!function) { - const char *arch = perf_env__arch(evsel__env(evsel)); -+ uint64_t mask = 0; - -+ arch__user_reg_mask((unsigned long *)&mask); - evsel__set_sample_bit(evsel, REGS_USER); - evsel__set_sample_bit(evsel, STACK_USER); - if (opts->sample_user_regs && -- DWARF_MINIMAL_REGS(arch) != arch__user_reg_mask()) { -+ DWARF_MINIMAL_REGS(arch) != mask) { - attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch); - pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, " - "specifying a subset with --user-regs may render DWARF unwinding unreliable, " - "so the minimal registers set (IP, SP) is explicitly forced.\n"); - } else { -- attr->sample_regs_user |= arch__user_reg_mask(); -+ attr->sample_regs_user |= mask; - } - attr->sample_stack_user = param->dump_size; - attr->exclude_callchain_user = 1; -diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c -index cda1c620968e..3dcd8dc4f81b 100644 ---- a/tools/perf/util/parse-regs-options.c -+++ b/tools/perf/util/parse-regs-options.c -@@ -16,7 +16,7 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - const struct sample_reg *r = NULL; - char *s, *os = NULL, *p; - int ret = -1; -- uint64_t mask; -+ uint64_t mask = 0; - - if (unset) - return 0; -@@ -28,9 +28,9 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - return -1; - - if (intr) -- mask = arch__intr_reg_mask(); -+ arch__intr_reg_mask((unsigned long *)&mask); - else -- mask = arch__user_reg_mask(); -+ arch__user_reg_mask((unsigned long *)&mask); - - /* str may be NULL in case no arg is passed to -I */ - if (str) { -diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c -index 44b90bbf2d07..7a96290fd1e6 100644 ---- a/tools/perf/util/perf_regs.c -+++ b/tools/perf/util/perf_regs.c -@@ -11,14 +11,14 @@ int __weak arch_sdt_arg_parse_op(char *old_op __maybe_unused, - return SDT_ARG_SKIP; - } - --uint64_t __weak arch__intr_reg_mask(void) -+void __weak arch__intr_reg_mask(unsigned long *mask) - { -- return 0; -+ *(uint64_t *)mask = 0; - } - --uint64_t __weak arch__user_reg_mask(void) -+void __weak arch__user_reg_mask(unsigned long *mask) - { -- return 0; -+ *(uint64_t *)mask = 0; - } - - static const struct sample_reg sample_reg_masks[] = { -diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h -index f2d0736d65cc..316d280e5cd7 100644 ---- a/tools/perf/util/perf_regs.h -+++ b/tools/perf/util/perf_regs.h -@@ -24,8 +24,8 @@ enum { - }; - - int arch_sdt_arg_parse_op(char *old_op, char **new_op); --uint64_t arch__intr_reg_mask(void); --uint64_t arch__user_reg_mask(void); -+void arch__intr_reg_mask(unsigned long *mask); -+void arch__user_reg_mask(unsigned long *mask); - const struct sample_reg *arch__sample_reg_masks(void); - - const char *perf_reg_name(int id, const char *arch); --- -2.43.0 - diff --git a/SPECS/kernel/0026-KVM-selftests-Add-fred-exception-tests.nmi b/SPECS/kernel/0026-KVM-selftests-Add-fred-exception-tests.nmi deleted file mode 100644 index 18eddb17b..000000000 --- a/SPECS/kernel/0026-KVM-selftests-Add-fred-exception-tests.nmi +++ /dev/null @@ -1,391 +0,0 @@ -From bae362a9e3d087209ade9bae2dc874ae58f3cee7 Mon Sep 17 00:00:00 2001 -From: Xin Li -Date: Thu, 11 May 2023 08:17:30 +0000 -Subject: [PATCH 26/44] KVM: selftests: Add fred exception tests - -Add tests for FRED event data and VMX nested-exception. - -FRED is designed to save a complete event context in its stack frame, -e.g., FRED saves the faulting linear address of a #PF into a 64-bit -event data field defined in FRED stack frame. As such, FRED VMX adds -event data handling during VMX transitions. - -Besides, FRED introduces event stack levels to dispatch an event handler -onto a stack based on current stack level and stack levels defined in -IA32_FRED_STKLVLS MSR for each exception vector. VMX nested-exception -support ensures a correct event stack level is chosen when a VM entry -injects a nested exception, which is regarded as occurred in ring 0. - -To fully test the underlying FRED VMX code, this test should be run one -more round with EPT disabled to inject page faults as nested exceptions. - -Originally-by: Shan Kang -Signed-off-by: Xin Li -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - tools/testing/selftests/kvm/Makefile.kvm | 1 + - .../selftests/kvm/include/x86/processor.h | 32 ++ - tools/testing/selftests/kvm/x86/fred_test.c | 293 ++++++++++++++++++ - 3 files changed, 326 insertions(+) - create mode 100644 tools/testing/selftests/kvm/x86/fred_test.c - -diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm -index f6fe7a07a0a2..c6dacd69d11c 100644 ---- a/tools/testing/selftests/kvm/Makefile.kvm -+++ b/tools/testing/selftests/kvm/Makefile.kvm -@@ -73,6 +73,7 @@ TEST_GEN_PROGS_x86 += x86/dirty_log_page_splitting_test - TEST_GEN_PROGS_x86 += x86/feature_msrs_test - TEST_GEN_PROGS_x86 += x86/exit_on_emulation_failure_test - TEST_GEN_PROGS_x86 += x86/fastops_test -+TEST_GEN_PROGS_x86 += x86/fred_test - TEST_GEN_PROGS_x86 += x86/fix_hypercall_test - TEST_GEN_PROGS_x86 += x86/hwcr_msr_test - TEST_GEN_PROGS_x86 += x86/hyperv_clock -diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h -index bcda4856180f..f411b7422aa5 100644 ---- a/tools/testing/selftests/kvm/include/x86/processor.h -+++ b/tools/testing/selftests/kvm/include/x86/processor.h -@@ -1465,4 +1465,36 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, - - bool sys_clocksource_is_based_on_tsc(void); - -+/* -+ * FRED related data structures and functions -+ */ -+ -+#define FRED_SSX_NMI BIT_ULL(18) -+ -+struct fred_stack { -+ u64 r15; -+ u64 r14; -+ u64 r13; -+ u64 r12; -+ u64 bp; -+ u64 bx; -+ u64 r11; -+ u64 r10; -+ u64 r9; -+ u64 r8; -+ u64 ax; -+ u64 cx; -+ u64 dx; -+ u64 si; -+ u64 di; -+ u64 error_code; -+ u64 ip; -+ u64 csx; -+ u64 flags; -+ u64 sp; -+ u64 ssx; -+ u64 event_data; -+ u64 reserved; -+}; -+ - #endif /* SELFTEST_KVM_PROCESSOR_H */ -diff --git a/tools/testing/selftests/kvm/x86/fred_test.c b/tools/testing/selftests/kvm/x86/fred_test.c -new file mode 100644 -index 000000000000..3d0703f6eba7 ---- /dev/null -+++ b/tools/testing/selftests/kvm/x86/fred_test.c -@@ -0,0 +1,293 @@ -+// SPDX-License-Identifier: GPL-2.0-only -+/* -+ * FRED nested exception tests -+ * -+ * Copyright (C) 2023, Intel, Inc. -+ */ -+#define _GNU_SOURCE /* for program_invocation_short_name */ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "apic.h" -+#include "kvm_util.h" -+#include "test_util.h" -+#include "guest_modes.h" -+#include "processor.h" -+ -+#define IRQ_VECTOR 0xAA -+ -+#define FRED_STKLVL(v, l) (_AT(unsigned long, l) << (2 * (v))) -+#define FRED_CONFIG_ENTRYPOINT(p) _AT(unsigned long, (p)) -+ -+/* This address is already mapped in guest page table. */ -+#define FRED_VALID_RSP 0x8000 -+ -+/* -+ * The following addresses are not yet mapped in both EPT and guest page -+ * tables at the beginning. As a result, it causes an EPT violation VM -+ * exit with an original guest #PF to access any of them for the first -+ * time. -+ * -+ * Use these addresses as guest FRED RSP0 to generate nested #PFs to test -+ * if event data are properly virtualized. -+ */ -+static unsigned long fred_invalid_rsp[4] = { -+ 0x0, -+ 0xf0000000, -+ 0xe0000000, -+ 0xd0000000, -+}; -+ -+extern char asm_user_nop[]; -+extern char asm_user_ud[]; -+extern char asm_done_fault[]; -+ -+extern void asm_test_fault(int test); -+ -+/* -+ * user level code for triggering faults. -+ */ -+asm(".pushsection .text\n" -+ ".align 4096\n" -+ -+ ".type asm_user_nop, @function\n" -+ "asm_user_nop:\n" -+ "1: .byte 0x90\n" -+ "jmp 1b\n" -+ -+ ".org asm_user_nop + 16, 0xcc\n" -+ ".type asm_user_ud, @function\n" -+ "asm_user_ud:\n" -+ /* Trigger a #UD */ -+ "ud2\n" -+ -+ ".align 4096, 0xcc\n" -+ ".popsection"); -+ -+/* Send current stack level and #PF address */ -+#define GUEST_SYNC_CSL_FA(__stage, __pf_address) \ -+ GUEST_SYNC_ARGS(__stage, __pf_address, 0, 0, 0) -+ -+void fred_entry_from_user(struct fred_stack *stack) -+{ -+ u32 current_stack_level = rdmsr(MSR_IA32_FRED_CONFIG) & 0x3; -+ -+ GUEST_SYNC_CSL_FA(current_stack_level, stack->event_data); -+ -+ /* Do NOT go back to user level, continue the next test instead */ -+ stack->ssx = 0x18; -+ stack->csx = 0x10; -+ stack->ip = (u64)&asm_done_fault; -+} -+ -+void fred_entry_from_kernel(struct fred_stack *stack) -+{ -+ /* -+ * Keep NMI blocked to delay the delivery of the next NMI until -+ * returning to user level. -+ * */ -+ stack->ssx &= ~FRED_SSX_NMI; -+} -+ -+#define PUSH_REGS \ -+ "push %rdi\n" \ -+ "push %rsi\n" \ -+ "push %rdx\n" \ -+ "push %rcx\n" \ -+ "push %rax\n" \ -+ "push %r8\n" \ -+ "push %r9\n" \ -+ "push %r10\n" \ -+ "push %r11\n" \ -+ "push %rbx\n" \ -+ "push %rbp\n" \ -+ "push %r12\n" \ -+ "push %r13\n" \ -+ "push %r14\n" \ -+ "push %r15\n" -+ -+#define POP_REGS \ -+ "pop %r15\n" \ -+ "pop %r14\n" \ -+ "pop %r13\n" \ -+ "pop %r12\n" \ -+ "pop %rbp\n" \ -+ "pop %rbx\n" \ -+ "pop %r11\n" \ -+ "pop %r10\n" \ -+ "pop %r9\n" \ -+ "pop %r8\n" \ -+ "pop %rax\n" \ -+ "pop %rcx\n" \ -+ "pop %rdx\n" \ -+ "pop %rsi\n" \ -+ "pop %rdi\n" -+ -+/* -+ * FRED entry points. -+ */ -+asm(".pushsection .text\n" -+ ".type asm_fred_entrypoint_user, @function\n" -+ ".align 4096\n" -+ "asm_fred_entrypoint_user:\n" -+ "endbr64\n" -+ PUSH_REGS -+ "movq %rsp, %rdi\n" -+ "call fred_entry_from_user\n" -+ POP_REGS -+ /* Do NOT go back to user level, continue the next test instead */ -+ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ -+ -+ ".org asm_fred_entrypoint_user + 256, 0xcc\n" -+ ".type asm_fred_entrypoint_kernel, @function\n" -+ "asm_fred_entrypoint_kernel:\n" -+ "endbr64\n" -+ PUSH_REGS -+ "movq %rsp, %rdi\n" -+ "call fred_entry_from_kernel\n" -+ POP_REGS -+ ".byte 0xf2,0x0f,0x01,0xca\n" /* ERETS */ -+ ".align 4096, 0xcc\n" -+ ".popsection"); -+ -+extern char asm_fred_entrypoint_user[]; -+ -+/* -+ * Prepare a FRED stack frame for ERETU to return to user level code, -+ * nop or ud2. -+ * -+ * Because FRED RSP0 is deliberately not mapped in guest page table, -+ * the delivery of interrupt/NMI or #UD from ring 3 causes a nested -+ * #PF, which is then delivered on FRED RSPx (x is 1, 2 or 3, -+ * determinated by MSR FRED_STKLVL[PF_VECTOR]). -+ */ -+asm(".pushsection .text\n" -+ ".type asm_test_fault, @function\n" -+ ".align 4096\n" -+ "asm_test_fault:\n" -+ "endbr64\n" -+ "push %rbp\n" -+ "mov %rsp, %rbp\n" -+ "and $(~0x3f), %rsp\n" -+ "push $0\n" -+ "push $0\n" -+ "mov $0x2b, %rax\n" -+ /* Unblock NMI */ -+ "bts $18, %rax\n" -+ /* Set long mode bit */ -+ "bts $57, %rax\n" -+ "push %rax\n" -+ /* No stack required for the FRED user level test code */ -+ "push $0\n" -+ "pushf\n" -+ "pop %rax\n" -+ /* Allow external interrupts */ -+ "bts $9, %rax\n" -+ "push %rax\n" -+ "mov $0x33, %rax\n" -+ "push %rax\n" -+ "cmp $0, %edi\n" -+ "jne 1f\n" -+ "lea asm_user_nop(%rip), %rax\n" -+ "jmp 2f\n" -+ "1: lea asm_user_ud(%rip), %rax\n" -+ "2: push %rax\n" -+ "push $0\n" -+ /* ERETU to user level code to allow event delivery immediately */ -+ ".byte 0xf3,0x0f,0x01,0xca\n" -+ "asm_done_fault:\n" -+ "mov %rbp, %rsp\n" -+ "pop %rbp\n" -+ "ret\n" -+ ".align 4096, 0xcc\n" -+ ".popsection"); -+ -+/* -+ * To fully test the underlying FRED VMX code, this test should be run one -+ * more round with EPT disabled to inject page faults as nested exceptions. -+ */ -+static void guest_code(void) -+{ -+ wrmsr(MSR_IA32_FRED_CONFIG, -+ FRED_CONFIG_ENTRYPOINT(asm_fred_entrypoint_user)); -+ -+ wrmsr(MSR_IA32_FRED_RSP1, FRED_VALID_RSP); -+ wrmsr(MSR_IA32_FRED_RSP2, FRED_VALID_RSP); -+ wrmsr(MSR_IA32_FRED_RSP3, FRED_VALID_RSP); -+ -+ /* Enable FRED */ -+ set_cr4(get_cr4() | X86_CR4_FRED); -+ -+ x2apic_enable(); -+ -+ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 1)); -+ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[1]); -+ /* 1: ud2 to generate #UD */ -+ asm_test_fault(1); -+ -+ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 2)); -+ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[2]); -+ asm volatile("cli"); -+ /* Create a pending interrupt on current vCPU */ -+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | -+ APIC_DM_FIXED | IRQ_VECTOR); -+ /* Return to ring 3 */ -+ asm_test_fault(0); -+ x2apic_write_reg(APIC_EOI, 0); -+ -+ wrmsr(MSR_IA32_FRED_STKLVLS, FRED_STKLVL(PF_VECTOR, 3)); -+ wrmsr(MSR_IA32_FRED_RSP0, fred_invalid_rsp[3]); -+ /* -+ * The first NMI is just to have NMI blocked in ring 0, because -+ * fred_entry_from_kernel() deliberately clears the NMI bit in -+ * FRED stack frame. -+ */ -+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | -+ APIC_DM_NMI | NMI_VECTOR); -+ /* The second NMI will be delivered after returning to ring 3 */ -+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | -+ APIC_DM_NMI | NMI_VECTOR); -+ /* Return to ring 3 */ -+ asm_test_fault(0); -+ -+ GUEST_DONE(); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ struct kvm_vcpu *vcpu; -+ struct kvm_vm *vm; -+ struct ucall uc; -+ uint64_t expected_current_stack_level = 1; -+ -+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_FRED)); -+ -+ vm = __vm_create_with_vcpus(VM_SHAPE(VM_MODE_PXXV48_4K_USER), 1, 0, -+ guest_code, &vcpu); -+ -+ while (true) { -+ uint64_t r; -+ -+ vcpu_run(vcpu); -+ -+ r = get_ucall(vcpu, &uc); -+ -+ if (r == UCALL_DONE) -+ break; -+ -+ if (r == UCALL_SYNC) { -+ TEST_ASSERT((uc.args[1] == expected_current_stack_level) && -+ (uc.args[2] == fred_invalid_rsp[expected_current_stack_level] - 8), -+ "Incorrect stack level %lx and #PF address %lx\n", -+ uc.args[1], uc.args[2]); -+ expected_current_stack_level++; -+ } -+ } -+ -+ kvm_vm_free(vm); -+ return 0; -+} --- -2.43.0 - diff --git a/SPECS/kernel/0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi b/SPECS/kernel/0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi new file mode 100644 index 000000000..74f1f4464 --- /dev/null +++ b/SPECS/kernel/0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi @@ -0,0 +1,45 @@ +From 8a16c90be344f69517684d96852307171e0555b7 Mon Sep 17 00:00:00 2001 +From: Chenyi Qiang +Date: Fri, 27 Dec 2024 00:47:38 -0800 +Subject: [PATCH 26/44] KVM: selftests: Add the 2nd VM exit controls MSR to the + hidden VMX MSR list + +Signed-off-by: Chenyi Qiang +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + tools/arch/x86/include/asm/msr-index.h | 1 + + tools/testing/selftests/kvm/x86/feature_msrs_test.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h +index 9e1720d73244f..baf5e16484185 100644 +--- a/tools/arch/x86/include/asm/msr-index.h ++++ b/tools/arch/x86/include/asm/msr-index.h +@@ -1225,6 +1225,7 @@ + #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 + #define MSR_IA32_VMX_VMFUNC 0x00000491 + #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 ++#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 + + /* Resctrl MSRs: */ + /* - Intel: */ +diff --git a/tools/testing/selftests/kvm/x86/feature_msrs_test.c b/tools/testing/selftests/kvm/x86/feature_msrs_test.c +index a72f13ae2edbb..815dcb7ad2145 100644 +--- a/tools/testing/selftests/kvm/x86/feature_msrs_test.c ++++ b/tools/testing/selftests/kvm/x86/feature_msrs_test.c +@@ -27,6 +27,7 @@ static bool is_hidden_vmx_msr(uint32_t msr) + case MSR_IA32_VMX_PINBASED_CTLS: + case MSR_IA32_VMX_PROCBASED_CTLS: + case MSR_IA32_VMX_EXIT_CTLS: ++ case MSR_IA32_VMX_EXIT_CTLS2: + case MSR_IA32_VMX_ENTRY_CTLS: + return true; + default: +-- +2.43.0 + diff --git a/SPECS/kernel/0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf b/SPECS/kernel/0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf deleted file mode 100644 index e97225454..000000000 --- a/SPECS/kernel/0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf +++ /dev/null @@ -1,466 +0,0 @@ -From d324a3c4549ba5b1ebf92549ee2fa806a1e70038 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 11:01:25 +0000 -Subject: [PATCH 026/100] perf tools: Enhance sample_regs_user/intr to capture - more registers - -Intel architectural PEBS supports to capture more vector registers like -OPMASK/YMM/ZMM registers besides already supported XMM registers. - -arch-PEBS vector registers (VCER) capturing on perf core/pmu driver -(Intel) has been supported by previous patches. This patch adds perf -tool's part support. In detail, add support for the new -sample_regs_intr/user_ext register selector in perf_event_attr. These 32 -bytes bitmap is used to select the new register group OPMASK, YMMH, ZMMH -and ZMM in VECR. Update perf regs to introduce the new registers. - -This single patch only introduces the generic support, x86/intel specific -support would be added in next patch. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - tools/include/uapi/linux/perf_event.h | 14 +++++++++++++ - tools/perf/builtin-script.c | 23 +++++++++++++++----- - tools/perf/util/evsel.c | 30 ++++++++++++++++++++------- - tools/perf/util/parse-regs-options.c | 23 ++++++++++++-------- - tools/perf/util/perf_regs.h | 16 +++++++++++++- - tools/perf/util/record.h | 4 ++-- - tools/perf/util/sample.h | 6 +++++- - tools/perf/util/session.c | 29 +++++++++++++++----------- - tools/perf/util/synthetic-events.c | 12 +++++++---- - 9 files changed, 116 insertions(+), 41 deletions(-) - -diff --git a/tools/include/uapi/linux/perf_event.h b/tools/include/uapi/linux/perf_event.h -index 78a362b80027..36f17a0fb39e 100644 ---- a/tools/include/uapi/linux/perf_event.h -+++ b/tools/include/uapi/linux/perf_event.h -@@ -382,6 +382,13 @@ enum perf_event_read_format { - #define PERF_ATTR_SIZE_VER6 120 /* Add: aux_sample_size */ - #define PERF_ATTR_SIZE_VER7 128 /* Add: sig_data */ - #define PERF_ATTR_SIZE_VER8 136 /* Add: config3 */ -+#define PERF_ATTR_SIZE_VER9 168 /* add: sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE] */ -+ -+#define PERF_EXT_REGS_ARRAY_SIZE 7 -+#define PERF_NUM_EXT_REGS (PERF_EXT_REGS_ARRAY_SIZE * 64) -+ -+#define PERF_SAMPLE_ARRAY_SIZE (PERF_EXT_REGS_ARRAY_SIZE + 1) -+#define PERF_SAMPLE_REGS_NUM ((PERF_SAMPLE_ARRAY_SIZE) * 64) - - /* - * 'struct perf_event_attr' contains various attributes that define -@@ -543,6 +550,13 @@ struct perf_event_attr { - __u64 sig_data; - - __u64 config3; /* extension of config2 */ -+ -+ /* -+ * Extension sets of regs to dump for each sample. -+ * See asm/perf_regs.h for details. -+ */ -+ __u64 sample_regs_intr_ext[PERF_EXT_REGS_ARRAY_SIZE]; -+ __u64 sample_regs_user_ext[PERF_EXT_REGS_ARRAY_SIZE]; - }; - - /* -diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c -index d9fbdcf72f25..130b03fb78a8 100644 ---- a/tools/perf/builtin-script.c -+++ b/tools/perf/builtin-script.c -@@ -724,21 +724,32 @@ static int perf_session__check_output_opt(struct perf_session *session) - } - - static int perf_sample__fprintf_regs(struct regs_dump *regs, uint64_t mask, const char *arch, -- FILE *fp) -+ unsigned long *mask_ext, FILE *fp) - { -+ unsigned int mask_size = sizeof(mask) * 8; - unsigned i = 0, r; - int printed = 0; -+ u64 val; - - if (!regs || !regs->regs) - return 0; - - printed += fprintf(fp, " ABI:%" PRIu64 " ", regs->abi); - -- for_each_set_bit(r, (unsigned long *) &mask, sizeof(mask) * 8) { -- u64 val = regs->regs[i++]; -+ for_each_set_bit(r, (unsigned long *)&mask, mask_size) { -+ val = regs->regs[i++]; - printed += fprintf(fp, "%5s:0x%"PRIx64" ", perf_reg_name(r, arch), val); - } - -+ if (!mask_ext) -+ return printed; -+ -+ for_each_set_bit(r, mask_ext, PERF_NUM_EXT_REGS) { -+ val = regs->regs[i++]; -+ printed += fprintf(fp, "%5s:0x%"PRIx64" ", -+ perf_reg_name(r + mask_size, arch), val); -+ } -+ - return printed; - } - -@@ -799,7 +810,8 @@ static int perf_sample__fprintf_iregs(struct perf_sample *sample, - return 0; - - return perf_sample__fprintf_regs(perf_sample__intr_regs(sample), -- attr->sample_regs_intr, arch, fp); -+ attr->sample_regs_intr, arch, -+ (unsigned long *)attr->sample_regs_intr_ext, fp); - } - - static int perf_sample__fprintf_uregs(struct perf_sample *sample, -@@ -809,7 +821,8 @@ static int perf_sample__fprintf_uregs(struct perf_sample *sample, - return 0; - - return perf_sample__fprintf_regs(perf_sample__user_regs(sample), -- attr->sample_regs_user, arch, fp); -+ attr->sample_regs_user, arch, -+ (unsigned long *)attr->sample_regs_user_ext, fp); - } - - static int perf_sample__fprintf_start(struct perf_script *script, -diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c -index f9e79b86b794..c89eeff51d28 100644 ---- a/tools/perf/util/evsel.c -+++ b/tools/perf/util/evsel.c -@@ -1046,7 +1046,7 @@ static void __evsel__config_callchain(struct evsel *evsel, struct record_opts *o - arch__user_reg_mask((unsigned long *)&mask); - evsel__set_sample_bit(evsel, REGS_USER); - evsel__set_sample_bit(evsel, STACK_USER); -- if (opts->sample_user_regs && -+ if (bitmap_weight(opts->sample_user_regs, PERF_SAMPLE_REGS_NUM) && - DWARF_MINIMAL_REGS(arch) != mask) { - attr->sample_regs_user |= DWARF_MINIMAL_REGS(arch); - pr_warning("WARNING: The use of --call-graph=dwarf may require all the user registers, " -@@ -1383,15 +1383,19 @@ void evsel__config(struct evsel *evsel, struct record_opts *opts, - if (callchain && callchain->enabled && !evsel->no_aux_samples) - evsel__config_callchain(evsel, opts, callchain); - -- if (opts->sample_intr_regs && !evsel->no_aux_samples && -- !evsel__is_dummy_event(evsel)) { -- attr->sample_regs_intr = opts->sample_intr_regs; -+ if (bitmap_weight(opts->sample_intr_regs, PERF_SAMPLE_REGS_NUM) && -+ !evsel->no_aux_samples && !evsel__is_dummy_event(evsel)) { -+ attr->sample_regs_intr = opts->sample_intr_regs[0]; -+ memcpy(attr->sample_regs_intr_ext, &opts->sample_intr_regs[1], -+ PERF_NUM_EXT_REGS / 8); - evsel__set_sample_bit(evsel, REGS_INTR); - } - -- if (opts->sample_user_regs && !evsel->no_aux_samples && -- !evsel__is_dummy_event(evsel)) { -- attr->sample_regs_user |= opts->sample_user_regs; -+ if (bitmap_weight(opts->sample_user_regs, PERF_SAMPLE_REGS_NUM) && -+ !evsel->no_aux_samples && !evsel__is_dummy_event(evsel)) { -+ attr->sample_regs_user |= opts->sample_user_regs[0]; -+ memcpy(attr->sample_regs_user_ext, &opts->sample_user_regs[1], -+ PERF_NUM_EXT_REGS / 8); - evsel__set_sample_bit(evsel, REGS_USER); - } - -@@ -3230,10 +3234,16 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, - - if (regs->abi) { - u64 mask = evsel->core.attr.sample_regs_user; -+ unsigned long *mask_ext = -+ (unsigned long *)evsel->core.attr.sample_regs_user_ext; -+ u64 *user_regs_mask; - - sz = hweight64(mask) * sizeof(u64); -+ sz += bitmap_weight(mask_ext, PERF_NUM_EXT_REGS) * sizeof(u64); - OVERFLOW_CHECK(array, sz, max_size); - regs->mask = mask; -+ user_regs_mask = (u64 *)regs->mask_ext; -+ memcpy(&user_regs_mask[1], mask_ext, PERF_NUM_EXT_REGS); - regs->regs = (u64 *)array; - array = (void *)array + sz; - } -@@ -3287,10 +3297,16 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, - - if (regs->abi != PERF_SAMPLE_REGS_ABI_NONE) { - u64 mask = evsel->core.attr.sample_regs_intr; -+ unsigned long *mask_ext = -+ (unsigned long *)evsel->core.attr.sample_regs_intr_ext; -+ u64 *intr_regs_mask; - - sz = hweight64(mask) * sizeof(u64); -+ sz += bitmap_weight(mask_ext, PERF_NUM_EXT_REGS) * sizeof(u64); - OVERFLOW_CHECK(array, sz, max_size); - regs->mask = mask; -+ intr_regs_mask = (u64 *)regs->mask_ext; -+ memcpy(&intr_regs_mask[1], mask_ext, PERF_NUM_EXT_REGS); - regs->regs = (u64 *)array; - array = (void *)array + sz; - } -diff --git a/tools/perf/util/parse-regs-options.c b/tools/perf/util/parse-regs-options.c -index 3dcd8dc4f81b..42b176705ccf 100644 ---- a/tools/perf/util/parse-regs-options.c -+++ b/tools/perf/util/parse-regs-options.c -@@ -12,11 +12,13 @@ - static int - __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - { -+ unsigned int size = PERF_SAMPLE_REGS_NUM; - uint64_t *mode = (uint64_t *)opt->value; - const struct sample_reg *r = NULL; - char *s, *os = NULL, *p; - int ret = -1; -- uint64_t mask = 0; -+ DECLARE_BITMAP(mask, size); -+ DECLARE_BITMAP(mask_tmp, size); - - if (unset) - return 0; -@@ -24,13 +26,14 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - /* - * cannot set it twice - */ -- if (*mode) -+ if (bitmap_weight((unsigned long *)mode, size)) - return -1; - -+ bitmap_zero(mask, size); - if (intr) -- arch__intr_reg_mask((unsigned long *)&mask); -+ arch__intr_reg_mask(mask); - else -- arch__user_reg_mask((unsigned long *)&mask); -+ arch__user_reg_mask(mask); - - /* str may be NULL in case no arg is passed to -I */ - if (str) { -@@ -47,7 +50,8 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - if (!strcmp(s, "?")) { - fprintf(stderr, "available registers: "); - for (r = arch__sample_reg_masks(); r->name; r++) { -- if (r->mask & mask) -+ bitmap_and(mask_tmp, mask, r->mask_ext, size); -+ if (bitmap_weight(mask_tmp, size)) - fprintf(stderr, "%s ", r->name); - } - fputc('\n', stderr); -@@ -55,7 +59,8 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - goto error; - } - for (r = arch__sample_reg_masks(); r->name; r++) { -- if ((r->mask & mask) && !strcasecmp(s, r->name)) -+ bitmap_and(mask_tmp, mask, r->mask_ext, size); -+ if (bitmap_weight(mask_tmp, size) && !strcasecmp(s, r->name)) - break; - } - if (!r || !r->name) { -@@ -64,7 +69,7 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - goto error; - } - -- *mode |= r->mask; -+ bitmap_or((unsigned long *)mode, (unsigned long *)mode, r->mask_ext, size); - - if (!p) - break; -@@ -75,8 +80,8 @@ __parse_regs(const struct option *opt, const char *str, int unset, bool intr) - ret = 0; - - /* default to all possible regs */ -- if (*mode == 0) -- *mode = mask; -+ if (!bitmap_weight((unsigned long *)mode, size)) -+ bitmap_or((unsigned long *)mode, (unsigned long *)mode, mask, size); - error: - free(os); - return ret; -diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h -index 316d280e5cd7..d60a74623a0f 100644 ---- a/tools/perf/util/perf_regs.h -+++ b/tools/perf/util/perf_regs.h -@@ -4,18 +4,32 @@ - - #include - #include -+#include -+#include -+#include "util/record.h" - - struct regs_dump; - - struct sample_reg { - const char *name; -- uint64_t mask; -+ union { -+ uint64_t mask; -+ DECLARE_BITMAP(mask_ext, PERF_SAMPLE_REGS_NUM); -+ }; - }; - - #define SMPL_REG_MASK(b) (1ULL << (b)) - #define SMPL_REG(n, b) { .name = #n, .mask = SMPL_REG_MASK(b) } - #define SMPL_REG2_MASK(b) (3ULL << (b)) - #define SMPL_REG2(n, b) { .name = #n, .mask = SMPL_REG2_MASK(b) } -+#define SMPL_REG_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0x1ULL << (b % __BITS_PER_LONG) } -+#define SMPL_REG2_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0x3ULL << (b % __BITS_PER_LONG) } -+#define SMPL_REG4_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0xfULL << (b % __BITS_PER_LONG) } -+#define SMPL_REG8_EXT(n, b) \ -+ { .name = #n, .mask_ext[b / __BITS_PER_LONG] = 0xffULL << (b % __BITS_PER_LONG) } - #define SMPL_REG_END { .name = NULL } - - enum { -diff --git a/tools/perf/util/record.h b/tools/perf/util/record.h -index ea3a6c4657ee..aa855f49cf3a 100644 ---- a/tools/perf/util/record.h -+++ b/tools/perf/util/record.h -@@ -58,8 +58,8 @@ struct record_opts { - unsigned int auxtrace_mmap_pages; - unsigned int user_freq; - u64 branch_stack; -- u64 sample_intr_regs; -- u64 sample_user_regs; -+ u64 sample_intr_regs[PERF_SAMPLE_ARRAY_SIZE]; -+ u64 sample_user_regs[PERF_SAMPLE_ARRAY_SIZE]; - u64 default_interval; - u64 user_interval; - size_t auxtrace_snapshot_size; -diff --git a/tools/perf/util/sample.h b/tools/perf/util/sample.h -index fae834144ef4..345a7e3c4b71 100644 ---- a/tools/perf/util/sample.h -+++ b/tools/perf/util/sample.h -@@ -4,13 +4,17 @@ - - #include - #include -+#include - - /* number of register is bound by the number of bits in regs_dump::mask (64) */ - #define PERF_SAMPLE_REGS_CACHE_SIZE (8 * sizeof(u64)) - - struct regs_dump { - u64 abi; -- u64 mask; -+ union { -+ u64 mask; -+ DECLARE_BITMAP(mask_ext, PERF_SAMPLE_REGS_NUM); -+ }; - u64 *regs; - - /* Cached values/mask filled by first register access. */ -diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c -index 26ae078278cd..276d3931184c 100644 ---- a/tools/perf/util/session.c -+++ b/tools/perf/util/session.c -@@ -915,12 +915,13 @@ static void branch_stack__printf(struct perf_sample *sample, - } - } - --static void regs_dump__printf(u64 mask, u64 *regs, const char *arch) -+static void regs_dump__printf(struct regs_dump *regs, const char *arch) - { -+ unsigned int size = PERF_SAMPLE_REGS_NUM; - unsigned rid, i = 0; - -- for_each_set_bit(rid, (unsigned long *) &mask, sizeof(mask) * 8) { -- u64 val = regs[i++]; -+ for_each_set_bit(rid, regs->mask_ext, size) { -+ u64 val = regs->regs[i++]; - - printf(".... %-5s 0x%016" PRIx64 "\n", - perf_reg_name(rid, arch), val); -@@ -941,16 +942,20 @@ static inline const char *regs_dump_abi(struct regs_dump *d) - return regs_abi[d->abi]; - } - --static void regs__printf(const char *type, struct regs_dump *regs, const char *arch) -+static void regs__printf(bool intr, struct regs_dump *regs, const char *arch) - { -- u64 mask = regs->mask; -+ u64 *mask = (u64 *)®s->mask_ext; - -- printf("... %s regs: mask 0x%" PRIx64 " ABI %s\n", -- type, -- mask, -- regs_dump_abi(regs)); -+ if (intr) -+ printf("... intr regs: mask 0x"); -+ else -+ printf("... user regs: mask 0x"); -+ -+ for (int i = 0; i < PERF_SAMPLE_ARRAY_SIZE; i++) -+ printf("%" PRIx64 "", mask[i]); -+ printf(" ABI %s\n", regs_dump_abi(regs)); - -- regs_dump__printf(mask, regs->regs, arch); -+ regs_dump__printf(regs, arch); - } - - static void regs_user__printf(struct perf_sample *sample, const char *arch) -@@ -963,7 +968,7 @@ static void regs_user__printf(struct perf_sample *sample, const char *arch) - user_regs = perf_sample__user_regs(sample); - - if (user_regs->regs) -- regs__printf("user", user_regs, arch); -+ regs__printf(false, user_regs, arch); - } - - static void regs_intr__printf(struct perf_sample *sample, const char *arch) -@@ -976,7 +981,7 @@ static void regs_intr__printf(struct perf_sample *sample, const char *arch) - intr_regs = perf_sample__intr_regs(sample); - - if (intr_regs->regs) -- regs__printf("intr", intr_regs, arch); -+ regs__printf(true, intr_regs, arch); - } - - static void stack_user__printf(struct stack_dump *dump) -diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c -index cb2c1ace304a..ee4c9fdd02a3 100644 ---- a/tools/perf/util/synthetic-events.c -+++ b/tools/perf/util/synthetic-events.c -@@ -1518,7 +1518,8 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, - if (type & PERF_SAMPLE_REGS_USER) { - if (sample->user_regs && sample->user_regs->abi) { - result += sizeof(u64); -- sz = hweight64(sample->user_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->user_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - result += sz; - } else { - result += sizeof(u64); -@@ -1546,7 +1547,8 @@ size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, - if (type & PERF_SAMPLE_REGS_INTR) { - if (sample->intr_regs && sample->intr_regs->abi) { - result += sizeof(u64); -- sz = hweight64(sample->intr_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->intr_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - result += sz; - } else { - result += sizeof(u64); -@@ -1723,7 +1725,8 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo - if (type & PERF_SAMPLE_REGS_USER) { - if (sample->user_regs && sample->user_regs->abi) { - *array++ = sample->user_regs->abi; -- sz = hweight64(sample->user_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->user_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - memcpy(array, sample->user_regs->regs, sz); - array = (void *)array + sz; - } else { -@@ -1759,7 +1762,8 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_fo - if (type & PERF_SAMPLE_REGS_INTR) { - if (sample->intr_regs && sample->intr_regs->abi) { - *array++ = sample->intr_regs->abi; -- sz = hweight64(sample->intr_regs->mask) * sizeof(u64); -+ sz = bitmap_weight(sample->intr_regs->mask_ext, -+ PERF_SAMPLE_REGS_NUM) * sizeof(u64); - memcpy(array, sample->intr_regs->regs, sz); - array = (void *)array + sz; - } else { --- -2.43.0 - diff --git a/SPECS/kernel/0026-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch b/SPECS/kernel/0026-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch deleted file mode 100644 index e8ed016e5..000000000 --- a/SPECS/kernel/0026-wifi-mt76-wed-use-proper-wed-reference-in-mt76-wed-d.patch +++ /dev/null @@ -1,170 +0,0 @@ -From be64fd5cb0ae6b027bd7f2cbdfd38dde02c6d7c4 Mon Sep 17 00:00:00 2001 -From: Lorenzo Bianconi -Date: Wed, 8 Oct 2025 12:41:48 +0200 -Subject: [PATCH 26/51] wifi: mt76: wed: use proper wed reference in mt76 wed - driver callabacks - -MT7996 driver can use both wed and wed_hif2 devices to offload traffic -from/to the wireless NIC. In the current codebase we assume to always -use the primary wed device in wed callbacks resulting in the following -crash if the hw runs wed_hif2 (e.g. 6GHz link). - -[ 297.455876] Unable to handle kernel read from unreadable memory at virtual address 000000000000080a -[ 297.464928] Mem abort info: -[ 297.467722] ESR = 0x0000000096000005 -[ 297.471461] EC = 0x25: DABT (current EL), IL = 32 bits -[ 297.476766] SET = 0, FnV = 0 -[ 297.479809] EA = 0, S1PTW = 0 -[ 297.482940] FSC = 0x05: level 1 translation fault -[ 297.487809] Data abort info: -[ 297.490679] ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 -[ 297.496156] CM = 0, WnR = 0, TnD = 0, TagAccess = 0 -[ 297.501196] GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 -[ 297.506500] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000107480000 -[ 297.512927] [000000000000080a] pgd=08000001097fb003, p4d=08000001097fb003, pud=08000001097fb003, pmd=0000000000000000 -[ 297.523532] Internal error: Oops: 0000000096000005 [#1] SMP -[ 297.715393] CPU: 2 UID: 0 PID: 45 Comm: kworker/u16:2 Tainted: G O 6.12.50 #0 -[ 297.723908] Tainted: [O]=OOT_MODULE -[ 297.727384] Hardware name: Banana Pi BPI-R4 (2x SFP+) (DT) -[ 297.732857] Workqueue: nf_ft_offload_del nf_flow_rule_route_ipv6 [nf_flow_table] -[ 297.740254] pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) -[ 297.747205] pc : mt76_wed_offload_disable+0x64/0xa0 [mt76] -[ 297.752688] lr : mtk_wed_flow_remove+0x58/0x80 -[ 297.757126] sp : ffffffc080fe3ae0 -[ 297.760430] x29: ffffffc080fe3ae0 x28: ffffffc080fe3be0 x27: 00000000deadbef7 -[ 297.767557] x26: ffffff80c5ebca00 x25: 0000000000000001 x24: ffffff80c85f4c00 -[ 297.774683] x23: ffffff80c1875b78 x22: ffffffc080d42cd0 x21: ffffffc080660018 -[ 297.781809] x20: ffffff80c6a076d0 x19: ffffff80c6a043c8 x18: 0000000000000000 -[ 297.788935] x17: 0000000000000000 x16: 0000000000000001 x15: 0000000000000000 -[ 297.796060] x14: 0000000000000019 x13: ffffff80c0ad8ec0 x12: 00000000fa83b2da -[ 297.803185] x11: ffffff80c02700c0 x10: ffffff80c0ad8ec0 x9 : ffffff81fef96200 -[ 297.810311] x8 : ffffff80c02700c0 x7 : ffffff80c02700d0 x6 : 0000000000000002 -[ 297.817435] x5 : 0000000000000400 x4 : 0000000000000000 x3 : 0000000000000000 -[ 297.824561] x2 : 0000000000000001 x1 : 0000000000000800 x0 : ffffff80c6a063c8 -[ 297.831686] Call trace: -[ 297.834123] mt76_wed_offload_disable+0x64/0xa0 [mt76] -[ 297.839254] mtk_wed_flow_remove+0x58/0x80 -[ 297.843342] mtk_flow_offload_cmd+0x434/0x574 -[ 297.847689] mtk_wed_setup_tc_block_cb+0x30/0x40 -[ 297.852295] nf_flow_offload_ipv6_hook+0x7f4/0x964 [nf_flow_table] -[ 297.858466] nf_flow_rule_route_ipv6+0x438/0x4a4 [nf_flow_table] -[ 297.864463] process_one_work+0x174/0x300 -[ 297.868465] worker_thread+0x278/0x430 -[ 297.872204] kthread+0xd8/0xdc -[ 297.875251] ret_from_fork+0x10/0x20 -[ 297.878820] Code: 928b5ae0 8b000273 91400a60 f943fa61 (79401421) -[ 297.884901] ---[ end trace 0000000000000000 ]--- - -Fix the issue detecting the proper wed reference to use running wed -callabacks. - -Fixes: 83eafc9251d6 ("wifi: mt76: mt7996: add wed tx support") -Tested-by: Daniel Pawlik -Tested-by: Matteo Croce -Signed-off-by: Lorenzo Bianconi -Link: https://patch.msgid.link/20251008-wed-fixes-v1-1-8f7678583385@kernel.org -Signed-off-by: Felix Fietkau ---- - drivers/net/wireless/mediatek/mt76/mt76.h | 9 +++++++++ - drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 1 + - drivers/net/wireless/mediatek/mt76/wed.c | 10 +++++----- - include/linux/soc/mediatek/mtk_wed.h | 1 + - 4 files changed, 16 insertions(+), 5 deletions(-) - -diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h -index 47c143e6a79a..eba26875aded 100644 ---- a/drivers/net/wireless/mediatek/mt76/mt76.h -+++ b/drivers/net/wireless/mediatek/mt76/mt76.h -@@ -1226,6 +1226,15 @@ static inline int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, - #define mt76_dereference(p, dev) \ - rcu_dereference_protected(p, lockdep_is_held(&(dev)->mutex)) - -+static inline struct mt76_dev *mt76_wed_to_dev(struct mtk_wed_device *wed) -+{ -+#ifdef CONFIG_NET_MEDIATEK_SOC_WED -+ if (wed->wlan.hif2) -+ return container_of(wed, struct mt76_dev, mmio.wed_hif2); -+#endif /* CONFIG_NET_MEDIATEK_SOC_WED */ -+ return container_of(wed, struct mt76_dev, mmio.wed); -+} -+ - static inline struct mt76_wcid * - __mt76_wcid_ptr(struct mt76_dev *dev, u16 idx) - { -diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c -index 30b40f4a91be..b2af25aef762 100644 ---- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c -+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c -@@ -562,6 +562,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, - - wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE; - wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf; -+ wed->wlan.hif2 = hif2; - - wed->wlan.amsdu_max_subframes = 8; - wed->wlan.amsdu_max_len = 1536; -diff --git a/drivers/net/wireless/mediatek/mt76/wed.c b/drivers/net/wireless/mediatek/mt76/wed.c -index 63f69e152b1c..3ff547e0b250 100644 ---- a/drivers/net/wireless/mediatek/mt76/wed.c -+++ b/drivers/net/wireless/mediatek/mt76/wed.c -@@ -8,7 +8,7 @@ - - void mt76_wed_release_rx_buf(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - int i; - - for (i = 0; i < dev->rx_token_size; i++) { -@@ -31,8 +31,8 @@ EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf); - #ifdef CONFIG_NET_MEDIATEK_SOC_WED - u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); - struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc; -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN]; - struct mt76_txwi_cache *t = NULL; - int i; -@@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_init_rx_buf); - - int mt76_wed_offload_enable(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - - spin_lock_bh(&dev->token_lock); - dev->token_size = wed->wlan.token_start; -@@ -164,7 +164,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_dma_setup); - - void mt76_wed_offload_disable(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - - spin_lock_bh(&dev->token_lock); - dev->token_size = dev->drv->token_size; -@@ -174,7 +174,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_offload_disable); - - void mt76_wed_reset_complete(struct mtk_wed_device *wed) - { -- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed); -+ struct mt76_dev *dev = mt76_wed_to_dev(wed); - - complete(&dev->mmio.wed_reset_complete); - } -diff --git a/include/linux/soc/mediatek/mtk_wed.h b/include/linux/soc/mediatek/mtk_wed.h -index d8949a4ed0dc..8d34bee714e8 100644 ---- a/include/linux/soc/mediatek/mtk_wed.h -+++ b/include/linux/soc/mediatek/mtk_wed.h -@@ -154,6 +154,7 @@ struct mtk_wed_device { - bool wcid_512; - bool hw_rro; - bool msi; -+ bool hif2; - - u16 token_start; - unsigned int nbuf; --- -2.43.0 - diff --git a/SPECS/kernel/0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi b/SPECS/kernel/0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi deleted file mode 100644 index cd6f260d1..000000000 --- a/SPECS/kernel/0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi +++ /dev/null @@ -1,45 +0,0 @@ -From f9a2b243be42dd5923308b6302a9e42ee231813e Mon Sep 17 00:00:00 2001 -From: Chenyi Qiang -Date: Fri, 27 Dec 2024 00:47:38 -0800 -Subject: [PATCH 27/44] KVM: selftests: Add the 2nd VM exit controls MSR to the - hidden VMX MSR list - -Signed-off-by: Chenyi Qiang -Signed-off-by: Xin Li (Intel) -Tested-by: Xuelian Guo ---- - -Change in v5: -* Add TB from Xuelian Guo. ---- - tools/arch/x86/include/asm/msr-index.h | 1 + - tools/testing/selftests/kvm/x86/feature_msrs_test.c | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/tools/arch/x86/include/asm/msr-index.h b/tools/arch/x86/include/asm/msr-index.h -index daebfd926f08..a40a87a942cb 100644 ---- a/tools/arch/x86/include/asm/msr-index.h -+++ b/tools/arch/x86/include/asm/msr-index.h -@@ -1202,6 +1202,7 @@ - #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 - #define MSR_IA32_VMX_VMFUNC 0x00000491 - #define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 -+#define MSR_IA32_VMX_EXIT_CTLS2 0x00000493 - - /* Resctrl MSRs: */ - /* - Intel: */ -diff --git a/tools/testing/selftests/kvm/x86/feature_msrs_test.c b/tools/testing/selftests/kvm/x86/feature_msrs_test.c -index a72f13ae2edb..815dcb7ad214 100644 ---- a/tools/testing/selftests/kvm/x86/feature_msrs_test.c -+++ b/tools/testing/selftests/kvm/x86/feature_msrs_test.c -@@ -27,6 +27,7 @@ static bool is_hidden_vmx_msr(uint32_t msr) - case MSR_IA32_VMX_PINBASED_CTLS: - case MSR_IA32_VMX_PROCBASED_CTLS: - case MSR_IA32_VMX_EXIT_CTLS: -+ case MSR_IA32_VMX_EXIT_CTLS2: - case MSR_IA32_VMX_ENTRY_CTLS: - return true; - default: --- -2.43.0 - diff --git a/SPECS/kernel/0027-perf-tools-Support-to-capture-more-vector-registers-x.perf b/SPECS/kernel/0027-perf-tools-Support-to-capture-more-vector-registers-x.perf deleted file mode 100644 index 84362fa5d..000000000 --- a/SPECS/kernel/0027-perf-tools-Support-to-capture-more-vector-registers-x.perf +++ /dev/null @@ -1,381 +0,0 @@ -From 6990e5be1d5915afc40ccedeb57b9b350bd5afad Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Thu, 14 Nov 2024 11:26:40 +0000 -Subject: [PATCH 027/100] perf tools: Support to capture more vector registers - (x86/Intel) - -Intel architectural PEBS supports to capture more vector registers like -OPMASK/YMM/ZMM registers besides already supported XMM registers. - -This patch adds Intel specific support to capture these new vector -registers for perf tools. - -Co-developed-by: Kan Liang -Signed-off-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - tools/arch/x86/include/uapi/asm/perf_regs.h | 79 ++++++++++- - tools/perf/arch/x86/util/perf_regs.c | 129 +++++++++++++++++- - .../perf/util/perf-regs-arch/perf_regs_x86.c | 82 +++++++++++ - 3 files changed, 285 insertions(+), 5 deletions(-) - -diff --git a/tools/arch/x86/include/uapi/asm/perf_regs.h b/tools/arch/x86/include/uapi/asm/perf_regs.h -index 1c7ab5af5cc1..c05c6ec127c8 100644 ---- a/tools/arch/x86/include/uapi/asm/perf_regs.h -+++ b/tools/arch/x86/include/uapi/asm/perf_regs.h -@@ -36,7 +36,7 @@ enum perf_event_x86_regs { - /* PERF_REG_INTEL_PT_MAX ignores the SSP register. */ - PERF_REG_INTEL_PT_MAX = PERF_REG_X86_R15 + 1, - -- /* These all need two bits set because they are 128bit */ -+ /* These all need two bits set because they are 128 bits */ - PERF_REG_X86_XMM0 = 32, - PERF_REG_X86_XMM1 = 34, - PERF_REG_X86_XMM2 = 36, -@@ -56,6 +56,83 @@ enum perf_event_x86_regs { - - /* These include both GPRs and XMMX registers */ - PERF_REG_X86_XMM_MAX = PERF_REG_X86_XMM15 + 2, -+ -+ /* Leave bits[127:64] for other GP registers, like R16 ~ R31.*/ -+ -+ /* -+ * Each YMM register need 4 bits to represent because they are 256 bits. -+ * PERF_REG_X86_YMMH0 = 128 -+ */ -+ PERF_REG_X86_YMM0 = 128, -+ PERF_REG_X86_YMM1 = PERF_REG_X86_YMM0 + 4, -+ PERF_REG_X86_YMM2 = PERF_REG_X86_YMM1 + 4, -+ PERF_REG_X86_YMM3 = PERF_REG_X86_YMM2 + 4, -+ PERF_REG_X86_YMM4 = PERF_REG_X86_YMM3 + 4, -+ PERF_REG_X86_YMM5 = PERF_REG_X86_YMM4 + 4, -+ PERF_REG_X86_YMM6 = PERF_REG_X86_YMM5 + 4, -+ PERF_REG_X86_YMM7 = PERF_REG_X86_YMM6 + 4, -+ PERF_REG_X86_YMM8 = PERF_REG_X86_YMM7 + 4, -+ PERF_REG_X86_YMM9 = PERF_REG_X86_YMM8 + 4, -+ PERF_REG_X86_YMM10 = PERF_REG_X86_YMM9 + 4, -+ PERF_REG_X86_YMM11 = PERF_REG_X86_YMM10 + 4, -+ PERF_REG_X86_YMM12 = PERF_REG_X86_YMM11 + 4, -+ PERF_REG_X86_YMM13 = PERF_REG_X86_YMM12 + 4, -+ PERF_REG_X86_YMM14 = PERF_REG_X86_YMM13 + 4, -+ PERF_REG_X86_YMM15 = PERF_REG_X86_YMM14 + 4, -+ PERF_REG_X86_YMM_MAX = PERF_REG_X86_YMM15 + 4, -+ -+ /* -+ * Each ZMM register needs 8 bits to represent because they are 512 bits -+ * PERF_REG_X86_ZMMH0 = 192 -+ */ -+ PERF_REG_X86_ZMM0 = PERF_REG_X86_YMM_MAX, -+ PERF_REG_X86_ZMM1 = PERF_REG_X86_ZMM0 + 8, -+ PERF_REG_X86_ZMM2 = PERF_REG_X86_ZMM1 + 8, -+ PERF_REG_X86_ZMM3 = PERF_REG_X86_ZMM2 + 8, -+ PERF_REG_X86_ZMM4 = PERF_REG_X86_ZMM3 + 8, -+ PERF_REG_X86_ZMM5 = PERF_REG_X86_ZMM4 + 8, -+ PERF_REG_X86_ZMM6 = PERF_REG_X86_ZMM5 + 8, -+ PERF_REG_X86_ZMM7 = PERF_REG_X86_ZMM6 + 8, -+ PERF_REG_X86_ZMM8 = PERF_REG_X86_ZMM7 + 8, -+ PERF_REG_X86_ZMM9 = PERF_REG_X86_ZMM8 + 8, -+ PERF_REG_X86_ZMM10 = PERF_REG_X86_ZMM9 + 8, -+ PERF_REG_X86_ZMM11 = PERF_REG_X86_ZMM10 + 8, -+ PERF_REG_X86_ZMM12 = PERF_REG_X86_ZMM11 + 8, -+ PERF_REG_X86_ZMM13 = PERF_REG_X86_ZMM12 + 8, -+ PERF_REG_X86_ZMM14 = PERF_REG_X86_ZMM13 + 8, -+ PERF_REG_X86_ZMM15 = PERF_REG_X86_ZMM14 + 8, -+ PERF_REG_X86_ZMM16 = PERF_REG_X86_ZMM15 + 8, -+ PERF_REG_X86_ZMM17 = PERF_REG_X86_ZMM16 + 8, -+ PERF_REG_X86_ZMM18 = PERF_REG_X86_ZMM17 + 8, -+ PERF_REG_X86_ZMM19 = PERF_REG_X86_ZMM18 + 8, -+ PERF_REG_X86_ZMM20 = PERF_REG_X86_ZMM19 + 8, -+ PERF_REG_X86_ZMM21 = PERF_REG_X86_ZMM20 + 8, -+ PERF_REG_X86_ZMM22 = PERF_REG_X86_ZMM21 + 8, -+ PERF_REG_X86_ZMM23 = PERF_REG_X86_ZMM22 + 8, -+ PERF_REG_X86_ZMM24 = PERF_REG_X86_ZMM23 + 8, -+ PERF_REG_X86_ZMM25 = PERF_REG_X86_ZMM24 + 8, -+ PERF_REG_X86_ZMM26 = PERF_REG_X86_ZMM25 + 8, -+ PERF_REG_X86_ZMM27 = PERF_REG_X86_ZMM26 + 8, -+ PERF_REG_X86_ZMM28 = PERF_REG_X86_ZMM27 + 8, -+ PERF_REG_X86_ZMM29 = PERF_REG_X86_ZMM28 + 8, -+ PERF_REG_X86_ZMM30 = PERF_REG_X86_ZMM29 + 8, -+ PERF_REG_X86_ZMM31 = PERF_REG_X86_ZMM30 + 8, -+ PERF_REG_X86_ZMM_MAX = PERF_REG_X86_ZMM31 + 8, -+ -+ /* -+ * OPMASK Registers -+ * PERF_REG_X86_OPMASK0 = 448 -+ */ -+ PERF_REG_X86_OPMASK0 = PERF_REG_X86_ZMM_MAX, -+ PERF_REG_X86_OPMASK1 = PERF_REG_X86_OPMASK0 + 1, -+ PERF_REG_X86_OPMASK2 = PERF_REG_X86_OPMASK1 + 1, -+ PERF_REG_X86_OPMASK3 = PERF_REG_X86_OPMASK2 + 1, -+ PERF_REG_X86_OPMASK4 = PERF_REG_X86_OPMASK3 + 1, -+ PERF_REG_X86_OPMASK5 = PERF_REG_X86_OPMASK4 + 1, -+ PERF_REG_X86_OPMASK6 = PERF_REG_X86_OPMASK5 + 1, -+ PERF_REG_X86_OPMASK7 = PERF_REG_X86_OPMASK6 + 1, -+ -+ PERF_REG_X86_VEC_MAX = PERF_REG_X86_OPMASK7 + 1, - }; - - #define PERF_REG_EXTENDED_MASK (~((1ULL << PERF_REG_X86_XMM0) - 1)) -diff --git a/tools/perf/arch/x86/util/perf_regs.c b/tools/perf/arch/x86/util/perf_regs.c -index 5b163f0a651a..bade6c64770c 100644 ---- a/tools/perf/arch/x86/util/perf_regs.c -+++ b/tools/perf/arch/x86/util/perf_regs.c -@@ -54,6 +54,66 @@ static const struct sample_reg sample_reg_masks[] = { - SMPL_REG2(XMM13, PERF_REG_X86_XMM13), - SMPL_REG2(XMM14, PERF_REG_X86_XMM14), - SMPL_REG2(XMM15, PERF_REG_X86_XMM15), -+ -+ SMPL_REG4_EXT(YMM0, PERF_REG_X86_YMM0), -+ SMPL_REG4_EXT(YMM1, PERF_REG_X86_YMM1), -+ SMPL_REG4_EXT(YMM2, PERF_REG_X86_YMM2), -+ SMPL_REG4_EXT(YMM3, PERF_REG_X86_YMM3), -+ SMPL_REG4_EXT(YMM4, PERF_REG_X86_YMM4), -+ SMPL_REG4_EXT(YMM5, PERF_REG_X86_YMM5), -+ SMPL_REG4_EXT(YMM6, PERF_REG_X86_YMM6), -+ SMPL_REG4_EXT(YMM7, PERF_REG_X86_YMM7), -+ SMPL_REG4_EXT(YMM8, PERF_REG_X86_YMM8), -+ SMPL_REG4_EXT(YMM9, PERF_REG_X86_YMM9), -+ SMPL_REG4_EXT(YMM10, PERF_REG_X86_YMM10), -+ SMPL_REG4_EXT(YMM11, PERF_REG_X86_YMM11), -+ SMPL_REG4_EXT(YMM12, PERF_REG_X86_YMM12), -+ SMPL_REG4_EXT(YMM13, PERF_REG_X86_YMM13), -+ SMPL_REG4_EXT(YMM14, PERF_REG_X86_YMM14), -+ SMPL_REG4_EXT(YMM15, PERF_REG_X86_YMM15), -+ -+ SMPL_REG8_EXT(ZMM0, PERF_REG_X86_ZMM0), -+ SMPL_REG8_EXT(ZMM1, PERF_REG_X86_ZMM1), -+ SMPL_REG8_EXT(ZMM2, PERF_REG_X86_ZMM2), -+ SMPL_REG8_EXT(ZMM3, PERF_REG_X86_ZMM3), -+ SMPL_REG8_EXT(ZMM4, PERF_REG_X86_ZMM4), -+ SMPL_REG8_EXT(ZMM5, PERF_REG_X86_ZMM5), -+ SMPL_REG8_EXT(ZMM6, PERF_REG_X86_ZMM6), -+ SMPL_REG8_EXT(ZMM7, PERF_REG_X86_ZMM7), -+ SMPL_REG8_EXT(ZMM8, PERF_REG_X86_ZMM8), -+ SMPL_REG8_EXT(ZMM9, PERF_REG_X86_ZMM9), -+ SMPL_REG8_EXT(ZMM10, PERF_REG_X86_ZMM10), -+ SMPL_REG8_EXT(ZMM11, PERF_REG_X86_ZMM11), -+ SMPL_REG8_EXT(ZMM12, PERF_REG_X86_ZMM12), -+ SMPL_REG8_EXT(ZMM13, PERF_REG_X86_ZMM13), -+ SMPL_REG8_EXT(ZMM14, PERF_REG_X86_ZMM14), -+ SMPL_REG8_EXT(ZMM15, PERF_REG_X86_ZMM15), -+ SMPL_REG8_EXT(ZMM16, PERF_REG_X86_ZMM16), -+ SMPL_REG8_EXT(ZMM17, PERF_REG_X86_ZMM17), -+ SMPL_REG8_EXT(ZMM18, PERF_REG_X86_ZMM18), -+ SMPL_REG8_EXT(ZMM19, PERF_REG_X86_ZMM19), -+ SMPL_REG8_EXT(ZMM20, PERF_REG_X86_ZMM20), -+ SMPL_REG8_EXT(ZMM21, PERF_REG_X86_ZMM21), -+ SMPL_REG8_EXT(ZMM22, PERF_REG_X86_ZMM22), -+ SMPL_REG8_EXT(ZMM23, PERF_REG_X86_ZMM23), -+ SMPL_REG8_EXT(ZMM24, PERF_REG_X86_ZMM24), -+ SMPL_REG8_EXT(ZMM25, PERF_REG_X86_ZMM25), -+ SMPL_REG8_EXT(ZMM26, PERF_REG_X86_ZMM26), -+ SMPL_REG8_EXT(ZMM27, PERF_REG_X86_ZMM27), -+ SMPL_REG8_EXT(ZMM28, PERF_REG_X86_ZMM28), -+ SMPL_REG8_EXT(ZMM29, PERF_REG_X86_ZMM29), -+ SMPL_REG8_EXT(ZMM30, PERF_REG_X86_ZMM30), -+ SMPL_REG8_EXT(ZMM31, PERF_REG_X86_ZMM31), -+ -+ SMPL_REG_EXT(OPMASK0, PERF_REG_X86_OPMASK0), -+ SMPL_REG_EXT(OPMASK1, PERF_REG_X86_OPMASK1), -+ SMPL_REG_EXT(OPMASK2, PERF_REG_X86_OPMASK2), -+ SMPL_REG_EXT(OPMASK3, PERF_REG_X86_OPMASK3), -+ SMPL_REG_EXT(OPMASK4, PERF_REG_X86_OPMASK4), -+ SMPL_REG_EXT(OPMASK5, PERF_REG_X86_OPMASK5), -+ SMPL_REG_EXT(OPMASK6, PERF_REG_X86_OPMASK6), -+ SMPL_REG_EXT(OPMASK7, PERF_REG_X86_OPMASK7), -+ - SMPL_REG_END - }; - -@@ -283,13 +343,59 @@ const struct sample_reg *arch__sample_reg_masks(void) - return sample_reg_masks; - } - --void arch__intr_reg_mask(unsigned long *mask) -+static void check_ext2_regs_mask(struct perf_event_attr *attr, bool user, -+ int idx, u64 fmask, unsigned long *mask) -+{ -+ u64 reg_mask[PERF_SAMPLE_ARRAY_SIZE] = { 0 }; -+ int fd; -+ -+ if (user) { -+ attr->sample_regs_user = 0; -+ attr->sample_regs_user_ext[idx] = fmask; -+ } else { -+ attr->sample_regs_intr = 0; -+ attr->sample_regs_intr_ext[idx] = fmask; -+ } -+ -+ /* reg_mask[] includes sample_regs_intr regs, so index need add 1. */ -+ reg_mask[idx + 1] = fmask; -+ -+ fd = sys_perf_event_open(attr, 0, -1, -1, 0); -+ if (fd != -1) { -+ close(fd); -+ bitmap_or(mask, mask, (unsigned long *)reg_mask, -+ PERF_SAMPLE_REGS_NUM); -+ } -+} -+ -+#define PERF_REG_EXTENDED_YMM_MASK GENMASK_ULL(63, 0) -+#define PERF_REG_EXTENDED_ZMM_MASK GENMASK_ULL(63, 0) -+#define PERF_REG_EXTENDED_OPMASK_MASK GENMASK_ULL(7, 0) -+ -+static void get_ext2_regs_mask(struct perf_event_attr *attr, bool user, -+ unsigned long *mask) -+{ -+ event_attr_init(attr); -+ -+ /* Check YMM regs, bits 128 ~ 191. */ -+ check_ext2_regs_mask(attr, user, 1, PERF_REG_EXTENDED_YMM_MASK, mask); -+ /* Check ZMM 0-7 regs, bits 192 ~ 255. */ -+ check_ext2_regs_mask(attr, user, 2, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check ZMM 8-15 regs, bits 256 ~ 319. */ -+ check_ext2_regs_mask(attr, user, 3, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check ZMM 16-23 regs, bits 320 ~ 383. */ -+ check_ext2_regs_mask(attr, user, 4, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check ZMM 16-23 regs, bits 384 ~ 447. */ -+ check_ext2_regs_mask(attr, user, 5, PERF_REG_EXTENDED_ZMM_MASK, mask); -+ /* Check OPMASK regs, bits 448 ~ 455. */ -+ check_ext2_regs_mask(attr, user, 6, PERF_REG_EXTENDED_OPMASK_MASK, mask); -+} -+ -+static void arch__get_reg_mask(unsigned long *mask, bool user) - { - struct perf_event_attr attr = { - .type = PERF_TYPE_HARDWARE, - .config = PERF_COUNT_HW_CPU_CYCLES, -- .sample_type = PERF_SAMPLE_REGS_INTR, -- .sample_regs_intr = PERF_REG_EXTENDED_MASK, - .precise_ip = 1, - .disabled = 1, - .exclude_kernel = 1, -@@ -298,6 +404,14 @@ void arch__intr_reg_mask(unsigned long *mask) - - *(u64 *)mask = PERF_REGS_MASK; - -+ if (user) { -+ attr.sample_type = PERF_SAMPLE_REGS_USER; -+ attr.sample_regs_user = PERF_REG_EXTENDED_MASK; -+ } else { -+ attr.sample_type = PERF_SAMPLE_REGS_INTR; -+ attr.sample_regs_intr = PERF_REG_EXTENDED_MASK; -+ } -+ - /* - * In an unnamed union, init it here to build on older gcc versions - */ -@@ -325,9 +439,16 @@ void arch__intr_reg_mask(unsigned long *mask) - close(fd); - *(u64 *)mask = PERF_REG_EXTENDED_MASK | PERF_REGS_MASK; - } -+ -+ get_ext2_regs_mask(&attr, user, mask); -+} -+ -+void arch__intr_reg_mask(unsigned long *mask) -+{ -+ arch__get_reg_mask(mask, false); - } - - void arch__user_reg_mask(unsigned long *mask) - { -- *(uint64_t *)mask = PERF_REGS_MASK; -+ arch__get_reg_mask(mask, true); - } -diff --git a/tools/perf/util/perf-regs-arch/perf_regs_x86.c b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -index c0e95215b577..eb1e3d716f27 100644 ---- a/tools/perf/util/perf-regs-arch/perf_regs_x86.c -+++ b/tools/perf/util/perf-regs-arch/perf_regs_x86.c -@@ -78,6 +78,88 @@ const char *__perf_reg_name_x86(int id) - XMM(14) - XMM(15) - #undef XMM -+ -+#define YMM(x) \ -+ case PERF_REG_X86_YMM ## x: \ -+ case PERF_REG_X86_YMM ## x + 1: \ -+ case PERF_REG_X86_YMM ## x + 2: \ -+ case PERF_REG_X86_YMM ## x + 3: \ -+ return "YMM" #x; -+ YMM(0) -+ YMM(1) -+ YMM(2) -+ YMM(3) -+ YMM(4) -+ YMM(5) -+ YMM(6) -+ YMM(7) -+ YMM(8) -+ YMM(9) -+ YMM(10) -+ YMM(11) -+ YMM(12) -+ YMM(13) -+ YMM(14) -+ YMM(15) -+#undef YMM -+ -+#define ZMM(x) \ -+ case PERF_REG_X86_ZMM ## x: \ -+ case PERF_REG_X86_ZMM ## x + 1: \ -+ case PERF_REG_X86_ZMM ## x + 2: \ -+ case PERF_REG_X86_ZMM ## x + 3: \ -+ case PERF_REG_X86_ZMM ## x + 4: \ -+ case PERF_REG_X86_ZMM ## x + 5: \ -+ case PERF_REG_X86_ZMM ## x + 6: \ -+ case PERF_REG_X86_ZMM ## x + 7: \ -+ return "ZMM" #x; -+ ZMM(0) -+ ZMM(1) -+ ZMM(2) -+ ZMM(3) -+ ZMM(4) -+ ZMM(5) -+ ZMM(6) -+ ZMM(7) -+ ZMM(8) -+ ZMM(9) -+ ZMM(10) -+ ZMM(11) -+ ZMM(12) -+ ZMM(13) -+ ZMM(14) -+ ZMM(15) -+ ZMM(16) -+ ZMM(17) -+ ZMM(18) -+ ZMM(19) -+ ZMM(20) -+ ZMM(21) -+ ZMM(22) -+ ZMM(23) -+ ZMM(24) -+ ZMM(25) -+ ZMM(26) -+ ZMM(27) -+ ZMM(28) -+ ZMM(29) -+ ZMM(30) -+ ZMM(31) -+#undef ZMM -+ -+#define OPMASK(x) \ -+ case PERF_REG_X86_OPMASK ## x: \ -+ return "opmask" #x; -+ -+ OPMASK(0) -+ OPMASK(1) -+ OPMASK(2) -+ OPMASK(3) -+ OPMASK(4) -+ OPMASK(5) -+ OPMASK(6) -+ OPMASK(7) -+#undef OPMASK - default: - return NULL; - } --- -2.43.0 - diff --git a/SPECS/kernel/0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi b/SPECS/kernel/0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi new file mode 100644 index 000000000..62208a1e8 --- /dev/null +++ b/SPECS/kernel/0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi @@ -0,0 +1,77 @@ +From e90bc85022702e9787922ed6135654f84e58b467 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Tue, 8 Jul 2025 04:37:17 -0700 +Subject: [PATCH 27/44] task_stack.h: Add a new helper + task_empty_stack_pointer() + +Introduce a new helper function, task_empty_stack_pointer(), to replace + + (unsigned long)task_stack_page(current) + THREAD_SIZE + +Sugguested-by: Sean Christopherson +Signed-off-by: Xin Li (Intel) +--- + arch/x86/include/asm/fred.h | 2 +- + arch/x86/kernel/dumpstack.c | 2 +- + arch/x86/kvm/vmx/vmx.c | 2 +- + include/linux/sched/task_stack.h | 5 +++++ + 4 files changed, 8 insertions(+), 3 deletions(-) + +diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h +index 12b34d5b2953e..263fd5267f711 100644 +--- a/arch/x86/include/asm/fred.h ++++ b/arch/x86/include/asm/fred.h +@@ -98,7 +98,7 @@ static __always_inline void fred_sync_rsp0(unsigned long rsp0) + + static __always_inline void fred_update_rsp0(void) + { +- unsigned long rsp0 = (unsigned long) task_stack_page(current) + THREAD_SIZE; ++ unsigned long rsp0 = (unsigned long)task_empty_stack_pointer(current); + + if (cpu_feature_enabled(X86_FEATURE_FRED) && (__this_cpu_read(fred_rsp0) != rsp0)) { + wrmsrns(MSR_IA32_FRED_RSP0, rsp0); +diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c +index 71ee20102a8af..18c19e6fd960c 100644 +--- a/arch/x86/kernel/dumpstack.c ++++ b/arch/x86/kernel/dumpstack.c +@@ -31,7 +31,7 @@ bool noinstr in_task_stack(unsigned long *stack, struct task_struct *task, + struct stack_info *info) + { + unsigned long *begin = task_stack_page(task); +- unsigned long *end = task_stack_page(task) + THREAD_SIZE; ++ unsigned long *end = task_empty_stack_pointer(task); + + if (stack < begin || stack >= end) + return false; +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 823ad3300f656..e845e56b12c4c 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -6578,7 +6578,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) + "RSP2=0x%016llx, RSP3=0x%016llx\n", + vmcs_read64(HOST_IA32_FRED_CONFIG), + vmcs_read64(HOST_IA32_FRED_STKLVLS), +- (unsigned long)task_stack_page(current) + THREAD_SIZE, ++ (unsigned long)task_empty_stack_pointer(current), + vmcs_read64(HOST_IA32_FRED_RSP1), + vmcs_read64(HOST_IA32_FRED_RSP2), + vmcs_read64(HOST_IA32_FRED_RSP3)); +diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h +index 1fab7e9043a3c..43050ec1012b8 100644 +--- a/include/linux/sched/task_stack.h ++++ b/include/linux/sched/task_stack.h +@@ -23,6 +23,11 @@ static __always_inline void *task_stack_page(const struct task_struct *task) + return task->stack; + } + ++static __always_inline void *task_empty_stack_pointer(const struct task_struct *task) ++{ ++ return (void *)((unsigned long)task_stack_page(task) + THREAD_SIZE); ++} ++ + #define setup_thread_stack(new,old) do { } while(0) + + static __always_inline unsigned long *end_of_stack(const struct task_struct *task) +-- +2.43.0 + diff --git a/SPECS/kernel/0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf b/SPECS/kernel/0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf deleted file mode 100644 index fefa96214..000000000 --- a/SPECS/kernel/0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf +++ /dev/null @@ -1,93 +0,0 @@ -From df18ccaa75adc4705ffede3ef28f269bee179308 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Tue, 29 Oct 2024 12:09:28 +0000 -Subject: [PATCH 028/100] perf tools/tests: Add vector registers PEBS sampling - test - -Current adaptive PEBS supports to capture some vector registers like XMM -register, and arch-PEBS supports to capture wider vector registers like -YMM and ZMM registers. This patch adds a perf test case to verify these -vector registers can be captured correctly. - -Suggested-by: Kan Liang -Signed-off-by: Dapeng Mi ---- - tools/perf/tests/shell/record.sh | 55 ++++++++++++++++++++++++++++++++ - 1 file changed, 55 insertions(+) - -diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh -index b1ad24fb3b33..6ef799128793 100755 ---- a/tools/perf/tests/shell/record.sh -+++ b/tools/perf/tests/shell/record.sh -@@ -120,6 +120,60 @@ test_register_capture() { - echo "Register capture test [Success]" - } - -+test_vec_register_capture() { -+ echo "Vector register capture test" -+ if ! perf record -o /dev/null --quiet -e instructions:p true 2> /dev/null -+ then -+ echo "Vector register capture test [Skipped missing event]" -+ return -+ fi -+ if ! perf record --intr-regs=\? 2>&1 | grep -q 'XMM0' -+ then -+ echo "Vector register capture test [Skipped missing XMM registers]" -+ return -+ fi -+ if ! perf record -o - --intr-regs=xmm0 -e instructions:p \ -+ -c 100000 ${testprog} 2> /dev/null \ -+ | perf script -F ip,sym,iregs -i - 2> /dev/null \ -+ | grep -q "XMM0:" -+ then -+ echo "Vector register capture test [Failed missing XMM output]" -+ err=1 -+ return -+ fi -+ echo "Vector registe (XMM) capture test [Success]" -+ if ! perf record --intr-regs=\? 2>&1 | grep -q 'YMM0' -+ then -+ echo "Vector register capture test [Skipped missing YMM registers]" -+ return -+ fi -+ if ! perf record -o - --intr-regs=ymm0 -e instructions:p \ -+ -c 100000 ${testprog} 2> /dev/null \ -+ | perf script -F ip,sym,iregs -i - 2> /dev/null \ -+ | grep -q "YMM0:" -+ then -+ echo "Vector register capture test [Failed missing YMM output]" -+ err=1 -+ return -+ fi -+ echo "Vector registe (YMM) capture test [Success]" -+ if ! perf record --intr-regs=\? 2>&1 | grep -q 'ZMM0' -+ then -+ echo "Vector register capture test [Skipped missing ZMM registers]" -+ return -+ fi -+ if ! perf record -o - --intr-regs=zmm0 -e instructions:p \ -+ -c 100000 ${testprog} 2> /dev/null \ -+ | perf script -F ip,sym,iregs -i - 2> /dev/null \ -+ | grep -q "ZMM0:" -+ then -+ echo "Vector register capture test [Failed missing ZMM output]" -+ err=1 -+ return -+ fi -+ echo "Vector register (ZMM) capture test [Success]" -+} -+ - test_system_wide() { - echo "Basic --system-wide mode test" - if ! perf record -aB --synth=no -o "${perfdata}" ${testprog} 2> /dev/null -@@ -395,6 +449,7 @@ fi - - test_per_thread - test_register_capture -+test_vec_register_capture - test_system_wide - test_workload - test_branch_counter --- -2.43.0 - diff --git a/SPECS/kernel/0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi b/SPECS/kernel/0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi deleted file mode 100644 index d9e5de974..000000000 --- a/SPECS/kernel/0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi +++ /dev/null @@ -1,77 +0,0 @@ -From a965c8232d9c74ee5c7340c206fc9776ce1a7961 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Tue, 8 Jul 2025 04:37:17 -0700 -Subject: [PATCH 28/44] task_stack.h: Add a new helper - task_empty_stack_pointer() - -Introduce a new helper function, task_empty_stack_pointer(), to replace - - (unsigned long)task_stack_page(current) + THREAD_SIZE - -Sugguested-by: Sean Christopherson -Signed-off-by: Xin Li (Intel) ---- - arch/x86/include/asm/fred.h | 2 +- - arch/x86/kernel/dumpstack.c | 2 +- - arch/x86/kvm/vmx/vmx.c | 2 +- - include/linux/sched/task_stack.h | 5 +++++ - 4 files changed, 8 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h -index 12b34d5b2953..263fd5267f71 100644 ---- a/arch/x86/include/asm/fred.h -+++ b/arch/x86/include/asm/fred.h -@@ -98,7 +98,7 @@ static __always_inline void fred_sync_rsp0(unsigned long rsp0) - - static __always_inline void fred_update_rsp0(void) - { -- unsigned long rsp0 = (unsigned long) task_stack_page(current) + THREAD_SIZE; -+ unsigned long rsp0 = (unsigned long)task_empty_stack_pointer(current); - - if (cpu_feature_enabled(X86_FEATURE_FRED) && (__this_cpu_read(fred_rsp0) != rsp0)) { - wrmsrns(MSR_IA32_FRED_RSP0, rsp0); -diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c -index 71ee20102a8a..18c19e6fd960 100644 ---- a/arch/x86/kernel/dumpstack.c -+++ b/arch/x86/kernel/dumpstack.c -@@ -31,7 +31,7 @@ bool noinstr in_task_stack(unsigned long *stack, struct task_struct *task, - struct stack_info *info) - { - unsigned long *begin = task_stack_page(task); -- unsigned long *end = task_stack_page(task) + THREAD_SIZE; -+ unsigned long *end = task_empty_stack_pointer(task); - - if (stack < begin || stack >= end) - return false; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index d04085f2d4a2..090b49679925 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -6607,7 +6607,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) - "RSP2=0x%016llx, RSP3=0x%016llx\n", - vmcs_read64(HOST_IA32_FRED_CONFIG), - vmcs_read64(HOST_IA32_FRED_STKLVLS), -- (unsigned long)task_stack_page(current) + THREAD_SIZE, -+ (unsigned long)task_empty_stack_pointer(current), - vmcs_read64(HOST_IA32_FRED_RSP1), - vmcs_read64(HOST_IA32_FRED_RSP2), - vmcs_read64(HOST_IA32_FRED_RSP3)); -diff --git a/include/linux/sched/task_stack.h b/include/linux/sched/task_stack.h -index 1fab7e9043a3..43050ec1012b 100644 ---- a/include/linux/sched/task_stack.h -+++ b/include/linux/sched/task_stack.h -@@ -23,6 +23,11 @@ static __always_inline void *task_stack_page(const struct task_struct *task) - return task->stack; - } - -+static __always_inline void *task_empty_stack_pointer(const struct task_struct *task) -+{ -+ return (void *)((unsigned long)task_stack_page(task) + THREAD_SIZE); -+} -+ - #define setup_thread_stack(new,old) do { } while(0) - - static __always_inline unsigned long *end_of_stack(const struct task_struct *task) --- -2.43.0 - diff --git a/SPECS/kernel/0028-x86-fred-Allow-variable-sized-event-frame.nmi b/SPECS/kernel/0028-x86-fred-Allow-variable-sized-event-frame.nmi new file mode 100644 index 000000000..e1e0d465b --- /dev/null +++ b/SPECS/kernel/0028-x86-fred-Allow-variable-sized-event-frame.nmi @@ -0,0 +1,191 @@ +From c9ceb2fcb58c3aad046d17899910abb540a5f703 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Mon, 17 Mar 2025 20:46:29 -0700 +Subject: [PATCH 28/44] x86/fred: Allow variable-sized event frame + +A FRED event frame could contain different amount of information for +different event types, or perhaps even for different instances of the +same event type. Thus the size of an event frame pushed by a FRED CPU +is not fixed and the address of the pt_regs structure that is used to +save a user level context of current task is not at a fixed offset +from top of current task kernel stack. + +Add a new field named 'user_pt_regs' in the thread_info structure to +save the address of user level context pt_regs structure, thus to +eliminate the need of any advance information of event frame size +and allow a FRED CPU to push variable-sized event frame. + +For IDT user level event delivery, a pt_regs structure is pushed by +hardware and software _always_ at a fixed offset from top of current +task kernel stack, so simply initialize user_pt_regs to point to the +pt_regs structure no matter whether one is pushed or not. + +While for FRED user level event delivery, user_pt_regs is updated with +a pt_regs structure pointer generated in asm_fred_entrypoint_user(). + +Suggested-by: H. Peter Anvin (Intel) +Signed-off-by: Xin Li (Intel) +--- + +Change in v3A: +* Add declaration for __top_init_kernel_stack[] (Intel lkp). + +Change in v3: +* Replace "(struct pt_regs *)TOP_OF_INIT_STACK - 1" with + (struct pt_regs *)__top_init_kernel_stack (Brian Gerst). +--- + arch/x86/entry/entry_fred.c | 10 ++++++++++ + arch/x86/include/asm/processor.h | 12 ++++++------ + arch/x86/include/asm/thread_info.h | 11 ++++++++--- + arch/x86/kernel/process.c | 22 ++++++++++++++++++++++ + include/linux/thread_info.h | 1 + + kernel/fork.c | 6 ++++++ + 6 files changed, 53 insertions(+), 9 deletions(-) + +diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c +index f004a4dc74c2d..a5f5bdd16ad8d 100644 +--- a/arch/x86/entry/entry_fred.c ++++ b/arch/x86/entry/entry_fred.c +@@ -228,6 +228,16 @@ __visible noinstr void fred_entry_from_user(struct pt_regs *regs) + /* Invalidate orig_ax so that syscall_get_nr() works correctly */ + regs->orig_ax = -1; + ++ /* ++ * A FRED event frame could contain different amount of information ++ * for different event types, or perhaps even for different instances ++ * of the same event type. Thus the size of an event frame pushed by ++ * a FRED CPU is not fixed and the address of the pt_regs structure ++ * that is used to save a user level context of current task is not ++ * at a fixed offset from top of current task stack. ++ */ ++ current->thread_info.user_pt_regs = regs; ++ + switch (regs->fred_ss.type) { + case EVENT_TYPE_EXTINT: + return fred_extint(regs); +diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h +index a24c7805acdb5..b7893536099a9 100644 +--- a/arch/x86/include/asm/processor.h ++++ b/arch/x86/include/asm/processor.h +@@ -645,12 +645,12 @@ static __always_inline void prefetchw(const void *x) + + #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) + +-#define task_pt_regs(task) \ +-({ \ +- unsigned long __ptr = (unsigned long)task_stack_page(task); \ +- __ptr += THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; \ +- ((struct pt_regs *)__ptr) - 1; \ +-}) ++/* ++ * Note, this can't be converted to an inline function as this header ++ * file defines 'struct thread_struct' which is used in the task_struct ++ * structure definition. ++ */ ++#define task_pt_regs(task) ((task)->thread_info.user_pt_regs) + + #ifdef CONFIG_X86_32 + #define INIT_THREAD { \ +diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h +index e71e0e8362ed8..0bdd2335ecd22 100644 +--- a/arch/x86/include/asm/thread_info.h ++++ b/arch/x86/include/asm/thread_info.h +@@ -56,6 +56,7 @@ + */ + #ifndef __ASSEMBLER__ + struct task_struct; ++struct pt_regs; + #include + #include + +@@ -66,11 +67,15 @@ struct thread_info { + #ifdef CONFIG_SMP + u32 cpu; /* current CPU */ + #endif ++ struct pt_regs *user_pt_regs; + }; + +-#define INIT_THREAD_INFO(tsk) \ +-{ \ +- .flags = 0, \ ++extern unsigned long __top_init_kernel_stack[]; ++ ++#define INIT_THREAD_INFO(tsk) \ ++{ \ ++ .flags = 0, \ ++ .user_pt_regs = (struct pt_regs *)__top_init_kernel_stack, \ + } + + #else /* !__ASSEMBLER__ */ +diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c +index 4c718f8adc592..d456c3d9da965 100644 +--- a/arch/x86/kernel/process.c ++++ b/arch/x86/kernel/process.c +@@ -114,6 +114,28 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) + return 0; + } + ++/* ++ * Initialize thread_info.user_pt_regs for IDT event delivery. ++ * ++ * For IDT user level event delivery, a pt_regs structure is pushed by both ++ * hardware and software and always resides at a fixed offset from top of ++ * current task kernel stack, thus thread_info.user_pt_regs is a per-task ++ * constant and NEVER changes after initialization. ++ * ++ * While for FRED user level event delivery, user_pt_regs is updated in ++ * fred_entry_from_user() immediately after user level event delivery. ++ * ++ * Note: thread_info.user_pt_regs of the init task is initialized at build ++ * time. ++ */ ++void arch_init_user_pt_regs(struct task_struct *tsk) ++{ ++ unsigned long init_sp = (unsigned long)task_empty_stack_pointer(tsk); ++ ++ init_sp -= TOP_OF_KERNEL_STACK_PADDING; ++ tsk->thread_info.user_pt_regs = (struct pt_regs *)init_sp - 1; ++} ++ + #ifdef CONFIG_X86_64 + void arch_release_task_struct(struct task_struct *tsk) + { +diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h +index dd925d84fa46c..f95d38ed04d7f 100644 +--- a/include/linux/thread_info.h ++++ b/include/linux/thread_info.h +@@ -225,6 +225,7 @@ void arch_task_cache_init(void); /* for CONFIG_SH */ + void arch_release_task_struct(struct task_struct *tsk); + int arch_dup_task_struct(struct task_struct *dst, + struct task_struct *src); ++void arch_init_user_pt_regs(struct task_struct *tsk); + + #endif /* __KERNEL__ */ + +diff --git a/kernel/fork.c b/kernel/fork.c +index 3da0f08615a95..962d48bea9f71 100644 +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -855,6 +855,10 @@ int __weak arch_dup_task_struct(struct task_struct *dst, + return 0; + } + ++void __weak arch_init_user_pt_regs(struct task_struct *tsk) ++{ ++} ++ + void set_task_stack_end_magic(struct task_struct *tsk) + { + unsigned long *stackend; +@@ -882,6 +886,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) + if (err) + goto free_tsk; + ++ arch_init_user_pt_regs(tsk); ++ + #ifdef CONFIG_THREAD_INFO_IN_TASK + refcount_set(&tsk->stack_refcount, 1); + #endif +-- +2.43.0 + diff --git a/SPECS/kernel/0029-iomap-allocate-s_dio_done_wq-for-async-reads-as-well.patch b/SPECS/kernel/0029-iomap-allocate-s_dio_done_wq-for-async-reads-as-well.patch deleted file mode 100644 index 971995d5d..000000000 --- a/SPECS/kernel/0029-iomap-allocate-s_dio_done_wq-for-async-reads-as-well.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 8c13b4db6deaca6496ea46e752512ed88b5941fc Mon Sep 17 00:00:00 2001 -From: Christoph Hellwig -Date: Mon, 24 Nov 2025 15:00:13 +0100 -Subject: [PATCH 29/51] iomap: allocate s_dio_done_wq for async reads as well - -Since commit 222f2c7c6d14 ("iomap: always run error completions in user -context"), read error completions are deferred to s_dio_done_wq. This -means the workqueue also needs to be allocated for async reads. - -Fixes: 222f2c7c6d14 ("iomap: always run error completions in user context") -Reported-by: syzbot+a2b9a4ed0d61b1efb3f5@syzkaller.appspotmail.com -Signed-off-by: Christoph Hellwig -Link: https://patch.msgid.link/20251124140013.902853-1-hch@lst.de -Tested-by: syzbot+a2b9a4ed0d61b1efb3f5@syzkaller.appspotmail.com -Reviewed-by: Dave Chinner -Reviewed-by: Darrick J. Wong -Signed-off-by: Christian Brauner ---- - fs/iomap/direct-io.c | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/fs/iomap/direct-io.c b/fs/iomap/direct-io.c -index 46aa85af13dc..d143831bb05c 100644 ---- a/fs/iomap/direct-io.c -+++ b/fs/iomap/direct-io.c -@@ -717,12 +717,12 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, - } - goto out_free_dio; - } -+ } - -- if (!wait_for_completion && !inode->i_sb->s_dio_done_wq) { -- ret = sb_init_dio_done_wq(inode->i_sb); -- if (ret < 0) -- goto out_free_dio; -- } -+ if (!wait_for_completion && !inode->i_sb->s_dio_done_wq) { -+ ret = sb_init_dio_done_wq(inode->i_sb); -+ if (ret < 0) -+ goto out_free_dio; - } - - inode_dio_begin(inode); --- -2.43.0 - diff --git a/SPECS/kernel/0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf b/SPECS/kernel/0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf deleted file mode 100644 index f873d51a2..000000000 --- a/SPECS/kernel/0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf +++ /dev/null @@ -1,29 +0,0 @@ -From 3338e742d86ce8f096e18376a7bbc6ecdf0b85ce Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Wed, 21 Aug 2024 08:28:53 +0000 -Subject: [PATCH 029/100] perf/x86/intel: Add PMU support for WildcatLake - -WildcatLake is a variant of PantherLake and shares same PMU features, -so directly reuse Pantherlake's code to enable PMU features for -WildcatLake. - -Signed-off-by: Dapeng Mi ---- - arch/x86/events/intel/core.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index 0ed9cb66b49e..b6ed211695ff 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -7956,6 +7956,7 @@ __init int intel_pmu_init(void) - break; - - case INTEL_PANTHERLAKE_L: -+ case INTEL_WILDCATLAKE_L: - pr_cont("Pantherlake Hybrid events, "); - name = "pantherlake_hybrid"; - goto lnl_common; --- -2.43.0 - diff --git a/SPECS/kernel/0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi b/SPECS/kernel/0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi new file mode 100644 index 000000000..017a81b2c --- /dev/null +++ b/SPECS/kernel/0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi @@ -0,0 +1,77 @@ +From 94caa6eca5e0c8c589f685694ec2bc9ad48d5469 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Mon, 17 Mar 2025 20:48:22 -0700 +Subject: [PATCH 29/44] x86: Remove the padding space at top of the init stack + +Because the owner of the init stack, init task, doesn't have any user +level context, there will NEVER be an actual pt_regs structure pushed +at top of the init stack. + +However a zeroed pt_regs structure is created at build time and kept +at top of the init stack for task_pt_regs() to function properly with +the init task in the same manner as a normal task with user level +context. + +Besides, task_pt_regs() no longer converts a fixed offset from top of +a task kernel stack to a pt_regs structure pointer, but rather returns +whatever in the thread_info.user_pt_regs field, which is initialized +at build time to '(struct pt_regs *)TOP_OF_INIT_STACK - 1' for the +init task. + +As a result, there is no point to reserve any padding space at top of +the init stack, so remove the padding space. + +Signed-off-by: Xin Li (Intel) +--- + arch/x86/include/asm/processor.h | 16 ++++++++++++++-- + arch/x86/kernel/vmlinux.lds.S | 7 +++++-- + 2 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h +index b7893536099a9..d7f438923d9c2 100644 +--- a/arch/x86/include/asm/processor.h ++++ b/arch/x86/include/asm/processor.h +@@ -640,8 +640,20 @@ static __always_inline void prefetchw(const void *x) + "m" (*(const char *)x)); + } + +-#define TOP_OF_INIT_STACK ((unsigned long)&init_stack + sizeof(init_stack) - \ +- TOP_OF_KERNEL_STACK_PADDING) ++extern unsigned long __end_init_stack[]; ++ ++/* ++ * No need to reserve extra padding space above the pt_regs structure ++ * at top of the init stack, because its owner init task doesn't have ++ * any user level context, thus there will NEVER be an actual pt_regs ++ * structure pushed at top of the init stack. ++ * ++ * However a zeroed pt_regs structure is created at build time and kept ++ * at top of the init stack for task_pt_regs() to function properly with ++ * the init task in the same manner as a normal task with user level ++ * context. ++ */ ++#define TOP_OF_INIT_STACK ((unsigned long)&__end_init_stack) + + #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) + +diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S +index d7af4a64c211b..3286c683d174c 100644 +--- a/arch/x86/kernel/vmlinux.lds.S ++++ b/arch/x86/kernel/vmlinux.lds.S +@@ -176,8 +176,11 @@ SECTIONS + /* init_task */ + INIT_TASK_DATA(THREAD_SIZE) + +- /* equivalent to task_pt_regs(&init_task) */ +- __top_init_kernel_stack = __end_init_stack - TOP_OF_KERNEL_STACK_PADDING - PTREGS_SIZE; ++ /* ++ * task_pt_regs(&init_task) is: ++ * '(struct pt_regs *)&__end_init_stack - 1' ++ */ ++ __top_init_kernel_stack = __end_init_stack - PTREGS_SIZE; + + #ifdef CONFIG_X86_32 + /* 32 bit has nosave before _edata */ +-- +2.43.0 + diff --git a/SPECS/kernel/0029-x86-fred-Allow-variable-sized-event-frame.nmi b/SPECS/kernel/0029-x86-fred-Allow-variable-sized-event-frame.nmi deleted file mode 100644 index 47b92153c..000000000 --- a/SPECS/kernel/0029-x86-fred-Allow-variable-sized-event-frame.nmi +++ /dev/null @@ -1,191 +0,0 @@ -From ff0b0040cf8eaf7226662dd875b6d987ea30f52d Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Mon, 17 Mar 2025 20:46:29 -0700 -Subject: [PATCH 29/44] x86/fred: Allow variable-sized event frame - -A FRED event frame could contain different amount of information for -different event types, or perhaps even for different instances of the -same event type. Thus the size of an event frame pushed by a FRED CPU -is not fixed and the address of the pt_regs structure that is used to -save a user level context of current task is not at a fixed offset -from top of current task kernel stack. - -Add a new field named 'user_pt_regs' in the thread_info structure to -save the address of user level context pt_regs structure, thus to -eliminate the need of any advance information of event frame size -and allow a FRED CPU to push variable-sized event frame. - -For IDT user level event delivery, a pt_regs structure is pushed by -hardware and software _always_ at a fixed offset from top of current -task kernel stack, so simply initialize user_pt_regs to point to the -pt_regs structure no matter whether one is pushed or not. - -While for FRED user level event delivery, user_pt_regs is updated with -a pt_regs structure pointer generated in asm_fred_entrypoint_user(). - -Suggested-by: H. Peter Anvin (Intel) -Signed-off-by: Xin Li (Intel) ---- - -Change in v3A: -* Add declaration for __top_init_kernel_stack[] (Intel lkp). - -Change in v3: -* Replace "(struct pt_regs *)TOP_OF_INIT_STACK - 1" with - (struct pt_regs *)__top_init_kernel_stack (Brian Gerst). ---- - arch/x86/entry/entry_fred.c | 10 ++++++++++ - arch/x86/include/asm/processor.h | 12 ++++++------ - arch/x86/include/asm/thread_info.h | 11 ++++++++--- - arch/x86/kernel/process.c | 22 ++++++++++++++++++++++ - include/linux/thread_info.h | 1 + - kernel/fork.c | 6 ++++++ - 6 files changed, 53 insertions(+), 9 deletions(-) - -diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c -index d80861a4cd00..f8847f2d6257 100644 ---- a/arch/x86/entry/entry_fred.c -+++ b/arch/x86/entry/entry_fred.c -@@ -229,6 +229,16 @@ __visible noinstr void fred_entry_from_user(struct pt_regs *regs) - /* Invalidate orig_ax so that syscall_get_nr() works correctly */ - regs->orig_ax = -1; - -+ /* -+ * A FRED event frame could contain different amount of information -+ * for different event types, or perhaps even for different instances -+ * of the same event type. Thus the size of an event frame pushed by -+ * a FRED CPU is not fixed and the address of the pt_regs structure -+ * that is used to save a user level context of current task is not -+ * at a fixed offset from top of current task stack. -+ */ -+ current->thread_info.user_pt_regs = regs; -+ - switch (regs->fred_ss.type) { - case EVENT_TYPE_EXTINT: - return fred_extint(regs); -diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h -index bde58f6510ac..769bffa0711e 100644 ---- a/arch/x86/include/asm/processor.h -+++ b/arch/x86/include/asm/processor.h -@@ -645,12 +645,12 @@ static __always_inline void prefetchw(const void *x) - - #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) - --#define task_pt_regs(task) \ --({ \ -- unsigned long __ptr = (unsigned long)task_stack_page(task); \ -- __ptr += THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; \ -- ((struct pt_regs *)__ptr) - 1; \ --}) -+/* -+ * Note, this can't be converted to an inline function as this header -+ * file defines 'struct thread_struct' which is used in the task_struct -+ * structure definition. -+ */ -+#define task_pt_regs(task) ((task)->thread_info.user_pt_regs) - - #ifdef CONFIG_X86_32 - #define INIT_THREAD { \ -diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h -index 9282465eea21..07c6a6a92c65 100644 ---- a/arch/x86/include/asm/thread_info.h -+++ b/arch/x86/include/asm/thread_info.h -@@ -56,6 +56,7 @@ - */ - #ifndef __ASSEMBLER__ - struct task_struct; -+struct pt_regs; - #include - #include - -@@ -66,11 +67,15 @@ struct thread_info { - #ifdef CONFIG_SMP - u32 cpu; /* current CPU */ - #endif -+ struct pt_regs *user_pt_regs; - }; - --#define INIT_THREAD_INFO(tsk) \ --{ \ -- .flags = 0, \ -+extern unsigned long __top_init_kernel_stack[]; -+ -+#define INIT_THREAD_INFO(tsk) \ -+{ \ -+ .flags = 0, \ -+ .user_pt_regs = (struct pt_regs *)__top_init_kernel_stack, \ - } - - #else /* !__ASSEMBLER__ */ -diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c -index 1b7960cf6eb0..94872f06458e 100644 ---- a/arch/x86/kernel/process.c -+++ b/arch/x86/kernel/process.c -@@ -104,6 +104,28 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) - return 0; - } - -+/* -+ * Initialize thread_info.user_pt_regs for IDT event delivery. -+ * -+ * For IDT user level event delivery, a pt_regs structure is pushed by both -+ * hardware and software and always resides at a fixed offset from top of -+ * current task kernel stack, thus thread_info.user_pt_regs is a per-task -+ * constant and NEVER changes after initialization. -+ * -+ * While for FRED user level event delivery, user_pt_regs is updated in -+ * fred_entry_from_user() immediately after user level event delivery. -+ * -+ * Note: thread_info.user_pt_regs of the init task is initialized at build -+ * time. -+ */ -+void arch_init_user_pt_regs(struct task_struct *tsk) -+{ -+ unsigned long init_sp = (unsigned long)task_empty_stack_pointer(tsk); -+ -+ init_sp -= TOP_OF_KERNEL_STACK_PADDING; -+ tsk->thread_info.user_pt_regs = (struct pt_regs *)init_sp - 1; -+} -+ - #ifdef CONFIG_X86_64 - void arch_release_task_struct(struct task_struct *tsk) - { -diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h -index dd925d84fa46..f95d38ed04d7 100644 ---- a/include/linux/thread_info.h -+++ b/include/linux/thread_info.h -@@ -225,6 +225,7 @@ void arch_task_cache_init(void); /* for CONFIG_SH */ - void arch_release_task_struct(struct task_struct *tsk); - int arch_dup_task_struct(struct task_struct *dst, - struct task_struct *src); -+void arch_init_user_pt_regs(struct task_struct *tsk); - - #endif /* __KERNEL__ */ - -diff --git a/kernel/fork.c b/kernel/fork.c -index af673856499d..0399720dd7b9 100644 ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -856,6 +856,10 @@ int __weak arch_dup_task_struct(struct task_struct *dst, - return 0; - } - -+void __weak arch_init_user_pt_regs(struct task_struct *tsk) -+{ -+} -+ - void set_task_stack_end_magic(struct task_struct *tsk) - { - unsigned long *stackend; -@@ -883,6 +887,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) - if (err) - goto free_tsk; - -+ arch_init_user_pt_regs(tsk); -+ - #ifdef CONFIG_THREAD_INFO_IN_TASK - refcount_set(&tsk->stack_refcount, 1); - #endif --- -2.43.0 - diff --git a/SPECS/kernel/0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi b/SPECS/kernel/0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi deleted file mode 100644 index b37d8e60b..000000000 --- a/SPECS/kernel/0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi +++ /dev/null @@ -1,77 +0,0 @@ -From 24b55d01c54cca5de5e1f6d696489736f1c903d6 Mon Sep 17 00:00:00 2001 -From: "Xin Li (Intel)" -Date: Mon, 17 Mar 2025 20:48:22 -0700 -Subject: [PATCH 30/44] x86: Remove the padding space at top of the init stack - -Because the owner of the init stack, init task, doesn't have any user -level context, there will NEVER be an actual pt_regs structure pushed -at top of the init stack. - -However a zeroed pt_regs structure is created at build time and kept -at top of the init stack for task_pt_regs() to function properly with -the init task in the same manner as a normal task with user level -context. - -Besides, task_pt_regs() no longer converts a fixed offset from top of -a task kernel stack to a pt_regs structure pointer, but rather returns -whatever in the thread_info.user_pt_regs field, which is initialized -at build time to '(struct pt_regs *)TOP_OF_INIT_STACK - 1' for the -init task. - -As a result, there is no point to reserve any padding space at top of -the init stack, so remove the padding space. - -Signed-off-by: Xin Li (Intel) ---- - arch/x86/include/asm/processor.h | 16 ++++++++++++++-- - arch/x86/kernel/vmlinux.lds.S | 7 +++++-- - 2 files changed, 19 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h -index 769bffa0711e..39c969f6b211 100644 ---- a/arch/x86/include/asm/processor.h -+++ b/arch/x86/include/asm/processor.h -@@ -640,8 +640,20 @@ static __always_inline void prefetchw(const void *x) - "m" (*(const char *)x)); - } - --#define TOP_OF_INIT_STACK ((unsigned long)&init_stack + sizeof(init_stack) - \ -- TOP_OF_KERNEL_STACK_PADDING) -+extern unsigned long __end_init_stack[]; -+ -+/* -+ * No need to reserve extra padding space above the pt_regs structure -+ * at top of the init stack, because its owner init task doesn't have -+ * any user level context, thus there will NEVER be an actual pt_regs -+ * structure pushed at top of the init stack. -+ * -+ * However a zeroed pt_regs structure is created at build time and kept -+ * at top of the init stack for task_pt_regs() to function properly with -+ * the init task in the same manner as a normal task with user level -+ * context. -+ */ -+#define TOP_OF_INIT_STACK ((unsigned long)&__end_init_stack) - - #define task_top_of_stack(task) ((unsigned long)(task_pt_regs(task) + 1)) - -diff --git a/arch/x86/kernel/vmlinux.lds.S b/arch/x86/kernel/vmlinux.lds.S -index 4fa0be732af1..0ca5bcffc6ed 100644 ---- a/arch/x86/kernel/vmlinux.lds.S -+++ b/arch/x86/kernel/vmlinux.lds.S -@@ -181,8 +181,11 @@ SECTIONS - /* init_task */ - INIT_TASK_DATA(THREAD_SIZE) - -- /* equivalent to task_pt_regs(&init_task) */ -- __top_init_kernel_stack = __end_init_stack - TOP_OF_KERNEL_STACK_PADDING - PTREGS_SIZE; -+ /* -+ * task_pt_regs(&init_task) is: -+ * '(struct pt_regs *)&__end_init_stack - 1' -+ */ -+ __top_init_kernel_stack = __end_init_stack - PTREGS_SIZE; - - #ifdef CONFIG_X86_32 - /* 32 bit has nosave before _edata */ --- -2.43.0 - diff --git a/SPECS/kernel/0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi b/SPECS/kernel/0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi new file mode 100644 index 000000000..4e739f197 --- /dev/null +++ b/SPECS/kernel/0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi @@ -0,0 +1,106 @@ +From a9e1f47b9dcac0d8c774e54b75d928a0d78267e4 Mon Sep 17 00:00:00 2001 +From: Sean Christopherson +Date: Wed, 14 May 2025 07:07:55 -0700 +Subject: [PATCH 30/44] x86/fred: Provide separate IRQ vs. NMI wrappers for + entry from KVM + +Provide separate wrappers for forwarding IRQs vs NMIs from KVM in +anticipation of adding support for NMI source reporting, which will add +an NMI-only parameter, i.e. will further pollute the current API with a +param that is a hardcoded for one of the two call sites. + +Opportunistically tag the non-FRED NMI wrapper __always_inline, as the +compiler could theoretically generate a function call and trigger and a +(completely benign) "leaving noinstr" warning. + +Signed-off-by: Sean Christopherson +Signed-off-by: Sohil Mehta +--- +v7: New patch +--- + arch/x86/include/asm/fred.h | 30 ++++++++++++++++++++++-------- + arch/x86/kvm/vmx/vmx.c | 4 ++-- + 2 files changed, 24 insertions(+), 10 deletions(-) + +diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h +index 263fd5267f711..15d1c8e958378 100644 +--- a/arch/x86/include/asm/fred.h ++++ b/arch/x86/include/asm/fred.h +@@ -9,6 +9,7 @@ + #include + + #include ++#include + #include + #include + +@@ -71,15 +72,27 @@ __visible void fred_entry_from_user(struct pt_regs *regs); + __visible void fred_entry_from_kernel(struct pt_regs *regs); + __visible void __fred_entry_from_kvm(struct pt_regs *regs); + +-/* Can be called from noinstr code, thus __always_inline */ +-static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) ++/* Must be called from noinstr code, thus __always_inline */ ++static __always_inline void fred_nmi_from_kvm(void) + { + struct fred_ss ss = { +- .ss =__KERNEL_DS, +- .type = type, +- .vector = vector, +- .nmi = type == EVENT_TYPE_NMI, +- .lm = 1, ++ .ss = __KERNEL_DS, ++ .type = EVENT_TYPE_NMI, ++ .vector = NMI_VECTOR, ++ .nmi = true, ++ .lm = 1, ++ }; ++ ++ asm_fred_entry_from_kvm(ss); ++} ++ ++static inline void fred_irq_from_kvm(unsigned int vector) ++{ ++ struct fred_ss ss = { ++ .ss = __KERNEL_DS, ++ .type = EVENT_TYPE_EXTINT, ++ .vector = vector, ++ .lm = 1, + }; + + asm_fred_entry_from_kvm(ss); +@@ -110,7 +123,8 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret + static inline void cpu_init_fred_exceptions(void) { } + static inline void cpu_init_fred_rsps(void) { } + static inline void fred_complete_exception_setup(void) { } +-static inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { } ++static __always_inline void fred_nmi_from_kvm(void) { } ++static inline void fred_irq_from_kvm(unsigned int vector) { } + static inline void fred_sync_rsp0(unsigned long rsp0) { } + static inline void fred_update_rsp0(void) { } + #endif /* CONFIG_X86_FRED */ +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index e845e56b12c4c..d37251f4e8b2d 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7235,7 +7235,7 @@ static void handle_external_interrupt_irqoff(struct kvm_vcpu *vcpu, + */ + kvm_before_interrupt(vcpu, KVM_HANDLING_IRQ); + if (IS_ENABLED(CONFIG_X86_FRED)) +- fred_entry_from_kvm(EVENT_TYPE_EXTINT, vector); ++ fred_irq_from_kvm(vector); + else + vmx_do_interrupt_irqoff(gate_offset((gate_desc *)host_idt_base + vector)); + kvm_after_interrupt(vcpu); +@@ -7521,7 +7521,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) + + kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); + if (cpu_feature_enabled(X86_FEATURE_FRED)) +- fred_entry_from_kvm(EVENT_TYPE_NMI, NMI_VECTOR); ++ fred_nmi_from_kvm(); + else + vmx_do_nmi_irqoff(); + kvm_after_interrupt(vcpu); +-- +2.43.0 + diff --git a/SPECS/kernel/0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf b/SPECS/kernel/0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf deleted file mode 100644 index 039723391..000000000 --- a/SPECS/kernel/0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf +++ /dev/null @@ -1,35 +0,0 @@ -From ba4e183fa2975441b9538062dae5cd8b4476ce9f Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Wed, 17 Aug 2022 05:31:27 -0700 -Subject: [PATCH 031/100] perf evsel: Update the hint for the usage of the load - latency event - -The current message is not providing enough information. It's hard for -a user to figure out what's the auxiliary event. - -Adding the event name of the auxiliary event in the hint. The user can -simply cut & paste. - -Suggested-by: Stephane Eranian -Signed-off-by: Kan Liang ---- - tools/perf/util/evsel.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c -index c89eeff51d28..0c516c8f2d4f 100644 ---- a/tools/perf/util/evsel.c -+++ b/tools/perf/util/evsel.c -@@ -3866,7 +3866,8 @@ int evsel__open_strerror(struct evsel *evsel, struct target *target, - break; - case ENODATA: - return scnprintf(msg, size, "Cannot collect data source with the load latency event alone. " -- "Please add an auxiliary event in front of the load latency event."); -+ "Please add an auxiliary event in front of the load latency event. " -+ "For example, -e {mem-loads-aux,%s}.", evsel__name(evsel)); - default: - break; - } --- -2.43.0 - diff --git a/SPECS/kernel/0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi b/SPECS/kernel/0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi new file mode 100644 index 000000000..cdb0269e9 --- /dev/null +++ b/SPECS/kernel/0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi @@ -0,0 +1,113 @@ +From ec499a1323d06e705bd45308dcd03c469be29d34 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 9 Jun 2025 12:40:04 -0700 +Subject: [PATCH 31/44] x86/fred: Pass event data to the NMI entry point from + KVM + +Extend the FRED NMI entry point from KVM to take an extra argument to +allow KVM to invoke the FRED event dispatch framework with event data. + +This API is used to pass the NMI-source bitmap for NMI-induced VM exits. +Read the VMCS exit qualification field to get the NMI-source information +and store it as event data precisely in the format expected by the FRED +event framework. + +Read the VMCS exit qualification unconditionally since almost all +upcoming CPUs are expected to enable FRED and NMI-source together. In +the rare case that NMI-source isn't enabled, the extra VMREAD would be +harmless since the exit qualification is expected to be zero. + +Suggested-by: Sean Christopherson +Originally-by: Zeng Guang +Signed-off-by: Sohil Mehta +--- +v7: Pass the event data from KVM only for NMI. (Sean) + +v6: No change + +v5: Read the VMCS exit qualification unconditionally. (Sean) + Combine related patches into one. +--- + arch/x86/entry/entry_64_fred.S | 2 +- + arch/x86/include/asm/fred.h | 11 ++++++----- + arch/x86/kvm/vmx/vmx.c | 2 +- + 3 files changed, 8 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S +index fafbd3e68cb87..14b385551012f 100644 +--- a/arch/x86/entry/entry_64_fred.S ++++ b/arch/x86/entry/entry_64_fred.S +@@ -93,7 +93,7 @@ SYM_FUNC_START(asm_fred_entry_from_kvm) + * +--------+-----------------+ + */ + push $0 /* Reserved, must be 0 */ +- push $0 /* Event data, 0 for IRQ/NMI */ ++ push %rsi /* Event data for NMI */ + push %rdi /* fred_ss handed in by the caller */ + push %rbp + pushf +diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h +index 15d1c8e958378..18deb29f50501 100644 +--- a/arch/x86/include/asm/fred.h ++++ b/arch/x86/include/asm/fred.h +@@ -66,14 +66,14 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) + + void asm_fred_entrypoint_user(void); + void asm_fred_entrypoint_kernel(void); +-void asm_fred_entry_from_kvm(struct fred_ss); ++void asm_fred_entry_from_kvm(struct fred_ss ss, unsigned long edata); + + __visible void fred_entry_from_user(struct pt_regs *regs); + __visible void fred_entry_from_kernel(struct pt_regs *regs); + __visible void __fred_entry_from_kvm(struct pt_regs *regs); + + /* Must be called from noinstr code, thus __always_inline */ +-static __always_inline void fred_nmi_from_kvm(void) ++static __always_inline void fred_nmi_from_kvm(unsigned long edata) + { + struct fred_ss ss = { + .ss = __KERNEL_DS, +@@ -83,7 +83,7 @@ static __always_inline void fred_nmi_from_kvm(void) + .lm = 1, + }; + +- asm_fred_entry_from_kvm(ss); ++ asm_fred_entry_from_kvm(ss, edata); + } + + static inline void fred_irq_from_kvm(unsigned int vector) +@@ -95,7 +95,8 @@ static inline void fred_irq_from_kvm(unsigned int vector) + .lm = 1, + }; + +- asm_fred_entry_from_kvm(ss); ++ /* Event data is always zero for IRQ */ ++ asm_fred_entry_from_kvm(ss, 0); + } + + void cpu_init_fred_exceptions(void); +@@ -123,7 +124,7 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret + static inline void cpu_init_fred_exceptions(void) { } + static inline void cpu_init_fred_rsps(void) { } + static inline void fred_complete_exception_setup(void) { } +-static __always_inline void fred_nmi_from_kvm(void) { } ++static __always_inline void fred_nmi_from_kvm(unsigned long edata) { } + static inline void fred_irq_from_kvm(unsigned int vector) { } + static inline void fred_sync_rsp0(unsigned long rsp0) { } + static inline void fred_update_rsp0(void) { } +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index d37251f4e8b2d..8f1d105f333f1 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7521,7 +7521,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) + + kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); + if (cpu_feature_enabled(X86_FEATURE_FRED)) +- fred_nmi_from_kvm(); ++ fred_nmi_from_kvm(vmx_get_exit_qual(vcpu)); + else + vmx_do_nmi_irqoff(); + kvm_after_interrupt(vcpu); +-- +2.43.0 + diff --git a/SPECS/kernel/0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi b/SPECS/kernel/0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi deleted file mode 100644 index bdfcf734a..000000000 --- a/SPECS/kernel/0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi +++ /dev/null @@ -1,106 +0,0 @@ -From 93a2531574a4a627a5088b29cfb4fee7282b97bf Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 14 May 2025 07:07:55 -0700 -Subject: [PATCH 31/44] x86/fred: Provide separate IRQ vs. NMI wrappers for - entry from KVM - -Provide separate wrappers for forwarding IRQs vs NMIs from KVM in -anticipation of adding support for NMI source reporting, which will add -an NMI-only parameter, i.e. will further pollute the current API with a -param that is a hardcoded for one of the two call sites. - -Opportunistically tag the non-FRED NMI wrapper __always_inline, as the -compiler could theoretically generate a function call and trigger and a -(completely benign) "leaving noinstr" warning. - -Signed-off-by: Sean Christopherson -Signed-off-by: Sohil Mehta ---- -v7: New patch ---- - arch/x86/include/asm/fred.h | 30 ++++++++++++++++++++++-------- - arch/x86/kvm/vmx/vmx.c | 4 ++-- - 2 files changed, 24 insertions(+), 10 deletions(-) - -diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h -index 263fd5267f71..15d1c8e95837 100644 ---- a/arch/x86/include/asm/fred.h -+++ b/arch/x86/include/asm/fred.h -@@ -9,6 +9,7 @@ - #include - - #include -+#include - #include - #include - -@@ -71,15 +72,27 @@ __visible void fred_entry_from_user(struct pt_regs *regs); - __visible void fred_entry_from_kernel(struct pt_regs *regs); - __visible void __fred_entry_from_kvm(struct pt_regs *regs); - --/* Can be called from noinstr code, thus __always_inline */ --static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) -+/* Must be called from noinstr code, thus __always_inline */ -+static __always_inline void fred_nmi_from_kvm(void) - { - struct fred_ss ss = { -- .ss =__KERNEL_DS, -- .type = type, -- .vector = vector, -- .nmi = type == EVENT_TYPE_NMI, -- .lm = 1, -+ .ss = __KERNEL_DS, -+ .type = EVENT_TYPE_NMI, -+ .vector = NMI_VECTOR, -+ .nmi = true, -+ .lm = 1, -+ }; -+ -+ asm_fred_entry_from_kvm(ss); -+} -+ -+static inline void fred_irq_from_kvm(unsigned int vector) -+{ -+ struct fred_ss ss = { -+ .ss = __KERNEL_DS, -+ .type = EVENT_TYPE_EXTINT, -+ .vector = vector, -+ .lm = 1, - }; - - asm_fred_entry_from_kvm(ss); -@@ -110,7 +123,8 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret - static inline void cpu_init_fred_exceptions(void) { } - static inline void cpu_init_fred_rsps(void) { } - static inline void fred_complete_exception_setup(void) { } --static inline void fred_entry_from_kvm(unsigned int type, unsigned int vector) { } -+static __always_inline void fred_nmi_from_kvm(void) { } -+static inline void fred_irq_from_kvm(unsigned int vector) { } - static inline void fred_sync_rsp0(unsigned long rsp0) { } - static inline void fred_update_rsp0(void) { } - #endif /* CONFIG_X86_FRED */ -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 090b49679925..4338e02c9229 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7256,7 +7256,7 @@ static void handle_external_interrupt_irqoff(struct kvm_vcpu *vcpu, - - kvm_before_interrupt(vcpu, KVM_HANDLING_IRQ); - if (cpu_feature_enabled(X86_FEATURE_FRED)) -- fred_entry_from_kvm(EVENT_TYPE_EXTINT, vector); -+ fred_irq_from_kvm(vector); - else - vmx_do_interrupt_irqoff(gate_offset((gate_desc *)host_idt_base + vector)); - kvm_after_interrupt(vcpu); -@@ -7542,7 +7542,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) - - kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); - if (cpu_feature_enabled(X86_FEATURE_FRED)) -- fred_entry_from_kvm(EVENT_TYPE_NMI, NMI_VECTOR); -+ fred_nmi_from_kvm(); - else - vmx_do_nmi_irqoff(); - kvm_after_interrupt(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf b/SPECS/kernel/0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf deleted file mode 100644 index 437d12ae6..000000000 --- a/SPECS/kernel/0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf +++ /dev/null @@ -1,31 +0,0 @@ -From 4ca9541349929f0caa8bc00ab966e2b36d835f8f Mon Sep 17 00:00:00 2001 -From: Zhenyu Wang -Date: Thu, 30 May 2024 11:19:23 +0000 -Subject: [PATCH 032/100] perf/x86/intel/cstate: Add Clearwater Forrest support - -Clearwater Forrest has same c-state residency counters like Sierra Forrest. -So this simply adds cpu model id for it. - -Cc: Artem Bityutskiy -Cc: Kan Liang -Reviewed-by: Kan Liang -Signed-off-by: Zhenyu Wang ---- - arch/x86/events/intel/cstate.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c -index ec753e39b007..a5f2e0be2337 100644 ---- a/arch/x86/events/intel/cstate.c -+++ b/arch/x86/events/intel/cstate.c -@@ -628,6 +628,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = { - X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &adl_cstates), - X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &srf_cstates), - X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &grr_cstates), -+ X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &srf_cstates), - - X86_MATCH_VFM(INTEL_ICELAKE_L, &icl_cstates), - X86_MATCH_VFM(INTEL_ICELAKE, &icl_cstates), --- -2.43.0 - diff --git a/SPECS/kernel/0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi b/SPECS/kernel/0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi new file mode 100644 index 000000000..dcde08e99 --- /dev/null +++ b/SPECS/kernel/0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi @@ -0,0 +1,62 @@ +From 265183f7f2ee2e2634c6d2657e9bf9017eae2113 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Tue, 25 Mar 2025 18:17:06 +0000 +Subject: [PATCH 32/44] x86/cpufeatures: Add the CPUID feature bit for + NMI-source reporting + +NMI-source reporting is introduced to report the sources of NMIs with +FRED event delivery based on vectors in NMI interrupt messages or the +local APIC. This enables the kernel to avoid the latency incurred by +going over the entire NMI handler list and reduces ambiguity about the +source of an NMI. + +Enumerate NMI-source reporting in cpufeatures.h. Also, since NMI-source +reporting uses the FRED event dispatch framework, make it dependent on +FRED in the CPUID dependency table. This ensures that NMI-source +reporting gets disabled when FRED is disabled. + +NMI-source reporting is intended as a kernel feature and does not need +userspace enumeration or configuration. There is no need to expose it to +userspace through /proc/cpuinfo. + +Originally-by: Jacob Pan +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: No change. + +v5: Add NMI-source to the CPUID dependency table. + Do not expose NMI-source feature through /proc/cpuinfo. +--- + arch/x86/include/asm/cpufeatures.h | 1 + + arch/x86/kernel/cpu/cpuid-deps.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h +index 4091a776e37aa..b6a31d230a487 100644 +--- a/arch/x86/include/asm/cpufeatures.h ++++ b/arch/x86/include/asm/cpufeatures.h +@@ -322,6 +322,7 @@ + #define X86_FEATURE_FRED (12*32+17) /* "fred" Flexible Return and Event Delivery */ + #define X86_FEATURE_LKGS (12*32+18) /* Load "kernel" (userspace) GS */ + #define X86_FEATURE_WRMSRNS (12*32+19) /* Non-serializing WRMSR */ ++#define X86_FEATURE_NMI_SOURCE (12*32+20) /* NMI-Source reporting with FRED */ + #define X86_FEATURE_AMX_FP16 (12*32+21) /* AMX fp16 Support */ + #define X86_FEATURE_AVX_IFMA (12*32+23) /* Support for VPMADD52[H,L]UQ */ + #define X86_FEATURE_LAM (12*32+26) /* "lam" Linear Address Masking */ +diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c +index 46efcbd6afa41..87a334f639d51 100644 +--- a/arch/x86/kernel/cpu/cpuid-deps.c ++++ b/arch/x86/kernel/cpu/cpuid-deps.c +@@ -88,6 +88,7 @@ static const struct cpuid_dep cpuid_deps[] = { + { X86_FEATURE_AMX_INT8, X86_FEATURE_AMX_TILE }, + { X86_FEATURE_SHSTK, X86_FEATURE_XSAVES }, + { X86_FEATURE_FRED, X86_FEATURE_LKGS }, ++ { X86_FEATURE_NMI_SOURCE, X86_FEATURE_FRED }, + { X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL }, + {} + }; +-- +2.43.0 + diff --git a/SPECS/kernel/0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi b/SPECS/kernel/0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi deleted file mode 100644 index 99813b49e..000000000 --- a/SPECS/kernel/0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi +++ /dev/null @@ -1,113 +0,0 @@ -From 92d3fbe4e179f178f01a25407284f25203660c7b Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 9 Jun 2025 12:40:04 -0700 -Subject: [PATCH 32/44] x86/fred: Pass event data to the NMI entry point from - KVM - -Extend the FRED NMI entry point from KVM to take an extra argument to -allow KVM to invoke the FRED event dispatch framework with event data. - -This API is used to pass the NMI-source bitmap for NMI-induced VM exits. -Read the VMCS exit qualification field to get the NMI-source information -and store it as event data precisely in the format expected by the FRED -event framework. - -Read the VMCS exit qualification unconditionally since almost all -upcoming CPUs are expected to enable FRED and NMI-source together. In -the rare case that NMI-source isn't enabled, the extra VMREAD would be -harmless since the exit qualification is expected to be zero. - -Suggested-by: Sean Christopherson -Originally-by: Zeng Guang -Signed-off-by: Sohil Mehta ---- -v7: Pass the event data from KVM only for NMI. (Sean) - -v6: No change - -v5: Read the VMCS exit qualification unconditionally. (Sean) - Combine related patches into one. ---- - arch/x86/entry/entry_64_fred.S | 2 +- - arch/x86/include/asm/fred.h | 11 ++++++----- - arch/x86/kvm/vmx/vmx.c | 2 +- - 3 files changed, 8 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S -index cb5ff2b1f6e7..1b6beb22a9b7 100644 ---- a/arch/x86/entry/entry_64_fred.S -+++ b/arch/x86/entry/entry_64_fred.S -@@ -93,7 +93,7 @@ SYM_FUNC_START(asm_fred_entry_from_kvm) - * +--------+-----------------+ - */ - push $0 /* Reserved, must be 0 */ -- push $0 /* Event data, 0 for IRQ/NMI */ -+ push %rsi /* Event data for NMI */ - push %rdi /* fred_ss handed in by the caller */ - push %rbp - pushf -diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h -index 15d1c8e95837..18deb29f5050 100644 ---- a/arch/x86/include/asm/fred.h -+++ b/arch/x86/include/asm/fred.h -@@ -66,14 +66,14 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) - - void asm_fred_entrypoint_user(void); - void asm_fred_entrypoint_kernel(void); --void asm_fred_entry_from_kvm(struct fred_ss); -+void asm_fred_entry_from_kvm(struct fred_ss ss, unsigned long edata); - - __visible void fred_entry_from_user(struct pt_regs *regs); - __visible void fred_entry_from_kernel(struct pt_regs *regs); - __visible void __fred_entry_from_kvm(struct pt_regs *regs); - - /* Must be called from noinstr code, thus __always_inline */ --static __always_inline void fred_nmi_from_kvm(void) -+static __always_inline void fred_nmi_from_kvm(unsigned long edata) - { - struct fred_ss ss = { - .ss = __KERNEL_DS, -@@ -83,7 +83,7 @@ static __always_inline void fred_nmi_from_kvm(void) - .lm = 1, - }; - -- asm_fred_entry_from_kvm(ss); -+ asm_fred_entry_from_kvm(ss, edata); - } - - static inline void fred_irq_from_kvm(unsigned int vector) -@@ -95,7 +95,8 @@ static inline void fred_irq_from_kvm(unsigned int vector) - .lm = 1, - }; - -- asm_fred_entry_from_kvm(ss); -+ /* Event data is always zero for IRQ */ -+ asm_fred_entry_from_kvm(ss, 0); - } - - void cpu_init_fred_exceptions(void); -@@ -123,7 +124,7 @@ static __always_inline unsigned long fred_event_data(struct pt_regs *regs) { ret - static inline void cpu_init_fred_exceptions(void) { } - static inline void cpu_init_fred_rsps(void) { } - static inline void fred_complete_exception_setup(void) { } --static __always_inline void fred_nmi_from_kvm(void) { } -+static __always_inline void fred_nmi_from_kvm(unsigned long edata) { } - static inline void fred_irq_from_kvm(unsigned int vector) { } - static inline void fred_sync_rsp0(unsigned long rsp0) { } - static inline void fred_update_rsp0(void) { } -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 4338e02c9229..36afe1dba9f0 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7542,7 +7542,7 @@ noinstr void vmx_handle_nmi(struct kvm_vcpu *vcpu) - - kvm_before_interrupt(vcpu, KVM_HANDLING_NMI); - if (cpu_feature_enabled(X86_FEATURE_FRED)) -- fred_nmi_from_kvm(); -+ fred_nmi_from_kvm(vmx_get_exit_qual(vcpu)); - else - vmx_do_nmi_irqoff(); - kvm_after_interrupt(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf b/SPECS/kernel/0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf deleted file mode 100644 index ab44599cb..000000000 --- a/SPECS/kernel/0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf +++ /dev/null @@ -1,60 +0,0 @@ -From 97f6605d3a333f5d5be1044776127d60cf5ec84c Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:01 +0800 -Subject: [PATCH 033/100] KVM: x86/pmu: Correct typo "_COUTNERS" to "_COUNTERS" - -Fix typos. "_COUTNERS" -> "_COUNTERS". - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - arch/x86/include/asm/kvm_host.h | 8 ++++---- - arch/x86/kvm/vmx/pmu_intel.c | 6 +++--- - 2 files changed, 7 insertions(+), 7 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index f19a76d3ca0e..7fb2bdcf42a8 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -545,10 +545,10 @@ struct kvm_pmc { - #define KVM_MAX_NR_GP_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_GP_COUNTERS, \ - KVM_MAX_NR_AMD_GP_COUNTERS) - --#define KVM_MAX_NR_INTEL_FIXED_COUTNERS 3 --#define KVM_MAX_NR_AMD_FIXED_COUTNERS 0 --#define KVM_MAX_NR_FIXED_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUTNERS, \ -- KVM_MAX_NR_AMD_FIXED_COUTNERS) -+#define KVM_MAX_NR_INTEL_FIXED_COUNTERS 3 -+#define KVM_MAX_NR_AMD_FIXED_COUNTERS 0 -+#define KVM_MAX_NR_FIXED_COUNTERS KVM_MAX(KVM_MAX_NR_INTEL_FIXED_COUNTERS, \ -+ KVM_MAX_NR_AMD_FIXED_COUNTERS) - - struct kvm_pmu { - u8 version; -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 0b173602821b..e8b37a38fbba 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -478,8 +478,8 @@ static __always_inline u64 intel_get_fixed_pmc_eventsel(unsigned int index) - }; - u64 eventsel; - -- BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUTNERS); -- BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUTNERS); -+ BUILD_BUG_ON(ARRAY_SIZE(fixed_pmc_perf_ids) != KVM_MAX_NR_INTEL_FIXED_COUNTERS); -+ BUILD_BUG_ON(index >= KVM_MAX_NR_INTEL_FIXED_COUNTERS); - - /* - * Yell if perf reports support for a fixed counter but perf doesn't -@@ -625,7 +625,7 @@ static void intel_pmu_init(struct kvm_vcpu *vcpu) - pmu->gp_counters[i].current_config = 0; - } - -- for (i = 0; i < KVM_MAX_NR_INTEL_FIXED_COUTNERS; i++) { -+ for (i = 0; i < KVM_MAX_NR_INTEL_FIXED_COUNTERS; i++) { - pmu->fixed_counters[i].type = KVM_PMC_FIXED; - pmu->fixed_counters[i].vcpu = vcpu; - pmu->fixed_counters[i].idx = i + KVM_FIXED_PMC_BASE_IDX; --- -2.43.0 - diff --git a/SPECS/kernel/0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi b/SPECS/kernel/0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi deleted file mode 100644 index ba5728740..000000000 --- a/SPECS/kernel/0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi +++ /dev/null @@ -1,62 +0,0 @@ -From 935fa17478d9e4794e9052128c8d933224d68fab Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Tue, 25 Mar 2025 18:17:06 +0000 -Subject: [PATCH 33/44] x86/cpufeatures: Add the CPUID feature bit for - NMI-source reporting - -NMI-source reporting is introduced to report the sources of NMIs with -FRED event delivery based on vectors in NMI interrupt messages or the -local APIC. This enables the kernel to avoid the latency incurred by -going over the entire NMI handler list and reduces ambiguity about the -source of an NMI. - -Enumerate NMI-source reporting in cpufeatures.h. Also, since NMI-source -reporting uses the FRED event dispatch framework, make it dependent on -FRED in the CPUID dependency table. This ensures that NMI-source -reporting gets disabled when FRED is disabled. - -NMI-source reporting is intended as a kernel feature and does not need -userspace enumeration or configuration. There is no need to expose it to -userspace through /proc/cpuinfo. - -Originally-by: Jacob Pan -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: No change. - -v5: Add NMI-source to the CPUID dependency table. - Do not expose NMI-source feature through /proc/cpuinfo. ---- - arch/x86/include/asm/cpufeatures.h | 1 + - arch/x86/kernel/cpu/cpuid-deps.c | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h -index 06fc0479a23f..6b20b92dcf2f 100644 ---- a/arch/x86/include/asm/cpufeatures.h -+++ b/arch/x86/include/asm/cpufeatures.h -@@ -322,6 +322,7 @@ - #define X86_FEATURE_FRED (12*32+17) /* "fred" Flexible Return and Event Delivery */ - #define X86_FEATURE_LKGS (12*32+18) /* Load "kernel" (userspace) GS */ - #define X86_FEATURE_WRMSRNS (12*32+19) /* Non-serializing WRMSR */ -+#define X86_FEATURE_NMI_SOURCE (12*32+20) /* NMI-Source reporting with FRED */ - #define X86_FEATURE_AMX_FP16 (12*32+21) /* AMX fp16 Support */ - #define X86_FEATURE_AVX_IFMA (12*32+23) /* Support for VPMADD52[H,L]UQ */ - #define X86_FEATURE_LAM (12*32+26) /* "lam" Linear Address Masking */ -diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c -index 46efcbd6afa4..87a334f639d5 100644 ---- a/arch/x86/kernel/cpu/cpuid-deps.c -+++ b/arch/x86/kernel/cpu/cpuid-deps.c -@@ -88,6 +88,7 @@ static const struct cpuid_dep cpuid_deps[] = { - { X86_FEATURE_AMX_INT8, X86_FEATURE_AMX_TILE }, - { X86_FEATURE_SHSTK, X86_FEATURE_XSAVES }, - { X86_FEATURE_FRED, X86_FEATURE_LKGS }, -+ { X86_FEATURE_NMI_SOURCE, X86_FEATURE_FRED }, - { X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL }, - {} - }; --- -2.43.0 - diff --git a/SPECS/kernel/0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi b/SPECS/kernel/0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi new file mode 100644 index 000000000..8f78f3e67 --- /dev/null +++ b/SPECS/kernel/0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi @@ -0,0 +1,307 @@ +From aab106dee39d86ec1bb627580d7b395179fc0d38 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Thu, 24 Apr 2025 23:16:45 +0000 +Subject: [PATCH 33/44] x86/nmi: Extend the registration interface to include + the NMI-source vector + +To prepare for NMI-source reporting, add a source vector argument to the +NMI handler registration interface. Later, this will be used to +register NMI handlers with a unique source vector that can be used to +identify the originator of the NMI. + +Vector 0 is reserved by the hardware for situations when a source vector +is not used while generating an NMI or the originator could not be +reliably identified. Registering an NMI handler with vector 0 is +equivalent to not using NMI-source reporting. + +For now, just extend the interface with no source information (vector 0) +for all the handlers. No functional change intended. + +Originally-by: Jacob Pan +Signed-off-by: Sohil Mehta +Reviewed-by: Xin Li (Intel) +--- +v7: Use an enum for defining source vectors (DaveH). + Use NMIS_NO_SOURCE instead of zero (DaveH). + Add review tag (Xin). + +v6: No change. + +v5: Split the patch into two parts. This one only extends the interface. +--- + arch/x86/events/amd/ibs.c | 2 +- + arch/x86/events/core.c | 2 +- + arch/x86/include/asm/nmi.h | 16 +++++++++++++++- + arch/x86/kernel/apic/hw_nmi.c | 3 +-- + arch/x86/kernel/cpu/mce/inject.c | 2 +- + arch/x86/kernel/cpu/mshyperv.c | 2 +- + arch/x86/kernel/kgdb.c | 6 ++---- + arch/x86/kernel/nmi_selftest.c | 6 +++--- + arch/x86/kernel/smp.c | 4 ++-- + arch/x86/platform/uv/uv_nmi.c | 4 ++-- + drivers/acpi/apei/ghes.c | 2 +- + drivers/char/ipmi/ipmi_watchdog.c | 3 +-- + drivers/edac/igen6_edac.c | 3 +-- + drivers/watchdog/hpwdt.c | 6 +++--- + 14 files changed, 35 insertions(+), 26 deletions(-) + +diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c +index 112f43b23ebf8..6f8f0d663f2fd 100644 +--- a/arch/x86/events/amd/ibs.c ++++ b/arch/x86/events/amd/ibs.c +@@ -1485,7 +1485,7 @@ static __init int perf_event_ibs_init(void) + if (ret) + goto err_op; + +- ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs"); ++ ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs", NMIS_NO_SOURCE); + if (ret) + goto err_nmi; + +diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c +index fa6c47b509897..c8a6175c48730 100644 +--- a/arch/x86/events/core.c ++++ b/arch/x86/events/core.c +@@ -2131,7 +2131,7 @@ static int __init init_hw_perf_events(void) + x86_pmu.config_mask = X86_RAW_EVENT_MASK; + + perf_events_lapic_init(); +- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI"); ++ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); + + unconstrained = (struct event_constraint) + __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, +diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h +index 79d88d12c8fbf..42820c4f59b9d 100644 +--- a/arch/x86/include/asm/nmi.h ++++ b/arch/x86/include/asm/nmi.h +@@ -48,12 +48,24 @@ enum { + + typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); + ++/** ++ * enum nmi_source_vectors - NMI-source vectors are used to identify the ++ * origin of an NMI and to route the NMI directly to the appropriate ++ * handler. ++ * ++ * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. ++ */ ++enum nmi_source_vectors { ++ NMIS_NO_SOURCE = 0, ++}; ++ + struct nmiaction { + struct list_head list; + nmi_handler_t handler; + u64 max_duration; + unsigned long flags; + const char *name; ++ enum nmi_source_vectors source_vector; + }; + + /** +@@ -62,6 +74,7 @@ struct nmiaction { + * @fn: The NMI handler + * @fg: Flags associated with the NMI handler + * @n: Name of the NMI handler ++ * @src: NMI-source vector for the NMI handler + * @init: Optional __init* attributes for struct nmiaction + * + * Adds the provided handler to the list of handlers for the specified +@@ -75,13 +88,14 @@ struct nmiaction { + * + * Return: 0 on success, or an error code on failure. + */ +-#define register_nmi_handler(t, fn, fg, n, init...) \ ++#define register_nmi_handler(t, fn, fg, n, src, init...)\ + ({ \ + static struct nmiaction init fn##_na = { \ + .list = LIST_HEAD_INIT(fn##_na.list), \ + .handler = (fn), \ + .name = (n), \ + .flags = (fg), \ ++ .source_vector = (src), \ + }; \ + __register_nmi_handler((t), &fn##_na); \ + }) +diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c +index 45af535c44a07..d09e771723ed2 100644 +--- a/arch/x86/kernel/apic/hw_nmi.c ++++ b/arch/x86/kernel/apic/hw_nmi.c +@@ -53,8 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); + + static int __init register_nmi_cpu_backtrace_handler(void) + { +- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, +- 0, "arch_bt"); ++ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); + return 0; + } + early_initcall(register_nmi_cpu_backtrace_handler); +diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c +index d02c4f556cd05..ba70ef8a1964f 100644 +--- a/arch/x86/kernel/cpu/mce/inject.c ++++ b/arch/x86/kernel/cpu/mce/inject.c +@@ -775,7 +775,7 @@ static int __init inject_init(void) + + debugfs_init(); + +- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); ++ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); + mce_register_injector_chain(&inject_nb); + + setup_inj_struct(&i_mce); +diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c +index c4febdbcfe4d8..a3b9b9beb338b 100644 +--- a/arch/x86/kernel/cpu/mshyperv.c ++++ b/arch/x86/kernel/cpu/mshyperv.c +@@ -557,7 +557,7 @@ static void __init ms_hyperv_init_platform(void) + } + + register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, +- "hv_nmi_unknown"); ++ "hv_nmi_unknown", NMIS_NO_SOURCE); + #endif + + #ifdef CONFIG_X86_IO_APIC +diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c +index 8b1a9733d13e3..80de0ac2e8ded 100644 +--- a/arch/x86/kernel/kgdb.c ++++ b/arch/x86/kernel/kgdb.c +@@ -602,13 +602,11 @@ int kgdb_arch_init(void) + if (retval) + goto out; + +- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, +- 0, "kgdb"); ++ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); + if (retval) + goto out1; + +- retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, +- 0, "kgdb"); ++ retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); + + if (retval) + goto out2; +diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c +index a010e9d062bf7..c4fffa868160f 100644 +--- a/arch/x86/kernel/nmi_selftest.c ++++ b/arch/x86/kernel/nmi_selftest.c +@@ -41,7 +41,7 @@ static void __init init_nmi_testsuite(void) + { + /* trap all the unknown NMIs we may generate */ + register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk", +- __initdata); ++ NMIS_NO_SOURCE, __initdata); + } + + static void __init cleanup_nmi_testsuite(void) +@@ -63,8 +63,8 @@ static void __init test_nmi_ipi(struct cpumask *mask) + { + unsigned long timeout; + +- if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, +- NMI_FLAG_FIRST, "nmi_selftest", __initdata)) { ++ if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, ++ "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { + nmi_fail = FAILURE; + return; + } +diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c +index b014e6d229f95..6c1d3ffbee6c9 100644 +--- a/arch/x86/kernel/smp.c ++++ b/arch/x86/kernel/smp.c +@@ -142,8 +142,8 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) + + static int register_stop_handler(void) + { +- return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, +- NMI_FLAG_FIRST, "smp_stop"); ++ return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, ++ "smp_stop", NMIS_NO_SOURCE); + } + + static void native_stop_other_cpus(int wait) +diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c +index 5c50e550ab635..5af368710f69d 100644 +--- a/arch/x86/platform/uv/uv_nmi.c ++++ b/arch/x86/platform/uv/uv_nmi.c +@@ -1029,10 +1029,10 @@ static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) + + static void uv_register_nmi_notifier(void) + { +- if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) ++ if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv", NMIS_NO_SOURCE)) + pr_warn("UV: NMI handler failed to register\n"); + +- if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) ++ if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping", NMIS_NO_SOURCE)) + pr_warn("UV: PING NMI handler failed to register\n"); + } + +diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c +index 97ee19f2cae06..38ea242ad3e39 100644 +--- a/drivers/acpi/apei/ghes.c ++++ b/drivers/acpi/apei/ghes.c +@@ -1443,7 +1443,7 @@ static void ghes_nmi_add(struct ghes *ghes) + { + mutex_lock(&ghes_list_mutex); + if (list_empty(&ghes_nmi)) +- register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); ++ register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes", NMIS_NO_SOURCE); + list_add_rcu(&ghes->list, &ghes_nmi); + mutex_unlock(&ghes_list_mutex); + } +diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c +index a013ddbf14662..7e2fd81325c49 100644 +--- a/drivers/char/ipmi/ipmi_watchdog.c ++++ b/drivers/char/ipmi/ipmi_watchdog.c +@@ -1252,8 +1252,7 @@ static void check_parms(void) + } + } + if (do_nmi && !nmi_handler_registered) { +- rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, +- "ipmi"); ++ rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, "ipmi", NMIS_NO_SOURCE); + if (rv) { + pr_warn("Can't register nmi handler\n"); + return; +diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c +index 2fc59f9eed691..61c0e285bd9b6 100644 +--- a/drivers/edac/igen6_edac.c ++++ b/drivers/edac/igen6_edac.c +@@ -1436,8 +1436,7 @@ static int register_err_handler(void) + return 0; + } + +- rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, +- 0, IGEN6_NMI_NAME); ++ rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, 0, IGEN6_NMI_NAME, NMIS_NO_SOURCE); + if (rc) { + igen6_printk(KERN_ERR, "Failed to register NMI handler\n"); + return rc; +diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c +index ae30e394d176e..90ae59a09270f 100644 +--- a/drivers/watchdog/hpwdt.c ++++ b/drivers/watchdog/hpwdt.c +@@ -242,13 +242,13 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) + /* + * Only one function can register for NMI_UNKNOWN + */ +- retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); ++ retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); + if (retval) + goto error; +- retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); ++ retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); + if (retval) + goto error1; +- retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); ++ retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); + if (retval) + goto error2; + +-- +2.43.0 + diff --git a/SPECS/kernel/0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf b/SPECS/kernel/0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf deleted file mode 100644 index a88fa4928..000000000 --- a/SPECS/kernel/0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf +++ /dev/null @@ -1,45 +0,0 @@ -From 3a05d313a273338b2395f31ae2d3f0b6469746f5 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:02 +0800 -Subject: [PATCH 034/100] KVM: selftests: Add timing_info bit support in - vmx_pmu_caps_test - -A new bit PERF_CAPABILITIES[17] called "PEBS_TIMING_INFO" bit is added -to indicated if PEBS supports to record timing information in a new -"Retried Latency" field. - -Since KVM requires user can only set host consistent PEBS capabilities, -otherwise the PERF_CAPABILITIES setting would fail, so add -pebs_timing_info bit into "immutable_caps" to block host inconsistent -PEBS configuration and cause errors. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c -index a1f5ff45d518..f8deea220156 100644 ---- a/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c -+++ b/tools/testing/selftests/kvm/x86/vmx_pmu_caps_test.c -@@ -29,7 +29,7 @@ static union perf_capabilities { - u64 pebs_baseline:1; - u64 perf_metrics:1; - u64 pebs_output_pt_available:1; -- u64 anythread_deprecated:1; -+ u64 pebs_timing_info:1; - }; - u64 capabilities; - } host_cap; -@@ -44,6 +44,7 @@ static const union perf_capabilities immutable_caps = { - .pebs_arch_reg = 1, - .pebs_format = -1, - .pebs_baseline = 1, -+ .pebs_timing_info = 1, - }; - - static const union perf_capabilities format_caps = { --- -2.43.0 - diff --git a/SPECS/kernel/0034-exfat-fix-refcount-leak-in-exfat_find.patch b/SPECS/kernel/0034-exfat-fix-refcount-leak-in-exfat_find.patch deleted file mode 100644 index 99079c0ef..000000000 --- a/SPECS/kernel/0034-exfat-fix-refcount-leak-in-exfat_find.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 9f3ac6b31ecb6a4f7a335c2c4db1b12c3d66ffcf Mon Sep 17 00:00:00 2001 -From: Shuhao Fu -Date: Tue, 21 Oct 2025 16:42:28 +0800 -Subject: [PATCH 34/51] exfat: fix refcount leak in exfat_find - -Fix refcount leaks in `exfat_find` related to `exfat_get_dentry_set`. - -Function `exfat_get_dentry_set` would increase the reference counter of -`es->bh` on success. Therefore, `exfat_put_dentry_set` must be called -after `exfat_get_dentry_set` to ensure refcount consistency. This patch -relocate two checks to avoid possible leaks. - -Fixes: 82ebecdc74ff ("exfat: fix improper check of dentry.stream.valid_size") -Fixes: 13940cef9549 ("exfat: add a check for invalid data size") -Signed-off-by: Shuhao Fu -Reviewed-by: Yuezhang Mo -Signed-off-by: Namjae Jeon ---- - fs/exfat/namei.c | 20 ++++++++++---------- - 1 file changed, 10 insertions(+), 10 deletions(-) - -diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c -index d8964d736814..fcd0db6b4721 100644 ---- a/fs/exfat/namei.c -+++ b/fs/exfat/namei.c -@@ -645,16 +645,6 @@ static int exfat_find(struct inode *dir, struct qstr *qname, - info->valid_size = le64_to_cpu(ep2->dentry.stream.valid_size); - info->size = le64_to_cpu(ep2->dentry.stream.size); - -- if (info->valid_size < 0) { -- exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size); -- return -EIO; -- } -- -- if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { -- exfat_fs_error(sb, "data size is invalid(%lld)", info->size); -- return -EIO; -- } -- - info->start_clu = le32_to_cpu(ep2->dentry.stream.start_clu); - if (!is_valid_cluster(sbi, info->start_clu) && info->size) { - exfat_warn(sb, "start_clu is invalid cluster(0x%x)", -@@ -692,6 +682,16 @@ static int exfat_find(struct inode *dir, struct qstr *qname, - 0); - exfat_put_dentry_set(&es, false); - -+ if (info->valid_size < 0) { -+ exfat_fs_error(sb, "data valid size is invalid(%lld)", info->valid_size); -+ return -EIO; -+ } -+ -+ if (unlikely(EXFAT_B_TO_CLU_ROUND_UP(info->size, sbi) > sbi->used_clusters)) { -+ exfat_fs_error(sb, "data size is invalid(%lld)", info->size); -+ return -EIO; -+ } -+ - if (ei->start_clu == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, - "non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)", --- -2.43.0 - diff --git a/SPECS/kernel/0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi b/SPECS/kernel/0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi new file mode 100644 index 000000000..e73b59d76 --- /dev/null +++ b/SPECS/kernel/0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi @@ -0,0 +1,233 @@ +From 37e883d4f3ab4fab94feb9b7da02ae299794d04e Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Wed, 11 Jun 2025 23:10:01 -0700 +Subject: [PATCH 34/44] x86/nmi: Assign and register NMI-source vectors + +Prior to NMI-source support, the vector information was ignored by the +hardware while delivering NMIs. With NMI-source reporting, the initial +architecture supports a 16-bit source bitmap to identify the source of +the NMI. Upon receiving an NMI, this bitmap is delivered to the kernel +as part of the FRED event delivery mechanism. + +Assign a vector space of 1-15 that is specific to NMI-source and +independent of the IDT vector space. Though unlikely, the hardware may +extend the NMI-source bitmap in the future. Add a code comment to +clarify how the kernel support can be easily extended. + +Being a bitmap, the NMI-source vectors do not have any inherent priority +associated with them. The order of executing the NMI handlers is up to +the kernel. Existing NMI handling already has a priority mechanism for +the NMI handlers, with CPU-specific (NMI_LOCAL) handlers executed first, +followed by platform NMI handlers and unknown NMI (NMI_UNKNOWN) handlers +being last. Within each of these NMI types, the handlers registered with +NMI_FLAG_FIRST are given priority. + +NMI-source follows the same priority scheme to avoid unnecessary +complexity. Therefore, the NMI-source vectors are assigned arbitrarily, +except for vector 2. + +Vector 2 is reserved for external NMIs corresponding to the Local APIC - +LINT1 pin. Some third-party chipsets may send NMI messages with a fixed +vector value of 2. Using vector 2 for something else would lead to +confusion about the exact source. Do not assign it to any handler. + +NMI-source vectors are only assigned for NMI_LOCAL type handlers. +Platform NMI handlers have a single handler registered per type. They +don't need additional source information to differentiate among them. + +Use the assigned vectors to register the respective NMI handlers. Warn +if the vector values are unexpected. + +A couple of NMI handlers, such as the microcode rendezvous and the crash +reboot, do not use the typical NMI registration interface. Leave them +as-is for now. + +Originally-by: Jacob Pan +Signed-off-by: Sohil Mehta +Reviewed-by: Xin Li (Intel) +--- +v7: Use an enum to define the NMI-source vectors. (DaveH) + Add a BUILD_BUG_ON to validate the allocated vector count. (DaveH) + Add review tag from Xin. + +v6: Store source vector unconditionally. (PeterZ) + Add a warning for unexpected source vector values. (PeterZ) + +v5: Move the vector defines to nmi.h. + Combine vector allocation and registration into one patch. + Simplify NMI vector names. + Describe usage of vector 2 for external NMIs. + Get rid of vector priorities. +--- + arch/x86/events/core.c | 2 +- + arch/x86/include/asm/nmi.h | 46 ++++++++++++++++++++++++++++++++ + arch/x86/kernel/apic/hw_nmi.c | 2 +- + arch/x86/kernel/cpu/mce/inject.c | 2 +- + arch/x86/kernel/kgdb.c | 2 +- + arch/x86/kernel/nmi.c | 7 +++++ + arch/x86/kernel/nmi_selftest.c | 2 +- + arch/x86/kernel/smp.c | 2 +- + 8 files changed, 59 insertions(+), 6 deletions(-) + +diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c +index c8a6175c48730..e0efe4c00444e 100644 +--- a/arch/x86/events/core.c ++++ b/arch/x86/events/core.c +@@ -2131,7 +2131,7 @@ static int __init init_hw_perf_events(void) + x86_pmu.config_mask = X86_RAW_EVENT_MASK; + + perf_events_lapic_init(); +- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); ++ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_VECTOR_PMI); + + unconstrained = (struct event_constraint) + __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, +diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h +index 42820c4f59b9d..a48958a236fd4 100644 +--- a/arch/x86/include/asm/nmi.h ++++ b/arch/x86/include/asm/nmi.h +@@ -53,12 +53,58 @@ typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); + * origin of an NMI and to route the NMI directly to the appropriate + * handler. + * ++ * On CPUs that support NMI-source reporting with FRED, receiving an NMI ++ * with a valid vector sets the corresponding bit in the NMI-source ++ * bitmap. The bitmap is delivered as FRED event data on the stack. ++ * ++ * Multiple NMIs are coalesced in the NMI-source bitmap until the next ++ * NMI delivery. If an NMI is received without a vector or beyond the ++ * defined range, the CPU sets bit 0 of the NMI-source bitmap. ++ * ++ * Third-party chipsets may send NMI messages with a fixed vector of 2. ++ * Using vector 2 for some other purpose would cause confusion between ++ * those external NMI messages and the other purpose. Avoid using it. ++ * ++ * The vectors are in no particular priority order. Add new vector ++ * assignments sequentially in the list below before the COUNT. ++ * + * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. ++ * @NMIS_VECTOR_TEST: NMI selftest. ++ * @NMIS_VECTOR_EXTERNAL: Reserved to match external NMI vector 2. ++ * @NMIS_VECTOR_SMP_STOP: Panic stop CPU. ++ * @NMIS_VECTOR_BT: CPU backtrace. ++ * @NMIS_VECTOR_KGDB: Kernel debugger. ++ * @NMIS_VECTOR_MCE: MCE injection. ++ * @NMIS_VECTOR_PMI: PerfMon counters. ++ * ++ * @NMIS_VECTOR_COUNT: Count of the defined vectors. + */ + enum nmi_source_vectors { + NMIS_NO_SOURCE = 0, ++ NMIS_VECTOR_TEST, ++ NMIS_VECTOR_EXTERNAL = 2, ++ NMIS_VECTOR_SMP_STOP, ++ NMIS_VECTOR_BT, ++ NMIS_VECTOR_KGDB, ++ NMIS_VECTOR_MCE, ++ NMIS_VECTOR_PMI, ++ ++ NMIS_VECTOR_COUNT + }; + ++/* ++ * The early (and likely all future) hardware implementations of ++ * NMI-source reporting would only support a 16-bit source bitmap, with ++ * 1-15 being valid source vectors. ++ * ++ * If the hardware ever supports a larger bitmap, the kernel support can ++ * easily be extended to 64 bits by modifying the MAX below. However, ++ * care must be taken to reallocate the latency sensitive NMI sources ++ * within the first 15 vectors. Any source vector beyond the supported ++ * maximum on prior systems would set bit 0 in the NMI-source bitmap. ++ */ ++#define NMIS_VECTORS_MAX 16 ++ + struct nmiaction { + struct list_head list; + nmi_handler_t handler; +diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c +index d09e771723ed2..4e04f13d2de98 100644 +--- a/arch/x86/kernel/apic/hw_nmi.c ++++ b/arch/x86/kernel/apic/hw_nmi.c +@@ -53,7 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); + + static int __init register_nmi_cpu_backtrace_handler(void) + { +- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); ++ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_VECTOR_BT); + return 0; + } + early_initcall(register_nmi_cpu_backtrace_handler); +diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c +index ba70ef8a1964f..320068e01c22d 100644 +--- a/arch/x86/kernel/cpu/mce/inject.c ++++ b/arch/x86/kernel/cpu/mce/inject.c +@@ -775,7 +775,7 @@ static int __init inject_init(void) + + debugfs_init(); + +- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); ++ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_VECTOR_MCE); + mce_register_injector_chain(&inject_nb); + + setup_inj_struct(&i_mce); +diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c +index 80de0ac2e8ded..788b83e9404b6 100644 +--- a/arch/x86/kernel/kgdb.c ++++ b/arch/x86/kernel/kgdb.c +@@ -602,7 +602,7 @@ int kgdb_arch_init(void) + if (retval) + goto out; + +- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); ++ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_VECTOR_KGDB); + if (retval) + goto out1; + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index be93ec7255bfc..8071ad32aa119 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -182,6 +182,13 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action) + if (WARN_ON_ONCE(!action->handler || !list_empty(&action->list))) + return -EINVAL; + ++ /* NMI-source reporting should only be used for NMI_LOCAL */ ++ WARN_ON_ONCE((type != NMI_LOCAL) && (action->source_vector != NMIS_NO_SOURCE)); ++ ++ /* Check for valid vector values. See comment above NMIS_VECTORS_MAX */ ++ BUILD_BUG_ON(NMIS_VECTOR_COUNT > NMIS_VECTORS_MAX); ++ WARN_ON_ONCE(action->source_vector >= NMIS_VECTOR_COUNT); ++ + raw_spin_lock_irqsave(&desc->lock, flags); + + /* +diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c +index c4fffa868160f..f3918888e4942 100644 +--- a/arch/x86/kernel/nmi_selftest.c ++++ b/arch/x86/kernel/nmi_selftest.c +@@ -64,7 +64,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) + unsigned long timeout; + + if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, +- "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { ++ "nmi_selftest", NMIS_VECTOR_TEST, __initdata)) { + nmi_fail = FAILURE; + return; + } +diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c +index 6c1d3ffbee6c9..694e31bf445c5 100644 +--- a/arch/x86/kernel/smp.c ++++ b/arch/x86/kernel/smp.c +@@ -143,7 +143,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) + static int register_stop_handler(void) + { + return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, +- "smp_stop", NMIS_NO_SOURCE); ++ "smp_stop", NMIS_VECTOR_SMP_STOP); + } + + static void native_stop_other_cpus(int wait) +-- +2.43.0 + diff --git a/SPECS/kernel/0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi b/SPECS/kernel/0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi deleted file mode 100644 index b52e2ceb2..000000000 --- a/SPECS/kernel/0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi +++ /dev/null @@ -1,307 +0,0 @@ -From a07868c89eecc5d557fbe808e962adc61138e384 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Thu, 24 Apr 2025 23:16:45 +0000 -Subject: [PATCH 34/44] x86/nmi: Extend the registration interface to include - the NMI-source vector - -To prepare for NMI-source reporting, add a source vector argument to the -NMI handler registration interface. Later, this will be used to -register NMI handlers with a unique source vector that can be used to -identify the originator of the NMI. - -Vector 0 is reserved by the hardware for situations when a source vector -is not used while generating an NMI or the originator could not be -reliably identified. Registering an NMI handler with vector 0 is -equivalent to not using NMI-source reporting. - -For now, just extend the interface with no source information (vector 0) -for all the handlers. No functional change intended. - -Originally-by: Jacob Pan -Signed-off-by: Sohil Mehta -Reviewed-by: Xin Li (Intel) ---- -v7: Use an enum for defining source vectors (DaveH). - Use NMIS_NO_SOURCE instead of zero (DaveH). - Add review tag (Xin). - -v6: No change. - -v5: Split the patch into two parts. This one only extends the interface. ---- - arch/x86/events/amd/ibs.c | 2 +- - arch/x86/events/core.c | 2 +- - arch/x86/include/asm/nmi.h | 16 +++++++++++++++- - arch/x86/kernel/apic/hw_nmi.c | 3 +-- - arch/x86/kernel/cpu/mce/inject.c | 2 +- - arch/x86/kernel/cpu/mshyperv.c | 2 +- - arch/x86/kernel/kgdb.c | 6 ++---- - arch/x86/kernel/nmi_selftest.c | 6 +++--- - arch/x86/kernel/smp.c | 4 ++-- - arch/x86/platform/uv/uv_nmi.c | 4 ++-- - drivers/acpi/apei/ghes.c | 2 +- - drivers/char/ipmi/ipmi_watchdog.c | 3 +-- - drivers/edac/igen6_edac.c | 3 +-- - drivers/watchdog/hpwdt.c | 6 +++--- - 14 files changed, 35 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/events/amd/ibs.c b/arch/x86/events/amd/ibs.c -index 112f43b23ebf..6f8f0d663f2f 100644 ---- a/arch/x86/events/amd/ibs.c -+++ b/arch/x86/events/amd/ibs.c -@@ -1485,7 +1485,7 @@ static __init int perf_event_ibs_init(void) - if (ret) - goto err_op; - -- ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs"); -+ ret = register_nmi_handler(NMI_LOCAL, perf_ibs_nmi_handler, 0, "perf_ibs", NMIS_NO_SOURCE); - if (ret) - goto err_nmi; - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 68d0bea0f62e..c7205c7c1376 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -2253,7 +2253,7 @@ static int __init init_hw_perf_events(void) - x86_pmu.config_mask = X86_RAW_EVENT_MASK; - - perf_events_lapic_init(); -- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI"); -+ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); - - unconstrained = (struct event_constraint) - __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, -diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h -index 79d88d12c8fb..42820c4f59b9 100644 ---- a/arch/x86/include/asm/nmi.h -+++ b/arch/x86/include/asm/nmi.h -@@ -48,12 +48,24 @@ enum { - - typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); - -+/** -+ * enum nmi_source_vectors - NMI-source vectors are used to identify the -+ * origin of an NMI and to route the NMI directly to the appropriate -+ * handler. -+ * -+ * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. -+ */ -+enum nmi_source_vectors { -+ NMIS_NO_SOURCE = 0, -+}; -+ - struct nmiaction { - struct list_head list; - nmi_handler_t handler; - u64 max_duration; - unsigned long flags; - const char *name; -+ enum nmi_source_vectors source_vector; - }; - - /** -@@ -62,6 +74,7 @@ struct nmiaction { - * @fn: The NMI handler - * @fg: Flags associated with the NMI handler - * @n: Name of the NMI handler -+ * @src: NMI-source vector for the NMI handler - * @init: Optional __init* attributes for struct nmiaction - * - * Adds the provided handler to the list of handlers for the specified -@@ -75,13 +88,14 @@ struct nmiaction { - * - * Return: 0 on success, or an error code on failure. - */ --#define register_nmi_handler(t, fn, fg, n, init...) \ -+#define register_nmi_handler(t, fn, fg, n, src, init...)\ - ({ \ - static struct nmiaction init fn##_na = { \ - .list = LIST_HEAD_INIT(fn##_na.list), \ - .handler = (fn), \ - .name = (n), \ - .flags = (fg), \ -+ .source_vector = (src), \ - }; \ - __register_nmi_handler((t), &fn##_na); \ - }) -diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c -index 45af535c44a0..d09e771723ed 100644 ---- a/arch/x86/kernel/apic/hw_nmi.c -+++ b/arch/x86/kernel/apic/hw_nmi.c -@@ -53,8 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); - - static int __init register_nmi_cpu_backtrace_handler(void) - { -- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, -- 0, "arch_bt"); -+ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); - return 0; - } - early_initcall(register_nmi_cpu_backtrace_handler); -diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c -index d02c4f556cd0..ba70ef8a1964 100644 ---- a/arch/x86/kernel/cpu/mce/inject.c -+++ b/arch/x86/kernel/cpu/mce/inject.c -@@ -775,7 +775,7 @@ static int __init inject_init(void) - - debugfs_init(); - -- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify"); -+ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); - mce_register_injector_chain(&inject_nb); - - setup_inj_struct(&i_mce); -diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c -index c78f860419d6..c093f7baab6a 100644 ---- a/arch/x86/kernel/cpu/mshyperv.c -+++ b/arch/x86/kernel/cpu/mshyperv.c -@@ -550,7 +550,7 @@ static void __init ms_hyperv_init_platform(void) - } - - register_nmi_handler(NMI_UNKNOWN, hv_nmi_unknown, NMI_FLAG_FIRST, -- "hv_nmi_unknown"); -+ "hv_nmi_unknown", NMIS_NO_SOURCE); - #endif - - #ifdef CONFIG_X86_IO_APIC -diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c -index 8b1a9733d13e..80de0ac2e8de 100644 ---- a/arch/x86/kernel/kgdb.c -+++ b/arch/x86/kernel/kgdb.c -@@ -602,13 +602,11 @@ int kgdb_arch_init(void) - if (retval) - goto out; - -- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, -- 0, "kgdb"); -+ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); - if (retval) - goto out1; - -- retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, -- 0, "kgdb"); -+ retval = register_nmi_handler(NMI_UNKNOWN, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); - - if (retval) - goto out2; -diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c -index a010e9d062bf..c4fffa868160 100644 ---- a/arch/x86/kernel/nmi_selftest.c -+++ b/arch/x86/kernel/nmi_selftest.c -@@ -41,7 +41,7 @@ static void __init init_nmi_testsuite(void) - { - /* trap all the unknown NMIs we may generate */ - register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk", -- __initdata); -+ NMIS_NO_SOURCE, __initdata); - } - - static void __init cleanup_nmi_testsuite(void) -@@ -63,8 +63,8 @@ static void __init test_nmi_ipi(struct cpumask *mask) - { - unsigned long timeout; - -- if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, -- NMI_FLAG_FIRST, "nmi_selftest", __initdata)) { -+ if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, -+ "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { - nmi_fail = FAILURE; - return; - } -diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c -index b014e6d229f9..6c1d3ffbee6c 100644 ---- a/arch/x86/kernel/smp.c -+++ b/arch/x86/kernel/smp.c -@@ -142,8 +142,8 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) - - static int register_stop_handler(void) - { -- return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, -- NMI_FLAG_FIRST, "smp_stop"); -+ return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, -+ "smp_stop", NMIS_NO_SOURCE); - } - - static void native_stop_other_cpus(int wait) -diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c -index 5c50e550ab63..5af368710f69 100644 ---- a/arch/x86/platform/uv/uv_nmi.c -+++ b/arch/x86/platform/uv/uv_nmi.c -@@ -1029,10 +1029,10 @@ static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) - - static void uv_register_nmi_notifier(void) - { -- if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) -+ if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv", NMIS_NO_SOURCE)) - pr_warn("UV: NMI handler failed to register\n"); - -- if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) -+ if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping", NMIS_NO_SOURCE)) - pr_warn("UV: PING NMI handler failed to register\n"); - } - -diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c -index a0d54993edb3..cbfb53250597 100644 ---- a/drivers/acpi/apei/ghes.c -+++ b/drivers/acpi/apei/ghes.c -@@ -1445,7 +1445,7 @@ static void ghes_nmi_add(struct ghes *ghes) - { - mutex_lock(&ghes_list_mutex); - if (list_empty(&ghes_nmi)) -- register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes"); -+ register_nmi_handler(NMI_LOCAL, ghes_notify_nmi, 0, "ghes", NMIS_NO_SOURCE); - list_add_rcu(&ghes->list, &ghes_nmi); - mutex_unlock(&ghes_list_mutex); - } -diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c -index a013ddbf1466..7e2fd81325c4 100644 ---- a/drivers/char/ipmi/ipmi_watchdog.c -+++ b/drivers/char/ipmi/ipmi_watchdog.c -@@ -1252,8 +1252,7 @@ static void check_parms(void) - } - } - if (do_nmi && !nmi_handler_registered) { -- rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, -- "ipmi"); -+ rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, "ipmi", NMIS_NO_SOURCE); - if (rv) { - pr_warn("Can't register nmi handler\n"); - return; -diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c -index 7968d3126f0e..d2423bab80d8 100644 ---- a/drivers/edac/igen6_edac.c -+++ b/drivers/edac/igen6_edac.c -@@ -1475,8 +1475,7 @@ static int register_err_handler(void) - return 0; - } - -- rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, -- 0, IGEN6_NMI_NAME); -+ rc = register_nmi_handler(NMI_SERR, ecclog_nmi_handler, 0, IGEN6_NMI_NAME, NMIS_NO_SOURCE); - if (rc) { - igen6_printk(KERN_ERR, "Failed to register NMI handler\n"); - return rc; -diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c -index ae30e394d176..90ae59a09270 100644 ---- a/drivers/watchdog/hpwdt.c -+++ b/drivers/watchdog/hpwdt.c -@@ -242,13 +242,13 @@ static int hpwdt_init_nmi_decoding(struct pci_dev *dev) - /* - * Only one function can register for NMI_UNKNOWN - */ -- retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); -+ retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); - if (retval) - goto error; -- retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); -+ retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); - if (retval) - goto error1; -- retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); -+ retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt", NMIS_NO_SOURCE); - if (retval) - goto error2; - --- -2.43.0 - diff --git a/SPECS/kernel/0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf b/SPECS/kernel/0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf deleted file mode 100644 index b547fb0b2..000000000 --- a/SPECS/kernel/0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf +++ /dev/null @@ -1,152 +0,0 @@ -From ba9cccf06b726564ba30e62fe3ddfa4800d51283 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:03 +0800 -Subject: [PATCH 035/100] KVM: Selftests: Validate more arch-events in - pmu_counters_test - -Clearwater Forest introduces 5 new architectural events (4 topdown -level 1 metrics events and LBR inserts event). This patch supports -to validate these 5 newly added events. The detailed info about these -5 events can be found in SDM section 21.2.7 "Pre-defined Architectural - Performance Events". - -It becomes unrealistic to traverse all possible combinations of -unavailable events mask (may need dozens of minutes to finish all -possible combination validation). So only limit unavailable events mask -traverse to the first 8 arch-events. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/include/x86/pmu.h | 10 +++++++++ - .../selftests/kvm/include/x86/processor.h | 7 +++++- - tools/testing/selftests/kvm/lib/x86/pmu.c | 5 +++++ - .../selftests/kvm/x86/pmu_counters_test.c | 22 ++++++++++++++----- - 4 files changed, 38 insertions(+), 6 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h -index 3c10c4dc0ae8..2aabda2da002 100644 ---- a/tools/testing/selftests/kvm/include/x86/pmu.h -+++ b/tools/testing/selftests/kvm/include/x86/pmu.h -@@ -61,6 +61,11 @@ - #define INTEL_ARCH_BRANCHES_RETIRED RAW_EVENT(0xc4, 0x00) - #define INTEL_ARCH_BRANCHES_MISPREDICTED RAW_EVENT(0xc5, 0x00) - #define INTEL_ARCH_TOPDOWN_SLOTS RAW_EVENT(0xa4, 0x01) -+#define INTEL_ARCH_TOPDOWN_BE_BOUND RAW_EVENT(0xa4, 0x02) -+#define INTEL_ARCH_TOPDOWN_BAD_SPEC RAW_EVENT(0x73, 0x00) -+#define INTEL_ARCH_TOPDOWN_FE_BOUND RAW_EVENT(0x9c, 0x01) -+#define INTEL_ARCH_TOPDOWN_RETIRING RAW_EVENT(0xc2, 0x02) -+#define INTEL_ARCH_LBR_INSERTS RAW_EVENT(0xe4, 0x01) - - #define AMD_ZEN_CORE_CYCLES RAW_EVENT(0x76, 0x00) - #define AMD_ZEN_INSTRUCTIONS_RETIRED RAW_EVENT(0xc0, 0x00) -@@ -80,6 +85,11 @@ enum intel_pmu_architectural_events { - INTEL_ARCH_BRANCHES_RETIRED_INDEX, - INTEL_ARCH_BRANCHES_MISPREDICTED_INDEX, - INTEL_ARCH_TOPDOWN_SLOTS_INDEX, -+ INTEL_ARCH_TOPDOWN_BE_BOUND_INDEX, -+ INTEL_ARCH_TOPDOWN_BAD_SPEC_INDEX, -+ INTEL_ARCH_TOPDOWN_FE_BOUND_INDEX, -+ INTEL_ARCH_TOPDOWN_RETIRING_INDEX, -+ INTEL_ARCH_LBR_INSERTS_INDEX, - NR_INTEL_ARCH_EVENTS, - }; - -diff --git a/tools/testing/selftests/kvm/include/x86/processor.h b/tools/testing/selftests/kvm/include/x86/processor.h -index 2efb05c2f2fb..232964f2a687 100644 ---- a/tools/testing/selftests/kvm/include/x86/processor.h -+++ b/tools/testing/selftests/kvm/include/x86/processor.h -@@ -265,7 +265,7 @@ struct kvm_x86_cpu_property { - #define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15) - #define X86_PROPERTY_PMU_GP_COUNTERS_BIT_WIDTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 16, 23) - #define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31) --#define X86_PROPERTY_PMU_EVENTS_MASK KVM_X86_CPU_PROPERTY(0xa, 0, EBX, 0, 7) -+#define X86_PROPERTY_PMU_EVENTS_MASK KVM_X86_CPU_PROPERTY(0xa, 0, EBX, 0, 12) - #define X86_PROPERTY_PMU_FIXED_COUNTERS_BITMASK KVM_X86_CPU_PROPERTY(0xa, 0, ECX, 0, 31) - #define X86_PROPERTY_PMU_NR_FIXED_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EDX, 0, 4) - #define X86_PROPERTY_PMU_FIXED_COUNTERS_BIT_WIDTH KVM_X86_CPU_PROPERTY(0xa, 0, EDX, 5, 12) -@@ -332,6 +332,11 @@ struct kvm_x86_pmu_feature { - #define X86_PMU_FEATURE_BRANCH_INSNS_RETIRED KVM_X86_PMU_FEATURE(EBX, 5) - #define X86_PMU_FEATURE_BRANCHES_MISPREDICTED KVM_X86_PMU_FEATURE(EBX, 6) - #define X86_PMU_FEATURE_TOPDOWN_SLOTS KVM_X86_PMU_FEATURE(EBX, 7) -+#define X86_PMU_FEATURE_TOPDOWN_BE_BOUND KVM_X86_PMU_FEATURE(EBX, 8) -+#define X86_PMU_FEATURE_TOPDOWN_BAD_SPEC KVM_X86_PMU_FEATURE(EBX, 9) -+#define X86_PMU_FEATURE_TOPDOWN_FE_BOUND KVM_X86_PMU_FEATURE(EBX, 10) -+#define X86_PMU_FEATURE_TOPDOWN_RETIRING KVM_X86_PMU_FEATURE(EBX, 11) -+#define X86_PMU_FEATURE_LBR_INSERTS KVM_X86_PMU_FEATURE(EBX, 12) - - #define X86_PMU_FEATURE_INSNS_RETIRED_FIXED KVM_X86_PMU_FEATURE(ECX, 0) - #define X86_PMU_FEATURE_CPU_CYCLES_FIXED KVM_X86_PMU_FEATURE(ECX, 1) -diff --git a/tools/testing/selftests/kvm/lib/x86/pmu.c b/tools/testing/selftests/kvm/lib/x86/pmu.c -index f31f0427c17c..5ab44bf54773 100644 ---- a/tools/testing/selftests/kvm/lib/x86/pmu.c -+++ b/tools/testing/selftests/kvm/lib/x86/pmu.c -@@ -19,6 +19,11 @@ const uint64_t intel_pmu_arch_events[] = { - INTEL_ARCH_BRANCHES_RETIRED, - INTEL_ARCH_BRANCHES_MISPREDICTED, - INTEL_ARCH_TOPDOWN_SLOTS, -+ INTEL_ARCH_TOPDOWN_BE_BOUND, -+ INTEL_ARCH_TOPDOWN_BAD_SPEC, -+ INTEL_ARCH_TOPDOWN_FE_BOUND, -+ INTEL_ARCH_TOPDOWN_RETIRING, -+ INTEL_ARCH_LBR_INSERTS, - }; - kvm_static_assert(ARRAY_SIZE(intel_pmu_arch_events) == NR_INTEL_ARCH_EVENTS); - -diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -index 8aaaf25b6111..342a72420177 100644 ---- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c -+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -@@ -75,6 +75,11 @@ static struct kvm_intel_pmu_event intel_event_to_feature(uint8_t idx) - [INTEL_ARCH_BRANCHES_RETIRED_INDEX] = { X86_PMU_FEATURE_BRANCH_INSNS_RETIRED, X86_PMU_FEATURE_NULL }, - [INTEL_ARCH_BRANCHES_MISPREDICTED_INDEX] = { X86_PMU_FEATURE_BRANCHES_MISPREDICTED, X86_PMU_FEATURE_NULL }, - [INTEL_ARCH_TOPDOWN_SLOTS_INDEX] = { X86_PMU_FEATURE_TOPDOWN_SLOTS, X86_PMU_FEATURE_TOPDOWN_SLOTS_FIXED }, -+ [INTEL_ARCH_TOPDOWN_BE_BOUND_INDEX] = { X86_PMU_FEATURE_TOPDOWN_BE_BOUND, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_TOPDOWN_BAD_SPEC_INDEX] = { X86_PMU_FEATURE_TOPDOWN_BAD_SPEC, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_TOPDOWN_FE_BOUND_INDEX] = { X86_PMU_FEATURE_TOPDOWN_FE_BOUND, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_TOPDOWN_RETIRING_INDEX] = { X86_PMU_FEATURE_TOPDOWN_RETIRING, X86_PMU_FEATURE_NULL }, -+ [INTEL_ARCH_LBR_INSERTS_INDEX] = { X86_PMU_FEATURE_LBR_INSERTS, X86_PMU_FEATURE_NULL }, - }; - - kvm_static_assert(ARRAY_SIZE(__intel_event_to_feature) == NR_INTEL_ARCH_EVENTS); -@@ -171,9 +176,12 @@ static void guest_assert_event_count(uint8_t idx, uint32_t pmc, uint32_t pmc_msr - fallthrough; - case INTEL_ARCH_CPU_CYCLES_INDEX: - case INTEL_ARCH_REFERENCE_CYCLES_INDEX: -+ case INTEL_ARCH_TOPDOWN_BE_BOUND_INDEX: -+ case INTEL_ARCH_TOPDOWN_FE_BOUND_INDEX: - GUEST_ASSERT_NE(count, 0); - break; - case INTEL_ARCH_TOPDOWN_SLOTS_INDEX: -+ case INTEL_ARCH_TOPDOWN_RETIRING_INDEX: - __GUEST_ASSERT(count >= NUM_INSNS_RETIRED, - "Expected top-down slots >= %u, got count = %lu", - NUM_INSNS_RETIRED, count); -@@ -612,15 +620,19 @@ static void test_intel_counters(void) - pr_info("Testing arch events, PMU version %u, perf_caps = %lx\n", - v, perf_caps[i]); - /* -- * To keep the total runtime reasonable, test every -- * possible non-zero, non-reserved bitmap combination -- * only with the native PMU version and the full bit -- * vector length. -+ * To keep the total runtime reasonable, especially after -+ * the total number of arch-events increasing to 13, It's -+ * impossible to test every possible non-zero, non-reserved -+ * bitmap combination. Only test the first 8-bits combination -+ * with the native PMU version and the full bit vector length. - */ - if (v == pmu_version) { -- for (k = 1; k < (BIT(NR_INTEL_ARCH_EVENTS) - 1); k++) -+ int max_events = min(NR_INTEL_ARCH_EVENTS, 8); -+ -+ for (k = 1; k < (BIT(max_events) - 1); k++) - test_arch_events(v, perf_caps[i], NR_INTEL_ARCH_EVENTS, k); - } -+ - /* - * Test single bits for all PMU version and lengths up - * the number of events +1 (to verify KVM doesn't do --- -2.43.0 - diff --git a/SPECS/kernel/0035-fs-ntfs3-Initialize-allocated-memory-before-use.patch b/SPECS/kernel/0035-fs-ntfs3-Initialize-allocated-memory-before-use.patch deleted file mode 100644 index 47ef528f2..000000000 --- a/SPECS/kernel/0035-fs-ntfs3-Initialize-allocated-memory-before-use.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 9f11482aa1bf1a2770fb33ee4a4aa05cd6259d79 Mon Sep 17 00:00:00 2001 -From: Bartlomiej Kubik -Date: Wed, 5 Nov 2025 22:18:08 +0100 -Subject: [PATCH 35/51] fs/ntfs3: Initialize allocated memory before use - -KMSAN reports: Multiple uninitialized values detected: - -- KMSAN: uninit-value in ntfs_read_hdr (3) -- KMSAN: uninit-value in bcmp (3) - -Memory is allocated by __getname(), which is a wrapper for -kmem_cache_alloc(). This memory is used before being properly -cleared. Change kmem_cache_alloc() to kmem_cache_zalloc() to -properly allocate and clear memory before use. - -Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") -Fixes: 78ab59fee07f ("fs/ntfs3: Rework file operations") -Tested-by: syzbot+332bd4e9d148f11a87dc@syzkaller.appspotmail.com -Reported-by: syzbot+332bd4e9d148f11a87dc@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=332bd4e9d148f11a87dc - -Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") -Fixes: 78ab59fee07f ("fs/ntfs3: Rework file operations") -Tested-by: syzbot+0399100e525dd9696764@syzkaller.appspotmail.com -Reported-by: syzbot+0399100e525dd9696764@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=0399100e525dd9696764 - -Reviewed-by: Khalid Aziz -Signed-off-by: Bartlomiej Kubik -Signed-off-by: Konstantin Komarov ---- - fs/ntfs3/inode.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c -index b08b00912165..7a89d31ccc1c 100644 ---- a/fs/ntfs3/inode.c -+++ b/fs/ntfs3/inode.c -@@ -1273,7 +1273,7 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir, - fa |= FILE_ATTRIBUTE_READONLY; - - /* Allocate PATH_MAX bytes. */ -- new_de = __getname(); -+ new_de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!new_de) { - err = -ENOMEM; - goto out1; -@@ -1714,7 +1714,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) - struct NTFS_DE *de; - - /* Allocate PATH_MAX bytes. */ -- de = __getname(); -+ de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!de) - return -ENOMEM; - -@@ -1752,7 +1752,7 @@ int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry) - return -EINVAL; - - /* Allocate PATH_MAX bytes. */ -- de = __getname(); -+ de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!de) - return -ENOMEM; - --- -2.43.0 - diff --git a/SPECS/kernel/0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi b/SPECS/kernel/0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi new file mode 100644 index 000000000..ecd83eefc --- /dev/null +++ b/SPECS/kernel/0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi @@ -0,0 +1,107 @@ +From 4f6cc876452148cc7132bfef9537c38c8736bb40 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Sat, 5 Apr 2025 17:18:24 +0000 +Subject: [PATCH 35/44] x86/nmi: Add support to handle NMIs with source + information + +The NMI-source bitmap is delivered as FRED event data to the kernel. +When available, use NMI-source based filtering to determine the exact +handlers to run. + +Activate NMI-source based filtering only for Local NMIs. While handling +platform NMI types (such as SERR and IOCHK), do not use the source +bitmap. They have only one handler registered per type, so there is no +need to disambiguate between multiple handlers. + +Some third-party chipsets may send NMI messages with a fixed vector of +2, which would result in bit 2 being set in the NMI-source bitmap. Skip +the local NMI handlers in this situation. + +Bit 0 of the source bitmap is set by the hardware whenever a source +vector was not used while generating an NMI, or the originator could not +be reliably identified. Poll all the registered handlers in that case. + +When multiple handlers need to be executed, adhere to the existing +priority scheme and execute the handlers registered with NMI_FLAG_FIRST +before others. + +The logic for handling legacy NMIs is unaffected since the source bitmap +would always have all bits set. + +Suggested-by: Peter Zijlstra (Intel) +Signed-off-by: Sohil Mehta +Reviewed-by: Xin Li (Intel) +--- +v7: Add review tag from Xin. + +v6: Get rid of a separate NMI source matching function (PeterZ) + Set source_bitmap to ULONG_MAX to match all sources by default + +v5: Significantly simplify NMI-source handling logic. + Get rid of a separate lookup table for NMI-source vectors. + Adhere to existing priority scheme for handling NMIs. +--- + arch/x86/kernel/nmi.c | 30 ++++++++++++++++++++++++++++++ + 1 file changed, 30 insertions(+) + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index 8071ad32aa119..3d2b636e93791 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -130,6 +130,7 @@ static void nmi_check_duration(struct nmiaction *action, u64 duration) + static int nmi_handle(unsigned int type, struct pt_regs *regs) + { + struct nmi_desc *desc = nmi_to_desc(type); ++ unsigned long source_bitmap = ULONG_MAX; + nmi_handler_t ehandler; + struct nmiaction *a; + int handled=0; +@@ -148,16 +149,45 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) + + rcu_read_lock(); + ++ /* ++ * Activate NMI source-based filtering only for Local NMIs. ++ * ++ * Platform NMI types (such as SERR and IOCHK) have only one ++ * handler registered per type, so there is no need to ++ * disambiguate between multiple handlers. ++ * ++ * Also, if a platform source ends up setting bit 2 in the ++ * source bitmap, the local NMI handlers would be skipped since ++ * none of them use this reserved vector. ++ * ++ * For Unknown NMIs, avoid using the source bitmap to ensure all ++ * potential handlers have a chance to claim responsibility. ++ */ ++ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) && type == NMI_LOCAL) { ++ source_bitmap = fred_event_data(regs); ++ ++ /* Reset the bitmap if a valid source could not be identified */ ++ if (WARN_ON_ONCE(!source_bitmap) || (source_bitmap & BIT(NMIS_NO_SOURCE))) ++ source_bitmap = ULONG_MAX; ++ } ++ + /* + * NMIs are edge-triggered, which means if you have enough + * of them concurrently, you can lose some because only one + * can be latched at any given time. Walk the whole list + * to handle those situations. ++ * ++ * However, NMI-source reporting does not have this limitation. ++ * When NMI sources have been identified, only run the handlers ++ * that match the reported vectors. + */ + list_for_each_entry_rcu(a, &desc->head, list) { + int thishandled; + u64 delta; + ++ if (!(source_bitmap & BIT(a->source_vector))) ++ continue; ++ + delta = sched_clock(); + thishandled = a->handler(type, regs); + handled += thishandled; +-- +2.43.0 + diff --git a/SPECS/kernel/0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi b/SPECS/kernel/0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi deleted file mode 100644 index ec21fe447..000000000 --- a/SPECS/kernel/0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi +++ /dev/null @@ -1,233 +0,0 @@ -From 56c4109f5224b35e215b6602acccb6fd5d7e7b20 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Wed, 11 Jun 2025 23:10:01 -0700 -Subject: [PATCH 35/44] x86/nmi: Assign and register NMI-source vectors - -Prior to NMI-source support, the vector information was ignored by the -hardware while delivering NMIs. With NMI-source reporting, the initial -architecture supports a 16-bit source bitmap to identify the source of -the NMI. Upon receiving an NMI, this bitmap is delivered to the kernel -as part of the FRED event delivery mechanism. - -Assign a vector space of 1-15 that is specific to NMI-source and -independent of the IDT vector space. Though unlikely, the hardware may -extend the NMI-source bitmap in the future. Add a code comment to -clarify how the kernel support can be easily extended. - -Being a bitmap, the NMI-source vectors do not have any inherent priority -associated with them. The order of executing the NMI handlers is up to -the kernel. Existing NMI handling already has a priority mechanism for -the NMI handlers, with CPU-specific (NMI_LOCAL) handlers executed first, -followed by platform NMI handlers and unknown NMI (NMI_UNKNOWN) handlers -being last. Within each of these NMI types, the handlers registered with -NMI_FLAG_FIRST are given priority. - -NMI-source follows the same priority scheme to avoid unnecessary -complexity. Therefore, the NMI-source vectors are assigned arbitrarily, -except for vector 2. - -Vector 2 is reserved for external NMIs corresponding to the Local APIC - -LINT1 pin. Some third-party chipsets may send NMI messages with a fixed -vector value of 2. Using vector 2 for something else would lead to -confusion about the exact source. Do not assign it to any handler. - -NMI-source vectors are only assigned for NMI_LOCAL type handlers. -Platform NMI handlers have a single handler registered per type. They -don't need additional source information to differentiate among them. - -Use the assigned vectors to register the respective NMI handlers. Warn -if the vector values are unexpected. - -A couple of NMI handlers, such as the microcode rendezvous and the crash -reboot, do not use the typical NMI registration interface. Leave them -as-is for now. - -Originally-by: Jacob Pan -Signed-off-by: Sohil Mehta -Reviewed-by: Xin Li (Intel) ---- -v7: Use an enum to define the NMI-source vectors. (DaveH) - Add a BUILD_BUG_ON to validate the allocated vector count. (DaveH) - Add review tag from Xin. - -v6: Store source vector unconditionally. (PeterZ) - Add a warning for unexpected source vector values. (PeterZ) - -v5: Move the vector defines to nmi.h. - Combine vector allocation and registration into one patch. - Simplify NMI vector names. - Describe usage of vector 2 for external NMIs. - Get rid of vector priorities. ---- - arch/x86/events/core.c | 2 +- - arch/x86/include/asm/nmi.h | 46 ++++++++++++++++++++++++++++++++ - arch/x86/kernel/apic/hw_nmi.c | 2 +- - arch/x86/kernel/cpu/mce/inject.c | 2 +- - arch/x86/kernel/kgdb.c | 2 +- - arch/x86/kernel/nmi.c | 7 +++++ - arch/x86/kernel/nmi_selftest.c | 2 +- - arch/x86/kernel/smp.c | 2 +- - 8 files changed, 59 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index c7205c7c1376..d02bac741bfe 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -2253,7 +2253,7 @@ static int __init init_hw_perf_events(void) - x86_pmu.config_mask = X86_RAW_EVENT_MASK; - - perf_events_lapic_init(); -- register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_NO_SOURCE); -+ register_nmi_handler(NMI_LOCAL, perf_event_nmi_handler, 0, "PMI", NMIS_VECTOR_PMI); - - unconstrained = (struct event_constraint) - __EVENT_CONSTRAINT(0, x86_pmu.cntr_mask64, -diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h -index 42820c4f59b9..a48958a236fd 100644 ---- a/arch/x86/include/asm/nmi.h -+++ b/arch/x86/include/asm/nmi.h -@@ -53,12 +53,58 @@ typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); - * origin of an NMI and to route the NMI directly to the appropriate - * handler. - * -+ * On CPUs that support NMI-source reporting with FRED, receiving an NMI -+ * with a valid vector sets the corresponding bit in the NMI-source -+ * bitmap. The bitmap is delivered as FRED event data on the stack. -+ * -+ * Multiple NMIs are coalesced in the NMI-source bitmap until the next -+ * NMI delivery. If an NMI is received without a vector or beyond the -+ * defined range, the CPU sets bit 0 of the NMI-source bitmap. -+ * -+ * Third-party chipsets may send NMI messages with a fixed vector of 2. -+ * Using vector 2 for some other purpose would cause confusion between -+ * those external NMI messages and the other purpose. Avoid using it. -+ * -+ * The vectors are in no particular priority order. Add new vector -+ * assignments sequentially in the list below before the COUNT. -+ * - * @NMIS_NO_SOURCE: Reserved for undefined or unidentified sources. -+ * @NMIS_VECTOR_TEST: NMI selftest. -+ * @NMIS_VECTOR_EXTERNAL: Reserved to match external NMI vector 2. -+ * @NMIS_VECTOR_SMP_STOP: Panic stop CPU. -+ * @NMIS_VECTOR_BT: CPU backtrace. -+ * @NMIS_VECTOR_KGDB: Kernel debugger. -+ * @NMIS_VECTOR_MCE: MCE injection. -+ * @NMIS_VECTOR_PMI: PerfMon counters. -+ * -+ * @NMIS_VECTOR_COUNT: Count of the defined vectors. - */ - enum nmi_source_vectors { - NMIS_NO_SOURCE = 0, -+ NMIS_VECTOR_TEST, -+ NMIS_VECTOR_EXTERNAL = 2, -+ NMIS_VECTOR_SMP_STOP, -+ NMIS_VECTOR_BT, -+ NMIS_VECTOR_KGDB, -+ NMIS_VECTOR_MCE, -+ NMIS_VECTOR_PMI, -+ -+ NMIS_VECTOR_COUNT - }; - -+/* -+ * The early (and likely all future) hardware implementations of -+ * NMI-source reporting would only support a 16-bit source bitmap, with -+ * 1-15 being valid source vectors. -+ * -+ * If the hardware ever supports a larger bitmap, the kernel support can -+ * easily be extended to 64 bits by modifying the MAX below. However, -+ * care must be taken to reallocate the latency sensitive NMI sources -+ * within the first 15 vectors. Any source vector beyond the supported -+ * maximum on prior systems would set bit 0 in the NMI-source bitmap. -+ */ -+#define NMIS_VECTORS_MAX 16 -+ - struct nmiaction { - struct list_head list; - nmi_handler_t handler; -diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c -index d09e771723ed..4e04f13d2de9 100644 ---- a/arch/x86/kernel/apic/hw_nmi.c -+++ b/arch/x86/kernel/apic/hw_nmi.c -@@ -53,7 +53,7 @@ NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler); - - static int __init register_nmi_cpu_backtrace_handler(void) - { -- register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_NO_SOURCE); -+ register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler, 0, "arch_bt", NMIS_VECTOR_BT); - return 0; - } - early_initcall(register_nmi_cpu_backtrace_handler); -diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c -index ba70ef8a1964..320068e01c22 100644 ---- a/arch/x86/kernel/cpu/mce/inject.c -+++ b/arch/x86/kernel/cpu/mce/inject.c -@@ -775,7 +775,7 @@ static int __init inject_init(void) - - debugfs_init(); - -- register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_NO_SOURCE); -+ register_nmi_handler(NMI_LOCAL, mce_raise_notify, 0, "mce_notify", NMIS_VECTOR_MCE); - mce_register_injector_chain(&inject_nb); - - setup_inj_struct(&i_mce); -diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c -index 80de0ac2e8de..788b83e9404b 100644 ---- a/arch/x86/kernel/kgdb.c -+++ b/arch/x86/kernel/kgdb.c -@@ -602,7 +602,7 @@ int kgdb_arch_init(void) - if (retval) - goto out; - -- retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_NO_SOURCE); -+ retval = register_nmi_handler(NMI_LOCAL, kgdb_nmi_handler, 0, "kgdb", NMIS_VECTOR_KGDB); - if (retval) - goto out1; - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index be93ec7255bf..8071ad32aa11 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -182,6 +182,13 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action) - if (WARN_ON_ONCE(!action->handler || !list_empty(&action->list))) - return -EINVAL; - -+ /* NMI-source reporting should only be used for NMI_LOCAL */ -+ WARN_ON_ONCE((type != NMI_LOCAL) && (action->source_vector != NMIS_NO_SOURCE)); -+ -+ /* Check for valid vector values. See comment above NMIS_VECTORS_MAX */ -+ BUILD_BUG_ON(NMIS_VECTOR_COUNT > NMIS_VECTORS_MAX); -+ WARN_ON_ONCE(action->source_vector >= NMIS_VECTOR_COUNT); -+ - raw_spin_lock_irqsave(&desc->lock, flags); - - /* -diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c -index c4fffa868160..f3918888e494 100644 ---- a/arch/x86/kernel/nmi_selftest.c -+++ b/arch/x86/kernel/nmi_selftest.c -@@ -64,7 +64,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) - unsigned long timeout; - - if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback, NMI_FLAG_FIRST, -- "nmi_selftest", NMIS_NO_SOURCE, __initdata)) { -+ "nmi_selftest", NMIS_VECTOR_TEST, __initdata)) { - nmi_fail = FAILURE; - return; - } -diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c -index 6c1d3ffbee6c..694e31bf445c 100644 ---- a/arch/x86/kernel/smp.c -+++ b/arch/x86/kernel/smp.c -@@ -143,7 +143,7 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_reboot) - static int register_stop_handler(void) - { - return register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, NMI_FLAG_FIRST, -- "smp_stop", NMIS_NO_SOURCE); -+ "smp_stop", NMIS_VECTOR_SMP_STOP); - } - - static void native_stop_other_cpus(int wait) --- -2.43.0 - diff --git a/SPECS/kernel/0036-KVM-selftests-Relax-precise-event-count-validation-as.perf b/SPECS/kernel/0036-KVM-selftests-Relax-precise-event-count-validation-as.perf deleted file mode 100644 index 60e4bad68..000000000 --- a/SPECS/kernel/0036-KVM-selftests-Relax-precise-event-count-validation-as.perf +++ /dev/null @@ -1,159 +0,0 @@ -From 3ed66f373b3c90405254ff19a9a0393d9f07ae73 Mon Sep 17 00:00:00 2001 -From: dongsheng -Date: Fri, 18 Jul 2025 08:19:04 +0800 -Subject: [PATCH 036/100] KVM: selftests: Relax precise event count validation - as overcount issue - -For Intel Atom CPUs, the PMU events "Instruction Retired" or -"Branch Instruction Retired" may be overcounted for some certain -instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD -and complex SGX/SMX/CSTATE instructions/flows. - -The detailed information can be found in the errata (section SRF7): -https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/ - -For the Atom platforms before Sierra Forest (including Sierra Forest), -Both 2 events "Instruction Retired" and "Branch Instruction Retired" would -be overcounted on these certain instructions, but for Clearwater Forest -only "Instruction Retired" event is overcounted on these instructions. - -As the overcount issue on VM-Exit/VM-Entry, it has no way to validate -the precise count for these 2 events on these affected Atom platforms, -so just relax the precise event count check for these 2 events on these -Atom platforms. - -Signed-off-by: dongsheng -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/include/x86/pmu.h | 9 +++++ - tools/testing/selftests/kvm/lib/x86/pmu.c | 38 +++++++++++++++++++ - .../selftests/kvm/x86/pmu_counters_test.c | 17 ++++++++- - 3 files changed, 62 insertions(+), 2 deletions(-) - -diff --git a/tools/testing/selftests/kvm/include/x86/pmu.h b/tools/testing/selftests/kvm/include/x86/pmu.h -index 2aabda2da002..db14c08abc59 100644 ---- a/tools/testing/selftests/kvm/include/x86/pmu.h -+++ b/tools/testing/selftests/kvm/include/x86/pmu.h -@@ -104,4 +104,13 @@ enum amd_pmu_zen_events { - extern const uint64_t intel_pmu_arch_events[]; - extern const uint64_t amd_pmu_zen_events[]; - -+/* -+ * Flags for "Instruction Retired" and "Branch Instruction Retired" -+ * overcount flaws. -+ */ -+#define INST_RETIRED_OVERCOUNT BIT(0) -+#define BR_RETIRED_OVERCOUNT BIT(1) -+ -+extern uint32_t detect_inst_overcount_flags(void); -+ - #endif /* SELFTEST_KVM_PMU_H */ -diff --git a/tools/testing/selftests/kvm/lib/x86/pmu.c b/tools/testing/selftests/kvm/lib/x86/pmu.c -index 5ab44bf54773..fd4ed577c88f 100644 ---- a/tools/testing/selftests/kvm/lib/x86/pmu.c -+++ b/tools/testing/selftests/kvm/lib/x86/pmu.c -@@ -8,6 +8,7 @@ - #include - - #include "kvm_util.h" -+#include "processor.h" - #include "pmu.h" - - const uint64_t intel_pmu_arch_events[] = { -@@ -34,3 +35,40 @@ const uint64_t amd_pmu_zen_events[] = { - AMD_ZEN_BRANCHES_MISPREDICTED, - }; - kvm_static_assert(ARRAY_SIZE(amd_pmu_zen_events) == NR_AMD_ZEN_EVENTS); -+ -+/* -+ * For Intel Atom CPUs, the PMU events "Instruction Retired" or -+ * "Branch Instruction Retired" may be overcounted for some certain -+ * instructions, like FAR CALL/JMP, RETF, IRET, VMENTRY/VMEXIT/VMPTRLD -+ * and complex SGX/SMX/CSTATE instructions/flows. -+ * -+ * The detailed information can be found in the errata (section SRF7): -+ * https://edc.intel.com/content/www/us/en/design/products-and-solutions/processors-and-chipsets/sierra-forest/xeon-6700-series-processor-with-e-cores-specification-update/errata-details/ -+ * -+ * For the Atom platforms before Sierra Forest (including Sierra Forest), -+ * Both 2 events "Instruction Retired" and "Branch Instruction Retired" would -+ * be overcounted on these certain instructions, but for Clearwater Forest -+ * only "Instruction Retired" event is overcounted on these instructions. -+ */ -+uint32_t detect_inst_overcount_flags(void) -+{ -+ uint32_t eax, ebx, ecx, edx; -+ uint32_t flags = 0; -+ -+ cpuid(1, &eax, &ebx, &ecx, &edx); -+ if (x86_family(eax) == 0x6) { -+ switch (x86_model(eax)) { -+ case 0xDD: /* Clearwater Forest */ -+ flags = INST_RETIRED_OVERCOUNT; -+ break; -+ case 0xAF: /* Sierra Forest */ -+ case 0x4D: /* Avaton, Rangely */ -+ case 0x5F: /* Denverton */ -+ case 0x86: /* Jacobsville */ -+ flags = INST_RETIRED_OVERCOUNT | BR_RETIRED_OVERCOUNT; -+ break; -+ } -+ } -+ -+ return flags; -+} -diff --git a/tools/testing/selftests/kvm/x86/pmu_counters_test.c b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -index 342a72420177..074cdf323406 100644 ---- a/tools/testing/selftests/kvm/x86/pmu_counters_test.c -+++ b/tools/testing/selftests/kvm/x86/pmu_counters_test.c -@@ -52,6 +52,9 @@ struct kvm_intel_pmu_event { - struct kvm_x86_pmu_feature fixed_event; - }; - -+ -+static uint8_t inst_overcount_flags; -+ - /* - * Wrap the array to appease the compiler, as the macros used to construct each - * kvm_x86_pmu_feature use syntax that's only valid in function scope, and the -@@ -163,10 +166,18 @@ static void guest_assert_event_count(uint8_t idx, uint32_t pmc, uint32_t pmc_msr - - switch (idx) { - case INTEL_ARCH_INSTRUCTIONS_RETIRED_INDEX: -- GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); -+ /* Relax precise count check due to VM-EXIT/VM-ENTRY overcount issue */ -+ if (inst_overcount_flags & INST_RETIRED_OVERCOUNT) -+ GUEST_ASSERT(count >= NUM_INSNS_RETIRED); -+ else -+ GUEST_ASSERT_EQ(count, NUM_INSNS_RETIRED); - break; - case INTEL_ARCH_BRANCHES_RETIRED_INDEX: -- GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); -+ /* Relax precise count check due to VM-EXIT/VM-ENTRY overcount issue */ -+ if (inst_overcount_flags & BR_RETIRED_OVERCOUNT) -+ GUEST_ASSERT(count >= NUM_BRANCH_INSNS_RETIRED); -+ else -+ GUEST_ASSERT_EQ(count, NUM_BRANCH_INSNS_RETIRED); - break; - case INTEL_ARCH_LLC_REFERENCES_INDEX: - case INTEL_ARCH_LLC_MISSES_INDEX: -@@ -335,6 +346,7 @@ static void test_arch_events(uint8_t pmu_version, uint64_t perf_capabilities, - length); - vcpu_set_cpuid_property(vcpu, X86_PROPERTY_PMU_EVENTS_MASK, - unavailable_mask); -+ sync_global_to_guest(vm, inst_overcount_flags); - - run_vcpu(vcpu); - -@@ -673,6 +685,7 @@ int main(int argc, char *argv[]) - - kvm_pmu_version = kvm_cpu_property(X86_PROPERTY_PMU_VERSION); - kvm_has_perf_caps = kvm_cpu_has(X86_FEATURE_PDCM); -+ inst_overcount_flags = detect_inst_overcount_flags(); - - test_intel_counters(); - --- -2.43.0 - diff --git a/SPECS/kernel/0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi b/SPECS/kernel/0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi deleted file mode 100644 index 7aaec7471..000000000 --- a/SPECS/kernel/0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi +++ /dev/null @@ -1,107 +0,0 @@ -From 89398493ca183026c1d93d898389402f5eff31f7 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Sat, 5 Apr 2025 17:18:24 +0000 -Subject: [PATCH 36/44] x86/nmi: Add support to handle NMIs with source - information - -The NMI-source bitmap is delivered as FRED event data to the kernel. -When available, use NMI-source based filtering to determine the exact -handlers to run. - -Activate NMI-source based filtering only for Local NMIs. While handling -platform NMI types (such as SERR and IOCHK), do not use the source -bitmap. They have only one handler registered per type, so there is no -need to disambiguate between multiple handlers. - -Some third-party chipsets may send NMI messages with a fixed vector of -2, which would result in bit 2 being set in the NMI-source bitmap. Skip -the local NMI handlers in this situation. - -Bit 0 of the source bitmap is set by the hardware whenever a source -vector was not used while generating an NMI, or the originator could not -be reliably identified. Poll all the registered handlers in that case. - -When multiple handlers need to be executed, adhere to the existing -priority scheme and execute the handlers registered with NMI_FLAG_FIRST -before others. - -The logic for handling legacy NMIs is unaffected since the source bitmap -would always have all bits set. - -Suggested-by: Peter Zijlstra (Intel) -Signed-off-by: Sohil Mehta -Reviewed-by: Xin Li (Intel) ---- -v7: Add review tag from Xin. - -v6: Get rid of a separate NMI source matching function (PeterZ) - Set source_bitmap to ULONG_MAX to match all sources by default - -v5: Significantly simplify NMI-source handling logic. - Get rid of a separate lookup table for NMI-source vectors. - Adhere to existing priority scheme for handling NMIs. ---- - arch/x86/kernel/nmi.c | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index 8071ad32aa11..3d2b636e9379 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -130,6 +130,7 @@ static void nmi_check_duration(struct nmiaction *action, u64 duration) - static int nmi_handle(unsigned int type, struct pt_regs *regs) - { - struct nmi_desc *desc = nmi_to_desc(type); -+ unsigned long source_bitmap = ULONG_MAX; - nmi_handler_t ehandler; - struct nmiaction *a; - int handled=0; -@@ -148,16 +149,45 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) - - rcu_read_lock(); - -+ /* -+ * Activate NMI source-based filtering only for Local NMIs. -+ * -+ * Platform NMI types (such as SERR and IOCHK) have only one -+ * handler registered per type, so there is no need to -+ * disambiguate between multiple handlers. -+ * -+ * Also, if a platform source ends up setting bit 2 in the -+ * source bitmap, the local NMI handlers would be skipped since -+ * none of them use this reserved vector. -+ * -+ * For Unknown NMIs, avoid using the source bitmap to ensure all -+ * potential handlers have a chance to claim responsibility. -+ */ -+ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) && type == NMI_LOCAL) { -+ source_bitmap = fred_event_data(regs); -+ -+ /* Reset the bitmap if a valid source could not be identified */ -+ if (WARN_ON_ONCE(!source_bitmap) || (source_bitmap & BIT(NMIS_NO_SOURCE))) -+ source_bitmap = ULONG_MAX; -+ } -+ - /* - * NMIs are edge-triggered, which means if you have enough - * of them concurrently, you can lose some because only one - * can be latched at any given time. Walk the whole list - * to handle those situations. -+ * -+ * However, NMI-source reporting does not have this limitation. -+ * When NMI sources have been identified, only run the handlers -+ * that match the reported vectors. - */ - list_for_each_entry_rcu(a, &desc->head, list) { - int thishandled; - u64 delta; - -+ if (!(source_bitmap & BIT(a->source_vector))) -+ continue; -+ - delta = sched_clock(); - thishandled = a->handler(type, regs); - handled += thishandled; --- -2.43.0 - diff --git a/SPECS/kernel/0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi b/SPECS/kernel/0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi new file mode 100644 index 000000000..5bf42935e --- /dev/null +++ b/SPECS/kernel/0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi @@ -0,0 +1,216 @@ +From 31a8c7e6103a43d895d445522a8bce022e4eaacc Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 28 Apr 2025 15:55:03 +0000 +Subject: [PATCH 36/44] x86/nmi: Prepare for the new NMI-source vector encoding + +When using the send_IPI_* APIC calls, callers typically use NMI vector +0x2 to trigger NMIs. The APIC APIs convert the NMI vector to the NMI +delivery mode, which is eventually used to program the APIC. + +Before FRED, the hardware would ignore the vector used with NMI delivery +mode. However, with NMI-source reporting, the vector information is +relayed to the destination CPU, which sets the corresponding bit in the +NMI-source bitmap. Unfortunately, the kernel now needs to maintain a new +set of NMI vectors and differentiate them from the IDT vectors. + +Instead of creating a parallel set of send_NMI_* APIs to handle +NMI-source vectors, enhance the existing send_IPI_* APIs with a new +encoding scheme to handle the NMI delivery mode along with the +NMI-source vector. + +NMI-source vectors would be encoded as: + APIC_DM_NMI (0x400) | NMI_SOURCE_VECTOR (0x1-0xF) + +Also, introduce a helper to prepare the ICR value with the encoded +delivery mode and vector. Update the guest paravirtual APIC code to use +the new helper as well. + +While at it, rename APIC_DM_FIXED_MASK to the more appropriate +APIC_DM_MASK. + +Suggested-by: Sean Christopherson +Co-developed-by: Xin Li (Intel) +Signed-off-by: Xin Li (Intel) +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: Remove a redundant else statement. (PeterZ) + +v5: Use a simiplified encoding scheme for NMI-source vectors. +--- + arch/x86/include/asm/apic.h | 30 +++++++++++++++++++++++++++++ + arch/x86/include/asm/apicdef.h | 2 +- + arch/x86/kernel/apic/ipi.c | 4 ++-- + arch/x86/kernel/apic/local.h | 24 ++++++++++++----------- + arch/x86/kernel/apic/x2apic_savic.c | 2 +- + arch/x86/kernel/kvm.c | 9 +-------- + drivers/thermal/intel/therm_throt.c | 2 +- + 7 files changed, 49 insertions(+), 24 deletions(-) + +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +index a26e66d66444a..741a24f47a24d 100644 +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -480,6 +480,36 @@ static __always_inline void apic_update_vector(unsigned int cpu, unsigned int ve + apic->update_vector(cpu, vector, set); + } + ++/* ++ * Prepare the delivery mode and vector for the ICR. ++ * ++ * NMI-source vectors have the NMI delivery mode encoded within them to ++ * differentiate them from the IDT vectors. IDT vector 0x2 (NMI_VECTOR) ++ * is treated as an NMI request but without any NMI-source information. ++ */ ++static inline u16 __prepare_ICR_DM_vector(u16 dm_vector) ++{ ++ u16 vector = dm_vector & APIC_VECTOR_MASK; ++ u16 dm = dm_vector & APIC_DM_MASK; ++ ++ if (dm == APIC_DM_NMI) { ++ /* ++ * Pre-FRED, the actual vector is ignored for NMIs, but ++ * zero it if NMI-source reporting is not supported to ++ * avoid breakage on misbehaving hardware or hypervisors. ++ */ ++ if (!cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) ++ vector = 0; ++ ++ return dm | vector; ++ } ++ ++ if (vector == NMI_VECTOR) ++ return APIC_DM_NMI; ++ ++ return APIC_DM_FIXED | vector; ++} ++ + #else /* CONFIG_X86_LOCAL_APIC */ + + static inline u32 apic_read(u32 reg) { return 0; } +diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h +index be39a543fbe5d..2cf59601c1486 100644 +--- a/arch/x86/include/asm/apicdef.h ++++ b/arch/x86/include/asm/apicdef.h +@@ -87,8 +87,8 @@ + #define APIC_ICR_BUSY 0x01000 + #define APIC_DEST_LOGICAL 0x00800 + #define APIC_DEST_PHYSICAL 0x00000 ++#define APIC_DM_MASK 0x00700 + #define APIC_DM_FIXED 0x00000 +-#define APIC_DM_FIXED_MASK 0x00700 + #define APIC_DM_LOWEST 0x00100 + #define APIC_DM_SMI 0x00200 + #define APIC_DM_REMRD 0x00300 +diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c +index 98a57cb4aa861..4e8bc42f3bd56 100644 +--- a/arch/x86/kernel/apic/ipi.c ++++ b/arch/x86/kernel/apic/ipi.c +@@ -158,7 +158,7 @@ static void __default_send_IPI_shortcut(unsigned int shortcut, int vector) + * issues where otherwise the system hangs when the panic CPU tries + * to stop the others before launching the kdump kernel. + */ +- if (unlikely(vector == NMI_VECTOR)) ++ if (unlikely(is_nmi_vector(vector))) + apic_mem_wait_icr_idle_timeout(); + else + apic_mem_wait_icr_idle(); +@@ -175,7 +175,7 @@ void __default_send_IPI_dest_field(unsigned int dest_mask, int vector, + unsigned int dest_mode) + { + /* See comment in __default_send_IPI_shortcut() */ +- if (unlikely(vector == NMI_VECTOR)) ++ if (unlikely(is_nmi_vector(vector))) + apic_mem_wait_icr_idle_timeout(); + else + apic_mem_wait_icr_idle(); +diff --git a/arch/x86/kernel/apic/local.h b/arch/x86/kernel/apic/local.h +index bdcf609eb2835..9a54c589a4bf2 100644 +--- a/arch/x86/kernel/apic/local.h ++++ b/arch/x86/kernel/apic/local.h +@@ -24,22 +24,24 @@ extern u32 x2apic_max_apicid; + + /* IPI */ + ++u16 __prepare_ICR_DM_vector(u16 vector); ++ + DECLARE_STATIC_KEY_FALSE(apic_use_ipi_shorthand); + ++/* NMI-source vectors have the delivery mode encoded within them */ ++static inline bool is_nmi_vector(u16 vector) ++{ ++ if ((vector & APIC_DM_MASK) == APIC_DM_NMI) ++ return true; ++ if ((vector & APIC_VECTOR_MASK) == NMI_VECTOR) ++ return true; ++ return false; ++} ++ + static inline unsigned int __prepare_ICR(unsigned int shortcut, int vector, + unsigned int dest) + { +- unsigned int icr = shortcut | dest; +- +- switch (vector) { +- default: +- icr |= APIC_DM_FIXED | vector; +- break; +- case NMI_VECTOR: +- icr |= APIC_DM_NMI; +- break; +- } +- return icr; ++ return shortcut | dest | __prepare_ICR_DM_vector(vector); + } + + void default_init_apic_ldr(void); +diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c +index dbc5678bc3b68..a7ed866b6e124 100644 +--- a/arch/x86/kernel/apic/x2apic_savic.c ++++ b/arch/x86/kernel/apic/x2apic_savic.c +@@ -174,7 +174,7 @@ static void savic_icr_write(u32 icr_low, u32 icr_high) + + dsh = icr_low & APIC_DEST_ALLBUT; + vector = icr_low & APIC_VECTOR_MASK; +- nmi = ((icr_low & APIC_DM_FIXED_MASK) == APIC_DM_NMI); ++ nmi = ((icr_low & APIC_DM_MASK) == APIC_DM_NMI); + + switch (dsh) { + case APIC_DEST_SELF: +diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c +index b67d7c59dca0b..76674265dceda 100644 +--- a/arch/x86/kernel/kvm.c ++++ b/arch/x86/kernel/kvm.c +@@ -517,14 +517,7 @@ static void __send_ipi_mask(const struct cpumask *mask, int vector) + + local_irq_save(flags); + +- switch (vector) { +- default: +- icr = APIC_DM_FIXED | vector; +- break; +- case NMI_VECTOR: +- icr = APIC_DM_NMI; +- break; +- } ++ icr = __prepare_ICR_DM_vector(vector); + + for_each_cpu(cpu, mask) { + apic_id = per_cpu(x86_cpu_to_apicid, cpu); +diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c +index debc94e2dc169..5c0d2de2986e3 100644 +--- a/drivers/thermal/intel/therm_throt.c ++++ b/drivers/thermal/intel/therm_throt.c +@@ -740,7 +740,7 @@ void intel_init_thermal(struct cpuinfo_x86 *c) + * BIOS has programmed on AP based on BSP's info we saved since BIOS + * is always setting the same value for all threads/cores. + */ +- if ((h & APIC_DM_FIXED_MASK) != APIC_DM_FIXED) ++ if ((h & APIC_DM_MASK) != APIC_DM_FIXED) + apic_write(APIC_LVTTHMR, lvtthmr_init); + + +-- +2.43.0 + diff --git a/SPECS/kernel/0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf b/SPECS/kernel/0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf deleted file mode 100644 index b6391b73c..000000000 --- a/SPECS/kernel/0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf +++ /dev/null @@ -1,59 +0,0 @@ -From fb7a5eb05f8661d9027a16da02d90bba5101d0e3 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Fri, 18 Jul 2025 08:19:05 +0800 -Subject: [PATCH 037/100] KVM: selftests: Relax branches event count check for - event_filter test - -As the branches event overcount issue on Atom platforms, once there are -VM-Exits triggered (external interrupts) in the guest loop, the measured -branch event count could be larger than NUM_BRANCHES, this would lead to -the pmu_event_filter_test print warning to info the measured branches -event count is mismatched with expected number (NUM_BRANCHES). - -To eliminate this warning, relax the branches event count check on the -Atom platform which have the branches event overcount issue. - -Signed-off-by: Dapeng Mi -Tested-by: Yi Lai ---- - tools/testing/selftests/kvm/x86/pmu_event_filter_test.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c -index c15513cd74d1..9c1a92f05786 100644 ---- a/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c -+++ b/tools/testing/selftests/kvm/x86/pmu_event_filter_test.c -@@ -60,6 +60,8 @@ struct { - uint64_t instructions_retired; - } pmc_results; - -+static uint8_t inst_overcount_flags; -+ - /* - * If we encounter a #GP during the guest PMU sanity check, then the guest - * PMU is not functional. Inform the hypervisor via GUEST_SYNC(0). -@@ -214,8 +216,10 @@ static void remove_event(struct __kvm_pmu_event_filter *f, uint64_t event) - do { \ - uint64_t br = pmc_results.branches_retired; \ - uint64_t ir = pmc_results.instructions_retired; \ -+ bool br_matched = inst_overcount_flags & BR_RETIRED_OVERCOUNT ? \ -+ br >= NUM_BRANCHES : br == NUM_BRANCHES; \ - \ -- if (br && br != NUM_BRANCHES) \ -+ if (br && !br_matched) \ - pr_info("%s: Branch instructions retired = %lu (expected %u)\n", \ - __func__, br, NUM_BRANCHES); \ - TEST_ASSERT(br, "%s: Branch instructions retired = %lu (expected > 0)", \ -@@ -850,6 +854,9 @@ int main(int argc, char *argv[]) - if (use_amd_pmu()) - test_amd_deny_list(vcpu); - -+ if (use_intel_pmu()) -+ inst_overcount_flags = detect_inst_overcount_flags(); -+ - test_without_filter(vcpu); - test_member_deny_list(vcpu); - test_member_allow_list(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi b/SPECS/kernel/0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi new file mode 100644 index 000000000..1566502c6 --- /dev/null +++ b/SPECS/kernel/0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi @@ -0,0 +1,131 @@ +From 13e7f0e6412f745d21fde28786a8425e3b4ec531 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 28 Apr 2025 20:09:02 +0000 +Subject: [PATCH 37/44] x86/nmi: Enable NMI-source for IPIs delivered as NMIs + +With the IPI handling APIs ready to support the new NMI encoding, encode +the NMI delivery mode directly with the NMI-source vectors to trigger +NMIs. + +Move most of the existing NMI-based IPIs to use the new NMI-source +vectors, except for the microcode rendezvous NMI and the crash reboot +NMI. NMI handling for them is special-cased in exc_nmi() and does not +need NMI-source reporting. + +However, in the future, it might be useful to assign a source vector to +all NMI sources to improve isolation and debuggability. + +Originally-by: Jacob Pan +Suggested-by: Sean Christopherson +Co-developed-by: Xin Li (Intel) +Signed-off-by: Xin Li (Intel) +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: Include asm/nmi.h to avoid compile errors. (LKP) + +v5: Encode APIC_DM_NMI directly with the NMI-source vector. +--- + arch/x86/include/asm/apic.h | 8 ++++++++ + arch/x86/kernel/apic/hw_nmi.c | 2 +- + arch/x86/kernel/cpu/mce/inject.c | 2 +- + arch/x86/kernel/kgdb.c | 2 +- + arch/x86/kernel/nmi_selftest.c | 2 +- + arch/x86/kernel/smp.c | 2 +- + 6 files changed, 13 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +index 741a24f47a24d..fd62f99e5b0fc 100644 +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + + #define ARCH_APICTIMER_STOPS_ON_C3 1 +@@ -23,6 +24,13 @@ + #define APIC_EXTNMI_ALL 1 + #define APIC_EXTNMI_NONE 2 + ++/* Trigger NMIs with source information */ ++#define TEST_NMI (APIC_DM_NMI | NMIS_VECTOR_TEST) ++#define SMP_STOP_NMI (APIC_DM_NMI | NMIS_VECTOR_SMP_STOP) ++#define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) ++#define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) ++#define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) ++ + /* + * Debugging macros + */ +diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c +index 4e04f13d2de98..586f4b25feae6 100644 +--- a/arch/x86/kernel/apic/hw_nmi.c ++++ b/arch/x86/kernel/apic/hw_nmi.c +@@ -33,7 +33,7 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh) + #ifdef arch_trigger_cpumask_backtrace + static void nmi_raise_cpu_backtrace(cpumask_t *mask) + { +- __apic_send_IPI_mask(mask, NMI_VECTOR); ++ __apic_send_IPI_mask(mask, BT_NMI); + } + + void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) +diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c +index 320068e01c22d..81a04836ac740 100644 +--- a/arch/x86/kernel/cpu/mce/inject.c ++++ b/arch/x86/kernel/cpu/mce/inject.c +@@ -270,7 +270,7 @@ static void __maybe_unused raise_mce(struct mce *m) + mce_irq_ipi, NULL, 0); + preempt_enable(); + } else if (m->inject_flags & MCJ_NMI_BROADCAST) +- __apic_send_IPI_mask(mce_inject_cpumask, NMI_VECTOR); ++ __apic_send_IPI_mask(mce_inject_cpumask, MCE_NMI); + } + start = jiffies; + while (!cpumask_empty(mce_inject_cpumask)) { +diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c +index 788b83e9404b6..8a342df84d8e1 100644 +--- a/arch/x86/kernel/kgdb.c ++++ b/arch/x86/kernel/kgdb.c +@@ -416,7 +416,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) + */ + void kgdb_roundup_cpus(void) + { +- apic_send_IPI_allbutself(NMI_VECTOR); ++ apic_send_IPI_allbutself(KGDB_NMI); + } + #endif + +diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c +index f3918888e4942..d5578370b47ff 100644 +--- a/arch/x86/kernel/nmi_selftest.c ++++ b/arch/x86/kernel/nmi_selftest.c +@@ -72,7 +72,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) + /* sync above data before sending NMI */ + wmb(); + +- __apic_send_IPI_mask(mask, NMI_VECTOR); ++ __apic_send_IPI_mask(mask, TEST_NMI); + + /* Don't wait longer than a second */ + timeout = USEC_PER_SEC; +diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c +index 694e31bf445c5..eb49ee5b312fc 100644 +--- a/arch/x86/kernel/smp.c ++++ b/arch/x86/kernel/smp.c +@@ -217,7 +217,7 @@ static void native_stop_other_cpus(int wait) + pr_emerg("Shutting down cpus with NMI\n"); + + for_each_cpu(cpu, &cpus_stop_mask) +- __apic_send_IPI(cpu, NMI_VECTOR); ++ __apic_send_IPI(cpu, SMP_STOP_NMI); + } + /* + * Don't wait longer than 10 ms if the caller didn't +-- +2.43.0 + diff --git a/SPECS/kernel/0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi b/SPECS/kernel/0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi deleted file mode 100644 index 7425f4c37..000000000 --- a/SPECS/kernel/0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi +++ /dev/null @@ -1,202 +0,0 @@ -From 4bfa94030a3b19925581faf2cfe28c2c016e573b Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 28 Apr 2025 15:55:03 +0000 -Subject: [PATCH 37/44] x86/nmi: Prepare for the new NMI-source vector encoding - -When using the send_IPI_* APIC calls, callers typically use NMI vector -0x2 to trigger NMIs. The APIC APIs convert the NMI vector to the NMI -delivery mode, which is eventually used to program the APIC. - -Before FRED, the hardware would ignore the vector used with NMI delivery -mode. However, with NMI-source reporting, the vector information is -relayed to the destination CPU, which sets the corresponding bit in the -NMI-source bitmap. Unfortunately, the kernel now needs to maintain a new -set of NMI vectors and differentiate them from the IDT vectors. - -Instead of creating a parallel set of send_NMI_* APIs to handle -NMI-source vectors, enhance the existing send_IPI_* APIs with a new -encoding scheme to handle the NMI delivery mode along with the -NMI-source vector. - -NMI-source vectors would be encoded as: - APIC_DM_NMI (0x400) | NMI_SOURCE_VECTOR (0x1-0xF) - -Also, introduce a helper to prepare the ICR value with the encoded -delivery mode and vector. Update the guest paravirtual APIC code to use -the new helper as well. - -While at it, rename APIC_DM_FIXED_MASK to the more appropriate -APIC_DM_MASK. - -Suggested-by: Sean Christopherson -Co-developed-by: Xin Li (Intel) -Signed-off-by: Xin Li (Intel) -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: Remove a redundant else statement. (PeterZ) - -v5: Use a simiplified encoding scheme for NMI-source vectors. ---- - arch/x86/include/asm/apic.h | 30 +++++++++++++++++++++++++++++ - arch/x86/include/asm/apicdef.h | 2 +- - arch/x86/kernel/apic/ipi.c | 4 ++-- - arch/x86/kernel/apic/local.h | 24 ++++++++++++----------- - arch/x86/kernel/kvm.c | 9 +-------- - drivers/thermal/intel/therm_throt.c | 2 +- - 6 files changed, 48 insertions(+), 23 deletions(-) - -diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h -index 07ba4935e873..7448b349f864 100644 ---- a/arch/x86/include/asm/apic.h -+++ b/arch/x86/include/asm/apic.h -@@ -470,6 +470,36 @@ static __always_inline bool apic_id_valid(u32 apic_id) - return apic_id <= apic->max_apic_id; - } - -+/* -+ * Prepare the delivery mode and vector for the ICR. -+ * -+ * NMI-source vectors have the NMI delivery mode encoded within them to -+ * differentiate them from the IDT vectors. IDT vector 0x2 (NMI_VECTOR) -+ * is treated as an NMI request but without any NMI-source information. -+ */ -+static inline u16 __prepare_ICR_DM_vector(u16 dm_vector) -+{ -+ u16 vector = dm_vector & APIC_VECTOR_MASK; -+ u16 dm = dm_vector & APIC_DM_MASK; -+ -+ if (dm == APIC_DM_NMI) { -+ /* -+ * Pre-FRED, the actual vector is ignored for NMIs, but -+ * zero it if NMI-source reporting is not supported to -+ * avoid breakage on misbehaving hardware or hypervisors. -+ */ -+ if (!cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) -+ vector = 0; -+ -+ return dm | vector; -+ } -+ -+ if (vector == NMI_VECTOR) -+ return APIC_DM_NMI; -+ -+ return APIC_DM_FIXED | vector; -+} -+ - #else /* CONFIG_X86_LOCAL_APIC */ - - static inline u32 apic_read(u32 reg) { return 0; } -diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h -index 094106b6a538..3fb8fa73f6aa 100644 ---- a/arch/x86/include/asm/apicdef.h -+++ b/arch/x86/include/asm/apicdef.h -@@ -87,8 +87,8 @@ - #define APIC_ICR_BUSY 0x01000 - #define APIC_DEST_LOGICAL 0x00800 - #define APIC_DEST_PHYSICAL 0x00000 -+#define APIC_DM_MASK 0x00700 - #define APIC_DM_FIXED 0x00000 --#define APIC_DM_FIXED_MASK 0x00700 - #define APIC_DM_LOWEST 0x00100 - #define APIC_DM_SMI 0x00200 - #define APIC_DM_REMRD 0x00300 -diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c -index 98a57cb4aa86..4e8bc42f3bd5 100644 ---- a/arch/x86/kernel/apic/ipi.c -+++ b/arch/x86/kernel/apic/ipi.c -@@ -158,7 +158,7 @@ static void __default_send_IPI_shortcut(unsigned int shortcut, int vector) - * issues where otherwise the system hangs when the panic CPU tries - * to stop the others before launching the kdump kernel. - */ -- if (unlikely(vector == NMI_VECTOR)) -+ if (unlikely(is_nmi_vector(vector))) - apic_mem_wait_icr_idle_timeout(); - else - apic_mem_wait_icr_idle(); -@@ -175,7 +175,7 @@ void __default_send_IPI_dest_field(unsigned int dest_mask, int vector, - unsigned int dest_mode) - { - /* See comment in __default_send_IPI_shortcut() */ -- if (unlikely(vector == NMI_VECTOR)) -+ if (unlikely(is_nmi_vector(vector))) - apic_mem_wait_icr_idle_timeout(); - else - apic_mem_wait_icr_idle(); -diff --git a/arch/x86/kernel/apic/local.h b/arch/x86/kernel/apic/local.h -index bdcf609eb283..9a54c589a4bf 100644 ---- a/arch/x86/kernel/apic/local.h -+++ b/arch/x86/kernel/apic/local.h -@@ -24,22 +24,24 @@ extern u32 x2apic_max_apicid; - - /* IPI */ - -+u16 __prepare_ICR_DM_vector(u16 vector); -+ - DECLARE_STATIC_KEY_FALSE(apic_use_ipi_shorthand); - -+/* NMI-source vectors have the delivery mode encoded within them */ -+static inline bool is_nmi_vector(u16 vector) -+{ -+ if ((vector & APIC_DM_MASK) == APIC_DM_NMI) -+ return true; -+ if ((vector & APIC_VECTOR_MASK) == NMI_VECTOR) -+ return true; -+ return false; -+} -+ - static inline unsigned int __prepare_ICR(unsigned int shortcut, int vector, - unsigned int dest) - { -- unsigned int icr = shortcut | dest; -- -- switch (vector) { -- default: -- icr |= APIC_DM_FIXED | vector; -- break; -- case NMI_VECTOR: -- icr |= APIC_DM_NMI; -- break; -- } -- return icr; -+ return shortcut | dest | __prepare_ICR_DM_vector(vector); - } - - void default_init_apic_ldr(void); -diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c -index 8ae750cde0c6..826eb399ac46 100644 ---- a/arch/x86/kernel/kvm.c -+++ b/arch/x86/kernel/kvm.c -@@ -518,14 +518,7 @@ static void __send_ipi_mask(const struct cpumask *mask, int vector) - - local_irq_save(flags); - -- switch (vector) { -- default: -- icr = APIC_DM_FIXED | vector; -- break; -- case NMI_VECTOR: -- icr = APIC_DM_NMI; -- break; -- } -+ icr = __prepare_ICR_DM_vector(vector); - - for_each_cpu(cpu, mask) { - apic_id = per_cpu(x86_cpu_to_apicid, cpu); -diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c -index debc94e2dc16..5c0d2de2986e 100644 ---- a/drivers/thermal/intel/therm_throt.c -+++ b/drivers/thermal/intel/therm_throt.c -@@ -740,7 +740,7 @@ void intel_init_thermal(struct cpuinfo_x86 *c) - * BIOS has programmed on AP based on BSP's info we saved since BIOS - * is always setting the same value for all threads/cores. - */ -- if ((h & APIC_DM_FIXED_MASK) != APIC_DM_FIXED) -+ if ((h & APIC_DM_MASK) != APIC_DM_FIXED) - apic_write(APIC_LVTTHMR, lvtthmr_init); - - --- -2.43.0 - diff --git a/SPECS/kernel/0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi b/SPECS/kernel/0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi new file mode 100644 index 000000000..82c981d75 --- /dev/null +++ b/SPECS/kernel/0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi @@ -0,0 +1,97 @@ +From 34f97e270ecbf5046f0e6d1e2b677739bae2f3c6 Mon Sep 17 00:00:00 2001 +From: Jacob Pan +Date: Tue, 9 Jul 2024 07:39:03 -0700 +Subject: [PATCH 38/44] perf/x86: Enable NMI-source reporting for perfmon + +Program the designated PMI NMI-source vector into the local vector table +for the PMU. An NMI for the PMU would directly invoke the PMI handler +without polling other NMI handlers, resulting in reduced PMI delivery +latency. + +Co-developed-by: Zeng Guang +Signed-off-by: Zeng Guang +Signed-off-by: Jacob Pan +Signed-off-by: Sohil Mehta +Tested-by: Sandipan Das # AMD overlapping bits +Reviewed-by: Kan Liang +Reviewed-by: Xin Li (Intel) +--- +v7: Pick up a review tag (Xin). + +v6: Pick up a tested-by tag (Sandipan). + +v5: No significant change. +--- + arch/x86/events/core.c | 4 ++-- + arch/x86/events/intel/core.c | 6 +++--- + arch/x86/include/asm/apic.h | 1 + + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c +index e0efe4c00444e..bcdf78db0e72d 100644 +--- a/arch/x86/events/core.c ++++ b/arch/x86/events/core.c +@@ -1704,7 +1704,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs) + * This generic handler doesn't seem to have any issues where the + * unmasking occurs so it was left at the top. + */ +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + + for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { + if (!test_bit(idx, cpuc->active_mask)) +@@ -1746,7 +1746,7 @@ void perf_events_lapic_init(void) + /* + * Always use NMI for PMU + */ +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + } + + static int +diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c +index fe65be0b9d9c4..44d3b8440fa9e 100644 +--- a/arch/x86/events/intel/core.c ++++ b/arch/x86/events/intel/core.c +@@ -3315,7 +3315,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) + * NMI handler. + */ + if (!late_ack && !mid_ack) +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + intel_bts_disable_local(); + cpuc->enabled = 0; + __intel_pmu_disable_all(true); +@@ -3352,7 +3352,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) + + done: + if (mid_ack) +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + /* Only restore PMU state when it's active. See x86_pmu_disable(). */ + cpuc->enabled = pmu_enabled; + if (pmu_enabled) +@@ -3365,7 +3365,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) + * Haswell CPUs. + */ + if (late_ack) +- apic_write(APIC_LVTPC, APIC_DM_NMI); ++ apic_write(APIC_LVTPC, PERF_NMI); + return handled; + } + +diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h +index fd62f99e5b0fc..535064faa719d 100644 +--- a/arch/x86/include/asm/apic.h ++++ b/arch/x86/include/asm/apic.h +@@ -30,6 +30,7 @@ + #define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) + #define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) + #define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) ++#define PERF_NMI (APIC_DM_NMI | NMIS_VECTOR_PMI) + + /* + * Debugging macros +-- +2.43.0 + diff --git a/SPECS/kernel/0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi b/SPECS/kernel/0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi deleted file mode 100644 index 1bb43bfad..000000000 --- a/SPECS/kernel/0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi +++ /dev/null @@ -1,131 +0,0 @@ -From 7edbfbfc60c698ae0c2daa70329ccc6508d930ec Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 28 Apr 2025 20:09:02 +0000 -Subject: [PATCH 38/44] x86/nmi: Enable NMI-source for IPIs delivered as NMIs - -With the IPI handling APIs ready to support the new NMI encoding, encode -the NMI delivery mode directly with the NMI-source vectors to trigger -NMIs. - -Move most of the existing NMI-based IPIs to use the new NMI-source -vectors, except for the microcode rendezvous NMI and the crash reboot -NMI. NMI handling for them is special-cased in exc_nmi() and does not -need NMI-source reporting. - -However, in the future, it might be useful to assign a source vector to -all NMI sources to improve isolation and debuggability. - -Originally-by: Jacob Pan -Suggested-by: Sean Christopherson -Co-developed-by: Xin Li (Intel) -Signed-off-by: Xin Li (Intel) -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: Include asm/nmi.h to avoid compile errors. (LKP) - -v5: Encode APIC_DM_NMI directly with the NMI-source vector. ---- - arch/x86/include/asm/apic.h | 8 ++++++++ - arch/x86/kernel/apic/hw_nmi.c | 2 +- - arch/x86/kernel/cpu/mce/inject.c | 2 +- - arch/x86/kernel/kgdb.c | 2 +- - arch/x86/kernel/nmi_selftest.c | 2 +- - arch/x86/kernel/smp.c | 2 +- - 6 files changed, 13 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h -index 7448b349f864..c1f06c6e86a5 100644 ---- a/arch/x86/include/asm/apic.h -+++ b/arch/x86/include/asm/apic.h -@@ -14,6 +14,7 @@ - #include - #include - #include -+#include - #include - - #define ARCH_APICTIMER_STOPS_ON_C3 1 -@@ -23,6 +24,13 @@ - #define APIC_EXTNMI_ALL 1 - #define APIC_EXTNMI_NONE 2 - -+/* Trigger NMIs with source information */ -+#define TEST_NMI (APIC_DM_NMI | NMIS_VECTOR_TEST) -+#define SMP_STOP_NMI (APIC_DM_NMI | NMIS_VECTOR_SMP_STOP) -+#define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) -+#define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) -+#define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) -+ - /* - * Debugging macros - */ -diff --git a/arch/x86/kernel/apic/hw_nmi.c b/arch/x86/kernel/apic/hw_nmi.c -index 4e04f13d2de9..586f4b25feae 100644 ---- a/arch/x86/kernel/apic/hw_nmi.c -+++ b/arch/x86/kernel/apic/hw_nmi.c -@@ -33,7 +33,7 @@ u64 hw_nmi_get_sample_period(int watchdog_thresh) - #ifdef arch_trigger_cpumask_backtrace - static void nmi_raise_cpu_backtrace(cpumask_t *mask) - { -- __apic_send_IPI_mask(mask, NMI_VECTOR); -+ __apic_send_IPI_mask(mask, BT_NMI); - } - - void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu) -diff --git a/arch/x86/kernel/cpu/mce/inject.c b/arch/x86/kernel/cpu/mce/inject.c -index 320068e01c22..81a04836ac74 100644 ---- a/arch/x86/kernel/cpu/mce/inject.c -+++ b/arch/x86/kernel/cpu/mce/inject.c -@@ -270,7 +270,7 @@ static void __maybe_unused raise_mce(struct mce *m) - mce_irq_ipi, NULL, 0); - preempt_enable(); - } else if (m->inject_flags & MCJ_NMI_BROADCAST) -- __apic_send_IPI_mask(mce_inject_cpumask, NMI_VECTOR); -+ __apic_send_IPI_mask(mce_inject_cpumask, MCE_NMI); - } - start = jiffies; - while (!cpumask_empty(mce_inject_cpumask)) { -diff --git a/arch/x86/kernel/kgdb.c b/arch/x86/kernel/kgdb.c -index 788b83e9404b..8a342df84d8e 100644 ---- a/arch/x86/kernel/kgdb.c -+++ b/arch/x86/kernel/kgdb.c -@@ -416,7 +416,7 @@ static void kgdb_disable_hw_debug(struct pt_regs *regs) - */ - void kgdb_roundup_cpus(void) - { -- apic_send_IPI_allbutself(NMI_VECTOR); -+ apic_send_IPI_allbutself(KGDB_NMI); - } - #endif - -diff --git a/arch/x86/kernel/nmi_selftest.c b/arch/x86/kernel/nmi_selftest.c -index f3918888e494..d5578370b47f 100644 ---- a/arch/x86/kernel/nmi_selftest.c -+++ b/arch/x86/kernel/nmi_selftest.c -@@ -72,7 +72,7 @@ static void __init test_nmi_ipi(struct cpumask *mask) - /* sync above data before sending NMI */ - wmb(); - -- __apic_send_IPI_mask(mask, NMI_VECTOR); -+ __apic_send_IPI_mask(mask, TEST_NMI); - - /* Don't wait longer than a second */ - timeout = USEC_PER_SEC; -diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c -index 694e31bf445c..eb49ee5b312f 100644 ---- a/arch/x86/kernel/smp.c -+++ b/arch/x86/kernel/smp.c -@@ -217,7 +217,7 @@ static void native_stop_other_cpus(int wait) - pr_emerg("Shutting down cpus with NMI\n"); - - for_each_cpu(cpu, &cpus_stop_mask) -- __apic_send_IPI(cpu, NMI_VECTOR); -+ __apic_send_IPI(cpu, SMP_STOP_NMI); - } - /* - * Don't wait longer than 10 ms if the caller didn't --- -2.43.0 - diff --git a/SPECS/kernel/0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf b/SPECS/kernel/0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf deleted file mode 100644 index 1d9f5af00..000000000 --- a/SPECS/kernel/0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf +++ /dev/null @@ -1,67 +0,0 @@ -From b75b09963f02f502960d824f08eaa755fa3dc924 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 11:53:07 -0700 -Subject: [PATCH 039/100] KVM: x86: Add kvm_icr_to_lapic_irq() helper to allow - for fastpath IPIs - -Extract the code for converting an ICR message into a kvm_lapic_irq -structure into a local helper so that a fast-only IPI path can share the -conversion logic. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/lapic.c | 30 ++++++++++++++++++------------ - 1 file changed, 18 insertions(+), 12 deletions(-) - -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index 8172c2042dd6..9f9846980625 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -1481,24 +1481,30 @@ void kvm_apic_set_eoi_accelerated(struct kvm_vcpu *vcpu, int vector) - } - EXPORT_SYMBOL_GPL(kvm_apic_set_eoi_accelerated); - --void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high) -+static void kvm_icr_to_lapic_irq(struct kvm_lapic *apic, u32 icr_low, -+ u32 icr_high, struct kvm_lapic_irq *irq) - { -- struct kvm_lapic_irq irq; -- - /* KVM has no delay and should always clear the BUSY/PENDING flag. */ - WARN_ON_ONCE(icr_low & APIC_ICR_BUSY); - -- irq.vector = icr_low & APIC_VECTOR_MASK; -- irq.delivery_mode = icr_low & APIC_MODE_MASK; -- irq.dest_mode = icr_low & APIC_DEST_MASK; -- irq.level = (icr_low & APIC_INT_ASSERT) != 0; -- irq.trig_mode = icr_low & APIC_INT_LEVELTRIG; -- irq.shorthand = icr_low & APIC_SHORT_MASK; -- irq.msi_redir_hint = false; -+ irq->vector = icr_low & APIC_VECTOR_MASK; -+ irq->delivery_mode = icr_low & APIC_MODE_MASK; -+ irq->dest_mode = icr_low & APIC_DEST_MASK; -+ irq->level = (icr_low & APIC_INT_ASSERT) != 0; -+ irq->trig_mode = icr_low & APIC_INT_LEVELTRIG; -+ irq->shorthand = icr_low & APIC_SHORT_MASK; -+ irq->msi_redir_hint = false; - if (apic_x2apic_mode(apic)) -- irq.dest_id = icr_high; -+ irq->dest_id = icr_high; - else -- irq.dest_id = GET_XAPIC_DEST_FIELD(icr_high); -+ irq->dest_id = GET_XAPIC_DEST_FIELD(icr_high); -+} -+ -+void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high) -+{ -+ struct kvm_lapic_irq irq; -+ -+ kvm_icr_to_lapic_irq(apic, icr_low, icr_high, &irq); - - trace_kvm_apic_ipi(icr_low, irq.dest_id); - --- -2.43.0 - diff --git a/SPECS/kernel/0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi b/SPECS/kernel/0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi deleted file mode 100644 index ea9ca3d56..000000000 --- a/SPECS/kernel/0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi +++ /dev/null @@ -1,97 +0,0 @@ -From 2ef0bc6ad76eec392bb9f9b10418c40339150dce Mon Sep 17 00:00:00 2001 -From: Jacob Pan -Date: Tue, 9 Jul 2024 07:39:03 -0700 -Subject: [PATCH 39/44] perf/x86: Enable NMI-source reporting for perfmon - -Program the designated PMI NMI-source vector into the local vector table -for the PMU. An NMI for the PMU would directly invoke the PMI handler -without polling other NMI handlers, resulting in reduced PMI delivery -latency. - -Co-developed-by: Zeng Guang -Signed-off-by: Zeng Guang -Signed-off-by: Jacob Pan -Signed-off-by: Sohil Mehta -Tested-by: Sandipan Das # AMD overlapping bits -Reviewed-by: Kan Liang -Reviewed-by: Xin Li (Intel) ---- -v7: Pick up a review tag (Xin). - -v6: Pick up a tested-by tag (Sandipan). - -v5: No significant change. ---- - arch/x86/events/core.c | 4 ++-- - arch/x86/events/intel/core.c | 6 +++--- - arch/x86/include/asm/apic.h | 1 + - 3 files changed, 6 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index d02bac741bfe..8081af5f66a0 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1813,7 +1813,7 @@ int x86_pmu_handle_irq(struct pt_regs *regs) - * This generic handler doesn't seem to have any issues where the - * unmasking occurs so it was left at the top. - */ -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - - for_each_set_bit(idx, x86_pmu.cntr_mask, X86_PMC_IDX_MAX) { - if (!test_bit(idx, cpuc->active_mask)) -@@ -1858,7 +1858,7 @@ void perf_events_lapic_init(void) - /* - * Always use NMI for PMU - */ -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - } - - static int -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index f0cd578a14e1..adc07ddbb5eb 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -3457,7 +3457,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) - * NMI handler. - */ - if (!late_ack && !mid_ack) -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - intel_bts_disable_local(); - cpuc->enabled = 0; - __intel_pmu_disable_all(true); -@@ -3494,7 +3494,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) - - done: - if (mid_ack) -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - /* Only restore PMU state when it's active. See x86_pmu_disable(). */ - cpuc->enabled = pmu_enabled; - if (pmu_enabled) -@@ -3507,7 +3507,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) - * Haswell CPUs. - */ - if (late_ack) -- apic_write(APIC_LVTPC, APIC_DM_NMI); -+ apic_write(APIC_LVTPC, PERF_NMI); - return handled; - } - -diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h -index c1f06c6e86a5..59bb11538b18 100644 ---- a/arch/x86/include/asm/apic.h -+++ b/arch/x86/include/asm/apic.h -@@ -30,6 +30,7 @@ - #define BT_NMI (APIC_DM_NMI | NMIS_VECTOR_BT) - #define KGDB_NMI (APIC_DM_NMI | NMIS_VECTOR_KGDB) - #define MCE_NMI (APIC_DM_NMI | NMIS_VECTOR_MCE) -+#define PERF_NMI (APIC_DM_NMI | NMIS_VECTOR_PMI) - - /* - * Debugging macros --- -2.43.0 - diff --git a/SPECS/kernel/0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi b/SPECS/kernel/0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi new file mode 100644 index 000000000..74277736c --- /dev/null +++ b/SPECS/kernel/0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi @@ -0,0 +1,41 @@ +From d129883bb9d1660ea0b1bed1196a7522fc9fe1fb Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Mon, 7 Apr 2025 11:06:15 +0000 +Subject: [PATCH 39/44] x86/nmi: Print source information with the unknown NMI + console message + +The NMI-source bitmap is a useful piece of information provided by the +NMI-source reporting feature. It is very helpful for debugging unknown +NMIs, as it can pinpoint the exact source that caused the NMI. + +Print the complete source bitmap along with the "unknown NMI" kernel log +message, since unexpected sources might have triggered the NMI. + +Signed-off-by: Sohil Mehta +--- +v7: No change. + +v6: Drop the tracepoint modification part for now. + +v5: New patch +--- + arch/x86/kernel/nmi.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index 3d2b636e93791..0b5bb20c5eb71 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -379,6 +379,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs) + pr_emerg_ratelimited("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", + reason, smp_processor_id()); + ++ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) ++ pr_emerg_ratelimited("NMI-source bitmap is 0x%lx\n", fred_event_data(regs)); ++ + if (unknown_nmi_panic || panic_on_unrecovered_nmi) + nmi_panic(regs, "NMI: Not continuing"); + +-- +2.43.0 + diff --git a/SPECS/kernel/0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf b/SPECS/kernel/0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf deleted file mode 100644 index 88772c090..000000000 --- a/SPECS/kernel/0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf +++ /dev/null @@ -1,105 +0,0 @@ -From c9633b0e2ca1a7fd7d154bcb2010c6d8633f5224 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 1 Aug 2025 14:30:06 -0700 -Subject: [PATCH 040/100] KVM: x86: Only allow "fast" IPIs in fastpath - WRMSR(X2APIC_ICR) handler - -Explicitly restrict fastpath ICR writes to IPIs that are "fast", i.e. can -be delivered without having to walk all vCPUs, and that target at most 16 -vCPUs. Artificially restricting ICR writes to physical mode guarantees -at most one vCPU will receive in IPI (because x2APIC IDs are read-only), -but that delivery might not be "fast". E.g. even if the vCPU exists, KVM -might have to iterate over 4096 vCPUs to find the right one. - -Limiting delivery to fast IPIs aligns the WRMSR fastpath with -kvm_arch_set_irq_inatomic() (which also runs with IRQs disabled), and will -allow dropping the semi-arbitrary restrictions on delivery mode and type. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/lapic.c | 27 +++++++++++++++++++++++++-- - arch/x86/kvm/lapic.h | 2 +- - arch/x86/kvm/x86.c | 2 +- - 3 files changed, 27 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index 9f9846980625..bd3232dd7a63 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -2439,7 +2439,7 @@ EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi); - - #define X2APIC_ICR_RESERVED_BITS (GENMASK_ULL(31, 20) | GENMASK_ULL(17, 16) | BIT(13)) - --int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) -+static int __kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data, bool fast) - { - if (data & X2APIC_ICR_RESERVED_BITS) - return 1; -@@ -2454,7 +2454,20 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) - */ - data &= ~APIC_ICR_BUSY; - -- kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32)); -+ if (fast) { -+ struct kvm_lapic_irq irq; -+ int ignored; -+ -+ kvm_icr_to_lapic_irq(apic, (u32)data, (u32)(data >> 32), &irq); -+ -+ if (!kvm_irq_delivery_to_apic_fast(apic->vcpu->kvm, apic, &irq, -+ &ignored, NULL)) -+ return -EWOULDBLOCK; -+ -+ trace_kvm_apic_ipi((u32)data, irq.dest_id); -+ } else { -+ kvm_apic_send_ipi(apic, (u32)data, (u32)(data >> 32)); -+ } - if (kvm_x86_ops.x2apic_icr_is_split) { - kvm_lapic_set_reg(apic, APIC_ICR, data); - kvm_lapic_set_reg(apic, APIC_ICR2, data >> 32); -@@ -2465,6 +2478,16 @@ int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) - return 0; - } - -+static int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data) -+{ -+ return __kvm_x2apic_icr_write(apic, data, false); -+} -+ -+int kvm_x2apic_icr_write_fast(struct kvm_lapic *apic, u64 data) -+{ -+ return __kvm_x2apic_icr_write(apic, data, true); -+} -+ - static u64 kvm_x2apic_icr_read(struct kvm_lapic *apic) - { - if (kvm_x86_ops.x2apic_icr_is_split) -diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h -index 72de14527698..1b2d408816aa 100644 ---- a/arch/x86/kvm/lapic.h -+++ b/arch/x86/kvm/lapic.h -@@ -137,7 +137,7 @@ int kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr); - void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu); - void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu); - --int kvm_x2apic_icr_write(struct kvm_lapic *apic, u64 data); -+int kvm_x2apic_icr_write_fast(struct kvm_lapic *apic, u64 data); - int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data); - int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a1c49bc681c4..8c8b7d7902a0 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2149,7 +2149,7 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data - ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) && - ((data & APIC_MODE_MASK) == APIC_DM_FIXED) && - ((u32)(data >> 32) != X2APIC_BROADCAST)) -- return kvm_x2apic_icr_write(vcpu->arch.apic, data); -+ return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); - - return 1; - } --- -2.43.0 - diff --git a/SPECS/kernel/0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi b/SPECS/kernel/0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi new file mode 100644 index 000000000..0053508bd --- /dev/null +++ b/SPECS/kernel/0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi @@ -0,0 +1,83 @@ +From 04ecfe7f7ed82822ca0ffab7dca906fa85172086 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Sun, 11 May 2025 11:08:16 -0700 +Subject: [PATCH 40/44] x86/nmi: Include source information in NMI handler + tracepoint + +The NMI-source bitmap is critical information provided by the NMI-source +reporting feature. It can help identify issues when multiple NMIs occur +simultaneously or if certain NMI handlers consistently misbehave. + +For enhanced debugging, add the source bitmap to the nmi_handler() +tracepoint. + +Signed-off-by: Sohil Mehta +--- +v6: Split the patch in two parts. + Print the source bitmap accurately in non NMI_LOCAL cases. + +v5: New patch +--- + arch/x86/kernel/nmi.c | 3 ++- + include/trace/events/nmi.h | 13 ++++++++----- + 2 files changed, 10 insertions(+), 6 deletions(-) + +diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c +index 0b5bb20c5eb71..76a27164e414c 100644 +--- a/arch/x86/kernel/nmi.c ++++ b/arch/x86/kernel/nmi.c +@@ -192,7 +192,8 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) + thishandled = a->handler(type, regs); + handled += thishandled; + delta = sched_clock() - delta; +- trace_nmi_handler(a->handler, (int)delta, thishandled); ++ trace_nmi_handler(a->handler, (int)delta, thishandled, ++ cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) ? fred_event_data(regs) : 0); + + nmi_check_duration(a, delta); + } +diff --git a/include/trace/events/nmi.h b/include/trace/events/nmi.h +index 18e0411398ba2..9ff8c4d49abe0 100644 +--- a/include/trace/events/nmi.h ++++ b/include/trace/events/nmi.h +@@ -10,29 +10,32 @@ + + TRACE_EVENT(nmi_handler, + +- TP_PROTO(void *handler, s64 delta_ns, int handled), ++ TP_PROTO(void *handler, s64 delta_ns, int handled, unsigned long source_bitmap), + +- TP_ARGS(handler, delta_ns, handled), ++ TP_ARGS(handler, delta_ns, handled, source_bitmap), + + TP_STRUCT__entry( + __field( void *, handler ) + __field( s64, delta_ns) + __field( int, handled ) ++ __field(unsigned long, source_bitmap) + ), + + TP_fast_assign( + __entry->handler = handler; + __entry->delta_ns = delta_ns; + __entry->handled = handled; ++ __entry->source_bitmap = source_bitmap; + ), + +- TP_printk("%ps() delta_ns: %lld handled: %d", ++ TP_printk("%ps() delta_ns: %lld handled: %d source_bitmap: 0x%lx", + __entry->handler, + __entry->delta_ns, +- __entry->handled) ++ __entry->handled, ++ __entry->source_bitmap) + ); + + #endif /* _TRACE_NMI_H */ + +-/* This part ust be outside protection */ ++/* This part must be outside protection */ + #include +-- +2.43.0 + diff --git a/SPECS/kernel/0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi b/SPECS/kernel/0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi deleted file mode 100644 index b057d22e8..000000000 --- a/SPECS/kernel/0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi +++ /dev/null @@ -1,41 +0,0 @@ -From 1bb482f979e679f6537d438bb177b73a1cf9d1f9 Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Mon, 7 Apr 2025 11:06:15 +0000 -Subject: [PATCH 40/44] x86/nmi: Print source information with the unknown NMI - console message - -The NMI-source bitmap is a useful piece of information provided by the -NMI-source reporting feature. It is very helpful for debugging unknown -NMIs, as it can pinpoint the exact source that caused the NMI. - -Print the complete source bitmap along with the "unknown NMI" kernel log -message, since unexpected sources might have triggered the NMI. - -Signed-off-by: Sohil Mehta ---- -v7: No change. - -v6: Drop the tracepoint modification part for now. - -v5: New patch ---- - arch/x86/kernel/nmi.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index 3d2b636e9379..0b5bb20c5eb7 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -379,6 +379,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs) - pr_emerg_ratelimited("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n", - reason, smp_processor_id()); - -+ if (cpu_feature_enabled(X86_FEATURE_NMI_SOURCE)) -+ pr_emerg_ratelimited("NMI-source bitmap is 0x%lx\n", fred_event_data(regs)); -+ - if (unknown_nmi_panic || panic_on_unrecovered_nmi) - nmi_panic(regs, "NMI: Not continuing"); - --- -2.43.0 - diff --git a/SPECS/kernel/0041-KVM-VMX-Implement-NMI-source-injection.nmi b/SPECS/kernel/0041-KVM-VMX-Implement-NMI-source-injection.nmi new file mode 100644 index 000000000..c91543dc9 --- /dev/null +++ b/SPECS/kernel/0041-KVM-VMX-Implement-NMI-source-injection.nmi @@ -0,0 +1,142 @@ +From fe019bcf52969f9d5e0f39d1f9c8e7842c1709e4 Mon Sep 17 00:00:00 2001 +From: Zeng Guang +Date: Thu, 23 Nov 2023 23:53:55 +0800 +Subject: [PATCH 41/44] KVM: VMX: Implement NMI source injection + +With NMI source the vector field along with NMI encoding to trigger NMI through +APIC ICR and LVT register can be repurposed to identify the originator (source) +of the NMI. NMI source vector is delivered when FRED is enabled and reported as +an accumulated 16-bit bitmask in the exception event data field pushed on the +stack for a FRED exception. + +Introduce two new elements in struct kvm_vcpu_arch used for NMI source injection. +"nmi_source_pending" collects multiple NMIs from different sources arriving at +local APIC simultanously and coalesces into a 16-bit bitmask. "nmi_source_inject" +indicates the NMI sources that will be delivered in current NMI injection. + +KVM injects NMI source into the guest by specifying the NMI source in the injected +event data field of the VMCS. + +Signed-off-by: Zeng Guang +--- + arch/x86/include/asm/kvm_host.h | 2 ++ + arch/x86/kvm/lapic.c | 1 + + arch/x86/kvm/vmx/vmx.c | 10 ++++++++++ + arch/x86/kvm/x86.c | 21 +++++++++++++++++++-- + 4 files changed, 32 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h +index 558f260a1afd7..995608ea874db 100644 +--- a/arch/x86/include/asm/kvm_host.h ++++ b/arch/x86/include/asm/kvm_host.h +@@ -970,6 +970,8 @@ struct kvm_vcpu_arch { + u64 tsc_scaling_ratio; /* current scaling ratio */ + + atomic_t nmi_queued; /* unprocessed asynchronous NMIs */ ++ atomic_t nmi_source_pending; /* unprocessed NMI Source */ ++ unsigned int nmi_source_inject; /* NMI Source to inject */ + /* Number of NMIs pending injection, not including hardware vNMIs. */ + unsigned int nmi_pending; + bool nmi_injected; /* Trying to inject an NMI this entry */ +diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c +index 0ae7f913d7826..acd0b4b5784d4 100644 +--- a/arch/x86/kvm/lapic.c ++++ b/arch/x86/kvm/lapic.c +@@ -1410,6 +1410,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, + + case APIC_DM_NMI: + result = 1; ++ atomic_or(1 << vector, &vcpu->arch.nmi_source_pending); + kvm_inject_nmi(vcpu); + kvm_vcpu_kick(vcpu); + break; +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 8f1d105f333f1..2515b8afd2d8f 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -5140,6 +5140,13 @@ void vmx_inject_nmi(struct kvm_vcpu *vcpu) + vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, + INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR); + ++ if (is_fred_enabled(vcpu)) { ++ vmcs_write64(INJECTED_EVENT_DATA, ++ guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE) ? ++ vcpu->arch.nmi_source_inject : 0); ++ vcpu->arch.nmi_source_inject = 0; ++ } ++ + vmx_clear_hlt(vcpu); + } + +@@ -7347,6 +7354,9 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, + switch (type) { + case INTR_TYPE_NMI_INTR: + vcpu->arch.nmi_injected = true; ++ ++ if (is_fred_enabled(vcpu)) ++ vcpu->arch.nmi_source_inject = vmcs_read64(event_data_field); + /* + * SDM 3: 27.7.1.2 (September 2008) + * Clear bit "block by NMI" before VM entry if a NMI +diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c +index 2f364c4e35879..295ab33cf2967 100644 +--- a/arch/x86/kvm/x86.c ++++ b/arch/x86/kvm/x86.c +@@ -10916,6 +10916,11 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, + return r; + } + ++static inline bool kvm_is_nmi_source_enabled(struct kvm_vcpu *vcpu) ++{ ++ return is_fred_enabled(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE); ++} ++ + static void process_nmi(struct kvm_vcpu *vcpu) + { + unsigned int limit; +@@ -10930,7 +10935,8 @@ static void process_nmi(struct kvm_vcpu *vcpu) + * blocks NMIs). KVM will immediately inject one of the two NMIs, and + * will request an NMI window to handle the second NMI. + */ +- if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected) ++ if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected || ++ kvm_is_nmi_source_enabled(vcpu)) + limit = 1; + else + limit = 2; +@@ -10944,13 +10950,22 @@ static void process_nmi(struct kvm_vcpu *vcpu) + + vcpu->arch.nmi_pending += atomic_xchg(&vcpu->arch.nmi_queued, 0); + vcpu->arch.nmi_pending = min(vcpu->arch.nmi_pending, limit); ++ vcpu->arch.nmi_source_inject |= atomic_xchg(&vcpu->arch.nmi_source_pending, 0); + + if (vcpu->arch.nmi_pending && + (kvm_x86_call(set_vnmi_pending)(vcpu))) + vcpu->arch.nmi_pending--; + +- if (vcpu->arch.nmi_pending) ++ if (vcpu->arch.nmi_pending) { + kvm_make_request(KVM_REQ_EVENT, vcpu); ++ /* ++ * In case nmi source supported, if new NMI arrives when vCPU is ++ * trying NMI injection, it can be coalesced together and requires ++ * one elimination of nmi_pending. ++ */ ++ if (vcpu->arch.nmi_injected && kvm_is_nmi_source_enabled(vcpu)) ++ vcpu->arch.nmi_pending--; ++ } + } + + /* Return total number of NMIs pending injection to the VM */ +@@ -12984,6 +12999,8 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) + vcpu->arch.smi_pending = 0; + vcpu->arch.smi_count = 0; + atomic_set(&vcpu->arch.nmi_queued, 0); ++ atomic_set(&vcpu->arch.nmi_source_pending, 0); ++ vcpu->arch.nmi_source_inject = 0; + vcpu->arch.nmi_pending = 0; + vcpu->arch.nmi_injected = false; + kvm_clear_interrupt_queue(vcpu); +-- +2.43.0 + diff --git a/SPECS/kernel/0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf b/SPECS/kernel/0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf deleted file mode 100644 index 752bf914b..000000000 --- a/SPECS/kernel/0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf +++ /dev/null @@ -1,43 +0,0 @@ -From 2b3e7165ae71445bc1beb9d199aa75d0654d074d Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 1 Aug 2025 14:30:37 -0700 -Subject: [PATCH 041/100] KVM: x86: Drop semi-arbitrary restrictions on IPI - type in fastpath - -Drop the restrictions on fastpath IPIs only working for fixed IRQs with a -physical destination now that the fastpath is explicitly limited to "fast" -delivery. Limiting delivery to a single physical APIC ID guarantees only -one vCPU will receive the event, but that isn't necessary "fast", e.g. if -the targeted vCPU is the last of 4096 vCPUs. And logical destination mode -or shorthand (to self) can also be fast, e.g. if only a few vCPUs are -being targeted. Lastly, there's nothing inherently slow about delivering -an NMI, INIT, SIPI, SMI, etc., i.e. there's no reason to artificially -limit fastpath delivery to fixed vector IRQs. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 8 +------- - 1 file changed, 1 insertion(+), 7 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 8c8b7d7902a0..ea117c4b20c8 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2145,13 +2145,7 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data - if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic)) - return 1; - -- if (((data & APIC_SHORT_MASK) == APIC_DEST_NOSHORT) && -- ((data & APIC_DEST_MASK) == APIC_DEST_PHYSICAL) && -- ((data & APIC_MODE_MASK) == APIC_DM_FIXED) && -- ((u32)(data >> 32) != X2APIC_BROADCAST)) -- return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); -- -- return 1; -+ return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); - } - - static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) --- -2.43.0 - diff --git a/SPECS/kernel/0041-comedi-pcl818-fix-null-ptr-deref-in-pcl818_ai_cancel.patch b/SPECS/kernel/0041-comedi-pcl818-fix-null-ptr-deref-in-pcl818_ai_cancel.patch deleted file mode 100644 index 64fe95b8c..000000000 --- a/SPECS/kernel/0041-comedi-pcl818-fix-null-ptr-deref-in-pcl818_ai_cancel.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 341e082ba6985c2e65f71ea3e27a06b2ed4ef211 Mon Sep 17 00:00:00 2001 -From: Nikita Zhandarovich -Date: Thu, 23 Oct 2025 17:14:56 +0300 -Subject: [PATCH 41/51] comedi: pcl818: fix null-ptr-deref in - pcl818_ai_cancel() - -Syzbot identified an issue [1] in pcl818_ai_cancel(), which stems from -the fact that in case of early device detach via pcl818_detach(), -subdevice dev->read_subdev may not have initialized its pointer to -&struct comedi_async as intended. Thus, any such dereferencing of -&s->async->cmd will lead to general protection fault and kernel crash. - -Mitigate this problem by removing a call to pcl818_ai_cancel() from -pcl818_detach() altogether. This way, if the subdevice setups its -support for async commands, everything async-related will be -handled via subdevice's own ->cancel() function in -comedi_device_detach_locked() even before pcl818_detach(). If no -support for asynchronous commands is provided, there is no need -to cancel anything either. - -[1] Syzbot crash: -Oops: general protection fault, probably for non-canonical -address 0xdffffc0000000005: 0000 [#1] SMP KASAN PTI -KASAN: null-ptr-deref in range [0x0000000000000028-0x000000000000002f] -CPU: 1 UID: 0 PID: 6050 Comm: syz.0.18 Not tainted syzkaller #0 PREEMPT(full) -Hardware name: Google Compute Engine/Google Compute Engine, BIOS Google 08/18/2025 -RIP: 0010:pcl818_ai_cancel+0x69/0x3f0 drivers/comedi/drivers/pcl818.c:762 -... -Call Trace: - - pcl818_detach+0x66/0xd0 drivers/comedi/drivers/pcl818.c:1115 - comedi_device_detach_locked+0x178/0x750 drivers/comedi/drivers.c:207 - do_devconfig_ioctl drivers/comedi/comedi_fops.c:848 [inline] - comedi_unlocked_ioctl+0xcde/0x1020 drivers/comedi/comedi_fops.c:2178 - vfs_ioctl fs/ioctl.c:51 [inline] - __do_sys_ioctl fs/ioctl.c:597 [inline] -... - -Reported-by: syzbot+fce5d9d5bd067d6fbe9b@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=fce5d9d5bd067d6fbe9b -Fixes: 00aba6e7b565 ("staging: comedi: pcl818: remove 'neverending_ai' from private data") -Cc: stable@kernel.org -Signed-off-by: Nikita Zhandarovich -Reviewed-by: Ian Abbott -Link: https://patch.msgid.link/20251023141457.398685-1-n.zhandarovich@fintech.ru -Signed-off-by: Greg Kroah-Hartman ---- - drivers/comedi/drivers/pcl818.c | 5 ++--- - 1 file changed, 2 insertions(+), 3 deletions(-) - -diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c -index 4127adcfb229..06fe06396f23 100644 ---- a/drivers/comedi/drivers/pcl818.c -+++ b/drivers/comedi/drivers/pcl818.c -@@ -1111,10 +1111,9 @@ static void pcl818_detach(struct comedi_device *dev) - { - struct pcl818_private *devpriv = dev->private; - -- if (devpriv) { -- pcl818_ai_cancel(dev, dev->read_subdev); -+ if (devpriv) - pcl818_reset(dev); -- } -+ - pcl818_free_dma(dev); - comedi_legacy_detach(dev); - } --- -2.43.0 - diff --git a/SPECS/kernel/0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi b/SPECS/kernel/0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi deleted file mode 100644 index 0d5c2cfbc..000000000 --- a/SPECS/kernel/0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi +++ /dev/null @@ -1,83 +0,0 @@ -From fbfb91590d8fafc778d8cd6ae725920ceba05bcf Mon Sep 17 00:00:00 2001 -From: Sohil Mehta -Date: Sun, 11 May 2025 11:08:16 -0700 -Subject: [PATCH 41/44] x86/nmi: Include source information in NMI handler - tracepoint - -The NMI-source bitmap is critical information provided by the NMI-source -reporting feature. It can help identify issues when multiple NMIs occur -simultaneously or if certain NMI handlers consistently misbehave. - -For enhanced debugging, add the source bitmap to the nmi_handler() -tracepoint. - -Signed-off-by: Sohil Mehta ---- -v6: Split the patch in two parts. - Print the source bitmap accurately in non NMI_LOCAL cases. - -v5: New patch ---- - arch/x86/kernel/nmi.c | 3 ++- - include/trace/events/nmi.h | 13 ++++++++----- - 2 files changed, 10 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c -index 0b5bb20c5eb7..76a27164e414 100644 ---- a/arch/x86/kernel/nmi.c -+++ b/arch/x86/kernel/nmi.c -@@ -192,7 +192,8 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) - thishandled = a->handler(type, regs); - handled += thishandled; - delta = sched_clock() - delta; -- trace_nmi_handler(a->handler, (int)delta, thishandled); -+ trace_nmi_handler(a->handler, (int)delta, thishandled, -+ cpu_feature_enabled(X86_FEATURE_NMI_SOURCE) ? fred_event_data(regs) : 0); - - nmi_check_duration(a, delta); - } -diff --git a/include/trace/events/nmi.h b/include/trace/events/nmi.h -index 18e0411398ba..6a22e89a39ca 100644 ---- a/include/trace/events/nmi.h -+++ b/include/trace/events/nmi.h -@@ -10,29 +10,32 @@ - - TRACE_EVENT(nmi_handler, - -- TP_PROTO(void *handler, s64 delta_ns, int handled), -+ TP_PROTO(void *handler, s64 delta_ns, int handled, unsigned long source_bitmap), - -- TP_ARGS(handler, delta_ns, handled), -+ TP_ARGS(handler, delta_ns, handled, source_bitmap), - - TP_STRUCT__entry( - __field( void *, handler ) - __field( s64, delta_ns) - __field( int, handled ) -+ __field(unsigned long, source_bitmap) - ), - - TP_fast_assign( - __entry->handler = handler; - __entry->delta_ns = delta_ns; - __entry->handled = handled; -+ __entry->source_bitmap = source_bitmap; - ), - -- TP_printk("%ps() delta_ns: %lld handled: %d", -+ TP_printk("%ps() delta_ns: %lld handled: %d source_bitmap: 0x%lx", - __entry->handler, - __entry->delta_ns, -- __entry->handled) -+ __entry->handled, -+ __entry->source_bitmap) - ); - - #endif /* _TRACE_NMI_H */ - --/* This part ust be outside protection */ -+/* This part must be outside protection */ - #include --- -2.43.0 - diff --git a/SPECS/kernel/0042-KVM-VMX-Implement-NMI-source-injection.nmi b/SPECS/kernel/0042-KVM-VMX-Implement-NMI-source-injection.nmi deleted file mode 100644 index 2083081b4..000000000 --- a/SPECS/kernel/0042-KVM-VMX-Implement-NMI-source-injection.nmi +++ /dev/null @@ -1,142 +0,0 @@ -From 64e7e822c4c3535413b83c11732b59a0b6a2fff9 Mon Sep 17 00:00:00 2001 -From: Zeng Guang -Date: Thu, 23 Nov 2023 23:53:55 +0800 -Subject: [PATCH 42/44] KVM: VMX: Implement NMI source injection - -With NMI source the vector field along with NMI encoding to trigger NMI through -APIC ICR and LVT register can be repurposed to identify the originator (source) -of the NMI. NMI source vector is delivered when FRED is enabled and reported as -an accumulated 16-bit bitmask in the exception event data field pushed on the -stack for a FRED exception. - -Introduce two new elements in struct kvm_vcpu_arch used for NMI source injection. -"nmi_source_pending" collects multiple NMIs from different sources arriving at -local APIC simultanously and coalesces into a 16-bit bitmask. "nmi_source_inject" -indicates the NMI sources that will be delivered in current NMI injection. - -KVM injects NMI source into the guest by specifying the NMI source in the injected -event data field of the VMCS. - -Signed-off-by: Zeng Guang ---- - arch/x86/include/asm/kvm_host.h | 2 ++ - arch/x86/kvm/lapic.c | 1 + - arch/x86/kvm/vmx/vmx.c | 10 ++++++++++ - arch/x86/kvm/x86.c | 21 +++++++++++++++++++-- - 4 files changed, 32 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 2634c16b09c4..d4657bbfe460 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -969,6 +969,8 @@ struct kvm_vcpu_arch { - u64 tsc_scaling_ratio; /* current scaling ratio */ - - atomic_t nmi_queued; /* unprocessed asynchronous NMIs */ -+ atomic_t nmi_source_pending; /* unprocessed NMI Source */ -+ unsigned int nmi_source_inject; /* NMI Source to inject */ - /* Number of NMIs pending injection, not including hardware vNMIs. */ - unsigned int nmi_pending; - bool nmi_injected; /* Trying to inject an NMI this entry */ -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index e19545b8cc98..08ad0bd5e387 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -1315,6 +1315,7 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, - - case APIC_DM_NMI: - result = 1; -+ atomic_or(1 << vector, &vcpu->arch.nmi_source_pending); - kvm_inject_nmi(vcpu); - kvm_vcpu_kick(vcpu); - break; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 36afe1dba9f0..8bbba7aa13c3 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -5203,6 +5203,13 @@ void vmx_inject_nmi(struct kvm_vcpu *vcpu) - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, - INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR); - -+ if (is_fred_enabled(vcpu)) { -+ vmcs_write64(INJECTED_EVENT_DATA, -+ guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE) ? -+ vcpu->arch.nmi_source_inject : 0); -+ vcpu->arch.nmi_source_inject = 0; -+ } -+ - vmx_clear_hlt(vcpu); - } - -@@ -7368,6 +7375,9 @@ static void __vmx_complete_interrupts(struct kvm_vcpu *vcpu, - switch (type) { - case INTR_TYPE_NMI_INTR: - vcpu->arch.nmi_injected = true; -+ -+ if (is_fred_enabled(vcpu)) -+ vcpu->arch.nmi_source_inject = vmcs_read64(event_data_field); - /* - * SDM 3: 27.7.1.2 (September 2008) - * Clear bit "block by NMI" before VM entry if a NMI -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index eb0a1e537a88..c36f22135434 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -10719,6 +10719,11 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu, - return r; - } - -+static inline bool kvm_is_nmi_source_enabled(struct kvm_vcpu *vcpu) -+{ -+ return is_fred_enabled(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_NMI_SOURCE); -+} -+ - static void process_nmi(struct kvm_vcpu *vcpu) - { - unsigned int limit; -@@ -10733,7 +10738,8 @@ static void process_nmi(struct kvm_vcpu *vcpu) - * blocks NMIs). KVM will immediately inject one of the two NMIs, and - * will request an NMI window to handle the second NMI. - */ -- if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected) -+ if (kvm_x86_call(get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected || -+ kvm_is_nmi_source_enabled(vcpu)) - limit = 1; - else - limit = 2; -@@ -10747,13 +10753,22 @@ static void process_nmi(struct kvm_vcpu *vcpu) - - vcpu->arch.nmi_pending += atomic_xchg(&vcpu->arch.nmi_queued, 0); - vcpu->arch.nmi_pending = min(vcpu->arch.nmi_pending, limit); -+ vcpu->arch.nmi_source_inject |= atomic_xchg(&vcpu->arch.nmi_source_pending, 0); - - if (vcpu->arch.nmi_pending && - (kvm_x86_call(set_vnmi_pending)(vcpu))) - vcpu->arch.nmi_pending--; - -- if (vcpu->arch.nmi_pending) -+ if (vcpu->arch.nmi_pending) { - kvm_make_request(KVM_REQ_EVENT, vcpu); -+ /* -+ * In case nmi source supported, if new NMI arrives when vCPU is -+ * trying NMI injection, it can be coalesced together and requires -+ * one elimination of nmi_pending. -+ */ -+ if (vcpu->arch.nmi_injected && kvm_is_nmi_source_enabled(vcpu)) -+ vcpu->arch.nmi_pending--; -+ } - } - - /* Return total number of NMIs pending injection to the VM */ -@@ -12780,6 +12795,8 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) - vcpu->arch.smi_pending = 0; - vcpu->arch.smi_count = 0; - atomic_set(&vcpu->arch.nmi_queued, 0); -+ atomic_set(&vcpu->arch.nmi_source_pending, 0); -+ vcpu->arch.nmi_source_inject = 0; - vcpu->arch.nmi_pending = 0; - vcpu->arch.nmi_injected = false; - kvm_clear_interrupt_queue(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi b/SPECS/kernel/0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi new file mode 100644 index 000000000..51b9b3d09 --- /dev/null +++ b/SPECS/kernel/0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi @@ -0,0 +1,36 @@ +From d1fce9be69dbc31277ccd237957469626601d47a Mon Sep 17 00:00:00 2001 +From: Zeng Guang +Date: Wed, 31 Jan 2024 21:52:30 +0800 +Subject: [PATCH 42/44] KVM: x86: Advise NMI Source to user space + +NMI Source can program unique values into the NMI vector at the originating +site which is generally ignored on handling of NMI interrupt, and repurpose +it to identify the different event source. It allows the software NMI exception +handler to call corresponding function reliably and efficiently without checking +all sources. + +The CPUID bit definition to support NMI Source: +CPUID.(EAX=07H.ECX=1):EAX.NMI_SOURCE[bit 20] + +Advertise NMI Source to user space for virtualization support. + +Signed-off-by: Zeng Guang +--- + arch/x86/kvm/cpuid.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c +index f4ff5ccbcf1e4..e4726b76915c9 100644 +--- a/arch/x86/kvm/cpuid.c ++++ b/arch/x86/kvm/cpuid.c +@@ -1019,6 +1019,7 @@ void kvm_set_cpu_caps(void) + F(AMX_FP16), + F(AVX_IFMA), + F(LAM), ++ F(NMI_SOURCE), + ); + + kvm_cpu_cap_init(CPUID_7_1_ECX, +-- +2.43.0 + diff --git a/SPECS/kernel/0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf b/SPECS/kernel/0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf deleted file mode 100644 index 1389e3b10..000000000 --- a/SPECS/kernel/0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf +++ /dev/null @@ -1,77 +0,0 @@ -From e6c9259fc57a374c6a1f0cbf22417b3a158273e2 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Mon, 4 Aug 2025 08:51:58 -0700 -Subject: [PATCH 042/100] KVM: x86: Unconditionally handle - MSR_IA32_TSC_DEADLINE in fastpath exits - -Drop the fastpath VM-Exit requirement that KVM can use the hypervisor -timer to emulate the APIC timer in TSC deadline mode. I.e. unconditionally -handle MSR_IA32_TSC_DEADLINE WRMSRs in the fastpath. Restricting the -fastpath to *maybe* using the VMX preemption timer is ineffective and -unnecessary. - -If the requested deadline can't be programmed into the VMX preemption -timer, KVM will fall back to hrtimers, i.e. the restriction is ineffective -as far as preventing any kind of worst case scenario. - -But guarding against a worst case scenario is completely unnecessary as -the "slow" path, start_sw_tscdeadline() => hrtimer_start(), explicitly -disables IRQs. In fact, the worst case scenario is when KVM thinks it -can use the VMX preemption timer, as KVM will eat the overhead of calling -into vmx_set_hv_timer() and falling back to hrtimers. - -Opportunistically limit kvm_can_use_hv_timer() to lapic.c as the fastpath -code was the only external user. - -Stating the obvious, this allows handling MSR_IA32_TSC_DEADLINE writes in -the fastpath on AMD CPUs. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/lapic.c | 2 +- - arch/x86/kvm/lapic.h | 1 - - arch/x86/kvm/x86.c | 3 --- - 3 files changed, 1 insertion(+), 5 deletions(-) - -diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c -index bd3232dd7a63..e19545b8cc98 100644 ---- a/arch/x86/kvm/lapic.c -+++ b/arch/x86/kvm/lapic.c -@@ -130,7 +130,7 @@ static bool kvm_can_post_timer_interrupt(struct kvm_vcpu *vcpu) - (kvm_mwait_in_guest(vcpu->kvm) || kvm_hlt_in_guest(vcpu->kvm)); - } - --bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu) -+static bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu) - { - return kvm_x86_ops.set_hv_timer - && !(kvm_mwait_in_guest(vcpu->kvm) || -diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h -index 1b2d408816aa..8b00e29741de 100644 ---- a/arch/x86/kvm/lapic.h -+++ b/arch/x86/kvm/lapic.h -@@ -249,7 +249,6 @@ void kvm_lapic_switch_to_hv_timer(struct kvm_vcpu *vcpu); - void kvm_lapic_expired_hv_timer(struct kvm_vcpu *vcpu); - bool kvm_lapic_hv_timer_in_use(struct kvm_vcpu *vcpu); - void kvm_lapic_restart_hv_timer(struct kvm_vcpu *vcpu); --bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu); - - static inline enum lapic_mode kvm_apic_mode(u64 apic_base) - { -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index ea117c4b20c8..63ca9185d133 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2150,9 +2150,6 @@ static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data - - static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) - { -- if (!kvm_can_use_hv_timer(vcpu)) -- return 1; -- - kvm_set_lapic_tscdeadline_msr(vcpu, data); - return 0; - } --- -2.43.0 - diff --git a/SPECS/kernel/0042-wifi-ath12k-Fix-MSDU-buffer-types-handling-in-RX-err.patch b/SPECS/kernel/0042-wifi-ath12k-Fix-MSDU-buffer-types-handling-in-RX-err.patch deleted file mode 100644 index 904b8ceb5..000000000 --- a/SPECS/kernel/0042-wifi-ath12k-Fix-MSDU-buffer-types-handling-in-RX-err.patch +++ /dev/null @@ -1,178 +0,0 @@ -From 4e6d41eb05d214a7bd69d6047ee4bbcccc6ed445 Mon Sep 17 00:00:00 2001 -From: Sarika Sharma -Date: Tue, 30 Sep 2025 14:45:50 +0530 -Subject: [PATCH 42/51] wifi: ath12k: Fix MSDU buffer types handling in RX - error path - -Currently, packets received on the REO exception ring from -unassociated peers are of MSDU buffer type, while the driver expects -link descriptor type packets. These packets are not parsed further due -to a return check on packet type in ath12k_hal_desc_reo_parse_err(), -but the associated skb is not freed. This may lead to kernel -crashes and buffer leaks. - -Hence to fix, update the RX error handler to explicitly drop -MSDU buffer type packets received on the REO exception ring. -This prevents further processing of invalid packets and ensures -stability in the RX error handling path. - -Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 - -Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") -Signed-off-by: Sarika Sharma -Reviewed-by: Baochen Qiang -Reviewed-by: Vasanthakumar Thiagarajan -Link: https://patch.msgid.link/20250930091551.3305312-2-sarika.sharma@oss.qualcomm.com -Signed-off-by: Jeff Johnson ---- - drivers/net/wireless/ath/ath12k/dp_rx.c | 70 ++++++++++++++++++++++-- - drivers/net/wireless/ath/ath12k/hal_rx.c | 10 +--- - 2 files changed, 66 insertions(+), 14 deletions(-) - -diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c -index 9048818984f1..0be911b4f316 100644 ---- a/drivers/net/wireless/ath/ath12k/dp_rx.c -+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c -@@ -1,7 +1,7 @@ - // SPDX-License-Identifier: BSD-3-Clause-Clear - /* - * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. -- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - - #include -@@ -3690,6 +3690,48 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, - return 0; - } - -+static int ath12k_dp_h_msdu_buffer_type(struct ath12k_base *ab, -+ struct list_head *list, -+ struct hal_reo_dest_ring *desc) -+{ -+ struct ath12k_rx_desc_info *desc_info; -+ struct ath12k_skb_rxcb *rxcb; -+ struct sk_buff *msdu; -+ u64 desc_va; -+ -+ desc_va = (u64)le32_to_cpu(desc->buf_va_hi) << 32 | -+ le32_to_cpu(desc->buf_va_lo); -+ desc_info = (struct ath12k_rx_desc_info *)(uintptr_t)desc_va; -+ if (!desc_info) { -+ u32 cookie; -+ -+ cookie = le32_get_bits(desc->buf_addr_info.info1, -+ BUFFER_ADDR_INFO1_SW_COOKIE); -+ desc_info = ath12k_dp_get_rx_desc(ab, cookie); -+ if (!desc_info) { -+ ath12k_warn(ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n", -+ cookie); -+ return -EINVAL; -+ } -+ } -+ -+ if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) { -+ ath12k_warn(ab, "rx exception, magic check failed with value: %u\n", -+ desc_info->magic); -+ return -EINVAL; -+ } -+ -+ msdu = desc_info->skb; -+ desc_info->skb = NULL; -+ list_add_tail(&desc_info->list, list); -+ rxcb = ATH12K_SKB_RXCB(msdu); -+ dma_unmap_single(ab->dev, rxcb->paddr, msdu->len + skb_tailroom(msdu), -+ DMA_FROM_DEVICE); -+ dev_kfree_skb_any(msdu); -+ -+ return 0; -+} -+ - int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - int budget) - { -@@ -3734,6 +3776,26 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - drop = false; - ab->device_stats.err_ring_pkts++; - -+ hw_link_id = le32_get_bits(reo_desc->info0, -+ HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); -+ device_id = hw_links[hw_link_id].device_id; -+ partner_ab = ath12k_ag_to_ab(ag, device_id); -+ -+ /* Below case is added to handle data packet from un-associated clients. -+ * As it is expected that AST lookup will fail for -+ * un-associated station's data packets. -+ */ -+ if (le32_get_bits(reo_desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE) == -+ HAL_REO_DEST_RING_BUFFER_TYPE_MSDU) { -+ if (!ath12k_dp_h_msdu_buffer_type(partner_ab, -+ &rx_desc_used_list[device_id], -+ reo_desc)) { -+ num_buffs_reaped[device_id]++; -+ tot_n_bufs_reaped++; -+ } -+ goto next_desc; -+ } -+ - ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr, - &desc_bank); - if (ret) { -@@ -3742,11 +3804,6 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - continue; - } - -- hw_link_id = le32_get_bits(reo_desc->info0, -- HAL_REO_DEST_RING_INFO0_SRC_LINK_ID); -- device_id = hw_links[hw_link_id].device_id; -- partner_ab = ath12k_ag_to_ab(ag, device_id); -- - pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params, - hw_links[hw_link_id].pdev_idx); - ar = partner_ab->pdevs[pdev_id].ar; -@@ -3795,6 +3852,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, - } - } - -+next_desc: - if (tot_n_bufs_reaped >= quota) { - tot_n_bufs_reaped = quota; - goto exit; -diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c -index 48aa48c48606..805c31e4243d 100644 ---- a/drivers/net/wireless/ath/ath12k/hal_rx.c -+++ b/drivers/net/wireless/ath/ath12k/hal_rx.c -@@ -1,7 +1,7 @@ - // SPDX-License-Identifier: BSD-3-Clause-Clear - /* - * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. -- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. -+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - - #include "debug.h" -@@ -320,7 +320,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab, - { - enum hal_reo_dest_ring_push_reason push_reason; - enum hal_reo_dest_ring_error_code err_code; -- u32 cookie, val; -+ u32 cookie; - - push_reason = le32_get_bits(desc->info0, - HAL_REO_DEST_RING_INFO0_PUSH_REASON); -@@ -335,12 +335,6 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab, - return -EINVAL; - } - -- val = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE); -- if (val != HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) { -- ath12k_warn(ab, "expected buffer type link_desc"); -- return -EINVAL; -- } -- - ath12k_hal_rx_reo_ent_paddr_get(ab, &desc->buf_addr_info, paddr, &cookie); - *desc_bank = u32_get_bits(cookie, DP_LINK_DESC_BANK_MASK); - --- -2.43.0 - diff --git a/SPECS/kernel/0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf b/SPECS/kernel/0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf deleted file mode 100644 index 8c6c8bc28..000000000 --- a/SPECS/kernel/0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf +++ /dev/null @@ -1,74 +0,0 @@ -From 78d9b7401b4a248cdc778a216257ed5df6f33467 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 08:18:23 -0700 -Subject: [PATCH 043/100] KVM: x86: Acquire SRCU in WRMSR fastpath iff - instruction needs to be skipped - -Acquire SRCU in the WRMSR fastpath if and only if an instruction needs to -be skipped, i.e. only if the fastpath succeeds. The reasoning in commit -3f2739bd1e0b ("KVM: x86: Acquire SRCU read lock when handling fastpath MSR -writes") about "avoid having to play whack-a-mole" seems sound, but in -hindsight unconditionally acquiring SRCU does more harm than good. - -While acquiring/releasing SRCU isn't slow per se, the things that are -_protected_ by kvm->srcu are generally safe to access only in the "slow" -VM-Exit path. E.g. accessing memslots in generic helpers is never safe, -because accessing guest memory with IRQs disabled is unless unsafe (except -when kvm_vcpu_read_guest_atomic() is used, but that API should never be -used in emulation helpers). - -In other words, playing whack-a-mole is actually desirable in this case, -because every access to an asset protected by kvm->srcu warrants further -scrutiny. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 21 ++++++++------------- - 1 file changed, 8 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 63ca9185d133..69c668f4d2b6 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2158,10 +2158,8 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { - u32 msr = kvm_rcx_read(vcpu); - u64 data; -- fastpath_t ret; - bool handled; -- -- kvm_vcpu_srcu_read_lock(vcpu); -+ int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -@@ -2177,19 +2175,16 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - break; - } - -- if (handled) { -- if (!kvm_skip_emulated_instruction(vcpu)) -- ret = EXIT_FASTPATH_EXIT_USERSPACE; -- else -- ret = EXIT_FASTPATH_REENTER_GUEST; -- trace_kvm_msr_write(msr, data); -- } else { -- ret = EXIT_FASTPATH_NONE; -- } -+ if (!handled) -+ return EXIT_FASTPATH_NONE; - -+ kvm_vcpu_srcu_read_lock(vcpu); -+ r = kvm_skip_emulated_instruction(vcpu); - kvm_vcpu_srcu_read_unlock(vcpu); - -- return ret; -+ trace_kvm_msr_write(msr, data); -+ -+ return r ? EXIT_FASTPATH_REENTER_GUEST : EXIT_FASTPATH_EXIT_USERSPACE; - } - EXPORT_SYMBOL_GPL(handle_fastpath_set_msr_irqoff); - --- -2.43.0 - diff --git a/SPECS/kernel/0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi b/SPECS/kernel/0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi deleted file mode 100644 index 85c388341..000000000 --- a/SPECS/kernel/0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi +++ /dev/null @@ -1,36 +0,0 @@ -From f84be478f482ad9a7816adafbe48750b9b0fe9f0 Mon Sep 17 00:00:00 2001 -From: Zeng Guang -Date: Wed, 31 Jan 2024 21:52:30 +0800 -Subject: [PATCH 43/44] KVM: x86: Advise NMI Source to user space - -NMI Source can program unique values into the NMI vector at the originating -site which is generally ignored on handling of NMI interrupt, and repurpose -it to identify the different event source. It allows the software NMI exception -handler to call corresponding function reliably and efficiently without checking -all sources. - -The CPUID bit definition to support NMI Source: -CPUID.(EAX=07H.ECX=1):EAX.NMI_SOURCE[bit 20] - -Advertise NMI Source to user space for virtualization support. - -Signed-off-by: Zeng Guang ---- - arch/x86/kvm/cpuid.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index 15217805c007..0a4f27ce2b06 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -1001,6 +1001,7 @@ void kvm_set_cpu_caps(void) - F(AMX_FP16), - F(AVX_IFMA), - F(LAM), -+ F(NMI_SOURCE), - ); - - kvm_cpu_cap_init(CPUID_7_1_EDX, --- -2.43.0 - diff --git a/SPECS/kernel/0043-ntfs3-fix-uninit-memory-after-failed-mi_read-in-mi_f.patch b/SPECS/kernel/0043-ntfs3-fix-uninit-memory-after-failed-mi_read-in-mi_f.patch deleted file mode 100644 index 6dcb229e6..000000000 --- a/SPECS/kernel/0043-ntfs3-fix-uninit-memory-after-failed-mi_read-in-mi_f.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 73d642bb9276696a16adb9b5cd5ce7e810e41ae6 Mon Sep 17 00:00:00 2001 -From: Raphael Pinsonneault-Thibeault -Date: Sun, 12 Oct 2025 16:16:34 -0400 -Subject: [PATCH 43/51] ntfs3: fix uninit memory after failed mi_read in - mi_format_new -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fix a KMSAN un-init bug found by syzkaller. - -ntfs_get_bh() expects a buffer from sb_getblk(), that buffer may not be -uptodate. We do not bring the buffer uptodate before setting it as -uptodate. If the buffer were to not be uptodate, it could mean adding a -buffer with un-init data to the mi record. Attempting to load that record -will trigger KMSAN. - -Avoid this by setting the buffer as uptodate, if it’s not already, by -overwriting it. - -Reported-by: syzbot+7a2ba6b7b66340cff225@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=7a2ba6b7b66340cff225 -Tested-by: syzbot+7a2ba6b7b66340cff225@syzkaller.appspotmail.com -Fixes: 4342306f0f0d5 ("fs/ntfs3: Add file operations and implementation") -Signed-off-by: Raphael Pinsonneault-Thibeault -Signed-off-by: Konstantin Komarov ---- - fs/ntfs3/fsntfs.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/fs/ntfs3/fsntfs.c b/fs/ntfs3/fsntfs.c -index c7a2f191254d..488bdefc1ad0 100644 ---- a/fs/ntfs3/fsntfs.c -+++ b/fs/ntfs3/fsntfs.c -@@ -1349,7 +1349,13 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo, - } - if (buffer_locked(bh)) - __wait_on_buffer(bh); -- set_buffer_uptodate(bh); -+ -+ lock_buffer(bh); -+ if (!buffer_uptodate(bh)) { -+ memset(bh->b_data, 0, blocksize); -+ set_buffer_uptodate(bh); -+ } -+ unlock_buffer(bh); - } else { - bh = ntfs_bread(sb, block); - if (!bh) { --- -2.43.0 - diff --git a/SPECS/kernel/0043-x86-fred-Enable-FRED-by-default.nmi b/SPECS/kernel/0043-x86-fred-Enable-FRED-by-default.nmi new file mode 100644 index 000000000..07e6478dc --- /dev/null +++ b/SPECS/kernel/0043-x86-fred-Enable-FRED-by-default.nmi @@ -0,0 +1,31 @@ +From fe4c0a9c022d16919105f76a56ee67958ef97555 Mon Sep 17 00:00:00 2001 +From: "Xin Li (Intel)" +Date: Fri, 14 Feb 2025 10:31:18 -0800 +Subject: [PATCH 43/44] x86/fred: Enable FRED by default + +Signed-off-by: Xin Li (Intel) +Tested-by: Xuelian Guo +--- + +Change in v5: +* Add TB from Xuelian Guo. +--- + arch/x86/kernel/cpu/common.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c +index a2a251549fa15..40ad8a394cdc8 100644 +--- a/arch/x86/kernel/cpu/common.c ++++ b/arch/x86/kernel/cpu/common.c +@@ -1721,7 +1721,7 @@ static void __init cpu_parse_early_param(void) + + /* Minimize the gap between FRED is available and available but disabled. */ + arglen = cmdline_find_option(boot_command_line, "fred", arg, sizeof(arg)); +- if (arglen != 2 || strncmp(arg, "on", 2)) ++ if (arglen == 3 && !strncmp(arg, "off", 3)) + setup_clear_cpu_cap(X86_FEATURE_FRED); + + arglen = cmdline_find_option(boot_command_line, "clearcpuid", arg, sizeof(arg)); +-- +2.43.0 + diff --git a/SPECS/kernel/0044-EDAC-ieh-Fix-a-compile-error.nmi b/SPECS/kernel/0044-EDAC-ieh-Fix-a-compile-error.nmi deleted file mode 100644 index 5f53780fc..000000000 --- a/SPECS/kernel/0044-EDAC-ieh-Fix-a-compile-error.nmi +++ /dev/null @@ -1,30 +0,0 @@ -From 0ff6292174c874dc857eaba8b8b40e0318329a3f Mon Sep 17 00:00:00 2001 -From: "Yu, Tao1" -Date: Thu, 10 Jul 2025 16:19:06 +0000 -Subject: [PATCH 44/44] EDAC/ieh: Fix a compile error - -NMI add a source vector argument to register_nmi_handler(), -EDAC sould use the new interface. - -Fixes: 17ade7d95513 ("EDAC/ieh: Add I/O device EDAC driver for Intel CPUs with IEH") -Signed-off-by: Tao Yu ---- - drivers/edac/ieh_edac.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/edac/ieh_edac.c b/drivers/edac/ieh_edac.c -index 46eca4bddea8..d108d8730438 100644 ---- a/drivers/edac/ieh_edac.c -+++ b/drivers/edac/ieh_edac.c -@@ -708,7 +708,7 @@ static int register_err_handler(void) - if (has_notification_by(IEH_NMI)) { - init_irq_work(&ieh_irq_work, ieh_irq_work_cb); - rc = register_nmi_handler(NMI_SERR, ieh_nmi_handler, -- 0, IEH_NMI_NAME); -+ 0, IEH_NMI_NAME, 0); - if (rc) { - ieh_printk(KERN_ERR, "Can't register NMI handler\n"); - return rc; --- -2.43.0 - diff --git a/SPECS/kernel/0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf b/SPECS/kernel/0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf deleted file mode 100644 index 3a0b8862a..000000000 --- a/SPECS/kernel/0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf +++ /dev/null @@ -1,47 +0,0 @@ -From 147e029e5b2a48a857d2e3b3a2e113730fdd6329 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 08:19:22 -0700 -Subject: [PATCH 044/100] KVM: x86: Unconditionally grab data from EDX:EAX in - WRMSR fastpath - -Always grab EDX:EAX in the WRMSR fastpath to deduplicate and simplify the -case statements, and to prepare for handling immediate variants of WRMSRNS -in the fastpath (the data register is explicitly provided in that case). -There's no harm in reading the registers, as their values are always -available, i.e. don't require VMREADs (or similarly slow operations). - -No real functional change intended. - -Cc: Xin Li -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 69c668f4d2b6..e6c221f9b92e 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2156,18 +2156,16 @@ static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) - - fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { -+ u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); -- u64 data; - bool handled; - int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -- data = kvm_read_edx_eax(vcpu); - handled = !handle_fastpath_set_x2apic_icr_irqoff(vcpu, data); - break; - case MSR_IA32_TSC_DEADLINE: -- data = kvm_read_edx_eax(vcpu); - handled = !handle_fastpath_set_tscdeadline(vcpu, data); - break; - default: --- -2.43.0 - diff --git a/SPECS/kernel/0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi b/SPECS/kernel/0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi new file mode 100644 index 000000000..808438267 --- /dev/null +++ b/SPECS/kernel/0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi @@ -0,0 +1,47 @@ +From 86ced05172c8e653565845642f8619f2dcf6ab42 Mon Sep 17 00:00:00 2001 +From: Sohil Mehta +Date: Fri, 5 Dec 2025 10:40:18 -0800 +Subject: [PATCH 44/44] fixup! KVM: VMX: Handle #MCs on VM-Enter/TD-Enter + outside of the fastpath + +The base code of FRED/NMI_source contains a problemtic commit which has +been fixed in the new code base. Backport the new implementation from +https://lore.kernel.org/all/20251118222328.2265758-3-seanjc@google.com/ + +Received this patch from Chenyi Qiang , applied +manually. + +Signed-off-by: Sohil Mehta +--- + arch/x86/kvm/vmx/vmx.c | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c +index 2515b8afd2d8f..4491c54b71556 100644 +--- a/arch/x86/kvm/vmx/vmx.c ++++ b/arch/x86/kvm/vmx/vmx.c +@@ -7255,10 +7255,19 @@ void vmx_handle_exit_irqoff(struct kvm_vcpu *vcpu) + if (to_vt(vcpu)->emulation_required) + return; + +- if (vmx_get_exit_reason(vcpu).basic == EXIT_REASON_EXTERNAL_INTERRUPT) ++ switch (vmx_get_exit_reason(vcpu).basic) { ++ case EXIT_REASON_EXTERNAL_INTERRUPT: + handle_external_interrupt_irqoff(vcpu, vmx_get_intr_info(vcpu)); +- else if (vmx_get_exit_reason(vcpu).basic == EXIT_REASON_EXCEPTION_NMI) ++ break; ++ case EXIT_REASON_EXCEPTION_NMI: + handle_exception_irqoff(vcpu, vmx_get_intr_info(vcpu)); ++ break; ++ case EXIT_REASON_MCE_DURING_VMENTRY: ++ kvm_machine_check(); ++ break; ++ default: ++ break; ++ } + } + + /* +-- +2.43.0 + diff --git a/SPECS/kernel/0044-ntfs3-Fix-uninit-buffer-allocated-by-__getname.patch b/SPECS/kernel/0044-ntfs3-Fix-uninit-buffer-allocated-by-__getname.patch deleted file mode 100644 index b4dbcb77f..000000000 --- a/SPECS/kernel/0044-ntfs3-Fix-uninit-buffer-allocated-by-__getname.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0f2007ed423b7f4b46a6e36432b33793ff24a857 Mon Sep 17 00:00:00 2001 -From: Sidharth Seela -Date: Tue, 23 Sep 2025 12:10:16 +0530 -Subject: [PATCH 44/51] ntfs3: Fix uninit buffer allocated by __getname() - -Fix uninit errors caused after buffer allocation given to 'de'; by -initializing the buffer with zeroes. The fix was found by using KMSAN. - -Reported-by: syzbot+332bd4e9d148f11a87dc@syzkaller.appspotmail.com -Fixes: 78ab59fee07f2 ("fs/ntfs3: Rework file operations") -Signed-off-by: Sidharth Seela -Signed-off-by: Konstantin Komarov ---- - fs/ntfs3/inode.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/fs/ntfs3/inode.c b/fs/ntfs3/inode.c -index 7a89d31ccc1c..68862da31345 100644 ---- a/fs/ntfs3/inode.c -+++ b/fs/ntfs3/inode.c -@@ -1717,6 +1717,7 @@ int ntfs_link_inode(struct inode *inode, struct dentry *dentry) - de = kmem_cache_zalloc(names_cachep, GFP_KERNEL); - if (!de) - return -ENOMEM; -+ memset(de, 0, PATH_MAX); - - /* Mark rw ntfs as dirty. It will be cleared at umount. */ - ntfs_set_state(sbi, NTFS_DIRTY_DIRTY); --- -2.43.0 - diff --git a/SPECS/kernel/0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf b/SPECS/kernel/0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf deleted file mode 100644 index fef20b430..000000000 --- a/SPECS/kernel/0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf +++ /dev/null @@ -1,83 +0,0 @@ -From 4f8df081826ebbd3b171763e977e72f4dbfffa02 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 12:11:20 -0700 -Subject: [PATCH 045/100] KVM: x86: Fold WRMSR fastpath helpers into the main - handler - -Fold the per-MSR WRMSR fastpath helpers into the main handler now that the -IPI path in particular is relatively tiny. In addition to eliminating a -decent amount of boilerplate, this removes the ugly -errno/1/0 => bool -conversion (which is "necessitated" by kvm_x2apic_icr_write_fast()). - -Opportunistically drop the comment about IPIs, as the purpose of the -fastpath is hopefully self-evident, and _if_ it needs more documentation, -the documentation (and rules!) should be placed in a more central location. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/x86.c | 34 +++++----------------------------- - 1 file changed, 5 insertions(+), 29 deletions(-) - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index e6c221f9b92e..a4441f036929 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2133,48 +2133,24 @@ static inline bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu) - kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending(); - } - --/* -- * The fast path for frequent and performance sensitive wrmsr emulation, -- * i.e. the sending of IPI, sending IPI early in the VM-Exit flow reduces -- * the latency of virtual IPI by avoiding the expensive bits of transitioning -- * from guest to host, e.g. reacquiring KVM's SRCU lock. In contrast to the -- * other cases which must be called after interrupts are enabled on the host. -- */ --static int handle_fastpath_set_x2apic_icr_irqoff(struct kvm_vcpu *vcpu, u64 data) --{ -- if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic)) -- return 1; -- -- return kvm_x2apic_icr_write_fast(vcpu->arch.apic, data); --} -- --static int handle_fastpath_set_tscdeadline(struct kvm_vcpu *vcpu, u64 data) --{ -- kvm_set_lapic_tscdeadline_msr(vcpu, data); -- return 0; --} -- - fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { - u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); -- bool handled; - int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -- handled = !handle_fastpath_set_x2apic_icr_irqoff(vcpu, data); -+ if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic) || -+ kvm_x2apic_icr_write_fast(vcpu->arch.apic, data)) -+ return EXIT_FASTPATH_NONE; - break; - case MSR_IA32_TSC_DEADLINE: -- handled = !handle_fastpath_set_tscdeadline(vcpu, data); -+ kvm_set_lapic_tscdeadline_msr(vcpu, data); - break; - default: -- handled = false; -- break; -- } -- -- if (!handled) - return EXIT_FASTPATH_NONE; -+ } - - kvm_vcpu_srcu_read_lock(vcpu); - r = kvm_skip_emulated_instruction(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf b/SPECS/kernel/0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf deleted file mode 100644 index 679bcff47..000000000 --- a/SPECS/kernel/0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf +++ /dev/null @@ -1,139 +0,0 @@ -From 0c532d8388151e10330a0c9b3390f205ac6a3283 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 17:34:40 -0700 -Subject: [PATCH 046/100] KVM: x86/pmu: Move kvm_init_pmu_capability() to pmu.c - -Move kvm_init_pmu_capability() to pmu.c so that future changes can access -variables that have no business being visible outside of pmu.c. -kvm_init_pmu_capability() is called once per module load, there's is zero -reason it needs to be inlined. - -No functional change intended. - -Cc: Dapeng Mi -Cc: Sandipan Das -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ - arch/x86/kvm/pmu.h | 47 +--------------------------------------------- - 2 files changed, 48 insertions(+), 46 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 75e9cfc689f8..eb17d90916ea 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -96,6 +96,53 @@ void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) - #undef __KVM_X86_PMU_OP - } - -+void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) -+{ -+ bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; -+ int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; -+ -+ /* -+ * Hybrid PMUs don't play nice with virtualization without careful -+ * configuration by userspace, and KVM's APIs for reporting supported -+ * vPMU features do not account for hybrid PMUs. Disable vPMU support -+ * for hybrid PMUs until KVM gains a way to let userspace opt-in. -+ */ -+ if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) -+ enable_pmu = false; -+ -+ if (enable_pmu) { -+ perf_get_x86_pmu_capability(&kvm_pmu_cap); -+ -+ /* -+ * WARN if perf did NOT disable hardware PMU if the number of -+ * architecturally required GP counters aren't present, i.e. if -+ * there are a non-zero number of counters, but fewer than what -+ * is architecturally required. -+ */ -+ if (!kvm_pmu_cap.num_counters_gp || -+ WARN_ON_ONCE(kvm_pmu_cap.num_counters_gp < min_nr_gp_ctrs)) -+ enable_pmu = false; -+ else if (is_intel && !kvm_pmu_cap.version) -+ enable_pmu = false; -+ } -+ -+ if (!enable_pmu) { -+ memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); -+ return; -+ } -+ -+ kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); -+ kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp, -+ pmu_ops->MAX_NR_GP_COUNTERS); -+ kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed, -+ KVM_MAX_NR_FIXED_COUNTERS); -+ -+ kvm_pmu_eventsel.INSTRUCTIONS_RETIRED = -+ perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS); -+ kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED = -+ perf_get_hw_event_config(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); -+} -+ - static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 103604c4b33b..71796270918f 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -180,52 +180,7 @@ static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) - extern struct x86_pmu_capability kvm_pmu_cap; - extern struct kvm_pmu_emulated_event_selectors kvm_pmu_eventsel; - --static inline void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) --{ -- bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; -- int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; -- -- /* -- * Hybrid PMUs don't play nice with virtualization without careful -- * configuration by userspace, and KVM's APIs for reporting supported -- * vPMU features do not account for hybrid PMUs. Disable vPMU support -- * for hybrid PMUs until KVM gains a way to let userspace opt-in. -- */ -- if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) -- enable_pmu = false; -- -- if (enable_pmu) { -- perf_get_x86_pmu_capability(&kvm_pmu_cap); -- -- /* -- * WARN if perf did NOT disable hardware PMU if the number of -- * architecturally required GP counters aren't present, i.e. if -- * there are a non-zero number of counters, but fewer than what -- * is architecturally required. -- */ -- if (!kvm_pmu_cap.num_counters_gp || -- WARN_ON_ONCE(kvm_pmu_cap.num_counters_gp < min_nr_gp_ctrs)) -- enable_pmu = false; -- else if (is_intel && !kvm_pmu_cap.version) -- enable_pmu = false; -- } -- -- if (!enable_pmu) { -- memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); -- return; -- } -- -- kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); -- kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp, -- pmu_ops->MAX_NR_GP_COUNTERS); -- kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed, -- KVM_MAX_NR_FIXED_COUNTERS); -- -- kvm_pmu_eventsel.INSTRUCTIONS_RETIRED = -- perf_get_hw_event_config(PERF_COUNT_HW_INSTRUCTIONS); -- kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED = -- perf_get_hw_event_config(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); --} -+void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); - - static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) - { --- -2.43.0 - diff --git a/SPECS/kernel/0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf b/SPECS/kernel/0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf deleted file mode 100644 index b167d9aeb..000000000 --- a/SPECS/kernel/0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf +++ /dev/null @@ -1,145 +0,0 @@ -From 973ca934c1905e7bf212d8e99c560b9490d2dce3 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 10:17:46 -0700 -Subject: [PATCH 047/100] KVM: x86/pmu: Add wrappers for counting emulated - instructions/branches - -Add wrappers for triggering instruction retired and branch retired PMU -events in anticipation of reworking the internal mechanisms to track -which PMCs need to be evaluated, e.g. to avoid having to walk and check -every PMC. - -Opportunistically bury "struct kvm_pmu_emulated_event_selectors" in pmu.c. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 22 ++++++++++++++++++---- - arch/x86/kvm/pmu.h | 9 ++------- - arch/x86/kvm/vmx/nested.c | 2 +- - arch/x86/kvm/x86.c | 6 +++--- - 4 files changed, 24 insertions(+), 15 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index eb17d90916ea..e1911b366c43 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -29,8 +29,11 @@ - struct x86_pmu_capability __read_mostly kvm_pmu_cap; - EXPORT_SYMBOL_GPL(kvm_pmu_cap); - --struct kvm_pmu_emulated_event_selectors __read_mostly kvm_pmu_eventsel; --EXPORT_SYMBOL_GPL(kvm_pmu_eventsel); -+struct kvm_pmu_emulated_event_selectors { -+ u64 INSTRUCTIONS_RETIRED; -+ u64 BRANCH_INSTRUCTIONS_RETIRED; -+}; -+static struct kvm_pmu_emulated_event_selectors __read_mostly kvm_pmu_eventsel; - - /* Precise Distribution of Instructions Retired (PDIR) */ - static const struct x86_cpu_id vmx_pebs_pdir_cpu[] = { -@@ -907,7 +910,7 @@ static inline bool cpl_is_matched(struct kvm_pmc *pmc) - select_user; - } - --void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) -+static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - { - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -@@ -944,7 +947,18 @@ void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - kvm_pmu_incr_counter(pmc); - } - } --EXPORT_SYMBOL_GPL(kvm_pmu_trigger_event); -+ -+void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu) -+{ -+ kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+} -+EXPORT_SYMBOL_GPL(kvm_pmu_instruction_retired); -+ -+void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu) -+{ -+ kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+} -+EXPORT_SYMBOL_GPL(kvm_pmu_branch_retired); - - static bool is_masked_filter_valid(const struct kvm_x86_pmu_event_filter *filter) - { -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 71796270918f..c4dbdc0e6fc5 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -23,11 +23,6 @@ - - #define KVM_FIXED_PMC_BASE_IDX INTEL_PMC_IDX_FIXED - --struct kvm_pmu_emulated_event_selectors { -- u64 INSTRUCTIONS_RETIRED; -- u64 BRANCH_INSTRUCTIONS_RETIRED; --}; -- - struct kvm_pmu_ops { - struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu, - unsigned int idx, u64 *mask); -@@ -178,7 +173,6 @@ static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) - } - - extern struct x86_pmu_capability kvm_pmu_cap; --extern struct kvm_pmu_emulated_event_selectors kvm_pmu_eventsel; - - void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); - -@@ -227,7 +221,8 @@ void kvm_pmu_init(struct kvm_vcpu *vcpu); - void kvm_pmu_cleanup(struct kvm_vcpu *vcpu); - void kvm_pmu_destroy(struct kvm_vcpu *vcpu); - int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp); --void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel); -+void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu); -+void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index b8ea1969113d..db2fd4eedc90 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -3690,7 +3690,7 @@ static int nested_vmx_run(struct kvm_vcpu *vcpu, bool launch) - return 1; - } - -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+ kvm_pmu_branch_retired(vcpu); - - if (CC(evmptrld_status == EVMPTRLD_VMFAIL)) - return nested_vmx_failInvalid(vcpu); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a4441f036929..f2b2eaaec6f8 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -8824,7 +8824,7 @@ int kvm_skip_emulated_instruction(struct kvm_vcpu *vcpu) - if (unlikely(!r)) - return 0; - -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+ kvm_pmu_instruction_retired(vcpu); - - /* - * rflags is the old, "raw" value of the flags. The new value has -@@ -9158,9 +9158,9 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - */ - if (!ctxt->have_exception || - exception_type(ctxt->exception.vector) == EXCPT_TRAP) { -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+ kvm_pmu_instruction_retired(vcpu); - if (ctxt->is_branch) -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+ kvm_pmu_branch_retired(vcpu); - kvm_rip_write(vcpu, ctxt->eip); - if (r && (ctxt->tf || (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP))) - r = kvm_vcpu_do_singlestep(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0047-wifi-ath11k-fix-peer-HE-MCS-assignment.patch b/SPECS/kernel/0047-wifi-ath11k-fix-peer-HE-MCS-assignment.patch deleted file mode 100644 index 4f98c0af1..000000000 --- a/SPECS/kernel/0047-wifi-ath11k-fix-peer-HE-MCS-assignment.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 5808c982fe02b6b3ba6b0d02de2c429a9e9c05bb Mon Sep 17 00:00:00 2001 -From: Baochen Qiang -Date: Fri, 17 Oct 2025 09:49:00 +0800 -Subject: [PATCH 47/51] wifi: ath11k: fix peer HE MCS assignment - -In ath11k_wmi_send_peer_assoc_cmd(), peer's transmit MCS is sent to -firmware as receive MCS while peer's receive MCS sent as transmit MCS, -which goes against firmwire's definition. - -While connecting to a misbehaved AP that advertises 0xffff (meaning not -supported) for 160 MHz transmit MCS map, firmware crashes due to 0xffff -is assigned to he_mcs->rx_mcs_set field. - - Ext Tag: HE Capabilities - [...] - Supported HE-MCS and NSS Set - [...] - Rx and Tx MCS Maps 160 MHz - [...] - Tx HE-MCS Map 160 MHz: 0xffff - -Swap the assignment to fix this issue. - -As the HE rate control mask is meant to limit our own transmit MCS, it -needs to go via he_mcs->rx_mcs_set field. With the aforementioned swapping -done, change is needed as well to apply it to the peer's receive MCS. - -Tested-on: WCN6855 hw2.1 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41 -Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 - -Fixes: 61fe43e7216d ("ath11k: add support for setting fixed HE rate/gi/ltf") -Signed-off-by: Baochen Qiang -Reviewed-by: Vasanthakumar Thiagarajan -Link: https://patch.msgid.link/20251017-ath11k-mcs-assignment-v1-2-da40825c1783@oss.qualcomm.com -Signed-off-by: Jeff Johnson ---- - drivers/net/wireless/ath/ath11k/mac.c | 4 ++-- - drivers/net/wireless/ath/ath11k/wmi.c | 7 +++++-- - 2 files changed, 7 insertions(+), 4 deletions(-) - -diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c -index 0e41b5a91d66..c3353bf8f16d 100644 ---- a/drivers/net/wireless/ath/ath11k/mac.c -+++ b/drivers/net/wireless/ath/ath11k/mac.c -@@ -2522,10 +2522,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, - he_tx_mcs = v; - } - v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); -+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; - - v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160); -- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; - - arg->peer_he_mcs_count++; -@@ -2535,10 +2535,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, - - default: - v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); -+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; - - v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); -- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask); - arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v; - - arg->peer_he_mcs_count++; -diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c -index e3b444333dee..d2972f081262 100644 ---- a/drivers/net/wireless/ath/ath11k/wmi.c -+++ b/drivers/net/wireless/ath/ath11k/wmi.c -@@ -2088,8 +2088,11 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar, - FIELD_PREP(WMI_TLV_LEN, - sizeof(*he_mcs) - TLV_HDR_SIZE); - -- he_mcs->rx_mcs_set = param->peer_he_tx_mcs_set[i]; -- he_mcs->tx_mcs_set = param->peer_he_rx_mcs_set[i]; -+ /* firmware interprets mcs->rx_mcs_set field as peer's -+ * RX capability -+ */ -+ he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i]; -+ he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i]; - ptr += sizeof(*he_mcs); - } - --- -2.43.0 - diff --git a/SPECS/kernel/0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf b/SPECS/kernel/0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf deleted file mode 100644 index 5f6344198..000000000 --- a/SPECS/kernel/0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf +++ /dev/null @@ -1,194 +0,0 @@ -From 2af921402abbff2c7480f7f0cdfdc66cf0e087ed Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 12:19:15 -0700 -Subject: [PATCH 048/100] KVM: x86/pmu: Calculate set of to-be-emulated PMCs at - time of WRMSRs - -Calculate and track PMCs that are counting instructions/branches retired -when the PMC's event selector (or fixed counter control) is modified -instead evaluating the event selector on-demand. Immediately recalc a -PMC's configuration on writes to avoid false negatives/positives when -KVM skips an emulated WRMSR, which is guaranteed to occur before the -main run loop processes KVM_REQ_PMU. - -Out of an abundance of caution, and because it's relatively cheap, recalc -reprogrammed PMCs in kvm_pmu_handle_event() as well. Recalculating in -response to KVM_REQ_PMU _should_ be unnecessary, but for now be paranoid -to avoid introducing easily-avoidable bugs in edge cases. The code can be -removed in the future if necessary, e.g. in the unlikely event that the -overhead of recalculating to-be-emulated PMCs is noticeable. - -Note! Deliberately don't check the PMU event filters, as doing so could -result in KVM consuming stale information. - -Tracking which PMCs are counting branches/instructions will allow grabbing -SRCU in the fastpath VM-Exit handlers if and only if a PMC event might be -triggered (to consult the event filters), and will also allow the upcoming -mediated PMU to do the right thing with respect to counting instructions -(the mediated PMU won't be able to update PMCs in the VM-Exit fastpath). - -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 3 ++ - arch/x86/kvm/pmu.c | 75 ++++++++++++++++++++++++--------- - arch/x86/kvm/pmu.h | 4 ++ - 3 files changed, 61 insertions(+), 21 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 7fb2bdcf42a8..95d7d727db03 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -579,6 +579,9 @@ struct kvm_pmu { - DECLARE_BITMAP(all_valid_pmc_idx, X86_PMC_IDX_MAX); - DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX); - -+ DECLARE_BITMAP(pmc_counting_instructions, X86_PMC_IDX_MAX); -+ DECLARE_BITMAP(pmc_counting_branches, X86_PMC_IDX_MAX); -+ - u64 ds_area; - u64 pebs_enable; - u64 pebs_enable_rsvd; -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e1911b366c43..b0f0275a2c2e 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -542,6 +542,47 @@ static int reprogram_counter(struct kvm_pmc *pmc) - eventsel & ARCH_PERFMON_EVENTSEL_INT); - } - -+static bool pmc_is_event_match(struct kvm_pmc *pmc, u64 eventsel) -+{ -+ /* -+ * Ignore checks for edge detect (all events currently emulated by KVM -+ * are always rising edges), pin control (unsupported by modern CPUs), -+ * and counter mask and its invert flag (KVM doesn't emulate multiple -+ * events in a single clock cycle). -+ * -+ * Note, the uppermost nibble of AMD's mask overlaps Intel's IN_TX (bit -+ * 32) and IN_TXCP (bit 33), as well as two reserved bits (bits 35:34). -+ * Checking the "in HLE/RTM transaction" flags is correct as the vCPU -+ * can't be in a transaction if KVM is emulating an instruction. -+ * -+ * Checking the reserved bits might be wrong if they are defined in the -+ * future, but so could ignoring them, so do the simple thing for now. -+ */ -+ return !((pmc->eventsel ^ eventsel) & AMD64_RAW_EVENT_MASK_NB); -+} -+ -+void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc) -+{ -+ bitmap_clear(pmu->pmc_counting_instructions, pmc->idx, 1); -+ bitmap_clear(pmu->pmc_counting_branches, pmc->idx, 1); -+ -+ /* -+ * Do NOT consult the PMU event filters, as the filters must be checked -+ * at the time of emulation to ensure KVM uses fresh information, e.g. -+ * omitting a PMC from a bitmap could result in a missed event if the -+ * filter is changed to allow counting the event. -+ */ -+ if (!pmc_speculative_in_use(pmc)) -+ return; -+ -+ if (pmc_is_event_match(pmc, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED)) -+ bitmap_set(pmu->pmc_counting_instructions, pmc->idx, 1); -+ -+ if (pmc_is_event_match(pmc, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED)) -+ bitmap_set(pmu->pmc_counting_branches, pmc->idx, 1); -+} -+EXPORT_SYMBOL_GPL(kvm_pmu_recalc_pmc_emulation); -+ - void kvm_pmu_handle_event(struct kvm_vcpu *vcpu) - { - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); -@@ -577,6 +618,9 @@ void kvm_pmu_handle_event(struct kvm_vcpu *vcpu) - */ - if (unlikely(pmu->need_cleanup)) - kvm_pmu_cleanup(vcpu); -+ -+ kvm_for_each_pmc(pmu, pmc, bit, bitmap) -+ kvm_pmu_recalc_pmc_emulation(pmu, pmc); - } - - int kvm_pmu_check_rdpmc_early(struct kvm_vcpu *vcpu, unsigned int idx) -@@ -910,7 +954,8 @@ static inline bool cpl_is_matched(struct kvm_pmc *pmc) - select_user; - } - --static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) -+static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, -+ const unsigned long *event_pmcs) - { - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -@@ -919,29 +964,17 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - - BUILD_BUG_ON(sizeof(pmu->global_ctrl) * BITS_PER_BYTE != X86_PMC_IDX_MAX); - -+ if (bitmap_empty(event_pmcs, X86_PMC_IDX_MAX)) -+ return; -+ - if (!kvm_pmu_has_perf_global_ctrl(pmu)) -- bitmap_copy(bitmap, pmu->all_valid_pmc_idx, X86_PMC_IDX_MAX); -- else if (!bitmap_and(bitmap, pmu->all_valid_pmc_idx, -+ bitmap_copy(bitmap, event_pmcs, X86_PMC_IDX_MAX); -+ else if (!bitmap_and(bitmap, event_pmcs, - (unsigned long *)&pmu->global_ctrl, X86_PMC_IDX_MAX)) - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- /* -- * Ignore checks for edge detect (all events currently emulated -- * but KVM are always rising edges), pin control (unsupported -- * by modern CPUs), and counter mask and its invert flag (KVM -- * doesn't emulate multiple events in a single clock cycle). -- * -- * Note, the uppermost nibble of AMD's mask overlaps Intel's -- * IN_TX (bit 32) and IN_TXCP (bit 33), as well as two reserved -- * bits (bits 35:34). Checking the "in HLE/RTM transaction" -- * flags is correct as the vCPU can't be in a transaction if -- * KVM is emulating an instruction. Checking the reserved bits -- * might be wrong if they are defined in the future, but so -- * could ignoring them, so do the simple thing for now. -- */ -- if (((pmc->eventsel ^ eventsel) & AMD64_RAW_EVENT_MASK_NB) || -- !pmc_event_is_allowed(pmc) || !cpl_is_matched(pmc)) -+ if (!pmc_event_is_allowed(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); -@@ -950,13 +983,13 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, u64 eventsel) - - void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu) - { -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED); -+ kvm_pmu_trigger_event(vcpu, vcpu_to_pmu(vcpu)->pmc_counting_instructions); - } - EXPORT_SYMBOL_GPL(kvm_pmu_instruction_retired); - - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu) - { -- kvm_pmu_trigger_event(vcpu, kvm_pmu_eventsel.BRANCH_INSTRUCTIONS_RETIRED); -+ kvm_pmu_trigger_event(vcpu, vcpu_to_pmu(vcpu)->pmc_counting_branches); - } - EXPORT_SYMBOL_GPL(kvm_pmu_branch_retired); - -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index c4dbdc0e6fc5..c6e34db08264 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -176,8 +176,12 @@ extern struct x86_pmu_capability kvm_pmu_cap; - - void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); - -+void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc); -+ - static inline void kvm_pmu_request_counter_reprogram(struct kvm_pmc *pmc) - { -+ kvm_pmu_recalc_pmc_emulation(pmc_to_pmu(pmc), pmc); -+ - set_bit(pmc->idx, pmc_to_pmu(pmc)->reprogram_pmi); - kvm_make_request(KVM_REQ_PMU, pmc->vcpu); - } --- -2.43.0 - diff --git a/SPECS/kernel/0048-wifi-rtl818x-rtl8187-Fix-potential-buffer-underflow-.patch b/SPECS/kernel/0048-wifi-rtl818x-rtl8187-Fix-potential-buffer-underflow-.patch deleted file mode 100644 index c58a92fc9..000000000 --- a/SPECS/kernel/0048-wifi-rtl818x-rtl8187-Fix-potential-buffer-underflow-.patch +++ /dev/null @@ -1,85 +0,0 @@ -From f60a0d857b51613bc16174202c14190b67e67ab7 Mon Sep 17 00:00:00 2001 -From: Seungjin Bae -Date: Mon, 17 Nov 2025 20:32:59 -0500 -Subject: [PATCH 48/51] wifi: rtl818x: rtl8187: Fix potential buffer underflow - in rtl8187_rx_cb() - -The rtl8187_rx_cb() calculates the rx descriptor header address -by subtracting its size from the skb tail pointer. -However, it does not validate if the received packet -(skb->len from urb->actual_length) is large enough to contain this -header. - -If a truncated packet is received, this will lead to a buffer -underflow, reading memory before the start of the skb data area, -and causing a kernel panic. - -Add length checks for both rtl8187 and rtl8187b descriptor headers -before attempting to access them, dropping the packet cleanly if the -check fails. - -Fixes: 6f7853f3cbe4 ("rtl8187: change rtl8187_dev.c to support RTL8187B (part 2)") -Signed-off-by: Seungjin Bae -Signed-off-by: Ping-Ke Shih -Link: https://patch.msgid.link/20251118013258.1789949-2-eeodqql09@gmail.com ---- - .../wireless/realtek/rtl818x/rtl8187/dev.c | 27 +++++++++++++------ - 1 file changed, 19 insertions(+), 8 deletions(-) - -diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c -index 0c5c66401daa..7aa2da0cd63c 100644 ---- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c -+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c -@@ -338,14 +338,16 @@ static void rtl8187_rx_cb(struct urb *urb) - spin_unlock_irqrestore(&priv->rx_queue.lock, f); - skb_put(skb, urb->actual_length); - -- if (unlikely(urb->status)) { -- dev_kfree_skb_irq(skb); -- return; -- } -+ if (unlikely(urb->status)) -+ goto free_skb; - - if (!priv->is_rtl8187b) { -- struct rtl8187_rx_hdr *hdr = -- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); -+ struct rtl8187_rx_hdr *hdr; -+ -+ if (skb->len < sizeof(struct rtl8187_rx_hdr)) -+ goto free_skb; -+ -+ hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - flags = le32_to_cpu(hdr->flags); - /* As with the RTL8187B below, the AGC is used to calculate - * signal strength. In this case, the scaling -@@ -355,8 +357,12 @@ static void rtl8187_rx_cb(struct urb *urb) - rx_status.antenna = (hdr->signal >> 7) & 1; - rx_status.mactime = le64_to_cpu(hdr->mac_time); - } else { -- struct rtl8187b_rx_hdr *hdr = -- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); -+ struct rtl8187b_rx_hdr *hdr; -+ -+ if (skb->len < sizeof(struct rtl8187b_rx_hdr)) -+ goto free_skb; -+ -+ hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); - /* The Realtek datasheet for the RTL8187B shows that the RX - * header contains the following quantities: signal quality, - * RSSI, AGC, the received power in dB, and the measured SNR. -@@ -409,6 +415,11 @@ static void rtl8187_rx_cb(struct urb *urb) - skb_unlink(skb, &priv->rx_queue); - dev_kfree_skb_irq(skb); - } -+ return; -+ -+free_skb: -+ dev_kfree_skb_irq(skb); -+ return; - } - - static int rtl8187_init_urbs(struct ieee80211_hw *dev) --- -2.43.0 - diff --git a/SPECS/kernel/0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf b/SPECS/kernel/0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf deleted file mode 100644 index fb06bdc2c..000000000 --- a/SPECS/kernel/0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf +++ /dev/null @@ -1,85 +0,0 @@ -From 7f1ae2a996dee94172025d61038addf3b98a74ea Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:33:11 -0700 -Subject: [PATCH 049/100] KVM: x86/pmu: Rename pmc_speculative_in_use() to - pmc_is_locally_enabled() - -Rename pmc_speculative_in_use() to pmc_is_locally_enabled() to better -capture what it actually tracks, and to show its relationship to -pmc_is_globally_enabled(). While neither AMD nor Intel refer to event -selectors or the fixed counter control MSR as "local", it's the obvious -name to pair with "global". - -As for "speculative", there's absolutely nothing speculative about the -checks. E.g. for PMUs without PERF_GLOBAL_CTRL, from the guest's -perspective, the counters are "in use" without any qualifications. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 6 +++--- - arch/x86/kvm/pmu.h | 2 +- - arch/x86/kvm/vmx/pmu_intel.c | 2 +- - 3 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index b0f0275a2c2e..e73c2a44028b 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -493,7 +493,7 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc) - - static bool pmc_event_is_allowed(struct kvm_pmc *pmc) - { -- return pmc_is_globally_enabled(pmc) && pmc_speculative_in_use(pmc) && -+ return pmc_is_globally_enabled(pmc) && pmc_is_locally_enabled(pmc) && - check_pmu_event_filter(pmc); - } - -@@ -572,7 +572,7 @@ void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc) - * omitting a PMC from a bitmap could result in a missed event if the - * filter is changed to allow counting the event. - */ -- if (!pmc_speculative_in_use(pmc)) -+ if (!pmc_is_locally_enabled(pmc)) - return; - - if (pmc_is_event_match(pmc, kvm_pmu_eventsel.INSTRUCTIONS_RETIRED)) -@@ -907,7 +907,7 @@ void kvm_pmu_cleanup(struct kvm_vcpu *vcpu) - pmu->pmc_in_use, X86_PMC_IDX_MAX); - - kvm_for_each_pmc(pmu, pmc, i, bitmask) { -- if (pmc->perf_event && !pmc_speculative_in_use(pmc)) -+ if (pmc->perf_event && !pmc_is_locally_enabled(pmc)) - pmc_stop_counter(pmc); - } - -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index c6e34db08264..5c3939e91f1d 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -160,7 +160,7 @@ static inline struct kvm_pmc *get_fixed_pmc(struct kvm_pmu *pmu, u32 msr) - return NULL; - } - --static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) -+static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); - -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index e8b37a38fbba..b2a2c4ebf448 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -762,7 +762,7 @@ void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu) - int bit, hw_idx; - - kvm_for_each_pmc(pmu, pmc, bit, (unsigned long *)&pmu->global_ctrl) { -- if (!pmc_speculative_in_use(pmc) || -+ if (!pmc_is_locally_enabled(pmc) || - !pmc_is_globally_enabled(pmc) || !pmc->perf_event) - continue; - --- -2.43.0 - diff --git a/SPECS/kernel/0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf b/SPECS/kernel/0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf deleted file mode 100644 index 00b1a77ad..000000000 --- a/SPECS/kernel/0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf +++ /dev/null @@ -1,57 +0,0 @@ -From c263d7ea7b358d134940a20445c26ee01702af71 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:36:47 -0700 -Subject: [PATCH 050/100] KVM: x86/pmu: Open code pmc_event_is_allowed() in its - callers - -Open code pmc_event_is_allowed() in its callers, as kvm_pmu_trigger_event() -only needs to check the event filter (both global and local enables are -consulted outside of the loop). - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 12 ++++-------- - 1 file changed, 4 insertions(+), 8 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e73c2a44028b..a495ab5d0556 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -491,12 +491,6 @@ static bool check_pmu_event_filter(struct kvm_pmc *pmc) - return is_fixed_event_allowed(filter, pmc->idx); - } - --static bool pmc_event_is_allowed(struct kvm_pmc *pmc) --{ -- return pmc_is_globally_enabled(pmc) && pmc_is_locally_enabled(pmc) && -- check_pmu_event_filter(pmc); --} -- - static int reprogram_counter(struct kvm_pmc *pmc) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -@@ -507,7 +501,8 @@ static int reprogram_counter(struct kvm_pmc *pmc) - - emulate_overflow = pmc_pause_counter(pmc); - -- if (!pmc_event_is_allowed(pmc)) -+ if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -+ !check_pmu_event_filter(pmc)) - return 0; - - if (emulate_overflow) -@@ -974,7 +969,8 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!pmc_event_is_allowed(pmc) || !cpl_is_matched(pmc)) -+ if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -+ !check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); --- -2.43.0 - diff --git a/SPECS/kernel/0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf b/SPECS/kernel/0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf deleted file mode 100644 index 996cc5400..000000000 --- a/SPECS/kernel/0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf +++ /dev/null @@ -1,34 +0,0 @@ -From b8f68b83d57e6128be521f34bc581866cf685623 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:38:11 -0700 -Subject: [PATCH 051/100] KVM: x86/pmu: Drop redundant check on PMC being - globally enabled for emulation - -When triggering PMC events in response to emulation, drop the redundant -checks on a PMC being globally and locally enabled, as the passed in bitmap -contains only PMCs that are locally enabled (and counting the right event), -and the local copy of the bitmap has already been masked with global_ctrl. - -No true functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index a495ab5d0556..bdcd9c6f0ec0 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -969,7 +969,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -+ if (!pmc_is_locally_enabled(pmc) || - !check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) - continue; - --- -2.43.0 - diff --git a/SPECS/kernel/0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf b/SPECS/kernel/0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf deleted file mode 100644 index 44d8b524e..000000000 --- a/SPECS/kernel/0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf +++ /dev/null @@ -1,31 +0,0 @@ -From d5a6332c6e3593d85d144069d9d7e637c0eb1c27 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:39:32 -0700 -Subject: [PATCH 052/100] KVM: x86/pmu: Drop redundant check on PMC being - locally enabled for emulation - -Drop the check on a PMC being locally enabled when triggering emulated -events, as the bitmap of passed-in PMCs only contains locally enabled PMCs. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index bdcd9c6f0ec0..422af7734846 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -969,8 +969,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!pmc_is_locally_enabled(pmc) || -- !check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) -+ if (!check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); --- -2.43.0 - diff --git a/SPECS/kernel/0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf b/SPECS/kernel/0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf deleted file mode 100644 index 223bc6369..000000000 --- a/SPECS/kernel/0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf +++ /dev/null @@ -1,50 +0,0 @@ -From 0b1ea8b95321e1d446235a8fcbea199a91581d5c Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:58:49 -0700 -Subject: [PATCH 053/100] KVM: x86/pmu: Rename check_pmu_event_filter() to - pmc_is_event_allowed() - -Rename check_pmu_event_filter() to make its polarity more obvious, and to -connect the dots to is_gp_event_allowed() and is_fixed_event_allowed(). - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 422af7734846..e75671b6e88c 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -476,7 +476,7 @@ static bool is_fixed_event_allowed(struct kvm_x86_pmu_event_filter *filter, - return true; - } - --static bool check_pmu_event_filter(struct kvm_pmc *pmc) -+static bool pmc_is_event_allowed(struct kvm_pmc *pmc) - { - struct kvm_x86_pmu_event_filter *filter; - struct kvm *kvm = pmc->vcpu->kvm; -@@ -502,7 +502,7 @@ static int reprogram_counter(struct kvm_pmc *pmc) - emulate_overflow = pmc_pause_counter(pmc); - - if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || -- !check_pmu_event_filter(pmc)) -+ !pmc_is_event_allowed(pmc)) - return 0; - - if (emulate_overflow) -@@ -969,7 +969,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - return; - - kvm_for_each_pmc(pmu, pmc, i, bitmap) { -- if (!check_pmu_event_filter(pmc) || !cpl_is_matched(pmc)) -+ if (!pmc_is_event_allowed(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); --- -2.43.0 - diff --git a/SPECS/kernel/0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf b/SPECS/kernel/0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf deleted file mode 100644 index 7d4818794..000000000 --- a/SPECS/kernel/0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf +++ /dev/null @@ -1,106 +0,0 @@ -From aa7ec2aa6714a53494483673831ad443cc3eeab6 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Thu, 31 Jul 2025 15:53:51 -0700 -Subject: [PATCH 054/100] KVM: x86: Push acquisition of SRCU in fastpath into - kvm_pmu_trigger_event() - -Acquire SRCU in the VM-Exit fastpath if and only if KVM needs to check the -PMU event filter, to further trim the amount of code that is executed with -SRCU protection in the fastpath. Counter-intuitively, holding SRCU can do -more harm than good due to masking potential bugs, and introducing a new -SRCU-protected asset to code reachable via kvm_skip_emulated_instruction() -would be quite notable, i.e. definitely worth auditing. - -E.g. the primary user of kvm->srcu is KVM's memslots, accessing memslots -all but guarantees guest memory may be accessed, accessing guest memory -can fault, and page faults might sleep, which isn't allowed while IRQs are -disabled. Not acquiring SRCU means the (hypothetical) illegal sleep would -be flagged when running with PROVE_RCU=y, even if DEBUG_ATOMIC_SLEEP=n. - -Note, performance is NOT a motivating factor, as SRCU lock/unlock only -adds ~15 cycles of latency to fastpath VM-Exits. I.e. overhead isn't a -concern _if_ SRCU protection needs to be extended beyond PMU events, e.g. -to honor userspace MSR filters. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 4 +++- - arch/x86/kvm/x86.c | 18 +++++------------- - 2 files changed, 8 insertions(+), 14 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e75671b6e88c..3206412a35a1 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -955,7 +955,7 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - DECLARE_BITMAP(bitmap, X86_PMC_IDX_MAX); - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); - struct kvm_pmc *pmc; -- int i; -+ int i, idx; - - BUILD_BUG_ON(sizeof(pmu->global_ctrl) * BITS_PER_BYTE != X86_PMC_IDX_MAX); - -@@ -968,12 +968,14 @@ static void kvm_pmu_trigger_event(struct kvm_vcpu *vcpu, - (unsigned long *)&pmu->global_ctrl, X86_PMC_IDX_MAX)) - return; - -+ idx = srcu_read_lock(&vcpu->kvm->srcu); - kvm_for_each_pmc(pmu, pmc, i, bitmap) { - if (!pmc_is_event_allowed(pmc) || !cpl_is_matched(pmc)) - continue; - - kvm_pmu_incr_counter(pmc); - } -+ srcu_read_unlock(&vcpu->kvm->srcu, idx); - } - - void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index f2b2eaaec6f8..a56f83b40a55 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2137,7 +2137,6 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - { - u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); -- int r; - - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): -@@ -2152,13 +2151,12 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - return EXIT_FASTPATH_NONE; - } - -- kvm_vcpu_srcu_read_lock(vcpu); -- r = kvm_skip_emulated_instruction(vcpu); -- kvm_vcpu_srcu_read_unlock(vcpu); -- - trace_kvm_msr_write(msr, data); - -- return r ? EXIT_FASTPATH_REENTER_GUEST : EXIT_FASTPATH_EXIT_USERSPACE; -+ if (!kvm_skip_emulated_instruction(vcpu)) -+ return EXIT_FASTPATH_EXIT_USERSPACE; -+ -+ return EXIT_FASTPATH_REENTER_GUEST; - } - EXPORT_SYMBOL_GPL(handle_fastpath_set_msr_irqoff); - -@@ -11251,13 +11249,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_halt); - - fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu) - { -- int ret; -- -- kvm_vcpu_srcu_read_lock(vcpu); -- ret = kvm_emulate_halt(vcpu); -- kvm_vcpu_srcu_read_unlock(vcpu); -- -- if (!ret) -+ if (!kvm_emulate_halt(vcpu)) - return EXIT_FASTPATH_EXIT_USERSPACE; - - if (kvm_vcpu_running(vcpu)) --- -2.43.0 - diff --git a/SPECS/kernel/0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf b/SPECS/kernel/0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf deleted file mode 100644 index abeea302d..000000000 --- a/SPECS/kernel/0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf +++ /dev/null @@ -1,82 +0,0 @@ -From f2e53da05e049ba69aef496b4383055ab9c85983 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 09:00:58 -0700 -Subject: [PATCH 055/100] KVM: x86: Add a fastpath handler for INVD - -Add a fastpath handler for INVD so that the common fastpath logic can be -trivially tested on both Intel and AMD. Under KVM, INVD is always: -(a) intercepted, (b) available to the guest, and (c) emulated as a nop, -with no side effects. Combined with INVD not having any inputs or outputs, -i.e. no register constraints, INVD is the perfect instruction for -exercising KVM's fastpath as it can be inserted into practically any -guest-side code stream. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/svm.c | 2 ++ - arch/x86/kvm/vmx/vmx.c | 2 ++ - arch/x86/kvm/x86.c | 9 +++++++++ - arch/x86/kvm/x86.h | 1 + - 4 files changed, 14 insertions(+) - -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 829d9d46718d..f7e1e665a826 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4200,6 +4200,8 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu) - return handle_fastpath_set_msr_irqoff(vcpu); - case SVM_EXIT_HLT: - return handle_fastpath_hlt(vcpu); -+ case SVM_EXIT_INVD: -+ return handle_fastpath_invd(vcpu); - default: - break; - } -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index aa157fe5b7b3..95765db52992 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7175,6 +7175,8 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu, - return handle_fastpath_preemption_timer(vcpu, force_immediate_exit); - case EXIT_REASON_HLT: - return handle_fastpath_hlt(vcpu); -+ case EXIT_REASON_INVD: -+ return handle_fastpath_invd(vcpu); - default: - return EXIT_FASTPATH_NONE; - } -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a56f83b40a55..5af2c5aed0f2 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2086,6 +2086,15 @@ int kvm_emulate_invd(struct kvm_vcpu *vcpu) - } - EXPORT_SYMBOL_GPL(kvm_emulate_invd); - -+fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu) -+{ -+ if (!kvm_emulate_invd(vcpu)) -+ return EXIT_FASTPATH_EXIT_USERSPACE; -+ -+ return EXIT_FASTPATH_REENTER_GUEST; -+} -+EXPORT_SYMBOL_GPL(handle_fastpath_invd); -+ - int kvm_handle_invalid_op(struct kvm_vcpu *vcpu) - { - kvm_queue_exception(vcpu, UD_VECTOR); -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index bcfd9b719ada..46220b04cdf2 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -439,6 +439,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - int emulation_type, void *insn, int insn_len); - fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu); - fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu); -+fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu); - - extern struct kvm_caps kvm_caps; - extern struct kvm_host_values kvm_host; --- -2.43.0 - diff --git a/SPECS/kernel/0056-perf-Skip-pmu_ctx-based-on-event_type.perf b/SPECS/kernel/0056-perf-Skip-pmu_ctx-based-on-event_type.perf deleted file mode 100644 index a07fe9992..000000000 --- a/SPECS/kernel/0056-perf-Skip-pmu_ctx-based-on-event_type.perf +++ /dev/null @@ -1,282 +0,0 @@ -From ba1d8614d882bf13222c12c2603d0d0be4caea70 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:42 +0000 -Subject: [PATCH 056/100] perf: Skip pmu_ctx based on event_type - -To optimize the cgroup context switch, the perf_event_pmu_context -iteration skips the PMUs without cgroup events. A bool cgroup was -introduced to indicate the case. It can work, but this way is hard to -extend for other cases, e.g. skipping non-mediated PMUs. It doesn't -make sense to keep adding bool variables. - -Pass the event_type instead of the specific bool variable. Check both -the event_type and related pmu_ctx variables to decide whether skipping -a PMU. - -Event flags, e.g., EVENT_CGROUP, should be cleard in the ctx->is_active. -Add EVENT_FLAGS to indicate such event flags. - -No functional change. - -Signed-off-by: Kan Liang -Tested-by: Yongwei Ma -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - kernel/events/core.c | 74 ++++++++++++++++++++++++-------------------- - 1 file changed, 40 insertions(+), 34 deletions(-) - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 0ff00d0b59ea..3054d6a866ac 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -164,7 +164,7 @@ enum event_type_t { - /* see ctx_resched() for details */ - EVENT_CPU = 0x10, - EVENT_CGROUP = 0x20, -- -+ EVENT_FLAGS = EVENT_CGROUP, - /* compound helpers */ - EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED, - EVENT_TIME_FROZEN = EVENT_TIME | EVENT_FROZEN, -@@ -778,27 +778,37 @@ do { \ - ___p; \ - }) - --#define for_each_epc(_epc, _ctx, _pmu, _cgroup) \ -+static bool perf_skip_pmu_ctx(struct perf_event_pmu_context *pmu_ctx, -+ enum event_type_t event_type) -+{ -+ if ((event_type & EVENT_CGROUP) && !pmu_ctx->nr_cgroups) -+ return true; -+ return false; -+} -+ -+#define for_each_epc(_epc, _ctx, _pmu, _event_type) \ - list_for_each_entry(_epc, &((_ctx)->pmu_ctx_list), pmu_ctx_entry) \ -- if (_cgroup && !_epc->nr_cgroups) \ -+ if (perf_skip_pmu_ctx(_epc, _event_type)) \ - continue; \ - else if (_pmu && _epc->pmu != _pmu) \ - continue; \ - else - --static void perf_ctx_disable(struct perf_event_context *ctx, bool cgroup) -+static void perf_ctx_disable(struct perf_event_context *ctx, -+ enum event_type_t event_type) - { - struct perf_event_pmu_context *pmu_ctx; - -- for_each_epc(pmu_ctx, ctx, NULL, cgroup) -+ for_each_epc(pmu_ctx, ctx, NULL, event_type) - perf_pmu_disable(pmu_ctx->pmu); - } - --static void perf_ctx_enable(struct perf_event_context *ctx, bool cgroup) -+static void perf_ctx_enable(struct perf_event_context *ctx, -+ enum event_type_t event_type) - { - struct perf_event_pmu_context *pmu_ctx; - -- for_each_epc(pmu_ctx, ctx, NULL, cgroup) -+ for_each_epc(pmu_ctx, ctx, NULL, event_type) - perf_pmu_enable(pmu_ctx->pmu); - } - -@@ -963,8 +973,7 @@ static void perf_cgroup_switch(struct task_struct *task) - return; - - WARN_ON_ONCE(cpuctx->ctx.nr_cgroups == 0); -- -- perf_ctx_disable(&cpuctx->ctx, true); -+ perf_ctx_disable(&cpuctx->ctx, EVENT_CGROUP); - - ctx_sched_out(&cpuctx->ctx, NULL, EVENT_ALL|EVENT_CGROUP); - /* -@@ -980,7 +989,7 @@ static void perf_cgroup_switch(struct task_struct *task) - */ - ctx_sched_in(&cpuctx->ctx, NULL, EVENT_ALL|EVENT_CGROUP); - -- perf_ctx_enable(&cpuctx->ctx, true); -+ perf_ctx_enable(&cpuctx->ctx, EVENT_CGROUP); - } - - static int perf_cgroup_ensure_storage(struct perf_event *event, -@@ -2904,11 +2913,11 @@ static void ctx_resched(struct perf_cpu_context *cpuctx, - - event_type &= EVENT_ALL; - -- for_each_epc(epc, &cpuctx->ctx, pmu, false) -+ for_each_epc(epc, &cpuctx->ctx, pmu, 0) - perf_pmu_disable(epc->pmu); - - if (task_ctx) { -- for_each_epc(epc, task_ctx, pmu, false) -+ for_each_epc(epc, task_ctx, pmu, 0) - perf_pmu_disable(epc->pmu); - - task_ctx_sched_out(task_ctx, pmu, event_type); -@@ -2928,11 +2937,11 @@ static void ctx_resched(struct perf_cpu_context *cpuctx, - - perf_event_sched_in(cpuctx, task_ctx, pmu); - -- for_each_epc(epc, &cpuctx->ctx, pmu, false) -+ for_each_epc(epc, &cpuctx->ctx, pmu, 0) - perf_pmu_enable(epc->pmu); - - if (task_ctx) { -- for_each_epc(epc, task_ctx, pmu, false) -+ for_each_epc(epc, task_ctx, pmu, 0) - perf_pmu_enable(epc->pmu); - } - } -@@ -3481,11 +3490,10 @@ static void - ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type) - { - struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ enum event_type_t active_type = event_type & ~EVENT_FLAGS; - struct perf_event_pmu_context *pmu_ctx; - int is_active = ctx->is_active; -- bool cgroup = event_type & EVENT_CGROUP; - -- event_type &= ~EVENT_CGROUP; - - lockdep_assert_held(&ctx->lock); - -@@ -3516,7 +3524,7 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - * see __load_acquire() in perf_event_time_now() - */ - barrier(); -- ctx->is_active &= ~event_type; -+ ctx->is_active &= ~active_type; - - if (!(ctx->is_active & EVENT_ALL)) { - /* -@@ -3537,7 +3545,7 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - - is_active ^= ctx->is_active; /* changed bits */ - -- for_each_epc(pmu_ctx, ctx, pmu, cgroup) -+ for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_out(pmu_ctx, is_active); - } - -@@ -3693,7 +3701,7 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next) - raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING); - if (context_equiv(ctx, next_ctx)) { - -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - - /* PMIs are disabled; ctx->nr_no_switch_fast is stable. */ - if (local_read(&ctx->nr_no_switch_fast) || -@@ -3717,7 +3725,7 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next) - - perf_ctx_sched_task_cb(ctx, task, false); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - - /* - * RCU_INIT_POINTER here is safe because we've not -@@ -3741,13 +3749,13 @@ perf_event_context_sched_out(struct task_struct *task, struct task_struct *next) - - if (do_switch) { - raw_spin_lock(&ctx->lock); -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - - inside_switch: - perf_ctx_sched_task_cb(ctx, task, false); - task_ctx_sched_out(ctx, NULL, EVENT_ALL); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - raw_spin_unlock(&ctx->lock); - } - } -@@ -4056,11 +4064,9 @@ static void - ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type) - { - struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ enum event_type_t active_type = event_type & ~EVENT_FLAGS; - struct perf_event_pmu_context *pmu_ctx; - int is_active = ctx->is_active; -- bool cgroup = event_type & EVENT_CGROUP; -- -- event_type &= ~EVENT_CGROUP; - - lockdep_assert_held(&ctx->lock); - -@@ -4078,7 +4084,7 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - barrier(); - } - -- ctx->is_active |= (event_type | EVENT_TIME); -+ ctx->is_active |= active_type | EVENT_TIME; - if (ctx->task) { - if (!(is_active & EVENT_ALL)) - cpuctx->task_ctx = ctx; -@@ -4093,13 +4099,13 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - * in order to give them the best chance of going on. - */ - if (is_active & EVENT_PINNED) { -- for_each_epc(pmu_ctx, ctx, pmu, cgroup) -+ for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_in(pmu_ctx, EVENT_PINNED); - } - - /* Then walk through the lower prio flexible groups */ - if (is_active & EVENT_FLEXIBLE) { -- for_each_epc(pmu_ctx, ctx, pmu, cgroup) -+ for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_in(pmu_ctx, EVENT_FLEXIBLE); - } - } -@@ -4116,11 +4122,11 @@ static void perf_event_context_sched_in(struct task_struct *task) - - if (cpuctx->task_ctx == ctx) { - perf_ctx_lock(cpuctx, ctx); -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - - perf_ctx_sched_task_cb(ctx, task, true); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - perf_ctx_unlock(cpuctx, ctx); - goto rcu_unlock; - } -@@ -4133,7 +4139,7 @@ static void perf_event_context_sched_in(struct task_struct *task) - if (!ctx->nr_events) - goto unlock; - -- perf_ctx_disable(ctx, false); -+ perf_ctx_disable(ctx, 0); - /* - * We want to keep the following priority order: - * cpu pinned (that don't need to move), task pinned, -@@ -4143,7 +4149,7 @@ static void perf_event_context_sched_in(struct task_struct *task) - * events, no need to flip the cpuctx's events around. - */ - if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree)) { -- perf_ctx_disable(&cpuctx->ctx, false); -+ perf_ctx_disable(&cpuctx->ctx, 0); - ctx_sched_out(&cpuctx->ctx, NULL, EVENT_FLEXIBLE); - } - -@@ -4152,9 +4158,9 @@ static void perf_event_context_sched_in(struct task_struct *task) - perf_ctx_sched_task_cb(cpuctx->task_ctx, task, true); - - if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree)) -- perf_ctx_enable(&cpuctx->ctx, false); -+ perf_ctx_enable(&cpuctx->ctx, 0); - -- perf_ctx_enable(ctx, false); -+ perf_ctx_enable(ctx, 0); - - unlock: - perf_ctx_unlock(cpuctx, ctx); --- -2.43.0 - diff --git a/SPECS/kernel/0057-perf-Add-generic-exclude_guest-support.perf b/SPECS/kernel/0057-perf-Add-generic-exclude_guest-support.perf deleted file mode 100644 index 3be1c66d3..000000000 --- a/SPECS/kernel/0057-perf-Add-generic-exclude_guest-support.perf +++ /dev/null @@ -1,64 +0,0 @@ -From f9f422924163d86939bac97108712ba5117fad24 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:45 +0000 -Subject: [PATCH 057/100] perf: Add generic exclude_guest support - -Only KVM knows the exact time when a guest is entering/exiting. Expose -two interfaces to KVM to switch the ownership of the PMU resources. - -All the pinned events must be scheduled in first. Extend the -perf_event_sched_in() helper to support extra flag, e.g., EVENT_GUEST. - -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - kernel/events/core.c | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 3054d6a866ac..d9c17f5f5195 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -2872,14 +2872,15 @@ static void task_ctx_sched_out(struct perf_event_context *ctx, - - static void perf_event_sched_in(struct perf_cpu_context *cpuctx, - struct perf_event_context *ctx, -- struct pmu *pmu) -+ struct pmu *pmu, -+ enum event_type_t event_type) - { -- ctx_sched_in(&cpuctx->ctx, pmu, EVENT_PINNED); -+ ctx_sched_in(&cpuctx->ctx, pmu, EVENT_PINNED | event_type); - if (ctx) -- ctx_sched_in(ctx, pmu, EVENT_PINNED); -- ctx_sched_in(&cpuctx->ctx, pmu, EVENT_FLEXIBLE); -+ ctx_sched_in(ctx, pmu, EVENT_PINNED | event_type); -+ ctx_sched_in(&cpuctx->ctx, pmu, EVENT_FLEXIBLE | event_type); - if (ctx) -- ctx_sched_in(ctx, pmu, EVENT_FLEXIBLE); -+ ctx_sched_in(ctx, pmu, EVENT_FLEXIBLE | event_type); - } - - /* -@@ -2935,7 +2936,7 @@ static void ctx_resched(struct perf_cpu_context *cpuctx, - else if (event_type & EVENT_PINNED) - ctx_sched_out(&cpuctx->ctx, pmu, EVENT_FLEXIBLE); - -- perf_event_sched_in(cpuctx, task_ctx, pmu); -+ perf_event_sched_in(cpuctx, task_ctx, pmu, 0); - - for_each_epc(epc, &cpuctx->ctx, pmu, 0) - perf_pmu_enable(epc->pmu); -@@ -4153,7 +4154,7 @@ static void perf_event_context_sched_in(struct task_struct *task) - ctx_sched_out(&cpuctx->ctx, NULL, EVENT_FLEXIBLE); - } - -- perf_event_sched_in(cpuctx, ctx, NULL); -+ perf_event_sched_in(cpuctx, ctx, NULL, 0); - - perf_ctx_sched_task_cb(cpuctx->task_ctx, task, true); - --- -2.43.0 - diff --git a/SPECS/kernel/0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf b/SPECS/kernel/0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf deleted file mode 100644 index ddb51ef4a..000000000 --- a/SPECS/kernel/0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf +++ /dev/null @@ -1,46 +0,0 @@ -From 7bcd131ab587cb29bd8eefd9dc46e310e9d986ea Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 10:01:17 -0700 -Subject: [PATCH 058/100] perf: Move security_perf_event_free() call to - __free_event() - -Move the freeing of any security state associated with a perf event from -_free_event() to __free_event(), i.e. invoke security_perf_event_free() in -the error paths for perf_event_alloc(). This will allow adding potential -error paths in perf_event_alloc() that can occur after allocating security -state. - -Note, kfree() and thus security_perf_event_free() is a nop if -event->security is NULL, i.e. calling security_perf_event_free() even if -security_perf_event_alloc() fails or is never reached is functionality ok. - -Signed-off-by: Sean Christopherson ---- - kernel/events/core.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index d9c17f5f5195..3af2ec8641ca 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -5602,6 +5602,8 @@ static void __free_event(struct perf_event *event) - { - struct pmu *pmu = event->pmu; - -+ security_perf_event_free(event); -+ - if (event->attach_state & PERF_ATTACH_CALLCHAIN) - put_callchain_buffers(); - -@@ -5665,8 +5667,6 @@ static void _free_event(struct perf_event *event) - - unaccount_event(event); - -- security_perf_event_free(event); -- - if (event->rb) { - /* - * Can happen when we close an event with re-directed output. --- -2.43.0 - diff --git a/SPECS/kernel/0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf b/SPECS/kernel/0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf deleted file mode 100644 index 1653e3b46..000000000 --- a/SPECS/kernel/0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf +++ /dev/null @@ -1,226 +0,0 @@ -From 09c2cfcf7086c69998f7e5b07fc8d6336ed53b9d Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:41 +0000 -Subject: [PATCH 059/100] perf: Add APIs to create/release mediated guest vPMUs - -Currently, exposing PMU capabilities to a KVM guest is done by emulating -guest PMCs via host perf events, i.e. by having KVM be "just" another user -of perf. As a result, the guest and host are effectively competing for -resources, and emulating guest accesses to vPMU resources requires -expensive actions (expensive relative to the native instruction). The -overhead and resource competition results in degraded guest performance -and ultimately very poor vPMU accuracy. - -To address the issues with the perf-emulated vPMU, introduce a "mediated -vPMU", where the data plane (PMCs and enable/disable knobs) is exposed -directly to the guest, but the control plane (event selectors and access -to fixed counters) is managed by KVM (via MSR interceptions). To allow -host perf usage of the PMU to (partially) co-exist with KVM/guest usage -of the PMU, KVM and perf will coordinate to a world switch between host -perf context and guest vPMU context near VM-Enter/VM-Exit. - -Add two exported APIs, perf_{create,release}_mediated_pmu(), to allow KVM -to create and release a mediated PMU instance (per VM). Because host perf -context will be deactivated while the guest is running, mediated PMU usage -will be mutually exclusive with perf analysis of the guest, i.e. perf -events that do NOT exclude the guest will not behave as expected. - -To avoid silent failure of !exclude_guest perf events, disallow creating a -mediated PMU if there are active !exclude_guest events, and on the perf -side, disallowing creating new !exclude_guest perf events while there is -at least one active mediated PMU. - -Exempt PMU resources that do not support mediated PMU usage, i.e. that are -outside the scope/view of KVM's vPMU and will not be swapped out while the -guest is running. - -Guard mediated PMU with a new kconfig to help readers identify code paths -that are unique to mediated PMU support, and to allow for adding arch- -specific hooks without stubs. KVM x86 is expected to be the only KVM -architecture to support a mediated PMU in the near future (e.g. arm64 is -trending toward a partitioned PMU implementation), and KVM x86 will select -PERF_GUEST_MEDIATED_PMU unconditionally, i.e. won't need stubs. - -Immediately select PERF_GUEST_MEDIATED_PMU when KVM x86 is enabled so that -all paths are compile tested. Full KVM support is on its way... - -Suggested-by: Sean Christopherson -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: add kconfig and WARNing, rewrite changelog, swizzle patch ordering] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/Kconfig | 1 + - include/linux/perf_event.h | 6 +++ - init/Kconfig | 4 ++ - kernel/events/core.c | 82 ++++++++++++++++++++++++++++++++++++++ - 4 files changed, 93 insertions(+) - -diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig -index 2c86673155c9..ee67357b5e36 100644 ---- a/arch/x86/kvm/Kconfig -+++ b/arch/x86/kvm/Kconfig -@@ -37,6 +37,7 @@ config KVM_X86 - select SCHED_INFO - select PERF_EVENTS - select GUEST_PERF_EVENTS -+ select PERF_GUEST_MEDIATED_PMU - select HAVE_KVM_MSI - select HAVE_KVM_CPU_RELAX_INTERCEPT - select HAVE_KVM_NO_POLL -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index dd9bfe42c011..a430a3ae4c52 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -306,6 +306,7 @@ struct perf_event_pmu_context; - #define PERF_PMU_CAP_AUX_PAUSE 0x0200 - #define PERF_PMU_CAP_AUX_PREFER_LARGE 0x0400 - #define PERF_PMU_CAP_MORE_EXT_REGS 0x0800 -+#define PERF_PMU_CAP_MEDIATED_VPMU 0x1000 - - /** - * pmu::scope -@@ -1918,6 +1919,11 @@ extern int perf_event_account_interrupt(struct perf_event *event); - extern int perf_event_period(struct perf_event *event, u64 value); - extern u64 perf_event_pause(struct perf_event *event, bool reset); - -+#ifdef CONFIG_PERF_GUEST_MEDIATED_PMU -+int perf_create_mediated_pmu(void); -+void perf_release_mediated_pmu(void); -+#endif -+ - #else /* !CONFIG_PERF_EVENTS: */ - - static inline void * -diff --git a/init/Kconfig b/init/Kconfig -index 836320251219..4d1c5eb98a80 100644 ---- a/init/Kconfig -+++ b/init/Kconfig -@@ -1980,6 +1980,10 @@ config GUEST_PERF_EVENTS - bool - depends on HAVE_PERF_EVENTS - -+config PERF_GUEST_MEDIATED_PMU -+ bool -+ depends on GUEST_PERF_EVENTS -+ - config PERF_USE_VMALLOC - bool - help -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 3af2ec8641ca..d9beaad6837c 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -5657,6 +5657,8 @@ static void __free_event(struct perf_event *event) - call_rcu(&event->rcu_head, free_event_rcu); - } - -+static void mediated_pmu_unaccount_event(struct perf_event *event); -+ - DEFINE_FREE(__free_event, struct perf_event *, if (_T) __free_event(_T)) - - /* vs perf_event_alloc() success */ -@@ -5666,6 +5668,7 @@ static void _free_event(struct perf_event *event) - irq_work_sync(&event->pending_disable_irq); - - unaccount_event(event); -+ mediated_pmu_unaccount_event(event); - - if (event->rb) { - /* -@@ -6188,6 +6191,81 @@ u64 perf_event_pause(struct perf_event *event, bool reset) - } - EXPORT_SYMBOL_GPL(perf_event_pause); - -+#ifdef CONFIG_PERF_GUEST_MEDIATED_PMU -+static atomic_t nr_include_guest_events __read_mostly; -+ -+static atomic_t nr_mediated_pmu_vms __read_mostly; -+static DEFINE_MUTEX(perf_mediated_pmu_mutex); -+ -+/* !exclude_guest event of PMU with PERF_PMU_CAP_MEDIATED_VPMU */ -+static inline bool is_include_guest_event(struct perf_event *event) -+{ -+ if ((event->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU) && -+ !event->attr.exclude_guest) -+ return true; -+ -+ return false; -+} -+ -+static int mediated_pmu_account_event(struct perf_event *event) -+{ -+ if (!is_include_guest_event(event)) -+ return 0; -+ -+ guard(mutex)(&perf_mediated_pmu_mutex); -+ -+ if (atomic_read(&nr_mediated_pmu_vms)) -+ return -EOPNOTSUPP; -+ -+ atomic_inc(&nr_include_guest_events); -+ return 0; -+} -+ -+static void mediated_pmu_unaccount_event(struct perf_event *event) -+{ -+ if (!is_include_guest_event(event)) -+ return; -+ -+ atomic_dec(&nr_include_guest_events); -+} -+ -+/* -+ * Currently invoked at VM creation to -+ * - Check whether there are existing !exclude_guest events of PMU with -+ * PERF_PMU_CAP_MEDIATED_VPMU -+ * - Set nr_mediated_pmu_vms to prevent !exclude_guest event creation on -+ * PMUs with PERF_PMU_CAP_MEDIATED_VPMU -+ * -+ * No impact for the PMU without PERF_PMU_CAP_MEDIATED_VPMU. The perf -+ * still owns all the PMU resources. -+ */ -+int perf_create_mediated_pmu(void) -+{ -+ guard(mutex)(&perf_mediated_pmu_mutex); -+ if (atomic_inc_not_zero(&nr_mediated_pmu_vms)) -+ return 0; -+ -+ if (atomic_read(&nr_include_guest_events)) -+ return -EBUSY; -+ -+ atomic_inc(&nr_mediated_pmu_vms); -+ return 0; -+} -+EXPORT_SYMBOL_GPL(perf_create_mediated_pmu); -+ -+void perf_release_mediated_pmu(void) -+{ -+ if (WARN_ON_ONCE(!atomic_read(&nr_mediated_pmu_vms))) -+ return; -+ -+ atomic_dec(&nr_mediated_pmu_vms); -+} -+EXPORT_SYMBOL_GPL(perf_release_mediated_pmu); -+#else -+static int mediated_pmu_account_event(struct perf_event *event) { return 0; } -+static void mediated_pmu_unaccount_event(struct perf_event *event) {} -+#endif -+ - /* - * Holding the top-level event's child_mutex means that any - * descendant process that has inherited this event will block -@@ -13115,6 +13193,10 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, - if (err) - return ERR_PTR(err); - -+ err = mediated_pmu_account_event(event); -+ if (err) -+ return ERR_PTR(err); -+ - /* symmetric to unaccount_event() in _free_event() */ - account_event(event); - --- -2.43.0 - diff --git a/SPECS/kernel/0060-perf-Clean-up-perf-ctx-time.perf b/SPECS/kernel/0060-perf-Clean-up-perf-ctx-time.perf deleted file mode 100644 index ed18cb2cf..000000000 --- a/SPECS/kernel/0060-perf-Clean-up-perf-ctx-time.perf +++ /dev/null @@ -1,223 +0,0 @@ -From 7912f8392baadefe96f23d4bbc26022313774e9c Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:43 +0000 -Subject: [PATCH 060/100] perf: Clean up perf ctx time - -The current perf tracks two timestamps for the normal ctx and cgroup. -The same type of variables and similar codes are used to track the -timestamps. In the following patch, the third timestamp to track the -guest time will be introduced. -To avoid the code duplication, add a new struct perf_time_ctx and factor -out a generic function update_perf_time_ctx(). - -No functional change. - -Suggested-by: Peter Zijlstra (Intel) -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - include/linux/perf_event.h | 13 +++---- - kernel/events/core.c | 70 +++++++++++++++++--------------------- - 2 files changed, 39 insertions(+), 44 deletions(-) - -Index: kernel-staging/include/linux/perf_event.h -=================================================================== ---- kernel-staging.orig/include/linux/perf_event.h -+++ kernel-staging/include/linux/perf_event.h -@@ -1000,6 +1000,11 @@ struct perf_event_groups { - u64 index; - }; - -+struct perf_time_ctx { -+ u64 time; -+ u64 stamp; -+ u64 offset; -+}; - - /** - * struct perf_event_context - event context structure -@@ -1038,9 +1043,7 @@ struct perf_event_context { - /* - * Context clock, runs when context enabled. - */ -- u64 time; -- u64 timestamp; -- u64 timeoffset; -+ struct perf_time_ctx time; - - /* - * These fields let us detect when two contexts have both -@@ -1173,9 +1176,7 @@ struct bpf_perf_event_data_kern { - * This is a per-cpu dynamically allocated data structure. - */ - struct perf_cgroup_info { -- u64 time; -- u64 timestamp; -- u64 timeoffset; -+ struct perf_time_ctx time; - int active; - }; - -Index: kernel-staging/kernel/events/core.c -=================================================================== ---- kernel-staging.orig/kernel/events/core.c -+++ kernel-staging/kernel/events/core.c -@@ -815,6 +815,24 @@ static void perf_ctx_enable(struct perf_ - static void ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type); - static void ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t event_type); - -+static inline void update_perf_time_ctx(struct perf_time_ctx *time, u64 now, bool adv) -+{ -+ if (adv) -+ time->time += now - time->stamp; -+ time->stamp = now; -+ -+ /* -+ * The above: time' = time + (now - timestamp), can be re-arranged -+ * into: time` = now + (time - timestamp), which gives a single value -+ * offset to compute future time without locks on. -+ * -+ * See perf_event_time_now(), which can be used from NMI context where -+ * it's (obviously) not possible to acquire ctx->lock in order to read -+ * both the above values in a consistent manner. -+ */ -+ WRITE_ONCE(time->offset, time->time - time->stamp); -+} -+ - #ifdef CONFIG_CGROUP_PERF - - static inline bool -@@ -856,7 +874,7 @@ static inline u64 perf_cgroup_event_time - struct perf_cgroup_info *t; - - t = per_cpu_ptr(event->cgrp->info, event->cpu); -- return t->time; -+ return t->time.time; - } - - static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now) -@@ -865,22 +883,11 @@ static inline u64 perf_cgroup_event_time - - t = per_cpu_ptr(event->cgrp->info, event->cpu); - if (!__load_acquire(&t->active)) -- return t->time; -- now += READ_ONCE(t->timeoffset); -+ return t->time.time; -+ now += READ_ONCE(t->time.offset); - return now; - } - --static inline void __update_cgrp_time(struct perf_cgroup_info *info, u64 now, bool adv) --{ -- if (adv) -- info->time += now - info->timestamp; -- info->timestamp = now; -- /* -- * see update_context_time() -- */ -- WRITE_ONCE(info->timeoffset, info->time - info->timestamp); --} -- - static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, bool final) - { - struct perf_cgroup *cgrp = cpuctx->cgrp; -@@ -894,7 +901,7 @@ static inline void update_cgrp_time_from - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); - -- __update_cgrp_time(info, now, true); -+ update_perf_time_ctx(&info->time, now, true); - if (final) - __store_release(&info->active, 0); - } -@@ -917,7 +924,7 @@ static inline void update_cgrp_time_from - * Do not update time when cgroup is not active - */ - if (info->active) -- __update_cgrp_time(info, perf_clock(), true); -+ update_perf_time_ctx(&info->time, perf_clock(), true); - } - - static inline void -@@ -941,7 +948,7 @@ perf_cgroup_set_timestamp(struct perf_cp - for (css = &cgrp->css; css; css = css->parent) { - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); -- __update_cgrp_time(info, ctx->timestamp, false); -+ update_perf_time_ctx(&info->time, ctx->time.stamp, false); - __store_release(&info->active, 1); - } - } -@@ -1562,20 +1569,7 @@ static void __update_context_time(struct - - lockdep_assert_held(&ctx->lock); - -- if (adv) -- ctx->time += now - ctx->timestamp; -- ctx->timestamp = now; -- -- /* -- * The above: time' = time + (now - timestamp), can be re-arranged -- * into: time` = now + (time - timestamp), which gives a single value -- * offset to compute future time without locks on. -- * -- * See perf_event_time_now(), which can be used from NMI context where -- * it's (obviously) not possible to acquire ctx->lock in order to read -- * both the above values in a consistent manner. -- */ -- WRITE_ONCE(ctx->timeoffset, ctx->time - ctx->timestamp); -+ update_perf_time_ctx(&ctx->time, now, adv); - } - - static void update_context_time(struct perf_event_context *ctx) -@@ -1593,7 +1587,7 @@ static u64 perf_event_time(struct perf_e - if (is_cgroup_event(event)) - return perf_cgroup_event_time(event); - -- return ctx->time; -+ return ctx->time.time; - } - - static u64 perf_event_time_now(struct perf_event *event, u64 now) -@@ -1607,9 +1601,9 @@ static u64 perf_event_time_now(struct pe - return perf_cgroup_event_time_now(event, now); - - if (!(__load_acquire(&ctx->is_active) & EVENT_TIME)) -- return ctx->time; -+ return ctx->time.time; - -- now += READ_ONCE(ctx->timeoffset); -+ now += READ_ONCE(ctx->time.offset); - return now; - } - -@@ -12087,7 +12081,7 @@ static void task_clock_event_update(stru - static void task_clock_event_start(struct perf_event *event, int flags) - { - event->hw.state = 0; -- local64_set(&event->hw.prev_count, event->ctx->time); -+ local64_set(&event->hw.prev_count, event->ctx->time.time); - perf_swevent_start_hrtimer(event); - } - -@@ -12096,7 +12090,7 @@ static void task_clock_event_stop(struct - event->hw.state = PERF_HES_STOPPED; - perf_swevent_cancel_hrtimer(event); - if (flags & PERF_EF_UPDATE) -- task_clock_event_update(event, event->ctx->time); -+ task_clock_event_update(event, event->ctx->time.time); - } - - static int task_clock_event_add(struct perf_event *event, int flags) -@@ -12116,8 +12110,8 @@ static void task_clock_event_del(struct - static void task_clock_event_read(struct perf_event *event) - { - u64 now = perf_clock(); -- u64 delta = now - event->ctx->timestamp; -- u64 time = event->ctx->time + delta; -+ u64 delta = now - event->ctx->time.stamp; -+ u64 time = event->ctx->time.time + delta; - - task_clock_event_update(event, time); - } diff --git a/SPECS/kernel/0061-perf-Add-a-EVENT_GUEST-flag.perf b/SPECS/kernel/0061-perf-Add-a-EVENT_GUEST-flag.perf deleted file mode 100644 index a6e65e37c..000000000 --- a/SPECS/kernel/0061-perf-Add-a-EVENT_GUEST-flag.perf +++ /dev/null @@ -1,531 +0,0 @@ -From cdb7cd281af6aaa5242da9b7ca41af74cc76972b Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:30:44 +0000 -Subject: [PATCH 061/100] perf: Add a EVENT_GUEST flag - -Current perf doesn't explicitly schedule out all exclude_guest events -while the guest is running. There is no problem with the current -emulated vPMU. Because perf owns all the PMU counters. It can mask the -counter which is assigned to an exclude_guest event when a guest is -running (Intel way), or set the corresponding HOSTONLY bit in evsentsel -(AMD way). The counter doesn't count when a guest is running. - -However, either way doesn't work with the introduced mediated vPMU. -A guest owns all the PMU counters when it's running. The host should not -mask any counters. The counter may be used by the guest. The evsentsel -may be overwritten. - -Perf should explicitly schedule out all exclude_guest events to release -the PMU resources when entering a guest, and resume the counting when -exiting the guest. - -It's possible that an exclude_guest event is created when a guest is -running. The new event should not be scheduled in as well. - -The ctx time is shared among different PMUs. The time cannot be stopped -when a guest is running. It is required to calculate the time for events -from other PMUs, e.g., uncore events. Add timeguest to track the guest -run time. For an exclude_guest event, the elapsed time equals -the ctx time - guest time. -Cgroup has dedicated times. Use the same method to deduct the guest time -from the cgroup time as well. - -Co-developed-by: Peter Zijlstra (Intel) -Signed-off-by: Peter Zijlstra (Intel) -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: massage comments] -Signed-off-by: Sean Christopherson ---- - include/linux/perf_event.h | 6 + - kernel/events/core.c | 230 +++++++++++++++++++++++++++++-------- - 2 files changed, 185 insertions(+), 51 deletions(-) - -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index 2b03fd042149..dfe33bc72180 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1045,6 +1045,11 @@ struct perf_event_context { - */ - struct perf_time_ctx time; - -+ /* -+ * Context clock, runs when in the guest mode. -+ */ -+ struct perf_time_ctx timeguest; -+ - /* - * These fields let us detect when two contexts have both - * been cloned (inherited) from a common ancestor. -@@ -1177,6 +1182,7 @@ struct bpf_perf_event_data_kern { - */ - struct perf_cgroup_info { - struct perf_time_ctx time; -+ struct perf_time_ctx timeguest; - int active; - }; - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index d31b186dc012..0b36899f5939 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -164,7 +164,19 @@ enum event_type_t { - /* see ctx_resched() for details */ - EVENT_CPU = 0x10, - EVENT_CGROUP = 0x20, -- EVENT_FLAGS = EVENT_CGROUP, -+ -+ /* -+ * EVENT_GUEST is set when scheduling in/out events between the host -+ * and a guest with a mediated vPMU. Among other things, EVENT_GUEST -+ * is used: -+ * -+ * - In for_each_epc() to skip PMUs that don't support events in a -+ * MEDIATED_VPMU guest, i.e. don't need to be context switched. -+ * - To indicate the start/end point of the events in a guest. Guest -+ * running time is deducted for host-only (exclude_guest) events. -+ */ -+ EVENT_GUEST = 0x40, -+ EVENT_FLAGS = EVENT_CGROUP | EVENT_GUEST, - /* compound helpers */ - EVENT_ALL = EVENT_FLEXIBLE | EVENT_PINNED, - EVENT_TIME_FROZEN = EVENT_TIME | EVENT_FROZEN, -@@ -457,6 +469,11 @@ static cpumask_var_t perf_online_pkg_mask; - static cpumask_var_t perf_online_sys_mask; - static struct kmem_cache *perf_event_cache; - -+static __always_inline bool is_guest_mediated_pmu_loaded(void) -+{ -+ return false; -+} -+ - /* - * perf event paranoia level: - * -1 - not paranoid at all -@@ -783,6 +800,9 @@ static bool perf_skip_pmu_ctx(struct perf_event_pmu_context *pmu_ctx, - { - if ((event_type & EVENT_CGROUP) && !pmu_ctx->nr_cgroups) - return true; -+ if ((event_type & EVENT_GUEST) && -+ !(pmu_ctx->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU)) -+ return true; - return false; - } - -@@ -833,6 +853,39 @@ static inline void update_perf_time_ctx(struct perf_time_ctx *time, u64 now, boo - WRITE_ONCE(time->offset, time->time - time->stamp); - } - -+static_assert(offsetof(struct perf_event_context, timeguest) - -+ offsetof(struct perf_event_context, time) == -+ sizeof(struct perf_time_ctx)); -+ -+#define T_TOTAL 0 -+#define T_GUEST 1 -+ -+static inline u64 __perf_event_time_ctx(struct perf_event *event, -+ struct perf_time_ctx *times) -+{ -+ u64 time = times[T_TOTAL].time; -+ -+ if (event->attr.exclude_guest) -+ time -= times[T_GUEST].time; -+ -+ return time; -+} -+ -+static inline u64 __perf_event_time_ctx_now(struct perf_event *event, -+ struct perf_time_ctx *times, -+ u64 now) -+{ -+ if (is_guest_mediated_pmu_loaded() && event->attr.exclude_guest) { -+ /* -+ * (now + times[total].offset) - (now + times[guest].offset) := -+ * times[total].offset - times[guest].offset -+ */ -+ return READ_ONCE(times[T_TOTAL].offset) - READ_ONCE(times[T_GUEST].offset); -+ } -+ -+ return now + READ_ONCE(times[T_TOTAL].offset); -+} -+ - #ifdef CONFIG_CGROUP_PERF - - static inline bool -@@ -869,12 +922,16 @@ static inline int is_cgroup_event(struct perf_event *event) - return event->cgrp != NULL; - } - -+static_assert(offsetof(struct perf_cgroup_info, timeguest) - -+ offsetof(struct perf_cgroup_info, time) == -+ sizeof(struct perf_time_ctx)); -+ - static inline u64 perf_cgroup_event_time(struct perf_event *event) - { - struct perf_cgroup_info *t; - - t = per_cpu_ptr(event->cgrp->info, event->cpu); -- return t->time.time; -+ return __perf_event_time_ctx(event, &t->time); - } - - static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now) -@@ -883,9 +940,21 @@ static inline u64 perf_cgroup_event_time_now(struct perf_event *event, u64 now) - - t = per_cpu_ptr(event->cgrp->info, event->cpu); - if (!__load_acquire(&t->active)) -- return t->time.time; -- now += READ_ONCE(t->time.offset); -- return now; -+ return __perf_event_time_ctx(event, &t->time); -+ -+ return __perf_event_time_ctx_now(event, &t->time, now); -+} -+ -+static inline void __update_cgrp_guest_time(struct perf_cgroup_info *info, u64 now, bool adv) -+{ -+ update_perf_time_ctx(&info->timeguest, now, adv); -+} -+ -+static inline void update_cgrp_time(struct perf_cgroup_info *info, u64 now) -+{ -+ update_perf_time_ctx(&info->time, now, true); -+ if (is_guest_mediated_pmu_loaded()) -+ __update_cgrp_guest_time(info, now, true); - } - - static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, bool final) -@@ -901,7 +970,7 @@ static inline void update_cgrp_time_from_cpuctx(struct perf_cpu_context *cpuctx, - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); - -- update_perf_time_ctx(&info->time, now, true); -+ update_cgrp_time(info, now); - if (final) - __store_release(&info->active, 0); - } -@@ -924,11 +993,11 @@ static inline void update_cgrp_time_from_event(struct perf_event *event) - * Do not update time when cgroup is not active - */ - if (info->active) -- update_perf_time_ctx(&info->time, perf_clock(), true); -+ update_cgrp_time(info, perf_clock()); - } - - static inline void --perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx) -+perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx, bool guest) - { - struct perf_event_context *ctx = &cpuctx->ctx; - struct perf_cgroup *cgrp = cpuctx->cgrp; -@@ -948,8 +1017,12 @@ perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx) - for (css = &cgrp->css; css; css = css->parent) { - cgrp = container_of(css, struct perf_cgroup, css); - info = this_cpu_ptr(cgrp->info); -- update_perf_time_ctx(&info->time, ctx->time.stamp, false); -- __store_release(&info->active, 1); -+ if (guest) { -+ __update_cgrp_guest_time(info, ctx->time.stamp, false); -+ } else { -+ update_perf_time_ctx(&info->time, ctx->time.stamp, false); -+ __store_release(&info->active, 1); -+ } - } - } - -@@ -1153,7 +1226,7 @@ static inline int perf_cgroup_connect(pid_t pid, struct perf_event *event, - } - - static inline void --perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx) -+perf_cgroup_set_timestamp(struct perf_cpu_context *cpuctx, bool guest) - { - } - -@@ -1565,16 +1638,24 @@ static void perf_unpin_context(struct perf_event_context *ctx) - */ - static void __update_context_time(struct perf_event_context *ctx, bool adv) - { -- u64 now = perf_clock(); -+ lockdep_assert_held(&ctx->lock); -+ -+ update_perf_time_ctx(&ctx->time, perf_clock(), adv); -+} - -+static void __update_context_guest_time(struct perf_event_context *ctx, bool adv) -+{ - lockdep_assert_held(&ctx->lock); - -- update_perf_time_ctx(&ctx->time, now, adv); -+ /* must be called after __update_context_time(); */ -+ update_perf_time_ctx(&ctx->timeguest, ctx->time.stamp, adv); - } - - static void update_context_time(struct perf_event_context *ctx) - { - __update_context_time(ctx, true); -+ if (is_guest_mediated_pmu_loaded()) -+ __update_context_guest_time(ctx, true); - } - - static u64 perf_event_time(struct perf_event *event) -@@ -1587,7 +1668,7 @@ static u64 perf_event_time(struct perf_event *event) - if (is_cgroup_event(event)) - return perf_cgroup_event_time(event); - -- return ctx->time.time; -+ return __perf_event_time_ctx(event, &ctx->time); - } - - static u64 perf_event_time_now(struct perf_event *event, u64 now) -@@ -1601,10 +1682,9 @@ static u64 perf_event_time_now(struct perf_event *event, u64 now) - return perf_cgroup_event_time_now(event, now); - - if (!(__load_acquire(&ctx->is_active) & EVENT_TIME)) -- return ctx->time.time; -+ return __perf_event_time_ctx(event, &ctx->time); - -- now += READ_ONCE(ctx->time.offset); -- return now; -+ return __perf_event_time_ctx_now(event, &ctx->time, now); - } - - static enum event_type_t get_event_type(struct perf_event *event) -@@ -2427,20 +2507,23 @@ group_sched_out(struct perf_event *group_event, struct perf_event_context *ctx) - } - - static inline void --__ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx, bool final) -+__ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx, -+ bool final, enum event_type_t event_type) - { - if (ctx->is_active & EVENT_TIME) { - if (ctx->is_active & EVENT_FROZEN) - return; -+ - update_context_time(ctx); -- update_cgrp_time_from_cpuctx(cpuctx, final); -+ /* vPMU should not stop time */ -+ update_cgrp_time_from_cpuctx(cpuctx, !(event_type & EVENT_GUEST) && final); - } - } - - static inline void - ctx_time_update(struct perf_cpu_context *cpuctx, struct perf_event_context *ctx) - { -- __ctx_time_update(cpuctx, ctx, false); -+ __ctx_time_update(cpuctx, ctx, false, 0); - } - - /* -@@ -3512,7 +3595,7 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - * - * would only update time for the pinned events. - */ -- __ctx_time_update(cpuctx, ctx, ctx == &cpuctx->ctx); -+ __ctx_time_update(cpuctx, ctx, ctx == &cpuctx->ctx, event_type); - - /* - * CPU-release for the below ->is_active store, -@@ -3538,7 +3621,18 @@ ctx_sched_out(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - cpuctx->task_ctx = NULL; - } - -- is_active ^= ctx->is_active; /* changed bits */ -+ if (event_type & EVENT_GUEST) { -+ /* -+ * Schedule out all exclude_guest events of PMU -+ * with PERF_PMU_CAP_MEDIATED_VPMU. -+ */ -+ is_active = EVENT_ALL; -+ __update_context_guest_time(ctx, false); -+ perf_cgroup_set_timestamp(cpuctx, true); -+ barrier(); -+ } else { -+ is_active ^= ctx->is_active; /* changed bits */ -+ } - - for_each_epc(pmu_ctx, ctx, pmu, event_type) - __pmu_ctx_sched_out(pmu_ctx, is_active); -@@ -3997,10 +4091,15 @@ static inline void group_update_userpage(struct perf_event *group_event) - event_update_userpage(event); - } - -+struct merge_sched_data { -+ int can_add_hw; -+ enum event_type_t event_type; -+}; -+ - static int merge_sched_in(struct perf_event *event, void *data) - { - struct perf_event_context *ctx = event->ctx; -- int *can_add_hw = data; -+ struct merge_sched_data *msd = data; - - if (event->state <= PERF_EVENT_STATE_OFF) - return 0; -@@ -4008,13 +4107,22 @@ static int merge_sched_in(struct perf_event *event, void *data) - if (!event_filter_match(event)) - return 0; - -- if (group_can_go_on(event, *can_add_hw)) { -+ /* -+ * Don't schedule in any host events from PMU with -+ * PERF_PMU_CAP_MEDIATED_VPMU, while a guest is running. -+ */ -+ if (is_guest_mediated_pmu_loaded() && -+ event->pmu_ctx->pmu->capabilities & PERF_PMU_CAP_MEDIATED_VPMU && -+ !(msd->event_type & EVENT_GUEST)) -+ return 0; -+ -+ if (group_can_go_on(event, msd->can_add_hw)) { - if (!group_sched_in(event, ctx)) - list_add_tail(&event->active_list, get_event_list(event)); - } - - if (event->state == PERF_EVENT_STATE_INACTIVE) { -- *can_add_hw = 0; -+ msd->can_add_hw = 0; - if (event->attr.pinned) { - perf_cgroup_event_disable(event, ctx); - perf_event_set_state(event, PERF_EVENT_STATE_ERROR); -@@ -4037,11 +4145,15 @@ static int merge_sched_in(struct perf_event *event, void *data) - - static void pmu_groups_sched_in(struct perf_event_context *ctx, - struct perf_event_groups *groups, -- struct pmu *pmu) -+ struct pmu *pmu, -+ enum event_type_t event_type) - { -- int can_add_hw = 1; -+ struct merge_sched_data msd = { -+ .can_add_hw = 1, -+ .event_type = event_type, -+ }; - visit_groups_merge(ctx, groups, smp_processor_id(), pmu, -- merge_sched_in, &can_add_hw); -+ merge_sched_in, &msd); - } - - static void __pmu_ctx_sched_in(struct perf_event_pmu_context *pmu_ctx, -@@ -4050,9 +4162,9 @@ static void __pmu_ctx_sched_in(struct perf_event_pmu_context *pmu_ctx, - struct perf_event_context *ctx = pmu_ctx->ctx; - - if (event_type & EVENT_PINNED) -- pmu_groups_sched_in(ctx, &ctx->pinned_groups, pmu_ctx->pmu); -+ pmu_groups_sched_in(ctx, &ctx->pinned_groups, pmu_ctx->pmu, event_type); - if (event_type & EVENT_FLEXIBLE) -- pmu_groups_sched_in(ctx, &ctx->flexible_groups, pmu_ctx->pmu); -+ pmu_groups_sched_in(ctx, &ctx->flexible_groups, pmu_ctx->pmu, event_type); - } - - static void -@@ -4069,9 +4181,11 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - return; - - if (!(is_active & EVENT_TIME)) { -+ /* EVENT_TIME should be active while the guest runs */ -+ WARN_ON_ONCE(event_type & EVENT_GUEST); - /* start ctx time */ - __update_context_time(ctx, false); -- perf_cgroup_set_timestamp(cpuctx); -+ perf_cgroup_set_timestamp(cpuctx, false); - /* - * CPU-release for the below ->is_active store, - * see __load_acquire() in perf_event_time_now() -@@ -4087,7 +4201,23 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - WARN_ON_ONCE(cpuctx->task_ctx != ctx); - } - -- is_active ^= ctx->is_active; /* changed bits */ -+ if (event_type & EVENT_GUEST) { -+ /* -+ * Schedule in the required exclude_guest events of PMU -+ * with PERF_PMU_CAP_MEDIATED_VPMU. -+ */ -+ is_active = event_type & EVENT_ALL; -+ -+ /* -+ * Update ctx time to set the new start time for -+ * the exclude_guest events. -+ */ -+ update_context_time(ctx); -+ update_cgrp_time_from_cpuctx(cpuctx, false); -+ barrier(); -+ } else { -+ is_active ^= ctx->is_active; /* changed bits */ -+ } - - /* - * First go through the list and put on any pinned groups -@@ -4095,13 +4225,13 @@ ctx_sched_in(struct perf_event_context *ctx, struct pmu *pmu, enum event_type_t - */ - if (is_active & EVENT_PINNED) { - for_each_epc(pmu_ctx, ctx, pmu, event_type) -- __pmu_ctx_sched_in(pmu_ctx, EVENT_PINNED); -+ __pmu_ctx_sched_in(pmu_ctx, EVENT_PINNED | (event_type & EVENT_GUEST)); - } - - /* Then walk through the lower prio flexible groups */ - if (is_active & EVENT_FLEXIBLE) { - for_each_epc(pmu_ctx, ctx, pmu, event_type) -- __pmu_ctx_sched_in(pmu_ctx, EVENT_FLEXIBLE); -+ __pmu_ctx_sched_in(pmu_ctx, EVENT_FLEXIBLE | (event_type & EVENT_GUEST)); - } - } - -@@ -6628,22 +6758,22 @@ void perf_event_update_userpage(struct perf_event *event) - goto unlock; - - /* -- * compute total_time_enabled, total_time_running -- * based on snapshot values taken when the event -- * was last scheduled in. -+ * Disable preemption to guarantee consistent time stamps are stored to -+ * the user page. -+ */ -+ preempt_disable(); -+ -+ /* -+ * Compute total_time_enabled, total_time_running based on snapshot -+ * values taken when the event was last scheduled in. - * -- * we cannot simply called update_context_time() -- * because of locking issue as we can be called in -- * NMI context -+ * We cannot simply call update_context_time() because doing so would -+ * lead to deadlock when called from NMI context. - */ - calc_timer_values(event, &now, &enabled, &running); - - userpg = rb->user_page; -- /* -- * Disable preemption to guarantee consistent time stamps are stored to -- * the user page. -- */ -- preempt_disable(); -+ - ++userpg->lock; - barrier(); - userpg->index = perf_event_index(event); -@@ -7943,13 +8073,11 @@ static void perf_output_read(struct perf_output_handle *handle, - u64 read_format = event->attr.read_format; - - /* -- * compute total_time_enabled, total_time_running -- * based on snapshot values taken when the event -- * was last scheduled in. -+ * Compute total_time_enabled, total_time_running based on snapshot -+ * values taken when the event was last scheduled in. - * -- * we cannot simply called update_context_time() -- * because of locking issue as we are called in -- * NMI context -+ * We cannot simply call update_context_time() because doing so would -+ * lead to deadlock when called from NMI context. - */ - if (read_format & PERF_FORMAT_TOTAL_TIMES) - calc_timer_values(event, &now, &enabled, &running); --- -2.43.0 - diff --git a/SPECS/kernel/0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf b/SPECS/kernel/0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf deleted file mode 100644 index d2f0b8002..000000000 --- a/SPECS/kernel/0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf +++ /dev/null @@ -1,125 +0,0 @@ -From 4cdfd036c183754b28cc9a0e9bdae165ebbfefcb Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Tue, 13 May 2025 10:23:53 -0700 -Subject: [PATCH 062/100] perf: Add APIs to load/put guest mediated PMU context - -Add exported APIs to load/put a guest mediated PMU context. KVM will -load the guest PMU shortly before VM-Enter, and put the guest PMU shortly -after VM-Exit. - -On the perf side of things, schedule out all exclude_guest events when the -guest context is loaded, and schedule them back in when the guest context -is put. I.e. yield the hardware PMU resources to the guest, by way of KVM. - -Note, perf is only responsible for managing host context. KVM is -responsible for loading/storing guest state to/from hardware. - -Suggested-by: Sean Christopherson -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: shuffle patches around, write changelog] -Signed-off-by: Sean Christopherson ---- - include/linux/perf_event.h | 2 ++ - kernel/events/core.c | 61 ++++++++++++++++++++++++++++++++++++++ - 2 files changed, 63 insertions(+) - -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index dfe33bc72180..57341843be12 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1929,6 +1929,8 @@ extern u64 perf_event_pause(struct perf_event *event, bool reset); - #ifdef CONFIG_PERF_GUEST_MEDIATED_PMU - int perf_create_mediated_pmu(void); - void perf_release_mediated_pmu(void); -+void perf_load_guest_context(unsigned long data); -+void perf_put_guest_context(void); - #endif - - #else /* !CONFIG_PERF_EVENTS: */ -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 0b36899f5939..45a8b3f0a6f3 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -469,10 +469,19 @@ static cpumask_var_t perf_online_pkg_mask; - static cpumask_var_t perf_online_sys_mask; - static struct kmem_cache *perf_event_cache; - -+#ifdef CONFIG_PERF_GUEST_MEDIATED_PMU -+static DEFINE_PER_CPU(bool, guest_ctx_loaded); -+ -+static __always_inline bool is_guest_mediated_pmu_loaded(void) -+{ -+ return __this_cpu_read(guest_ctx_loaded); -+} -+#else - static __always_inline bool is_guest_mediated_pmu_loaded(void) - { - return false; - } -+#endif - - /* - * perf event paranoia level: -@@ -6385,6 +6394,58 @@ void perf_release_mediated_pmu(void) - atomic_dec(&nr_mediated_pmu_vms); - } - EXPORT_SYMBOL_GPL(perf_release_mediated_pmu); -+ -+/* When loading a guest's mediated PMU, schedule out all exclude_guest events. */ -+void perf_load_guest_context(unsigned long data) -+{ -+ struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ -+ lockdep_assert_irqs_disabled(); -+ -+ guard(perf_ctx_lock)(cpuctx, cpuctx->task_ctx); -+ -+ if (WARN_ON_ONCE(__this_cpu_read(guest_ctx_loaded))) -+ return; -+ -+ perf_ctx_disable(&cpuctx->ctx, EVENT_GUEST); -+ ctx_sched_out(&cpuctx->ctx, NULL, EVENT_GUEST); -+ if (cpuctx->task_ctx) { -+ perf_ctx_disable(cpuctx->task_ctx, EVENT_GUEST); -+ task_ctx_sched_out(cpuctx->task_ctx, NULL, EVENT_GUEST); -+ } -+ -+ perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); -+ if (cpuctx->task_ctx) -+ perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); -+ -+ __this_cpu_write(guest_ctx_loaded, true); -+} -+EXPORT_SYMBOL_GPL(perf_load_guest_context); -+ -+void perf_put_guest_context(void) -+{ -+ struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context); -+ -+ lockdep_assert_irqs_disabled(); -+ -+ guard(perf_ctx_lock)(cpuctx, cpuctx->task_ctx); -+ -+ if (WARN_ON_ONCE(!__this_cpu_read(guest_ctx_loaded))) -+ return; -+ -+ perf_ctx_disable(&cpuctx->ctx, EVENT_GUEST); -+ if (cpuctx->task_ctx) -+ perf_ctx_disable(cpuctx->task_ctx, EVENT_GUEST); -+ -+ perf_event_sched_in(cpuctx, cpuctx->task_ctx, NULL, EVENT_GUEST); -+ -+ if (cpuctx->task_ctx) -+ perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); -+ perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); -+ -+ __this_cpu_write(guest_ctx_loaded, false); -+} -+EXPORT_SYMBOL_GPL(perf_put_guest_context); - #else - static int mediated_pmu_account_event(struct perf_event *event) { return 0; } - static void mediated_pmu_unaccount_event(struct perf_event *event) {} --- -2.43.0 - diff --git a/SPECS/kernel/0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf b/SPECS/kernel/0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf deleted file mode 100644 index 3d5b3e65a..000000000 --- a/SPECS/kernel/0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf +++ /dev/null @@ -1,260 +0,0 @@ -From 73de4b308edcc8a9de03ccfd91b1d796685d5d00 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 07:35:40 -0700 -Subject: [PATCH 063/100] perf: core/x86: Register a new vector for handling - mediated guest PMIs - -Wire up system vector 0xf5 for handling PMIs (i.e. interrupts delivered -through the LVTPC) while running KVM guests with a mediated PMU. Perf -currently delivers all PMIs as NMIs, e.g. so that events that trigger while -IRQs are disabled aren't delayed and generate useless records, but due to -the multiplexing of NMIs throughout the system, correctly identifying NMIs -for a mediated PMU is practically infeasible. - -To (greatly) simplify identifying guest mediated PMU PMIs, perf will -switch the CPU's LVTPC between PERF_GUEST_MEDIATED_PMI_VECTOR and NMI when -guest PMU context is loaded/put. I.e. PMIs that are generated by the CPU -while the guest is active will be identified purely based on the IRQ -vector. - -Route the vector through perf, e.g. as opposed to letting KVM attach a -handler directly a la posted interrupt notification vectors, as perf owns -the LVTPC and thus is the rightful owner of PERF_GUEST_MEDIATED_PMI_VECTOR. -Functionally, having KVM directly own the vector would be fine (both KVM -and perf will be completely aware of when a mediated PMU is active), but -would lead to an undesirable split in ownership: perf would be responsible -for installing the vector, but not handling the resulting IRQs. - -Add a new perf_guest_info_callbacks hook (and static call) to allow KVM to -register its handler with perf when running guests with mediated PMUs. - -Note, because KVM always runs guests with host IRQs enabled, there is no -danger of a PMI being delayed from the guest's perspective due to using a -regular IRQ instead of an NMI. - -Signed-off-by: Sean Christopherson ---- - arch/x86/entry/entry_fred.c | 1 + - arch/x86/include/asm/hardirq.h | 3 +++ - arch/x86/include/asm/idtentry.h | 6 ++++++ - arch/x86/include/asm/irq_vectors.h | 4 +++- - arch/x86/kernel/idt.c | 3 +++ - arch/x86/kernel/irq.c | 19 +++++++++++++++++++ - include/linux/perf_event.h | 8 ++++++++ - kernel/events/core.c | 9 +++++++-- - .../beauty/arch/x86/include/asm/irq_vectors.h | 3 ++- - virt/kvm/kvm_main.c | 3 +++ - 10 files changed, 55 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c -index f004a4dc74c2..d80861a4cd00 100644 ---- a/arch/x86/entry/entry_fred.c -+++ b/arch/x86/entry/entry_fred.c -@@ -114,6 +114,7 @@ static idtentry_t sysvec_table[NR_SYSTEM_VECTORS] __ro_after_init = { - - SYSVEC(IRQ_WORK_VECTOR, irq_work), - -+ SYSVEC(PERF_GUEST_MEDIATED_PMI_VECTOR, perf_guest_mediated_pmi_handler), - SYSVEC(POSTED_INTR_VECTOR, kvm_posted_intr_ipi), - SYSVEC(POSTED_INTR_WAKEUP_VECTOR, kvm_posted_intr_wakeup_ipi), - SYSVEC(POSTED_INTR_NESTED_VECTOR, kvm_posted_intr_nested_ipi), -diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h -index f00c09ffe6a9..f221d001e4ed 100644 ---- a/arch/x86/include/asm/hardirq.h -+++ b/arch/x86/include/asm/hardirq.h -@@ -18,6 +18,9 @@ typedef struct { - unsigned int kvm_posted_intr_ipis; - unsigned int kvm_posted_intr_wakeup_ipis; - unsigned int kvm_posted_intr_nested_ipis; -+#endif -+#ifdef CONFIG_GUEST_PERF_EVENTS -+ unsigned int perf_guest_mediated_pmis; - #endif - unsigned int x86_platform_ipis; /* arch dependent */ - unsigned int apic_perf_irqs; -diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h -index a4ec27c67988..5109c24b3c1c 100644 ---- a/arch/x86/include/asm/idtentry.h -+++ b/arch/x86/include/asm/idtentry.h -@@ -751,6 +751,12 @@ DECLARE_IDTENTRY_SYSVEC(POSTED_INTR_NESTED_VECTOR, sysvec_kvm_posted_intr_nested - # define fred_sysvec_kvm_posted_intr_nested_ipi NULL - #endif - -+# ifdef CONFIG_GUEST_PERF_EVENTS -+DECLARE_IDTENTRY_SYSVEC(PERF_GUEST_MEDIATED_PMI_VECTOR, sysvec_perf_guest_mediated_pmi_handler); -+#else -+# define fred_sysvec_perf_guest_mediated_pmi_handler NULL -+#endif -+ - # ifdef CONFIG_X86_POSTED_MSI - DECLARE_IDTENTRY_SYSVEC(POSTED_MSI_NOTIFICATION_VECTOR, sysvec_posted_msi_notification); - #else -diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h -index 47051871b436..85253fc8e384 100644 ---- a/arch/x86/include/asm/irq_vectors.h -+++ b/arch/x86/include/asm/irq_vectors.h -@@ -77,7 +77,9 @@ - */ - #define IRQ_WORK_VECTOR 0xf6 - --/* 0xf5 - unused, was UV_BAU_MESSAGE */ -+/* IRQ vector for PMIs when running a guest with a mediated PMU. */ -+#define PERF_GUEST_MEDIATED_PMI_VECTOR 0xf5 -+ - #define DEFERRED_ERROR_VECTOR 0xf4 - - /* Vector on which hypervisor callbacks will be delivered */ -diff --git a/arch/x86/kernel/idt.c b/arch/x86/kernel/idt.c -index f445bec516a0..260456588756 100644 ---- a/arch/x86/kernel/idt.c -+++ b/arch/x86/kernel/idt.c -@@ -158,6 +158,9 @@ static const __initconst struct idt_data apic_idts[] = { - INTG(POSTED_INTR_WAKEUP_VECTOR, asm_sysvec_kvm_posted_intr_wakeup_ipi), - INTG(POSTED_INTR_NESTED_VECTOR, asm_sysvec_kvm_posted_intr_nested_ipi), - # endif -+#ifdef CONFIG_GUEST_PERF_EVENTS -+ INTG(PERF_GUEST_MEDIATED_PMI_VECTOR, asm_sysvec_perf_guest_mediated_pmi_handler), -+#endif - # ifdef CONFIG_IRQ_WORK - INTG(IRQ_WORK_VECTOR, asm_sysvec_irq_work), - # endif -diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c -index 10721a125226..e9050e69717e 100644 ---- a/arch/x86/kernel/irq.c -+++ b/arch/x86/kernel/irq.c -@@ -191,6 +191,13 @@ int arch_show_interrupts(struct seq_file *p, int prec) - irq_stats(j)->kvm_posted_intr_wakeup_ipis); - seq_puts(p, " Posted-interrupt wakeup event\n"); - #endif -+#ifdef CONFIG_GUEST_PERF_EVENTS -+ seq_printf(p, "%*s: ", prec, "VPMI"); -+ for_each_online_cpu(j) -+ seq_printf(p, "%10u ", -+ irq_stats(j)->perf_guest_mediated_pmis); -+ seq_puts(p, " Perf Guest Mediated PMI\n"); -+#endif - #ifdef CONFIG_X86_POSTED_MSI - seq_printf(p, "%*s: ", prec, "PMN"); - for_each_online_cpu(j) -@@ -348,6 +355,18 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_x86_platform_ipi) - } - #endif - -+#ifdef CONFIG_GUEST_PERF_EVENTS -+/* -+ * Handler for PERF_GUEST_MEDIATED_PMI_VECTOR. -+ */ -+DEFINE_IDTENTRY_SYSVEC(sysvec_perf_guest_mediated_pmi_handler) -+{ -+ apic_eoi(); -+ inc_irq_stat(perf_guest_mediated_pmis); -+ perf_guest_handle_mediated_pmi(); -+} -+#endif -+ - #if IS_ENABLED(CONFIG_KVM) - static void dummy_handler(void) {} - static void (*kvm_posted_intr_wakeup_handler)(void) = dummy_handler; -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index 57341843be12..63a16f8e74a8 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1681,6 +1681,8 @@ struct perf_guest_info_callbacks { - unsigned int (*state)(void); - unsigned long (*get_ip)(void); - unsigned int (*handle_intel_pt_intr)(void); -+ -+ void (*handle_mediated_pmi)(void); - }; - - #ifdef CONFIG_GUEST_PERF_EVENTS -@@ -1690,6 +1692,7 @@ extern struct perf_guest_info_callbacks __rcu *perf_guest_cbs; - DECLARE_STATIC_CALL(__perf_guest_state, *perf_guest_cbs->state); - DECLARE_STATIC_CALL(__perf_guest_get_ip, *perf_guest_cbs->get_ip); - DECLARE_STATIC_CALL(__perf_guest_handle_intel_pt_intr, *perf_guest_cbs->handle_intel_pt_intr); -+DECLARE_STATIC_CALL(__perf_guest_handle_mediated_pmi, *perf_guest_cbs->handle_mediated_pmi); - - static inline unsigned int perf_guest_state(void) - { -@@ -1706,6 +1709,11 @@ static inline unsigned int perf_guest_handle_intel_pt_intr(void) - return static_call(__perf_guest_handle_intel_pt_intr)(); - } - -+static inline void perf_guest_handle_mediated_pmi(void) -+{ -+ static_call(__perf_guest_handle_mediated_pmi)(); -+} -+ - extern void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs); - extern void perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs); - -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 45a8b3f0a6f3..23f9e68b89ad 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -7633,6 +7633,7 @@ struct perf_guest_info_callbacks __rcu *perf_guest_cbs; - DEFINE_STATIC_CALL_RET0(__perf_guest_state, *perf_guest_cbs->state); - DEFINE_STATIC_CALL_RET0(__perf_guest_get_ip, *perf_guest_cbs->get_ip); - DEFINE_STATIC_CALL_RET0(__perf_guest_handle_intel_pt_intr, *perf_guest_cbs->handle_intel_pt_intr); -+DEFINE_STATIC_CALL_RET0(__perf_guest_handle_mediated_pmi, *perf_guest_cbs->handle_mediated_pmi); - - void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) - { -@@ -7647,6 +7648,10 @@ void perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) - if (cbs->handle_intel_pt_intr) - static_call_update(__perf_guest_handle_intel_pt_intr, - cbs->handle_intel_pt_intr); -+ -+ if (cbs->handle_mediated_pmi) -+ static_call_update(__perf_guest_handle_mediated_pmi, -+ cbs->handle_mediated_pmi); - } - EXPORT_SYMBOL_GPL(perf_register_guest_info_callbacks); - -@@ -7658,8 +7663,8 @@ void perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs) - rcu_assign_pointer(perf_guest_cbs, NULL); - static_call_update(__perf_guest_state, (void *)&__static_call_return0); - static_call_update(__perf_guest_get_ip, (void *)&__static_call_return0); -- static_call_update(__perf_guest_handle_intel_pt_intr, -- (void *)&__static_call_return0); -+ static_call_update(__perf_guest_handle_intel_pt_intr, (void *)&__static_call_return0); -+ static_call_update(__perf_guest_handle_mediated_pmi, (void *)&__static_call_return0); - synchronize_rcu(); - } - EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks); -diff --git a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h -index 47051871b436..6e1d5b955aae 100644 ---- a/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h -+++ b/tools/perf/trace/beauty/arch/x86/include/asm/irq_vectors.h -@@ -77,7 +77,8 @@ - */ - #define IRQ_WORK_VECTOR 0xf6 - --/* 0xf5 - unused, was UV_BAU_MESSAGE */ -+#define PERF_GUEST_MEDIATED_PMI_VECTOR 0xf5 -+ - #define DEFERRED_ERROR_VECTOR 0xf4 - - /* Vector on which hypervisor callbacks will be delivered */ -diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index 6c07dd423458..ecafab2e17d9 100644 ---- a/virt/kvm/kvm_main.c -+++ b/virt/kvm/kvm_main.c -@@ -6426,11 +6426,14 @@ static struct perf_guest_info_callbacks kvm_guest_cbs = { - .state = kvm_guest_state, - .get_ip = kvm_guest_get_ip, - .handle_intel_pt_intr = NULL, -+ .handle_mediated_pmi = NULL, - }; - - void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void)) - { - kvm_guest_cbs.handle_intel_pt_intr = pt_intr_handler; -+ kvm_guest_cbs.handle_mediated_pmi = NULL; -+ - perf_register_guest_info_callbacks(&kvm_guest_cbs); - } - void kvm_unregister_perf_callbacks(void) --- -2.43.0 - diff --git a/SPECS/kernel/0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf b/SPECS/kernel/0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf deleted file mode 100644 index fca9bdd41..000000000 --- a/SPECS/kernel/0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf +++ /dev/null @@ -1,118 +0,0 @@ -From bb41c19e08c1b4bdbef275667c7cecfc458c6e30 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 9 Jul 2025 09:05:45 -0700 -Subject: [PATCH 064/100] perf/x86: Switch LVTPC to/from mediated PMI vector on - guest load/put context - -Add arch hooks to the mediated vPMU load/put APIs, and use the hooks to -switch PMIs to the dedicated mediated PMU IRQ vector on load, and back to -perf's standard NMI when the guest context is put. I.e. route PMIs to -PERF_GUEST_MEDIATED_PMI_VECTOR when the guest context is active, and to -NMIs while the host context is active. - -While running with guest context loaded, ignore all NMIs (in perf). Any -NMI that arrives while the LVTPC points at the mediated PMU IRQ vector -can't possibly be due to a host perf event. - -Signed-off-by: Xiong Zhang -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: use arch hook instead of per-PMU callback] -Signed-off-by: Sean Christopherson ---- - arch/x86/events/core.c | 27 +++++++++++++++++++++++++++ - include/linux/perf_event.h | 3 +++ - kernel/events/core.c | 4 ++++ - 3 files changed, 34 insertions(+) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 56d197a56d59..dc143693ce98 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -55,6 +55,8 @@ DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { - .pmu = &pmu, - }; - -+static DEFINE_PER_CPU(bool, x86_guest_ctx_loaded); -+ - DEFINE_STATIC_KEY_FALSE(rdpmc_never_available_key); - DEFINE_STATIC_KEY_FALSE(rdpmc_always_available_key); - DEFINE_STATIC_KEY_FALSE(perf_is_hybrid); -@@ -1866,6 +1868,16 @@ perf_event_nmi_handler(unsigned int cmd, struct pt_regs *regs) - u64 finish_clock; - int ret; - -+ /* -+ * Ignore all NMIs when a guest's mediated PMU context is loaded. Any -+ * such NMI can't be due to a PMI as the CPU's LVTPC is switched to/from -+ * the dedicated mediated PMI IRQ vector while host events are quiesced. -+ * Attempting to handle a PMI while the guest's context is loaded will -+ * generate false positives and clobber guest state. -+ */ -+ if (this_cpu_read(x86_guest_ctx_loaded)) -+ return NMI_DONE; -+ - /* - * All PMUs/events that share this PMI handler should make sure to - * increment active_events for their events. -@@ -2841,6 +2853,21 @@ static struct pmu pmu = { - .filter = x86_pmu_filter, - }; - -+void arch_perf_load_guest_context(unsigned long data) -+{ -+ u32 masked = data & APIC_LVT_MASKED; -+ -+ apic_write(APIC_LVTPC, -+ APIC_DM_FIXED | PERF_GUEST_MEDIATED_PMI_VECTOR | masked); -+ this_cpu_write(x86_guest_ctx_loaded, true); -+} -+ -+void arch_perf_put_guest_context(void) -+{ -+ this_cpu_write(x86_guest_ctx_loaded, false); -+ apic_write(APIC_LVTPC, APIC_DM_NMI); -+} -+ - void arch_perf_update_userpage(struct perf_event *event, - struct perf_event_mmap_page *userpg, u64 now) - { -diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h -index 63a16f8e74a8..012dbc9de139 100644 ---- a/include/linux/perf_event.h -+++ b/include/linux/perf_event.h -@@ -1850,6 +1850,9 @@ static inline unsigned long perf_arch_guest_misc_flags(struct pt_regs *regs) - # define perf_arch_guest_misc_flags(regs) perf_arch_guest_misc_flags(regs) - #endif - -+extern void arch_perf_load_guest_context(unsigned long data); -+extern void arch_perf_put_guest_context(void); -+ - static inline bool needs_branch_stack(struct perf_event *event) - { - return event->attr.branch_sample_type != 0; -diff --git a/kernel/events/core.c b/kernel/events/core.c -index 23f9e68b89ad..e69108914c80 100644 ---- a/kernel/events/core.c -+++ b/kernel/events/core.c -@@ -6414,6 +6414,8 @@ void perf_load_guest_context(unsigned long data) - task_ctx_sched_out(cpuctx->task_ctx, NULL, EVENT_GUEST); - } - -+ arch_perf_load_guest_context(data); -+ - perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); - if (cpuctx->task_ctx) - perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); -@@ -6439,6 +6441,8 @@ void perf_put_guest_context(void) - - perf_event_sched_in(cpuctx, cpuctx->task_ctx, NULL, EVENT_GUEST); - -+ arch_perf_put_guest_context(); -+ - if (cpuctx->task_ctx) - perf_ctx_enable(cpuctx->task_ctx, EVENT_GUEST); - perf_ctx_enable(&cpuctx->ctx, EVENT_GUEST); --- -2.43.0 - diff --git a/SPECS/kernel/0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf b/SPECS/kernel/0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf deleted file mode 100644 index ff5a6c603..000000000 --- a/SPECS/kernel/0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf +++ /dev/null @@ -1,39 +0,0 @@ -From ac602f207da73491aad5025c65c4b07fee9d8caa Mon Sep 17 00:00:00 2001 -From: Sandipan Das -Date: Mon, 24 Mar 2025 17:30:52 +0000 -Subject: [PATCH 065/100] perf/x86/core: Do not set bit width for unavailable - counters - -Not all x86 processors have fixed counters. It may also be the case that -a processor has only fixed counters and no general-purpose counters. Set -the bit widths corresponding to each counter type only if such counters -are available. - -Fixes: b3d9468a8bd2 ("perf, x86: Expose perf capability to other modules") -Signed-off-by: Sandipan Das -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/events/core.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index dc143693ce98..6036be2afe0a 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -3239,8 +3239,8 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) - cap->version = x86_pmu.version; - cap->num_counters_gp = x86_pmu_num_counters(NULL); - cap->num_counters_fixed = x86_pmu_num_counters_fixed(NULL); -- cap->bit_width_gp = x86_pmu.cntval_bits; -- cap->bit_width_fixed = x86_pmu.cntval_bits; -+ cap->bit_width_gp = cap->num_counters_gp ? x86_pmu.cntval_bits : 0; -+ cap->bit_width_fixed = cap->num_counters_fixed ? x86_pmu.cntval_bits : 0; - cap->events_mask = (unsigned int)x86_pmu.events_maskl; - cap->events_mask_len = x86_pmu.events_mask_len; - cap->pebs_ept = x86_pmu.pebs_ept; --- -2.43.0 - diff --git a/SPECS/kernel/0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf b/SPECS/kernel/0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf deleted file mode 100644 index 6e444de76..000000000 --- a/SPECS/kernel/0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf +++ /dev/null @@ -1,44 +0,0 @@ -From f0f9e833e53e50f397f7ae7db590a841d53fd19c Mon Sep 17 00:00:00 2001 -From: Mingwei Zhang -Date: Mon, 24 Mar 2025 17:30:53 +0000 -Subject: [PATCH 066/100] perf/x86/core: Plumb mediated PMU capability from - x86_pmu to x86_pmu_cap - -Plumb mediated PMU capability to x86_pmu_cap in order to let any kernel -entity such as KVM know that host PMU support mediated PMU mode and has -the implementation. - -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/events/core.c | 1 + - arch/x86/include/asm/perf_event.h | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index 6036be2afe0a..68d0bea0f62e 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -3244,6 +3244,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) - cap->events_mask = (unsigned int)x86_pmu.events_maskl; - cap->events_mask_len = x86_pmu.events_mask_len; - cap->pebs_ept = x86_pmu.pebs_ept; -+ cap->mediated = !!(pmu.capabilities & PERF_PMU_CAP_MEDIATED_VPMU); - } - EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability); - -diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h -index e723666c1de2..a3ef52e943b0 100644 ---- a/arch/x86/include/asm/perf_event.h -+++ b/arch/x86/include/asm/perf_event.h -@@ -305,6 +305,7 @@ struct x86_pmu_capability { - unsigned int events_mask; - int events_mask_len; - unsigned int pebs_ept :1; -+ unsigned int mediated :1; - }; - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf b/SPECS/kernel/0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf deleted file mode 100644 index 2db51f01d..000000000 --- a/SPECS/kernel/0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf +++ /dev/null @@ -1,45 +0,0 @@ -From 56db0f02cf340a054a08db9482339290189eac16 Mon Sep 17 00:00:00 2001 -From: Kan Liang -Date: Mon, 24 Mar 2025 17:31:13 +0000 -Subject: [PATCH 067/100] perf/x86/intel: Support PERF_PMU_CAP_MEDIATED_VPMU - -Apply the PERF_PMU_CAP_MEDIATED_VPMU for Intel core PMU. It only indicates -that the perf side of core PMU is ready to support the mediated vPMU. -Besides the capability, the hypervisor, a.k.a. KVM, still needs to check -the PMU version and other PMU features/capabilities to decide whether to -enable support mediated vPMUs. - -Signed-off-by: Kan Liang -Signed-off-by: Mingwei Zhang -[sean: massage changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/events/intel/core.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c -index b6ed211695ff..f0cd578a14e1 100644 ---- a/arch/x86/events/intel/core.c -+++ b/arch/x86/events/intel/core.c -@@ -5675,6 +5675,8 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu) - else - pmu->intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS; - -+ pmu->pmu.capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; -+ - intel_pmu_check_event_constraints_all(&pmu->pmu); - - intel_pmu_check_extra_regs(pmu->extra_regs); -@@ -7293,6 +7295,9 @@ __init int intel_pmu_init(void) - pr_cont(" AnyThread deprecated, "); - } - -+ /* The perf side of core PMU is ready to support the mediated vPMU. */ -+ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; -+ - /* - * Many features on and after V6 require dynamic constraint, - * e.g., Arch PEBS, ACR. --- -2.43.0 - diff --git a/SPECS/kernel/0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf b/SPECS/kernel/0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf deleted file mode 100644 index 710b360e5..000000000 --- a/SPECS/kernel/0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf +++ /dev/null @@ -1,39 +0,0 @@ -From e8342617d4c8bd471926a482e2b039461cdc0862 Mon Sep 17 00:00:00 2001 -From: Sandipan Das -Date: Mon, 24 Mar 2025 17:31:14 +0000 -Subject: [PATCH 068/100] perf/x86/amd: Support PERF_PMU_CAP_MEDIATED_VPMU for - AMD host - -Apply the PERF_PMU_CAP_MEDIATED_VPMU flag for version 2 and later -implementations of the core PMU. Aside from having Global Control and -Status registers, virtualizing the PMU using the mediated model requires -an interface to set or clear the overflow bits in the Global Status MSRs -while restoring or saving the PMU context of a vCPU. - -PerfMonV2-capable hardware has additional MSRs for this purpose, namely -PerfCntrGlobalStatusSet and PerfCntrGlobalStatusClr, thereby making it -suitable for use with mediated vPMU. - -Signed-off-by: Sandipan Das -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/events/amd/core.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c -index b20661b8621d..8179fb5f1ee3 100644 ---- a/arch/x86/events/amd/core.c -+++ b/arch/x86/events/amd/core.c -@@ -1433,6 +1433,8 @@ static int __init amd_core_pmu_init(void) - - amd_pmu_global_cntr_mask = x86_pmu.cntr_mask64; - -+ x86_get_pmu(smp_processor_id())->capabilities |= PERF_PMU_CAP_MEDIATED_VPMU; -+ - /* Update PMC handling functions */ - x86_pmu.enable_all = amd_pmu_v2_enable_all; - x86_pmu.disable_all = amd_pmu_v2_disable_all; --- -2.43.0 - diff --git a/SPECS/kernel/0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf b/SPECS/kernel/0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf deleted file mode 100644 index f57899436..000000000 --- a/SPECS/kernel/0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf +++ /dev/null @@ -1,58 +0,0 @@ -From ae551c391dfe878f8fdfe386cfb0b884a0ee4e10 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 16 May 2025 07:09:39 -0700 -Subject: [PATCH 069/100] KVM: VMX: Setup canonical VMCS config prior to - kvm_x86_vendor_init() - -Setup the golden VMCS config during vmx_init(), before the call to -kvm_x86_vendor_init(), instead of waiting until the callback to do -hardware setup. setup_vmcs_config() only touches VMX state, i.e. doesn't -poke anything in kvm.ko, and has no runtime dependencies beyond -hv_init_evmcs(). - -Setting the VMCS config early on will allow referencing VMCS and VMX -capabilities at any point during setup, e.g. to check for PERF_GLOBAL_CTRL -save/load support during mediated PMU initialization. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 13 +++++++++---- - 1 file changed, 9 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 95765db52992..ed10013dac95 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -8335,8 +8335,6 @@ __init int vmx_hardware_setup(void) - - vmx_setup_user_return_msrs(); - -- if (setup_vmcs_config(&vmcs_config, &vmx_capability) < 0) -- return -EIO; - - if (boot_cpu_has(X86_FEATURE_NX)) - kvm_enable_efer_bits(EFER_NX); -@@ -8560,11 +8558,18 @@ int __init vmx_init(void) - return -EOPNOTSUPP; - - /* -- * Note, hv_init_evmcs() touches only VMX knobs, i.e. there's nothing -- * to unwind if a later step fails. -+ * Note, VMCS and eVMCS configuration only touch VMX knobs/variables, -+ * i.e. there's nothing to unwind if a later step fails. - */ - hv_init_evmcs(); - -+ /* -+ * Parse the VMCS config and VMX capabilities before anything else, so -+ * that the information is available to all setup flows. -+ */ -+ if (setup_vmcs_config(&vmcs_config, &vmx_capability) < 0) -+ return -EIO; -+ - r = kvm_x86_vendor_init(&vt_init_ops); - if (r) - return r; --- -2.43.0 - diff --git a/SPECS/kernel/0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf b/SPECS/kernel/0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf deleted file mode 100644 index e3357d928..000000000 --- a/SPECS/kernel/0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf +++ /dev/null @@ -1,43 +0,0 @@ -From 20223d7e37fe68853f3ce86876d015d8ca4b4e27 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Mon, 4 Aug 2025 13:24:23 -0700 -Subject: [PATCH 070/100] KVM: SVM: Check pmu->version, not enable_pmu, when - getting PMC MSRs - -Gate access to PMC MSRs based on pmu->version, not on kvm->arch.enable_pmu, -to more accurately reflect KVM's behavior. This is a glorified nop, as -pmu->version and pmu->nr_arch_gp_counters can only be non-zero if -amd_pmu_refresh() is reached, kvm_pmu_refresh() invokes amd_pmu_refresh() -if and only if kvm->arch.enable_pmu is true, and amd_pmu_refresh() forces -pmu->version to be 1 or 2. - -I.e. the following holds true: - - !pmu->nr_arch_gp_counters || kvm->arch.enable_pmu == (pmu->version > 0) - -and so the only way for amd_pmu_get_pmc() to return a non-NULL value is if -both kvm->arch.enable_pmu and pmu->version evaluate to true. - -No real functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 288f7f2a46f2..7b8577f3c57a 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -41,7 +41,7 @@ static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr, - struct kvm_vcpu *vcpu = pmu_to_vcpu(pmu); - unsigned int idx; - -- if (!vcpu->kvm->arch.enable_pmu) -+ if (!pmu->version) - return NULL; - - switch (msr) { --- -2.43.0 - diff --git a/SPECS/kernel/0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf b/SPECS/kernel/0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf deleted file mode 100644 index c0f5146c3..000000000 --- a/SPECS/kernel/0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf +++ /dev/null @@ -1,118 +0,0 @@ -From 1b7729987d29af6aaf929efc0ab417c87176a6eb Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 17:46:44 -0700 -Subject: [PATCH 071/100] KVM: Add a simplified wrapper for registering perf - callbacks - -Add a parameter-less API for registering perf callbacks in anticipation of -introducing another x86-only parameter for handling mediated PMU PMIs. - -No functional change intended. - -Signed-off-by: Sean Christopherson ---- - arch/arm64/kvm/arm.c | 2 +- - arch/loongarch/kvm/main.c | 2 +- - arch/riscv/kvm/main.c | 2 +- - arch/x86/kvm/x86.c | 2 +- - include/linux/kvm_host.h | 11 +++++++++-- - virt/kvm/kvm_main.c | 5 +++-- - 6 files changed, 16 insertions(+), 8 deletions(-) - -diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c -index 888f7c7abf54..6c604b5214f2 100644 ---- a/arch/arm64/kvm/arm.c -+++ b/arch/arm64/kvm/arm.c -@@ -2328,7 +2328,7 @@ static int __init init_subsystems(void) - if (err) - goto out; - -- kvm_register_perf_callbacks(NULL); -+ kvm_register_perf_callbacks(); - - out: - if (err) -diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c -index 80ea63d465b8..f62326fe29fa 100644 ---- a/arch/loongarch/kvm/main.c -+++ b/arch/loongarch/kvm/main.c -@@ -394,7 +394,7 @@ static int kvm_loongarch_env_init(void) - } - - kvm_init_gcsr_flag(); -- kvm_register_perf_callbacks(NULL); -+ kvm_register_perf_callbacks(); - - /* Register LoongArch IPI interrupt controller interface. */ - ret = kvm_loongarch_register_ipi_device(); -diff --git a/arch/riscv/kvm/main.c b/arch/riscv/kvm/main.c -index 67c876de74ef..cbe842c2f615 100644 ---- a/arch/riscv/kvm/main.c -+++ b/arch/riscv/kvm/main.c -@@ -159,7 +159,7 @@ static int __init riscv_kvm_init(void) - kvm_info("AIA available with %d guest external interrupts\n", - kvm_riscv_aia_nr_hgei); - -- kvm_register_perf_callbacks(NULL); -+ kvm_register_perf_callbacks(); - - rc = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); - if (rc) { -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 5af2c5aed0f2..d80bbd5e0859 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9689,7 +9689,7 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - set_hv_tscchange_cb(kvm_hyperv_tsc_notifier); - #endif - -- kvm_register_perf_callbacks(ops->handle_intel_pt_intr); -+ __kvm_register_perf_callbacks(ops->handle_intel_pt_intr, NULL); - - if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && tdp_mmu_enabled) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SW_PROTECTED_VM); -diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h -index 15656b7fba6c..20c50eaa0089 100644 ---- a/include/linux/kvm_host.h -+++ b/include/linux/kvm_host.h -@@ -1731,10 +1731,17 @@ static inline bool kvm_arch_intc_initialized(struct kvm *kvm) - #ifdef CONFIG_GUEST_PERF_EVENTS - unsigned long kvm_arch_vcpu_get_ip(struct kvm_vcpu *vcpu); - --void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void)); -+void __kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void), -+ void (*mediated_pmi_handler)(void)); -+ -+static inline void kvm_register_perf_callbacks(void) -+{ -+ __kvm_register_perf_callbacks(NULL, NULL); -+} -+ - void kvm_unregister_perf_callbacks(void); - #else --static inline void kvm_register_perf_callbacks(void *ign) {} -+static inline void kvm_register_perf_callbacks(void) {} - static inline void kvm_unregister_perf_callbacks(void) {} - #endif /* CONFIG_GUEST_PERF_EVENTS */ - -diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c -index ecafab2e17d9..d477a7fda0ae 100644 ---- a/virt/kvm/kvm_main.c -+++ b/virt/kvm/kvm_main.c -@@ -6429,10 +6429,11 @@ static struct perf_guest_info_callbacks kvm_guest_cbs = { - .handle_mediated_pmi = NULL, - }; - --void kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void)) -+void __kvm_register_perf_callbacks(unsigned int (*pt_intr_handler)(void), -+ void (*mediated_pmi_handler)(void)) - { - kvm_guest_cbs.handle_intel_pt_intr = pt_intr_handler; -- kvm_guest_cbs.handle_mediated_pmi = NULL; -+ kvm_guest_cbs.handle_mediated_pmi = mediated_pmi_handler; - - perf_register_guest_info_callbacks(&kvm_guest_cbs); - } --- -2.43.0 - diff --git a/SPECS/kernel/0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf b/SPECS/kernel/0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf deleted file mode 100644 index 30b594ac9..000000000 --- a/SPECS/kernel/0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf +++ /dev/null @@ -1,72 +0,0 @@ -From bee18b05e80661bc9fa45d1d2ea5796ae76ac8de Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 17:38:40 -0700 -Subject: [PATCH 072/100] KVM: x86/pmu: Snapshot host (i.e. perf's) reported - PMU capabilities - -Take a snapshot of the unadulterated PMU capabilities provided by perf so -that KVM can compare guest vPMU capabilities against hardware capabilities -when determining whether or not to intercept PMU MSRs (and RDPMC). - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 15 ++++++++++----- - 1 file changed, 10 insertions(+), 5 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 3206412a35a1..0f3e011824ed 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -26,6 +26,10 @@ - /* This is enough to filter the vast majority of currently defined events. */ - #define KVM_PMU_EVENT_FILTER_MAX_EVENTS 300 - -+/* Unadultered PMU capabilities of the host, i.e. of hardware. */ -+static struct x86_pmu_capability __read_mostly kvm_host_pmu; -+ -+/* KVM's PMU capabilities, i.e. the intersection of KVM and hardware support. */ - struct x86_pmu_capability __read_mostly kvm_pmu_cap; - EXPORT_SYMBOL_GPL(kvm_pmu_cap); - -@@ -104,6 +108,8 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; - int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; - -+ perf_get_x86_pmu_capability(&kvm_host_pmu); -+ - /* - * Hybrid PMUs don't play nice with virtualization without careful - * configuration by userspace, and KVM's APIs for reporting supported -@@ -114,18 +120,16 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - enable_pmu = false; - - if (enable_pmu) { -- perf_get_x86_pmu_capability(&kvm_pmu_cap); -- - /* - * WARN if perf did NOT disable hardware PMU if the number of - * architecturally required GP counters aren't present, i.e. if - * there are a non-zero number of counters, but fewer than what - * is architecturally required. - */ -- if (!kvm_pmu_cap.num_counters_gp || -- WARN_ON_ONCE(kvm_pmu_cap.num_counters_gp < min_nr_gp_ctrs)) -+ if (!kvm_host_pmu.num_counters_gp || -+ WARN_ON_ONCE(kvm_host_pmu.num_counters_gp < min_nr_gp_ctrs)) - enable_pmu = false; -- else if (is_intel && !kvm_pmu_cap.version) -+ else if (is_intel && !kvm_host_pmu.version) - enable_pmu = false; - } - -@@ -134,6 +138,7 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - return; - } - -+ memcpy(&kvm_pmu_cap, &kvm_host_pmu, sizeof(kvm_host_pmu)); - kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); - kvm_pmu_cap.num_counters_gp = min(kvm_pmu_cap.num_counters_gp, - pmu_ops->MAX_NR_GP_COUNTERS); --- -2.43.0 - diff --git a/SPECS/kernel/0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf b/SPECS/kernel/0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf deleted file mode 100644 index 4855f1c3b..000000000 --- a/SPECS/kernel/0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf +++ /dev/null @@ -1,202 +0,0 @@ -From 231dc35b989bd5c6fb4a8b412f2d838ec82b3353 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 4 Aug 2025 14:03:05 -0700 -Subject: [PATCH 073/100] KVM: x86/pmu: Start stubbing in mediated PMU support - -Introduce enable_mediated_pmu as a global variable, with the intent of -exposing it to userspace a vendor module parameter, to control and reflect -mediated vPMU support. Wire up the perf plumbing to create+release a -mediated PMU, but defer exposing the parameter to userspace until KVM -support for a mediated PMUs is fully landed. - -To (a) minimize compatibility issues, (b) to give userspace a chance to -opt out of the restrictive side-effects of perf_create_mediated_pmu(), -and (c) to avoid adding new dependencies between enabling an in-kernel -irqchip and a mediated vPMU, defer "creating" a mediated PMU in perf -until the first vCPU is created. - -Regarding userspace compatibility, an alternative solution would be to -make the mediated PMU fully opt-in, e.g. to avoid unexpected failure due -to perf_create_mediated_pmu() failing. Ironically, that approach creates -an even bigger compatibility issue, as turning on enable_mediated_pmu -would silently break VMMs that don't utilize KVM_CAP_PMU_CAPABILITY (well, -silently until the guest tried to access PMU assets). - -Regarding an in-kernel irqchip, create a mediated PMU if and only if the -VM has an in-kernel local APIC, as the mediated PMU will take a hard -dependency on forwarding PMIs to the guest without bouncing through host -userspace. Silently "drop" the PMU instead of rejecting KVM_CREATE_VCPU, -as KVM's existing vPMU support doesn't function correctly if the local -APIC is emulated by userspace, e.g. PMIs will never be delivered. I.e. -it's far, far more likely that rejecting KVM_CREATE_VCPU would cause -problems, e.g. for tests or userspace daemons that just want to probe -basic KVM functionality. - -Note! Deliberately make mediated PMU creation "sticky", i.e. don't unwind -it on failure to create a vCPU. Practically speaking, there's no harm to -having a VM with a mediated PMU and no vCPUs. To avoid an "impossible" VM -setup, reject KVM_CAP_PMU_CAPABILITY if a mediated PMU has been created, -i.e. don't let userspace disable PMU support after failed vCPU creation -(with PMU support enabled). - -Defer vendor specific requirements and constraints to the future. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 1 + - arch/x86/kvm/pmu.c | 4 ++++ - arch/x86/kvm/pmu.h | 7 +++++++ - arch/x86/kvm/x86.c | 37 +++++++++++++++++++++++++++++++-- - arch/x86/kvm/x86.h | 1 + - 5 files changed, 48 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 95d7d727db03..83682be4c088 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -1471,6 +1471,7 @@ struct kvm_arch { - - bool bus_lock_detection_enabled; - bool enable_pmu; -+ bool created_mediated_pmu; - - u32 notify_window; - u32 notify_vmexit_flags; -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 0f3e011824ed..4d4bb9b17412 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -133,6 +133,10 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - enable_pmu = false; - } - -+ if (!enable_pmu || !enable_mediated_pmu || !kvm_host_pmu.mediated || -+ !pmu_ops->is_mediated_pmu_supported(&kvm_host_pmu)) -+ enable_mediated_pmu = false; -+ - if (!enable_pmu) { - memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); - return; -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 5c3939e91f1d..a5c7c026b919 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -37,6 +37,8 @@ struct kvm_pmu_ops { - void (*deliver_pmi)(struct kvm_vcpu *vcpu); - void (*cleanup)(struct kvm_vcpu *vcpu); - -+ bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); -+ - const u64 EVENTSEL_EVENT; - const int MAX_NR_GP_COUNTERS; - const int MIN_NR_GP_COUNTERS; -@@ -58,6 +60,11 @@ static inline bool kvm_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) - return pmu->version > 1; - } - -+static inline bool kvm_vcpu_has_mediated_pmu(struct kvm_vcpu *vcpu) -+{ -+ return enable_mediated_pmu && vcpu_to_pmu(vcpu)->version; -+} -+ - /* - * KVM tracks all counters in 64-bit bitmaps, with general purpose counters - * mapped to bits 31:0 and fixed counters mapped to 63:32, e.g. fixed counter 0 -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index d80bbd5e0859..396d1aa81732 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -187,6 +187,10 @@ bool __read_mostly enable_pmu = true; - EXPORT_SYMBOL_GPL(enable_pmu); - module_param(enable_pmu, bool, 0444); - -+/* Enable/disabled mediated PMU virtualization. */ -+bool __read_mostly enable_mediated_pmu; -+EXPORT_SYMBOL_GPL(enable_mediated_pmu); -+ - bool __read_mostly eager_page_split = true; - module_param(eager_page_split, bool, 0644); - -@@ -6542,7 +6546,7 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, - break; - - mutex_lock(&kvm->lock); -- if (!kvm->created_vcpus) { -+ if (!kvm->created_vcpus && !kvm->arch.created_mediated_pmu) { - kvm->arch.enable_pmu = !(cap->args[0] & KVM_PMU_CAP_DISABLE); - r = 0; - } -@@ -12174,8 +12178,13 @@ static int sync_regs(struct kvm_vcpu *vcpu) - return 0; - } - -+#define PERF_MEDIATED_PMU_MSG \ -+ "Failed to enable mediated vPMU, try disabling system wide perf events and nmi_watchdog.\n" -+ - int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) - { -+ int r; -+ - if (kvm_check_tsc_unstable() && kvm->created_vcpus) - pr_warn_once("SMP vm created on host with unstable TSC; " - "guest TSC will not be reliable\n"); -@@ -12186,7 +12195,29 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) - if (id >= kvm->arch.max_vcpu_ids) - return -EINVAL; - -- return kvm_x86_call(vcpu_precreate)(kvm); -+ /* -+ * Note, any actions done by .vcpu_create() must be idempotent with -+ * respect to creating multiple vCPUs, and therefore are not undone if -+ * creating a vCPU fails (including failure during pre-create). -+ */ -+ r = kvm_x86_call(vcpu_precreate)(kvm); -+ if (r) -+ return r; -+ -+ if (enable_mediated_pmu && kvm->arch.enable_pmu && -+ !kvm->arch.created_mediated_pmu) { -+ if (irqchip_in_kernel(kvm)) { -+ r = perf_create_mediated_pmu(); -+ if (r) { -+ pr_warn_ratelimited(PERF_MEDIATED_PMU_MSG); -+ return r; -+ } -+ kvm->arch.created_mediated_pmu = true; -+ } else { -+ kvm->arch.enable_pmu = false; -+ } -+ } -+ return 0; - } - - int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) -@@ -12818,6 +12849,8 @@ void kvm_arch_destroy_vm(struct kvm *kvm) - __x86_set_memory_region(kvm, TSS_PRIVATE_MEMSLOT, 0, 0); - mutex_unlock(&kvm->slots_lock); - } -+ if (kvm->arch.created_mediated_pmu) -+ perf_release_mediated_pmu(); - kvm_destroy_vcpus(kvm); - kvm_free_msr_filter(srcu_dereference_check(kvm->arch.msr_filter, &kvm->srcu, 1)); - #ifdef CONFIG_KVM_IOAPIC -diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h -index 46220b04cdf2..bd1149768acc 100644 ---- a/arch/x86/kvm/x86.h -+++ b/arch/x86/kvm/x86.h -@@ -445,6 +445,7 @@ extern struct kvm_caps kvm_caps; - extern struct kvm_host_values kvm_host; - - extern bool enable_pmu; -+extern bool enable_mediated_pmu; - - /* - * Get a filtered version of KVM's supported XCR0 that strips out dynamic --- -2.43.0 - diff --git a/SPECS/kernel/0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf b/SPECS/kernel/0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf deleted file mode 100644 index 650679db7..000000000 --- a/SPECS/kernel/0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf +++ /dev/null @@ -1,91 +0,0 @@ -From dac0c96ac12dabd83741644c885efb613af2eda5 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:54 +0000 -Subject: [PATCH 074/100] KVM: x86/pmu: Implement Intel mediated PMU - requirements and constraints - -Implement Intel PMU requirements and constraints for mediated PMU support. -Require host PMU version 4+ so that PERF_GLOBAL_STATUS_SET can be used to -precisely load the guest's status value into hardware, and require full- -width writes so that KVM can precisely load guest counter values. - -Disable PEBS and LBRs if mediated PMU support is enabled, as they won't be -supported in the initial implementation. - -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: split to separate patch, add full-width writes dependency] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/capabilities.h | 3 ++- - arch/x86/kvm/vmx/pmu_intel.c | 17 +++++++++++++++++ - arch/x86/kvm/vmx/vmx.c | 3 ++- - 3 files changed, 21 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 5316c27f6099..854e54c352f8 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -389,7 +389,8 @@ static inline bool vmx_pt_mode_is_host_guest(void) - - static inline bool vmx_pebs_supported(void) - { -- return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept; -+ return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept && -+ !enable_mediated_pmu; - } - - static inline bool cpu_has_notify_vmexit(void) -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index b2a2c4ebf448..c264a60791a5 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -776,6 +776,20 @@ void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu) - } - } - -+static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pmu) -+{ -+ u64 host_perf_cap = 0; -+ -+ if (boot_cpu_has(X86_FEATURE_PDCM)) -+ rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap); -+ -+ /* -+ * Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width -+ * writes so that KVM can precisely load guest counter values. -+ */ -+ return host_pmu->version >= 4 && host_perf_cap & PMU_CAP_FW_WRITES; -+} -+ - struct kvm_pmu_ops intel_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = intel_msr_idx_to_pmc, -@@ -787,6 +801,9 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = { - .reset = intel_pmu_reset, - .deliver_pmi = intel_pmu_deliver_pmi, - .cleanup = intel_pmu_cleanup, -+ -+ .is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported, -+ - .EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = 1, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index ed10013dac95..8c6343494e62 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7795,7 +7795,8 @@ static __init u64 vmx_get_perf_capabilities(void) - if (boot_cpu_has(X86_FEATURE_PDCM)) - rdmsrq(MSR_IA32_PERF_CAPABILITIES, host_perf_cap); - -- if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR)) { -+ if (!cpu_feature_enabled(X86_FEATURE_ARCH_LBR) && -+ !enable_mediated_pmu) { - x86_perf_get_lbr(&vmx_lbr_caps); - - /* --- -2.43.0 - diff --git a/SPECS/kernel/0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf b/SPECS/kernel/0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf deleted file mode 100644 index 969ebbab3..000000000 --- a/SPECS/kernel/0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf +++ /dev/null @@ -1,46 +0,0 @@ -From b6dad8e5f2fe290dfc3c85b62b78d8b95e06fdeb Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 14 May 2025 10:59:09 -0700 -Subject: [PATCH 075/100] KVM: x86/pmu: Implement AMD mediated PMU requirements - -Require host PMU version 2+ for AMD mediated PMU support, as -PERF_GLOBAL_CTRL and friends are hard requirements for the mediated PMU. - -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: extract to separate patch, write changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 7b8577f3c57a..96be2c3e0d65 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -227,6 +227,11 @@ static void amd_pmu_init(struct kvm_vcpu *vcpu) - } - } - -+static bool amd_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pmu) -+{ -+ return host_pmu->version >= 2; -+} -+ - struct kvm_pmu_ops amd_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = amd_msr_idx_to_pmc, -@@ -236,6 +241,9 @@ struct kvm_pmu_ops amd_pmu_ops __initdata = { - .set_msr = amd_pmu_set_msr, - .refresh = amd_pmu_refresh, - .init = amd_pmu_init, -+ -+ .is_mediated_pmu_supported = amd_pmu_is_mediated_pmu_supported, -+ - .EVENTSEL_EVENT = AMD64_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_AMD_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = AMD64_NUM_COUNTERS, --- -2.43.0 - diff --git a/SPECS/kernel/0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf b/SPECS/kernel/0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf deleted file mode 100644 index 01a3a3ab3..000000000 --- a/SPECS/kernel/0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf +++ /dev/null @@ -1,78 +0,0 @@ -From 6d5e6fc135a4215da8c17be8b0bc71afed54e07a Mon Sep 17 00:00:00 2001 -From: Xiong Zhang -Date: Tue, 13 May 2025 17:47:23 -0700 -Subject: [PATCH 076/100] KVM: x86/pmu: Register PMI handler for mediated vPMU - -Register a dedicated PMI handler with perf's callback when mediated PMU -support is enabled. Perf routes PMIs that arrive while guest context is -loaded to the provided callback, by modifying the CPU's LVTPC to point at -a dedicated mediated PMI IRQ vector. - -WARN upon receipt of a mediated PMI if there is no active vCPU, or if the -vCPU doesn't have a mediated PMU. Even if a PMI manages to skid past -VM-Exit, it should never be delayed all the way beyond unloading the vCPU. -And while running vCPUs without a mediated PMU, the LVTPC should never be -wired up to the mediated PMI IRQ vector, i.e. should always be routed -through perf's NMI handler. - -Signed-off-by: Xiong Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 10 ++++++++++ - arch/x86/kvm/pmu.h | 2 ++ - arch/x86/kvm/x86.c | 3 ++- - 3 files changed, 14 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 4d4bb9b17412..680523e9d11e 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -155,6 +155,16 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - perf_get_hw_event_config(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); - } - -+void kvm_handle_guest_mediated_pmi(void) -+{ -+ struct kvm_vcpu *vcpu = kvm_get_running_vcpu(); -+ -+ if (WARN_ON_ONCE(!vcpu || !kvm_vcpu_has_mediated_pmu(vcpu))) -+ return; -+ -+ kvm_make_request(KVM_REQ_PMI, vcpu); -+} -+ - static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index a5c7c026b919..9849c2bb720d 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -46,6 +46,8 @@ struct kvm_pmu_ops { - - void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops); - -+void kvm_handle_guest_mediated_pmi(void); -+ - static inline bool kvm_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) - { - /* -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 396d1aa81732..2c34dd3f0222 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9693,7 +9693,8 @@ int kvm_x86_vendor_init(struct kvm_x86_init_ops *ops) - set_hv_tscchange_cb(kvm_hyperv_tsc_notifier); - #endif - -- __kvm_register_perf_callbacks(ops->handle_intel_pt_intr, NULL); -+ __kvm_register_perf_callbacks(ops->handle_intel_pt_intr, -+ enable_mediated_pmu ? kvm_handle_guest_mediated_pmi : NULL); - - if (IS_ENABLED(CONFIG_KVM_SW_PROTECTED_VM) && tdp_mmu_enabled) - kvm_caps.supported_vm_types |= BIT(KVM_X86_SW_PROTECTED_VM); --- -2.43.0 - diff --git a/SPECS/kernel/0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf b/SPECS/kernel/0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf deleted file mode 100644 index 0b01b39a6..000000000 --- a/SPECS/kernel/0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf +++ /dev/null @@ -1,56 +0,0 @@ -From cb3aa7500be47c492e240ffbc193f252ff7d8752 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:56 +0000 -Subject: [PATCH 077/100] KVM: x86: Rename vmx_vmentry/vmexit_ctrl() helpers - -Rename the two helpers vmx_vmentry/vmexit_ctrl() to -vmx_get_initial_vmentry/vmexit_ctrl() to represent their real meaning. - -No functional change intended. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 8c6343494e62..7b0b51809f0e 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4304,7 +4304,7 @@ static u32 vmx_pin_based_exec_ctrl(struct vcpu_vmx *vmx) - return pin_based_exec_ctrl; - } - --static u32 vmx_vmentry_ctrl(void) -+static u32 vmx_get_initial_vmentry_ctrl(void) - { - u32 vmentry_ctrl = vmcs_config.vmentry_ctrl; - -@@ -4321,7 +4321,7 @@ static u32 vmx_vmentry_ctrl(void) - return vmentry_ctrl; - } - --static u32 vmx_vmexit_ctrl(void) -+static u32 vmx_get_initial_vmexit_ctrl(void) - { - u32 vmexit_ctrl = vmcs_config.vmexit_ctrl; - -@@ -4686,10 +4686,10 @@ static void init_vmcs(struct vcpu_vmx *vmx) - if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT) - vmcs_write64(GUEST_IA32_PAT, vmx->vcpu.arch.pat); - -- vm_exit_controls_set(vmx, vmx_vmexit_ctrl()); -+ vm_exit_controls_set(vmx, vmx_get_initial_vmexit_ctrl()); - - /* 22.2.1, 20.8.1 */ -- vm_entry_controls_set(vmx, vmx_vmentry_ctrl()); -+ vm_entry_controls_set(vmx, vmx_get_initial_vmentry_ctrl()); - - vmx->vcpu.arch.cr0_guest_owned_bits = vmx_l1_guest_owned_cr0_bits(); - vmcs_writel(CR0_GUEST_HOST_MASK, ~vmx->vcpu.arch.cr0_guest_owned_bits); --- -2.43.0 - diff --git a/SPECS/kernel/0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf b/SPECS/kernel/0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf deleted file mode 100644 index 3ce8456b6..000000000 --- a/SPECS/kernel/0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf +++ /dev/null @@ -1,130 +0,0 @@ -From 950f2db5d51aa3fc7071b92d0d9107ab762b4df1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:58 +0000 -Subject: [PATCH 078/100] KVM: x86/pmu: Move PMU_CAP_{FW_WRITES,LBR_FMT} into - msr-index.h header - -Move PMU_CAP_{FW_WRITES,LBR_FMT} into msr-index.h and rename them with -PERF_CAP prefix to keep consistent with other perf capabilities macros. - -No functional change intended. - -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/msr-index.h | 3 +++ - arch/x86/kvm/vmx/capabilities.h | 3 --- - arch/x86/kvm/vmx/pmu_intel.c | 6 +++--- - arch/x86/kvm/vmx/vmx.c | 12 ++++++------ - 4 files changed, 12 insertions(+), 12 deletions(-) - -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 84a1cbe6ab30..1570487e7213 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -315,9 +315,12 @@ - #define PERF_CAP_PT_IDX 16 - - #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 -+ -+#define PERF_CAP_LBR_FMT 0x3f - #define PERF_CAP_PEBS_TRAP BIT_ULL(6) - #define PERF_CAP_ARCH_REG BIT_ULL(7) - #define PERF_CAP_PEBS_FORMAT 0xf00 -+#define PERF_CAP_FW_WRITES BIT_ULL(13) - #define PERF_CAP_PEBS_BASELINE BIT_ULL(14) - #define PERF_CAP_PEBS_TIMING_INFO BIT_ULL(17) - #define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 854e54c352f8..26ff606ff139 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -20,9 +20,6 @@ extern int __read_mostly pt_mode; - #define PT_MODE_SYSTEM 0 - #define PT_MODE_HOST_GUEST 1 - --#define PMU_CAP_FW_WRITES (1ULL << 13) --#define PMU_CAP_LBR_FMT 0x3f -- - struct nested_vmx_msrs { - /* - * We only store the "true" versions of the VMX capability MSRs. We -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index c264a60791a5..5a9841d75117 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -138,7 +138,7 @@ static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu) - - static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu) - { -- return (vcpu_get_perf_capabilities(vcpu) & PMU_CAP_FW_WRITES) != 0; -+ return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0; - } - - static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr) -@@ -588,7 +588,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - - perf_capabilities = vcpu_get_perf_capabilities(vcpu); - if (intel_pmu_lbr_is_compatible(vcpu) && -- (perf_capabilities & PMU_CAP_LBR_FMT)) -+ (perf_capabilities & PERF_CAP_LBR_FMT)) - memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps)); - else - lbr_desc->records.nr = 0; -@@ -787,7 +787,7 @@ static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_ - * Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width - * writes so that KVM can precisely load guest counter values. - */ -- return host_pmu->version >= 4 && host_perf_cap & PMU_CAP_FW_WRITES; -+ return host_pmu->version >= 4 && host_perf_cap & PERF_CAP_FW_WRITES; - } - - struct kvm_pmu_ops intel_pmu_ops __initdata = { -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 7b0b51809f0e..93b87f9e6dfd 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -2127,7 +2127,7 @@ u64 vmx_get_supported_debugctl(struct kvm_vcpu *vcpu, bool host_initiated) - (host_initiated || guest_cpu_cap_has(vcpu, X86_FEATURE_BUS_LOCK_DETECT))) - debugctl |= DEBUGCTLMSR_BUS_LOCK_DETECT; - -- if ((kvm_caps.supported_perf_cap & PMU_CAP_LBR_FMT) && -+ if ((kvm_caps.supported_perf_cap & PERF_CAP_LBR_FMT) && - (host_initiated || intel_pmu_lbr_is_enabled(vcpu))) - debugctl |= DEBUGCTLMSR_LBR | DEBUGCTLMSR_FREEZE_LBRS_ON_PMI; - -@@ -2412,9 +2412,9 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - vmx->pt_desc.guest.addr_a[index / 2] = data; - break; - case MSR_IA32_PERF_CAPABILITIES: -- if (data & PMU_CAP_LBR_FMT) { -- if ((data & PMU_CAP_LBR_FMT) != -- (kvm_caps.supported_perf_cap & PMU_CAP_LBR_FMT)) -+ if (data & PERF_CAP_LBR_FMT) { -+ if ((data & PERF_CAP_LBR_FMT) != -+ (kvm_caps.supported_perf_cap & PERF_CAP_LBR_FMT)) - return 1; - if (!cpuid_model_is_consistent(vcpu)) - return 1; -@@ -7786,7 +7786,7 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - - static __init u64 vmx_get_perf_capabilities(void) - { -- u64 perf_cap = PMU_CAP_FW_WRITES; -+ u64 perf_cap = PERF_CAP_FW_WRITES; - u64 host_perf_cap = 0; - - if (!enable_pmu) -@@ -7807,7 +7807,7 @@ static __init u64 vmx_get_perf_capabilities(void) - if (!vmx_lbr_caps.has_callstack) - memset(&vmx_lbr_caps, 0, sizeof(vmx_lbr_caps)); - else if (vmx_lbr_caps.nr) -- perf_cap |= host_perf_cap & PMU_CAP_LBR_FMT; -+ perf_cap |= host_perf_cap & PERF_CAP_LBR_FMT; - } - - if (vmx_pebs_supported()) { --- -2.43.0 - diff --git a/SPECS/kernel/0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf b/SPECS/kernel/0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf deleted file mode 100644 index 749209671..000000000 --- a/SPECS/kernel/0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf +++ /dev/null @@ -1,229 +0,0 @@ -From b40fc5e290947f2498a4ed3640e7c41aa843545b Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 8 Jul 2025 15:57:44 -0700 -Subject: [PATCH 079/100] KVM: x86: Rework KVM_REQ_MSR_FILTER_CHANGED into a - generic RECALC_INTERCEPTS - -Rework the MSR_FILTER_CHANGED request into a more generic RECALC_INTERCEPTS -request, and expand the responsibilities of vendor code to recalculate all -intercepts that vary based on userspace input, e.g. instruction intercepts -that are tied to guest CPUID. - -Providing a generic recalc request will allow the upcoming mediated PMU -support to trigger a recalc when PMU features, e.g. PERF_CAPABILITIES, are -set by userspace, without having to make multiple calls to/from PMU code. -As a bonus, using a request will effectively coalesce recalcs, e.g. will -reduce the number of recalcs for normal usage from 3+ to 1 (vCPU create, -set CPUID, set PERF_CAPABILITIES (Intel only), set filter). - -The downside is that MSR filter changes that are done in isolation will do -a small amount of unnecessary work, but that's already a relatively slow -path, and the cost of recalculating instruction intercepts is negligible. - -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm-x86-ops.h | 2 +- - arch/x86/include/asm/kvm_host.h | 4 ++-- - arch/x86/kvm/svm/svm.c | 8 ++++---- - arch/x86/kvm/vmx/main.c | 14 +++++++------- - arch/x86/kvm/vmx/vmx.c | 9 +++++++-- - arch/x86/kvm/vmx/x86_ops.h | 2 +- - arch/x86/kvm/x86.c | 15 +++++++-------- - 7 files changed, 29 insertions(+), 25 deletions(-) - -diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h -index 18a5c3119e1a..7c240e23bd52 100644 ---- a/arch/x86/include/asm/kvm-x86-ops.h -+++ b/arch/x86/include/asm/kvm-x86-ops.h -@@ -138,7 +138,7 @@ KVM_X86_OP(check_emulate_instruction) - KVM_X86_OP(apic_init_signal_blocked) - KVM_X86_OP_OPTIONAL(enable_l2_tlb_flush) - KVM_X86_OP_OPTIONAL(migrate_timers) --KVM_X86_OP(recalc_msr_intercepts) -+KVM_X86_OP(recalc_intercepts) - KVM_X86_OP(complete_emulated_msr) - KVM_X86_OP(vcpu_deliver_sipi_vector) - KVM_X86_OP_OPTIONAL_RET0(vcpu_get_apicv_inhibit_reasons); -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 83682be4c088..ecd0634599ff 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -120,7 +120,7 @@ - #define KVM_REQ_TLB_FLUSH_GUEST \ - KVM_ARCH_REQ_FLAGS(27, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) - #define KVM_REQ_APF_READY KVM_ARCH_REQ(28) --#define KVM_REQ_MSR_FILTER_CHANGED KVM_ARCH_REQ(29) -+#define KVM_REQ_RECALC_INTERCEPTS KVM_ARCH_REQ(29) - #define KVM_REQ_UPDATE_CPU_DIRTY_LOGGING \ - KVM_ARCH_REQ_FLAGS(30, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) - #define KVM_REQ_MMU_FREE_OBSOLETE_ROOTS \ -@@ -1912,7 +1912,7 @@ struct kvm_x86_ops { - int (*enable_l2_tlb_flush)(struct kvm_vcpu *vcpu); - - void (*migrate_timers)(struct kvm_vcpu *vcpu); -- void (*recalc_msr_intercepts)(struct kvm_vcpu *vcpu); -+ void (*recalc_intercepts)(struct kvm_vcpu *vcpu); - int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err); - - void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector); -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index f7e1e665a826..3d9dcc66a407 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -1077,7 +1077,7 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) - } - } - --static void svm_recalc_intercepts_after_set_cpuid(struct kvm_vcpu *vcpu) -+static void svm_recalc_intercepts(struct kvm_vcpu *vcpu) - { - svm_recalc_instruction_intercepts(vcpu); - svm_recalc_msr_intercepts(vcpu); -@@ -1225,7 +1225,7 @@ static void init_vmcb(struct kvm_vcpu *vcpu) - - svm_hv_init_vmcb(vmcb); - -- svm_recalc_intercepts_after_set_cpuid(vcpu); -+ svm_recalc_intercepts(vcpu); - - vmcb_mark_all_dirty(vmcb); - -@@ -4479,7 +4479,7 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - if (sev_guest(vcpu->kvm)) - sev_vcpu_after_set_cpuid(svm); - -- svm_recalc_intercepts_after_set_cpuid(vcpu); -+ svm_recalc_intercepts(vcpu); - } - - static bool svm_has_wbinvd_exit(void) -@@ -5181,7 +5181,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { - - .apic_init_signal_blocked = svm_apic_init_signal_blocked, - -- .recalc_msr_intercepts = svm_recalc_msr_intercepts, -+ .recalc_intercepts = svm_recalc_intercepts, - .complete_emulated_msr = svm_complete_emulated_msr, - - .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, -diff --git a/arch/x86/kvm/vmx/main.c b/arch/x86/kvm/vmx/main.c -index dbab1c15b0cd..68dcafd177a8 100644 ---- a/arch/x86/kvm/vmx/main.c -+++ b/arch/x86/kvm/vmx/main.c -@@ -188,18 +188,18 @@ static int vt_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - return vmx_get_msr(vcpu, msr_info); - } - --static void vt_recalc_msr_intercepts(struct kvm_vcpu *vcpu) -+static void vt_recalc_intercepts(struct kvm_vcpu *vcpu) - { - /* -- * TDX doesn't allow VMM to configure interception of MSR accesses. -- * TDX guest requests MSR accesses by calling TDVMCALL. The MSR -- * filters will be applied when handling the TDVMCALL for RDMSR/WRMSR -- * if the userspace has set any. -+ * TDX doesn't allow VMM to configure interception of instructions or -+ * MSR accesses. TDX guest requests MSR accesses by calling TDVMCALL. -+ * The MSR filters will be applied when handling the TDVMCALL for -+ * RDMSR/WRMSR if the userspace has set any. - */ - if (is_td_vcpu(vcpu)) - return; - -- vmx_recalc_msr_intercepts(vcpu); -+ vmx_recalc_intercepts(vcpu); - } - - static int vt_complete_emulated_msr(struct kvm_vcpu *vcpu, int err) -@@ -995,7 +995,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = { - .apic_init_signal_blocked = vt_op(apic_init_signal_blocked), - .migrate_timers = vmx_migrate_timers, - -- .recalc_msr_intercepts = vt_op(recalc_msr_intercepts), -+ .recalc_intercepts = vt_op(recalc_intercepts), - .complete_emulated_msr = vt_op(complete_emulated_msr), - - .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 93b87f9e6dfd..2244ca074e9d 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4068,7 +4068,7 @@ void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) - } - } - --void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) -+static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - if (!cpu_has_vmx_msr_bitmap()) - return; -@@ -4121,6 +4121,11 @@ void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - */ - } - -+void vmx_recalc_intercepts(struct kvm_vcpu *vcpu) -+{ -+ vmx_recalc_msr_intercepts(vcpu); -+} -+ - static int vmx_deliver_nested_posted_interrupt(struct kvm_vcpu *vcpu, - int vector) - { -@@ -7778,7 +7783,7 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - ~FEAT_CTL_SGX_LC_ENABLED; - - /* Recalc MSR interception to account for feature changes. */ -- vmx_recalc_msr_intercepts(vcpu); -+ vmx_recalc_intercepts(vcpu); - - /* Refresh #PF interception to account for MAXPHYADDR changes. */ - vmx_update_exception_bitmap(vcpu); -diff --git a/arch/x86/kvm/vmx/x86_ops.h b/arch/x86/kvm/vmx/x86_ops.h -index 2b3424f638db..2c590ff44ced 100644 ---- a/arch/x86/kvm/vmx/x86_ops.h -+++ b/arch/x86/kvm/vmx/x86_ops.h -@@ -52,7 +52,7 @@ void vmx_deliver_interrupt(struct kvm_lapic *apic, int delivery_mode, - int trig_mode, int vector); - void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu); - bool vmx_has_emulated_msr(struct kvm *kvm, u32 index); --void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu); -+void vmx_recalc_intercepts(struct kvm_vcpu *vcpu); - void vmx_prepare_switch_to_guest(struct kvm_vcpu *vcpu); - void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu); - int vmx_get_feature_msr(u32 msr, u64 *data); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 2c34dd3f0222..69f5d9deb75f 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -6742,7 +6742,11 @@ static int kvm_vm_ioctl_set_msr_filter(struct kvm *kvm, - - kvm_free_msr_filter(old_filter); - -- kvm_make_all_cpus_request(kvm, KVM_REQ_MSR_FILTER_CHANGED); -+ /* -+ * Recalc MSR intercepts as userspace may want to intercept accesses to -+ * MSRs that KVM would otherwise pass through to the guest. -+ */ -+ kvm_make_all_cpus_request(kvm, KVM_REQ_RECALC_INTERCEPTS); - - return 0; - } -@@ -10765,13 +10769,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) - if (kvm_check_request(KVM_REQ_APF_READY, vcpu)) - kvm_check_async_pf_completion(vcpu); - -- /* -- * Recalc MSR intercepts as userspace may want to intercept -- * accesses to MSRs that KVM would otherwise pass through to -- * the guest. -- */ -- if (kvm_check_request(KVM_REQ_MSR_FILTER_CHANGED, vcpu)) -- kvm_x86_call(recalc_msr_intercepts)(vcpu); -+ if (kvm_check_request(KVM_REQ_RECALC_INTERCEPTS, vcpu)) -+ kvm_x86_call(recalc_intercepts)(vcpu); - - if (kvm_check_request(KVM_REQ_UPDATE_CPU_DIRTY_LOGGING, vcpu)) - kvm_x86_call(update_cpu_dirty_logging)(vcpu); --- -2.43.0 - diff --git a/SPECS/kernel/0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf b/SPECS/kernel/0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf deleted file mode 100644 index 63e6584ec..000000000 --- a/SPECS/kernel/0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf +++ /dev/null @@ -1,71 +0,0 @@ -From 940fa63d1e69037d664f6317434d6cce6cb2d7af Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 8 Jul 2025 16:00:22 -0700 -Subject: [PATCH 080/100] KVM: x86: Use KVM_REQ_RECALC_INTERCEPTS to react to - CPUID updates - -Defer recalculating MSR and instruction intercepts after a CPUID update -via RECALC_INTERCEPTS to converge on RECALC_INTERCEPTS as the "official" -mechanism for triggering recalcs. As a bonus, because KVM does a "recalc" -during vCPU creation, and every functional VMM sets CPUID at least once, -for all intents and purposes this saves at least one recalc. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/cpuid.c | 2 ++ - arch/x86/kvm/svm/svm.c | 4 +--- - arch/x86/kvm/vmx/vmx.c | 3 --- - 3 files changed, 3 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c -index e2836a255b16..cc16e28bfab2 100644 ---- a/arch/x86/kvm/cpuid.c -+++ b/arch/x86/kvm/cpuid.c -@@ -448,6 +448,8 @@ void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - * adjustments to the reserved GPA bits. - */ - kvm_mmu_after_set_cpuid(vcpu); -+ -+ kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); - } - - int cpuid_query_maxphyaddr(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 3d9dcc66a407..ef7dffc54dca 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -1225,7 +1225,7 @@ static void init_vmcb(struct kvm_vcpu *vcpu) - - svm_hv_init_vmcb(vmcb); - -- svm_recalc_intercepts(vcpu); -+ kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); - - vmcb_mark_all_dirty(vmcb); - -@@ -4478,8 +4478,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - - if (sev_guest(vcpu->kvm)) - sev_vcpu_after_set_cpuid(svm); -- -- svm_recalc_intercepts(vcpu); - } - - static bool svm_has_wbinvd_exit(void) -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 2244ca074e9d..6094de4855d6 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -7782,9 +7782,6 @@ void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) - vmx->msr_ia32_feature_control_valid_bits &= - ~FEAT_CTL_SGX_LC_ENABLED; - -- /* Recalc MSR interception to account for feature changes. */ -- vmx_recalc_intercepts(vcpu); -- - /* Refresh #PF interception to account for MAXPHYADDR changes. */ - vmx_update_exception_bitmap(vcpu); - } --- -2.43.0 - diff --git a/SPECS/kernel/0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf b/SPECS/kernel/0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf deleted file mode 100644 index a5e14d09c..000000000 --- a/SPECS/kernel/0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf +++ /dev/null @@ -1,75 +0,0 @@ -From b698b7cf5519ae476e4597381f0a9f75cb404162 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:30:59 +0000 -Subject: [PATCH 081/100] KVM: VMX: Add helpers to toggle/change a bit in VMCS - execution controls - -Expand the VMCS controls builder macros to generate helpers to change a -bit to the desired value, and use the new helpers when toggling APICv -related controls. - -No functional change intended. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -[sean: rewrite changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/vmx.c | 20 +++++++------------- - arch/x86/kvm/vmx/vmx.h | 8 ++++++++ - 2 files changed, 15 insertions(+), 13 deletions(-) - -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 6094de4855d6..baea4a9cf74f 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4356,19 +4356,13 @@ void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) - - pin_controls_set(vmx, vmx_pin_based_exec_ctrl(vmx)); - -- if (kvm_vcpu_apicv_active(vcpu)) { -- secondary_exec_controls_setbit(vmx, -- SECONDARY_EXEC_APIC_REGISTER_VIRT | -- SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); -- if (enable_ipiv) -- tertiary_exec_controls_setbit(vmx, TERTIARY_EXEC_IPI_VIRT); -- } else { -- secondary_exec_controls_clearbit(vmx, -- SECONDARY_EXEC_APIC_REGISTER_VIRT | -- SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); -- if (enable_ipiv) -- tertiary_exec_controls_clearbit(vmx, TERTIARY_EXEC_IPI_VIRT); -- } -+ secondary_exec_controls_changebit(vmx, -+ SECONDARY_EXEC_APIC_REGISTER_VIRT | -+ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY, -+ kvm_vcpu_apicv_active(vcpu)); -+ if (enable_ipiv) -+ tertiary_exec_controls_changebit(vmx, TERTIARY_EXEC_IPI_VIRT, -+ kvm_vcpu_apicv_active(vcpu)); - - vmx_update_msr_bitmap_x2apic(vcpu); - } -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index d3389baf3ab3..a4e5bcd1d023 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -608,6 +608,14 @@ static __always_inline void lname##_controls_clearbit(struct vcpu_vmx *vmx, u##b - { \ - BUILD_BUG_ON(!(val & (KVM_REQUIRED_VMX_##uname | KVM_OPTIONAL_VMX_##uname))); \ - lname##_controls_set(vmx, lname##_controls_get(vmx) & ~val); \ -+} \ -+static __always_inline void lname##_controls_changebit(struct vcpu_vmx *vmx, u##bits val, \ -+ bool set) \ -+{ \ -+ if (set) \ -+ lname##_controls_setbit(vmx, val); \ -+ else \ -+ lname##_controls_clearbit(vmx, val); \ - } - BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) - BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) --- -2.43.0 - diff --git a/SPECS/kernel/0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf b/SPECS/kernel/0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf deleted file mode 100644 index 9657d75b6..000000000 --- a/SPECS/kernel/0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf +++ /dev/null @@ -1,130 +0,0 @@ -From 0930af415779b92c44a0dc2a9647cf1ba8a83852 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:00 +0000 -Subject: [PATCH 082/100] KVM: x86/pmu: Disable RDPMC interception for - compatible mediated vPMU - -Disable RDPMC interception for vCPUs with a mediated vPMU that is -compatible with the host PMU, i.e. that doesn't require KVM emulation of -RDPMC to honor the guest's vCPU model. With a mediated vPMU, all guest -state accessible via RDPMC is loaded into hardware while the guest is -running. - -Adust RDPMC interception only for non-TDX guests, as the TDX module is -responsible for managing RDPMC intercepts based on the TD configuration. - -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sandipan Das -Signed-off-by: Sandipan Das -Signed-off-by: Dapeng Mi -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 26 ++++++++++++++++++++++++++ - arch/x86/kvm/pmu.h | 1 + - arch/x86/kvm/svm/svm.c | 5 +++++ - arch/x86/kvm/vmx/vmx.c | 7 +++++++ - arch/x86/kvm/x86.c | 1 + - 5 files changed, 40 insertions(+) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 680523e9d11e..674f42d083a9 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -712,6 +712,32 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned idx, u64 *data) - return 0; - } - -+bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu)) -+ return true; -+ -+ /* -+ * VMware allows access to these Pseduo-PMCs even when read via RDPMC -+ * in Ring3 when CR4.PCE=0. -+ */ -+ if (enable_vmware_backdoor) -+ return true; -+ -+ /* -+ * Note! Check *host* PMU capabilities, not KVM's PMU capabilities, as -+ * KVM's capabilities are constrained based on KVM support, i.e. KVM's -+ * capabilities themselves may be a subset of hardware capabilities. -+ */ -+ return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp || -+ pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed || -+ pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) || -+ pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1); -+} -+EXPORT_SYMBOL_GPL(kvm_need_rdpmc_intercept); -+ - void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu) - { - if (lapic_in_kernel(vcpu)) { -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 9849c2bb720d..506c203587ea 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -238,6 +238,7 @@ void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu); - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); -+bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu); - - extern struct kvm_pmu_ops intel_pmu_ops; - extern struct kvm_pmu_ops amd_pmu_ops; -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index ef7dffc54dca..2d42962b47aa 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -1075,6 +1075,11 @@ static void svm_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) - svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK; - } - } -+ -+ if (kvm_need_rdpmc_intercept(vcpu)) -+ svm_set_intercept(svm, INTERCEPT_RDPMC); -+ else -+ svm_clr_intercept(svm, INTERCEPT_RDPMC); - } - - static void svm_recalc_intercepts(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index baea4a9cf74f..2f7db32710e3 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4121,8 +4121,15 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - */ - } - -+static void vmx_recalc_instruction_intercepts(struct kvm_vcpu *vcpu) -+{ -+ exec_controls_changebit(to_vmx(vcpu), CPU_BASED_RDPMC_EXITING, -+ kvm_need_rdpmc_intercept(vcpu)); -+} -+ - void vmx_recalc_intercepts(struct kvm_vcpu *vcpu) - { -+ vmx_recalc_instruction_intercepts(vcpu); - vmx_recalc_msr_intercepts(vcpu); - } - -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 69f5d9deb75f..b8014435c988 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -3793,6 +3793,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - - vcpu->arch.perf_capabilities = data; - kvm_pmu_refresh(vcpu); -+ kvm_make_request(KVM_REQ_RECALC_INTERCEPTS, vcpu); - break; - case MSR_IA32_PRED_CMD: { - u64 reserved_bits = ~(PRED_CMD_IBPB | PRED_CMD_SBPB); --- -2.43.0 - diff --git a/SPECS/kernel/0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf b/SPECS/kernel/0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf deleted file mode 100644 index a5b3371cc..000000000 --- a/SPECS/kernel/0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf +++ /dev/null @@ -1,276 +0,0 @@ -From 64c78cd9fec12e5974afe503733aff6edb1e6b02 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:01 +0000 -Subject: [PATCH 083/100] KVM: x86/pmu: Load/save GLOBAL_CTRL via entry/exit - fields for mediated PMU - -When running a guest with a mediated PMU, context switch PERF_GLOBAL_CTRL -via the dedicated VMCS fields for both host and guest. For the host, -always zero GLOBAL_CTRL on exit as the guest's state will still be loaded -in hardware (KVM will context switch the bulk of PMU state outside of the -inner run loop). For the guest, use the dedicated fields to atomically -load and save PERF_GLOBAL_CTRL on all entry/exits. - -Note, VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL was introduced by Sapphire -Rapids, and is expected to be supported on all CPUs with PMU v4+. WARN if -that expectation is not met. Alternatively, KVM could manually save -PERF_GLOBAL_CTRL via the MSR save list, but the associated complexity and -runtime overhead is unjustified given that the feature should always be -available on relevant CPUs. - -To minimize VM-Entry latency, propagate IA32_PERF_GLOBAL_CTRL to the VMCS -on-demand. But to minimize complexity, read IA32_PERF_GLOBAL_CTRL out of -the VMCS on all non-failing VM-Exits. I.e. partially cache the MSR. -KVM could track GLOBAL_CTRL as an EXREG and defer all reads, but writes -are rare, i.e. the dirty tracking for an EXREG is unnecessary, and it's -not obvious that shaving ~15-20 cycles per exit is meaningful given the -total overhead associated with mediated PMU context switches. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm-x86-pmu-ops.h | 2 ++ - arch/x86/include/asm/vmx.h | 1 + - arch/x86/kvm/pmu.c | 13 +++++++++-- - arch/x86/kvm/pmu.h | 3 ++- - arch/x86/kvm/vmx/capabilities.h | 5 +++++ - arch/x86/kvm/vmx/pmu_intel.c | 19 +++++++++++++++- - arch/x86/kvm/vmx/vmx.c | 31 +++++++++++++++++++++++++- - arch/x86/kvm/vmx/vmx.h | 3 ++- - 8 files changed, 71 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h -index 9159bf1a4730..ad2cc82abf79 100644 ---- a/arch/x86/include/asm/kvm-x86-pmu-ops.h -+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h -@@ -23,5 +23,7 @@ KVM_X86_PMU_OP_OPTIONAL(reset) - KVM_X86_PMU_OP_OPTIONAL(deliver_pmi) - KVM_X86_PMU_OP_OPTIONAL(cleanup) - -+KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl) -+ - #undef KVM_X86_PMU_OP - #undef KVM_X86_PMU_OP_OPTIONAL -diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h -index cca7d6641287..af71666c3a37 100644 ---- a/arch/x86/include/asm/vmx.h -+++ b/arch/x86/include/asm/vmx.h -@@ -106,6 +106,7 @@ - #define VM_EXIT_CLEAR_BNDCFGS 0x00800000 - #define VM_EXIT_PT_CONCEAL_PIP 0x01000000 - #define VM_EXIT_CLEAR_IA32_RTIT_CTL 0x02000000 -+#define VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL 0x40000000 - - #define VM_EXIT_ALWAYSON_WITHOUT_TRUE_MSR 0x00036dff - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 674f42d083a9..a4fe0e76df79 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -103,7 +103,7 @@ void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops) - #undef __KVM_X86_PMU_OP - } - --void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) -+void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops) - { - bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; - int min_nr_gp_ctrs = pmu_ops->MIN_NR_GP_COUNTERS; -@@ -137,6 +137,9 @@ void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops) - !pmu_ops->is_mediated_pmu_supported(&kvm_host_pmu)) - enable_mediated_pmu = false; - -+ if (!enable_mediated_pmu) -+ pmu_ops->write_global_ctrl = NULL; -+ - if (!enable_pmu) { - memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); - return; -@@ -831,6 +834,9 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - diff = pmu->global_ctrl ^ data; - pmu->global_ctrl = data; - reprogram_counters(pmu, diff); -+ -+ if (kvm_vcpu_has_mediated_pmu(vcpu)) -+ kvm_pmu_call(write_global_ctrl)(data); - } - break; - case MSR_CORE_PERF_GLOBAL_OVF_CTRL: -@@ -921,8 +927,11 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu) - * in the global controls). Emulate that behavior when refreshing the - * PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL. - */ -- if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) -+ if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) { - pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0); -+ if (kvm_vcpu_has_mediated_pmu(vcpu)) -+ kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); -+ } - } - - void kvm_pmu_init(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 506c203587ea..2ff469334c1a 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -38,6 +38,7 @@ struct kvm_pmu_ops { - void (*cleanup)(struct kvm_vcpu *vcpu); - - bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); -+ void (*write_global_ctrl)(u64 global_ctrl); - - const u64 EVENTSEL_EVENT; - const int MAX_NR_GP_COUNTERS; -@@ -183,7 +184,7 @@ static inline bool pmc_is_locally_enabled(struct kvm_pmc *pmc) - - extern struct x86_pmu_capability kvm_pmu_cap; - --void kvm_init_pmu_capability(const struct kvm_pmu_ops *pmu_ops); -+void kvm_init_pmu_capability(struct kvm_pmu_ops *pmu_ops); - - void kvm_pmu_recalc_pmc_emulation(struct kvm_pmu *pmu, struct kvm_pmc *pmc); - -diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h -index 26ff606ff139..874c6dd34665 100644 ---- a/arch/x86/kvm/vmx/capabilities.h -+++ b/arch/x86/kvm/vmx/capabilities.h -@@ -100,6 +100,11 @@ static inline bool cpu_has_load_perf_global_ctrl(void) - return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; - } - -+static inline bool cpu_has_save_perf_global_ctrl(void) -+{ -+ return vmcs_config.vmexit_ctrl & VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL; -+} -+ - static inline bool cpu_has_vmx_mpx(void) - { - return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS; -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 5a9841d75117..79aad6355fa3 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -787,7 +787,23 @@ static bool intel_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_ - * Require v4+ for MSR_CORE_PERF_GLOBAL_STATUS_SET, and full-width - * writes so that KVM can precisely load guest counter values. - */ -- return host_pmu->version >= 4 && host_perf_cap & PERF_CAP_FW_WRITES; -+ if (host_pmu->version < 4 || !(host_perf_cap & PERF_CAP_FW_WRITES)) -+ return false; -+ -+ /* -+ * All CPUs that support a mediated PMU are expected to support loading -+ * and saving PERF_GLOBAL_CTRL via dedicated VMCS fields. -+ */ -+ if (WARN_ON_ONCE(!cpu_has_load_perf_global_ctrl() || -+ !cpu_has_save_perf_global_ctrl())) -+ return false; -+ -+ return true; -+} -+ -+static void intel_pmu_write_global_ctrl(u64 global_ctrl) -+{ -+ vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, global_ctrl); - } - - struct kvm_pmu_ops intel_pmu_ops __initdata = { -@@ -803,6 +819,7 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = { - .cleanup = intel_pmu_cleanup, - - .is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported, -+ .write_global_ctrl = intel_pmu_write_global_ctrl, - - .EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS, -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 2f7db32710e3..1233a0afb31e 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -4115,6 +4115,18 @@ static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - vmx_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, - !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); - -+ if (enable_mediated_pmu) { -+ bool is_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu); -+ struct vcpu_vmx *vmx = to_vmx(vcpu); -+ -+ vm_entry_controls_changebit(vmx, -+ VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -+ -+ vm_exit_controls_changebit(vmx, -+ VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -+ } -+ - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be - * filtered by userspace. -@@ -4282,6 +4294,16 @@ void vmx_set_constant_host_state(struct vcpu_vmx *vmx) - - if (cpu_has_load_ia32_efer()) - vmcs_write64(HOST_IA32_EFER, kvm_host.efer); -+ -+ /* -+ * When running a guest with a mediated PMU, guest state is resident in -+ * hardware after VM-Exit. Zero PERF_GLOBAL_CTRL on exit so that host -+ * activity doesn't bleed into the guest counters. When running with -+ * an emulated PMU, PERF_GLOBAL_CTRL is dynamically computed on every -+ * entry/exit to merge guest and host PMU usage. -+ */ -+ if (enable_mediated_pmu) -+ vmcs_write64(HOST_IA32_PERF_GLOBAL_CTRL, 0); - } - - void set_cr4_guest_host_mask(struct vcpu_vmx *vmx) -@@ -4349,7 +4371,8 @@ static u32 vmx_get_initial_vmexit_ctrl(void) - VM_EXIT_CLEAR_IA32_RTIT_CTL); - /* Loading of EFER and PERF_GLOBAL_CTRL are toggled dynamically */ - return vmexit_ctrl & -- ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER); -+ ~(VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_LOAD_IA32_EFER | -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL); - } - - void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) -@@ -7087,6 +7110,9 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx) - struct perf_guest_switch_msr *msrs; - struct kvm_pmu *pmu = vcpu_to_pmu(&vmx->vcpu); - -+ if (kvm_vcpu_has_mediated_pmu(&vmx->vcpu)) -+ return; -+ - pmu->host_cross_mapped_mask = 0; - if (pmu->pebs_enable & pmu->global_ctrl) - intel_pmu_cross_mapped_check(pmu); -@@ -7407,6 +7433,9 @@ fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) - - vmx->loaded_vmcs->launched = 1; - -+ if (!msr_write_intercepted(vmx, MSR_CORE_PERF_GLOBAL_CTRL)) -+ vcpu_to_pmu(vcpu)->global_ctrl = vmcs_read64(GUEST_IA32_PERF_GLOBAL_CTRL); -+ - vmx_recover_nmi_blocking(vmx); - vmx_complete_interrupts(vmx); - -diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h -index a4e5bcd1d023..7eb57f5cb975 100644 ---- a/arch/x86/kvm/vmx/vmx.h -+++ b/arch/x86/kvm/vmx/vmx.h -@@ -506,7 +506,8 @@ static inline u8 vmx_get_rvi(void) - VM_EXIT_LOAD_IA32_EFER | \ - VM_EXIT_CLEAR_BNDCFGS | \ - VM_EXIT_PT_CONCEAL_PIP | \ -- VM_EXIT_CLEAR_IA32_RTIT_CTL) -+ VM_EXIT_CLEAR_IA32_RTIT_CTL | \ -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL) - - #define KVM_REQUIRED_VMX_PIN_BASED_VM_EXEC_CONTROL \ - (PIN_BASED_EXT_INTR_MASK | \ --- -2.43.0 - diff --git a/SPECS/kernel/0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf b/SPECS/kernel/0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf deleted file mode 100644 index 58169c51d..000000000 --- a/SPECS/kernel/0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf +++ /dev/null @@ -1,88 +0,0 @@ -From 009e272d43d7524424506e6939f14963bb4db36b Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Tue, 13 May 2025 13:53:29 -0700 -Subject: [PATCH 084/100] KVM: x86/pmu: Use BIT_ULL() instead of open coded - equivalents - -Replace a variety of "1ull << N" and "(u64)1 << N" snippets with BIT_ULL() -in the PMU code. - -No functional change intended. - -Signed-off-by: Dapeng Mi -[sean: split to separate patch, write changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 4 ++-- - arch/x86/kvm/vmx/pmu_intel.c | 15 ++++++--------- - 2 files changed, 8 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 96be2c3e0d65..b777c3743304 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -199,11 +199,11 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.num_counters_gp); - - if (pmu->version > 1) { -- pmu->global_ctrl_rsvd = ~((1ull << pmu->nr_arch_gp_counters) - 1); -+ pmu->global_ctrl_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1); - pmu->global_status_rsvd = pmu->global_ctrl_rsvd; - } - -- pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << 48) - 1; -+ pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(48) - 1; - pmu->reserved_bits = 0xfffffff000280000ull; - pmu->raw_event_mask = AMD64_RAW_EVENT_MASK; - /* not applicable to AMD; but clean them to prevent any fall out */ -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 79aad6355fa3..7592a5fb7675 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -536,11 +536,10 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.num_counters_gp); - eax.split.bit_width = min_t(int, eax.split.bit_width, - kvm_pmu_cap.bit_width_gp); -- pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1; -+ pmu->counter_bitmask[KVM_PMC_GP] = BIT_ULL(eax.split.bit_width) - 1; - eax.split.mask_length = min_t(int, eax.split.mask_length, - kvm_pmu_cap.events_mask_len); -- pmu->available_event_types = ~entry->ebx & -- ((1ull << eax.split.mask_length) - 1); -+ pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1); - - if (pmu->version == 1) { - pmu->nr_arch_fixed_counters = 0; -@@ -549,16 +548,15 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.num_counters_fixed); - edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, - kvm_pmu_cap.bit_width_fixed); -- pmu->counter_bitmask[KVM_PMC_FIXED] = -- ((u64)1 << edx.split.bit_width_fixed) - 1; -+ pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1; - } - - intel_pmu_enable_fixed_counter_bits(pmu, INTEL_FIXED_0_KERNEL | - INTEL_FIXED_0_USER | - INTEL_FIXED_0_ENABLE_PMI); - -- counter_rsvd = ~(((1ull << pmu->nr_arch_gp_counters) - 1) | -- (((1ull << pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX)); -+ counter_rsvd = ~((BIT_ULL(pmu->nr_arch_gp_counters) - 1) | -+ ((BIT_ULL(pmu->nr_arch_fixed_counters) - 1) << KVM_FIXED_PMC_BASE_IDX)); - pmu->global_ctrl_rsvd = counter_rsvd; - - /* -@@ -603,8 +601,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - pmu->pebs_data_cfg_rsvd = ~0xff00000full; - intel_pmu_enable_fixed_counter_bits(pmu, ICL_FIXED_0_ADAPTIVE); - } else { -- pmu->pebs_enable_rsvd = -- ~((1ull << pmu->nr_arch_gp_counters) - 1); -+ pmu->pebs_enable_rsvd = ~(BIT_ULL(pmu->nr_arch_gp_counters) - 1); - } - } - } --- -2.43.0 - diff --git a/SPECS/kernel/0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf b/SPECS/kernel/0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf deleted file mode 100644 index 6f4433b7e..000000000 --- a/SPECS/kernel/0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf +++ /dev/null @@ -1,65 +0,0 @@ -From 69ff2f2b422e4fb4fbc4b0e80078a5011faf0c5c Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 14:21:22 -0700 -Subject: [PATCH 085/100] KVM: x86/pmu: Move initialization of valid PMCs - bitmask to common x86 - -Move all initialization of all_valid_pmc_idx to common code, as the logic -is 100% common to Intel and AMD, and KVM heavily relies on Intel and AMD -having the same semantics. E.g. the fact that AMD doesn't support fixed -counters doesn't allow KVM to use all_valid_pmc_idx[63:32] for other -purposes. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 4 ++++ - arch/x86/kvm/svm/pmu.c | 1 - - arch/x86/kvm/vmx/pmu_intel.c | 5 ----- - 3 files changed, 4 insertions(+), 6 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index a4fe0e76df79..4246e1d2cfcc 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -932,6 +932,10 @@ void kvm_pmu_refresh(struct kvm_vcpu *vcpu) - if (kvm_vcpu_has_mediated_pmu(vcpu)) - kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); - } -+ -+ bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); -+ bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX, -+ pmu->nr_arch_fixed_counters); - } - - void kvm_pmu_init(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index b777c3743304..9ffd44a5d474 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -209,7 +209,6 @@ static void amd_pmu_refresh(struct kvm_vcpu *vcpu) - /* not applicable to AMD; but clean them to prevent any fall out */ - pmu->counter_bitmask[KVM_PMC_FIXED] = 0; - pmu->nr_arch_fixed_counters = 0; -- bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); - } - - static void amd_pmu_init(struct kvm_vcpu *vcpu) -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 7592a5fb7675..3ca8859b8091 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -579,11 +579,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED); - } - -- bitmap_set(pmu->all_valid_pmc_idx, -- 0, pmu->nr_arch_gp_counters); -- bitmap_set(pmu->all_valid_pmc_idx, -- INTEL_PMC_MAX_GENERIC, pmu->nr_arch_fixed_counters); -- - perf_capabilities = vcpu_get_perf_capabilities(vcpu); - if (intel_pmu_lbr_is_compatible(vcpu) && - (perf_capabilities & PERF_CAP_LBR_FMT)) --- -2.43.0 - diff --git a/SPECS/kernel/0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf b/SPECS/kernel/0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf deleted file mode 100644 index 61c0c8a3b..000000000 --- a/SPECS/kernel/0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf +++ /dev/null @@ -1,91 +0,0 @@ -From 267ad7a219b29998a24f05cfff06ea6b95b4808b Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 13 May 2025 14:30:55 -0700 -Subject: [PATCH 086/100] KVM: x86/pmu: Restrict GLOBAL_{CTRL,STATUS}, fixed - PMCs, and PEBS to PMU v2+ - -Restrict support for GLOBAL_CTRL, GLOBAL_STATUS, fixed PMCs, and PEBS to -v2 or later vPMUs. The SDM explicitly states that GLOBAL_{CTRL,STATUS} and -fixed counters were introduced with PMU v2, and PEBS has hard dependencies -on fixed counters and the bitmap MSR layouts established by PMU v2. - -Reported-by: Dapeng Mi -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/pmu_intel.c | 51 ++++++++++++++++++------------------ - 1 file changed, 25 insertions(+), 26 deletions(-) - -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 3ca8859b8091..04db3e0f0f2d 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -541,16 +541,33 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - kvm_pmu_cap.events_mask_len); - pmu->available_event_types = ~entry->ebx & (BIT_ULL(eax.split.mask_length) - 1); - -- if (pmu->version == 1) { -- pmu->nr_arch_fixed_counters = 0; -- } else { -- pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed, -- kvm_pmu_cap.num_counters_fixed); -- edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, -- kvm_pmu_cap.bit_width_fixed); -- pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1; -+ entry = kvm_find_cpuid_entry_index(vcpu, 7, 0); -+ if (entry && -+ (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) && -+ (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) { -+ pmu->reserved_bits ^= HSW_IN_TX; -+ pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED); - } - -+ perf_capabilities = vcpu_get_perf_capabilities(vcpu); -+ if (intel_pmu_lbr_is_compatible(vcpu) && -+ (perf_capabilities & PERF_CAP_LBR_FMT)) -+ memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps)); -+ else -+ lbr_desc->records.nr = 0; -+ -+ if (lbr_desc->records.nr) -+ bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1); -+ -+ if (pmu->version == 1) -+ return; -+ -+ pmu->nr_arch_fixed_counters = min_t(int, edx.split.num_counters_fixed, -+ kvm_pmu_cap.num_counters_fixed); -+ edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, -+ kvm_pmu_cap.bit_width_fixed); -+ pmu->counter_bitmask[KVM_PMC_FIXED] = BIT_ULL(edx.split.bit_width_fixed) - 1; -+ - intel_pmu_enable_fixed_counter_bits(pmu, INTEL_FIXED_0_KERNEL | - INTEL_FIXED_0_USER | - INTEL_FIXED_0_ENABLE_PMI); -@@ -571,24 +588,6 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) - pmu->global_status_rsvd &= - ~MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI; - -- entry = kvm_find_cpuid_entry_index(vcpu, 7, 0); -- if (entry && -- (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) && -- (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) { -- pmu->reserved_bits ^= HSW_IN_TX; -- pmu->raw_event_mask |= (HSW_IN_TX|HSW_IN_TX_CHECKPOINTED); -- } -- -- perf_capabilities = vcpu_get_perf_capabilities(vcpu); -- if (intel_pmu_lbr_is_compatible(vcpu) && -- (perf_capabilities & PERF_CAP_LBR_FMT)) -- memcpy(&lbr_desc->records, &vmx_lbr_caps, sizeof(vmx_lbr_caps)); -- else -- lbr_desc->records.nr = 0; -- -- if (lbr_desc->records.nr) -- bitmap_set(pmu->all_valid_pmc_idx, INTEL_PMC_IDX_FIXED_VLBR, 1); -- - if (perf_capabilities & PERF_CAP_PEBS_FORMAT) { - if (perf_capabilities & PERF_CAP_PEBS_BASELINE) { - pmu->pebs_enable_rsvd = counter_rsvd; --- -2.43.0 - diff --git a/SPECS/kernel/0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf b/SPECS/kernel/0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf deleted file mode 100644 index 6ee85ade1..000000000 --- a/SPECS/kernel/0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf +++ /dev/null @@ -1,296 +0,0 @@ -From ec95386c5f989f70ace870007dbba0237ad17312 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:03 +0000 -Subject: [PATCH 087/100] KVM: x86/pmu: Disable interception of select PMU MSRs - for mediated vPMUs - -For vCPUs with a mediated vPMU, disable interception of counter MSRs for -PMCs that are exposed to the guest, and for GLOBAL_CTRL and related MSRs -if they are fully supported according to the vCPU model, i.e. if the MSRs -and all bits supported by hardware exist from the guest's point of view. - -Do NOT passthrough event selector or fixed counter control MSRs, so that -KVM can enforce userspace-defined event filters, e.g. to prevent use of -AnyThread events (which is unfortunately a setting in the fixed counter -control MSR). - -Defer support for nested passthrough of mediated PMU MSRs to the future, -as the logic for nested MSR interception is unfortunately vendor specific. - -Suggested-by: Sean Christopherson -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sandipan Das -Signed-off-by: Sandipan Das -Signed-off-by: Dapeng Mi -[sean: squash patches, massage changelog, refresh VMX MSRs on filter change] -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/msr-index.h | 1 + - arch/x86/kvm/pmu.c | 34 ++++++++++++------ - arch/x86/kvm/pmu.h | 1 + - arch/x86/kvm/svm/svm.c | 36 +++++++++++++++++++ - arch/x86/kvm/vmx/pmu_intel.c | 13 ------- - arch/x86/kvm/vmx/pmu_intel.h | 15 ++++++++ - arch/x86/kvm/vmx/vmx.c | 59 ++++++++++++++++++++++++++------ - 7 files changed, 124 insertions(+), 35 deletions(-) - -Index: kernel-staging/arch/x86/kvm/pmu.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/pmu.c -+++ kernel-staging/arch/x86/kvm/pmu.c -@@ -715,18 +715,14 @@ int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, - return 0; - } - --bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu) -+bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu) - { - struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); - - if (!kvm_vcpu_has_mediated_pmu(vcpu)) - return true; - -- /* -- * VMware allows access to these Pseduo-PMCs even when read via RDPMC -- * in Ring3 when CR4.PCE=0. -- */ -- if (enable_vmware_backdoor) -+ if (!kvm_pmu_has_perf_global_ctrl(pmu)) - return true; - - /* -@@ -735,7 +731,22 @@ bool kvm_need_rdpmc_intercept(struct kvm - * capabilities themselves may be a subset of hardware capabilities. - */ - return pmu->nr_arch_gp_counters != kvm_host_pmu.num_counters_gp || -- pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed || -+ pmu->nr_arch_fixed_counters != kvm_host_pmu.num_counters_fixed; -+} -+EXPORT_SYMBOL_GPL(kvm_need_perf_global_ctrl_intercept); -+ -+bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ /* -+ * VMware allows access to these Pseduo-PMCs even when read via RDPMC -+ * in Ring3 when CR4.PCE=0. -+ */ -+ if (enable_vmware_backdoor) -+ return true; -+ -+ return kvm_need_perf_global_ctrl_intercept(vcpu) || - pmu->counter_bitmask[KVM_PMC_GP] != (BIT_ULL(kvm_host_pmu.bit_width_gp) - 1) || - pmu->counter_bitmask[KVM_PMC_FIXED] != (BIT_ULL(kvm_host_pmu.bit_width_fixed) - 1); - } -@@ -932,11 +943,12 @@ void kvm_pmu_refresh(struct kvm_vcpu *vc - * in the global controls). Emulate that behavior when refreshing the - * PMU so that userspace doesn't need to manually set PERF_GLOBAL_CTRL. - */ -- if (kvm_pmu_has_perf_global_ctrl(pmu) && pmu->nr_arch_gp_counters) { -+ if (pmu->nr_arch_gp_counters && -+ (kvm_pmu_has_perf_global_ctrl(pmu) || kvm_vcpu_has_mediated_pmu(vcpu))) - pmu->global_ctrl = GENMASK_ULL(pmu->nr_arch_gp_counters - 1, 0); -- if (kvm_vcpu_has_mediated_pmu(vcpu)) -- kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); -- } -+ -+ if (kvm_vcpu_has_mediated_pmu(vcpu)) -+ kvm_pmu_call(write_global_ctrl)(pmu->global_ctrl); - - bitmap_set(pmu->all_valid_pmc_idx, 0, pmu->nr_arch_gp_counters); - bitmap_set(pmu->all_valid_pmc_idx, KVM_FIXED_PMC_BASE_IDX, -Index: kernel-staging/arch/x86/kvm/pmu.h -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/pmu.h -+++ kernel-staging/arch/x86/kvm/pmu.h -@@ -239,6 +239,7 @@ void kvm_pmu_instruction_retired(struct - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); -+bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu); - bool kvm_need_rdpmc_intercept(struct kvm_vcpu *vcpu); - - extern struct kvm_pmu_ops intel_pmu_ops; -Index: kernel-staging/arch/x86/kvm/svm/svm.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/svm/svm.c -+++ kernel-staging/arch/x86/kvm/svm/svm.c -@@ -778,6 +778,40 @@ void svm_vcpu_free_msrpm(void *msrpm) - __free_pages(virt_to_page(msrpm), get_order(MSRPM_SIZE)); - } - -+static void svm_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) -+{ -+ bool intercept = !kvm_vcpu_has_mediated_pmu(vcpu); -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ int i; -+ -+ if (!enable_mediated_pmu) -+ return; -+ -+ /* Legacy counters are always available for AMD CPUs with a PMU. */ -+ for (i = 0; i < min(pmu->nr_arch_gp_counters, AMD64_NUM_COUNTERS); i++) -+ svm_set_intercept_for_msr(vcpu, MSR_K7_PERFCTR0 + i, -+ MSR_TYPE_RW, intercept); -+ -+ intercept |= !guest_cpu_cap_has(vcpu, X86_FEATURE_PERFCTR_CORE); -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) -+ svm_set_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i, -+ MSR_TYPE_RW, intercept); -+ -+ for ( ; i < kvm_pmu_cap.num_counters_gp; i++) -+ svm_enable_intercept_for_msr(vcpu, MSR_F15H_PERF_CTR + 2 * i, -+ MSR_TYPE_RW); -+ -+ intercept = kvm_need_perf_global_ctrl_intercept(vcpu); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL, -+ MSR_TYPE_RW, intercept); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, -+ MSR_TYPE_RW, intercept); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, -+ MSR_TYPE_RW, intercept); -+ svm_set_intercept_for_msr(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET, -+ MSR_TYPE_RW, intercept); -+} -+ - static void svm_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - struct vcpu_svm *svm = to_svm(vcpu); -@@ -835,6 +869,8 @@ static void svm_recalc_msr_intercepts(st - if (sev_es_guest(vcpu->kvm)) - sev_es_recalc_msr_intercepts(vcpu); - -+ svm_recalc_pmu_msr_intercepts(vcpu); -+ - /* - * x2APIC intercepts are modified on-demand and cannot be filtered by - * userspace. -Index: kernel-staging/arch/x86/kvm/vmx/pmu_intel.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/vmx/pmu_intel.c -+++ kernel-staging/arch/x86/kvm/vmx/pmu_intel.c -@@ -128,19 +128,6 @@ static struct kvm_pmc *intel_rdpmc_ecx_t - return &counters[array_index_nospec(idx, num_counters)]; - } - --static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu) --{ -- if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM)) -- return 0; -- -- return vcpu->arch.perf_capabilities; --} -- --static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu) --{ -- return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0; --} -- - static inline struct kvm_pmc *get_fw_gp_pmc(struct kvm_pmu *pmu, u32 msr) - { - if (!fw_writes_is_enabled(pmu_to_vcpu(pmu))) -Index: kernel-staging/arch/x86/kvm/vmx/pmu_intel.h -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/vmx/pmu_intel.h -+++ kernel-staging/arch/x86/kvm/vmx/pmu_intel.h -@@ -4,6 +4,21 @@ - - #include - -+#include "cpuid.h" -+ -+static inline u64 vcpu_get_perf_capabilities(struct kvm_vcpu *vcpu) -+{ -+ if (!guest_cpu_cap_has(vcpu, X86_FEATURE_PDCM)) -+ return 0; -+ -+ return vcpu->arch.perf_capabilities; -+} -+ -+static inline bool fw_writes_is_enabled(struct kvm_vcpu *vcpu) -+{ -+ return (vcpu_get_perf_capabilities(vcpu) & PERF_CAP_FW_WRITES) != 0; -+} -+ - bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu); - int intel_pmu_create_guest_lbr_event(struct kvm_vcpu *vcpu); - -Index: kernel-staging/arch/x86/kvm/vmx/vmx.c -=================================================================== ---- kernel-staging.orig/arch/x86/kvm/vmx/vmx.c -+++ kernel-staging/arch/x86/kvm/vmx/vmx.c -@@ -4068,6 +4068,53 @@ void pt_update_intercept_for_msr(struct - } - } - -+static void vmx_recalc_pmu_msr_intercepts(struct kvm_vcpu *vcpu) -+{ -+ bool has_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu); -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct vcpu_vmx *vmx = to_vmx(vcpu); -+ bool intercept = !has_mediated_pmu; -+ int i; -+ -+ if (!enable_mediated_pmu) -+ return; -+ -+ vm_entry_controls_changebit(vmx, VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, -+ has_mediated_pmu); -+ -+ vm_exit_controls_changebit(vmx, VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | -+ VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, -+ has_mediated_pmu); -+ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i, -+ MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, MSR_TYPE_RW, -+ intercept || !fw_writes_is_enabled(vcpu)); -+ } -+ for ( ; i < kvm_pmu_cap.num_counters_gp; i++) { -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PERFCTR0 + i, -+ MSR_TYPE_RW, true); -+ vmx_set_intercept_for_msr(vcpu, MSR_IA32_PMC0 + i, -+ MSR_TYPE_RW, true); -+ } -+ -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i, -+ MSR_TYPE_RW, intercept); -+ for ( ; i < kvm_pmu_cap.num_counters_fixed; i++) -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_FIXED_CTR0 + i, -+ MSR_TYPE_RW, true); -+ -+ intercept = kvm_need_perf_global_ctrl_intercept(vcpu); -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_STATUS, -+ MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, -+ MSR_TYPE_RW, intercept); -+ vmx_set_intercept_for_msr(vcpu, MSR_CORE_PERF_GLOBAL_OVF_CTRL, -+ MSR_TYPE_RW, intercept); -+} -+ - static void vmx_recalc_msr_intercepts(struct kvm_vcpu *vcpu) - { - if (!cpu_has_vmx_msr_bitmap()) -@@ -4115,17 +4162,7 @@ static void vmx_recalc_msr_intercepts(st - vmx_set_intercept_for_msr(vcpu, MSR_IA32_FLUSH_CMD, MSR_TYPE_W, - !guest_cpu_cap_has(vcpu, X86_FEATURE_FLUSH_L1D)); - -- if (enable_mediated_pmu) { -- bool is_mediated_pmu = kvm_vcpu_has_mediated_pmu(vcpu); -- struct vcpu_vmx *vmx = to_vmx(vcpu); -- -- vm_entry_controls_changebit(vmx, -- VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -- -- vm_exit_controls_changebit(vmx, -- VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | -- VM_EXIT_SAVE_IA32_PERF_GLOBAL_CTRL, is_mediated_pmu); -- } -+ vmx_recalc_pmu_msr_intercepts(vcpu); - - /* - * x2APIC and LBR MSR intercepts are modified on-demand and cannot be diff --git a/SPECS/kernel/0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf b/SPECS/kernel/0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf deleted file mode 100644 index 1903f1f63..000000000 --- a/SPECS/kernel/0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf +++ /dev/null @@ -1,55 +0,0 @@ -From d7cb98e68c98e985d87a26f79a892cbc787bec94 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:07 +0000 -Subject: [PATCH 088/100] KVM: x86/pmu: Bypass perf checks when emulating - mediated PMU counter accesses - -When emulating a PMC counter read or write for a mediated PMU, bypass the -perf checks and emulated_counter logic as the counters aren't proxied -through perf, i.e. pmc->counter always holds the guest's up-to-date value, -and thus there's no need to defer emulated overflow checks. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: split from event filtering change, write shortlog+changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 5 +++++ - arch/x86/kvm/pmu.h | 3 +++ - 2 files changed, 8 insertions(+) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 817ef852bdf9..082d2905882b 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -377,6 +377,11 @@ static void pmc_update_sample_period(struct kvm_pmc *pmc) - - void pmc_write_counter(struct kvm_pmc *pmc, u64 val) - { -+ if (kvm_vcpu_has_mediated_pmu(pmc->vcpu)) { -+ pmc->counter = val & pmc_bitmask(pmc); -+ return; -+ } -+ - /* - * Drop any unconsumed accumulated counts, the WRMSR is a write, not a - * read-modify-write. Adjust the counter value so that its value is -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 356b08e92bc9..9a199109d672 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -111,6 +111,9 @@ static inline u64 pmc_read_counter(struct kvm_pmc *pmc) - { - u64 counter, enabled, running; - -+ if (kvm_vcpu_has_mediated_pmu(pmc->vcpu)) -+ return pmc->counter & pmc_bitmask(pmc); -+ - counter = pmc->counter + pmc->emulated_counter; - - if (pmc->perf_event && !pmc->is_paused) --- -2.43.0 - diff --git a/SPECS/kernel/0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf b/SPECS/kernel/0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf deleted file mode 100644 index 87baa99ac..000000000 --- a/SPECS/kernel/0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf +++ /dev/null @@ -1,101 +0,0 @@ -From a9ca9d609170d9ee2f4954bf7b489b440d4cc6e0 Mon Sep 17 00:00:00 2001 -From: Mingwei Zhang -Date: Mon, 24 Mar 2025 17:31:06 +0000 -Subject: [PATCH 089/100] KVM: x86/pmu: Introduce eventsel_hw to prepare for - pmu event filtering - -Introduce eventsel_hw and fixed_ctr_ctrl_hw to store the actual HW value in -PMU event selector MSRs. In mediated PMU checks events before allowing the -event values written to the PMU MSRs. However, to match the HW behavior, -when PMU event checks fails, KVM should allow guest to read the value back. - -This essentially requires an extra variable to separate the guest requested -value from actual PMU MSR value. Note this only applies to event selectors. - -Signed-off-by: Mingwei Zhang -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 2 ++ - arch/x86/kvm/pmu.c | 7 +++++-- - arch/x86/kvm/svm/pmu.c | 1 + - arch/x86/kvm/vmx/pmu_intel.c | 2 ++ - 4 files changed, 10 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index ecd0634599ff..31b2bdbf8ea6 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -528,6 +528,7 @@ struct kvm_pmc { - */ - u64 emulated_counter; - u64 eventsel; -+ u64 eventsel_hw; - struct perf_event *perf_event; - struct kvm_vcpu *vcpu; - /* -@@ -556,6 +557,7 @@ struct kvm_pmu { - unsigned nr_arch_fixed_counters; - unsigned available_event_types; - u64 fixed_ctr_ctrl; -+ u64 fixed_ctr_ctrl_hw; - u64 fixed_ctr_ctrl_rsvd; - u64 global_ctrl; - u64 global_status; -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 082d2905882b..e39ae37f0280 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -890,11 +890,14 @@ static void kvm_pmu_reset(struct kvm_vcpu *vcpu) - pmc->counter = 0; - pmc->emulated_counter = 0; - -- if (pmc_is_gp(pmc)) -+ if (pmc_is_gp(pmc)) { - pmc->eventsel = 0; -+ pmc->eventsel_hw = 0; -+ } - } - -- pmu->fixed_ctr_ctrl = pmu->global_ctrl = pmu->global_status = 0; -+ pmu->fixed_ctr_ctrl = pmu->fixed_ctr_ctrl_hw = 0; -+ pmu->global_ctrl = pmu->global_status = 0; - - kvm_pmu_call(reset)(vcpu); - } -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 9ffd44a5d474..9641ef5d0dd7 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -165,6 +165,7 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - data &= ~pmu->reserved_bits; - if (data != pmc->eventsel) { - pmc->eventsel = data; -+ pmc->eventsel_hw = data; - kvm_pmu_request_counter_reprogram(pmc); - } - return 0; -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 8005a1728dec..6835b2fde047 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -61,6 +61,7 @@ static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data) - int i; - - pmu->fixed_ctr_ctrl = data; -+ pmu->fixed_ctr_ctrl_hw = data; - for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { - u8 new_ctrl = fixed_ctrl_field(data, i); - u8 old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i); -@@ -430,6 +431,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - - if (data != pmc->eventsel) { - pmc->eventsel = data; -+ pmc->eventsel_hw = data; - kvm_pmu_request_counter_reprogram(pmc); - } - break; --- -2.43.0 - diff --git a/SPECS/kernel/0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf b/SPECS/kernel/0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf deleted file mode 100644 index 39001fdf2..000000000 --- a/SPECS/kernel/0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf +++ /dev/null @@ -1,69 +0,0 @@ -From 43a7fefcd23f3e1430fbcb02863ba2f3f0ea026f Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:07 +0000 -Subject: [PATCH 090/100] KVM: x86/pmu: Reprogram mediated PMU event selectors - on event filter updates - -Refresh the event selectors that are programmed into hardware when a PMC -is "reprogrammed" for a mediated PMU, i.e. if userspace changes the PMU -event filters - -Note, KVM doesn't utilize the reprogramming infrastructure to handle -counter overflow for mediated PMUs, as there's no need to reprogram a -non-existent perf event. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -[sean: add a helper to document behavior, split patch and rewrite changelog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index e39ae37f0280..b4c6a7704a01 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -518,6 +518,25 @@ static bool pmc_is_event_allowed(struct kvm_pmc *pmc) - return is_fixed_event_allowed(filter, pmc->idx); - } - -+static void kvm_mediated_pmu_refresh_event_filter(struct kvm_pmc *pmc) -+{ -+ bool allowed = pmc_is_event_allowed(pmc); -+ struct kvm_pmu *pmu = pmc_to_pmu(pmc); -+ -+ if (pmc_is_gp(pmc)) { -+ pmc->eventsel_hw &= ~ARCH_PERFMON_EVENTSEL_ENABLE; -+ if (allowed) -+ pmc->eventsel_hw |= pmc->eventsel & -+ ARCH_PERFMON_EVENTSEL_ENABLE; -+ } else { -+ u64 mask = intel_fixed_bits_by_idx(pmc->idx - KVM_FIXED_PMC_BASE_IDX, 0xf); -+ -+ pmu->fixed_ctr_ctrl_hw &= ~mask; -+ if (allowed) -+ pmu->fixed_ctr_ctrl_hw |= pmu->fixed_ctr_ctrl & mask; -+ } -+} -+ - static int reprogram_counter(struct kvm_pmc *pmc) - { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); -@@ -526,6 +545,11 @@ static int reprogram_counter(struct kvm_pmc *pmc) - bool emulate_overflow; - u8 fixed_ctr_ctrl; - -+ if (kvm_vcpu_has_mediated_pmu(pmu_to_vcpu(pmu))) { -+ kvm_mediated_pmu_refresh_event_filter(pmc); -+ return 0; -+ } -+ - emulate_overflow = pmc_pause_counter(pmc); - - if (!pmc_is_globally_enabled(pmc) || !pmc_is_locally_enabled(pmc) || --- -2.43.0 - diff --git a/SPECS/kernel/0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf b/SPECS/kernel/0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf deleted file mode 100644 index 5de637397..000000000 --- a/SPECS/kernel/0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf +++ /dev/null @@ -1,43 +0,0 @@ -From 7554705da0e56ce2626c25add280522c2620f30e Mon Sep 17 00:00:00 2001 -From: Sandipan Das -Date: Mon, 24 Mar 2025 17:31:08 +0000 -Subject: [PATCH 091/100] KVM: x86/pmu: Always stuff GuestOnly=1,HostOnly=0 for - mediated PMCs on AMD - -On AMD platforms, there is no way to restore PerfCntrGlobalCtl at -VM-Entry or clear it at VM-Exit. Since the register states will be -restored before entering and saved after exiting guest context, the -counters can keep ticking and even overflow leading to chaos while -still in host context. - -To avoid this, intecept event selectors, which is already done by mediated -PMU. In addition, always set the GuestOnly bit and clear the HostOnly bit -for PMU selectors on AMD. Doing so allows the counters run only in guest -context even if their enable bits are still set after VM exit and before -host/guest PMU context switch. - -Signed-off-by: Sandipan Das -Signed-off-by: Mingwei Zhang -[sean: massage shortlog] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/pmu.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index 9641ef5d0dd7..a5e70a4e7647 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -165,7 +165,8 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) - data &= ~pmu->reserved_bits; - if (data != pmc->eventsel) { - pmc->eventsel = data; -- pmc->eventsel_hw = data; -+ pmc->eventsel_hw = (data & ~AMD64_EVENTSEL_HOSTONLY) | -+ AMD64_EVENTSEL_GUESTONLY; - kvm_pmu_request_counter_reprogram(pmc); - } - return 0; --- -2.43.0 - diff --git a/SPECS/kernel/0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf b/SPECS/kernel/0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf deleted file mode 100644 index 5fed52aa2..000000000 --- a/SPECS/kernel/0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf +++ /dev/null @@ -1,393 +0,0 @@ -From fd815369165deaa89dc8f6f7ad917205f6138859 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:09 +0000 -Subject: [PATCH 092/100] KVM: x86/pmu: Load/put mediated PMU context when - entering/exiting guest - -Implement the PMU "world switch" between host perf and guest mediated PMU. -When loading guest state, call into perf to switch from host to guest, and -then load guest state into hardware, and then reverse those actions when -putting guest state. - -On the KVM side, when loading guest state, zero PERF_GLOBAL_CTRL to ensure -all counters are disabled, then load selectors and counters, and finally -call into vendor code to load control/status information. While VMX and -SVM use different mechanisms to avoid counting host activity while guest -controls are loaded, both implementations require PERF_GLOBAL_CTRL to be -zeroed when the event selectors are in flux. - -When putting guest state, reverse the order, and save and zero controls -and status prior to saving+zeroing selectors and counters. Defer clearing -PERF_GLOBAL_CTRL to vendor code, as only SVM needs to manually clear the -MSR; VMX configures PERF_GLOBAL_CTRL to be atomically cleared by the CPU -on VM-Exit. - -Handle the difference in MSR layouts between Intel and AMD by communicating -the bases and stride via kvm_pmu_ops. Because KVM requires Intel v4 (and -full-width writes) and AMD v2, the MSRs to load/save are constant for a -given vendor, i.e. do not vary based on the guest PMU, and do not vary -based on host PMU (because KVM will simply disable mediated PMU support if -the necessary MSRs are unsupported). - -Except for retrieving the guest's PERF_GLOBAL_CTRL, which needs to be read -before invoking any fastpath handler (spoiler alert), perform the context -switch around KVM's inner run loop. State only needs to be synchronized -from hardware before KVM can access the software "caches". - -Note, VMX already grabs the guest's PERF_GLOBAL_CTRL immediately after -VM-Exit, as hardware saves value into the VMCS. - -Co-developed-by: Mingwei Zhang -Signed-off-by: Mingwei Zhang -Co-developed-by: Sandipan Das -Signed-off-by: Sandipan Das -Signed-off-by: Dapeng Mi -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm-x86-pmu-ops.h | 2 + - arch/x86/include/asm/msr-index.h | 1 + - arch/x86/kvm/pmu.c | 111 +++++++++++++++++++++++++ - arch/x86/kvm/pmu.h | 10 +++ - arch/x86/kvm/svm/pmu.c | 34 ++++++++ - arch/x86/kvm/svm/svm.c | 3 + - arch/x86/kvm/vmx/pmu_intel.c | 45 ++++++++++ - arch/x86/kvm/x86.c | 4 + - 8 files changed, 210 insertions(+) - -diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h -index ad2cc82abf79..f0aa6996811f 100644 ---- a/arch/x86/include/asm/kvm-x86-pmu-ops.h -+++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h -@@ -24,6 +24,8 @@ KVM_X86_PMU_OP_OPTIONAL(deliver_pmi) - KVM_X86_PMU_OP_OPTIONAL(cleanup) - - KVM_X86_PMU_OP_OPTIONAL(write_global_ctrl) -+KVM_X86_PMU_OP(mediated_load) -+KVM_X86_PMU_OP(mediated_put) - - #undef KVM_X86_PMU_OP - #undef KVM_X86_PMU_OP_OPTIONAL -diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h -index 156c9709a9d9..6b22abdc856a 100644 ---- a/arch/x86/include/asm/msr-index.h -+++ b/arch/x86/include/asm/msr-index.h -@@ -1205,6 +1205,7 @@ - #define MSR_CORE_PERF_GLOBAL_STATUS 0x0000038e - #define MSR_CORE_PERF_GLOBAL_CTRL 0x0000038f - #define MSR_CORE_PERF_GLOBAL_OVF_CTRL 0x00000390 -+#define MSR_CORE_PERF_GLOBAL_STATUS_SET 0x00000391 - - #define MSR_PERF_METRICS 0x00000329 - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index b4c6a7704a01..77042cad3155 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -1234,3 +1234,114 @@ int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp) - kfree(filter); - return r; - } -+ -+static __always_inline u32 fixed_counter_msr(u32 idx) -+{ -+ return kvm_pmu_ops.FIXED_COUNTER_BASE + idx * kvm_pmu_ops.MSR_STRIDE; -+} -+ -+static __always_inline u32 gp_counter_msr(u32 idx) -+{ -+ return kvm_pmu_ops.GP_COUNTER_BASE + idx * kvm_pmu_ops.MSR_STRIDE; -+} -+ -+static __always_inline u32 gp_eventsel_msr(u32 idx) -+{ -+ return kvm_pmu_ops.GP_EVENTSEL_BASE + idx * kvm_pmu_ops.MSR_STRIDE; -+} -+ -+static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct kvm_pmc *pmc; -+ u32 i; -+ -+ /* -+ * No need to zero out unexposed GP/fixed counters/selectors since RDPMC -+ * is intercepted if hardware has counters that aren't visible to the -+ * guest (KVM will inject #GP as appropriate). -+ */ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ pmc = &pmu->gp_counters[i]; -+ -+ wrmsrl(gp_counter_msr(i), pmc->counter); -+ wrmsrl(gp_eventsel_msr(i), pmc->eventsel_hw); -+ } -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { -+ pmc = &pmu->fixed_counters[i]; -+ -+ wrmsrl(fixed_counter_msr(i), pmc->counter); -+ } -+} -+ -+void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu) -+{ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu) || -+ KVM_BUG_ON(!lapic_in_kernel(vcpu), vcpu->kvm)) -+ return; -+ -+ lockdep_assert_irqs_disabled(); -+ -+ perf_load_guest_context(kvm_lapic_get_reg(vcpu->arch.apic, APIC_LVTPC)); -+ -+ /* -+ * Disable all counters before loading event selectors and PMCs so that -+ * KVM doesn't enable or load guest counters while host events are -+ * active. VMX will enable/disabled counters at VM-Enter/VM-Exit by -+ * atomically loading PERF_GLOBAL_CONTROL. SVM effectively performs -+ * the switch by configuring all events to be GUEST_ONLY. -+ */ -+ wrmsrl(kvm_pmu_ops.PERF_GLOBAL_CTRL, 0); -+ -+ kvm_pmu_load_guest_pmcs(vcpu); -+ -+ kvm_pmu_call(mediated_load)(vcpu); -+} -+ -+static void kvm_pmu_put_guest_pmcs(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct kvm_pmc *pmc; -+ u32 i; -+ -+ /* -+ * Clear selectors and counters to ensure hardware doesn't count using -+ * guest controls when the host (perf) restores its state. -+ */ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ pmc = &pmu->gp_counters[i]; -+ -+ pmc->counter = rdpmc(i); -+ if (pmc->counter) -+ wrmsrl(gp_counter_msr(i), 0); -+ if (pmc->eventsel_hw) -+ wrmsrl(gp_eventsel_msr(i), 0); -+ } -+ -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { -+ pmc = &pmu->fixed_counters[i]; -+ -+ pmc->counter = rdpmc(INTEL_PMC_FIXED_RDPMC_BASE | i); -+ if (pmc->counter) -+ wrmsrl(fixed_counter_msr(i), 0); -+ } -+} -+ -+void kvm_mediated_pmu_put(struct kvm_vcpu *vcpu) -+{ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu) || -+ KVM_BUG_ON(!lapic_in_kernel(vcpu), vcpu->kvm)) -+ return; -+ -+ lockdep_assert_irqs_disabled(); -+ -+ /* -+ * Defer handling of PERF_GLOBAL_CTRL to vendor code. On Intel, it's -+ * atomically cleared on VM-Exit, i.e. doesn't need to be clear here. -+ */ -+ kvm_pmu_call(mediated_put)(vcpu); -+ -+ kvm_pmu_put_guest_pmcs(vcpu); -+ -+ perf_put_guest_context(); -+} -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 9a199109d672..25b583da9ee2 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -38,11 +38,19 @@ struct kvm_pmu_ops { - void (*cleanup)(struct kvm_vcpu *vcpu); - - bool (*is_mediated_pmu_supported)(struct x86_pmu_capability *host_pmu); -+ void (*mediated_load)(struct kvm_vcpu *vcpu); -+ void (*mediated_put)(struct kvm_vcpu *vcpu); - void (*write_global_ctrl)(u64 global_ctrl); - - const u64 EVENTSEL_EVENT; - const int MAX_NR_GP_COUNTERS; - const int MIN_NR_GP_COUNTERS; -+ -+ const u32 PERF_GLOBAL_CTRL; -+ const u32 GP_EVENTSEL_BASE; -+ const u32 GP_COUNTER_BASE; -+ const u32 FIXED_COUNTER_BASE; -+ const u32 MSR_STRIDE; - }; - - void kvm_pmu_ops_update(const struct kvm_pmu_ops *pmu_ops); -@@ -240,6 +248,8 @@ void kvm_pmu_destroy(struct kvm_vcpu *vcpu); - int kvm_vm_ioctl_set_pmu_event_filter(struct kvm *kvm, void __user *argp); - void kvm_pmu_instruction_retired(struct kvm_vcpu *vcpu); - void kvm_pmu_branch_retired(struct kvm_vcpu *vcpu); -+void kvm_mediated_pmu_load(struct kvm_vcpu *vcpu); -+void kvm_mediated_pmu_put(struct kvm_vcpu *vcpu); - - bool is_vmware_backdoor_pmc(u32 pmc_idx); - bool kvm_need_perf_global_ctrl_intercept(struct kvm_vcpu *vcpu); -diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c -index a5e70a4e7647..c03720b30785 100644 ---- a/arch/x86/kvm/svm/pmu.c -+++ b/arch/x86/kvm/svm/pmu.c -@@ -233,6 +233,32 @@ static bool amd_pmu_is_mediated_pmu_supported(struct x86_pmu_capability *host_pm - return host_pmu->version >= 2; - } - -+static void amd_mediated_pmu_load(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ u64 global_status; -+ -+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, global_status); -+ /* Clear host global_status MSR if non-zero. */ -+ if (global_status) -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, global_status); -+ -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET, pmu->global_status); -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, pmu->global_ctrl); -+} -+ -+static void amd_mediated_pmu_put(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, 0); -+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, pmu->global_status); -+ -+ /* Clear global status bits if non-zero */ -+ if (pmu->global_status) -+ wrmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, pmu->global_status); -+} -+ - struct kvm_pmu_ops amd_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = amd_msr_idx_to_pmc, -@@ -244,8 +270,16 @@ struct kvm_pmu_ops amd_pmu_ops __initdata = { - .init = amd_pmu_init, - - .is_mediated_pmu_supported = amd_pmu_is_mediated_pmu_supported, -+ .mediated_load = amd_mediated_pmu_load, -+ .mediated_put = amd_mediated_pmu_put, - - .EVENTSEL_EVENT = AMD64_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_AMD_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = AMD64_NUM_COUNTERS, -+ -+ .PERF_GLOBAL_CTRL = MSR_AMD64_PERF_CNTR_GLOBAL_CTL, -+ .GP_EVENTSEL_BASE = MSR_F15H_PERF_CTL0, -+ .GP_COUNTER_BASE = MSR_F15H_PERF_CTR0, -+ .FIXED_COUNTER_BASE = 0, -+ .MSR_STRIDE = 2, - }; -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index add50b64256c..ca6f453cc160 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -4416,6 +4416,9 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags) - - vcpu->arch.regs_avail &= ~SVM_REGS_LAZY_LOAD_SET; - -+ if (!msr_write_intercepted(vcpu, MSR_AMD64_PERF_CNTR_GLOBAL_CTL)) -+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, vcpu_to_pmu(vcpu)->global_ctrl); -+ - /* - * We need to handle MC intercepts here before the vcpu has a chance to - * change the physical cpu -diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c -index 6835b2fde047..19fd6d359e59 100644 ---- a/arch/x86/kvm/vmx/pmu_intel.c -+++ b/arch/x86/kvm/vmx/pmu_intel.c -@@ -786,6 +786,43 @@ static void intel_pmu_write_global_ctrl(u64 global_ctrl) - vmcs_write64(GUEST_IA32_PERF_GLOBAL_CTRL, global_ctrl); - } - -+ -+static void intel_mediated_pmu_load(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ u64 global_status, toggle; -+ -+ rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, global_status); -+ toggle = pmu->global_status ^ global_status; -+ if (global_status & toggle) -+ wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, global_status & toggle); -+ if (pmu->global_status & toggle) -+ wrmsrq(MSR_CORE_PERF_GLOBAL_STATUS_SET, pmu->global_status & toggle); -+ -+ wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, pmu->fixed_ctr_ctrl_hw); -+} -+ -+static void intel_mediated_pmu_put(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ /* MSR_CORE_PERF_GLOBAL_CTRL is already saved at VM-exit. */ -+ rdmsrq(MSR_CORE_PERF_GLOBAL_STATUS, pmu->global_status); -+ -+ /* Clear hardware MSR_CORE_PERF_GLOBAL_STATUS MSR, if non-zero. */ -+ if (pmu->global_status) -+ wrmsrq(MSR_CORE_PERF_GLOBAL_OVF_CTRL, pmu->global_status); -+ -+ /* -+ * Clear hardware FIXED_CTR_CTRL MSR to avoid information leakage and -+ * also to avoid accidentally enabling fixed counters (based on guest -+ * state) while running in the host, e.g. when setting global ctrl. -+ */ -+ if (pmu->fixed_ctr_ctrl_hw) -+ wrmsrq(MSR_CORE_PERF_FIXED_CTR_CTRL, 0); -+} -+ -+ - struct kvm_pmu_ops intel_pmu_ops __initdata = { - .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc, - .msr_idx_to_pmc = intel_msr_idx_to_pmc, -@@ -799,9 +836,17 @@ struct kvm_pmu_ops intel_pmu_ops __initdata = { - .cleanup = intel_pmu_cleanup, - - .is_mediated_pmu_supported = intel_pmu_is_mediated_pmu_supported, -+ .mediated_load = intel_mediated_pmu_load, -+ .mediated_put = intel_mediated_pmu_put, - .write_global_ctrl = intel_pmu_write_global_ctrl, - - .EVENTSEL_EVENT = ARCH_PERFMON_EVENTSEL_EVENT, - .MAX_NR_GP_COUNTERS = KVM_MAX_NR_INTEL_GP_COUNTERS, - .MIN_NR_GP_COUNTERS = 1, -+ -+ .PERF_GLOBAL_CTRL = MSR_CORE_PERF_GLOBAL_CTRL, -+ .GP_EVENTSEL_BASE = MSR_P6_EVNTSEL0, -+ .GP_COUNTER_BASE = MSR_IA32_PMC0, -+ .FIXED_COUNTER_BASE = MSR_CORE_PERF_FIXED_CTR0, -+ .MSR_STRIDE = 1, - }; -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index b8014435c988..7fb94ef64e18 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -10906,6 +10906,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) - run_flags |= KVM_RUN_LOAD_DEBUGCTL; - vcpu->arch.host_debugctl = debug_ctl; - -+ kvm_mediated_pmu_load(vcpu); -+ - guest_timing_enter_irqoff(); - - for (;;) { -@@ -10936,6 +10938,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) - ++vcpu->stat.exits; - } - -+ kvm_mediated_pmu_put(vcpu); -+ - /* - * Do this here before restoring debug registers on the host. And - * since we do this before handling the vmexit, a DR access vmexit --- -2.43.0 - diff --git a/SPECS/kernel/0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf b/SPECS/kernel/0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf deleted file mode 100644 index bda48b72e..000000000 --- a/SPECS/kernel/0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf +++ /dev/null @@ -1,76 +0,0 @@ -From 5269e5df9fd7e611d8e30484f5ef9a06ebb4da67 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Wed, 30 Jul 2025 16:26:16 -0700 -Subject: [PATCH 093/100] KVM: x86/pmu: Disallow emulation in the fastpath if - mediated PMCs are active - -Don't handle exits in the fastpath if emulation is required, i.e. if an -instruction needs to be skipped, the mediated PMU is enabled, and one or -more PMCs is counting instructions. With the mediated PMU, KVM's cache of -PMU state is inconsistent with respect to hardware until KVM exits the -inner run loop (when the mediated PMU is "put"). - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.h | 10 ++++++++++ - arch/x86/kvm/x86.c | 9 +++++++++ - 2 files changed, 19 insertions(+) - -diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h -index 25b583da9ee2..0925246731cb 100644 ---- a/arch/x86/kvm/pmu.h -+++ b/arch/x86/kvm/pmu.h -@@ -234,6 +234,16 @@ static inline bool pmc_is_globally_enabled(struct kvm_pmc *pmc) - return test_bit(pmc->idx, (unsigned long *)&pmu->global_ctrl); - } - -+static inline bool kvm_pmu_is_fastpath_emulation_allowed(struct kvm_vcpu *vcpu) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ -+ return !kvm_vcpu_has_mediated_pmu(vcpu) || -+ !bitmap_intersects(pmu->pmc_counting_instructions, -+ (unsigned long *)&pmu->global_ctrl, -+ X86_PMC_IDX_MAX); -+} -+ - void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu); - void kvm_pmu_handle_event(struct kvm_vcpu *vcpu); - int kvm_pmu_rdpmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data); -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index 7fb94ef64e18..6bdf7ef0b535 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -2092,6 +2092,9 @@ EXPORT_SYMBOL_GPL(kvm_emulate_invd); - - fastpath_t handle_fastpath_invd(struct kvm_vcpu *vcpu) - { -+ if (!kvm_pmu_is_fastpath_emulation_allowed(vcpu)) -+ return EXIT_FASTPATH_NONE; -+ - if (!kvm_emulate_invd(vcpu)) - return EXIT_FASTPATH_EXIT_USERSPACE; - -@@ -2151,6 +2154,9 @@ fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu) - u64 data = kvm_read_edx_eax(vcpu); - u32 msr = kvm_rcx_read(vcpu); - -+ if (!kvm_pmu_is_fastpath_emulation_allowed(vcpu)) -+ return EXIT_FASTPATH_NONE; -+ - switch (msr) { - case APIC_BASE_MSR + (APIC_ICR >> 4): - if (!lapic_in_kernel(vcpu) || !apic_x2apic_mode(vcpu->arch.apic) || -@@ -11267,6 +11273,9 @@ EXPORT_SYMBOL_GPL(kvm_emulate_halt); - - fastpath_t handle_fastpath_hlt(struct kvm_vcpu *vcpu) - { -+ if (!kvm_pmu_is_fastpath_emulation_allowed(vcpu)) -+ return EXIT_FASTPATH_NONE; -+ - if (!kvm_emulate_halt(vcpu)) - return EXIT_FASTPATH_EXIT_USERSPACE; - --- -2.43.0 - diff --git a/SPECS/kernel/0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf b/SPECS/kernel/0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf deleted file mode 100644 index f21ff298f..000000000 --- a/SPECS/kernel/0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf +++ /dev/null @@ -1,75 +0,0 @@ -From bdba07134ecf40e31f5052fcaabbb9a596bd89d1 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:10 +0000 -Subject: [PATCH 094/100] KVM: x86/pmu: Handle emulated instruction for - mediated vPMU - -Mediated vPMU needs to accumulate the emulated instructions into counter -and load the counter into HW at vm-entry. - -Moreover, if the accumulation leads to counter overflow, KVM needs to -update GLOBAL_STATUS and inject PMI into guest as well. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 39 +++++++++++++++++++++++++++++++++++++-- - 1 file changed, 37 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index 77042cad3155..ddab1630a978 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -1018,10 +1018,45 @@ void kvm_pmu_destroy(struct kvm_vcpu *vcpu) - kvm_pmu_reset(vcpu); - } - -+static bool pmc_is_pmi_enabled(struct kvm_pmc *pmc) -+{ -+ u8 fixed_ctr_ctrl; -+ -+ if (pmc_is_gp(pmc)) -+ return pmc->eventsel & ARCH_PERFMON_EVENTSEL_INT; -+ -+ fixed_ctr_ctrl = fixed_ctrl_field(pmc_to_pmu(pmc)->fixed_ctr_ctrl, -+ pmc->idx - KVM_FIXED_PMC_BASE_IDX); -+ return fixed_ctr_ctrl & INTEL_FIXED_0_ENABLE_PMI; -+} -+ - static void kvm_pmu_incr_counter(struct kvm_pmc *pmc) - { -- pmc->emulated_counter++; -- kvm_pmu_request_counter_reprogram(pmc); -+ struct kvm_vcpu *vcpu = pmc->vcpu; -+ -+ /* -+ * For perf-based PMUs, accumulate software-emulated events separately -+ * from pmc->counter, as pmc->counter is offset by the count of the -+ * associated perf event. Request reprogramming, which will consult -+ * both emulated and hardware-generated events to detect overflow. -+ */ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu)) { -+ pmc->emulated_counter++; -+ kvm_pmu_request_counter_reprogram(pmc); -+ return; -+ } -+ -+ /* -+ * For mediated PMUs, pmc->counter is updated when the vCPU's PMU is -+ * put, and will be loaded into hardware when the PMU is loaded. Simply -+ * increment the counter and signal overflow if it wraps to zero. -+ */ -+ pmc->counter = (pmc->counter + 1) & pmc_bitmask(pmc); -+ if (!pmc->counter) { -+ pmc_to_pmu(pmc)->global_status |= BIT_ULL(pmc->idx); -+ if (pmc_is_pmi_enabled(pmc)) -+ kvm_make_request(KVM_REQ_PMI, vcpu); -+ } - } - - static inline bool cpl_is_matched(struct kvm_pmc *pmc) --- -2.43.0 - diff --git a/SPECS/kernel/0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf b/SPECS/kernel/0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf deleted file mode 100644 index 69a25d4a4..000000000 --- a/SPECS/kernel/0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf +++ /dev/null @@ -1,74 +0,0 @@ -From 8ce94c1009c52f79d3719ab1a62281a455ba6174 Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:11 +0000 -Subject: [PATCH 095/100] KVM: nVMX: Add macros to simplify nested MSR - interception setting - -Add macros nested_vmx_merge_msr_bitmaps_xxx() to simplify nested MSR -interception setting. No function change intended. - -Suggested-by: Sean Christopherson -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/nested.c | 35 +++++++++++++++++++---------------- - 1 file changed, 19 insertions(+), 16 deletions(-) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index db2fd4eedc90..47f1f0c7d3a7 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -614,6 +614,19 @@ static inline void nested_vmx_set_intercept_for_msr(struct vcpu_vmx *vmx, - msr_bitmap_l0, msr); - } - -+#define nested_vmx_merge_msr_bitmaps(msr, type) \ -+ nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, \ -+ msr_bitmap_l0, msr, type) -+ -+#define nested_vmx_merge_msr_bitmaps_read(msr) \ -+ nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_R) -+ -+#define nested_vmx_merge_msr_bitmaps_write(msr) \ -+ nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_W) -+ -+#define nested_vmx_merge_msr_bitmaps_rw(msr) \ -+ nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_RW) -+ - /* - * Merge L0's and L1's MSR bitmap, return false to indicate that - * we do not use the hardware. -@@ -697,23 +710,13 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - * other runtime changes to vmcs01's bitmap, e.g. dynamic pass-through. - */ - #ifdef CONFIG_X86_64 -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_FS_BASE, MSR_TYPE_RW); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_GS_BASE, MSR_TYPE_RW); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_KERNEL_GS_BASE, MSR_TYPE_RW); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_FS_BASE); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_GS_BASE); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_KERNEL_GS_BASE); - #endif -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_IA32_SPEC_CTRL, MSR_TYPE_RW); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_IA32_PRED_CMD, MSR_TYPE_W); -- -- nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, -- MSR_IA32_FLUSH_CMD, MSR_TYPE_W); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_SPEC_CTRL); -+ nested_vmx_merge_msr_bitmaps_write(MSR_IA32_PRED_CMD); -+ nested_vmx_merge_msr_bitmaps_write(MSR_IA32_FLUSH_CMD); - - nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, - MSR_IA32_APERF, MSR_TYPE_R); --- -2.43.0 - diff --git a/SPECS/kernel/0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf b/SPECS/kernel/0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf deleted file mode 100644 index 73e6f8cba..000000000 --- a/SPECS/kernel/0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf +++ /dev/null @@ -1,70 +0,0 @@ -From 9e783fa42339f5f089b6e4c9972d92066fd9175b Mon Sep 17 00:00:00 2001 -From: Mingwei Zhang -Date: Mon, 24 Mar 2025 17:31:12 +0000 -Subject: [PATCH 096/100] KVM: nVMX: Disable PMU MSR interception as - appropriate while running L2 - -Merge KVM's PMU MSR interception bitmaps with those of L1, i.e. merge the -bitmaps of vmcs01 and vmcs12, e.g. so that KVM doesn't interpose on MSR -accesses unnecessarily if L1 exposes a mediated PMU (or equivalent) to L2. - -Signed-off-by: Mingwei Zhang -Co-developed-by: Dapeng Mi -Signed-off-by: Dapeng Mi -[sean: rewrite changelog and comment, omit MSRs that are always intercepted] -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/vmx/nested.c | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) - -diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c -index 47f1f0c7d3a7..b986a6fb684c 100644 ---- a/arch/x86/kvm/vmx/nested.c -+++ b/arch/x86/kvm/vmx/nested.c -@@ -627,6 +627,34 @@ static inline void nested_vmx_set_intercept_for_msr(struct vcpu_vmx *vmx, - #define nested_vmx_merge_msr_bitmaps_rw(msr) \ - nested_vmx_merge_msr_bitmaps(msr, MSR_TYPE_RW) - -+static void nested_vmx_merge_pmu_msr_bitmaps(struct kvm_vcpu *vcpu, -+ unsigned long *msr_bitmap_l1, -+ unsigned long *msr_bitmap_l0) -+{ -+ struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); -+ struct vcpu_vmx *vmx = to_vmx(vcpu); -+ int i; -+ -+ /* -+ * Skip the merges if the vCPU doesn't have a mediated PMU MSR, i.e. if -+ * none of the MSRs can possibly be passed through to L1. -+ */ -+ if (!kvm_vcpu_has_mediated_pmu(vcpu)) -+ return; -+ -+ for (i = 0; i < pmu->nr_arch_gp_counters; i++) { -+ nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_PERFCTR0 + i); -+ nested_vmx_merge_msr_bitmaps_rw(MSR_IA32_PMC0 + i); -+ } -+ -+ for (i = 0; i < pmu->nr_arch_fixed_counters; i++) -+ nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_FIXED_CTR0 + i); -+ -+ nested_vmx_merge_msr_bitmaps_rw(MSR_CORE_PERF_GLOBAL_CTRL); -+ nested_vmx_merge_msr_bitmaps_read(MSR_CORE_PERF_GLOBAL_STATUS); -+ nested_vmx_merge_msr_bitmaps_write(MSR_CORE_PERF_GLOBAL_OVF_CTRL); -+} -+ - /* - * Merge L0's and L1's MSR bitmap, return false to indicate that - * we do not use the hardware. -@@ -724,6 +752,8 @@ static inline bool nested_vmx_prepare_msr_bitmap(struct kvm_vcpu *vcpu, - nested_vmx_set_intercept_for_msr(vmx, msr_bitmap_l1, msr_bitmap_l0, - MSR_IA32_MPERF, MSR_TYPE_R); - -+ nested_vmx_merge_pmu_msr_bitmaps(vcpu, msr_bitmap_l1, msr_bitmap_l0); -+ - kvm_vcpu_unmap(vcpu, &map); - - vmx->nested.force_msr_bitmap_recalc = false; --- -2.43.0 - diff --git a/SPECS/kernel/0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf b/SPECS/kernel/0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf deleted file mode 100644 index f64e1656d..000000000 --- a/SPECS/kernel/0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf +++ /dev/null @@ -1,56 +0,0 @@ -From 6a79b2c24060d637b19cee73dbb314b6a2b26d6f Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Tue, 5 Aug 2025 17:33:47 -0700 -Subject: [PATCH 097/100] KVM: nSVM: Disable PMU MSR interception as - appropriate while running L2 - -Add MSRs that might be passed through to L1 when running with a mediated -PMU to the nested SVM's set of to-be-merged MSR indices, i.e. disable -interception of PMU MSRs when running L2 if both KVM (L0) and L1 disable -interception. There is no need for KVM to interpose on such MSR accesses, -e.g. if L1 exposes a mediated PMU (or equivalent) to L2. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/svm/nested.c | 18 +++++++++++++++++- - 1 file changed, 17 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c -index b7fd2e869998..de2b9db2d0ba 100644 ---- a/arch/x86/kvm/svm/nested.c -+++ b/arch/x86/kvm/svm/nested.c -@@ -194,7 +194,7 @@ void recalc_intercepts(struct vcpu_svm *svm) - * Hardcode the capacity of the array based on the maximum number of _offsets_. - * MSRs are batched together, so there are fewer offsets than MSRs. - */ --static int nested_svm_msrpm_merge_offsets[7] __ro_after_init; -+static int nested_svm_msrpm_merge_offsets[10] __ro_after_init; - static int nested_svm_nr_msrpm_merge_offsets __ro_after_init; - typedef unsigned long nsvm_msrpm_merge_t; - -@@ -222,6 +222,22 @@ int __init nested_svm_init_msrpm_merge_offsets(void) - MSR_IA32_LASTBRANCHTOIP, - MSR_IA32_LASTINTFROMIP, - MSR_IA32_LASTINTTOIP, -+ -+ MSR_K7_PERFCTR0, -+ MSR_K7_PERFCTR1, -+ MSR_K7_PERFCTR2, -+ MSR_K7_PERFCTR3, -+ MSR_F15H_PERF_CTR0, -+ MSR_F15H_PERF_CTR1, -+ MSR_F15H_PERF_CTR2, -+ MSR_F15H_PERF_CTR3, -+ MSR_F15H_PERF_CTR4, -+ MSR_F15H_PERF_CTR5, -+ -+ MSR_AMD64_PERF_CNTR_GLOBAL_CTL, -+ MSR_AMD64_PERF_CNTR_GLOBAL_STATUS, -+ MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_CLR, -+ MSR_AMD64_PERF_CNTR_GLOBAL_STATUS_SET, - }; - int i, j; - --- -2.43.0 - diff --git a/SPECS/kernel/0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf b/SPECS/kernel/0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf deleted file mode 100644 index e8155afed..000000000 --- a/SPECS/kernel/0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf +++ /dev/null @@ -1,119 +0,0 @@ -From 73ca5e5868cb9cdd409a333e60deca6fd1f08ccc Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Mon, 24 Mar 2025 17:31:15 +0000 -Subject: [PATCH 098/100] KVM: x86/pmu: Expose enable_mediated_pmu parameter to - user space - -Expose enable_mediated_pmu parameter to user space, i.e. allow userspace -to enable/disable mediated vPMU support. - -Document the mediated versus perf-based behavior as part of the -kernel-parameters.txt entry, and opportunistically add an entry for the -core enable_pmu param as well. - -Signed-off-by: Dapeng Mi -Signed-off-by: Mingwei Zhang -Co-developed-by: Sean Christopherson -Signed-off-by: Sean Christopherson ---- - .../admin-guide/kernel-parameters.txt | 49 +++++++++++++++++++ - arch/x86/kvm/svm/svm.c | 2 + - arch/x86/kvm/vmx/vmx.c | 2 + - 3 files changed, 53 insertions(+) - -diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt -index 747a55abf494..b28b2daa8a4e 100644 ---- a/Documentation/admin-guide/kernel-parameters.txt -+++ b/Documentation/admin-guide/kernel-parameters.txt -@@ -2903,6 +2903,26 @@ - - Default is Y (on). - -+ kvm.enable_pmu=[KVM,X86] -+ If enabled, KVM will virtualize PMU functionality based -+ on the virtual CPU model defined by userspace. This -+ can be overridden on a per-VM basis via -+ KVM_CAP_PMU_CAPABILITY. -+ -+ If disabled, KVM will not virtualize PMU functionality, -+ e.g. MSRs, PMCs, PMIs, etc., even if userspace defines -+ a virtual CPU model that contains PMU assets. -+ -+ Note, KVM's vPMU support implicitly requires running -+ with an in-kernel local APIC, e.g. to deliver PMIs to -+ the guest. Running without an in-kernel local APIC is -+ not supported, though KVM will allow such a combination -+ (with severely degraded functionality). -+ -+ See also enable_mediated_pmu. -+ -+ Default is Y (on). -+ - kvm.enable_virt_at_load=[KVM,ARM64,LOONGARCH,MIPS,RISCV,X86] - If enabled, KVM will enable virtualization in hardware - when KVM is loaded, and disable virtualization when KVM -@@ -2949,6 +2969,35 @@ - If the value is 0 (the default), KVM will pick a period based - on the ratio, such that a page is zapped after 1 hour on average. - -+ kvm-{amd,intel}.enable_mediated_pmu=[KVM,AMD,INTEL] -+ If enabled, KVM will provide a mediated virtual PMU, -+ instead of the default perf-based virtual PMU (if -+ kvm.enable_pmu is true and PMU is enumerated via the -+ virtual CPU model). -+ -+ With a perf-based vPMU, KVM operates as a user of perf, -+ i.e. emulates guest PMU counters using perf events. -+ KVM-created perf events are managed by perf as regular -+ (guest-only) events, e.g. are scheduled in/out, contend -+ for hardware resources, etc. Using a perf-based vPMU -+ allows guest and host usage of the PMU to co-exist, but -+ incurs non-trivial overhead and can result in silently -+ dropped guest events (due to resource contention). -+ -+ With a mediated vPMU, hardware PMU state is context -+ switched around the world switch to/from the guest. -+ KVM mediates which events the guest can utilize, but -+ gives the guest direct access to all other PMU assets -+ when possible (KVM may intercept some accesses if the -+ virtual CPU model provides a subset of hardware PMU -+ functionality). Using a mediated vPMU significantly -+ reduces PMU virtualization overhead and eliminates lost -+ guest events, but is mutually exclusive with using perf -+ to profile KVM guests and adds latency to most VM-Exits -+ (to context switch PMU state). -+ -+ Default is N (off). -+ - kvm-amd.nested= [KVM,AMD] Control nested virtualization feature in - KVM/SVM. Default is 1 (enabled). - -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index ca6f453cc160..2797c3ab7854 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -178,6 +178,8 @@ module_param(intercept_smi, bool, 0444); - bool vnmi = true; - module_param(vnmi, bool, 0444); - -+module_param(enable_mediated_pmu, bool, 0444); -+ - static bool svm_gp_erratum_intercept = true; - - static u8 rsm_ins_bytes[] = "\x0f\xaa"; -diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c -index 85bd82d41f94..4a4691beba55 100644 ---- a/arch/x86/kvm/vmx/vmx.c -+++ b/arch/x86/kvm/vmx/vmx.c -@@ -151,6 +151,8 @@ module_param_named(preemption_timer, enable_preemption_timer, bool, S_IRUGO); - extern bool __read_mostly allow_smaller_maxphyaddr; - module_param(allow_smaller_maxphyaddr, bool, S_IRUGO); - -+module_param(enable_mediated_pmu, bool, 0444); -+ - #define KVM_VM_CR0_ALWAYS_OFF (X86_CR0_NW | X86_CR0_CD) - #define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST X86_CR0_NE - #define KVM_VM_CR0_ALWAYS_ON \ --- -2.43.0 - diff --git a/SPECS/kernel/0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf b/SPECS/kernel/0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf deleted file mode 100644 index 76f8287a9..000000000 --- a/SPECS/kernel/0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf +++ /dev/null @@ -1,47 +0,0 @@ -From a20f48706a03d7c6b606b37193caee67aa366b69 Mon Sep 17 00:00:00 2001 -From: Sean Christopherson -Date: Fri, 1 Aug 2025 10:21:34 -0700 -Subject: [PATCH 099/100] KVM: x86/pmu: Elide WRMSRs when loading guest PMCs if - values already match - -When loading a mediated PMU state, elide the WRMSRs to load PMCs with the -guest's value if the value in hardware already matches the guest's value. -For the relatively common case where neither the guest nor the host is -actively using the PMU, i.e. when all/many counters are '0', eliding the -WRMSRs reduces the latency of handling VM-Exit by a measurable amount -(WRMSR is significantly more expensive than RDPMC). - -As measured by KVM-Unit-Tests' CPUID VM-Exit testcase, this provides a -a ~25% reduction in latency (4k => 3k cycles) on Intel Emerald Rapids, -and a ~13% reduction (6.2k => 5.3k cycles) on AMD Turing. - -Signed-off-by: Sean Christopherson ---- - arch/x86/kvm/pmu.c | 6 ++++-- - 1 file changed, 4 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c -index ddab1630a978..0e5048ae86fa 100644 ---- a/arch/x86/kvm/pmu.c -+++ b/arch/x86/kvm/pmu.c -@@ -1299,13 +1299,15 @@ static void kvm_pmu_load_guest_pmcs(struct kvm_vcpu *vcpu) - for (i = 0; i < pmu->nr_arch_gp_counters; i++) { - pmc = &pmu->gp_counters[i]; - -- wrmsrl(gp_counter_msr(i), pmc->counter); -+ if (pmc->counter != rdpmc(i)) -+ wrmsrl(gp_counter_msr(i), pmc->counter); - wrmsrl(gp_eventsel_msr(i), pmc->eventsel_hw); - } - for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { - pmc = &pmu->fixed_counters[i]; - -- wrmsrl(fixed_counter_msr(i), pmc->counter); -+ if (pmc->counter != rdpmc(INTEL_PMC_FIXED_RDPMC_BASE | i)) -+ wrmsrl(fixed_counter_msr(i), pmc->counter); - } - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68254.patch b/SPECS/kernel/CVE-2025-68254.patch deleted file mode 100644 index 633f966ec..000000000 --- a/SPECS/kernel/CVE-2025-68254.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 2744a2ac9d9e08c0f04bf0b8b751dea2fac256c5 Mon Sep 17 00:00:00 2001 -From: Navaneeth K -Date: Thu, 20 Nov 2025 16:35:20 +0000 -Subject: [PATCH 11/16] staging: rtl8723bs: fix out-of-bounds read in OnBeacon - ESR IE parsing - -The Extended Supported Rates (ESR) IE handling in OnBeacon accessed -*(p + 1 + ielen) and *(p + 2 + ielen) without verifying that these -offsets lie within the received frame buffer. A malformed beacon with -an ESR IE positioned at the end of the buffer could cause an -out-of-bounds read, potentially triggering a kernel panic. - -Add a boundary check to ensure that the ESR IE body and the subsequent -bytes are within the limits of the frame before attempting to access -them. - -This prevents OOB reads caused by malformed beacon frames. - -Signed-off-by: Navaneeth K -Cc: stable -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -index 7d451cd5835a..32aa168bcb07 100644 ---- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -@@ -579,9 +579,11 @@ unsigned int OnBeacon(struct adapter *padapter, union recv_frame *precv_frame) - - p = rtw_get_ie(pframe + sizeof(struct ieee80211_hdr_3addr) + _BEACON_IE_OFFSET_, WLAN_EID_EXT_SUPP_RATES, &ielen, precv_frame->u.hdr.len - sizeof(struct ieee80211_hdr_3addr) - _BEACON_IE_OFFSET_); - if (p && ielen > 0) { -- if ((*(p + 1 + ielen) == 0x2D) && (*(p + 2 + ielen) != 0x2D)) -- /* Invalid value 0x2D is detected in Extended Supported Rates (ESR) IE. Try to fix the IE length to avoid failed Beacon parsing. */ -- *(p + 1) = ielen - 1; -+ if (p + 2 + ielen < pframe + len) { -+ if ((*(p + 1 + ielen) == 0x2D) && (*(p + 2 + ielen) != 0x2D)) -+ /* Invalid value 0x2D is detected in Extended Supported Rates (ESR) IE. Try to fix the IE length to avoid failed Beacon parsing. */ -+ *(p + 1) = ielen - 1; -+ } - } - - if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68255.patch b/SPECS/kernel/CVE-2025-68255.patch deleted file mode 100644 index 08a9183fa..000000000 --- a/SPECS/kernel/CVE-2025-68255.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 648f7276946334bcfa9025b6003649cc18a62bb5 Mon Sep 17 00:00:00 2001 -From: Navaneeth K -Date: Thu, 20 Nov 2025 16:33:08 +0000 -Subject: [PATCH 03/16] staging: rtl8723bs: fix stack buffer overflow in - OnAssocReq IE parsing - -The Supported Rates IE length from an incoming Association Request frame -was used directly as the memcpy() length when copying into a fixed-size -16-byte stack buffer (supportRate). A malicious station can advertise an -IE length larger than 16 bytes, causing a stack buffer overflow. - -Clamp ie_len to the buffer size before copying the Supported Rates IE, -and correct the bounds check when merging Extended Supported Rates to -prevent a second potential overflow. - -This prevents kernel stack corruption triggered by malformed association -requests. - -Signed-off-by: Navaneeth K -Cc: stable -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/rtl8723bs/core/rtw_mlme_ext.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -index bc980d21d50e..7d451cd5835a 100644 ---- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -+++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c -@@ -1033,6 +1033,9 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) - status = WLAN_STATUS_CHALLENGE_FAIL; - goto OnAssocReqFail; - } else { -+ if (ie_len > sizeof(supportRate)) -+ ie_len = sizeof(supportRate); -+ - memcpy(supportRate, p+2, ie_len); - supportRateNum = ie_len; - -@@ -1040,7 +1043,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) - pkt_len - WLAN_HDR_A3_LEN - ie_offset); - if (p) { - -- if (supportRateNum <= sizeof(supportRate)) { -+ if (supportRateNum + ie_len <= sizeof(supportRate)) { - memcpy(supportRate+supportRateNum, p+2, ie_len); - supportRateNum += ie_len; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68256.patch b/SPECS/kernel/CVE-2025-68256.patch deleted file mode 100644 index ea7f8bff3..000000000 --- a/SPECS/kernel/CVE-2025-68256.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 3cfddd5d4ae1dd9ad31ac72fa869504cc0e335b5 Mon Sep 17 00:00:00 2001 -From: Navaneeth K -Date: Thu, 20 Nov 2025 16:23:52 +0000 -Subject: [PATCH 04/16] staging: rtl8723bs: fix out-of-bounds read in - rtw_get_ie() parser - -The Information Element (IE) parser rtw_get_ie() trusted the length -byte of each IE without validating that the IE body (len bytes after -the 2-byte header) fits inside the remaining frame buffer. A malformed -frame can advertise an IE length larger than the available data, causing -the parser to increment its pointer beyond the buffer end. This results -in out-of-bounds reads or, depending on the pattern, an infinite loop. - -Fix by validating that (offset + 2 + len) does not exceed the limit -before accepting the IE or advancing to the next element. - -This prevents OOB reads and ensures the parser terminates safely on -malformed frames. - -Signed-off-by: Navaneeth K -Cc: stable -Signed-off-by: Greg Kroah-Hartman ---- - drivers/staging/rtl8723bs/core/rtw_ieee80211.c | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c -index 53d4c113b19c..df35c616e71f 100644 ---- a/drivers/staging/rtl8723bs/core/rtw_ieee80211.c -+++ b/drivers/staging/rtl8723bs/core/rtw_ieee80211.c -@@ -140,22 +140,24 @@ u8 *rtw_get_ie(u8 *pbuf, signed int index, signed int *len, signed int limit) - signed int tmp, i; - u8 *p; - -- if (limit < 1) -+ if (limit < 2) - return NULL; - - p = pbuf; - i = 0; - *len = 0; -- while (1) { -+ while (i + 2 <= limit) { -+ tmp = *(p + 1); -+ if (i + 2 + tmp > limit) -+ break; -+ - if (*p == index) { -- *len = *(p + 1); -+ *len = tmp; - return p; - } -- tmp = *(p + 1); -+ - p += (tmp + 2); - i += (tmp + 2); -- if (i >= limit) -- break; - } - return NULL; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68259.patch b/SPECS/kernel/CVE-2025-68259.patch deleted file mode 100644 index 8c27e1639..000000000 --- a/SPECS/kernel/CVE-2025-68259.patch +++ /dev/null @@ -1,203 +0,0 @@ -From 48ba05e336e086025ade1c23e913177fb4756e7d Mon Sep 17 00:00:00 2001 -From: Omar Sandoval -Date: Tue, 4 Nov 2025 09:55:26 -0800 -Subject: [PATCH 10/16] KVM: SVM: Don't skip unrelated instruction if INT3/INTO - is replaced - -When re-injecting a soft interrupt from an INT3, INT0, or (select) INTn -instruction, discard the exception and retry the instruction if the code -stream is changed (e.g. by a different vCPU) between when the CPU -executes the instruction and when KVM decodes the instruction to get the -next RIP. - -As effectively predicted by commit 6ef88d6e36c2 ("KVM: SVM: Re-inject -INT3/INTO instead of retrying the instruction"), failure to verify that -the correct INTn instruction was decoded can effectively clobber guest -state due to decoding the wrong instruction and thus specifying the -wrong next RIP. - -The bug most often manifests as "Oops: int3" panics on static branch -checks in Linux guests. Enabling or disabling a static branch in Linux -uses the kernel's "text poke" code patching mechanism. To modify code -while other CPUs may be executing that code, Linux (temporarily) -replaces the first byte of the original instruction with an int3 (opcode -0xcc), then patches in the new code stream except for the first byte, -and finally replaces the int3 with the first byte of the new code -stream. If a CPU hits the int3, i.e. executes the code while it's being -modified, then the guest kernel must look up the RIP to determine how to -handle the #BP, e.g. by emulating the new instruction. If the RIP is -incorrect, then this lookup fails and the guest kernel panics. - -The bug reproduces almost instantly by hacking the guest kernel to -repeatedly check a static branch[1] while running a drgn script[2] on -the host to constantly swap out the memory containing the guest's TSS. - -[1]: https://gist.github.com/osandov/44d17c51c28c0ac998ea0334edf90b5a -[2]: https://gist.github.com/osandov/10e45e45afa29b11e0c7209247afc00b - -Fixes: 6ef88d6e36c2 ("KVM: SVM: Re-inject INT3/INTO instead of retrying the instruction") -Cc: stable@vger.kernel.org -Co-developed-by: Sean Christopherson -Signed-off-by: Omar Sandoval -Link: https://patch.msgid.link/1cc6dcdf36e3add7ee7c8d90ad58414eeb6c3d34.1762278762.git.osandov@fb.com -Signed-off-by: Sean Christopherson ---- - arch/x86/include/asm/kvm_host.h | 9 +++++++++ - arch/x86/kvm/svm/svm.c | 24 +++++++++++++----------- - arch/x86/kvm/x86.c | 21 +++++++++++++++++++++ - 3 files changed, 43 insertions(+), 11 deletions(-) - -diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h -index 12e7dfb0e8c4..06f1b2cedaeb 100644 ---- a/arch/x86/include/asm/kvm_host.h -+++ b/arch/x86/include/asm/kvm_host.h -@@ -2135,6 +2135,11 @@ u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); - * the gfn, i.e. retrying the instruction will hit a - * !PRESENT fault, which results in a new shadow page - * and sends KVM back to square one. -+ * -+ * EMULTYPE_SKIP_SOFT_INT - Set in combination with EMULTYPE_SKIP to only skip -+ * an instruction if it could generate a given software -+ * interrupt, which must be encoded via -+ * EMULTYPE_SET_SOFT_INT_VECTOR(). - */ - #define EMULTYPE_NO_DECODE (1 << 0) - #define EMULTYPE_TRAP_UD (1 << 1) -@@ -2145,6 +2150,10 @@ u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); - #define EMULTYPE_PF (1 << 6) - #define EMULTYPE_COMPLETE_USER_EXIT (1 << 7) - #define EMULTYPE_WRITE_PF_TO_SP (1 << 8) -+#define EMULTYPE_SKIP_SOFT_INT (1 << 9) -+ -+#define EMULTYPE_SET_SOFT_INT_VECTOR(v) ((u32)((v) & 0xff) << 16) -+#define EMULTYPE_GET_SOFT_INT_VECTOR(e) (((e) >> 16) & 0xff) - - static inline bool kvm_can_emulate_event_vectoring(int emul_type) - { -diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c -index 4e97f51a4126..b85db1d65046 100644 ---- a/arch/x86/kvm/svm/svm.c -+++ b/arch/x86/kvm/svm/svm.c -@@ -282,6 +282,7 @@ static void svm_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) - } - - static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, -+ int emul_type, - bool commit_side_effects) - { - struct vcpu_svm *svm = to_svm(vcpu); -@@ -303,7 +304,7 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, - if (unlikely(!commit_side_effects)) - old_rflags = svm->vmcb->save.rflags; - -- if (!kvm_emulate_instruction(vcpu, EMULTYPE_SKIP)) -+ if (!kvm_emulate_instruction(vcpu, emul_type)) - return 0; - - if (unlikely(!commit_side_effects)) -@@ -321,11 +322,13 @@ static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, - - static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) - { -- return __svm_skip_emulated_instruction(vcpu, true); -+ return __svm_skip_emulated_instruction(vcpu, EMULTYPE_SKIP, true); - } - --static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu) -+static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu, u8 vector) - { -+ const int emul_type = EMULTYPE_SKIP | EMULTYPE_SKIP_SOFT_INT | -+ EMULTYPE_SET_SOFT_INT_VECTOR(vector); - unsigned long rip, old_rip = kvm_rip_read(vcpu); - struct vcpu_svm *svm = to_svm(vcpu); - -@@ -341,7 +344,7 @@ static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu) - * in use, the skip must not commit any side effects such as clearing - * the interrupt shadow or RFLAGS.RF. - */ -- if (!__svm_skip_emulated_instruction(vcpu, !nrips)) -+ if (!__svm_skip_emulated_instruction(vcpu, emul_type, !nrips)) - return -EIO; - - rip = kvm_rip_read(vcpu); -@@ -377,7 +380,7 @@ static void svm_inject_exception(struct kvm_vcpu *vcpu) - kvm_deliver_exception_payload(vcpu, ex); - - if (kvm_exception_is_soft(ex->vector) && -- svm_update_soft_interrupt_rip(vcpu)) -+ svm_update_soft_interrupt_rip(vcpu, ex->vector)) - return; - - svm->vmcb->control.event_inj = ex->vector -@@ -3705,11 +3708,12 @@ static bool svm_set_vnmi_pending(struct kvm_vcpu *vcpu) - - static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) - { -+ struct kvm_queued_interrupt *intr = &vcpu->arch.interrupt; - struct vcpu_svm *svm = to_svm(vcpu); - u32 type; - -- if (vcpu->arch.interrupt.soft) { -- if (svm_update_soft_interrupt_rip(vcpu)) -+ if (intr->soft) { -+ if (svm_update_soft_interrupt_rip(vcpu, intr->nr)) - return; - - type = SVM_EVTINJ_TYPE_SOFT; -@@ -3717,12 +3721,10 @@ static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) - type = SVM_EVTINJ_TYPE_INTR; - } - -- trace_kvm_inj_virq(vcpu->arch.interrupt.nr, -- vcpu->arch.interrupt.soft, reinjected); -+ trace_kvm_inj_virq(intr->nr, intr->soft, reinjected); - ++vcpu->stat.irq_injections; - -- svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr | -- SVM_EVTINJ_VALID | type; -+ svm->vmcb->control.event_inj = intr->nr | SVM_EVTINJ_VALID | type; - } - - void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, -diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c -index a152b00efcce..dd4fdbfa7901 100644 ---- a/arch/x86/kvm/x86.c -+++ b/arch/x86/kvm/x86.c -@@ -9305,6 +9305,23 @@ static bool is_vmware_backdoor_opcode(struct x86_emulate_ctxt *ctxt) - return false; - } - -+static bool is_soft_int_instruction(struct x86_emulate_ctxt *ctxt, -+ int emulation_type) -+{ -+ u8 vector = EMULTYPE_GET_SOFT_INT_VECTOR(emulation_type); -+ -+ switch (ctxt->b) { -+ case 0xcc: -+ return vector == BP_VECTOR; -+ case 0xcd: -+ return vector == ctxt->src.val; -+ case 0xce: -+ return vector == OF_VECTOR; -+ default: -+ return false; -+ } -+} -+ - /* - * Decode an instruction for emulation. The caller is responsible for handling - * code breakpoints. Note, manually detecting code breakpoints is unnecessary -@@ -9415,6 +9432,10 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, - * injecting single-step #DBs. - */ - if (emulation_type & EMULTYPE_SKIP) { -+ if (emulation_type & EMULTYPE_SKIP_SOFT_INT && -+ !is_soft_int_instruction(ctxt, emulation_type)) -+ return 0; -+ - if (ctxt->mode != X86EMUL_MODE_PROT64) - ctxt->eip = (u32)ctxt->_eip; - else --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68261.patch b/SPECS/kernel/CVE-2025-68261.patch deleted file mode 100644 index b622bd2a3..000000000 --- a/SPECS/kernel/CVE-2025-68261.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 6d247f2c40f1d8fb51c7a5b76a282bdec27b4e2f Mon Sep 17 00:00:00 2001 -From: Alexey Nepomnyashih -Date: Tue, 4 Nov 2025 09:33:25 +0000 -Subject: [PATCH 09/16] ext4: add i_data_sem protection in - ext4_destroy_inline_data_nolock() - -Fix a race between inline data destruction and block mapping. - -The function ext4_destroy_inline_data_nolock() changes the inode data -layout by clearing EXT4_INODE_INLINE_DATA and setting EXT4_INODE_EXTENTS. -At the same time, another thread may execute ext4_map_blocks(), which -tests EXT4_INODE_EXTENTS to decide whether to call ext4_ext_map_blocks() -or ext4_ind_map_blocks(). - -Without i_data_sem protection, ext4_ind_map_blocks() may receive inode -with EXT4_INODE_EXTENTS flag and triggering assert. - -kernel BUG at fs/ext4/indirect.c:546! -EXT4-fs (loop2): unmounting filesystem. -invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI -Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 -RIP: 0010:ext4_ind_map_blocks.cold+0x2b/0x5a fs/ext4/indirect.c:546 - -Call Trace: - - ext4_map_blocks+0xb9b/0x16f0 fs/ext4/inode.c:681 - _ext4_get_block+0x242/0x590 fs/ext4/inode.c:822 - ext4_block_write_begin+0x48b/0x12c0 fs/ext4/inode.c:1124 - ext4_write_begin+0x598/0xef0 fs/ext4/inode.c:1255 - ext4_da_write_begin+0x21e/0x9c0 fs/ext4/inode.c:3000 - generic_perform_write+0x259/0x5d0 mm/filemap.c:3846 - ext4_buffered_write_iter+0x15b/0x470 fs/ext4/file.c:285 - ext4_file_write_iter+0x8e0/0x17f0 fs/ext4/file.c:679 - call_write_iter include/linux/fs.h:2271 [inline] - do_iter_readv_writev+0x212/0x3c0 fs/read_write.c:735 - do_iter_write+0x186/0x710 fs/read_write.c:861 - vfs_iter_write+0x70/0xa0 fs/read_write.c:902 - iter_file_splice_write+0x73b/0xc90 fs/splice.c:685 - do_splice_from fs/splice.c:763 [inline] - direct_splice_actor+0x10f/0x170 fs/splice.c:950 - splice_direct_to_actor+0x33a/0xa10 fs/splice.c:896 - do_splice_direct+0x1a9/0x280 fs/splice.c:1002 - do_sendfile+0xb13/0x12c0 fs/read_write.c:1255 - __do_sys_sendfile64 fs/read_write.c:1323 [inline] - __se_sys_sendfile64 fs/read_write.c:1309 [inline] - __x64_sys_sendfile64+0x1cf/0x210 fs/read_write.c:1309 - do_syscall_x64 arch/x86/entry/common.c:51 [inline] - do_syscall_64+0x35/0x80 arch/x86/entry/common.c:81 - entry_SYSCALL_64_after_hwframe+0x6e/0xd8 - -Fixes: c755e251357a ("ext4: fix deadlock between inline_data and ext4_expand_extra_isize_ea()") -Cc: stable@vger.kernel.org # v4.11+ -Signed-off-by: Alexey Nepomnyashih -Message-ID: <20251104093326.697381-1-sdl@nppct.ru> -Signed-off-by: Theodore Ts'o ---- - fs/ext4/inline.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c -index 1b094a4f3866..ef7821f7fd26 100644 ---- a/fs/ext4/inline.c -+++ b/fs/ext4/inline.c -@@ -446,9 +446,13 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, - if (!ei->i_inline_off) - return 0; - -+ down_write(&ei->i_data_sem); -+ - error = ext4_get_inode_loc(inode, &is.iloc); -- if (error) -+ if (error) { -+ up_write(&ei->i_data_sem); - return error; -+ } - - error = ext4_xattr_ibody_find(inode, &i, &is); - if (error) -@@ -487,6 +491,7 @@ static int ext4_destroy_inline_data_nolock(handle_t *handle, - brelse(is.iloc.bh); - if (error == -ENODATA) - error = 0; -+ up_write(&ei->i_data_sem); - return error; - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68262.patch b/SPECS/kernel/CVE-2025-68262.patch deleted file mode 100644 index 94c68b7aa..000000000 --- a/SPECS/kernel/CVE-2025-68262.patch +++ /dev/null @@ -1,85 +0,0 @@ -From aa3da9b470ef096347e14c1df537c4430c672027 Mon Sep 17 00:00:00 2001 -From: Giovanni Cabiddu -Date: Thu, 20 Nov 2025 16:26:09 +0000 -Subject: [PATCH 08/16] crypto: zstd - fix double-free in per-CPU stream - cleanup - -The crypto/zstd module has a double-free bug that occurs when multiple -tfms are allocated and freed. - -The issue happens because zstd_streams (per-CPU contexts) are freed in -zstd_exit() during every tfm destruction, rather than being managed at -the module level. When multiple tfms exist, each tfm exit attempts to -free the same shared per-CPU streams, resulting in a double-free. - -This leads to a stack trace similar to: - - BUG: Bad page state in process kworker/u16:1 pfn:106fd93 - page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x106fd93 - flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff) - page_type: 0xffffffff() - raw: 0017ffffc0000000 dead000000000100 dead000000000122 0000000000000000 - raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000 - page dumped because: nonzero entire_mapcount - Modules linked in: ... - CPU: 3 UID: 0 PID: 2506 Comm: kworker/u16:1 Kdump: loaded Tainted: G B - Hardware name: ... - Workqueue: btrfs-delalloc btrfs_work_helper - Call Trace: - - dump_stack_lvl+0x5d/0x80 - bad_page+0x71/0xd0 - free_unref_page_prepare+0x24e/0x490 - free_unref_page+0x60/0x170 - crypto_acomp_free_streams+0x5d/0xc0 - crypto_acomp_exit_tfm+0x23/0x50 - crypto_destroy_tfm+0x60/0xc0 - ... - -Change the lifecycle management of zstd_streams to free the streams only -once during module cleanup. - -Fixes: f5ad93ffb541 ("crypto: zstd - convert to acomp") -Cc: stable@vger.kernel.org -Signed-off-by: Giovanni Cabiddu -Reviewed-by: Suman Kumar Chakraborty -Signed-off-by: Herbert Xu ---- - crypto/zstd.c | 7 +------ - 1 file changed, 1 insertion(+), 6 deletions(-) - -diff --git a/crypto/zstd.c b/crypto/zstd.c -index ac318d333b68..32a339b74f34 100644 ---- a/crypto/zstd.c -+++ b/crypto/zstd.c -@@ -75,11 +75,6 @@ static int zstd_init(struct crypto_acomp *acomp_tfm) - return ret; - } - --static void zstd_exit(struct crypto_acomp *acomp_tfm) --{ -- crypto_acomp_free_streams(&zstd_streams); --} -- - static int zstd_compress_one(struct acomp_req *req, struct zstd_ctx *ctx, - const void *src, void *dst, unsigned int *dlen) - { -@@ -297,7 +292,6 @@ static struct acomp_alg zstd_acomp = { - .cra_module = THIS_MODULE, - }, - .init = zstd_init, -- .exit = zstd_exit, - .compress = zstd_compress, - .decompress = zstd_decompress, - }; -@@ -310,6 +304,7 @@ static int __init zstd_mod_init(void) - static void __exit zstd_mod_fini(void) - { - crypto_unregister_acomp(&zstd_acomp); -+ crypto_acomp_free_streams(&zstd_streams); - } - - module_init(zstd_mod_init); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68263.patch b/SPECS/kernel/CVE-2025-68263.patch deleted file mode 100644 index d90eb1ea4..000000000 --- a/SPECS/kernel/CVE-2025-68263.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 949657704ce45da9a394966c0e19c5f383f192fc Mon Sep 17 00:00:00 2001 -From: Qianchang Zhao -Date: Wed, 26 Nov 2025 12:24:18 +0900 -Subject: [PATCH 02/16] ksmbd: ipc: fix use-after-free in ipc_msg_send_request - -ipc_msg_send_request() waits for a generic netlink reply using an -ipc_msg_table_entry on the stack. The generic netlink handler -(handle_generic_event()/handle_response()) fills entry->response under -ipc_msg_table_lock, but ipc_msg_send_request() used to validate and free -entry->response without holding the same lock. - -Under high concurrency this allows a race where handle_response() is -copying data into entry->response while ipc_msg_send_request() has just -freed it, leading to a slab-use-after-free reported by KASAN in -handle_generic_event(): - - BUG: KASAN: slab-use-after-free in handle_generic_event+0x3c4/0x5f0 [ksmbd] - Write of size 12 at addr ffff888198ee6e20 by task pool/109349 - ... - Freed by task: - kvfree - ipc_msg_send_request [ksmbd] - ksmbd_rpc_open -> ksmbd_session_rpc_open [ksmbd] - -Fix by: -- Taking ipc_msg_table_lock in ipc_msg_send_request() while validating - entry->response, freeing it when invalid, and removing the entry from - ipc_msg_table. -- Returning the final entry->response pointer to the caller only after - the hash entry is removed under the lock. -- Returning NULL in the error path, preserving the original API - semantics. - -This makes all accesses to entry->response consistent with -handle_response(), which already updates and fills the response buffer -under ipc_msg_table_lock, and closes the race that allowed the UAF. - -Cc: stable@vger.kernel.org -Reported-by: Qianchang Zhao -Reported-by: Zhitong Liu -Signed-off-by: Qianchang Zhao -Acked-by: Namjae Jeon -Signed-off-by: Steve French ---- - fs/smb/server/transport_ipc.c | 7 +++++-- - 1 file changed, 5 insertions(+), 2 deletions(-) - -diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c -index 2c08cccfa680..2dbabe2d8005 100644 ---- a/fs/smb/server/transport_ipc.c -+++ b/fs/smb/server/transport_ipc.c -@@ -553,12 +553,16 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle - up_write(&ipc_msg_table_lock); - - ret = ipc_msg_send(msg); -- if (ret) -+ if (ret) { -+ down_write(&ipc_msg_table_lock); - goto out; -+ } - - ret = wait_event_interruptible_timeout(entry.wait, - entry.response != NULL, - IPC_WAIT_TIMEOUT); -+ -+ down_write(&ipc_msg_table_lock); - if (entry.response) { - ret = ipc_validate_msg(&entry); - if (ret) { -@@ -567,7 +571,6 @@ static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle - } - } - out: -- down_write(&ipc_msg_table_lock); - hash_del(&entry.ipc_table_hlist); - up_write(&ipc_msg_table_lock); - return entry.response; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68264.patch b/SPECS/kernel/CVE-2025-68264.patch deleted file mode 100644 index b67af4abb..000000000 --- a/SPECS/kernel/CVE-2025-68264.patch +++ /dev/null @@ -1,67 +0,0 @@ -From 3a19a33f01c018e91e4a04519d9b6ff73f1c5a34 Mon Sep 17 00:00:00 2001 -From: Deepanshu Kartikey -Date: Mon, 20 Oct 2025 11:39:36 +0530 -Subject: [PATCH 13/16] ext4: refresh inline data size before write operations - -The cached ei->i_inline_size can become stale between the initial size -check and when ext4_update_inline_data()/ext4_create_inline_data() use -it. Although ext4_get_max_inline_size() reads the correct value at the -time of the check, concurrent xattr operations can modify i_inline_size -before ext4_write_lock_xattr() is acquired. - -This causes ext4_update_inline_data() and ext4_create_inline_data() to -work with stale capacity values, leading to a BUG_ON() crash in -ext4_write_inline_data(): - - kernel BUG at fs/ext4/inline.c:1331! - BUG_ON(pos + len > EXT4_I(inode)->i_inline_size); - -The race window: -1. ext4_get_max_inline_size() reads i_inline_size = 60 (correct) -2. Size check passes for 50-byte write -3. [Another thread adds xattr, i_inline_size changes to 40] -4. ext4_write_lock_xattr() acquires lock -5. ext4_update_inline_data() uses stale i_inline_size = 60 -6. Attempts to write 50 bytes but only 40 bytes actually available -7. BUG_ON() triggers - -Fix this by recalculating i_inline_size via ext4_find_inline_data_nolock() -immediately after acquiring xattr_sem. This ensures ext4_update_inline_data() -and ext4_create_inline_data() work with current values that are protected -from concurrent modifications. - -This is similar to commit a54c4613dac1 ("ext4: fix race writing to an -inline_data file while its xattrs are changing") which fixed i_inline_off -staleness. This patch addresses the related i_inline_size staleness issue. - -Reported-by: syzbot+f3185be57d7e8dda32b8@syzkaller.appspotmail.com -Link: https://syzkaller.appspot.com/bug?extid=f3185be57d7e8dda32b8 -Cc: stable@kernel.org -Signed-off-by: Deepanshu Kartikey -Message-ID: <20251020060936.474314-1-kartikey406@gmail.com> -Signed-off-by: Theodore Ts'o ---- - fs/ext4/inline.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c -index ef7821f7fd26..1f6bc05593df 100644 ---- a/fs/ext4/inline.c -+++ b/fs/ext4/inline.c -@@ -418,7 +418,12 @@ static int ext4_prepare_inline_data(handle_t *handle, struct inode *inode, - return -ENOSPC; - - ext4_write_lock_xattr(inode, &no_expand); -- -+ /* -+ * ei->i_inline_size may have changed since the initial check -+ * if other xattrs were added. Recalculate to ensure -+ * ext4_update_inline_data() validates against current capacity. -+ */ -+ (void) ext4_find_inline_data_nolock(inode); - if (ei->i_inline_off) - ret = ext4_update_inline_data(handle, inode, len); - else --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68265.patch b/SPECS/kernel/CVE-2025-68265.patch deleted file mode 100644 index 4bda50400..000000000 --- a/SPECS/kernel/CVE-2025-68265.patch +++ /dev/null @@ -1,93 +0,0 @@ -From dab5fec018f0974102e33357731bde1fdf207406 Mon Sep 17 00:00:00 2001 -From: Keith Busch -Date: Tue, 4 Nov 2025 14:48:30 -0800 -Subject: [PATCH 01/16] nvme: fix admin request_queue lifetime - -The namespaces can access the controller's admin request_queue, and -stale references on the namespaces may exist after tearing down the -controller. Ensure the admin request_queue is active by moving the -controller's 'put' to after all controller references have been released -to ensure no one is can access the request_queue. This fixes a reported -use-after-free bug: - - BUG: KASAN: slab-use-after-free in blk_queue_enter+0x41c/0x4a0 - Read of size 8 at addr ffff88c0a53819f8 by task nvme/3287 - CPU: 67 UID: 0 PID: 3287 Comm: nvme Tainted: G E 6.13.2-ga1582f1a031e #15 - Tainted: [E]=UNSIGNED_MODULE - Hardware name: Jabil /EGS 2S MB1, BIOS 1.00 06/18/2025 - Call Trace: - - dump_stack_lvl+0x4f/0x60 - print_report+0xc4/0x620 - ? _raw_spin_lock_irqsave+0x70/0xb0 - ? _raw_read_unlock_irqrestore+0x30/0x30 - ? blk_queue_enter+0x41c/0x4a0 - kasan_report+0xab/0xe0 - ? blk_queue_enter+0x41c/0x4a0 - blk_queue_enter+0x41c/0x4a0 - ? __irq_work_queue_local+0x75/0x1d0 - ? blk_queue_start_drain+0x70/0x70 - ? irq_work_queue+0x18/0x20 - ? vprintk_emit.part.0+0x1cc/0x350 - ? wake_up_klogd_work_func+0x60/0x60 - blk_mq_alloc_request+0x2b7/0x6b0 - ? __blk_mq_alloc_requests+0x1060/0x1060 - ? __switch_to+0x5b7/0x1060 - nvme_submit_user_cmd+0xa9/0x330 - nvme_user_cmd.isra.0+0x240/0x3f0 - ? force_sigsegv+0xe0/0xe0 - ? nvme_user_cmd64+0x400/0x400 - ? vfs_fileattr_set+0x9b0/0x9b0 - ? cgroup_update_frozen_flag+0x24/0x1c0 - ? cgroup_leave_frozen+0x204/0x330 - ? nvme_ioctl+0x7c/0x2c0 - blkdev_ioctl+0x1a8/0x4d0 - ? blkdev_common_ioctl+0x1930/0x1930 - ? fdget+0x54/0x380 - __x64_sys_ioctl+0x129/0x190 - do_syscall_64+0x5b/0x160 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - RIP: 0033:0x7f765f703b0b - Code: ff ff ff 85 c0 79 9b 49 c7 c4 ff ff ff ff 5b 5d 4c 89 e0 41 5c c3 66 0f 1f 84 00 00 00 00 00 f3 0f 1e fa b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d dd 52 0f 00 f7 d8 64 89 01 48 - RSP: 002b:00007ffe2cefe808 EFLAGS: 00000202 ORIG_RAX: 0000000000000010 - RAX: ffffffffffffffda RBX: 00007ffe2cefe860 RCX: 00007f765f703b0b - RDX: 00007ffe2cefe860 RSI: 00000000c0484e41 RDI: 0000000000000003 - RBP: 0000000000000000 R08: 0000000000000003 R09: 0000000000000000 - R10: 00007f765f611d50 R11: 0000000000000202 R12: 0000000000000003 - R13: 00000000c0484e41 R14: 0000000000000001 R15: 00007ffe2cefea60 - - -Reported-by: Casey Chen -Reviewed-by: Christoph Hellwig -Reviewed-by: Hannes Reinecke -Reviewed-by: Ming Lei -Reviewed-by: Chaitanya Kulkarni -Signed-off-by: Keith Busch ---- - drivers/nvme/host/core.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c -index 5714d4993282..28c598008124 100644 ---- a/drivers/nvme/host/core.c -+++ b/drivers/nvme/host/core.c -@@ -4896,7 +4896,6 @@ void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl) - */ - nvme_stop_keep_alive(ctrl); - blk_mq_destroy_queue(ctrl->admin_q); -- blk_put_queue(ctrl->admin_q); - if (ctrl->ops->flags & NVME_F_FABRICS) { - blk_mq_destroy_queue(ctrl->fabrics_q); - blk_put_queue(ctrl->fabrics_q); -@@ -5040,6 +5039,8 @@ static void nvme_free_ctrl(struct device *dev) - container_of(dev, struct nvme_ctrl, ctrl_device); - struct nvme_subsystem *subsys = ctrl->subsys; - -+ if (ctrl->admin_q) -+ blk_put_queue(ctrl->admin_q); - if (!subsys || ctrl->instance != subsys->instance) - ida_free(&nvme_instance_ida, ctrl->instance); - nvme_free_cels(ctrl); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68281.patch b/SPECS/kernel/CVE-2025-68281.patch deleted file mode 100644 index 2105677bc..000000000 --- a/SPECS/kernel/CVE-2025-68281.patch +++ /dev/null @@ -1,36 +0,0 @@ -From b657ebe7d692c906e63577986cf754d38ec5610f Mon Sep 17 00:00:00 2001 -From: Niranjan H Y -Date: Mon, 10 Nov 2025 20:56:46 +0530 -Subject: [PATCH 07/16] ASoC: SDCA: bug fix while parsing - mipi-sdca-control-cn-list - -"struct sdca_control" declares "values" field as integer array. -But the memory allocated to it is of char array. This causes -crash for sdca_parse_function API. This patch addresses the -issue by allocating correct data size. - -Signed-off-by: Niranjan H Y -Reviewed-by: Charles Keepax -Link: https://patch.msgid.link/20251110152646.192-1-niranjan.hy@ti.com -Signed-off-by: Mark Brown ---- - sound/soc/sdca/sdca_functions.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c -index 13f68f7b6dd6..0ccb6775f4de 100644 ---- a/sound/soc/sdca/sdca_functions.c -+++ b/sound/soc/sdca/sdca_functions.c -@@ -894,7 +894,8 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti - return ret; - } - -- control->values = devm_kzalloc(dev, hweight64(control->cn_list), GFP_KERNEL); -+ control->values = devm_kcalloc(dev, hweight64(control->cn_list), -+ sizeof(int), GFP_KERNEL); - if (!control->values) - return -ENOMEM; - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68323.patch b/SPECS/kernel/CVE-2025-68323.patch deleted file mode 100644 index db3cd6eb8..000000000 --- a/SPECS/kernel/CVE-2025-68323.patch +++ /dev/null @@ -1,136 +0,0 @@ -From b9320969035ba986d397638c4fdbfc247140f217 Mon Sep 17 00:00:00 2001 -From: Duoming Zhou -Date: Tue, 25 Nov 2025 18:36:27 +0800 -Subject: [PATCH 16/16] usb: typec: ucsi: fix use-after-free caused by - uec->work - -The delayed work uec->work is scheduled in gaokun_ucsi_probe() -but never properly canceled in gaokun_ucsi_remove(). This creates -use-after-free scenarios where the ucsi and gaokun_ucsi structure -are freed after ucsi_destroy() completes execution, while the -gaokun_ucsi_register_worker() might be either currently executing -or still pending in the work queue. The already-freed gaokun_ucsi -or ucsi structure may then be accessed. - -Furthermore, the race window is 3 seconds, which is sufficiently -long to make this bug easily reproducible. The following is the -trace captured by KASAN: - -================================================================== -BUG: KASAN: slab-use-after-free in __run_timers+0x5ec/0x630 -Write of size 8 at addr ffff00000ec28cc8 by task swapper/0/0 -... -Call trace: - show_stack+0x18/0x24 (C) - dump_stack_lvl+0x78/0x90 - print_report+0x114/0x580 - kasan_report+0xa4/0xf0 - __asan_report_store8_noabort+0x20/0x2c - __run_timers+0x5ec/0x630 - run_timer_softirq+0xe8/0x1cc - handle_softirqs+0x294/0x720 - __do_softirq+0x14/0x20 - ____do_softirq+0x10/0x1c - call_on_irq_stack+0x30/0x48 - do_softirq_own_stack+0x1c/0x28 - __irq_exit_rcu+0x27c/0x364 - irq_exit_rcu+0x10/0x1c - el1_interrupt+0x40/0x60 - el1h_64_irq_handler+0x18/0x24 - el1h_64_irq+0x6c/0x70 - arch_local_irq_enable+0x4/0x8 (P) - do_idle+0x334/0x458 - cpu_startup_entry+0x60/0x70 - rest_init+0x158/0x174 - start_kernel+0x2f8/0x394 - __primary_switched+0x8c/0x94 - -Allocated by task 72 on cpu 0 at 27.510341s: - kasan_save_stack+0x2c/0x54 - kasan_save_track+0x24/0x5c - kasan_save_alloc_info+0x40/0x54 - __kasan_kmalloc+0xa0/0xb8 - __kmalloc_node_track_caller_noprof+0x1c0/0x588 - devm_kmalloc+0x7c/0x1c8 - gaokun_ucsi_probe+0xa0/0x840 auxiliary_bus_probe+0x94/0xf8 - really_probe+0x17c/0x5b8 - __driver_probe_device+0x158/0x2c4 - driver_probe_device+0x10c/0x264 - __device_attach_driver+0x168/0x2d0 - bus_for_each_drv+0x100/0x188 - __device_attach+0x174/0x368 - device_initial_probe+0x14/0x20 - bus_probe_device+0x120/0x150 - device_add+0xb3c/0x10fc - __auxiliary_device_add+0x88/0x130 -... - -Freed by task 73 on cpu 1 at 28.910627s: - kasan_save_stack+0x2c/0x54 - kasan_save_track+0x24/0x5c - __kasan_save_free_info+0x4c/0x74 - __kasan_slab_free+0x60/0x8c - kfree+0xd4/0x410 - devres_release_all+0x140/0x1f0 - device_unbind_cleanup+0x20/0x190 - device_release_driver_internal+0x344/0x460 - device_release_driver+0x18/0x24 - bus_remove_device+0x198/0x274 - device_del+0x310/0xa84 -... - -The buggy address belongs to the object at ffff00000ec28c00 - which belongs to the cache kmalloc-512 of size 512 -The buggy address is located 200 bytes inside of - freed 512-byte region -The buggy address belongs to the physical page: -page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x4ec28 -head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 -flags: 0x3fffe0000000040(head|node=0|zone=0|lastcpupid=0x1ffff) -page_type: f5(slab) -raw: 03fffe0000000040 ffff000008801c80 dead000000000122 0000000000000000 -raw: 0000000000000000 0000000080100010 00000000f5000000 0000000000000000 -head: 03fffe0000000040 ffff000008801c80 dead000000000122 0000000000000000 -head: 0000000000000000 0000000080100010 00000000f5000000 0000000000000000 -head: 03fffe0000000002 fffffdffc03b0a01 00000000ffffffff 00000000ffffffff -head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000004 -page dumped because: kasan: bad access detected - -Memory state around the buggy address: - ffff00000ec28b80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc - ffff00000ec28c00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ->ffff00000ec28c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb - ^ - ffff00000ec28d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb - ffff00000ec28d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb -================================================================== - -Add disable_delayed_work_sync() in gaokun_ucsi_remove() to ensure -that uec->work is properly canceled and prevented from executing -after the ucsi and gaokun_ucsi structure have been deallocated. - -Fixes: 00327d7f2c8c ("usb: typec: ucsi: add Huawei Matebook E Go ucsi driver") -Cc: stable -Signed-off-by: Duoming Zhou -Reviewed-by: Heikki Krogerus -Link: https://patch.msgid.link/cc31e12ef9ffbf86676585b02233165fd33f0d8e.1764065838.git.duoming@zju.edu.cn -Signed-off-by: Greg Kroah-Hartman ---- - drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c -index 7b5222081bbb..d6d82be35675 100644 ---- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c -+++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c -@@ -502,6 +502,7 @@ static void gaokun_ucsi_remove(struct auxiliary_device *adev) - { - struct gaokun_ucsi *uec = auxiliary_get_drvdata(adev); - -+ disable_delayed_work_sync(&uec->work); - gaokun_ec_unregister_notify(uec->ec, &uec->nb); - ucsi_unregister(uec->ucsi); - ucsi_destroy(uec->ucsi); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68325.patch b/SPECS/kernel/CVE-2025-68325.patch deleted file mode 100644 index 0ac23abeb..000000000 --- a/SPECS/kernel/CVE-2025-68325.patch +++ /dev/null @@ -1,154 +0,0 @@ -From 6d2cb43e5e4a6d4f72e046d2caa4f402124feea7 Mon Sep 17 00:00:00 2001 -From: Xiang Mei -Date: Thu, 27 Nov 2025 17:14:14 -0700 -Subject: [PATCH 14/16] net/sched: sch_cake: Fix incorrect qlen reduction in - cake_drop -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In cake_drop(), qdisc_tree_reduce_backlog() is used to update the qlen -and backlog of the qdisc hierarchy. Its caller, cake_enqueue(), assumes -that the parent qdisc will enqueue the current packet. However, this -assumption breaks when cake_enqueue() returns NET_XMIT_CN: the parent -qdisc stops enqueuing current packet, leaving the tree qlen/backlog -accounting inconsistent. This mismatch can lead to a NULL dereference -(e.g., when the parent Qdisc is qfq_qdisc). - -This patch computes the qlen/backlog delta in a more robust way by -observing the difference before and after the series of cake_drop() -calls, and then compensates the qdisc tree accounting if cake_enqueue() -returns NET_XMIT_CN. - -To ensure correct compensation when ACK thinning is enabled, a new -variable is introduced to keep qlen unchanged. - -Fixes: 15de71d06a40 ("net/sched: Make cake_enqueue return NET_XMIT_CN when past buffer_limit") -Signed-off-by: Xiang Mei -Reviewed-by: Toke Høiland-Jørgensen -Link: https://patch.msgid.link/20251128001415.377823-1-xmei5@asu.edu -Signed-off-by: Paolo Abeni ---- - net/sched/sch_cake.c | 58 ++++++++++++++++++++++++-------------------- - 1 file changed, 32 insertions(+), 26 deletions(-) - -diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c -index 32bacfc314c2..d325a90cde9e 100644 ---- a/net/sched/sch_cake.c -+++ b/net/sched/sch_cake.c -@@ -1597,7 +1597,6 @@ static unsigned int cake_drop(struct Qdisc *sch, struct sk_buff **to_free) - - qdisc_drop_reason(skb, sch, to_free, SKB_DROP_REASON_QDISC_OVERLIMIT); - sch->q.qlen--; -- qdisc_tree_reduce_backlog(sch, 1, len); - - cake_heapify(q, 0); - -@@ -1743,14 +1742,14 @@ static void cake_reconfigure(struct Qdisc *sch); - static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - struct sk_buff **to_free) - { -+ u32 idx, tin, prev_qlen, prev_backlog, drop_id; - struct cake_sched_data *q = qdisc_priv(sch); -- int len = qdisc_pkt_len(skb); -- int ret; -+ int len = qdisc_pkt_len(skb), ret; - struct sk_buff *ack = NULL; - ktime_t now = ktime_get(); - struct cake_tin_data *b; - struct cake_flow *flow; -- u32 idx, tin; -+ bool same_flow = false; - - /* choose flow to insert into */ - idx = cake_classify(sch, &b, skb, q->flow_mode, &ret); -@@ -1823,6 +1822,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - consume_skb(skb); - } else { - /* not splitting */ -+ int ack_pkt_len = 0; -+ - cobalt_set_enqueue_time(skb, now); - get_cobalt_cb(skb)->adjusted_len = cake_overhead(q, skb); - flow_queue_add(flow, skb); -@@ -1833,13 +1834,13 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - if (ack) { - b->ack_drops++; - sch->qstats.drops++; -- b->bytes += qdisc_pkt_len(ack); -- len -= qdisc_pkt_len(ack); -+ ack_pkt_len = qdisc_pkt_len(ack); -+ b->bytes += ack_pkt_len; - q->buffer_used += skb->truesize - ack->truesize; - if (q->rate_flags & CAKE_FLAG_INGRESS) - cake_advance_shaper(q, b, ack, now, true); - -- qdisc_tree_reduce_backlog(sch, 1, qdisc_pkt_len(ack)); -+ qdisc_tree_reduce_backlog(sch, 1, ack_pkt_len); - consume_skb(ack); - } else { - sch->q.qlen++; -@@ -1848,11 +1849,11 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - - /* stats */ - b->packets++; -- b->bytes += len; -- b->backlogs[idx] += len; -- b->tin_backlog += len; -- sch->qstats.backlog += len; -- q->avg_window_bytes += len; -+ b->bytes += len - ack_pkt_len; -+ b->backlogs[idx] += len - ack_pkt_len; -+ b->tin_backlog += len - ack_pkt_len; -+ sch->qstats.backlog += len - ack_pkt_len; -+ q->avg_window_bytes += len - ack_pkt_len; - } - - if (q->overflow_timeout) -@@ -1927,24 +1928,29 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch, - if (q->buffer_used > q->buffer_max_used) - q->buffer_max_used = q->buffer_used; - -- if (q->buffer_used > q->buffer_limit) { -- bool same_flow = false; -- u32 dropped = 0; -- u32 drop_id; -+ if (q->buffer_used <= q->buffer_limit) -+ return NET_XMIT_SUCCESS; - -- while (q->buffer_used > q->buffer_limit) { -- dropped++; -- drop_id = cake_drop(sch, to_free); -+ prev_qlen = sch->q.qlen; -+ prev_backlog = sch->qstats.backlog; - -- if ((drop_id >> 16) == tin && -- (drop_id & 0xFFFF) == idx) -- same_flow = true; -- } -- b->drop_overlimit += dropped; -+ while (q->buffer_used > q->buffer_limit) { -+ drop_id = cake_drop(sch, to_free); -+ if ((drop_id >> 16) == tin && -+ (drop_id & 0xFFFF) == idx) -+ same_flow = true; -+ } -+ -+ prev_qlen -= sch->q.qlen; -+ prev_backlog -= sch->qstats.backlog; -+ b->drop_overlimit += prev_qlen; - -- if (same_flow) -- return NET_XMIT_CN; -+ if (same_flow) { -+ qdisc_tree_reduce_backlog(sch, prev_qlen - 1, -+ prev_backlog - len); -+ return NET_XMIT_CN; - } -+ qdisc_tree_reduce_backlog(sch, prev_qlen, prev_backlog); - return NET_XMIT_SUCCESS; - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68333.patch b/SPECS/kernel/CVE-2025-68333.patch deleted file mode 100644 index 0c2bc6e52..000000000 --- a/SPECS/kernel/CVE-2025-68333.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 8c4125e4f8e5d21f655964e52355ae29c369170d Mon Sep 17 00:00:00 2001 -From: Zqiang -Date: Thu, 13 Nov 2025 19:43:55 +0800 -Subject: [PATCH 17/51] sched_ext: Fix possible deadlock in the - deferred_irq_workfn() - -For PREEMPT_RT=y kernels, the deferred_irq_workfn() is executed in -the per-cpu irq_work/* task context and not disable-irq, if the rq -returned by container_of() is current CPU's rq, the following scenarios -may occur: - -lock(&rq->__lock); - - lock(&rq->__lock); - -This commit use IRQ_WORK_INIT_HARD() to replace init_irq_work() to -initialize rq->scx.deferred_irq_work, make the deferred_irq_workfn() -is always invoked in hard-irq context. - -Signed-off-by: Zqiang -Signed-off-by: Tejun Heo ---- - kernel/sched/ext.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/kernel/sched/ext.c b/kernel/sched/ext.c -index 1e4740de66c2..16a7ae9b29ae 100644 ---- a/kernel/sched/ext.c -+++ b/kernel/sched/ext.c -@@ -5377,7 +5377,7 @@ void __init init_sched_ext_class(void) - BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_kick_if_idle, GFP_KERNEL, n)); - BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_preempt, GFP_KERNEL, n)); - BUG_ON(!zalloc_cpumask_var_node(&rq->scx.cpus_to_wait, GFP_KERNEL, n)); -- init_irq_work(&rq->scx.deferred_irq_work, deferred_irq_workfn); -+ rq->scx.deferred_irq_work = IRQ_WORK_INIT_HARD(deferred_irq_workfn); - init_irq_work(&rq->scx.kick_cpus_irq_work, kick_cpus_irq_workfn); - - if (cpu_online(cpu)) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68336.patch b/SPECS/kernel/CVE-2025-68336.patch deleted file mode 100644 index 90d508203..000000000 --- a/SPECS/kernel/CVE-2025-68336.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 0e5be7270b58b9bcee83584be1eac03cd0845eba Mon Sep 17 00:00:00 2001 -From: Alexander Sverdlin -Date: Fri, 19 Sep 2025 11:12:38 +0200 -Subject: [PATCH 18/51] locking/spinlock/debug: Fix data-race in - do_raw_write_lock - -KCSAN reports: - -BUG: KCSAN: data-race in do_raw_write_lock / do_raw_write_lock - -write (marked) to 0xffff800009cf504c of 4 bytes by task 1102 on cpu 1: - do_raw_write_lock+0x120/0x204 - _raw_write_lock_irq - do_exit - call_usermodehelper_exec_async - ret_from_fork - -read to 0xffff800009cf504c of 4 bytes by task 1103 on cpu 0: - do_raw_write_lock+0x88/0x204 - _raw_write_lock_irq - do_exit - call_usermodehelper_exec_async - ret_from_fork - -value changed: 0xffffffff -> 0x00000001 - -Reported by Kernel Concurrency Sanitizer on: -CPU: 0 PID: 1103 Comm: kworker/u4:1 6.1.111 - -Commit 1a365e822372 ("locking/spinlock/debug: Fix various data races") has -adressed most of these races, but seems to be not consistent/not complete. - ->From do_raw_write_lock() only debug_write_lock_after() part has been -converted to WRITE_ONCE(), but not debug_write_lock_before() part. -Do it now. - -Fixes: 1a365e822372 ("locking/spinlock/debug: Fix various data races") -Reported-by: Adrian Freihofer -Signed-off-by: Alexander Sverdlin -Signed-off-by: Boqun Feng -Signed-off-by: Peter Zijlstra (Intel) -Reviewed-by: Paul E. McKenney -Acked-by: Waiman Long -Cc: stable@vger.kernel.org ---- - kernel/locking/spinlock_debug.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/kernel/locking/spinlock_debug.c b/kernel/locking/spinlock_debug.c -index 87b03d2e41db..2338b3adfb55 100644 ---- a/kernel/locking/spinlock_debug.c -+++ b/kernel/locking/spinlock_debug.c -@@ -184,8 +184,8 @@ void do_raw_read_unlock(rwlock_t *lock) - static inline void debug_write_lock_before(rwlock_t *lock) - { - RWLOCK_BUG_ON(lock->magic != RWLOCK_MAGIC, lock, "bad magic"); -- RWLOCK_BUG_ON(lock->owner == current, lock, "recursion"); -- RWLOCK_BUG_ON(lock->owner_cpu == raw_smp_processor_id(), -+ RWLOCK_BUG_ON(READ_ONCE(lock->owner) == current, lock, "recursion"); -+ RWLOCK_BUG_ON(READ_ONCE(lock->owner_cpu) == raw_smp_processor_id(), - lock, "cpu recursion"); - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68337.patch b/SPECS/kernel/CVE-2025-68337.patch deleted file mode 100644 index 19b159468..000000000 --- a/SPECS/kernel/CVE-2025-68337.patch +++ /dev/null @@ -1,96 +0,0 @@ -From f5085db4ff19b30506acecd97e8db9f55f819488 Mon Sep 17 00:00:00 2001 -From: Ye Bin -Date: Sat, 25 Oct 2025 15:26:57 +0800 -Subject: [PATCH 27/51] jbd2: avoid bug_on in jbd2_journal_get_create_access() - when file system corrupted - -There's issue when file system corrupted: -------------[ cut here ]------------ -kernel BUG at fs/jbd2/transaction.c:1289! -Oops: invalid opcode: 0000 [#1] SMP KASAN PTI -CPU=5 UID=0 PID=2031 Comm: mkdir Not tainted 6.18.0-rc1-next -RIP=0010=jbd2_journal_get_create_access+0x3b6/0x4d0 -RSP=0018:ffff888117aafa30 EFLAGS=00010202 -RAX=0000000000000000 RBX=ffff88811a86b000 RCX=ffffffff89a63534 -RDX=1ffff110200ec602 RSI=0000000000000004 RDI=ffff888100763010 -RBP=ffff888100763000 R08=0000000000000001 R09=ffff888100763028 -R10=0000000000000003 R11=0000000000000000 R12=0000000000000000 -R13=ffff88812c432000 R14=ffff88812c608000 R15=ffff888120bfc000 -CS=0010 DS=0000 ES=0000 CR0=0000000080050033 -CR2=00007f91d6970c99 CR3=00000001159c4000 CR4=00000000000006f0 -Call Trace: - - __ext4_journal_get_create_access+0x42/0x170 - ext4_getblk+0x319/0x6f0 - ext4_bread+0x11/0x100 - ext4_append+0x1e6/0x4a0 - ext4_init_new_dir+0x145/0x1d0 - ext4_mkdir+0x326/0x920 - vfs_mkdir+0x45c/0x740 - do_mkdirat+0x234/0x2f0 - __x64_sys_mkdir+0xd6/0x120 - do_syscall_64+0x5f/0xfa0 - entry_SYSCALL_64_after_hwframe+0x76/0x7e - -The above issue occurs with us in errors=continue mode when accompanied by -storage failures. There have been many inconsistencies in the file system -data. -In the case of file system data inconsistency, for example, if the block -bitmap of a referenced block is not set, it can lead to the situation where -a block being committed is allocated and used again. As a result, the -following condition will not be satisfied then trigger BUG_ON. Of course, -it is entirely possible to construct a problematic image that can trigger -this BUG_ON through specific operations. In fact, I have constructed such -an image and easily reproduced this issue. -Therefore, J_ASSERT() holds true only under ideal conditions, but it may -not necessarily be satisfied in exceptional scenarios. Using J_ASSERT() -directly in abnormal situations would cause the system to crash, which is -clearly not what we want. So here we directly trigger a JBD abort instead -of immediately invoking BUG_ON. - -Fixes: 470decc613ab ("[PATCH] jbd2: initial copy of files from jbd") -Signed-off-by: Ye Bin -Reviewed-by: Jan Kara -Message-ID: <20251025072657.307851-1-yebin@huaweicloud.com> -Signed-off-by: Theodore Ts'o -Cc: stable@kernel.org ---- - fs/jbd2/transaction.c | 19 ++++++++++++++----- - 1 file changed, 14 insertions(+), 5 deletions(-) - -diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c -index 3e510564de6e..9ce626ac947e 100644 ---- a/fs/jbd2/transaction.c -+++ b/fs/jbd2/transaction.c -@@ -1284,14 +1284,23 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) - * committing transaction's lists, but it HAS to be in Forget state in - * that case: the transaction must have deleted the buffer for it to be - * reused here. -+ * In the case of file system data inconsistency, for example, if the -+ * block bitmap of a referenced block is not set, it can lead to the -+ * situation where a block being committed is allocated and used again. -+ * As a result, the following condition will not be satisfied, so here -+ * we directly trigger a JBD abort instead of immediately invoking -+ * bugon. - */ - spin_lock(&jh->b_state_lock); -- J_ASSERT_JH(jh, (jh->b_transaction == transaction || -- jh->b_transaction == NULL || -- (jh->b_transaction == journal->j_committing_transaction && -- jh->b_jlist == BJ_Forget))); -+ if (!(jh->b_transaction == transaction || jh->b_transaction == NULL || -+ (jh->b_transaction == journal->j_committing_transaction && -+ jh->b_jlist == BJ_Forget)) || jh->b_next_transaction != NULL) { -+ err = -EROFS; -+ spin_unlock(&jh->b_state_lock); -+ jbd2_journal_abort(journal, err); -+ goto out; -+ } - -- J_ASSERT_JH(jh, jh->b_next_transaction == NULL); - J_ASSERT_JH(jh, buffer_locked(jh2bh(jh))); - - if (jh->b_transaction == NULL) { --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68345.patch b/SPECS/kernel/CVE-2025-68345.patch deleted file mode 100644 index 48c3ba7d0..000000000 --- a/SPECS/kernel/CVE-2025-68345.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 304d6abf76904b1c7c581343dcf0c00235d7b21c Mon Sep 17 00:00:00 2001 -From: Denis Arefev -Date: Tue, 2 Dec 2025 13:13:36 +0300 -Subject: [PATCH 19/51] ALSA: hda: cs35l41: Fix NULL pointer dereference in - cs35l41_hda_read_acpi() - -The acpi_get_first_physical_node() function can return NULL, in which -case the get_device() function also returns NULL, but this value is -then dereferenced without checking,so add a check to prevent a crash. - -Found by Linux Verification Center (linuxtesting.org) with SVACE. - -Fixes: 7b2f3eb492da ("ALSA: hda: cs35l41: Add support for CS35L41 in HDA systems") -Cc: stable@vger.kernel.org -Signed-off-by: Denis Arefev -Reviewed-by: Richard Fitzgerald -Signed-off-by: Takashi Iwai -Link: https://patch.msgid.link/20251202101338.11437-1-arefev@swemel.ru ---- - sound/hda/codecs/side-codecs/cs35l41_hda.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda.c b/sound/hda/codecs/side-codecs/cs35l41_hda.c -index 0ef77fae0402..8d0cfe0a7ecb 100644 ---- a/sound/hda/codecs/side-codecs/cs35l41_hda.c -+++ b/sound/hda/codecs/side-codecs/cs35l41_hda.c -@@ -1917,6 +1917,8 @@ static int cs35l41_hda_read_acpi(struct cs35l41_hda *cs35l41, const char *hid, i - - cs35l41->dacpi = adev; - physdev = get_device(acpi_get_first_physical_node(adev)); -+ if (!physdev) -+ return -ENODEV; - - sub = acpi_get_subsystem_id(ACPI_HANDLE(physdev)); - if (IS_ERR(sub)) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68346.patch b/SPECS/kernel/CVE-2025-68346.patch deleted file mode 100644 index 21949e06b..000000000 --- a/SPECS/kernel/CVE-2025-68346.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 69a1cc4950f8628a85e213ef8f4c437965921a27 Mon Sep 17 00:00:00 2001 -From: Junrui Luo -Date: Fri, 28 Nov 2025 12:06:31 +0800 -Subject: [PATCH 20/51] ALSA: dice: fix buffer overflow in - detect_stream_formats() - -The function detect_stream_formats() reads the stream_count value directly -from a FireWire device without validating it. This can lead to -out-of-bounds writes when a malicious device provides a stream_count value -greater than MAX_STREAMS. - -Fix by applying the same validation to both TX and RX stream counts in -detect_stream_formats(). - -Reported-by: Yuhao Jiang -Reported-by: Junrui Luo -Fixes: 58579c056c1c ("ALSA: dice: use extended protocol to detect available stream formats") -Cc: stable@vger.kernel.org -Reviewed-by: Takashi Sakamoto -Signed-off-by: Junrui Luo -Link: https://patch.msgid.link/SYBPR01MB7881B043FC68B4C0DA40B73DAFDCA@SYBPR01MB7881.ausprd01.prod.outlook.com -Signed-off-by: Takashi Iwai ---- - sound/firewire/dice/dice-extension.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/sound/firewire/dice/dice-extension.c b/sound/firewire/dice/dice-extension.c -index 02f4a8318e38..48bfb3ad93ce 100644 ---- a/sound/firewire/dice/dice-extension.c -+++ b/sound/firewire/dice/dice-extension.c -@@ -116,7 +116,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) - break; - - base_offset += EXT_APP_STREAM_ENTRIES; -- stream_count = be32_to_cpu(reg[0]); -+ stream_count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS); - err = read_stream_entries(dice, section_addr, base_offset, - stream_count, mode, - dice->tx_pcm_chs, -@@ -125,7 +125,7 @@ static int detect_stream_formats(struct snd_dice *dice, u64 section_addr) - break; - - base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE; -- stream_count = be32_to_cpu(reg[1]); -+ stream_count = min_t(unsigned int, be32_to_cpu(reg[1]), MAX_STREAMS); - err = read_stream_entries(dice, section_addr, base_offset, - stream_count, - mode, dice->rx_pcm_chs, --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68347.patch b/SPECS/kernel/CVE-2025-68347.patch deleted file mode 100644 index bb7e4dd60..000000000 --- a/SPECS/kernel/CVE-2025-68347.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 8022191f0a098bca7795638e8b8849032ea28152 Mon Sep 17 00:00:00 2001 -From: Junrui Luo -Date: Wed, 3 Dec 2025 12:27:03 +0800 -Subject: [PATCH 21/51] ALSA: firewire-motu: fix buffer overflow in hwdep read - for DSP events - -The DSP event handling code in hwdep_read() could write more bytes to -the user buffer than requested, when a user provides a buffer smaller -than the event header size (8 bytes). - -Fix by using min_t() to clamp the copy size, This ensures we never copy -more than the user requested. - -Reported-by: Yuhao Jiang -Reported-by: Junrui Luo -Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") -Signed-off-by: Junrui Luo -Link: https://patch.msgid.link/SYBPR01MB78810656377E79E58350D951AFD9A@SYBPR01MB7881.ausprd01.prod.outlook.com -Signed-off-by: Takashi Iwai ---- - sound/firewire/motu/motu-hwdep.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c -index fa2685665db3..e594765747d5 100644 ---- a/sound/firewire/motu/motu-hwdep.c -+++ b/sound/firewire/motu/motu-hwdep.c -@@ -83,10 +83,11 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, - event.motu_register_dsp_change.type = SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE; - event.motu_register_dsp_change.count = - (consumed - sizeof(event.motu_register_dsp_change)) / 4; -- if (copy_to_user(buf, &event, sizeof(event.motu_register_dsp_change))) -+ if (copy_to_user(buf, &event, -+ min_t(long, count, sizeof(event.motu_register_dsp_change)))) - return -EFAULT; - -- count = consumed; -+ count = min_t(long, count, consumed); - } else { - spin_unlock_irq(&motu->lock); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68348.patch b/SPECS/kernel/CVE-2025-68348.patch deleted file mode 100644 index 0dfa149fe..000000000 --- a/SPECS/kernel/CVE-2025-68348.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 75c42bfca8ba40b5ee59d6e66bd6ff64789e301c Mon Sep 17 00:00:00 2001 -From: Shaurya Rane -Date: Thu, 4 Dec 2025 23:42:59 +0530 -Subject: [PATCH 22/51] block: fix memory leak in __blkdev_issue_zero_pages - -Move the fatal signal check before bio_alloc() to prevent a memory -leak when BLKDEV_ZERO_KILLABLE is set and a fatal signal is pending. - -Previously, the bio was allocated before checking for a fatal signal. -If a signal was pending, the code would break out of the loop without -freeing or chaining the just-allocated bio, causing a memory leak. - -This matches the pattern already used in __blkdev_issue_write_zeroes() -where the signal check precedes the allocation. - -Fixes: bf86bcdb4012 ("blk-lib: check for kill signal in ioctl BLKZEROOUT") -Reported-by: syzbot+527a7e48a3d3d315d862@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=527a7e48a3d3d315d862 -Signed-off-by: Shaurya Rane -Reviewed-by: Keith Busch -Tested-by: syzbot+527a7e48a3d3d315d862@syzkaller.appspotmail.com -Signed-off-by: Jens Axboe ---- - block/blk-lib.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/block/blk-lib.c b/block/blk-lib.c -index 4c9f20a689f7..8cb2987db786 100644 ---- a/block/blk-lib.c -+++ b/block/blk-lib.c -@@ -200,13 +200,13 @@ static void __blkdev_issue_zero_pages(struct block_device *bdev, - unsigned int nr_vecs = __blkdev_sectors_to_bio_pages(nr_sects); - struct bio *bio; - -- bio = bio_alloc(bdev, nr_vecs, REQ_OP_WRITE, gfp_mask); -- bio->bi_iter.bi_sector = sector; -- - if ((flags & BLKDEV_ZERO_KILLABLE) && - fatal_signal_pending(current)) - break; - -+ bio = bio_alloc(bdev, nr_vecs, REQ_OP_WRITE, gfp_mask); -+ bio->bi_iter.bi_sector = sector; -+ - do { - unsigned int len, added; - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68349.patch b/SPECS/kernel/CVE-2025-68349.patch deleted file mode 100644 index f7e1577f9..000000000 --- a/SPECS/kernel/CVE-2025-68349.patch +++ /dev/null @@ -1,38 +0,0 @@ -From c80547245308a4267488800b321de344d8f469ca Mon Sep 17 00:00:00 2001 -From: Jonathan Curley -Date: Wed, 12 Nov 2025 18:02:42 +0000 -Subject: [PATCH 04/51] NFSv4/pNFS: Clear NFS_INO_LAYOUTCOMMIT in - pnfs_mark_layout_stateid_invalid - -Fixes a crash when layout is null during this call stack: - -write_inode - -> nfs4_write_inode - -> pnfs_layoutcommit_inode - -pnfs_set_layoutcommit relies on the lseg refcount to keep the layout -around. Need to clear NFS_INO_LAYOUTCOMMIT otherwise we might attempt -to reference a null layout. - -Fixes: fe1cf9469d7bc ("pNFS: Clear all layout segment state in pnfs_mark_layout_stateid_invalid") -Signed-off-by: Jonathan Curley -Signed-off-by: Trond Myklebust ---- - fs/nfs/pnfs.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c -index a3135b5af7ee..7ce2e840217c 100644 ---- a/fs/nfs/pnfs.c -+++ b/fs/nfs/pnfs.c -@@ -464,6 +464,7 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo, - struct pnfs_layout_segment *lseg, *next; - - set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags); -+ clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(lo->plh_inode)->flags); - list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list) - pnfs_clear_lseg_state(lseg, lseg_list); - pnfs_clear_layoutreturn_info(lo); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68353.patch b/SPECS/kernel/CVE-2025-68353.patch deleted file mode 100644 index 3409beeb4..000000000 --- a/SPECS/kernel/CVE-2025-68353.patch +++ /dev/null @@ -1,93 +0,0 @@ -From 40b6c2110b3bcbc801775dcb517c609c4fa1eab4 Mon Sep 17 00:00:00 2001 -From: Antoine Tenart -Date: Wed, 26 Nov 2025 11:26:25 +0100 -Subject: [PATCH 23/51] net: vxlan: prevent NULL deref in vxlan_xmit_one - -Neither sock4 nor sock6 pointers are guaranteed to be non-NULL in -vxlan_xmit_one, e.g. if the iface is brought down. This can lead to the -following NULL dereference: - - BUG: kernel NULL pointer dereference, address: 0000000000000010 - Oops: Oops: 0000 [#1] SMP NOPTI - RIP: 0010:vxlan_xmit_one+0xbb3/0x1580 - Call Trace: - vxlan_xmit+0x429/0x610 - dev_hard_start_xmit+0x55/0xa0 - __dev_queue_xmit+0x6d0/0x7f0 - ip_finish_output2+0x24b/0x590 - ip_output+0x63/0x110 - -Mentioned commits changed the code path in vxlan_xmit_one and as a side -effect the sock4/6 pointer validity checks in vxlan(6)_get_route were -lost. Fix this by adding back checks. - -Since both commits being fixed were released in the same version (v6.7) -and are strongly related, bundle the fixes in a single commit. - -Reported-by: Liang Li -Fixes: 6f19b2c136d9 ("vxlan: use generic function for tunnel IPv4 route lookup") -Fixes: 2aceb896ee18 ("vxlan: use generic function for tunnel IPv6 route lookup") -Cc: Beniamino Galvani -Signed-off-by: Antoine Tenart -Reviewed-by: Ido Schimmel -Tested-by: Ido Schimmel -Link: https://patch.msgid.link/20251126102627.74223-1-atenart@kernel.org -Signed-off-by: Jakub Kicinski ---- - drivers/net/vxlan/vxlan_core.c | 18 +++++++++++++++--- - 1 file changed, 15 insertions(+), 3 deletions(-) - -diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c -index dab864bc733c..df9f37df282a 100644 ---- a/drivers/net/vxlan/vxlan_core.c -+++ b/drivers/net/vxlan/vxlan_core.c -@@ -2348,7 +2348,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - int addr_family; - __u8 tos, ttl; - int ifindex; -- int err; -+ int err = 0; - u32 flags = vxlan->cfg.flags; - bool use_cache; - bool udp_sum = false; -@@ -2453,12 +2453,18 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - - rcu_read_lock(); - if (addr_family == AF_INET) { -- struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock); -+ struct vxlan_sock *sock4; - u16 ipcb_flags = 0; - struct rtable *rt; - __be16 df = 0; - __be32 saddr; - -+ sock4 = rcu_dereference(vxlan->vn4_sock); -+ if (unlikely(!sock4)) { -+ reason = SKB_DROP_REASON_DEV_READY; -+ goto tx_error; -+ } -+ - if (!ifindex) - ifindex = sock4->sock->sk->sk_bound_dev_if; - -@@ -2533,10 +2539,16 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, - ipcb_flags); - #if IS_ENABLED(CONFIG_IPV6) - } else { -- struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock); -+ struct vxlan_sock *sock6; - struct in6_addr saddr; - u16 ip6cb_flags = 0; - -+ sock6 = rcu_dereference(vxlan->vn6_sock); -+ if (unlikely(!sock6)) { -+ reason = SKB_DROP_REASON_DEV_READY; -+ goto tx_error; -+ } -+ - if (!ifindex) - ifindex = sock6->sock->sk->sk_bound_dev_if; - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68354.patch b/SPECS/kernel/CVE-2025-68354.patch deleted file mode 100644 index aebdd714b..000000000 --- a/SPECS/kernel/CVE-2025-68354.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 698e858cd9b3e1a76716eb31e3a1128f23e9db61 Mon Sep 17 00:00:00 2001 -From: sparkhuang -Date: Thu, 27 Nov 2025 10:57:16 +0800 -Subject: [PATCH 28/51] regulator: core: Protect regulator_supply_alias_list - with regulator_list_mutex - -regulator_supply_alias_list was accessed without any locking in -regulator_supply_alias(), regulator_register_supply_alias(), and -regulator_unregister_supply_alias(). Concurrent registration, -unregistration and lookups can race, leading to: - -1 use-after-free if an alias entry is removed while being read, -2 duplicate entries when two threads register the same alias, -3 inconsistent alias mappings observed by consumers. - -Protect all traversals, insertions and deletions on -regulator_supply_alias_list with the existing regulator_list_mutex. - -Fixes: a06ccd9c3785f ("regulator: core: Add ability to create a lookup alias for supply") -Signed-off-by: sparkhuang -Reviewed-by: Charles Keepax -Link: https://patch.msgid.link/20251127025716.5440-1-huangshaobo3@xiaomi.com -Signed-off-by: Mark Brown ---- - drivers/regulator/core.c | 32 ++++++++++++++++++++------------ - 1 file changed, 20 insertions(+), 12 deletions(-) - -diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c -index 554d83c4af0c..9c7c65063839 100644 ---- a/drivers/regulator/core.c -+++ b/drivers/regulator/core.c -@@ -1942,6 +1942,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply) - { - struct regulator_supply_alias *map; - -+ mutex_lock(®ulator_list_mutex); - map = regulator_find_supply_alias(*dev, *supply); - if (map) { - dev_dbg(*dev, "Mapping supply %s to %s,%s\n", -@@ -1950,6 +1951,7 @@ static void regulator_supply_alias(struct device **dev, const char **supply) - *dev = map->alias_dev; - *supply = map->alias_supply; - } -+ mutex_unlock(®ulator_list_mutex); - } - - static int regulator_match(struct device *dev, const void *data) -@@ -2492,22 +2494,26 @@ int regulator_register_supply_alias(struct device *dev, const char *id, - const char *alias_id) - { - struct regulator_supply_alias *map; -+ struct regulator_supply_alias *new_map; - -- map = regulator_find_supply_alias(dev, id); -- if (map) -- return -EEXIST; -- -- map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL); -- if (!map) -+ new_map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL); -+ if (!new_map) - return -ENOMEM; - -- map->src_dev = dev; -- map->src_supply = id; -- map->alias_dev = alias_dev; -- map->alias_supply = alias_id; -- -- list_add(&map->list, ®ulator_supply_alias_list); -+ mutex_lock(®ulator_list_mutex); -+ map = regulator_find_supply_alias(dev, id); -+ if (map) { -+ mutex_unlock(®ulator_list_mutex); -+ kfree(new_map); -+ return -EEXIST; -+ } - -+ new_map->src_dev = dev; -+ new_map->src_supply = id; -+ new_map->alias_dev = alias_dev; -+ new_map->alias_supply = alias_id; -+ list_add(&new_map->list, ®ulator_supply_alias_list); -+ mutex_unlock(®ulator_list_mutex); - pr_info("Adding alias for supply %s,%s -> %s,%s\n", - id, dev_name(dev), alias_id, dev_name(alias_dev)); - -@@ -2527,11 +2533,13 @@ void regulator_unregister_supply_alias(struct device *dev, const char *id) - { - struct regulator_supply_alias *map; - -+ mutex_lock(®ulator_list_mutex); - map = regulator_find_supply_alias(dev, id); - if (map) { - list_del(&map->list); - kfree(map); - } -+ mutex_unlock(®ulator_list_mutex); - } - EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68358.patch b/SPECS/kernel/CVE-2025-68358.patch deleted file mode 100644 index 455384a0b..000000000 --- a/SPECS/kernel/CVE-2025-68358.patch +++ /dev/null @@ -1,251 +0,0 @@ -From b1848a20aec6b8b9d692098f47b19dc2c7a6e6b9 Mon Sep 17 00:00:00 2001 -From: Boris Burkov -Date: Wed, 1 Oct 2025 17:20:22 -0700 -Subject: [PATCH 25/51] btrfs: fix racy bitfield write in - btrfs_clear_space_info_full() - -From the memory-barriers.txt document regarding memory barrier ordering -guarantees: - - (*) These guarantees do not apply to bitfields, because compilers often - generate code to modify these using non-atomic read-modify-write - sequences. Do not attempt to use bitfields to synchronize parallel - algorithms. - - (*) Even in cases where bitfields are protected by locks, all fields - in a given bitfield must be protected by one lock. If two fields - in a given bitfield are protected by different locks, the compiler's - non-atomic read-modify-write sequences can cause an update to one - field to corrupt the value of an adjacent field. - -btrfs_space_info has a bitfield sharing an underlying word consisting of -the fields full, chunk_alloc, and flush: - -struct btrfs_space_info { - struct btrfs_fs_info * fs_info; /* 0 8 */ - struct btrfs_space_info * parent; /* 8 8 */ - ... - int clamp; /* 172 4 */ - unsigned int full:1; /* 176: 0 4 */ - unsigned int chunk_alloc:1; /* 176: 1 4 */ - unsigned int flush:1; /* 176: 2 4 */ - ... - -Therefore, to be safe from parallel read-modify-writes losing a write to -one of the bitfield members protected by a lock, all writes to all the -bitfields must use the lock. They almost universally do, except for -btrfs_clear_space_info_full() which iterates over the space_infos and -writes out found->full = 0 without a lock. - -Imagine that we have one thread completing a transaction in which we -finished deleting a block_group and are thus calling -btrfs_clear_space_info_full() while simultaneously the data reclaim -ticket infrastructure is running do_async_reclaim_data_space(): - - T1 T2 -btrfs_commit_transaction - btrfs_clear_space_info_full - data_sinfo->full = 0 - READ: full:0, chunk_alloc:0, flush:1 - do_async_reclaim_data_space(data_sinfo) - spin_lock(&space_info->lock); - if(list_empty(tickets)) - space_info->flush = 0; - READ: full: 0, chunk_alloc:0, flush:1 - MOD/WRITE: full: 0, chunk_alloc:0, flush:0 - spin_unlock(&space_info->lock); - return; - MOD/WRITE: full:0, chunk_alloc:0, flush:1 - -and now data_sinfo->flush is 1 but the reclaim worker has exited. This -breaks the invariant that flush is 0 iff there is no work queued or -running. Once this invariant is violated, future allocations that go -into __reserve_bytes() will add tickets to space_info->tickets but will -see space_info->flush is set to 1 and not queue the work. After this, -they will block forever on the resulting ticket, as it is now impossible -to kick the worker again. - -I also confirmed by looking at the assembly of the affected kernel that -it is doing RMW operations. For example, to set the flush (3rd) bit to 0, -the assembly is: - andb $0xfb,0x60(%rbx) -and similarly for setting the full (1st) bit to 0: - andb $0xfe,-0x20(%rax) - -So I think this is really a bug on practical systems. I have observed -a number of systems in this exact state, but am currently unable to -reproduce it. - -Rather than leaving this footgun lying around for the future, take -advantage of the fact that there is room in the struct anyway, and that -it is already quite large and simply change the three bitfield members to -bools. This avoids writes to space_info->full having any effect on -writes to space_info->flush, regardless of locking. - -Fixes: 957780eb2788 ("Btrfs: introduce ticketed enospc infrastructure") -Reviewed-by: Qu Wenruo -Signed-off-by: Boris Burkov -Reviewed-by: David Sterba -Signed-off-by: David Sterba ---- - fs/btrfs/block-group.c | 6 +++--- - fs/btrfs/space-info.c | 22 +++++++++++----------- - fs/btrfs/space-info.h | 6 +++--- - 3 files changed, 17 insertions(+), 17 deletions(-) - -diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c -index 499a9edf0ca3..a368d6ac98ed 100644 ---- a/fs/btrfs/block-group.c -+++ b/fs/btrfs/block-group.c -@@ -4215,7 +4215,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, - mutex_unlock(&fs_info->chunk_mutex); - } else { - /* Proceed with allocation */ -- space_info->chunk_alloc = 1; -+ space_info->chunk_alloc = true; - wait_for_alloc = false; - spin_unlock(&space_info->lock); - } -@@ -4264,7 +4264,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, - spin_lock(&space_info->lock); - if (ret < 0) { - if (ret == -ENOSPC) -- space_info->full = 1; -+ space_info->full = true; - else - goto out; - } else { -@@ -4274,7 +4274,7 @@ int btrfs_chunk_alloc(struct btrfs_trans_handle *trans, - - space_info->force_alloc = CHUNK_ALLOC_NO_FORCE; - out: -- space_info->chunk_alloc = 0; -+ space_info->chunk_alloc = false; - spin_unlock(&space_info->lock); - mutex_unlock(&fs_info->chunk_mutex); - -diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c -index 0481c693ac2e..b05ab1122a42 100644 ---- a/fs/btrfs/space-info.c -+++ b/fs/btrfs/space-info.c -@@ -192,7 +192,7 @@ void btrfs_clear_space_info_full(struct btrfs_fs_info *info) - struct btrfs_space_info *found; - - list_for_each_entry(found, head, list) -- found->full = 0; -+ found->full = false; - } - - /* -@@ -372,7 +372,7 @@ void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info, - space_info->bytes_readonly += block_group->bytes_super; - btrfs_space_info_update_bytes_zone_unusable(space_info, block_group->zone_unusable); - if (block_group->length > 0) -- space_info->full = 0; -+ space_info->full = false; - btrfs_try_granting_tickets(info, space_info); - spin_unlock(&space_info->lock); - -@@ -1146,7 +1146,7 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info) - spin_lock(&space_info->lock); - to_reclaim = btrfs_calc_reclaim_metadata_size(fs_info, space_info); - if (!to_reclaim) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1158,7 +1158,7 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info) - flush_space(fs_info, space_info, to_reclaim, flush_state, false); - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1201,7 +1201,7 @@ static void do_async_reclaim_metadata_space(struct btrfs_space_info *space_info) - flush_state = FLUSH_DELAYED_ITEMS_NR; - commit_cycles--; - } else { -- space_info->flush = 0; -+ space_info->flush = false; - } - } else { - flush_state = FLUSH_DELAYED_ITEMS_NR; -@@ -1383,7 +1383,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1394,7 +1394,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - flush_space(fs_info, space_info, U64_MAX, ALLOC_CHUNK_FORCE, false); - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1411,7 +1411,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - data_flush_states[flush_state], false); - spin_lock(&space_info->lock); - if (list_empty(&space_info->tickets)) { -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - return; - } -@@ -1428,7 +1428,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - if (maybe_fail_all_tickets(fs_info, space_info)) - flush_state = 0; - else -- space_info->flush = 0; -+ space_info->flush = false; - } else { - flush_state = 0; - } -@@ -1444,7 +1444,7 @@ static void do_async_reclaim_data_space(struct btrfs_space_info *space_info) - - aborted_fs: - maybe_fail_all_tickets(fs_info, space_info); -- space_info->flush = 0; -+ space_info->flush = false; - spin_unlock(&space_info->lock); - } - -@@ -1825,7 +1825,7 @@ static int __reserve_bytes(struct btrfs_fs_info *fs_info, - */ - maybe_clamp_preempt(fs_info, space_info); - -- space_info->flush = 1; -+ space_info->flush = true; - trace_btrfs_trigger_flush(fs_info, - space_info->flags, - orig_bytes, flush, -diff --git a/fs/btrfs/space-info.h b/fs/btrfs/space-info.h -index 679f22efb407..a846f63585c9 100644 ---- a/fs/btrfs/space-info.h -+++ b/fs/btrfs/space-info.h -@@ -142,11 +142,11 @@ struct btrfs_space_info { - flushing. The value is >> clamp, so turns - out to be a 2^clamp divisor. */ - -- unsigned int full:1; /* indicates that we cannot allocate any more -+ bool full; /* indicates that we cannot allocate any more - chunks for this space */ -- unsigned int chunk_alloc:1; /* set if we are allocating a chunk */ -+ bool chunk_alloc; /* set if we are allocating a chunk */ - -- unsigned int flush:1; /* set if we are trying to make space */ -+ bool flush; /* set if we are trying to make space */ - - unsigned int force_alloc; /* set if we need to force a chunk - alloc for this space */ --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68359.patch b/SPECS/kernel/CVE-2025-68359.patch deleted file mode 100644 index 7eb082868..000000000 --- a/SPECS/kernel/CVE-2025-68359.patch +++ /dev/null @@ -1,150 +0,0 @@ -From 0a25d5c140f5359ada727cdb3f5f378599fea799 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Miquel=20Sabat=C3=A9=20Sol=C3=A0?= -Date: Wed, 1 Oct 2025 20:05:03 +0200 -Subject: [PATCH 30/51] btrfs: fix double free of qgroup record after failure - to add delayed ref head -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In the previous code it was possible to incur into a double kfree() -scenario when calling add_delayed_ref_head(). This could happen if the -record was reported to already exist in the -btrfs_qgroup_trace_extent_nolock() call, but then there was an error -later on add_delayed_ref_head(). In this case, since -add_delayed_ref_head() returned an error, the caller went to free the -record. Since add_delayed_ref_head() couldn't set this kfree'd pointer -to NULL, then kfree() would have acted on a non-NULL 'record' object -which was pointing to memory already freed by the callee. - -The problem comes from the fact that the responsibility to kfree the -object is on both the caller and the callee at the same time. Hence, the -fix for this is to shift the ownership of the 'qrecord' object out of -the add_delayed_ref_head(). That is, we will never attempt to kfree() -the given object inside of this function, and will expect the caller to -act on the 'qrecord' object on its own. The only exception where the -'qrecord' object cannot be kfree'd is if it was inserted into the -tracing logic, for which we already have the 'qrecord_inserted_ret' -boolean to account for this. Hence, the caller has to kfree the object -only if add_delayed_ref_head() reports not to have inserted it on the -tracing logic. - -As a side-effect of the above, we must guarantee that -'qrecord_inserted_ret' is properly initialized at the start of the -function, not at the end, and then set when an actual insert -happens. This way we avoid 'qrecord_inserted_ret' having an invalid -value on an early exit. - -The documentation from the add_delayed_ref_head() has also been updated -to reflect on the exact ownership of the 'qrecord' object. - -Fixes: 6ef8fbce0104 ("btrfs: fix missing error handling when adding delayed ref with qgroups enabled") -Reviewed-by: Filipe Manana -Signed-off-by: Miquel Sabaté Solà -Signed-off-by: Filipe Manana -Signed-off-by: David Sterba ---- - fs/btrfs/delayed-ref.c | 43 ++++++++++++++++++++++++++++++++---------- - 1 file changed, 33 insertions(+), 10 deletions(-) - -diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c -index ca382c5b186f..39c7ad123167 100644 ---- a/fs/btrfs/delayed-ref.c -+++ b/fs/btrfs/delayed-ref.c -@@ -798,9 +798,13 @@ static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, - } - - /* -- * helper function to actually insert a head node into the rbtree. -- * this does all the dirty work in terms of maintaining the correct -- * overall modification count. -+ * Helper function to actually insert a head node into the xarray. This does all -+ * the dirty work in terms of maintaining the correct overall modification -+ * count. -+ * -+ * The caller is responsible for calling kfree() on @qrecord. More specifically, -+ * if this function reports that it did not insert it as noted in -+ * @qrecord_inserted_ret, then it's safe to call kfree() on it. - * - * Returns an error pointer in case of an error. - */ -@@ -814,7 +818,14 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_head *existing; - struct btrfs_delayed_ref_root *delayed_refs; - const unsigned long index = (head_ref->bytenr >> fs_info->sectorsize_bits); -- bool qrecord_inserted = false; -+ -+ /* -+ * If 'qrecord_inserted_ret' is provided, then the first thing we need -+ * to do is to initialize it to false just in case we have an exit -+ * before trying to insert the record. -+ */ -+ if (qrecord_inserted_ret) -+ *qrecord_inserted_ret = false; - - delayed_refs = &trans->transaction->delayed_refs; - lockdep_assert_held(&delayed_refs->lock); -@@ -833,6 +844,12 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - - /* Record qgroup extent info if provided */ - if (qrecord) { -+ /* -+ * Setting 'qrecord' but not 'qrecord_inserted_ret' will likely -+ * result in a memory leakage. -+ */ -+ ASSERT(qrecord_inserted_ret != NULL); -+ - int ret; - - ret = btrfs_qgroup_trace_extent_nolock(fs_info, delayed_refs, qrecord, -@@ -840,12 +857,10 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - if (ret) { - /* Clean up if insertion fails or item exists. */ - xa_release(&delayed_refs->dirty_extents, index); -- /* Caller responsible for freeing qrecord on error. */ - if (ret < 0) - return ERR_PTR(ret); -- kfree(qrecord); -- } else { -- qrecord_inserted = true; -+ } else if (qrecord_inserted_ret) { -+ *qrecord_inserted_ret = true; - } - } - -@@ -888,8 +903,6 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, - delayed_refs->num_heads++; - delayed_refs->num_heads_ready++; - } -- if (qrecord_inserted_ret) -- *qrecord_inserted_ret = qrecord_inserted; - - return head_ref; - } -@@ -1049,6 +1062,14 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, - xa_release(&delayed_refs->head_refs, index); - spin_unlock(&delayed_refs->lock); - ret = PTR_ERR(new_head_ref); -+ -+ /* -+ * It's only safe to call kfree() on 'qrecord' if -+ * add_delayed_ref_head() has _not_ inserted it for -+ * tracing. Otherwise we need to handle this here. -+ */ -+ if (!qrecord_reserved || qrecord_inserted) -+ goto free_head_ref; - goto free_record; - } - head_ref = new_head_ref; -@@ -1071,6 +1092,8 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, - - if (qrecord_inserted) - return btrfs_qgroup_trace_extent_post(trans, record, generic_ref->bytenr); -+ -+ kfree(record); - return 0; - - free_record: --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68363.patch b/SPECS/kernel/CVE-2025-68363.patch deleted file mode 100644 index dda9678f2..000000000 --- a/SPECS/kernel/CVE-2025-68363.patch +++ /dev/null @@ -1,65 +0,0 @@ -From dc2df6d0a3b828a3f8d487b75f1a769c7345ca3e Mon Sep 17 00:00:00 2001 -From: Martin KaFai Lau -Date: Wed, 12 Nov 2025 15:23:30 -0800 -Subject: [PATCH 09/51] bpf: Check skb->transport_header is set in - bpf_skb_check_mtu - -The bpf_skb_check_mtu helper needs to use skb->transport_header when -the BPF_MTU_CHK_SEGS flag is used: - - bpf_skb_check_mtu(skb, ifindex, &mtu_len, 0, BPF_MTU_CHK_SEGS) - -The transport_header is not always set. There is a WARN_ON_ONCE -report when CONFIG_DEBUG_NET is enabled + skb->gso_size is set + -bpf_prog_test_run is used: - -WARNING: CPU: 1 PID: 2216 at ./include/linux/skbuff.h:3071 - skb_gso_validate_network_len - bpf_skb_check_mtu - bpf_prog_3920e25740a41171_tc_chk_segs_flag # A test in the next patch - bpf_test_run - bpf_prog_test_run_skb - -For a normal ingress skb (not test_run), skb_reset_transport_header -is performed but there is plan to avoid setting it as described in -commit 2170a1f09148 ("net: no longer reset transport_header in __netif_receive_skb_core()"). - -This patch fixes the bpf helper by checking -skb_transport_header_was_set(). The check is done just before -skb->transport_header is used, to avoid breaking the existing bpf prog. -The WARN_ON_ONCE is limited to bpf_prog_test_run, so targeting bpf-next. - -Fixes: 34b2021cc616 ("bpf: Add BPF-helper for MTU checking") -Cc: Jesper Dangaard Brouer -Reported-by: Kaiyan Mei -Reported-by: Yinhao Hu -Signed-off-by: Martin KaFai Lau -Link: https://lore.kernel.org/r/20251112232331.1566074-1-martin.lau@linux.dev -Signed-off-by: Alexei Starovoitov ---- - net/core/filter.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/net/core/filter.c b/net/core/filter.c -index b20d5fecdbc9..47366ec94e58 100644 ---- a/net/core/filter.c -+++ b/net/core/filter.c -@@ -6414,9 +6414,12 @@ BPF_CALL_5(bpf_skb_check_mtu, struct sk_buff *, skb, - */ - if (skb_is_gso(skb)) { - ret = BPF_MTU_CHK_RET_SUCCESS; -- if (flags & BPF_MTU_CHK_SEGS && -- !skb_gso_validate_network_len(skb, mtu)) -- ret = BPF_MTU_CHK_RET_SEGS_TOOBIG; -+ if (flags & BPF_MTU_CHK_SEGS) { -+ if (!skb_transport_header_was_set(skb)) -+ return -EINVAL; -+ if (!skb_gso_validate_network_len(skb, mtu)) -+ ret = BPF_MTU_CHK_RET_SEGS_TOOBIG; -+ } - } - out: - *mtu_len = mtu; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68366.patch b/SPECS/kernel/CVE-2025-68366.patch deleted file mode 100644 index 104ecfd0d..000000000 --- a/SPECS/kernel/CVE-2025-68366.patch +++ /dev/null @@ -1,67 +0,0 @@ -From f06abb6afac3a44f47c0a309729af7fd22a1eb50 Mon Sep 17 00:00:00 2001 -From: Zheng Qixing -Date: Mon, 10 Nov 2025 20:49:20 +0800 -Subject: [PATCH 05/51] nbd: defer config unlock in nbd_genl_connect - -There is one use-after-free warning when running NBD_CMD_CONNECT and -NBD_CLEAR_SOCK: - -nbd_genl_connect - nbd_alloc_and_init_config // config_refs=1 - nbd_start_device // config_refs=2 - set NBD_RT_HAS_CONFIG_REF open nbd // config_refs=3 - recv_work done // config_refs=2 - NBD_CLEAR_SOCK // config_refs=1 - close nbd // config_refs=0 - refcount_inc -> uaf - -------------[ cut here ]------------ -refcount_t: addition on 0; use-after-free. -WARNING: CPU: 24 PID: 1014 at lib/refcount.c:25 refcount_warn_saturate+0x12e/0x290 - nbd_genl_connect+0x16d0/0x1ab0 - genl_family_rcv_msg_doit+0x1f3/0x310 - genl_rcv_msg+0x44a/0x790 - -The issue can be easily reproduced by adding a small delay before -refcount_inc(&nbd->config_refs) in nbd_genl_connect(): - - mutex_unlock(&nbd->config_lock); - if (!ret) { - set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags); -+ printk("before sleep\n"); -+ mdelay(5 * 1000); -+ printk("after sleep\n"); - refcount_inc(&nbd->config_refs); - nbd_connect_reply(info, nbd->index); - } - -Fixes: e46c7287b1c2 ("nbd: add a basic netlink interface") -Signed-off-by: Zheng Qixing -Reviewed-by: Yu Kuai -Signed-off-by: Jens Axboe ---- - drivers/block/nbd.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c -index ad39ab95ea66..99fde2be65f9 100644 ---- a/drivers/block/nbd.c -+++ b/drivers/block/nbd.c -@@ -2241,12 +2241,13 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info) - - ret = nbd_start_device(nbd); - out: -- mutex_unlock(&nbd->config_lock); - if (!ret) { - set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags); - refcount_inc(&nbd->config_refs); - nbd_connect_reply(info, nbd->index); - } -+ mutex_unlock(&nbd->config_lock); -+ - nbd_config_put(nbd); - if (put_dev) - nbd_put(nbd); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68368.patch b/SPECS/kernel/CVE-2025-68368.patch deleted file mode 100644 index 93fbaebf3..000000000 --- a/SPECS/kernel/CVE-2025-68368.patch +++ /dev/null @@ -1,174 +0,0 @@ -From aaa0398c24a0d4ea8ce04f089d6b0a3f0b0d293b Mon Sep 17 00:00:00 2001 -From: Li Nan -Date: Mon, 3 Nov 2025 20:57:54 +0800 -Subject: [PATCH 32/51] md: init bioset in mddev_init - -IO operations may be needed before md_run(), such as updating metadata -after writing sysfs. Without bioset, this triggers a NULL pointer -dereference as below: - - BUG: kernel NULL pointer dereference, address: 0000000000000020 - Call Trace: - md_update_sb+0x658/0xe00 - new_level_store+0xc5/0x120 - md_attr_store+0xc9/0x1e0 - sysfs_kf_write+0x6f/0xa0 - kernfs_fop_write_iter+0x141/0x2a0 - vfs_write+0x1fc/0x5a0 - ksys_write+0x79/0x180 - __x64_sys_write+0x1d/0x30 - x64_sys_call+0x2818/0x2880 - do_syscall_64+0xa9/0x580 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -Reproducer -``` - mdadm -CR /dev/md0 -l1 -n2 /dev/sd[cd] - echo inactive > /sys/block/md0/md/array_state - echo 10 > /sys/block/md0/md/new_level -``` - -mddev_init() can only be called once per mddev, no need to test if bioset -has been initialized anymore. - -Link: https://lore.kernel.org/linux-raid/20251103125757.1405796-3-linan666@huaweicloud.com -Fixes: d981ed841930 ("md: Add new_level sysfs interface") -Signed-off-by: Li Nan -Reviewed-by: Xiao Ni -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 68 +++++++++++++++++++++++-------------------------- - 1 file changed, 32 insertions(+), 36 deletions(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index 4e033c26fdd4..b260aff7882c 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -679,6 +679,7 @@ static void no_op(struct percpu_ref *r) {} - - int mddev_init(struct mddev *mddev) - { -+ int err = 0; - - if (percpu_ref_init(&mddev->active_io, active_io_release, - PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) -@@ -686,10 +687,23 @@ int mddev_init(struct mddev *mddev) - - if (percpu_ref_init(&mddev->writes_pending, no_op, - PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) { -- percpu_ref_exit(&mddev->active_io); -- return -ENOMEM; -+ err = -ENOMEM; -+ goto exit_acitve_io; - } - -+ err = bioset_init(&mddev->bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -+ if (err) -+ goto exit_writes_pending; -+ -+ err = bioset_init(&mddev->sync_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -+ if (err) -+ goto exit_bio_set; -+ -+ err = bioset_init(&mddev->io_clone_set, BIO_POOL_SIZE, -+ offsetof(struct md_io_clone, bio_clone), 0); -+ if (err) -+ goto exit_sync_set; -+ - /* We want to start with the refcount at zero */ - percpu_ref_put(&mddev->writes_pending); - -@@ -719,11 +733,24 @@ int mddev_init(struct mddev *mddev) - INIT_WORK(&mddev->del_work, mddev_delayed_delete); - - return 0; -+ -+exit_sync_set: -+ bioset_exit(&mddev->sync_set); -+exit_bio_set: -+ bioset_exit(&mddev->bio_set); -+exit_writes_pending: -+ percpu_ref_exit(&mddev->writes_pending); -+exit_acitve_io: -+ percpu_ref_exit(&mddev->active_io); -+ return err; - } - EXPORT_SYMBOL_GPL(mddev_init); - - void mddev_destroy(struct mddev *mddev) - { -+ bioset_exit(&mddev->bio_set); -+ bioset_exit(&mddev->sync_set); -+ bioset_exit(&mddev->io_clone_set); - percpu_ref_exit(&mddev->active_io); - percpu_ref_exit(&mddev->writes_pending); - } -@@ -6212,29 +6239,9 @@ int md_run(struct mddev *mddev) - nowait = nowait && bdev_nowait(rdev->bdev); - } - -- if (!bioset_initialized(&mddev->bio_set)) { -- err = bioset_init(&mddev->bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -- if (err) -- return err; -- } -- if (!bioset_initialized(&mddev->sync_set)) { -- err = bioset_init(&mddev->sync_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS); -- if (err) -- goto exit_bio_set; -- } -- -- if (!bioset_initialized(&mddev->io_clone_set)) { -- err = bioset_init(&mddev->io_clone_set, BIO_POOL_SIZE, -- offsetof(struct md_io_clone, bio_clone), 0); -- if (err) -- goto exit_sync_set; -- } -- - pers = get_pers(mddev->level, mddev->clevel); -- if (!pers) { -- err = -EINVAL; -- goto abort; -- } -+ if (!pers) -+ return -EINVAL; - if (mddev->level != pers->head.id) { - mddev->level = pers->head.id; - mddev->new_level = pers->head.id; -@@ -6245,8 +6252,7 @@ int md_run(struct mddev *mddev) - pers->start_reshape == NULL) { - /* This personality cannot handle reshaping... */ - put_pers(pers); -- err = -EINVAL; -- goto abort; -+ return -EINVAL; - } - - if (pers->sync_request) { -@@ -6373,12 +6379,6 @@ int md_run(struct mddev *mddev) - mddev->private = NULL; - put_pers(pers); - mddev->bitmap_ops->destroy(mddev); --abort: -- bioset_exit(&mddev->io_clone_set); --exit_sync_set: -- bioset_exit(&mddev->sync_set); --exit_bio_set: -- bioset_exit(&mddev->bio_set); - return err; - } - EXPORT_SYMBOL_GPL(md_run); -@@ -6599,10 +6599,6 @@ static void __md_stop(struct mddev *mddev) - mddev->private = NULL; - put_pers(pers); - clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); -- -- bioset_exit(&mddev->bio_set); -- bioset_exit(&mddev->sync_set); -- bioset_exit(&mddev->io_clone_set); - } - - void md_stop(struct mddev *mddev) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68371.patch b/SPECS/kernel/CVE-2025-68371.patch deleted file mode 100644 index c9be703f4..000000000 --- a/SPECS/kernel/CVE-2025-68371.patch +++ /dev/null @@ -1,90 +0,0 @@ -From db94ca4006fcd8604d4e687d1417ff2a7d9391e7 Mon Sep 17 00:00:00 2001 -From: Mike McGowen -Date: Thu, 6 Nov 2025 10:38:20 -0600 -Subject: [PATCH 33/51] scsi: smartpqi: Fix device resources accessed after - device removal - -Correct possible race conditions during device removal. - -Previously, a scheduled work item to reset a LUN could still execute -after the device was removed, leading to use-after-free and other -resource access issues. - -This race condition occurs because the abort handler may schedule a LUN -reset concurrently with device removal via sdev_destroy(), leading to -use-after-free and improper access to freed resources. - - - Check in the device reset handler if the device is still present in - the controller's SCSI device list before running; if not, the reset - is skipped. - - - Cancel any pending TMF work that has not started in sdev_destroy(). - - - Ensure device freeing in sdev_destroy() is done while holding the - LUN reset mutex to avoid races with ongoing resets. - -Fixes: 2d80f4054f7f ("scsi: smartpqi: Update deleting a LUN via sysfs") -Reviewed-by: Scott Teel -Reviewed-by: Scott Benesh -Signed-off-by: Mike McGowen -Signed-off-by: Don Brace -Link: https://patch.msgid.link/20251106163823.786828-3-don.brace@microchip.com -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/smartpqi/smartpqi_init.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c -index 125944941601..9f4f6e74f3e3 100644 ---- a/drivers/scsi/smartpqi/smartpqi_init.c -+++ b/drivers/scsi/smartpqi/smartpqi_init.c -@@ -6409,10 +6409,22 @@ static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev - - static int pqi_device_reset_handler(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device, u8 lun, struct scsi_cmnd *scmd, u8 scsi_opcode) - { -+ unsigned long flags; - int rc; - - mutex_lock(&ctrl_info->lun_reset_mutex); - -+ spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); -+ if (pqi_find_scsi_dev(ctrl_info, device->bus, device->target, device->lun) == NULL) { -+ dev_warn(&ctrl_info->pci_dev->dev, -+ "skipping reset of scsi %d:%d:%d:%u, device has been removed\n", -+ ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun); -+ spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); -+ mutex_unlock(&ctrl_info->lun_reset_mutex); -+ return 0; -+ } -+ spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); -+ - dev_err(&ctrl_info->pci_dev->dev, - "resetting scsi %d:%d:%d:%u SCSI cmd at %p due to cmd opcode 0x%02x\n", - ctrl_info->scsi_host->host_no, device->bus, device->target, lun, scmd, scsi_opcode); -@@ -6593,7 +6605,9 @@ static void pqi_sdev_destroy(struct scsi_device *sdev) - { - struct pqi_ctrl_info *ctrl_info; - struct pqi_scsi_dev *device; -+ struct pqi_tmf_work *tmf_work; - int mutex_acquired; -+ unsigned int lun; - unsigned long flags; - - ctrl_info = shost_to_hba(sdev->host); -@@ -6620,8 +6634,13 @@ static void pqi_sdev_destroy(struct scsi_device *sdev) - - mutex_unlock(&ctrl_info->scan_mutex); - -+ for (lun = 0, tmf_work = device->tmf_work; lun < PQI_MAX_LUNS_PER_DEVICE; lun++, tmf_work++) -+ cancel_work_sync(&tmf_work->work_struct); -+ -+ mutex_lock(&ctrl_info->lun_reset_mutex); - pqi_dev_info(ctrl_info, "removed", device); - pqi_free_device(device); -+ mutex_unlock(&ctrl_info->lun_reset_mutex); - } - - static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68372.patch b/SPECS/kernel/CVE-2025-68372.patch deleted file mode 100644 index 15aad0b19..000000000 --- a/SPECS/kernel/CVE-2025-68372.patch +++ /dev/null @@ -1,97 +0,0 @@ -From 96303d7f8485aca392dadff87901e54df5d61498 Mon Sep 17 00:00:00 2001 -From: Zheng Qixing -Date: Sat, 8 Nov 2025 15:02:02 +0800 -Subject: [PATCH 51/51] nbd: defer config put in recv_work - -There is one uaf issue in recv_work when running NBD_CLEAR_SOCK and -NBD_CMD_RECONFIGURE: - nbd_genl_connect // conf_ref=2 (connect and recv_work A) - nbd_open // conf_ref=3 - recv_work A done // conf_ref=2 - NBD_CLEAR_SOCK // conf_ref=1 - nbd_genl_reconfigure // conf_ref=2 (trigger recv_work B) - close nbd // conf_ref=1 - recv_work B - config_put // conf_ref=0 - atomic_dec(&config->recv_threads); -> UAF - -Or only running NBD_CLEAR_SOCK: - nbd_genl_connect // conf_ref=2 - nbd_open // conf_ref=3 - NBD_CLEAR_SOCK // conf_ref=2 - close nbd - nbd_release - config_put // conf_ref=1 - recv_work - config_put // conf_ref=0 - atomic_dec(&config->recv_threads); -> UAF - -Commit 87aac3a80af5 ("nbd: call nbd_config_put() before notifying the -waiter") moved nbd_config_put() to run before waking up the waiter in -recv_work, in order to ensure that nbd_start_device_ioctl() would not -be woken up while nbd->task_recv was still uncleared. - -However, in nbd_start_device_ioctl(), after being woken up it explicitly -calls flush_workqueue() to make sure all current works are finished. -Therefore, there is no need to move the config put ahead of the wakeup. - -Move nbd_config_put() to the end of recv_work, so that the reference is -held for the whole lifetime of the worker thread. This makes sure the -config cannot be freed while recv_work is still running, even if clear -+ reconfigure interleave. - -In addition, we don't need to worry about recv_work dropping the last -nbd_put (which causes deadlock): - -path A (netlink with NBD_CFLAG_DESTROY_ON_DISCONNECT): - connect // nbd_refs=1 (trigger recv_work) - open nbd // nbd_refs=2 - NBD_CLEAR_SOCK - close nbd - nbd_release - nbd_disconnect_and_put - flush_workqueue // recv_work done - nbd_config_put - nbd_put // nbd_refs=1 - nbd_put // nbd_refs=0 - queue_work - -path B (netlink without NBD_CFLAG_DESTROY_ON_DISCONNECT): - connect // nbd_refs=2 (trigger recv_work) - open nbd // nbd_refs=3 - NBD_CLEAR_SOCK // conf_refs=2 - close nbd - nbd_release - nbd_config_put // conf_refs=1 - nbd_put // nbd_refs=2 - recv_work done // conf_refs=0, nbd_refs=1 - rmmod // nbd_refs=0 - -Reported-by: syzbot+56fbf4c7ddf65e95c7cc@syzkaller.appspotmail.com -Closes: https://lore.kernel.org/all/6907edce.a70a0220.37351b.0014.GAE@google.com/T/ -Fixes: 87aac3a80af5 ("nbd: make the config put is called before the notifying the waiter") -Depends-on: e2daec488c57 ("nbd: Fix hungtask when nbd_config_put") -Signed-off-by: Zheng Qixing -Signed-off-by: Jens Axboe ---- - drivers/block/nbd.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c -index 99fde2be65f9..9ba4b20b80ba 100644 ---- a/drivers/block/nbd.c -+++ b/drivers/block/nbd.c -@@ -1024,9 +1024,9 @@ static void recv_work(struct work_struct *work) - nbd_mark_nsock_dead(nbd, nsock, 1); - mutex_unlock(&nsock->tx_lock); - -- nbd_config_put(nbd); - atomic_dec(&config->recv_threads); - wake_up(&config->recv_wq); -+ nbd_config_put(nbd); - kfree(args); - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68373-2.patch b/SPECS/kernel/CVE-2025-68373-2.patch deleted file mode 100644 index 03003a60d..000000000 --- a/SPECS/kernel/CVE-2025-68373-2.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 61589ec93c17b6993ec2541e2647b0e6df015185 Mon Sep 17 00:00:00 2001 -From: Xiao Ni -Date: Wed, 29 Oct 2025 14:34:19 +0800 -Subject: [PATCH 37/51] md: avoid repeated calls to del_gendisk - -There is a uaf problem which is found by case 23rdev-lifetime: - -Oops: general protection fault, probably for non-canonical address 0xdead000000000122 -RIP: 0010:bdi_unregister+0x4b/0x170 -Call Trace: - - __del_gendisk+0x356/0x3e0 - mddev_unlock+0x351/0x360 - rdev_attr_store+0x217/0x280 - kernfs_fop_write_iter+0x14a/0x210 - vfs_write+0x29e/0x550 - ksys_write+0x74/0xf0 - do_syscall_64+0xbb/0x380 - entry_SYSCALL_64_after_hwframe+0x77/0x7f -RIP: 0033:0x7ff5250a177e - -The sequence is: -1. rdev remove path gets reconfig_mutex -2. rdev remove path release reconfig_mutex in mddev_unlock -3. md stop calls do_md_stop and sets MD_DELETED -4. rdev remove path calls del_gendisk because MD_DELETED is set -5. md stop path release reconfig_mutex and calls del_gendisk again - -So there is a race condition we should resolve. This patch adds a -flag MD_DO_DELETE to avoid the race condition. - -Link: https://lore.kernel.org/linux-raid/20251029063419.21700-1-xni@redhat.com -Fixes: 9e59d609763f ("md: call del_gendisk in control path") -Signed-off-by: Xiao Ni -Suggested-by: Yu Kuai -Reviewed-by: Li Nan -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 3 ++- - drivers/md/md.h | 1 + - 2 files changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index 5aa08663b9bb..55262f7bdf72 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -914,7 +914,8 @@ void mddev_unlock(struct mddev *mddev) - * do_md_stop. dm raid only uses md_stop to stop. So dm raid - * doesn't need to check MD_DELETED when getting reconfig lock - */ -- if (test_bit(MD_DELETED, &mddev->flags)) { -+ if (test_bit(MD_DELETED, &mddev->flags) && -+ !test_and_set_bit(MD_DO_DELETE, &mddev->flags)) { - kobject_del(&mddev->kobj); - del_gendisk(mddev->gendisk); - } -diff --git a/drivers/md/md.h b/drivers/md/md.h -index 51af29a03079..e0bc35543217 100644 ---- a/drivers/md/md.h -+++ b/drivers/md/md.h -@@ -353,6 +353,7 @@ enum mddev_flags { - MD_HAS_MULTIPLE_PPLS, - MD_NOT_READY, - MD_BROKEN, -+ MD_DO_DELETE, - MD_DELETED, - }; - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68373.patch b/SPECS/kernel/CVE-2025-68373.patch deleted file mode 100644 index bb066a182..000000000 --- a/SPECS/kernel/CVE-2025-68373.patch +++ /dev/null @@ -1,55 +0,0 @@ -From ceb9af48dda3ef7c2542123c15646819e37dc69d Mon Sep 17 00:00:00 2001 -From: Xiao Ni -Date: Sun, 28 Sep 2025 09:24:24 +0800 -Subject: [PATCH 36/51] md: delete mddev kobj before deleting gendisk kobj - -In sync del gendisk path, it deletes gendisk first and the directory -/sys/block/md is removed. Then it releases mddev kobj in a delayed work. -If we enable debug log in sysfs_remove_group, we can see the debug log -'sysfs group bitmap not found for kobject md'. It's the reason that the -parent kobj has been deleted, so it can't find parent directory. - -In creating path, it allocs gendisk first, then adds mddev kobj. So it -should delete mddev kobj before deleting gendisk. - -Before commit 9e59d609763f ("md: call del_gendisk in control path"), it -releases mddev kobj first. If the kobj hasn't been deleted, it does clean -job and deletes the kobj. Then it calls del_gendisk and releases gendisk -kobj. So it doesn't need to call kobject_del to delete mddev kobj. After -this patch, in sync del gendisk path, the sequence changes. So it needs -to call kobject_del to delete mddev kobj. - -After this patch, the sequence is: -1. kobject del mddev kobj -2. del_gendisk deletes gendisk kobj -3. mddev_delayed_delete releases mddev kobj -4. md_kobj_release releases gendisk kobj - -Link: https://lore.kernel.org/linux-raid/20250928012424.61370-1-xni@redhat.com -Fixes: 9e59d609763f ("md: call del_gendisk in control path") -Signed-off-by: Xiao Ni -Reviewed-by: Li Nan -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index b260aff7882c..5aa08663b9bb 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -914,8 +914,10 @@ void mddev_unlock(struct mddev *mddev) - * do_md_stop. dm raid only uses md_stop to stop. So dm raid - * doesn't need to check MD_DELETED when getting reconfig lock - */ -- if (test_bit(MD_DELETED, &mddev->flags)) -+ if (test_bit(MD_DELETED, &mddev->flags)) { -+ kobject_del(&mddev->kobj); - del_gendisk(mddev->gendisk); -+ } - } - } - EXPORT_SYMBOL_GPL(mddev_unlock); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68374.patch b/SPECS/kernel/CVE-2025-68374.patch deleted file mode 100644 index 6fc45f42c..000000000 --- a/SPECS/kernel/CVE-2025-68374.patch +++ /dev/null @@ -1,109 +0,0 @@ -From ae3bcdd6bd49bedc7047b5ba709fba63a171a85a Mon Sep 17 00:00:00 2001 -From: Yun Zhou -Date: Wed, 15 Oct 2025 16:32:27 +0800 -Subject: [PATCH 39/51] md: fix rcu protection in md_wakeup_thread - -We attempted to use RCU to protect the pointer 'thread', but directly -passed the value when calling md_wakeup_thread(). This means that the -RCU pointer has been acquired before rcu_read_lock(), which renders -rcu_read_lock() ineffective and could lead to a use-after-free. - -Link: https://lore.kernel.org/linux-raid/20251015083227.1079009-1-yun.zhou@windriver.com -Fixes: 446931543982 ("md: protect md_thread with rcu") -Signed-off-by: Yun Zhou -Reviewed-by: Li Nan -Reviewed-by: Yu Kuai -Signed-off-by: Yu Kuai ---- - drivers/md/md.c | 14 ++++++-------- - drivers/md/md.h | 8 +++++++- - 2 files changed, 13 insertions(+), 9 deletions(-) - -diff --git a/drivers/md/md.c b/drivers/md/md.c -index 55262f7bdf72..ec9d6e29e2d1 100644 ---- a/drivers/md/md.c -+++ b/drivers/md/md.c -@@ -100,7 +100,7 @@ static int remove_and_add_spares(struct mddev *mddev, - struct md_rdev *this); - static void mddev_detach(struct mddev *mddev); - static void export_rdev(struct md_rdev *rdev, struct mddev *mddev); --static void md_wakeup_thread_directly(struct md_thread __rcu *thread); -+static void md_wakeup_thread_directly(struct md_thread __rcu **thread); - - /* - * Default number of read corrections we'll attempt on an rdev -@@ -5012,7 +5012,7 @@ static void stop_sync_thread(struct mddev *mddev, bool locked) - * Thread might be blocked waiting for metadata update which will now - * never happen - */ -- md_wakeup_thread_directly(mddev->sync_thread); -+ md_wakeup_thread_directly(&mddev->sync_thread); - if (work_pending(&mddev->sync_work)) - flush_work(&mddev->sync_work); - -@@ -8193,22 +8193,21 @@ static int md_thread(void *arg) - return 0; - } - --static void md_wakeup_thread_directly(struct md_thread __rcu *thread) -+static void md_wakeup_thread_directly(struct md_thread __rcu **thread) - { - struct md_thread *t; - - rcu_read_lock(); -- t = rcu_dereference(thread); -+ t = rcu_dereference(*thread); - if (t) - wake_up_process(t->tsk); - rcu_read_unlock(); - } - --void md_wakeup_thread(struct md_thread __rcu *thread) -+void __md_wakeup_thread(struct md_thread __rcu *thread) - { - struct md_thread *t; - -- rcu_read_lock(); - t = rcu_dereference(thread); - if (t) { - pr_debug("md: waking up MD thread %s.\n", t->tsk->comm); -@@ -8216,9 +8215,8 @@ void md_wakeup_thread(struct md_thread __rcu *thread) - if (wq_has_sleeper(&t->wqueue)) - wake_up(&t->wqueue); - } -- rcu_read_unlock(); - } --EXPORT_SYMBOL(md_wakeup_thread); -+EXPORT_SYMBOL(__md_wakeup_thread); - - struct md_thread *md_register_thread(void (*run) (struct md_thread *), - struct mddev *mddev, const char *name) -diff --git a/drivers/md/md.h b/drivers/md/md.h -index e0bc35543217..0a1a227f106b 100644 ---- a/drivers/md/md.h -+++ b/drivers/md/md.h -@@ -879,6 +879,12 @@ struct md_io_clone { - - #define THREAD_WAKEUP 0 - -+#define md_wakeup_thread(thread) do { \ -+ rcu_read_lock(); \ -+ __md_wakeup_thread(thread); \ -+ rcu_read_unlock(); \ -+} while (0) -+ - static inline void safe_put_page(struct page *p) - { - if (p) put_page(p); -@@ -892,7 +898,7 @@ extern struct md_thread *md_register_thread( - struct mddev *mddev, - const char *name); - extern void md_unregister_thread(struct mddev *mddev, struct md_thread __rcu **threadp); --extern void md_wakeup_thread(struct md_thread __rcu *thread); -+extern void __md_wakeup_thread(struct md_thread __rcu *thread); - extern void md_check_recovery(struct mddev *mddev); - extern void md_reap_sync_thread(struct mddev *mddev); - extern enum sync_action md_sync_action(struct mddev *mddev); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68375.patch b/SPECS/kernel/CVE-2025-68375.patch deleted file mode 100644 index 10d9ee902..000000000 --- a/SPECS/kernel/CVE-2025-68375.patch +++ /dev/null @@ -1,93 +0,0 @@ -From e45840cde6f163484bf568430d0ef58b0ed13bfc Mon Sep 17 00:00:00 2001 -From: Dapeng Mi -Date: Wed, 29 Oct 2025 18:21:26 +0800 -Subject: [PATCH 11/51] perf/x86: Fix NULL event access and potential PEBS - record loss - -When intel_pmu_drain_pebs_icl() is called to drain PEBS records, the -perf_event_overflow() could be called to process the last PEBS record. - -While perf_event_overflow() could trigger the interrupt throttle and -stop all events of the group, like what the below call-chain shows. - -perf_event_overflow() - -> __perf_event_overflow() - ->__perf_event_account_interrupt() - -> perf_event_throttle_group() - -> perf_event_throttle() - -> event->pmu->stop() - -> x86_pmu_stop() - -The side effect of stopping the events is that all corresponding event -pointers in cpuc->events[] array are cleared to NULL. - -Assume there are two PEBS events (event a and event b) in a group. When -intel_pmu_drain_pebs_icl() calls perf_event_overflow() to process the -last PEBS record of PEBS event a, interrupt throttle is triggered and -all pointers of event a and event b are cleared to NULL. Then -intel_pmu_drain_pebs_icl() tries to process the last PEBS record of -event b and encounters NULL pointer access. - -To avoid this issue, move cpuc->events[] clearing from x86_pmu_stop() -to x86_pmu_del(). It's safe since cpuc->active_mask or -cpuc->pebs_enabled is always checked before access the event pointer -from cpuc->events[]. - -Closes: https://lore.kernel.org/oe-lkp/202507042103.a15d2923-lkp@intel.com -Fixes: 9734e25fbf5a ("perf: Fix the throttle logic for a group") -Reported-by: kernel test robot -Suggested-by: Peter Zijlstra -Signed-off-by: Dapeng Mi -Signed-off-by: Peter Zijlstra (Intel) -Link: https://patch.msgid.link/20251029102136.61364-3-dapeng1.mi@linux.intel.com ---- - arch/x86/events/core.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c -index add8fc04baf0..d28a05ebac16 100644 ---- a/arch/x86/events/core.c -+++ b/arch/x86/events/core.c -@@ -1454,6 +1454,7 @@ static void x86_pmu_enable(struct pmu *pmu) - hwc->state |= PERF_HES_ARCH; - - x86_pmu_stop(event, PERF_EF_UPDATE); -+ cpuc->events[hwc->idx] = NULL; - } - - /* -@@ -1475,6 +1476,7 @@ static void x86_pmu_enable(struct pmu *pmu) - * if cpuc->enabled = 0, then no wrmsr as - * per x86_pmu_enable_event() - */ -+ cpuc->events[hwc->idx] = event; - x86_pmu_start(event, PERF_EF_RELOAD); - } - cpuc->n_added = 0; -@@ -1640,7 +1642,6 @@ static void x86_pmu_start(struct perf_event *event, int flags) - - event->hw.state = 0; - -- cpuc->events[idx] = event; - __set_bit(idx, cpuc->active_mask); - static_call(x86_pmu_enable)(event); - perf_event_update_userpage(event); -@@ -1719,7 +1720,6 @@ void x86_pmu_stop(struct perf_event *event, int flags) - if (test_bit(hwc->idx, cpuc->active_mask)) { - static_call(x86_pmu_disable)(event); - __clear_bit(hwc->idx, cpuc->active_mask); -- cpuc->events[hwc->idx] = NULL; - WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); - hwc->state |= PERF_HES_STOPPED; - } -@@ -1757,6 +1757,7 @@ static void x86_pmu_del(struct perf_event *event, int flags) - * Not a TXN, therefore cleanup properly. - */ - x86_pmu_stop(event, PERF_EF_UPDATE); -+ cpuc->events[event->hw.idx] = NULL; - - for (i = 0; i < cpuc->n_events; i++) { - if (event == cpuc->event_list[i]) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68378.patch b/SPECS/kernel/CVE-2025-68378.patch deleted file mode 100644 index e27b975e0..000000000 --- a/SPECS/kernel/CVE-2025-68378.patch +++ /dev/null @@ -1,107 +0,0 @@ -From eb89fe28dc12b280026c630e5085076fcac932ea Mon Sep 17 00:00:00 2001 -From: Arnaud Lecomte -Date: Sat, 25 Oct 2025 19:29:41 +0000 -Subject: [PATCH 49/51] bpf: Fix stackmap overflow check in __bpf_get_stackid() - -Syzkaller reported a KASAN slab-out-of-bounds write in __bpf_get_stackid() -when copying stack trace data. The issue occurs when the perf trace - contains more stack entries than the stack map bucket can hold, - leading to an out-of-bounds write in the bucket's data array. - -Fixes: ee2a098851bf ("bpf: Adjust BPF stack helper functions to accommodate skip > 0") -Reported-by: syzbot+c9b724fbb41cf2538b7b@syzkaller.appspotmail.com -Signed-off-by: Arnaud Lecomte -Signed-off-by: Andrii Nakryiko -Acked-by: Yonghong Song -Acked-by: Song Liu -Link: https://lore.kernel.org/bpf/20251025192941.1500-1-contact@arnaud-lcm.com - -Closes: https://syzkaller.appspot.com/bug?extid=c9b724fbb41cf2538b7b ---- - kernel/bpf/stackmap.c | 37 ++++++++++++++++++++++++++++++------- - 1 file changed, 30 insertions(+), 7 deletions(-) - -diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c -index 3615c06b7dfa..87a3e09c072a 100644 ---- a/kernel/bpf/stackmap.c -+++ b/kernel/bpf/stackmap.c -@@ -42,6 +42,28 @@ static inline int stack_map_data_size(struct bpf_map *map) - sizeof(struct bpf_stack_build_id) : sizeof(u64); - } - -+/** -+ * stack_map_calculate_max_depth - Calculate maximum allowed stack trace depth -+ * @size: Size of the buffer/map value in bytes -+ * @elem_size: Size of each stack trace element -+ * @flags: BPF stack trace flags (BPF_F_USER_STACK, BPF_F_USER_BUILD_ID, ...) -+ * -+ * Return: Maximum number of stack trace entries that can be safely stored -+ */ -+static u32 stack_map_calculate_max_depth(u32 size, u32 elem_size, u64 flags) -+{ -+ u32 skip = flags & BPF_F_SKIP_FIELD_MASK; -+ u32 max_depth; -+ u32 curr_sysctl_max_stack = READ_ONCE(sysctl_perf_event_max_stack); -+ -+ max_depth = size / elem_size; -+ max_depth += skip; -+ if (max_depth > curr_sysctl_max_stack) -+ return curr_sysctl_max_stack; -+ -+ return max_depth; -+} -+ - static int prealloc_elems_and_freelist(struct bpf_stack_map *smap) - { - u64 elem_size = sizeof(struct stack_map_bucket) + -@@ -229,8 +251,8 @@ static long __bpf_get_stackid(struct bpf_map *map, - { - struct bpf_stack_map *smap = container_of(map, struct bpf_stack_map, map); - struct stack_map_bucket *bucket, *new_bucket, *old_bucket; -+ u32 hash, id, trace_nr, trace_len, i, max_depth; - u32 skip = flags & BPF_F_SKIP_FIELD_MASK; -- u32 hash, id, trace_nr, trace_len, i; - bool user = flags & BPF_F_USER_STACK; - u64 *ips; - bool hash_matches; -@@ -239,7 +261,8 @@ static long __bpf_get_stackid(struct bpf_map *map, - /* skipping more than usable stack trace */ - return -EFAULT; - -- trace_nr = trace->nr - skip; -+ max_depth = stack_map_calculate_max_depth(map->value_size, stack_map_data_size(map), flags); -+ trace_nr = min_t(u32, trace->nr - skip, max_depth - skip); - trace_len = trace_nr * sizeof(u64); - ips = trace->ip + skip; - hash = jhash2((u32 *)ips, trace_len / sizeof(u32), 0); -@@ -371,15 +394,11 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, - return -EFAULT; - - nr_kernel = count_kernel_ip(trace); -+ __u64 nr = trace->nr; /* save original */ - - if (kernel) { -- __u64 nr = trace->nr; -- - trace->nr = nr_kernel; - ret = __bpf_get_stackid(map, trace, flags); -- -- /* restore nr */ -- trace->nr = nr; - } else { /* user */ - u64 skip = flags & BPF_F_SKIP_FIELD_MASK; - -@@ -390,6 +409,10 @@ BPF_CALL_3(bpf_get_stackid_pe, struct bpf_perf_event_data_kern *, ctx, - flags = (flags & ~BPF_F_SKIP_FIELD_MASK) | skip; - ret = __bpf_get_stackid(map, trace, flags); - } -+ -+ /* restore nr */ -+ trace->nr = nr; -+ - return ret; - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68379.patch b/SPECS/kernel/CVE-2025-68379.patch deleted file mode 100644 index c519d206d..000000000 --- a/SPECS/kernel/CVE-2025-68379.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 1f4b41aa489a5328e4d7c8ebc49ca038d828f294 Mon Sep 17 00:00:00 2001 -From: Zhu Yanjun -Date: Mon, 27 Oct 2025 14:52:03 -0700 -Subject: [PATCH 10/51] RDMA/rxe: Fix null deref on srq->rq.queue after resize - failure - -A NULL pointer dereference can occur in rxe_srq_chk_attr() when -ibv_modify_srq() is invoked twice in succession under certain error -conditions. The first call may fail in rxe_queue_resize(), which leads -rxe_srq_from_attr() to set srq->rq.queue = NULL. The second call then -triggers a crash (null deref) when accessing -srq->rq.queue->buf->index_mask. - -Call Trace: - -rxe_modify_srq+0x170/0x480 [rdma_rxe] -? __pfx_rxe_modify_srq+0x10/0x10 [rdma_rxe] -? uverbs_try_lock_object+0x4f/0xa0 [ib_uverbs] -? rdma_lookup_get_uobject+0x1f0/0x380 [ib_uverbs] -ib_uverbs_modify_srq+0x204/0x290 [ib_uverbs] -? __pfx_ib_uverbs_modify_srq+0x10/0x10 [ib_uverbs] -? tryinc_node_nr_active+0xe6/0x150 -? uverbs_fill_udata+0xed/0x4f0 [ib_uverbs] -ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0x2c0/0x470 [ib_uverbs] -? __pfx_ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0x10/0x10 [ib_uverbs] -? uverbs_fill_udata+0xed/0x4f0 [ib_uverbs] -ib_uverbs_run_method+0x55a/0x6e0 [ib_uverbs] -? __pfx_ib_uverbs_handler_UVERBS_METHOD_INVOKE_WRITE+0x10/0x10 [ib_uverbs] -ib_uverbs_cmd_verbs+0x54d/0x800 [ib_uverbs] -? __pfx_ib_uverbs_cmd_verbs+0x10/0x10 [ib_uverbs] -? __pfx___raw_spin_lock_irqsave+0x10/0x10 -? __pfx_do_vfs_ioctl+0x10/0x10 -? ioctl_has_perm.constprop.0.isra.0+0x2c7/0x4c0 -? __pfx_ioctl_has_perm.constprop.0.isra.0+0x10/0x10 -ib_uverbs_ioctl+0x13e/0x220 [ib_uverbs] -? __pfx_ib_uverbs_ioctl+0x10/0x10 [ib_uverbs] -__x64_sys_ioctl+0x138/0x1c0 -do_syscall_64+0x82/0x250 -? fdget_pos+0x58/0x4c0 -? ksys_write+0xf3/0x1c0 -? __pfx_ksys_write+0x10/0x10 -? do_syscall_64+0xc8/0x250 -? __pfx_vm_mmap_pgoff+0x10/0x10 -? fget+0x173/0x230 -? fput+0x2a/0x80 -? ksys_mmap_pgoff+0x224/0x4c0 -? do_syscall_64+0xc8/0x250 -? do_user_addr_fault+0x37b/0xfe0 -? clear_bhb_loop+0x50/0xa0 -? clear_bhb_loop+0x50/0xa0 -? clear_bhb_loop+0x50/0xa0 -entry_SYSCALL_64_after_hwframe+0x76/0x7e - -Fixes: 8700e3e7c485 ("Soft RoCE driver") -Tested-by: Liu Yi -Signed-off-by: Zhu Yanjun -Link: https://patch.msgid.link/20251027215203.1321-1-yanjun.zhu@linux.dev -Signed-off-by: Leon Romanovsky ---- - drivers/infiniband/sw/rxe/rxe_srq.c | 7 +------ - 1 file changed, 1 insertion(+), 6 deletions(-) - -diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c -index 3661cb627d28..2a234f26ac10 100644 ---- a/drivers/infiniband/sw/rxe/rxe_srq.c -+++ b/drivers/infiniband/sw/rxe/rxe_srq.c -@@ -171,7 +171,7 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, - udata, mi, &srq->rq.producer_lock, - &srq->rq.consumer_lock); - if (err) -- goto err_free; -+ return err; - - srq->rq.max_wr = attr->max_wr; - } -@@ -180,11 +180,6 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq, - srq->limit = attr->srq_limit; - - return 0; -- --err_free: -- rxe_queue_cleanup(q); -- srq->rq.queue = NULL; -- return err; - } - - void rxe_srq_cleanup(struct rxe_pool_elem *elem) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68724.patch b/SPECS/kernel/CVE-2025-68724.patch deleted file mode 100644 index 3c35eee5a..000000000 --- a/SPECS/kernel/CVE-2025-68724.patch +++ /dev/null @@ -1,58 +0,0 @@ -From d6a0faaefb15d8f072edb31a1c76441dc49b45f0 Mon Sep 17 00:00:00 2001 -From: Thorsten Blum -Date: Mon, 13 Oct 2025 13:40:10 +0200 -Subject: [PATCH 46/51] crypto: asymmetric_keys - prevent overflow in - asymmetric_key_generate_id - -Use check_add_overflow() to guard against potential integer overflows -when adding the binary blob lengths and the size of an asymmetric_key_id -structure and return ERR_PTR(-EOVERFLOW) accordingly. This prevents a -possible buffer overflow when copying data from potentially malicious -X.509 certificate fields that can be arbitrarily large, such as ASN.1 -INTEGER serial numbers, issuer names, etc. - -Fixes: 7901c1a8effb ("KEYS: Implement binary asymmetric key ID handling") -Signed-off-by: Thorsten Blum -Reviewed-by: Lukas Wunner -Signed-off-by: Herbert Xu ---- - crypto/asymmetric_keys/asymmetric_type.c | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c -index ba2d9d1ea235..348966ea2175 100644 ---- a/crypto/asymmetric_keys/asymmetric_type.c -+++ b/crypto/asymmetric_keys/asymmetric_type.c -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -141,12 +142,17 @@ struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, - size_t len_2) - { - struct asymmetric_key_id *kid; -- -- kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2, -- GFP_KERNEL); -+ size_t kid_sz; -+ size_t len; -+ -+ if (check_add_overflow(len_1, len_2, &len)) -+ return ERR_PTR(-EOVERFLOW); -+ if (check_add_overflow(sizeof(struct asymmetric_key_id), len, &kid_sz)) -+ return ERR_PTR(-EOVERFLOW); -+ kid = kmalloc(kid_sz, GFP_KERNEL); - if (!kid) - return ERR_PTR(-ENOMEM); -- kid->len = len_1 + len_2; -+ kid->len = len; - memcpy(kid->data, val_1, len_1); - memcpy(kid->data + len_1, val_2, len_2); - return kid; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68725.patch b/SPECS/kernel/CVE-2025-68725.patch deleted file mode 100644 index dba15828b..000000000 --- a/SPECS/kernel/CVE-2025-68725.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 7b4c169fc288f3e11fabc60de37408991505f87e Mon Sep 17 00:00:00 2001 -From: Daniel Borkmann -Date: Mon, 20 Oct 2025 09:54:41 +0200 -Subject: [PATCH 50/51] bpf: Do not let BPF test infra emit invalid GSO types - to stack - -Yinhao et al. reported that their fuzzer tool was able to trigger a -skb_warn_bad_offload() from netif_skb_features() -> gso_features_check(). -When a BPF program - triggered via BPF test infra - pushes the packet -to the loopback device via bpf_clone_redirect() then mentioned offload -warning can be seen. GSO-related features are then rightfully disabled. - -We get into this situation due to convert___skb_to_skb() setting -gso_segs and gso_size but not gso_type. Technically, it makes sense -that this warning triggers since the GSO properties are malformed due -to the gso_type. Potentially, the gso_type could be marked non-trustworthy -through setting it at least to SKB_GSO_DODGY without any other specific -assumptions, but that also feels wrong given we should not go further -into the GSO engine in the first place. - -The checks were added in 121d57af308d ("gso: validate gso_type in GSO -handlers") because there were malicious (syzbot) senders that combine -a protocol with a non-matching gso_type. If we would want to drop such -packets, gso_features_check() currently only returns feature flags via -netif_skb_features(), so one location for potentially dropping such skbs -could be validate_xmit_unreadable_skb(), but then otoh it would be -an additional check in the fast-path for a very corner case. Given -bpf_clone_redirect() is the only place where BPF test infra could emit -such packets, lets reject them right there. - -Fixes: 850a88cc4096 ("bpf: Expose __sk_buff wire_len/gso_segs to BPF_PROG_TEST_RUN") -Fixes: cf62089b0edd ("bpf: Add gso_size to __sk_buff") -Reported-by: Yinhao Hu -Reported-by: Kaiyan Mei -Reported-by: Dongliang Mu -Signed-off-by: Daniel Borkmann -Signed-off-by: Martin KaFai Lau -Link: https://patch.msgid.link/20251020075441.127980-1-daniel@iogearbox.net ---- - net/bpf/test_run.c | 5 +++++ - net/core/filter.c | 7 +++++++ - 2 files changed, 12 insertions(+) - -diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c -index 9728dbd4c66c..848be4ce7226 100644 ---- a/net/bpf/test_run.c -+++ b/net/bpf/test_run.c -@@ -950,6 +950,11 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) - - if (__skb->gso_segs > GSO_MAX_SEGS) - return -EINVAL; -+ -+ /* Currently GSO type is zero/unset. If this gets extended with -+ * a small list of accepted GSO types in future, the filter for -+ * an unset GSO type in bpf_clone_redirect() can be lifted. -+ */ - skb_shinfo(skb)->gso_segs = __skb->gso_segs; - skb_shinfo(skb)->gso_size = __skb->gso_size; - skb_shinfo(skb)->hwtstamps.hwtstamp = __skb->hwtstamp; -diff --git a/net/core/filter.c b/net/core/filter.c -index 47366ec94e58..5185e1245c5d 100644 ---- a/net/core/filter.c -+++ b/net/core/filter.c -@@ -2458,6 +2458,13 @@ BPF_CALL_3(bpf_clone_redirect, struct sk_buff *, skb, u32, ifindex, u64, flags) - if (unlikely(flags & (~(BPF_F_INGRESS) | BPF_F_REDIRECT_INTERNAL))) - return -EINVAL; - -+ /* BPF test infra's convert___skb_to_skb() can create type-less -+ * GSO packets. gso_features_check() will detect this as a bad -+ * offload. However, lets not leak them out in the first place. -+ */ -+ if (unlikely(skb_is_gso(skb) && !skb_shinfo(skb)->gso_type)) -+ return -EBADMSG; -+ - dev = dev_get_by_index_rcu(dev_net(skb->dev), ifindex); - if (unlikely(!dev)) - return -EINVAL; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68730.patch b/SPECS/kernel/CVE-2025-68730.patch deleted file mode 100644 index d1f7c20d8..000000000 --- a/SPECS/kernel/CVE-2025-68730.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 58a027fb9bf73f41dccbae40ea2827d5adaf8b49 Mon Sep 17 00:00:00 2001 -From: Jacek Lawrynowicz -Date: Thu, 25 Sep 2025 16:51:14 +0200 -Subject: [PATCH 14/51] accel/ivpu: Fix page fault in - ivpu_bo_unbind_all_bos_from_context() - -Don't add BO to the vdev->bo_list in ivpu_gem_create_object(). -When failure happens inside drm_gem_shmem_create(), the BO is not -fully created and ivpu_gem_bo_free() callback will not be called -causing a deleted BO to be left on the list. - -Fixes: 8d88e4cdce4f ("accel/ivpu: Use GEM shmem helper for all buffers") -Signed-off-by: Jacek Lawrynowicz -Signed-off-by: Maciej Falkowski -Reviewed-by: Karol Wachowski -Signed-off-by: Karol Wachowski -Link: https://lore.kernel.org/r/20250925145114.1446283-1-maciej.falkowski@linux.intel.com ---- - drivers/accel/ivpu/ivpu_gem.c | 12 +++++++++++- - 1 file changed, 11 insertions(+), 1 deletion(-) - -diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c -index 4c0898102697..7b997ae83a77 100644 ---- a/drivers/accel/ivpu/ivpu_gem.c -+++ b/drivers/accel/ivpu/ivpu_gem.c -@@ -182,10 +182,12 @@ struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t siz - struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, - struct dma_buf *dma_buf) - { -+ struct ivpu_device *vdev = to_ivpu_device(dev); - struct device *attach_dev = dev->dev; - struct dma_buf_attachment *attach; - struct sg_table *sgt; - struct drm_gem_object *obj; -+ struct ivpu_bo *bo; - int ret; - - attach = dma_buf_attach(dma_buf, attach_dev); -@@ -209,6 +211,14 @@ struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, - obj->import_attach = attach; - obj->resv = dma_buf->resv; - -+ bo = to_ivpu_bo(obj); -+ -+ mutex_lock(&vdev->bo_list_lock); -+ list_add_tail(&bo->bo_list_node, &vdev->bo_list); -+ mutex_unlock(&vdev->bo_list_lock); -+ -+ ivpu_dbg(vdev, BO, "import: bo %8p size %9zu\n", bo, ivpu_bo_size(bo)); -+ - return obj; - - fail_unmap: -@@ -246,7 +256,7 @@ static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 fla - list_add_tail(&bo->bo_list_node, &vdev->bo_list); - mutex_unlock(&vdev->bo_list_lock); - -- ivpu_dbg_bo(vdev, bo, "alloc"); -+ ivpu_dbg(vdev, BO, " alloc: bo %8p size %9llu\n", bo, size); - - return bo; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68732.patch b/SPECS/kernel/CVE-2025-68732.patch deleted file mode 100644 index b85b3eeb9..000000000 --- a/SPECS/kernel/CVE-2025-68732.patch +++ /dev/null @@ -1,53 +0,0 @@ -From f090523b6281ef5ccedf4198a1bb83dbe8ae5930 Mon Sep 17 00:00:00 2001 -From: Mainak Sen -Date: Mon, 7 Jul 2025 18:17:39 +0900 -Subject: [PATCH 13/51] gpu: host1x: Fix race in syncpt alloc/free - -Fix race condition between host1x_syncpt_alloc() -and host1x_syncpt_put() by using kref_put_mutex() -instead of kref_put() + manual mutex locking. - -This ensures no thread can acquire the -syncpt_mutex after the refcount drops to zero -but before syncpt_release acquires it. -This prevents races where syncpoints could -be allocated while still being cleaned up -from a previous release. - -Remove explicit mutex locking in syncpt_release -as kref_put_mutex() handles this atomically. - -Signed-off-by: Mainak Sen -Fixes: f5ba33fb9690 ("gpu: host1x: Reserve VBLANK syncpoints at initialization") -Signed-off-by: Mikko Perttunen -Signed-off-by: Thierry Reding -Link: https://lore.kernel.org/r/20250707-host1x-syncpt-race-fix-v1-1-28b0776e70bc@nvidia.com ---- - drivers/gpu/host1x/syncpt.c | 4 +--- - 1 file changed, 1 insertion(+), 3 deletions(-) - -diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c -index f63d14a57a1d..acc7d82e0585 100644 ---- a/drivers/gpu/host1x/syncpt.c -+++ b/drivers/gpu/host1x/syncpt.c -@@ -345,8 +345,6 @@ static void syncpt_release(struct kref *ref) - - sp->locked = false; - -- mutex_lock(&sp->host->syncpt_mutex); -- - host1x_syncpt_base_free(sp->base); - kfree(sp->name); - sp->base = NULL; -@@ -369,7 +367,7 @@ void host1x_syncpt_put(struct host1x_syncpt *sp) - if (!sp) - return; - -- kref_put(&sp->ref, syncpt_release); -+ kref_put_mutex(&sp->ref, syncpt_release, &sp->host->syncpt_mutex); - } - EXPORT_SYMBOL(host1x_syncpt_put); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68733.patch b/SPECS/kernel/CVE-2025-68733.patch deleted file mode 100644 index d46b24edf..000000000 --- a/SPECS/kernel/CVE-2025-68733.patch +++ /dev/null @@ -1,96 +0,0 @@ -From ad55184080e5f96a9a37d6c82c2a36a8b228005b Mon Sep 17 00:00:00 2001 -From: Konstantin Andreev -Date: Tue, 17 Jun 2025 00:32:16 +0300 -Subject: [PATCH 16/51] smack: fix bug: unprivileged task can create labels - -If an unprivileged task is allowed to relabel itself -(/smack/relabel-self is not empty), -it can freely create new labels by writing their -names into own /proc/PID/attr/smack/current - -This occurs because do_setattr() imports -the provided label in advance, -before checking "relabel-self" list. - -This change ensures that the "relabel-self" list -is checked before importing the label. - -Fixes: 38416e53936e ("Smack: limited capability for changing process label") -Signed-off-by: Konstantin Andreev -Signed-off-by: Casey Schaufler ---- - security/smack/smack_lsm.c | 41 +++++++++++++++++++++++++------------- - 1 file changed, 27 insertions(+), 14 deletions(-) - -diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c -index fc340a6f0dde..834458b40895 100644 ---- a/security/smack/smack_lsm.c -+++ b/security/smack/smack_lsm.c -@@ -3731,8 +3731,8 @@ static int do_setattr(u64 attr, void *value, size_t size) - struct task_smack *tsp = smack_cred(current_cred()); - struct cred *new; - struct smack_known *skp; -- struct smack_known_list_elem *sklep; -- int rc; -+ char *labelstr; -+ int rc = 0; - - if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) - return -EPERM; -@@ -3743,28 +3743,41 @@ static int do_setattr(u64 attr, void *value, size_t size) - if (attr != LSM_ATTR_CURRENT) - return -EOPNOTSUPP; - -- skp = smk_import_entry(value, size); -- if (IS_ERR(skp)) -- return PTR_ERR(skp); -+ labelstr = smk_parse_smack(value, size); -+ if (IS_ERR(labelstr)) -+ return PTR_ERR(labelstr); - - /* - * No process is ever allowed the web ("@") label - * and the star ("*") label. - */ -- if (skp == &smack_known_web || skp == &smack_known_star) -- return -EINVAL; -+ if (labelstr[1] == '\0' /* '@', '*' */) { -+ const char c = labelstr[0]; -+ -+ if (c == *smack_known_web.smk_known || -+ c == *smack_known_star.smk_known) { -+ rc = -EPERM; -+ goto free_labelstr; -+ } -+ } - - if (!smack_privileged(CAP_MAC_ADMIN)) { -- rc = -EPERM; -+ const struct smack_known_list_elem *sklep; - list_for_each_entry(sklep, &tsp->smk_relabel, list) -- if (sklep->smk_label == skp) { -- rc = 0; -- break; -- } -- if (rc) -- return rc; -+ if (strcmp(sklep->smk_label->smk_known, labelstr) == 0) -+ goto free_labelstr; -+ rc = -EPERM; - } - -+free_labelstr: -+ kfree(labelstr); -+ if (rc) -+ return -EPERM; -+ -+ skp = smk_import_entry(value, size); -+ if (IS_ERR(skp)) -+ return PTR_ERR(skp); -+ - new = prepare_creds(); - if (new == NULL) - return -ENOMEM; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68736.patch b/SPECS/kernel/CVE-2025-68736.patch deleted file mode 100644 index ffe051d07..000000000 --- a/SPECS/kernel/CVE-2025-68736.patch +++ /dev/null @@ -1,166 +0,0 @@ -From bc3496008e6039fed140466b7c6f7d86920b4437 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= -Date: Wed, 7 Jan 2026 19:28:19 -0800 -Subject: [PATCH 12/51] landlock: Fix handling of disconnected directories -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Disconnected files or directories can appear when they are visible and -opened from a bind mount, but have been renamed or moved from the source -of the bind mount in a way that makes them inaccessible from the mount -point (i.e. out of scope). - -Previously, access rights tied to files or directories opened through a -disconnected directory were collected by walking the related hierarchy -down to the root of the filesystem, without taking into account the -mount point because it couldn't be found. This could lead to -inconsistent access results, potential access right widening, and -hard-to-debug renames, especially since such paths cannot be printed. - -For a sandboxed task to create a disconnected directory, it needs to -have write access (i.e. FS_MAKE_REG, FS_REMOVE_FILE, and FS_REFER) to -the underlying source of the bind mount, and read access to the related -mount point. Because a sandboxed task cannot acquire more access -rights than those defined by its Landlock domain, this could lead to -inconsistent access rights due to missing permissions that should be -inherited from the mount point hierarchy, while inheriting permissions -from the filesystem hierarchy hidden by this mount point instead. - -Landlock now handles files and directories opened from disconnected -directories by taking into account the filesystem hierarchy when the -mount point is not found in the hierarchy walk, and also always taking -into account the mount point from which these disconnected directories -were opened. This ensures that a rename is not allowed if it would -widen access rights [1]. - -The rationale is that, even if disconnected hierarchies might not be -visible or accessible to a sandboxed task, relying on the collected -access rights from them improves the guarantee that access rights will -not be widened during a rename because of the access right comparison -between the source and the destination (see LANDLOCK_ACCESS_FS_REFER). -It may look like this would grant more access on disconnected files and -directories, but the security policies are always enforced for all the -evaluated hierarchies. This new behavior should be less surprising to -users and safer from an access control perspective. - -Remove a wrong WARN_ON_ONCE() canary in collect_domain_accesses() and -fix the related comment. - -Because opened files have their access rights stored in the related file -security properties, there is no impact for disconnected or unlinked -files. - -Cc: Christian Brauner -Cc: Günther Noack -Cc: Song Liu -Reported-by: Tingmao Wang -Closes: https://lore.kernel.org/r/027d5190-b37a-40a8-84e9-4ccbc352bcdf@maowtm.org -Closes: https://lore.kernel.org/r/09b24128f86973a6022e6aa8338945fcfb9a33e4.1749925391.git.m@maowtm.org -Fixes: b91c3e4ea756 ("landlock: Add support for file reparenting with LANDLOCK_ACCESS_FS_REFER") -Fixes: cb2c7d1a1776 ("landlock: Support filesystem access-control") -Link: https://lore.kernel.org/r/b0f46246-f2c5-42ca-93ce-0d629702a987@maowtm.org [1] -Reviewed-by: Tingmao Wang -Link: https://lore.kernel.org/r/20251128172200.760753-2-mic@digikod.net -Signed-off-by: Mickaël Salaün ---- - security/landlock/errata/abi-1.h | 16 +++++++++++++ - security/landlock/fs.c | 40 ++++++++++++++++++++++---------- - 2 files changed, 44 insertions(+), 12 deletions(-) - create mode 100644 security/landlock/errata/abi-1.h - -diff --git a/security/landlock/errata/abi-1.h b/security/landlock/errata/abi-1.h -new file mode 100644 -index 000000000000..e8a2bff2e5b6 ---- /dev/null -+++ b/security/landlock/errata/abi-1.h -@@ -0,0 +1,16 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+ -+/** -+ * DOC: erratum_3 -+ * -+ * Erratum 3: Disconnected directory handling -+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -+ * -+ * This fix addresses an issue with disconnected directories that occur when a -+ * directory is moved outside the scope of a bind mount. The change ensures -+ * that evaluated access rights include both those from the disconnected file -+ * hierarchy down to its filesystem root and those from the related mount point -+ * hierarchy. This prevents access right widening through rename or link -+ * actions. -+ */ -+LANDLOCK_ERRATUM(3) -diff --git a/security/landlock/fs.c b/security/landlock/fs.c -index c04f8879ad03..725440836771 100644 ---- a/security/landlock/fs.c -+++ b/security/landlock/fs.c -@@ -909,21 +909,31 @@ static bool is_access_to_paths_allowed( - break; - } - } -+ - if (unlikely(IS_ROOT(walker_path.dentry))) { -- /* -- * Stops at disconnected root directories. Only allows -- * access to internal filesystems (e.g. nsfs, which is -- * reachable through /proc//ns/). -- */ -- if (walker_path.mnt->mnt_flags & MNT_INTERNAL) { -+ if (likely(walker_path.mnt->mnt_flags & MNT_INTERNAL)) { -+ /* -+ * Stops and allows access when reaching disconnected root -+ * directories that are part of internal filesystems (e.g. nsfs, -+ * which is reachable through /proc//ns/). -+ */ - allowed_parent1 = true; - allowed_parent2 = true; -+ break; - } -- break; -+ -+ /* -+ * We reached a disconnected root directory from a bind mount. -+ * Let's continue the walk with the mount point we missed. -+ */ -+ dput(walker_path.dentry); -+ walker_path.dentry = walker_path.mnt->mnt_root; -+ dget(walker_path.dentry); -+ } else { -+ parent_dentry = dget_parent(walker_path.dentry); -+ dput(walker_path.dentry); -+ walker_path.dentry = parent_dentry; - } -- parent_dentry = dget_parent(walker_path.dentry); -- dput(walker_path.dentry); -- walker_path.dentry = parent_dentry; - } - path_put(&walker_path); - -@@ -1021,6 +1031,9 @@ static access_mask_t maybe_remove(const struct dentry *const dentry) - * file. While walking from @dir to @mnt_root, we record all the domain's - * allowed accesses in @layer_masks_dom. - * -+ * Because of disconnected directories, this walk may not reach @mnt_dir. In -+ * this case, the walk will continue to @mnt_dir after this call. -+ * - * This is similar to is_access_to_paths_allowed() but much simpler because it - * only handles walking on the same mount point and only checks one set of - * accesses. -@@ -1062,8 +1075,11 @@ static bool collect_domain_accesses( - break; - } - -- /* We should not reach a root other than @mnt_root. */ -- if (dir == mnt_root || WARN_ON_ONCE(IS_ROOT(dir))) -+ /* -+ * Stops at the mount point or the filesystem root for a disconnected -+ * directory. -+ */ -+ if (dir == mnt_root || unlikely(IS_ROOT(dir))) - break; - - parent_dentry = dget_parent(dir); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68740.patch b/SPECS/kernel/CVE-2025-68740.patch deleted file mode 100644 index 6187120bf..000000000 --- a/SPECS/kernel/CVE-2025-68740.patch +++ /dev/null @@ -1,70 +0,0 @@ -From 0429f67c4e1b72e706819940e4d51d9edbbaeeb5 Mon Sep 17 00:00:00 2001 -From: Zhao Yipeng -Date: Thu, 20 Nov 2025 15:18:05 +0800 -Subject: [PATCH 38/51] ima: Handle error code returned by - ima_filter_rule_match() - -In ima_match_rules(), if ima_filter_rule_match() returns -ENOENT due to -the rule being NULL, the function incorrectly skips the 'if (!rc)' check -and sets 'result = true'. The LSM rule is considered a match, causing -extra files to be measured by IMA. - -This issue can be reproduced in the following scenario: -After unloading the SELinux policy module via 'semodule -d', if an IMA -measurement is triggered before ima_lsm_rules is updated, -in ima_match_rules(), the first call to ima_filter_rule_match() returns --ESTALE. This causes the code to enter the 'if (rc == -ESTALE && -!rule_reinitialized)' block, perform ima_lsm_copy_rule() and retry. In -ima_lsm_copy_rule(), since the SELinux module has been removed, the rule -becomes NULL, and the second call to ima_filter_rule_match() returns --ENOENT. This bypasses the 'if (!rc)' check and results in a false match. - -Call trace: - selinux_audit_rule_match+0x310/0x3b8 - security_audit_rule_match+0x60/0xa0 - ima_match_rules+0x2e4/0x4a0 - ima_match_policy+0x9c/0x1e8 - ima_get_action+0x48/0x60 - process_measurement+0xf8/0xa98 - ima_bprm_check+0x98/0xd8 - security_bprm_check+0x5c/0x78 - search_binary_handler+0x6c/0x318 - exec_binprm+0x58/0x1b8 - bprm_execve+0xb8/0x130 - do_execveat_common.isra.0+0x1a8/0x258 - __arm64_sys_execve+0x48/0x68 - invoke_syscall+0x50/0x128 - el0_svc_common.constprop.0+0xc8/0xf0 - do_el0_svc+0x24/0x38 - el0_svc+0x44/0x200 - el0t_64_sync_handler+0x100/0x130 - el0t_64_sync+0x3c8/0x3d0 - -Fix this by changing 'if (!rc)' to 'if (rc <= 0)' to ensure that error -codes like -ENOENT do not bypass the check and accidentally result in a -successful match. - -Fixes: 4af4662fa4a9d ("integrity: IMA policy") -Signed-off-by: Zhao Yipeng -Reviewed-by: Roberto Sassu -Signed-off-by: Mimi Zohar ---- - security/integrity/ima/ima_policy.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c -index 128fab897930..db6d55af5a80 100644 ---- a/security/integrity/ima/ima_policy.c -+++ b/security/integrity/ima/ima_policy.c -@@ -674,7 +674,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, - goto retry; - } - } -- if (!rc) { -+ if (rc <= 0) { - result = false; - goto out; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68741.patch b/SPECS/kernel/CVE-2025-68741.patch deleted file mode 100644 index 428374b9c..000000000 --- a/SPECS/kernel/CVE-2025-68741.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 99c7757fe51f5f72bacacd88c7909ed9844f9de6 Mon Sep 17 00:00:00 2001 -From: Zilin Guan -Date: Thu, 13 Nov 2025 15:12:46 +0000 -Subject: [PATCH 31/51] scsi: qla2xxx: Fix improper freeing of purex item - -In qla2xxx_process_purls_iocb(), an item is allocated via -qla27xx_copy_multiple_pkt(), which internally calls -qla24xx_alloc_purex_item(). - -The qla24xx_alloc_purex_item() function may return a pre-allocated item -from a per-adapter pool for small allocations, instead of dynamically -allocating memory with kzalloc(). - -An error handling path in qla2xxx_process_purls_iocb() incorrectly uses -kfree() to release the item. If the item was from the pre-allocated -pool, calling kfree() on it is a bug that can lead to memory corruption. - -Fix this by using the correct deallocation function, -qla24xx_free_purex_item(), which properly handles both dynamically -allocated and pre-allocated items. - -Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe") -Signed-off-by: Zilin Guan -Reviewed-by: Himanshu Madhani -Link: https://patch.msgid.link/20251113151246.762510-1-zilin@seu.edu.cn -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/qla2xxx/qla_nvme.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c -index 316594aa40cc..42eb65a62f1f 100644 ---- a/drivers/scsi/qla2xxx/qla_nvme.c -+++ b/drivers/scsi/qla2xxx/qla_nvme.c -@@ -1292,7 +1292,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp) - a.reason = FCNVME_RJT_RC_LOGIC; - a.explanation = FCNVME_RJT_EXP_NONE; - xmt_reject = true; -- kfree(item); -+ qla24xx_free_purex_item(item); - goto out; - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68742.patch b/SPECS/kernel/CVE-2025-68742.patch deleted file mode 100644 index c28490007..000000000 --- a/SPECS/kernel/CVE-2025-68742.patch +++ /dev/null @@ -1,87 +0,0 @@ -From e1789aa8bb9c5635ce265730122a9e7d7a68243b Mon Sep 17 00:00:00 2001 -From: Pu Lehui -Date: Sat, 15 Nov 2025 10:23:43 +0000 -Subject: [PATCH 40/51] bpf: Fix invalid prog->stats access when - update_effective_progs fails - -Syzkaller triggers an invalid memory access issue following fault -injection in update_effective_progs. The issue can be described as -follows: - -__cgroup_bpf_detach - update_effective_progs - compute_effective_progs - bpf_prog_array_alloc <-- fault inject - purge_effective_progs - /* change to dummy_bpf_prog */ - array->items[index] = &dummy_bpf_prog.prog - ----softirq start--- -__do_softirq - ... - __cgroup_bpf_run_filter_skb - __bpf_prog_run_save_cb - bpf_prog_run - stats = this_cpu_ptr(prog->stats) - /* invalid memory access */ - flags = u64_stats_update_begin_irqsave(&stats->syncp) ----softirq end--- - - static_branch_dec(&cgroup_bpf_enabled_key[atype]) - -The reason is that fault injection caused update_effective_progs to fail -and then changed the original prog into dummy_bpf_prog.prog in -purge_effective_progs. Then a softirq came, and accessing the members of -dummy_bpf_prog.prog in the softirq triggers invalid mem access. - -To fix it, skip updating stats when stats is NULL. - -Fixes: 492ecee892c2 ("bpf: enable program stats") -Signed-off-by: Pu Lehui -Link: https://lore.kernel.org/r/20251115102343.2200727-1-pulehui@huaweicloud.com -Signed-off-by: Alexei Starovoitov ---- - include/linux/filter.h | 12 +++++++----- - kernel/bpf/syscall.c | 3 +++ - 2 files changed, 10 insertions(+), 5 deletions(-) - -diff --git a/include/linux/filter.h b/include/linux/filter.h -index 152f2fc7b65a..70e2f5051676 100644 ---- a/include/linux/filter.h -+++ b/include/linux/filter.h -@@ -709,11 +709,13 @@ static __always_inline u32 __bpf_prog_run(const struct bpf_prog *prog, - ret = dfunc(ctx, prog->insnsi, prog->bpf_func); - - duration = sched_clock() - start; -- stats = this_cpu_ptr(prog->stats); -- flags = u64_stats_update_begin_irqsave(&stats->syncp); -- u64_stats_inc(&stats->cnt); -- u64_stats_add(&stats->nsecs, duration); -- u64_stats_update_end_irqrestore(&stats->syncp, flags); -+ if (likely(prog->stats)) { -+ stats = this_cpu_ptr(prog->stats); -+ flags = u64_stats_update_begin_irqsave(&stats->syncp); -+ u64_stats_inc(&stats->cnt); -+ u64_stats_add(&stats->nsecs, duration); -+ u64_stats_update_end_irqrestore(&stats->syncp, flags); -+ } - } else { - ret = dfunc(ctx, prog->insnsi, prog->bpf_func); - } -diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c -index 0fbfa8532c39..c7b4f597a293 100644 ---- a/kernel/bpf/syscall.c -+++ b/kernel/bpf/syscall.c -@@ -2406,6 +2406,9 @@ void notrace bpf_prog_inc_misses_counter(struct bpf_prog *prog) - struct bpf_prog_stats *stats; - unsigned int flags; - -+ if (unlikely(!prog->stats)) -+ return; -+ - stats = this_cpu_ptr(prog->stats); - flags = u64_stats_update_begin_irqsave(&stats->syncp); - u64_stats_inc(&stats->misses); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68743.patch b/SPECS/kernel/CVE-2025-68743.patch deleted file mode 100644 index 46547393c..000000000 --- a/SPECS/kernel/CVE-2025-68743.patch +++ /dev/null @@ -1,82 +0,0 @@ -From 344804c70dbf3af503917e969c083c088fc9ec5b Mon Sep 17 00:00:00 2001 -From: Nuno Das Neves -Date: Wed, 7 Jan 2026 18:35:30 -0800 -Subject: [PATCH 45/51] mshv: Fix create memory region overlap check - -The current check is incorrect; it only checks if the beginning or end -of a region is within an existing region. This doesn't account for -userspace specifying a region that begins before and ends after an -existing region. - -Change the logic to a range intersection check against gfns and uaddrs -for each region. - -Remove mshv_partition_region_by_uaddr() as it is no longer used. - -Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") -Reported-by: Michael Kelley -Closes: https://lore.kernel.org/linux-hyperv/SN6PR02MB41575BE0406D3AB22E1D7DB5D4C2A@SN6PR02MB4157.namprd02.prod.outlook.com/ -Signed-off-by: Nuno Das Neves -Reviewed-by: Michael Kelley -Signed-off-by: Wei Liu -(cherry picked from commit ba9eb9b86d232854e983203dc2fb1ba18e316681) ---- - drivers/hv/mshv_root_main.c | 31 +++++++++++-------------------- - 1 file changed, 11 insertions(+), 20 deletions(-) - -diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c -index cad09ff5f94d..999c73d19de2 100644 ---- a/drivers/hv/mshv_root_main.c -+++ b/drivers/hv/mshv_root_main.c -@@ -1198,21 +1198,6 @@ mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn) - return NULL; - } - --static struct mshv_mem_region * --mshv_partition_region_by_uaddr(struct mshv_partition *partition, u64 uaddr) --{ -- struct mshv_mem_region *region; -- -- hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) { -- if (uaddr >= region->start_uaddr && -- uaddr < region->start_uaddr + -- (region->nr_pages << HV_HYP_PAGE_SHIFT)) -- return region; -- } -- -- return NULL; --} -- - /* - * NB: caller checks and makes sure mem->size is page aligned - * Returns: 0 with regionpp updated on success, or -errno -@@ -1222,15 +1207,21 @@ static int mshv_partition_create_region(struct mshv_partition *partition, - struct mshv_mem_region **regionpp, - bool is_mmio) - { -- struct mshv_mem_region *region; -+ struct mshv_mem_region *region, *rg; - u64 nr_pages = HVPFN_DOWN(mem->size); - - /* Reject overlapping regions */ -- if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) || -- mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1) || -- mshv_partition_region_by_uaddr(partition, mem->userspace_addr) || -- mshv_partition_region_by_uaddr(partition, mem->userspace_addr + mem->size - 1)) -+ hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) { -+ u64 rg_size = rg->nr_pages << HV_HYP_PAGE_SHIFT; -+ -+ if ((mem->guest_pfn + nr_pages <= rg->start_gfn || -+ rg->start_gfn + rg->nr_pages <= mem->guest_pfn) && -+ (mem->userspace_addr + mem->size <= rg->start_uaddr || -+ rg->start_uaddr + rg_size <= mem->userspace_addr)) -+ continue; -+ - return -EEXIST; -+ } - - region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages); - if (!region) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68744.patch b/SPECS/kernel/CVE-2025-68744.patch deleted file mode 100644 index eb2513fd3..000000000 --- a/SPECS/kernel/CVE-2025-68744.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 324978028345fa41753231006bc6ff774893375f Mon Sep 17 00:00:00 2001 -From: Leon Hwang -Date: Wed, 5 Nov 2025 23:14:06 +0800 -Subject: [PATCH 06/51] bpf: Free special fields when update [lru_,]percpu_hash - maps - -As [lru_,]percpu_hash maps support BPF_KPTR_{REF,PERCPU}, missing -calls to 'bpf_obj_free_fields()' in 'pcpu_copy_value()' could cause the -memory referenced by BPF_KPTR_{REF,PERCPU} fields to be held until the -map gets freed. - -Fix this by calling 'bpf_obj_free_fields()' after -'copy_map_value[,_long]()' in 'pcpu_copy_value()'. - -Fixes: 65334e64a493 ("bpf: Support kptrs in percpu hashmap and percpu LRU hashmap") -Signed-off-by: Leon Hwang -Acked-by: Yonghong Song -Link: https://lore.kernel.org/r/20251105151407.12723-2-leon.hwang@linux.dev -Signed-off-by: Alexei Starovoitov ---- - kernel/bpf/hashtab.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c -index 71f9931ac64c..f3bc22ea503f 100644 ---- a/kernel/bpf/hashtab.c -+++ b/kernel/bpf/hashtab.c -@@ -939,15 +939,21 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) - static void pcpu_copy_value(struct bpf_htab *htab, void __percpu *pptr, - void *value, bool onallcpus) - { -+ void *ptr; -+ - if (!onallcpus) { - /* copy true value_size bytes */ -- copy_map_value(&htab->map, this_cpu_ptr(pptr), value); -+ ptr = this_cpu_ptr(pptr); -+ copy_map_value(&htab->map, ptr, value); -+ bpf_obj_free_fields(htab->map.record, ptr); - } else { - u32 size = round_up(htab->map.value_size, 8); - int off = 0, cpu; - - for_each_possible_cpu(cpu) { -- copy_map_value_long(&htab->map, per_cpu_ptr(pptr, cpu), value + off); -+ ptr = per_cpu_ptr(pptr, cpu); -+ copy_map_value_long(&htab->map, ptr, value + off); -+ bpf_obj_free_fields(htab->map.record, ptr); - off += size; - } - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68745.patch b/SPECS/kernel/CVE-2025-68745.patch deleted file mode 100644 index b880f1423..000000000 --- a/SPECS/kernel/CVE-2025-68745.patch +++ /dev/null @@ -1,106 +0,0 @@ -From df107b79cd30292576fb7a0494bd6c64cde9721b Mon Sep 17 00:00:00 2001 -From: Tony Battersby -Date: Mon, 10 Nov 2025 11:07:37 -0500 -Subject: [PATCH 03/51] scsi: qla2xxx: Clear cmds after chip reset - -Commit aefed3e5548f ("scsi: qla2xxx: target: Fix offline port handling -and host reset handling") caused two problems: - -1. Commands sent to FW, after chip reset got stuck and never freed as FW - is not going to respond to them anymore. - -2. BUG_ON(cmd->sg_mapped) in qlt_free_cmd(). Commit 26f9ce53817a - ("scsi: qla2xxx: Fix missed DMA unmap for aborted commands") - attempted to fix this, but introduced another bug under different - circumstances when two different CPUs were racing to call - qlt_unmap_sg() at the same time: BUG_ON(!valid_dma_direction(dir)) in - dma_unmap_sg_attrs(). - -So revert "scsi: qla2xxx: Fix missed DMA unmap for aborted commands" and -partially revert "scsi: qla2xxx: target: Fix offline port handling and -host reset handling" at __qla2x00_abort_all_cmds. - -Fixes: aefed3e5548f ("scsi: qla2xxx: target: Fix offline port handling and host reset handling") -Fixes: 26f9ce53817a ("scsi: qla2xxx: Fix missed DMA unmap for aborted commands") -Co-developed-by: Dmitry Bogdanov -Signed-off-by: Dmitry Bogdanov -Signed-off-by: Tony Battersby -Link: https://patch.msgid.link/0e7e5d26-e7a0-42d1-8235-40eeb27f3e98@cybernetics.com -Signed-off-by: Martin K. Petersen ---- - drivers/scsi/qla2xxx/qla_os.c | 20 ++++++++++++++++++-- - drivers/scsi/qla2xxx/qla_target.c | 5 +---- - drivers/scsi/qla2xxx/qla_target.h | 1 + - 3 files changed, 20 insertions(+), 6 deletions(-) - -diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c -index 4460421834cb..b515ed301940 100644 ---- a/drivers/scsi/qla2xxx/qla_os.c -+++ b/drivers/scsi/qla2xxx/qla_os.c -@@ -1881,10 +1881,26 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res) - continue; - } - cmd = (struct qla_tgt_cmd *)sp; -- cmd->aborted = 1; -+ -+ if (cmd->sg_mapped) -+ qlt_unmap_sg(vha, cmd); -+ -+ if (cmd->state == QLA_TGT_STATE_NEED_DATA) { -+ cmd->aborted = 1; -+ cmd->write_data_transferred = 0; -+ cmd->state = QLA_TGT_STATE_DATA_IN; -+ ha->tgt.tgt_ops->handle_data(cmd); -+ } else { -+ ha->tgt.tgt_ops->free_cmd(cmd); -+ } - break; - case TYPE_TGT_TMCMD: -- /* Skip task management functions. */ -+ /* -+ * Currently, only ABTS response gets on the -+ * outstanding_cmds[] -+ */ -+ ha->tgt.tgt_ops->free_mcmd( -+ (struct qla_tgt_mgmt_cmd *) sp); - break; - default: - break; -diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c -index 1e81582085e3..4c6aff59fe3f 100644 ---- a/drivers/scsi/qla2xxx/qla_target.c -+++ b/drivers/scsi/qla2xxx/qla_target.c -@@ -2443,7 +2443,7 @@ static int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm) - return -1; - } - --static void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) -+void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd) - { - struct qla_hw_data *ha; - struct qla_qpair *qpair; -@@ -3773,9 +3773,6 @@ int qlt_abort_cmd(struct qla_tgt_cmd *cmd) - - spin_lock_irqsave(&cmd->cmd_lock, flags); - if (cmd->aborted) { -- if (cmd->sg_mapped) -- qlt_unmap_sg(vha, cmd); -- - spin_unlock_irqrestore(&cmd->cmd_lock, flags); - /* - * It's normal to see 2 calls in this path: -diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h -index 15a59c125c53..c483966d0a84 100644 ---- a/drivers/scsi/qla2xxx/qla_target.h -+++ b/drivers/scsi/qla2xxx/qla_target.h -@@ -1058,6 +1058,7 @@ extern int qlt_abort_cmd(struct qla_tgt_cmd *); - extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); - extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); - extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); -+extern void qlt_unmap_sg(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd); - extern void qlt_async_event(uint16_t, struct scsi_qla_host *, uint16_t *); - extern void qlt_enable_vha(struct scsi_qla_host *); - extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68749.patch b/SPECS/kernel/CVE-2025-68749.patch deleted file mode 100644 index 9c6d4431f..000000000 --- a/SPECS/kernel/CVE-2025-68749.patch +++ /dev/null @@ -1,51 +0,0 @@ -From bcc739c9ef5136a1cfbcf34623882561694d47a0 Mon Sep 17 00:00:00 2001 -From: Tomasz Rusinowicz -Date: Wed, 29 Oct 2025 08:14:51 +0100 -Subject: [PATCH 01/51] accel/ivpu: Fix race condition when unbinding BOs - -Fix 'Memory manager not clean during takedown' warning that occurs -when ivpu_gem_bo_free() removes the BO from the BOs list before it -gets unmapped. Then file_priv_unbind() triggers a warning in -drm_mm_takedown() during context teardown. - -Protect the unmapping sequence with bo_list_lock to ensure the BO is -always fully unmapped when removed from the list. This ensures the BO -is either fully unmapped at context teardown time or present on the -list and unmapped by file_priv_unbind(). - -Fixes: 48aea7f2a2ef ("accel/ivpu: Fix locking in ivpu_bo_remove_all_bos_from_context()") -Signed-off-by: Tomasz Rusinowicz -Reviewed-by: Jeff Hugo -Signed-off-by: Karol Wachowski -Link: https://patch.msgid.link/20251029071451.184243-1-karol.wachowski@linux.intel.com ---- - drivers/accel/ivpu/ivpu_gem.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c -index 59cfcf3eaded..4c0898102697 100644 ---- a/drivers/accel/ivpu/ivpu_gem.c -+++ b/drivers/accel/ivpu/ivpu_gem.c -@@ -283,14 +283,18 @@ static void ivpu_gem_bo_free(struct drm_gem_object *obj) - - mutex_lock(&vdev->bo_list_lock); - list_del(&bo->bo_list_node); -- mutex_unlock(&vdev->bo_list_lock); - - drm_WARN_ON(&vdev->drm, !drm_gem_is_imported(&bo->base.base) && - !dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ)); - drm_WARN_ON(&vdev->drm, ivpu_bo_size(bo) == 0); - drm_WARN_ON(&vdev->drm, bo->base.vaddr); - -+ ivpu_bo_lock(bo); - ivpu_bo_unbind_locked(bo); -+ ivpu_bo_unlock(bo); -+ -+ mutex_unlock(&vdev->bo_list_lock); -+ - drm_WARN_ON(&vdev->drm, bo->mmu_mapped); - drm_WARN_ON(&vdev->drm, bo->ctx); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68752.patch b/SPECS/kernel/CVE-2025-68752.patch deleted file mode 100644 index 44494c6ba..000000000 --- a/SPECS/kernel/CVE-2025-68752.patch +++ /dev/null @@ -1,49 +0,0 @@ -From a0dfcb10317fec90540dcf3a1569d585fd595903 Mon Sep 17 00:00:00 2001 -From: Michal Schmidt -Date: Wed, 26 Nov 2025 10:48:49 +0100 -Subject: [PATCH 33/38] iavf: Implement settime64 with -EOPNOTSUPP - -ptp_clock_settime() assumes every ptp_clock has implemented settime64(). -Stub it with -EOPNOTSUPP to prevent a NULL dereference. - -The fix is similar to commit 329d050bbe63 ("gve: Implement settime64 -with -EOPNOTSUPP"). - -Fixes: d734223b2f0d ("iavf: add initial framework for registering PTP clock") -Signed-off-by: Michal Schmidt -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Tim Hostetler -Link: https://patch.msgid.link/20251126094850.2842557-1-mschmidt@redhat.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/ethernet/intel/iavf/iavf_ptp.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/drivers/net/ethernet/intel/iavf/iavf_ptp.c b/drivers/net/ethernet/intel/iavf/iavf_ptp.c -index b4d5eda2e84f..9cbd8c154031 100644 ---- a/drivers/net/ethernet/intel/iavf/iavf_ptp.c -+++ b/drivers/net/ethernet/intel/iavf/iavf_ptp.c -@@ -252,6 +252,12 @@ static int iavf_ptp_gettimex64(struct ptp_clock_info *info, - return iavf_read_phc_indirect(adapter, ts, sts); - } - -+static int iavf_ptp_settime64(struct ptp_clock_info *info, -+ const struct timespec64 *ts) -+{ -+ return -EOPNOTSUPP; -+} -+ - /** - * iavf_ptp_cache_phc_time - Cache PHC time for performing timestamp extension - * @adapter: private adapter structure -@@ -320,6 +326,7 @@ static int iavf_ptp_register_clock(struct iavf_adapter *adapter) - KBUILD_MODNAME, dev_name(dev)); - ptp_info->owner = THIS_MODULE; - ptp_info->gettimex64 = iavf_ptp_gettimex64; -+ ptp_info->settime64 = iavf_ptp_settime64; - ptp_info->do_aux_work = iavf_ptp_do_aux_work; - - clock = ptp_clock_register(ptp_info, dev); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68753.patch b/SPECS/kernel/CVE-2025-68753.patch deleted file mode 100644 index 1412236ab..000000000 --- a/SPECS/kernel/CVE-2025-68753.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 7cd04bc7f7197c72a6dcdf9aa05a281e9cacaf7f Mon Sep 17 00:00:00 2001 -From: Junrui Luo -Date: Tue, 9 Dec 2025 13:16:41 +0800 -Subject: [PATCH 31/38] ALSA: firewire-motu: add bounds check in put_user loop - for DSP events - -In the DSP event handling code, a put_user() loop copies event data. -When the user buffer size is not aligned to 4 bytes, it could overwrite -beyond the buffer boundary. - -Fix by adding a bounds check before put_user(). - -Suggested-by: Takashi Iwai -Fixes: 634ec0b2906e ("ALSA: firewire-motu: notify event for parameter change in register DSP model") -Signed-off-by: Junrui Luo -Link: https://patch.msgid.link/SYBPR01MB788112C72AF8A1C8C448B4B8AFA3A@SYBPR01MB7881.ausprd01.prod.outlook.com -Signed-off-by: Takashi Iwai ---- - sound/firewire/motu/motu-hwdep.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c -index fa2685665db3..c13086fbb041 100644 ---- a/sound/firewire/motu/motu-hwdep.c -+++ b/sound/firewire/motu/motu-hwdep.c -@@ -75,7 +75,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, - while (consumed < count && - snd_motu_register_dsp_message_parser_copy_event(motu, &ev)) { - ptr = (u32 __user *)(buf + consumed); -- if (put_user(ev, ptr)) -+ if (consumed + sizeof(ev) > count || put_user(ev, ptr)) - return -EFAULT; - consumed += sizeof(ev); - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68756.patch b/SPECS/kernel/CVE-2025-68756.patch deleted file mode 100644 index 8bf587a8c..000000000 --- a/SPECS/kernel/CVE-2025-68756.patch +++ /dev/null @@ -1,164 +0,0 @@ -From 1008e1348b94fbead6292fa3074f45b6eaa5c97f Mon Sep 17 00:00:00 2001 -From: Mohamed Khalfella -Date: Fri, 5 Dec 2025 13:17:02 -0800 -Subject: [PATCH 30/38] block: Use RCU in blk_mq_[un]quiesce_tagset() instead - of set->tag_list_lock - -blk_mq_{add,del}_queue_tag_set() functions add and remove queues from -tagset, the functions make sure that tagset and queues are marked as -shared when two or more queues are attached to the same tagset. -Initially a tagset starts as unshared and when the number of added -queues reaches two, blk_mq_add_queue_tag_set() marks it as shared along -with all the queues attached to it. When the number of attached queues -drops to 1 blk_mq_del_queue_tag_set() need to mark both the tagset and -the remaining queues as unshared. - -Both functions need to freeze current queues in tagset before setting on -unsetting BLK_MQ_F_TAG_QUEUE_SHARED flag. While doing so, both functions -hold set->tag_list_lock mutex, which makes sense as we do not want -queues to be added or deleted in the process. This used to work fine -until commit 98d81f0df70c ("nvme: use blk_mq_[un]quiesce_tagset") -made the nvme driver quiesce tagset instead of quiscing individual -queues. blk_mq_quiesce_tagset() does the job and quiesce the queues in -set->tag_list while holding set->tag_list_lock also. - -This results in deadlock between two threads with these stacktraces: - - __schedule+0x47c/0xbb0 - ? timerqueue_add+0x66/0xb0 - schedule+0x1c/0xa0 - schedule_preempt_disabled+0xa/0x10 - __mutex_lock.constprop.0+0x271/0x600 - blk_mq_quiesce_tagset+0x25/0xc0 - nvme_dev_disable+0x9c/0x250 - nvme_timeout+0x1fc/0x520 - blk_mq_handle_expired+0x5c/0x90 - bt_iter+0x7e/0x90 - blk_mq_queue_tag_busy_iter+0x27e/0x550 - ? __blk_mq_complete_request_remote+0x10/0x10 - ? __blk_mq_complete_request_remote+0x10/0x10 - ? __call_rcu_common.constprop.0+0x1c0/0x210 - blk_mq_timeout_work+0x12d/0x170 - process_one_work+0x12e/0x2d0 - worker_thread+0x288/0x3a0 - ? rescuer_thread+0x480/0x480 - kthread+0xb8/0xe0 - ? kthread_park+0x80/0x80 - ret_from_fork+0x2d/0x50 - ? kthread_park+0x80/0x80 - ret_from_fork_asm+0x11/0x20 - - __schedule+0x47c/0xbb0 - ? xas_find+0x161/0x1a0 - schedule+0x1c/0xa0 - blk_mq_freeze_queue_wait+0x3d/0x70 - ? destroy_sched_domains_rcu+0x30/0x30 - blk_mq_update_tag_set_shared+0x44/0x80 - blk_mq_exit_queue+0x141/0x150 - del_gendisk+0x25a/0x2d0 - nvme_ns_remove+0xc9/0x170 - nvme_remove_namespaces+0xc7/0x100 - nvme_remove+0x62/0x150 - pci_device_remove+0x23/0x60 - device_release_driver_internal+0x159/0x200 - unbind_store+0x99/0xa0 - kernfs_fop_write_iter+0x112/0x1e0 - vfs_write+0x2b1/0x3d0 - ksys_write+0x4e/0xb0 - do_syscall_64+0x5b/0x160 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -The top stacktrace is showing nvme_timeout() called to handle nvme -command timeout. timeout handler is trying to disable the controller and -as a first step, it needs to blk_mq_quiesce_tagset() to tell blk-mq not -to call queue callback handlers. The thread is stuck waiting for -set->tag_list_lock as it tries to walk the queues in set->tag_list. - -The lock is held by the second thread in the bottom stack which is -waiting for one of queues to be frozen. The queue usage counter will -drop to zero after nvme_timeout() finishes, and this will not happen -because the thread will wait for this mutex forever. - -Given that [un]quiescing queue is an operation that does not need to -sleep, update blk_mq_[un]quiesce_tagset() to use RCU instead of taking -set->tag_list_lock, update blk_mq_{add,del}_queue_tag_set() to use RCU -safe list operations. Also, delete INIT_LIST_HEAD(&q->tag_set_list) -in blk_mq_del_queue_tag_set() because we can not re-initialize it while -the list is being traversed under RCU. The deleted queue will not be -added/deleted to/from a tagset and it will be freed in blk_free_queue() -after the end of RCU grace period. - -Signed-off-by: Mohamed Khalfella -Fixes: 98d81f0df70c ("nvme: use blk_mq_[un]quiesce_tagset") -Reviewed-by: Ming Lei -Reviewed-by: Bart Van Assche -Signed-off-by: Jens Axboe ---- - block/blk-mq.c | 17 ++++++++--------- - 1 file changed, 8 insertions(+), 9 deletions(-) - -diff --git a/block/blk-mq.c b/block/blk-mq.c -index 19f62b070ca9..63b01c416d42 100644 ---- a/block/blk-mq.c -+++ b/block/blk-mq.c -@@ -335,12 +335,12 @@ void blk_mq_quiesce_tagset(struct blk_mq_tag_set *set) - { - struct request_queue *q; - -- mutex_lock(&set->tag_list_lock); -- list_for_each_entry(q, &set->tag_list, tag_set_list) { -+ rcu_read_lock(); -+ list_for_each_entry_rcu(q, &set->tag_list, tag_set_list) { - if (!blk_queue_skip_tagset_quiesce(q)) - blk_mq_quiesce_queue_nowait(q); - } -- mutex_unlock(&set->tag_list_lock); -+ rcu_read_unlock(); - - blk_mq_wait_quiesce_done(set); - } -@@ -350,12 +350,12 @@ void blk_mq_unquiesce_tagset(struct blk_mq_tag_set *set) - { - struct request_queue *q; - -- mutex_lock(&set->tag_list_lock); -- list_for_each_entry(q, &set->tag_list, tag_set_list) { -+ rcu_read_lock(); -+ list_for_each_entry_rcu(q, &set->tag_list, tag_set_list) { - if (!blk_queue_skip_tagset_quiesce(q)) - blk_mq_unquiesce_queue(q); - } -- mutex_unlock(&set->tag_list_lock); -+ rcu_read_unlock(); - } - EXPORT_SYMBOL_GPL(blk_mq_unquiesce_tagset); - -@@ -4303,7 +4303,7 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) - struct blk_mq_tag_set *set = q->tag_set; - - mutex_lock(&set->tag_list_lock); -- list_del(&q->tag_set_list); -+ list_del_rcu(&q->tag_set_list); - if (list_is_singular(&set->tag_list)) { - /* just transitioned to unshared */ - set->flags &= ~BLK_MQ_F_TAG_QUEUE_SHARED; -@@ -4311,7 +4311,6 @@ static void blk_mq_del_queue_tag_set(struct request_queue *q) - blk_mq_update_tag_set_shared(set, false); - } - mutex_unlock(&set->tag_list_lock); -- INIT_LIST_HEAD(&q->tag_set_list); - } - - static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, -@@ -4330,7 +4329,7 @@ static void blk_mq_add_queue_tag_set(struct blk_mq_tag_set *set, - } - if (set->flags & BLK_MQ_F_TAG_QUEUE_SHARED) - queue_set_hctx_shared(q, true); -- list_add_tail(&q->tag_set_list, &set->tag_list); -+ list_add_tail_rcu(&q->tag_set_list, &set->tag_list); - - mutex_unlock(&set->tag_list_lock); - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68759.patch b/SPECS/kernel/CVE-2025-68759.patch deleted file mode 100644 index d72512995..000000000 --- a/SPECS/kernel/CVE-2025-68759.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 8537f8719ea3f8a8697d95ca396abf177e15bbba Mon Sep 17 00:00:00 2001 -From: Abdun Nihaal -Date: Fri, 14 Nov 2025 15:15:26 +0530 -Subject: [PATCH 29/38] wifi: rtl818x: Fix potential memory leaks in - rtl8180_init_rx_ring() - -In rtl8180_init_rx_ring(), memory is allocated for skb packets and DMA -allocations in a loop. When an allocation fails, the previously -successful allocations are not freed on exit. - -Fix that by jumping to err_free_rings label on error, which calls -rtl8180_free_rx_ring() to free the allocations. Remove the free of -rx_ring in rtl8180_init_rx_ring() error path, and set the freed -priv->rx_buf entry to null, to avoid double free. - -Fixes: f653211197f3 ("Add rtl8180 wireless driver") -Signed-off-by: Abdun Nihaal -Reviewed-by: Ping-Ke Shih -Signed-off-by: Ping-Ke Shih -Link: https://patch.msgid.link/20251114094527.79842-1-nihaal@cse.iitm.ac.in ---- - drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) - -diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c -index 2905baea6239..070c0431c482 100644 ---- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c -+++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c -@@ -1023,9 +1023,6 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) - dma_addr_t *mapping; - entry = priv->rx_ring + priv->rx_ring_sz*i; - if (!skb) { -- dma_free_coherent(&priv->pdev->dev, -- priv->rx_ring_sz * 32, -- priv->rx_ring, priv->rx_ring_dma); - wiphy_err(dev->wiphy, "Cannot allocate RX skb\n"); - return -ENOMEM; - } -@@ -1037,9 +1034,7 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) - - if (dma_mapping_error(&priv->pdev->dev, *mapping)) { - kfree_skb(skb); -- dma_free_coherent(&priv->pdev->dev, -- priv->rx_ring_sz * 32, -- priv->rx_ring, priv->rx_ring_dma); -+ priv->rx_buf[i] = NULL; - wiphy_err(dev->wiphy, "Cannot map DMA for RX skb\n"); - return -ENOMEM; - } -@@ -1130,7 +1125,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) - - ret = rtl8180_init_rx_ring(dev); - if (ret) -- return ret; -+ goto err_free_rings; - - for (i = 0; i < (dev->queues + 1); i++) - if ((ret = rtl8180_init_tx_ring(dev, i, 16))) --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68762.patch b/SPECS/kernel/CVE-2025-68762.patch deleted file mode 100644 index f809f4af5..000000000 --- a/SPECS/kernel/CVE-2025-68762.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 5cfe3b59e6cfec1452878ce5a0390ff00bef2ca1 Mon Sep 17 00:00:00 2001 -From: Breno Leitao -Date: Thu, 27 Nov 2025 07:30:15 -0800 -Subject: [PATCH 28/38] net: netpoll: initialize work queue before error checks - -Prevent a kernel warning when netconsole setup fails on devices with -IFF_DISABLE_NETPOLL flag. The warning (at kernel/workqueue.c:4242 in -__flush_work) occurs because the cleanup path tries to cancel an -uninitialized work queue. - -When __netpoll_setup() encounters a device with IFF_DISABLE_NETPOLL, -it fails early and calls skb_pool_flush() for cleanup. This function -calls cancel_work_sync(&np->refill_wq), but refill_wq hasn't been -initialized yet, triggering the warning. - -Move INIT_WORK() to the beginning of __netpoll_setup(), ensuring the -work queue is properly initialized before any potential failure points. -This allows the cleanup path to safely cancel the work queue regardless -of where the setup fails. - -Fixes: 248f6571fd4c5 ("netpoll: Optimize skb refilling on critical path") -Signed-off-by: Breno Leitao -Link: https://patch.msgid.link/20251127-netpoll_fix_init_work-v1-1-65c07806d736@debian.org -Signed-off-by: Jakub Kicinski ---- - net/core/netpoll.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/net/core/netpoll.c b/net/core/netpoll.c -index be5658ff74ee..27f573d2c5e3 100644 ---- a/net/core/netpoll.c -+++ b/net/core/netpoll.c -@@ -554,6 +554,7 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) - int err; - - skb_queue_head_init(&np->skb_pool); -+ INIT_WORK(&np->refill_wq, refill_skbs_work_handler); - - if (ndev->priv_flags & IFF_DISABLE_NETPOLL) { - np_err(np, "%s doesn't support polling, aborting\n", -@@ -592,7 +593,6 @@ int __netpoll_setup(struct netpoll *np, struct net_device *ndev) - - /* fill up the skb queue */ - refill_skbs(np); -- INIT_WORK(&np->refill_wq, refill_skbs_work_handler); - - /* last thing to do is link it to the net device structure */ - rcu_assign_pointer(ndev->npinfo, npinfo); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68764.patch b/SPECS/kernel/CVE-2025-68764.patch deleted file mode 100644 index 9a26d8274..000000000 --- a/SPECS/kernel/CVE-2025-68764.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 123e370c0d704fea3440f903d59f9ffd6147d111 Mon Sep 17 00:00:00 2001 -From: Trond Myklebust -Date: Fri, 28 Nov 2025 14:22:44 -0500 -Subject: [PATCH 27/38] NFS: Automounted filesystems should inherit - ro,noexec,nodev,sync flags - -When a filesystem is being automounted, it needs to preserve the -user-set superblock mount options, such as the "ro" flag. - -Reported-by: Li Lingfeng -Link: https://lore.kernel.org/all/20240604112636.236517-3-lilingfeng@huaweicloud.com/ -Fixes: f2aedb713c28 ("NFS: Add fs_context support.") -Signed-off-by: Trond Myklebust ---- - fs/nfs/namespace.c | 6 ++++++ - fs/nfs/super.c | 4 ---- - 2 files changed, 6 insertions(+), 4 deletions(-) - -diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c -index 7f1ec9c67ff2..c74e45a89500 100644 ---- a/fs/nfs/namespace.c -+++ b/fs/nfs/namespace.c -@@ -149,6 +149,7 @@ struct vfsmount *nfs_d_automount(struct path *path) - struct vfsmount *mnt = ERR_PTR(-ENOMEM); - struct nfs_server *server = NFS_SB(path->dentry->d_sb); - struct nfs_client *client = server->nfs_client; -+ unsigned long s_flags = path->dentry->d_sb->s_flags; - int timeout = READ_ONCE(nfs_mountpoint_expiry_timeout); - int ret; - -@@ -174,6 +175,11 @@ struct vfsmount *nfs_d_automount(struct path *path) - fc->net_ns = get_net(client->cl_net); - } - -+ /* Inherit the flags covered by NFS_SB_MASK */ -+ fc->sb_flags_mask |= NFS_SB_MASK; -+ fc->sb_flags &= ~NFS_SB_MASK; -+ fc->sb_flags |= s_flags & NFS_SB_MASK; -+ - /* for submounts we want the same server; referrals will reassign */ - memcpy(&ctx->nfs_server._address, &client->cl_addr, client->cl_addrlen); - ctx->nfs_server.addrlen = client->cl_addrlen; -diff --git a/fs/nfs/super.c b/fs/nfs/super.c -index 72dee6f3050e..cf4ab3a5e690 100644 ---- a/fs/nfs/super.c -+++ b/fs/nfs/super.c -@@ -1334,10 +1334,6 @@ int nfs_get_tree_common(struct fs_context *fc) - if (server->flags & NFS_MOUNT_NOAC) - fc->sb_flags |= SB_SYNCHRONOUS; - -- if (ctx->clone_data.sb) -- if (ctx->clone_data.sb->s_flags & SB_SYNCHRONOUS) -- fc->sb_flags |= SB_SYNCHRONOUS; -- - /* Get a superblock - note that we may end up sharing one that already exists */ - fc->s_fs_info = server; - s = sget_fc(fc, compare_super, nfs_set_super); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68768-1.patch b/SPECS/kernel/CVE-2025-68768-1.patch deleted file mode 100644 index 773578a8d..000000000 --- a/SPECS/kernel/CVE-2025-68768-1.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 6df6d25354ec54477df1b9f13457666db545849e Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Sat, 6 Dec 2025 17:09:39 -0800 -Subject: [PATCH 24/38] inet: frags: avoid theoretical race in ip_frag_reinit() - -In ip_frag_reinit() we want to move the frag timeout timer into -the future. If the timer fires in the meantime we inadvertently -scheduled it again, and since the timer assumes a ref on frag_queue -we need to acquire one to balance things out. - -This is technically racy, we should have acquired the reference -_before_ we touch the timer, it may fire again before we take the ref. -Avoid this entire dance by using mod_timer_pending() which only modifies -the timer if its pending (and which exists since Linux v2.6.30) - -Note that this was the only place we ever took a ref on frag_queue -since Eric's conversion to RCU. So we could potentially replace -the whole refcnt field with an atomic flag and a bit more RCU. - -Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") -Reviewed-by: Eric Dumazet -Link: https://patch.msgid.link/20251207010942.1672972-2-kuba@kernel.org -Signed-off-by: Jakub Kicinski ---- - net/ipv4/inet_fragment.c | 4 +++- - net/ipv4/ip_fragment.c | 4 +--- - 2 files changed, 4 insertions(+), 4 deletions(-) - -diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c -index 470ab17ceb51..d310742a23dd 100644 ---- a/net/ipv4/inet_fragment.c -+++ b/net/ipv4/inet_fragment.c -@@ -327,7 +327,9 @@ static struct inet_frag_queue *inet_frag_alloc(struct fqdir *fqdir, - - timer_setup(&q->timer, f->frag_expire, 0); - spin_lock_init(&q->lock); -- /* One reference for the timer, one for the hash table. */ -+ /* One reference for the timer, one for the hash table. -+ * We never take any extra references, only decrement this field. -+ */ - refcount_set(&q->refcnt, 2); - - return q; -diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c -index f7012479713b..d7bccdc9dc69 100644 ---- a/net/ipv4/ip_fragment.c -+++ b/net/ipv4/ip_fragment.c -@@ -242,10 +242,8 @@ static int ip_frag_reinit(struct ipq *qp) - { - unsigned int sum_truesize = 0; - -- if (!mod_timer(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) { -- refcount_inc(&qp->q.refcnt); -+ if (!mod_timer_pending(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) - return -ETIMEDOUT; -- } - - sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, - SKB_DROP_REASON_FRAG_TOO_FAR); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68768-2.patch b/SPECS/kernel/CVE-2025-68768-2.patch deleted file mode 100644 index ca32abed7..000000000 --- a/SPECS/kernel/CVE-2025-68768-2.patch +++ /dev/null @@ -1,90 +0,0 @@ -From 562944a68129db7e84836c708ca15ea8fcd3205c Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Sat, 6 Dec 2025 17:09:40 -0800 -Subject: [PATCH 25/38] inet: frags: add inet_frag_queue_flush() - -Instead of exporting inet_frag_rbtree_purge() which requires that -caller takes care of memory accounting, add a new helper. We will -need to call it from a few places in the next patch. - -Reviewed-by: Eric Dumazet -Link: https://patch.msgid.link/20251207010942.1672972-3-kuba@kernel.org -Signed-off-by: Jakub Kicinski ---- - include/net/inet_frag.h | 5 ++--- - net/ipv4/inet_fragment.c | 15 ++++++++++++--- - net/ipv4/ip_fragment.c | 6 +----- - 3 files changed, 15 insertions(+), 11 deletions(-) - -diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h -index 0eccd9c3a883..3ffaceee7bbc 100644 ---- a/include/net/inet_frag.h -+++ b/include/net/inet_frag.h -@@ -141,9 +141,8 @@ void inet_frag_kill(struct inet_frag_queue *q, int *refs); - void inet_frag_destroy(struct inet_frag_queue *q); - struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key); - --/* Free all skbs in the queue; return the sum of their truesizes. */ --unsigned int inet_frag_rbtree_purge(struct rb_root *root, -- enum skb_drop_reason reason); -+void inet_frag_queue_flush(struct inet_frag_queue *q, -+ enum skb_drop_reason reason); - - static inline void inet_frag_putn(struct inet_frag_queue *q, int refs) - { -diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c -index d310742a23dd..41b2f0373fc9 100644 ---- a/net/ipv4/inet_fragment.c -+++ b/net/ipv4/inet_fragment.c -@@ -263,8 +263,8 @@ static void inet_frag_destroy_rcu(struct rcu_head *head) - kmem_cache_free(f->frags_cachep, q); - } - --unsigned int inet_frag_rbtree_purge(struct rb_root *root, -- enum skb_drop_reason reason) -+static unsigned int -+inet_frag_rbtree_purge(struct rb_root *root, enum skb_drop_reason reason) - { - struct rb_node *p = rb_first(root); - unsigned int sum = 0; -@@ -284,7 +284,16 @@ unsigned int inet_frag_rbtree_purge(struct rb_root *root, - } - return sum; - } --EXPORT_SYMBOL(inet_frag_rbtree_purge); -+ -+void inet_frag_queue_flush(struct inet_frag_queue *q, -+ enum skb_drop_reason reason) -+{ -+ unsigned int sum; -+ -+ sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); -+ sub_frag_mem_limit(q->fqdir, sum); -+} -+EXPORT_SYMBOL(inet_frag_queue_flush); - - void inet_frag_destroy(struct inet_frag_queue *q) - { -diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c -index d7bccdc9dc69..32f1c1a46ba7 100644 ---- a/net/ipv4/ip_fragment.c -+++ b/net/ipv4/ip_fragment.c -@@ -240,14 +240,10 @@ static int ip_frag_too_far(struct ipq *qp) - - static int ip_frag_reinit(struct ipq *qp) - { -- unsigned int sum_truesize = 0; -- - if (!mod_timer_pending(&qp->q.timer, jiffies + qp->q.fqdir->timeout)) - return -ETIMEDOUT; - -- sum_truesize = inet_frag_rbtree_purge(&qp->q.rb_fragments, -- SKB_DROP_REASON_FRAG_TOO_FAR); -- sub_frag_mem_limit(qp->q.fqdir, sum_truesize); -+ inet_frag_queue_flush(&qp->q, SKB_DROP_REASON_FRAG_TOO_FAR); - - qp->q.flags = 0; - qp->q.len = 0; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68768-3.patch b/SPECS/kernel/CVE-2025-68768-3.patch deleted file mode 100644 index 7610b34c8..000000000 --- a/SPECS/kernel/CVE-2025-68768-3.patch +++ /dev/null @@ -1,181 +0,0 @@ -From c5a20b5a005fa8f9a23df5755403253c8d925e6f Mon Sep 17 00:00:00 2001 -From: Jakub Kicinski -Date: Sat, 6 Dec 2025 17:09:41 -0800 -Subject: [PATCH 26/38] inet: frags: flush pending skbs in fqdir_pre_exit() - -We have been seeing occasional deadlocks on pernet_ops_rwsem since -September in NIPA. The stuck task was usually modprobe (often loading -a driver like ipvlan), trying to take the lock as a Writer. -lockdep does not track readers for rwsems so the read wasn't obvious -from the reports. - -On closer inspection the Reader holding the lock was conntrack looping -forever in nf_conntrack_cleanup_net_list(). Based on past experience -with occasional NIPA crashes I looked thru the tests which run before -the crash and noticed that the crash follows ip_defrag.sh. An immediate -red flag. Scouring thru (de)fragmentation queues reveals skbs sitting -around, holding conntrack references. - -The problem is that since conntrack depends on nf_defrag_ipv6, -nf_defrag_ipv6 will load first. Since nf_defrag_ipv6 loads first its -netns exit hooks run _after_ conntrack's netns exit hook. - -Flush all fragment queue SKBs during fqdir_pre_exit() to release -conntrack references before conntrack cleanup runs. Also flush -the queues in timer expiry handlers when they discover fqdir->dead -is set, in case packet sneaks in while we're running the pre_exit -flush. - -The commit under Fixes is not exactly the culprit, but I think -previously the timer firing would eventually unblock the spinning -conntrack. - -Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units") -Reviewed-by: Eric Dumazet -Link: https://patch.msgid.link/20251207010942.1672972-4-kuba@kernel.org -Signed-off-by: Jakub Kicinski ---- - include/net/inet_frag.h | 13 +------------ - include/net/ipv6_frag.h | 9 ++++++--- - net/ipv4/inet_fragment.c | 36 ++++++++++++++++++++++++++++++++++++ - net/ipv4/ip_fragment.c | 12 +++++++----- - 4 files changed, 50 insertions(+), 20 deletions(-) - -diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h -index 3ffaceee7bbc..365925c9d262 100644 ---- a/include/net/inet_frag.h -+++ b/include/net/inet_frag.h -@@ -123,18 +123,7 @@ void inet_frags_fini(struct inet_frags *); - - int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net); - --static inline void fqdir_pre_exit(struct fqdir *fqdir) --{ -- /* Prevent creation of new frags. -- * Pairs with READ_ONCE() in inet_frag_find(). -- */ -- WRITE_ONCE(fqdir->high_thresh, 0); -- -- /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() -- * and ip6frag_expire_frag_queue(). -- */ -- WRITE_ONCE(fqdir->dead, true); --} -+void fqdir_pre_exit(struct fqdir *fqdir); - void fqdir_exit(struct fqdir *fqdir); - - void inet_frag_kill(struct inet_frag_queue *q, int *refs); -diff --git a/include/net/ipv6_frag.h b/include/net/ipv6_frag.h -index 38ef66826939..41d9fc6965f9 100644 ---- a/include/net/ipv6_frag.h -+++ b/include/net/ipv6_frag.h -@@ -69,9 +69,6 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) - int refs = 1; - - rcu_read_lock(); -- /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ -- if (READ_ONCE(fq->q.fqdir->dead)) -- goto out_rcu_unlock; - spin_lock(&fq->q.lock); - - if (fq->q.flags & INET_FRAG_COMPLETE) -@@ -80,6 +77,12 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq) - fq->q.flags |= INET_FRAG_DROP; - inet_frag_kill(&fq->q, &refs); - -+ /* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */ -+ if (READ_ONCE(fq->q.fqdir->dead)) { -+ inet_frag_queue_flush(&fq->q, 0); -+ goto out; -+ } -+ - dev = dev_get_by_index_rcu(net, fq->iif); - if (!dev) - goto out; -diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c -index 41b2f0373fc9..4683560a5fb2 100644 ---- a/net/ipv4/inet_fragment.c -+++ b/net/ipv4/inet_fragment.c -@@ -218,6 +218,41 @@ static int __init inet_frag_wq_init(void) - - pure_initcall(inet_frag_wq_init); - -+void fqdir_pre_exit(struct fqdir *fqdir) -+{ -+ struct inet_frag_queue *fq; -+ struct rhashtable_iter hti; -+ -+ /* Prevent creation of new frags. -+ * Pairs with READ_ONCE() in inet_frag_find(). -+ */ -+ WRITE_ONCE(fqdir->high_thresh, 0); -+ -+ /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire() -+ * and ip6frag_expire_frag_queue(). -+ */ -+ WRITE_ONCE(fqdir->dead, true); -+ -+ rhashtable_walk_enter(&fqdir->rhashtable, &hti); -+ rhashtable_walk_start(&hti); -+ -+ while ((fq = rhashtable_walk_next(&hti))) { -+ if (IS_ERR(fq)) { -+ if (PTR_ERR(fq) != -EAGAIN) -+ break; -+ continue; -+ } -+ spin_lock_bh(&fq->lock); -+ if (!(fq->flags & INET_FRAG_COMPLETE)) -+ inet_frag_queue_flush(fq, 0); -+ spin_unlock_bh(&fq->lock); -+ } -+ -+ rhashtable_walk_stop(&hti); -+ rhashtable_walk_exit(&hti); -+} -+EXPORT_SYMBOL(fqdir_pre_exit); -+ - void fqdir_exit(struct fqdir *fqdir) - { - INIT_WORK(&fqdir->destroy_work, fqdir_work_fn); -@@ -290,6 +325,7 @@ void inet_frag_queue_flush(struct inet_frag_queue *q, - { - unsigned int sum; - -+ reason = reason ?: SKB_DROP_REASON_FRAG_REASM_TIMEOUT; - sum = inet_frag_rbtree_purge(&q->rb_fragments, reason); - sub_frag_mem_limit(q->fqdir, sum); - } -diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c -index 32f1c1a46ba7..56b0f738d2f2 100644 ---- a/net/ipv4/ip_fragment.c -+++ b/net/ipv4/ip_fragment.c -@@ -134,11 +134,6 @@ static void ip_expire(struct timer_list *t) - net = qp->q.fqdir->net; - - rcu_read_lock(); -- -- /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ -- if (READ_ONCE(qp->q.fqdir->dead)) -- goto out_rcu_unlock; -- - spin_lock(&qp->q.lock); - - if (qp->q.flags & INET_FRAG_COMPLETE) -@@ -146,6 +141,13 @@ static void ip_expire(struct timer_list *t) - - qp->q.flags |= INET_FRAG_DROP; - inet_frag_kill(&qp->q, &refs); -+ -+ /* Paired with WRITE_ONCE() in fqdir_pre_exit(). */ -+ if (READ_ONCE(qp->q.fqdir->dead)) { -+ inet_frag_queue_flush(&qp->q, 0); -+ goto out; -+ } -+ - __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); - __IP_INC_STATS(net, IPSTATS_MIB_REASMTIMEOUT); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68791.patch b/SPECS/kernel/CVE-2025-68791.patch deleted file mode 100644 index 8c78b18ec..000000000 --- a/SPECS/kernel/CVE-2025-68791.patch +++ /dev/null @@ -1,74 +0,0 @@ -From c49f1aa60797aa137c9c61564b8de707bd7777b8 Mon Sep 17 00:00:00 2001 -From: Cheng Ding -Date: Tue, 21 Oct 2025 22:46:42 +0200 -Subject: [PATCH 23/38] fuse: missing copy_finish in fuse-over-io-uring - argument copies - -Fix a possible reference count leak of payload pages during -fuse argument copies. - -[Joanne: simplified error cleanup] - -Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support") -Cc: stable@vger.kernel.org # v6.14 -Signed-off-by: Cheng Ding -Signed-off-by: Bernd Schubert -Reviewed-by: Joanne Koong -Signed-off-by: Miklos Szeredi ---- - fs/fuse/dev.c | 2 +- - fs/fuse/dev_uring.c | 5 ++++- - fs/fuse/fuse_dev_i.h | 1 + - 3 files changed, 6 insertions(+), 2 deletions(-) - -diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c -index 612d4da6d7d9..6c296d4fa2f2 100644 ---- a/fs/fuse/dev.c -+++ b/fs/fuse/dev.c -@@ -827,7 +827,7 @@ void fuse_copy_init(struct fuse_copy_state *cs, bool write, - } - - /* Unmap and put previous page of userspace buffer */ --static void fuse_copy_finish(struct fuse_copy_state *cs) -+void fuse_copy_finish(struct fuse_copy_state *cs) - { - if (cs->currbuf) { - struct pipe_buffer *buf = cs->currbuf; -diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c -index 33e9c19cbc3c..f7bd3395ae91 100644 ---- a/fs/fuse/dev_uring.c -+++ b/fs/fuse/dev_uring.c -@@ -598,7 +598,9 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, - cs.is_uring = true; - cs.req = req; - -- return fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); -+ err = fuse_copy_out_args(&cs, args, ring_in_out.payload_sz); -+ fuse_copy_finish(&cs); -+ return err; - } - - /* -@@ -649,6 +651,7 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, - /* copy the payload */ - err = fuse_copy_args(&cs, num_args, args->in_pages, - (struct fuse_arg *)in_args, 0); -+ fuse_copy_finish(&cs); - if (err) { - pr_info_ratelimited("%s fuse_copy_args failed\n", __func__); - return err; -diff --git a/fs/fuse/fuse_dev_i.h b/fs/fuse/fuse_dev_i.h -index 5a9bd771a319..fcebacd061e2 100644 ---- a/fs/fuse/fuse_dev_i.h -+++ b/fs/fuse/fuse_dev_i.h -@@ -53,6 +53,7 @@ void fuse_dev_end_requests(struct list_head *head); - - void fuse_copy_init(struct fuse_copy_state *cs, bool write, - struct iov_iter *iter); -+void fuse_copy_finish(struct fuse_copy_state *cs); - int fuse_copy_args(struct fuse_copy_state *cs, unsigned int numargs, - unsigned int argpages, struct fuse_arg *args, - int zeroing); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68805.patch b/SPECS/kernel/CVE-2025-68805.patch deleted file mode 100644 index f549c42e2..000000000 --- a/SPECS/kernel/CVE-2025-68805.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 4948bfda68ea9e1fd5e4fe6e07dca9e69aefabc3 Mon Sep 17 00:00:00 2001 -From: Joanne Koong -Date: Tue, 25 Nov 2025 10:13:47 -0800 -Subject: [PATCH 22/38] fuse: fix io-uring list corruption for terminated - non-committed requests - -When a request is terminated before it has been committed, the request -is not removed from the queue's list. This leaves a dangling list entry -that leads to list corruption and use-after-free issues. - -Remove the request from the queue's list for terminated non-committed -requests. - -Signed-off-by: Joanne Koong -Fixes: c090c8abae4b ("fuse: Add io-uring sqe commit and fetch support") -Cc: stable@vger.kernel.org -Reviewed-by: Bernd Schubert -Signed-off-by: Miklos Szeredi ---- - fs/fuse/dev_uring.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c -index 249b210becb1..33e9c19cbc3c 100644 ---- a/fs/fuse/dev_uring.c -+++ b/fs/fuse/dev_uring.c -@@ -85,6 +85,7 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, - lockdep_assert_not_held(&queue->lock); - spin_lock(&queue->lock); - ent->fuse_req = NULL; -+ list_del_init(&req->list); - if (test_bit(FR_BACKGROUND, &req->flags)) { - queue->active_background--; - spin_lock(&fc->bg_lock); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68807.patch b/SPECS/kernel/CVE-2025-68807.patch deleted file mode 100644 index a4e453580..000000000 --- a/SPECS/kernel/CVE-2025-68807.patch +++ /dev/null @@ -1,199 +0,0 @@ -From 763f409df079c83b8f0ccbdc8dd22a381ca05cb9 Mon Sep 17 00:00:00 2001 -From: Ming Lei -Date: Fri, 12 Dec 2025 22:35:00 +0800 -Subject: [PATCH 21/38] block: fix race between wbt_enable_default and IO - submission - -When wbt_enable_default() is moved out of queue freezing in elevator_change(), -it can cause the wbt inflight counter to become negative (-1), leading to hung -tasks in the writeback path. Tasks get stuck in wbt_wait() because the counter -is in an inconsistent state. - -The issue occurs because wbt_enable_default() could race with IO submission, -allowing the counter to be decremented before proper initialization. This manifests -as: - - rq_wait[0]: - inflight: -1 - has_waiters: True - -rwb_enabled() checks the state, which can be updated exactly between wbt_wait() -(rq_qos_throttle()) and wbt_track()(rq_qos_track()), then the inflight counter -will become negative. - -And results in hung task warnings like: - task:kworker/u24:39 state:D stack:0 pid:14767 - Call Trace: - rq_qos_wait+0xb4/0x150 - wbt_wait+0xa9/0x100 - __rq_qos_throttle+0x24/0x40 - blk_mq_submit_bio+0x672/0x7b0 - ... - -Fix this by: - -1. Splitting wbt_enable_default() into: - - __wbt_enable_default(): Returns true if wbt_init() should be called - - wbt_enable_default(): Wrapper for existing callers (no init) - - wbt_init_enable_default(): New function that checks and inits WBT - -2. Using wbt_init_enable_default() in blk_register_queue() to ensure - proper initialization during queue registration - -3. Move wbt_init() out of wbt_enable_default() which is only for enabling - disabled wbt from bfq and iocost, and wbt_init() isn't needed. Then the - original lock warning can be avoided. - -4. Removing the ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT flag and its handling - code since it's no longer needed - -This ensures WBT is properly initialized before any IO can be submitted, -preventing the counter from going negative. - -Cc: Nilay Shroff -Cc: Yu Kuai -Cc: Guangwu Zhang -Fixes: 78c271344b6f ("block: move wbt_enable_default() out of queue freezing from sched ->exit()") -Signed-off-by: Ming Lei -Reviewed-by: Nilay Shroff -Signed-off-by: Jens Axboe ---- - block/bfq-iosched.c | 2 +- - block/blk-sysfs.c | 2 +- - block/blk-wbt.c | 20 ++++++++++++++++---- - block/blk-wbt.h | 5 +++++ - block/elevator.c | 5 ----- - block/elevator.h | 1 - - 6 files changed, 23 insertions(+), 12 deletions(-) - -diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c -index 4a8d3d96bfe4..6e54b1d3d8bc 100644 ---- a/block/bfq-iosched.c -+++ b/block/bfq-iosched.c -@@ -7181,7 +7181,7 @@ static void bfq_exit_queue(struct elevator_queue *e) - - blk_stat_disable_accounting(bfqd->queue); - blk_queue_flag_clear(QUEUE_FLAG_DISABLE_WBT_DEF, bfqd->queue); -- set_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT, &e->flags); -+ wbt_enable_default(bfqd->queue->disk); - - kfree(bfqd); - } -diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c -index 9e292d42df60..aaf77f729c5a 100644 ---- a/block/blk-sysfs.c -+++ b/block/blk-sysfs.c -@@ -935,7 +935,7 @@ int blk_register_queue(struct gendisk *disk) - elevator_set_default(q); - - blk_queue_flag_set(QUEUE_FLAG_REGISTERED, q); -- wbt_enable_default(disk); -+ wbt_init_enable_default(disk); - - /* Now everything is ready and send out KOBJ_ADD uevent */ - kobject_uevent(&disk->queue_kobj, KOBJ_ADD); -diff --git a/block/blk-wbt.c b/block/blk-wbt.c -index eb8037bae0bd..0974875f77bd 100644 ---- a/block/blk-wbt.c -+++ b/block/blk-wbt.c -@@ -699,7 +699,7 @@ static void wbt_requeue(struct rq_qos *rqos, struct request *rq) - /* - * Enable wbt if defaults are configured that way - */ --void wbt_enable_default(struct gendisk *disk) -+static bool __wbt_enable_default(struct gendisk *disk) - { - struct request_queue *q = disk->queue; - struct rq_qos *rqos; -@@ -716,19 +716,31 @@ void wbt_enable_default(struct gendisk *disk) - if (enable && RQWB(rqos)->enable_state == WBT_STATE_OFF_DEFAULT) - RQWB(rqos)->enable_state = WBT_STATE_ON_DEFAULT; - mutex_unlock(&disk->rqos_state_mutex); -- return; -+ return false; - } - mutex_unlock(&disk->rqos_state_mutex); - - /* Queue not registered? Maybe shutting down... */ - if (!blk_queue_registered(q)) -- return; -+ return false; - - if (queue_is_mq(q) && enable) -- wbt_init(disk); -+ return true; -+ return false; -+} -+ -+void wbt_enable_default(struct gendisk *disk) -+{ -+ __wbt_enable_default(disk); - } - EXPORT_SYMBOL_GPL(wbt_enable_default); - -+void wbt_init_enable_default(struct gendisk *disk) -+{ -+ if (__wbt_enable_default(disk)) -+ WARN_ON_ONCE(wbt_init(disk)); -+} -+ - u64 wbt_default_latency_nsec(struct request_queue *q) - { - /* -diff --git a/block/blk-wbt.h b/block/blk-wbt.h -index e5fc653b9b76..925f22475738 100644 ---- a/block/blk-wbt.h -+++ b/block/blk-wbt.h -@@ -5,6 +5,7 @@ - #ifdef CONFIG_BLK_WBT - - int wbt_init(struct gendisk *disk); -+void wbt_init_enable_default(struct gendisk *disk); - void wbt_disable_default(struct gendisk *disk); - void wbt_enable_default(struct gendisk *disk); - -@@ -16,6 +17,10 @@ u64 wbt_default_latency_nsec(struct request_queue *); - - #else - -+static inline void wbt_init_enable_default(struct gendisk *disk) -+{ -+} -+ - static inline void wbt_disable_default(struct gendisk *disk) - { - } -diff --git a/block/elevator.c b/block/elevator.c -index e2ebfbf107b3..40cf859b5f53 100644 ---- a/block/elevator.c -+++ b/block/elevator.c -@@ -640,14 +640,9 @@ static int elevator_change_done(struct request_queue *q, - int ret = 0; - - if (ctx->old) { -- bool enable_wbt = test_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT, -- &ctx->old->flags); -- - elv_unregister_queue(q, ctx->old); - blk_mq_free_sched_tags(ctx->old->et, q->tag_set); - kobject_put(&ctx->old->kobj); -- if (enable_wbt) -- wbt_enable_default(q->disk); - } - if (ctx->new) { - ret = elv_register_queue(q, ctx->new, !ctx->no_uevent); -diff --git a/block/elevator.h b/block/elevator.h -index c4d20155065e..63a3e38b9440 100644 ---- a/block/elevator.h -+++ b/block/elevator.h -@@ -132,7 +132,6 @@ struct elevator_queue - - #define ELEVATOR_FLAG_REGISTERED 0 - #define ELEVATOR_FLAG_DYING 1 --#define ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT 2 - - /* - * block elevator interface --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-68823.patch b/SPECS/kernel/CVE-2025-68823.patch deleted file mode 100644 index 139200f21..000000000 --- a/SPECS/kernel/CVE-2025-68823.patch +++ /dev/null @@ -1,113 +0,0 @@ -From 01e1404951461d78c37d49ad15eddb75267fa7e3 Mon Sep 17 00:00:00 2001 -From: Ming Lei -Date: Fri, 12 Dec 2025 22:34:15 +0800 -Subject: [PATCH 20/38] ublk: fix deadlock when reading partition table - -When one process(such as udev) opens ublk block device (e.g., to read -the partition table via bdev_open()), a deadlock[1] can occur: - -1. bdev_open() grabs disk->open_mutex -2. The process issues read I/O to ublk backend to read partition table -3. In __ublk_complete_rq(), blk_update_request() or blk_mq_end_request() - runs bio->bi_end_io() callbacks -4. If this triggers fput() on file descriptor of ublk block device, the - work may be deferred to current task's task work (see fput() implementation) -5. This eventually calls blkdev_release() from the same context -6. blkdev_release() tries to grab disk->open_mutex again -7. Deadlock: same task waiting for a mutex it already holds - -The fix is to run blk_update_request() and blk_mq_end_request() with bottom -halves disabled. This forces blkdev_release() to run in kernel work-queue -context instead of current task work context, and allows ublk server to make -forward progress, and avoids the deadlock. - -Fixes: 71f28f3136af ("ublk_drv: add io_uring based userspace block driver") -Link: https://github.com/ublk-org/ublksrv/issues/170 [1] -Signed-off-by: Ming Lei -Reviewed-by: Caleb Sander Mateos -[axboe: rewrite comment in ublk] -Signed-off-by: Jens Axboe ---- - drivers/block/ublk_drv.c | 32 ++++++++++++++++++++++++++++---- - 1 file changed, 28 insertions(+), 4 deletions(-) - -diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c -index 021274a339b5..89699fc09559 100644 ---- a/drivers/block/ublk_drv.c -+++ b/drivers/block/ublk_drv.c -@@ -1150,6 +1150,13 @@ static inline struct ublk_uring_cmd_pdu *ublk_get_uring_cmd_pdu( - return io_uring_cmd_to_pdu(ioucmd, struct ublk_uring_cmd_pdu); - } - -+static void ublk_end_request(struct request *req, blk_status_t error) -+{ -+ local_bh_disable(); -+ blk_mq_end_request(req, error); -+ local_bh_enable(); -+} -+ - /* todo: handle partial completion */ - static inline void __ublk_complete_rq(struct request *req) - { -@@ -1157,6 +1164,7 @@ static inline void __ublk_complete_rq(struct request *req) - struct ublk_io *io = &ubq->ios[req->tag]; - unsigned int unmapped_bytes; - blk_status_t res = BLK_STS_OK; -+ bool requeue; - - /* failed read IO if nothing is read */ - if (!io->res && req_op(req) == REQ_OP_READ) -@@ -1188,14 +1196,30 @@ static inline void __ublk_complete_rq(struct request *req) - if (unlikely(unmapped_bytes < io->res)) - io->res = unmapped_bytes; - -- if (blk_update_request(req, BLK_STS_OK, io->res)) -+ /* -+ * Run bio->bi_end_io() with softirqs disabled. If the final fput -+ * happens off this path, then that will prevent ublk's blkdev_release() -+ * from being called on current's task work, see fput() implementation. -+ * -+ * Otherwise, ublk server may not provide forward progress in case of -+ * reading the partition table from bdev_open() with disk->open_mutex -+ * held, and causes dead lock as we could already be holding -+ * disk->open_mutex here. -+ * -+ * Preferably we would not be doing IO with a mutex held that is also -+ * used for release, but this work-around will suffice for now. -+ */ -+ local_bh_disable(); -+ requeue = blk_update_request(req, BLK_STS_OK, io->res); -+ local_bh_enable(); -+ if (requeue) - blk_mq_requeue_request(req, true); - else if (likely(!blk_should_fake_timeout(req->q))) - __blk_mq_end_request(req, BLK_STS_OK); - - return; - exit: -- blk_mq_end_request(req, res); -+ ublk_end_request(req, res); - } - - static struct io_uring_cmd *__ublk_prep_compl_io_cmd(struct ublk_io *io, -@@ -1235,7 +1259,7 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq, - if (ublk_nosrv_dev_should_queue_io(ubq->dev)) - blk_mq_requeue_request(rq, false); - else -- blk_mq_end_request(rq, BLK_STS_IOERR); -+ ublk_end_request(rq, BLK_STS_IOERR); - } - - static void -@@ -1259,7 +1283,7 @@ static bool ublk_auto_buf_reg(const struct ublk_queue *ubq, struct request *req, - ublk_auto_buf_reg_fallback(ubq, io); - return true; - } -- blk_mq_end_request(req, BLK_STS_IOERR); -+ ublk_end_request(req, BLK_STS_IOERR); - return false; - } - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71070-1.patch b/SPECS/kernel/CVE-2025-71070-1.patch deleted file mode 100644 index 20ff60db4..000000000 --- a/SPECS/kernel/CVE-2025-71070-1.patch +++ /dev/null @@ -1,101 +0,0 @@ -From 61375d91c986cd67a1f859b931820e109a662b73 Mon Sep 17 00:00:00 2001 -From: Caleb Sander Mateos -Date: Wed, 17 Sep 2025 19:49:40 -0600 -Subject: [PATCH 17/38] ublk: add helpers to check ublk_device flags - -Introduce ublk_device analogues of the ublk_queue flag helpers: -- ublk_support_zero_copy() -> ublk_dev_support_user_copy() -- ublk_support_auto_buf_reg() -> ublk_dev_support_auto_buf_reg() -- ublk_support_user_copy() -> ublk_dev_support_user_copy() -- ublk_need_map_io() -> ublk_dev_need_map_io() -- ublk_need_req_ref() -> ublk_dev_need_req_ref() -- ublk_need_get_data() -> ublk_dev_need_get_data() - -These will be used in subsequent changes to avoid accessing the -ublk_queue just for the flags, and instead use the ublk_device. - -Signed-off-by: Caleb Sander Mateos -Reviewed-by: Ming Lei -Signed-off-by: Jens Axboe ---- - drivers/block/ublk_drv.c | 34 ++++++++++++++++++++++++++++++++++ - 1 file changed, 34 insertions(+) - -diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c -index 67d4a867aec4..9a770d91e840 100644 ---- a/drivers/block/ublk_drv.c -+++ b/drivers/block/ublk_drv.c -@@ -664,22 +664,44 @@ static inline bool ublk_support_zero_copy(const struct ublk_queue *ubq) - return ubq->flags & UBLK_F_SUPPORT_ZERO_COPY; - } - -+static inline bool ublk_dev_support_zero_copy(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY; -+} -+ - static inline bool ublk_support_auto_buf_reg(const struct ublk_queue *ubq) - { - return ubq->flags & UBLK_F_AUTO_BUF_REG; - } - -+static inline bool ublk_dev_support_auto_buf_reg(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_AUTO_BUF_REG; -+} -+ - static inline bool ublk_support_user_copy(const struct ublk_queue *ubq) - { - return ubq->flags & UBLK_F_USER_COPY; - } - -+static inline bool ublk_dev_support_user_copy(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_USER_COPY; -+} -+ - static inline bool ublk_need_map_io(const struct ublk_queue *ubq) - { - return !ublk_support_user_copy(ubq) && !ublk_support_zero_copy(ubq) && - !ublk_support_auto_buf_reg(ubq); - } - -+static inline bool ublk_dev_need_map_io(const struct ublk_device *ub) -+{ -+ return !ublk_dev_support_user_copy(ub) && -+ !ublk_dev_support_zero_copy(ub) && -+ !ublk_dev_support_auto_buf_reg(ub); -+} -+ - static inline bool ublk_need_req_ref(const struct ublk_queue *ubq) - { - /* -@@ -697,6 +719,13 @@ static inline bool ublk_need_req_ref(const struct ublk_queue *ubq) - ublk_support_auto_buf_reg(ubq); - } - -+static inline bool ublk_dev_need_req_ref(const struct ublk_device *ub) -+{ -+ return ublk_dev_support_user_copy(ub) || -+ ublk_dev_support_zero_copy(ub) || -+ ublk_dev_support_auto_buf_reg(ub); -+} -+ - static inline void ublk_init_req_ref(const struct ublk_queue *ubq, - struct ublk_io *io) - { -@@ -728,6 +757,11 @@ static inline bool ublk_need_get_data(const struct ublk_queue *ubq) - return ubq->flags & UBLK_F_NEED_GET_DATA; - } - -+static inline bool ublk_dev_need_get_data(const struct ublk_device *ub) -+{ -+ return ub->dev_info.flags & UBLK_F_NEED_GET_DATA; -+} -+ - /* Called in slow path only, keep it noinline for trace purpose */ - static noinline struct ublk_device *ublk_get_device(struct ublk_device *ub) - { --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71070-2.patch b/SPECS/kernel/CVE-2025-71070-2.patch deleted file mode 100644 index 4ff315c75..000000000 --- a/SPECS/kernel/CVE-2025-71070-2.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 04329cce6eb616853738bee0b4aedfe8d84452cd Mon Sep 17 00:00:00 2001 -From: Caleb Sander Mateos -Date: Fri, 12 Dec 2025 17:19:49 -0700 -Subject: [PATCH 18/38] ublk: clean up user copy references on ublk server exit - -If a ublk server process releases a ublk char device file, any requests -dispatched to the ublk server but not yet completed will retain a ref -value of UBLK_REFCOUNT_INIT. Before commit e63d2228ef83 ("ublk: simplify -aborting ublk request"), __ublk_fail_req() would decrement the reference -count before completing the failed request. However, that commit -optimized __ublk_fail_req() to call __ublk_complete_rq() directly -without decrementing the request reference count. -The leaked reference count incorrectly allows user copy and zero copy -operations on the completed ublk request. It also triggers the -WARN_ON_ONCE(refcount_read(&io->ref)) warnings in ublk_queue_reinit() -and ublk_deinit_queue(). -Commit c5c5eb24ed61 ("ublk: avoid ublk_io_release() called after ublk -char dev is closed") already fixed the issue for ublk devices using -UBLK_F_SUPPORT_ZERO_COPY or UBLK_F_AUTO_BUF_REG. However, the reference -count leak also affects UBLK_F_USER_COPY, the other reference-counted -data copy mode. Fix the condition in ublk_check_and_reset_active_ref() -to include all reference-counted data copy modes. This ensures that any -ublk requests still owned by the ublk server when it exits have their -reference counts reset to 0. - -Signed-off-by: Caleb Sander Mateos -Fixes: e63d2228ef83 ("ublk: simplify aborting ublk request") -Reviewed-by: Ming Lei -Signed-off-by: Jens Axboe ---- - drivers/block/ublk_drv.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c -index 9a770d91e840..021274a339b5 100644 ---- a/drivers/block/ublk_drv.c -+++ b/drivers/block/ublk_drv.c -@@ -1634,8 +1634,7 @@ static bool ublk_check_and_reset_active_ref(struct ublk_device *ub) - { - int i, j; - -- if (!(ub->dev_info.flags & (UBLK_F_SUPPORT_ZERO_COPY | -- UBLK_F_AUTO_BUF_REG))) -+ if (!ublk_dev_need_req_ref(ub)) - return false; - - for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71074.patch b/SPECS/kernel/CVE-2025-71074.patch deleted file mode 100644 index ce60285fa..000000000 --- a/SPECS/kernel/CVE-2025-71074.patch +++ /dev/null @@ -1,136 +0,0 @@ -From 332c42437dca12e7139e5c58747cdcae44c78e27 Mon Sep 17 00:00:00 2001 -From: Al Viro -Date: Fri, 14 Nov 2025 02:18:22 -0500 -Subject: [PATCH 19/38] functionfs: fix the open/removal races - -ffs_epfile_open() can race with removal, ending up with file->private_data -pointing to freed object. - -There is a total count of opened files on functionfs (both ep0 and -dynamic ones) and when it hits zero, dynamic files get removed. -Unfortunately, that removal can happen while another thread is -in ffs_epfile_open(), but has not incremented the count yet. -In that case open will succeed, leaving us with UAF on any subsequent -read() or write(). - -The root cause is that ffs->opened is misused; atomic_dec_and_test() vs. -atomic_add_return() is not a good idea, when object remains visible all -along. - -To untangle that - * serialize openers on ffs->mutex (both for ep0 and for dynamic files) - * have dynamic ones use atomic_inc_not_zero() and fail if we had -zero ->opened; in that case the file we are opening is doomed. - * have the inodes of dynamic files marked on removal (from the -callback of simple_recursive_removal()) - clear ->i_private there. - * have open of dynamic ones verify they hadn't been already removed, -along with checking that state is FFS_ACTIVE. - -Reviewed-by: Greg Kroah-Hartman -Signed-off-by: Al Viro ---- - drivers/usb/gadget/function/f_fs.c | 53 ++++++++++++++++++++++++------ - 1 file changed, 43 insertions(+), 10 deletions(-) - -diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c -index 04058261cdd0..ce94a5101537 100644 ---- a/drivers/usb/gadget/function/f_fs.c -+++ b/drivers/usb/gadget/function/f_fs.c -@@ -640,13 +640,22 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, - - static int ffs_ep0_open(struct inode *inode, struct file *file) - { -- struct ffs_data *ffs = inode->i_private; -+ struct ffs_data *ffs = inode->i_sb->s_fs_info; -+ int ret; - -- if (ffs->state == FFS_CLOSING) -- return -EBUSY; -+ /* Acquire mutex */ -+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); -+ if (ret < 0) -+ return ret; - -- file->private_data = ffs; - ffs_data_opened(ffs); -+ if (ffs->state == FFS_CLOSING) { -+ ffs_data_closed(ffs); -+ mutex_unlock(&ffs->mutex); -+ return -EBUSY; -+ } -+ mutex_unlock(&ffs->mutex); -+ file->private_data = ffs; - - return stream_open(inode, file); - } -@@ -1193,14 +1202,33 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) - static int - ffs_epfile_open(struct inode *inode, struct file *file) - { -- struct ffs_epfile *epfile = inode->i_private; -+ struct ffs_data *ffs = inode->i_sb->s_fs_info; -+ struct ffs_epfile *epfile; -+ int ret; - -- if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) -+ /* Acquire mutex */ -+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK); -+ if (ret < 0) -+ return ret; -+ -+ if (!atomic_inc_not_zero(&ffs->opened)) { -+ mutex_unlock(&ffs->mutex); -+ return -ENODEV; -+ } -+ /* -+ * we want the state to be FFS_ACTIVE; FFS_ACTIVE alone is -+ * not enough, though - we might have been through FFS_CLOSING -+ * and back to FFS_ACTIVE, with our file already removed. -+ */ -+ epfile = smp_load_acquire(&inode->i_private); -+ if (unlikely(ffs->state != FFS_ACTIVE || !epfile)) { -+ mutex_unlock(&ffs->mutex); -+ ffs_data_closed(ffs); - return -ENODEV; -+ } -+ mutex_unlock(&ffs->mutex); - - file->private_data = epfile; -- ffs_data_opened(epfile->ffs); -- - return stream_open(inode, file); - } - -@@ -1332,7 +1360,7 @@ static void ffs_dmabuf_put(struct dma_buf_attachment *attach) - static int - ffs_epfile_release(struct inode *inode, struct file *file) - { -- struct ffs_epfile *epfile = inode->i_private; -+ struct ffs_epfile *epfile = file->private_data; - struct ffs_dmabuf_priv *priv, *tmp; - struct ffs_data *ffs = epfile->ffs; - -@@ -2352,6 +2380,11 @@ static int ffs_epfiles_create(struct ffs_data *ffs) - return 0; - } - -+static void clear_one(struct dentry *dentry) -+{ -+ smp_store_release(&dentry->d_inode->i_private, NULL); -+} -+ - static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) - { - struct ffs_epfile *epfile = epfiles; -@@ -2359,7 +2392,7 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count) - for (; count; --count, ++epfile) { - BUG_ON(mutex_is_locked(&epfile->mutex)); - if (epfile->dentry) { -- simple_recursive_removal(epfile->dentry, NULL); -+ simple_recursive_removal(epfile->dentry, clear_one); - epfile->dentry = NULL; - } - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71090.patch b/SPECS/kernel/CVE-2025-71090.patch deleted file mode 100644 index 16f48d069..000000000 --- a/SPECS/kernel/CVE-2025-71090.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 7f986c0e5ce17634353dcdc1d2578804b358b269 Mon Sep 17 00:00:00 2001 -From: Chuck Lever -Date: Mon, 1 Dec 2025 17:09:55 -0500 -Subject: [PATCH 16/38] nfsd: fix nfsd_file reference leak in - nfsd4_add_rdaccess_to_wrdeleg() - -nfsd4_add_rdaccess_to_wrdeleg() unconditionally overwrites -fp->fi_fds[O_RDONLY] with a newly acquired nfsd_file. However, if -the client already has a SHARE_ACCESS_READ open from a previous OPEN -operation, this action overwrites the existing pointer without -releasing its reference, orphaning the previous reference. - -Additionally, the function originally stored the same nfsd_file -pointer in both fp->fi_fds[O_RDONLY] and fp->fi_rdeleg_file with -only a single reference. When put_deleg_file() runs, it clears -fi_rdeleg_file and calls nfs4_file_put_access() to release the file. - -However, nfs4_file_put_access() only releases fi_fds[O_RDONLY] when -the fi_access[O_RDONLY] counter drops to zero. If another READ open -exists on the file, the counter remains elevated and the nfsd_file -reference from the delegation is never released. This potentially -causes open conflicts on that file. - -Then, on server shutdown, these leaks cause __nfsd_file_cache_purge() -to encounter files with an elevated reference count that cannot be -cleaned up, ultimately triggering a BUG() in kmem_cache_destroy() -because there are still nfsd_file objects allocated in that cache. - -Fixes: e7a8ebc305f2 ("NFSD: Offer write delegation for OPEN with OPEN4_SHARE_ACCESS_WRITE") -Cc: stable@vger.kernel.org -Reviewed-by: Jeff Layton -Signed-off-by: Chuck Lever ---- - fs/nfsd/nfs4state.c | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c -index 87e2a4a3f3a7..aa6b017ec176 100644 ---- a/fs/nfsd/nfs4state.c -+++ b/fs/nfsd/nfs4state.c -@@ -1218,8 +1218,10 @@ static void put_deleg_file(struct nfs4_file *fp) - - if (nf) - nfsd_file_put(nf); -- if (rnf) -+ if (rnf) { -+ nfsd_file_put(rnf); - nfs4_file_put_access(fp, NFS4_SHARE_ACCESS_READ); -+ } - } - - static void nfs4_unlock_deleg_lease(struct nfs4_delegation *dp) -@@ -6187,10 +6189,14 @@ nfsd4_add_rdaccess_to_wrdeleg(struct svc_rqst *rqstp, struct nfsd4_open *open, - fp = stp->st_stid.sc_file; - spin_lock(&fp->fi_lock); - __nfs4_file_get_access(fp, NFS4_SHARE_ACCESS_READ); -- fp = stp->st_stid.sc_file; -- fp->fi_fds[O_RDONLY] = nf; -- fp->fi_rdeleg_file = nf; -+ if (!fp->fi_fds[O_RDONLY]) { -+ fp->fi_fds[O_RDONLY] = nf; -+ nf = NULL; -+ } -+ fp->fi_rdeleg_file = nfsd_file_get(fp->fi_fds[O_RDONLY]); - spin_unlock(&fp->fi_lock); -+ if (nf) -+ nfsd_file_put(nf); - } - return true; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71115.patch b/SPECS/kernel/CVE-2025-71115.patch deleted file mode 100644 index d30c2410e..000000000 --- a/SPECS/kernel/CVE-2025-71115.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 16ecd5094ab8e04c3f208f31ff7d3a33f2e22f87 Mon Sep 17 00:00:00 2001 -From: Johannes Berg -Date: Wed, 24 Sep 2025 11:32:13 +0200 -Subject: [PATCH 15/38] um: init cpu_tasks[] earlier - -This is currently done in uml_finishsetup(), but e.g. with -KCOV enabled we'll crash because some init code can call -into e.g. memparse(), which has coverage annotations, and -then the checks in check_kcov_mode() crash because current -is NULL. - -Simply initialize the cpu_tasks[] array statically, which -fixes the crash. For the later SMP work, it seems to have -not really caused any problems yet, but initialize all of -the entries anyway. - -Link: https://patch.msgid.link/20250924113214.c76cd74d0583.I974f691ebb1a2b47915bd2b04cc38e5263b9447f@changeid -Signed-off-by: Johannes Berg ---- - arch/um/kernel/process.c | 4 +++- - arch/um/kernel/um_arch.c | 2 -- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c -index 9c9c66dc45f0..13d461712c99 100644 ---- a/arch/um/kernel/process.c -+++ b/arch/um/kernel/process.c -@@ -43,7 +43,9 @@ - * cares about its entry, so it's OK if another processor is modifying its - * entry. - */ --struct task_struct *cpu_tasks[NR_CPUS]; -+struct task_struct *cpu_tasks[NR_CPUS] = { -+ [0 ... NR_CPUS - 1] = &init_task, -+}; - EXPORT_SYMBOL(cpu_tasks); - - void free_stack(unsigned long stack, int order) -diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c -index 2f5ee045bc7a..f2edd95d7663 100644 ---- a/arch/um/kernel/um_arch.c -+++ b/arch/um/kernel/um_arch.c -@@ -242,8 +242,6 @@ static struct notifier_block panic_exit_notifier = { - - void uml_finishsetup(void) - { -- cpu_tasks[0] = &init_task; -- - atomic_notifier_chain_register(&panic_notifier_list, - &panic_exit_notifier); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71117.patch b/SPECS/kernel/CVE-2025-71117.patch deleted file mode 100644 index 20576bf06..000000000 --- a/SPECS/kernel/CVE-2025-71117.patch +++ /dev/null @@ -1,219 +0,0 @@ -From 0052e8a55c69e9476b1f36764f4c70f5155c93a3 Mon Sep 17 00:00:00 2001 -From: Bart Van Assche -Date: Fri, 14 Nov 2025 13:04:07 -0800 -Subject: [PATCH 10/38] block: Remove queue freezing from several sysfs store - callbacks - -Freezing the request queue from inside sysfs store callbacks may cause a -deadlock in combination with the dm-multipath driver and the -queue_if_no_path option. Additionally, freezing the request queue slows -down system boot on systems where sysfs attributes are set synchronously. - -Fix this by removing the blk_mq_freeze_queue() / blk_mq_unfreeze_queue() -calls from the store callbacks that do not strictly need these callbacks. -Add the __data_racy annotation to request_queue.rq_timeout to suppress -KCSAN data race reports about the rq_timeout reads. - -This patch may cause a small delay in applying the new settings. - -For all the attributes affected by this patch, I/O will complete -correctly whether the old or the new value of the attribute is used. - -This patch affects the following sysfs attributes: -* io_poll_delay -* io_timeout -* nomerges -* read_ahead_kb -* rq_affinity - -Here is an example of a deadlock triggered by running test srp/002 -if this patch is not applied: - -task:multipathd -Call Trace: - - __schedule+0x8c1/0x1bf0 - schedule+0xdd/0x270 - schedule_preempt_disabled+0x1c/0x30 - __mutex_lock+0xb89/0x1650 - mutex_lock_nested+0x1f/0x30 - dm_table_set_restrictions+0x823/0xdf0 - __bind+0x166/0x590 - dm_swap_table+0x2a7/0x490 - do_resume+0x1b1/0x610 - dev_suspend+0x55/0x1a0 - ctl_ioctl+0x3a5/0x7e0 - dm_ctl_ioctl+0x12/0x20 - __x64_sys_ioctl+0x127/0x1a0 - x64_sys_call+0xe2b/0x17d0 - do_syscall_64+0x96/0x3a0 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - -task:(udev-worker) -Call Trace: - - __schedule+0x8c1/0x1bf0 - schedule+0xdd/0x270 - blk_mq_freeze_queue_wait+0xf2/0x140 - blk_mq_freeze_queue_nomemsave+0x23/0x30 - queue_ra_store+0x14e/0x290 - queue_attr_store+0x23e/0x2c0 - sysfs_kf_write+0xde/0x140 - kernfs_fop_write_iter+0x3b2/0x630 - vfs_write+0x4fd/0x1390 - ksys_write+0xfd/0x230 - __x64_sys_write+0x76/0xc0 - x64_sys_call+0x276/0x17d0 - do_syscall_64+0x96/0x3a0 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 - - -Cc: Christoph Hellwig -Cc: Ming Lei -Cc: Nilay Shroff -Cc: Martin Wilck -Cc: Benjamin Marzinski -Cc: stable@vger.kernel.org -Fixes: af2814149883 ("block: freeze the queue in queue_attr_store") -Signed-off-by: Bart Van Assche -Reviewed-by: Nilay Shroff -Signed-off-by: Jens Axboe ---- - block/blk-sysfs.c | 26 ++++++++------------------ - include/linux/blkdev.h | 2 +- - 2 files changed, 9 insertions(+), 19 deletions(-) - -diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c -index 9b03261b3e04..9e292d42df60 100644 ---- a/block/blk-sysfs.c -+++ b/block/blk-sysfs.c -@@ -146,21 +146,22 @@ queue_ra_store(struct gendisk *disk, const char *page, size_t count) - { - unsigned long ra_kb; - ssize_t ret; -- unsigned int memflags; - struct request_queue *q = disk->queue; - - ret = queue_var_store(&ra_kb, page, count); - if (ret < 0) - return ret; - /* -- * ->ra_pages is protected by ->limits_lock because it is usually -- * calculated from the queue limits by queue_limits_commit_update. -+ * The ->ra_pages change below is protected by ->limits_lock because it -+ * is usually calculated from the queue limits by -+ * queue_limits_commit_update(). -+ * -+ * bdi->ra_pages reads are not serialized against bdi->ra_pages writes. -+ * Use WRITE_ONCE() to write bdi->ra_pages once. - */ - mutex_lock(&q->limits_lock); -- memflags = blk_mq_freeze_queue(q); -- disk->bdi->ra_pages = ra_kb >> (PAGE_SHIFT - 10); -+ WRITE_ONCE(disk->bdi->ra_pages, ra_kb >> (PAGE_SHIFT - 10)); - mutex_unlock(&q->limits_lock); -- blk_mq_unfreeze_queue(q, memflags); - - return ret; - } -@@ -378,21 +379,18 @@ static ssize_t queue_nomerges_store(struct gendisk *disk, const char *page, - size_t count) - { - unsigned long nm; -- unsigned int memflags; - struct request_queue *q = disk->queue; - ssize_t ret = queue_var_store(&nm, page, count); - - if (ret < 0) - return ret; - -- memflags = blk_mq_freeze_queue(q); - blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, q); - blk_queue_flag_clear(QUEUE_FLAG_NOXMERGES, q); - if (nm == 2) - blk_queue_flag_set(QUEUE_FLAG_NOMERGES, q); - else if (nm) - blk_queue_flag_set(QUEUE_FLAG_NOXMERGES, q); -- blk_mq_unfreeze_queue(q, memflags); - - return ret; - } -@@ -412,7 +410,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count) - #ifdef CONFIG_SMP - struct request_queue *q = disk->queue; - unsigned long val; -- unsigned int memflags; - - ret = queue_var_store(&val, page, count); - if (ret < 0) -@@ -424,7 +421,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count) - * are accessed individually using atomic test_bit operation. So we - * don't grab any lock while updating these flags. - */ -- memflags = blk_mq_freeze_queue(q); - if (val == 2) { - blk_queue_flag_set(QUEUE_FLAG_SAME_COMP, q); - blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, q); -@@ -435,7 +431,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count) - blk_queue_flag_clear(QUEUE_FLAG_SAME_COMP, q); - blk_queue_flag_clear(QUEUE_FLAG_SAME_FORCE, q); - } -- blk_mq_unfreeze_queue(q, memflags); - #endif - return ret; - } -@@ -449,11 +444,9 @@ static ssize_t queue_poll_delay_store(struct gendisk *disk, const char *page, - static ssize_t queue_poll_store(struct gendisk *disk, const char *page, - size_t count) - { -- unsigned int memflags; - ssize_t ret = count; - struct request_queue *q = disk->queue; - -- memflags = blk_mq_freeze_queue(q); - if (!(q->limits.features & BLK_FEAT_POLL)) { - ret = -EINVAL; - goto out; -@@ -462,7 +455,6 @@ static ssize_t queue_poll_store(struct gendisk *disk, const char *page, - pr_info_ratelimited("writes to the poll attribute are ignored.\n"); - pr_info_ratelimited("please use driver specific parameters instead.\n"); - out: -- blk_mq_unfreeze_queue(q, memflags); - return ret; - } - -@@ -475,7 +467,7 @@ static ssize_t queue_io_timeout_show(struct gendisk *disk, char *page) - static ssize_t queue_io_timeout_store(struct gendisk *disk, const char *page, - size_t count) - { -- unsigned int val, memflags; -+ unsigned int val; - int err; - struct request_queue *q = disk->queue; - -@@ -483,9 +475,7 @@ static ssize_t queue_io_timeout_store(struct gendisk *disk, const char *page, - if (err || val == 0) - return -EINVAL; - -- memflags = blk_mq_freeze_queue(q); - blk_queue_rq_timeout(q, msecs_to_jiffies(val)); -- blk_mq_unfreeze_queue(q, memflags); - - return count; - } -diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h -index 37fa7169fa9f..a73ed5d85c22 100644 ---- a/include/linux/blkdev.h -+++ b/include/linux/blkdev.h -@@ -485,7 +485,7 @@ struct request_queue { - */ - unsigned long queue_flags; - -- unsigned int rq_timeout; -+ unsigned int __data_racy rq_timeout; - - unsigned int queue_depth; - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71128.patch b/SPECS/kernel/CVE-2025-71128.patch deleted file mode 100644 index 061128e94..000000000 --- a/SPECS/kernel/CVE-2025-71128.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 687b69709d0498240643fde5aa6481b40389f88a Mon Sep 17 00:00:00 2001 -From: Frode Nordahl -Date: Sat, 13 Dec 2025 10:13:36 +0000 -Subject: [PATCH 11/38] erspan: Initialize options_len before referencing - options. - -The struct ip_tunnel_info has a flexible array member named -options that is protected by a counted_by(options_len) -attribute. - -The compiler will use this information to enforce runtime bounds -checking deployed by FORTIFY_SOURCE string helpers. - -As laid out in the GCC documentation, the counter must be -initialized before the first reference to the flexible array -member. - -After scanning through the files that use struct ip_tunnel_info -and also refer to options or options_len, it appears the normal -case is to use the ip_tunnel_info_opts_set() helper. - -Said helper would initialize options_len properly before copying -data into options, however in the GRE ERSPAN code a partial -update is done, preventing the use of the helper function. - -Before this change the handling of ERSPAN traffic in GRE tunnels -would cause a kernel panic when the kernel is compiled with -GCC 15+ and having FORTIFY_SOURCE configured: - -memcpy: detected buffer overflow: 4 byte write of buffer size 0 - -Call Trace: - - __fortify_panic+0xd/0xf - erspan_rcv.cold+0x68/0x83 - ? ip_route_input_slow+0x816/0x9d0 - gre_rcv+0x1b2/0x1c0 - gre_rcv+0x8e/0x100 - ? raw_v4_input+0x2a0/0x2b0 - ip_protocol_deliver_rcu+0x1ea/0x210 - ip_local_deliver_finish+0x86/0x110 - ip_local_deliver+0x65/0x110 - ? ip_rcv_finish_core+0xd6/0x360 - ip_rcv+0x186/0x1a0 - -Cc: stable@vger.kernel.org -Link: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-counted_005fby-variable-attribute -Reported-at: https://launchpad.net/bugs/2129580 -Fixes: bb5e62f2d547 ("net: Add options as a flexible array to struct ip_tunnel_info") -Signed-off-by: Frode Nordahl -Reviewed-by: Simon Horman -Link: https://patch.msgid.link/20251213101338.4693-1-fnordahl@ubuntu.com -Signed-off-by: Paolo Abeni ---- - net/ipv4/ip_gre.c | 6 ++++-- - net/ipv6/ip6_gre.c | 6 ++++-- - 2 files changed, 8 insertions(+), 4 deletions(-) - -diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c -index f5b9004d6938..f8a8a524a199 100644 ---- a/net/ipv4/ip_gre.c -+++ b/net/ipv4/ip_gre.c -@@ -330,6 +330,10 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, - if (!tun_dst) - return PACKET_REJECT; - -+ /* MUST set options_len before referencing options */ -+ info = &tun_dst->u.tun_info; -+ info->options_len = sizeof(*md); -+ - /* skb can be uncloned in __iptunnel_pull_header, so - * old pkt_md is no longer valid and we need to reset - * it -@@ -344,10 +348,8 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, - memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE : - ERSPAN_V2_MDSIZE); - -- info = &tun_dst->u.tun_info; - __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, - info->key.tun_flags); -- info->options_len = sizeof(*md); - } - - skb_reset_mac_header(skb); -diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c -index 74d49dd6124d..cc1757eaf942 100644 ---- a/net/ipv6/ip6_gre.c -+++ b/net/ipv6/ip6_gre.c -@@ -535,6 +535,10 @@ static int ip6erspan_rcv(struct sk_buff *skb, - if (!tun_dst) - return PACKET_REJECT; - -+ /* MUST set options_len before referencing options */ -+ info = &tun_dst->u.tun_info; -+ info->options_len = sizeof(*md); -+ - /* skb can be uncloned in __iptunnel_pull_header, so - * old pkt_md is no longer valid and we need to reset - * it -@@ -543,7 +547,6 @@ static int ip6erspan_rcv(struct sk_buff *skb, - skb_network_header_len(skb); - pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len + - sizeof(*ershdr)); -- info = &tun_dst->u.tun_info; - md = ip_tunnel_info_opts(info); - md->version = ver; - md2 = &md->u.md2; -@@ -551,7 +554,6 @@ static int ip6erspan_rcv(struct sk_buff *skb, - ERSPAN_V2_MDSIZE); - __set_bit(IP_TUNNEL_ERSPAN_OPT_BIT, - info->key.tun_flags); -- info->options_len = sizeof(*md); - - ip6_tnl_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); - --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71139-1.patch b/SPECS/kernel/CVE-2025-71139-1.patch deleted file mode 100644 index 83995468e..000000000 --- a/SPECS/kernel/CVE-2025-71139-1.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 3cc295f54779c5e6aa1412d1990100ba8fe14ae8 Mon Sep 17 00:00:00 2001 -From: Pingfan Liu -Date: Tue, 16 Dec 2025 09:48:51 +0800 -Subject: [PATCH 12/38] kernel/kexec: change the prototype of - kimage_map_segment() - -The kexec segment index will be required to extract the corresponding -information for that segment in kimage_map_segment(). Additionally, -kexec_segment already holds the kexec relocation destination address and -size. Therefore, the prototype of kimage_map_segment() can be changed. - -Link: https://lkml.kernel.org/r/20251216014852.8737-1-piliu@redhat.com -Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation") -Signed-off-by: Pingfan Liu -Acked-by: Baoquan He -Cc: Mimi Zohar -Cc: Roberto Sassu -Cc: Alexander Graf -Cc: Steven Chen -Cc: -Signed-off-by: Andrew Morton ---- - include/linux/kexec.h | 4 ++-- - kernel/kexec_core.c | 9 ++++++--- - security/integrity/ima/ima_kexec.c | 4 +--- - 3 files changed, 9 insertions(+), 8 deletions(-) - -diff --git a/include/linux/kexec.h b/include/linux/kexec.h -index 39fe3e6cd282..6c8abb2534cf 100644 ---- a/include/linux/kexec.h -+++ b/include/linux/kexec.h -@@ -527,7 +527,7 @@ extern bool kexec_file_dbg_print; - #define kexec_dprintk(fmt, arg...) \ - do { if (kexec_file_dbg_print) pr_info(fmt, ##arg); } while (0) - --extern void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size); -+extern void *kimage_map_segment(struct kimage *image, int idx); - extern void kimage_unmap_segment(void *buffer); - #else /* !CONFIG_KEXEC_CORE */ - struct pt_regs; -@@ -537,7 +537,7 @@ static inline void __crash_kexec(struct pt_regs *regs) { } - static inline void crash_kexec(struct pt_regs *regs) { } - static inline int kexec_should_crash(struct task_struct *p) { return 0; } - static inline int kexec_crash_loaded(void) { return 0; } --static inline void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size) -+static inline void *kimage_map_segment(struct kimage *image, int idx) - { return NULL; } - static inline void kimage_unmap_segment(void *buffer) { } - #define kexec_in_progress false -diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c -index 31203f0bacaf..8af0891732d0 100644 ---- a/kernel/kexec_core.c -+++ b/kernel/kexec_core.c -@@ -961,17 +961,20 @@ int kimage_load_segment(struct kimage *image, int idx) - return result; - } - --void *kimage_map_segment(struct kimage *image, -- unsigned long addr, unsigned long size) -+void *kimage_map_segment(struct kimage *image, int idx) - { -+ unsigned long addr, size, eaddr; - unsigned long src_page_addr, dest_page_addr = 0; -- unsigned long eaddr = addr + size; - kimage_entry_t *ptr, entry; - struct page **src_pages; - unsigned int npages; - void *vaddr = NULL; - int i; - -+ addr = image->segment[idx].mem; -+ size = image->segment[idx].memsz; -+ eaddr = addr + size; -+ - /* - * Collect the source pages and map them in a contiguous VA range. - */ -diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c -index 7362f68f2d8b..5beb69edd12f 100644 ---- a/security/integrity/ima/ima_kexec.c -+++ b/security/integrity/ima/ima_kexec.c -@@ -250,9 +250,7 @@ void ima_kexec_post_load(struct kimage *image) - if (!image->ima_buffer_addr) - return; - -- ima_kexec_buffer = kimage_map_segment(image, -- image->ima_buffer_addr, -- image->ima_buffer_size); -+ ima_kexec_buffer = kimage_map_segment(image, image->ima_segment_index); - if (!ima_kexec_buffer) { - pr_err("Could not map measurements buffer.\n"); - return; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71139-2.patch b/SPECS/kernel/CVE-2025-71139-2.patch deleted file mode 100644 index 341adfba8..000000000 --- a/SPECS/kernel/CVE-2025-71139-2.patch +++ /dev/null @@ -1,88 +0,0 @@ -From 68cb92cb11c6bd015aa8b638624f54f269ab2657 Mon Sep 17 00:00:00 2001 -From: Pingfan Liu -Date: Tue, 16 Dec 2025 09:48:52 +0800 -Subject: [PATCH 13/38] kernel/kexec: fix IMA when allocation happens in CMA - area - -*** Bug description *** - -When I tested kexec with the latest kernel, I ran into the following warning: - -[ 40.712410] ------------[ cut here ]------------ -[ 40.712576] WARNING: CPU: 2 PID: 1562 at kernel/kexec_core.c:1001 kimage_map_segment+0x144/0x198 -[...] -[ 40.816047] Call trace: -[ 40.818498] kimage_map_segment+0x144/0x198 (P) -[ 40.823221] ima_kexec_post_load+0x58/0xc0 -[ 40.827246] __do_sys_kexec_file_load+0x29c/0x368 -[...] -[ 40.855423] ---[ end trace 0000000000000000 ]--- - -*** How to reproduce *** - -This bug is only triggered when the kexec target address is allocated in -the CMA area. If no CMA area is reserved in the kernel, use the "cma=" -option in the kernel command line to reserve one. - -*** Root cause *** -The commit 07d24902977e ("kexec: enable CMA based contiguous -allocation") allocates the kexec target address directly on the CMA area -to avoid copying during the jump. In this case, there is no IND_SOURCE -for the kexec segment. But the current implementation of -kimage_map_segment() assumes that IND_SOURCE pages exist and map them -into a contiguous virtual address by vmap(). - -*** Solution *** -If IMA segment is allocated in the CMA area, use its page_address() -directly. - -Link: https://lkml.kernel.org/r/20251216014852.8737-2-piliu@redhat.com -Fixes: 07d24902977e ("kexec: enable CMA based contiguous allocation") -Signed-off-by: Pingfan Liu -Acked-by: Baoquan He -Cc: Alexander Graf -Cc: Steven Chen -Cc: Mimi Zohar -Cc: Roberto Sassu -Cc: -Signed-off-by: Andrew Morton ---- - kernel/kexec_core.c | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c -index 8af0891732d0..2f1c62c817e6 100644 ---- a/kernel/kexec_core.c -+++ b/kernel/kexec_core.c -@@ -968,13 +968,17 @@ void *kimage_map_segment(struct kimage *image, int idx) - kimage_entry_t *ptr, entry; - struct page **src_pages; - unsigned int npages; -+ struct page *cma; - void *vaddr = NULL; - int i; - -+ cma = image->segment_cma[idx]; -+ if (cma) -+ return page_address(cma); -+ - addr = image->segment[idx].mem; - size = image->segment[idx].memsz; - eaddr = addr + size; -- - /* - * Collect the source pages and map them in a contiguous VA range. - */ -@@ -1015,7 +1019,8 @@ void *kimage_map_segment(struct kimage *image, int idx) - - void kimage_unmap_segment(void *segment_buffer) - { -- vunmap(segment_buffer); -+ if (is_vmalloc_addr(segment_buffer)) -+ vunmap(segment_buffer); - } - - struct kexec_load_limit { --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71142.patch b/SPECS/kernel/CVE-2025-71142.patch deleted file mode 100644 index 32a0fe88b..000000000 --- a/SPECS/kernel/CVE-2025-71142.patch +++ /dev/null @@ -1,112 +0,0 @@ -From ef88484b98a0cb6edcbccdebc0eac1062ae97acc Mon Sep 17 00:00:00 2001 -From: Chen Ridong -Date: Thu, 18 Dec 2025 01:59:50 +0000 -Subject: [PATCH 14/38] cpuset: fix warning when disabling remote partition - -A warning was triggered as follows: - -WARNING: kernel/cgroup/cpuset.c:1651 at remote_partition_disable+0xf7/0x110 -RIP: 0010:remote_partition_disable+0xf7/0x110 -RSP: 0018:ffffc90001947d88 EFLAGS: 00000206 -RAX: 0000000000007fff RBX: ffff888103b6e000 RCX: 0000000000006f40 -RDX: 0000000000006f00 RSI: ffffc90001947da8 RDI: ffff888103b6e000 -RBP: ffff888103b6e000 R08: 0000000000000000 R09: 0000000000000000 -R10: 0000000000000001 R11: ffff88810b2e2728 R12: ffffc90001947da8 -R13: 0000000000000000 R14: ffffc90001947da8 R15: ffff8881081f1c00 -CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 -CR2: 00007f55c8bbe0b2 CR3: 000000010b14c000 CR4: 00000000000006f0 -Call Trace: - - update_prstate+0x2d3/0x580 - cpuset_partition_write+0x94/0xf0 - kernfs_fop_write_iter+0x147/0x200 - vfs_write+0x35d/0x500 - ksys_write+0x66/0xe0 - do_syscall_64+0x6b/0x390 - entry_SYSCALL_64_after_hwframe+0x4b/0x53 -RIP: 0033:0x7f55c8cd4887 - -Reproduction steps (on a 16-CPU machine): - - # cd /sys/fs/cgroup/ - # mkdir A1 - # echo +cpuset > A1/cgroup.subtree_control - # echo "0-14" > A1/cpuset.cpus.exclusive - # mkdir A1/A2 - # echo "0-14" > A1/A2/cpuset.cpus.exclusive - # echo "root" > A1/A2/cpuset.cpus.partition - # echo 0 > /sys/devices/system/cpu/cpu15/online - # echo member > A1/A2/cpuset.cpus.partition - -When CPU 15 is offlined, subpartitions_cpus gets cleared because no CPUs -remain available for the top_cpuset, forcing partitions to share CPUs with -the top_cpuset. In this scenario, disabling the remote partition triggers -a warning stating that effective_xcpus is not a subset of -subpartitions_cpus. Partitions should be invalidated in this case to -inform users that the partition is now invalid(cpus are shared with -top_cpuset). - -To fix this issue: -1. Only emit the warning only if subpartitions_cpus is not empty and the - effective_xcpus is not a subset of subpartitions_cpus. -2. During the CPU hotplug process, invalidate partitions if - subpartitions_cpus is empty. - -Fixes: f62a5d39368e ("cgroup/cpuset: Remove remote_partition_check() & make update_cpumasks_hier() handle remote partition") -Signed-off-by: Chen Ridong -Reviewed-by: Waiman Long -Signed-off-by: Tejun Heo ---- - kernel/cgroup/cpuset.c | 21 ++++++++++++++++----- - 1 file changed, 16 insertions(+), 5 deletions(-) - -diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c -index fd890b34a840..bf380f11b874 100644 ---- a/kernel/cgroup/cpuset.c -+++ b/kernel/cgroup/cpuset.c -@@ -1496,7 +1496,14 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp) - bool isolcpus_updated; - - WARN_ON_ONCE(!is_remote_partition(cs)); -- WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus)); -+ /* -+ * When a CPU is offlined, top_cpuset may end up with no available CPUs, -+ * which should clear subpartitions_cpus. We should not emit a warning for this -+ * scenario: the hierarchy is updated from top to bottom, so subpartitions_cpus -+ * may already be cleared when disabling the partition. -+ */ -+ WARN_ON_ONCE(!cpumask_subset(cs->effective_xcpus, subpartitions_cpus) && -+ !cpumask_empty(subpartitions_cpus)); - - spin_lock_irq(&callback_lock); - list_del_init(&cs->remote_sibling); -@@ -3844,8 +3851,9 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) - if (remote || (is_partition_valid(cs) && is_partition_valid(parent))) - compute_partition_effective_cpumask(cs, &new_cpus); - -- if (remote && cpumask_empty(&new_cpus) && -- partition_is_populated(cs, NULL)) { -+ if (remote && (cpumask_empty(subpartitions_cpus) || -+ (cpumask_empty(&new_cpus) && -+ partition_is_populated(cs, NULL)))) { - cs->prs_err = PERR_HOTPLUG; - remote_partition_disable(cs, tmp); - compute_effective_cpumask(&new_cpus, cs, parent); -@@ -3858,9 +3866,12 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp) - * 1) empty effective cpus but not valid empty partition. - * 2) parent is invalid or doesn't grant any cpus to child - * partitions. -+ * 3) subpartitions_cpus is empty. - */ -- if (is_local_partition(cs) && (!is_partition_valid(parent) || -- tasks_nocpu_error(parent, cs, &new_cpus))) -+ if (is_local_partition(cs) && -+ (!is_partition_valid(parent) || -+ tasks_nocpu_error(parent, cs, &new_cpus) || -+ cpumask_empty(subpartitions_cpus))) - partcmd = partcmd_invalidate; - /* - * On the other hand, an invalid partition root may be transitioned --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2025-71161.patch b/SPECS/kernel/CVE-2025-71161.patch deleted file mode 100644 index a2ba95045..000000000 --- a/SPECS/kernel/CVE-2025-71161.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 4398ba78106304ff9c60518b9d86e17cbc41089e Mon Sep 17 00:00:00 2001 -From: Mikulas Patocka -Date: Fri, 14 Nov 2025 16:54:01 +0100 -Subject: [PATCH 08/38] dm-verity: disable recursive forward error correction - -There are two problems with the recursive correction: - -1. It may cause denial-of-service. In fec_read_bufs, there is a loop that -has 253 iterations. For each iteration, we may call verity_hash_for_block -recursively. There is a limit of 4 nested recursions - that means that -there may be at most 253^4 (4 billion) iterations. Red Hat QE team -actually created an image that pushes dm-verity to this limit - and this -image just makes the udev-worker process get stuck in the 'D' state. - -2. It doesn't work. In fec_read_bufs we store data into the variable -"fio->bufs", but fio bufs is shared between recursive invocations, if -"verity_hash_for_block" invoked correction recursively, it would -overwrite partially filled fio->bufs. - -Signed-off-by: Mikulas Patocka -Reported-by: Guangwu Zhang -Reviewed-by: Sami Tolvanen -Reviewed-by: Eric Biggers ---- - drivers/md/dm-verity-fec.c | 4 +--- - drivers/md/dm-verity-fec.h | 3 --- - drivers/md/dm-verity-target.c | 2 +- - 3 files changed, 2 insertions(+), 7 deletions(-) - -diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c -index 72047b47a7a0..e41bde1d3b15 100644 ---- a/drivers/md/dm-verity-fec.c -+++ b/drivers/md/dm-verity-fec.c -@@ -413,10 +413,8 @@ int verity_fec_decode(struct dm_verity *v, struct dm_verity_io *io, - if (!verity_fec_is_enabled(v)) - return -EOPNOTSUPP; - -- if (fio->level >= DM_VERITY_FEC_MAX_RECURSION) { -- DMWARN_LIMIT("%s: FEC: recursion too deep", v->data_dev->name); -+ if (fio->level) - return -EIO; -- } - - fio->level++; - -diff --git a/drivers/md/dm-verity-fec.h b/drivers/md/dm-verity-fec.h -index 09123a612953..ec37e607cb3f 100644 ---- a/drivers/md/dm-verity-fec.h -+++ b/drivers/md/dm-verity-fec.h -@@ -23,9 +23,6 @@ - #define DM_VERITY_FEC_BUF_MAX \ - (1 << (PAGE_SHIFT - DM_VERITY_FEC_BUF_RS_BITS)) - --/* maximum recursion level for verity_fec_decode */ --#define DM_VERITY_FEC_MAX_RECURSION 4 -- - #define DM_VERITY_OPT_FEC_DEV "use_fec_from_device" - #define DM_VERITY_OPT_FEC_BLOCKS "fec_blocks" - #define DM_VERITY_OPT_FEC_START "fec_start" -diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c -index 66a00a8ccb39..c8695c079cfe 100644 ---- a/drivers/md/dm-verity-target.c -+++ b/drivers/md/dm-verity-target.c -@@ -1690,7 +1690,7 @@ static struct target_type verity_target = { - .name = "verity", - /* Note: the LSMs depend on the singleton and immutable features */ - .features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE, -- .version = {1, 12, 0}, -+ .version = {1, 13, 0}, - .module = THIS_MODULE, - .ctr = verity_ctr, - .dtr = verity_dtr, --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-22981.patch b/SPECS/kernel/CVE-2026-22981.patch deleted file mode 100644 index ce2aee1a8..000000000 --- a/SPECS/kernel/CVE-2026-22981.patch +++ /dev/null @@ -1,275 +0,0 @@ -From 0c2dcb673e8d902de98f3a24c07e788dcf14c35c Mon Sep 17 00:00:00 2001 -From: Emil Tantilov -Date: Thu, 20 Nov 2025 16:12:15 -0800 -Subject: [PATCH 07/38] idpf: detach and close netdevs while handling a reset - -Protect the reset path from callbacks by setting the netdevs to detached -state and close any netdevs in UP state until the reset handling has -completed. During a reset, the driver will de-allocate resources for the -vport, and there is no guarantee that those will recover, which is why the -existing vport_ctrl_lock does not provide sufficient protection. - -idpf_detach_and_close() is called right before reset handling. If the -reset handling succeeds, the netdevs state is recovered via call to -idpf_attach_and_open(). If the reset handling fails the netdevs remain -down. The detach/down calls are protected with RTNL lock to avoid racing -with callbacks. On the recovery side the attach can be done without -holding the RTNL lock as there are no callbacks expected at that point, -due to detach/close always being done first in that flow. - -The previous logic restoring the netdevs state based on the -IDPF_VPORT_UP_REQUESTED flag in the init task is not needed anymore, hence -the removal of idpf_set_vport_state(). The IDPF_VPORT_UP_REQUESTED is -still being used to restore the state of the netdevs following the reset, -but has no use outside of the reset handling flow. - -idpf_init_hard_reset() is converted to void, since it was used as such and -there is no error handling being done based on its return value. - -Before this change, invoking hard and soft resets simultaneously will -cause the driver to lose the vport state: -ip -br a - UP -echo 1 > /sys/class/net/ens801f0/device/reset& \ -ethtool -L ens801f0 combined 8 -ip -br a - DOWN -ip link set up -ip -br a - DOWN - -Also in case of a failure in the reset path, the netdev is left -exposed to external callbacks, while vport resources are not -initialized, leading to a crash on subsequent ifup/down: -[408471.398966] idpf 0000:83:00.0: HW reset detected -[408471.411744] idpf 0000:83:00.0: Device HW Reset initiated -[408472.277901] idpf 0000:83:00.0: The driver was unable to contact the device's firmware. Check that the FW is running. Driver state= 0x2 -[408508.125551] BUG: kernel NULL pointer dereference, address: 0000000000000078 -[408508.126112] #PF: supervisor read access in kernel mode -[408508.126687] #PF: error_code(0x0000) - not-present page -[408508.127256] PGD 2aae2f067 P4D 0 -[408508.127824] Oops: Oops: 0000 [#1] SMP NOPTI -... -[408508.130871] RIP: 0010:idpf_stop+0x39/0x70 [idpf] -... -[408508.139193] Call Trace: -[408508.139637] -[408508.140077] __dev_close_many+0xbb/0x260 -[408508.140533] __dev_change_flags+0x1cf/0x280 -[408508.140987] netif_change_flags+0x26/0x70 -[408508.141434] dev_change_flags+0x3d/0xb0 -[408508.141878] devinet_ioctl+0x460/0x890 -[408508.142321] inet_ioctl+0x18e/0x1d0 -[408508.142762] ? _copy_to_user+0x22/0x70 -[408508.143207] sock_do_ioctl+0x3d/0xe0 -[408508.143652] sock_ioctl+0x10e/0x330 -[408508.144091] ? find_held_lock+0x2b/0x80 -[408508.144537] __x64_sys_ioctl+0x96/0xe0 -[408508.144979] do_syscall_64+0x79/0x3d0 -[408508.145415] entry_SYSCALL_64_after_hwframe+0x76/0x7e -[408508.145860] RIP: 0033:0x7f3e0bb4caff - -Fixes: 0fe45467a104 ("idpf: add create vport and netdev configuration") -Signed-off-by: Emil Tantilov -Reviewed-by: Madhu Chittim -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen ---- - drivers/net/ethernet/intel/idpf/idpf_lib.c | 121 ++++++++++++--------- - 1 file changed, 72 insertions(+), 49 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index 7e22790aa9a8..99c7ffe118e5 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -727,6 +727,65 @@ static int idpf_init_mac_addr(struct idpf_vport *vport, - return 0; - } - -+static void idpf_detach_and_close(struct idpf_adapter *adapter) -+{ -+ int max_vports = adapter->max_vports; -+ -+ for (int i = 0; i < max_vports; i++) { -+ struct net_device *netdev = adapter->netdevs[i]; -+ -+ /* If the interface is in detached state, that means the -+ * previous reset was not handled successfully for this -+ * vport. -+ */ -+ if (!netif_device_present(netdev)) -+ continue; -+ -+ /* Hold RTNL to protect racing with callbacks */ -+ rtnl_lock(); -+ netif_device_detach(netdev); -+ if (netif_running(netdev)) { -+ set_bit(IDPF_VPORT_UP_REQUESTED, -+ adapter->vport_config[i]->flags); -+ dev_close(netdev); -+ } -+ rtnl_unlock(); -+ } -+} -+ -+static void idpf_attach_and_open(struct idpf_adapter *adapter) -+{ -+ int max_vports = adapter->max_vports; -+ -+ for (int i = 0; i < max_vports; i++) { -+ struct idpf_vport *vport = adapter->vports[i]; -+ struct idpf_vport_config *vport_config; -+ struct net_device *netdev; -+ -+ /* In case of a critical error in the init task, the vport -+ * will be freed. Only continue to restore the netdevs -+ * if the vport is allocated. -+ */ -+ if (!vport) -+ continue; -+ -+ /* No need for RTNL on attach as this function is called -+ * following detach and dev_close(). We do take RTNL for -+ * dev_open() below as it can race with external callbacks -+ * following the call to netif_device_attach(). -+ */ -+ netdev = adapter->netdevs[i]; -+ netif_device_attach(netdev); -+ vport_config = adapter->vport_config[vport->idx]; -+ if (test_and_clear_bit(IDPF_VPORT_UP_REQUESTED, -+ vport_config->flags)) { -+ rtnl_lock(); -+ dev_open(netdev, NULL); -+ rtnl_unlock(); -+ } -+ } -+} -+ - /** - * idpf_cfg_netdev - Allocate, configure and register a netdev - * @vport: main vport structure -@@ -1036,10 +1095,11 @@ static void idpf_vport_dealloc(struct idpf_vport *vport) - idpf_idc_deinit_vport_aux_device(vport->vdev_info); - - idpf_deinit_mac_addr(vport); -- idpf_vport_stop(vport, true); - -- if (!test_bit(IDPF_HR_RESET_IN_PROG, adapter->flags)) -+ if (!test_bit(IDPF_HR_RESET_IN_PROG, adapter->flags)) { -+ idpf_vport_stop(vport, true); - idpf_decfg_netdev(vport); -+ } - if (test_bit(IDPF_REMOVE_IN_PROG, adapter->flags)) - idpf_del_all_mac_filters(vport); - -@@ -1523,7 +1583,6 @@ void idpf_init_task(struct work_struct *work) - struct idpf_vport_config *vport_config; - struct idpf_vport_max_q max_q; - struct idpf_adapter *adapter; -- struct idpf_netdev_priv *np; - struct idpf_vport *vport; - u16 num_default_vports; - struct pci_dev *pdev; -@@ -1581,12 +1640,6 @@ void idpf_init_task(struct work_struct *work) - if (err) - goto handle_err; - -- /* Once state is put into DOWN, driver is ready for dev_open */ -- np = netdev_priv(vport->netdev); -- np->state = __IDPF_VPORT_DOWN; -- if (test_and_clear_bit(IDPF_VPORT_UP_REQUESTED, vport_config->flags)) -- idpf_vport_open(vport, true); -- - /* Spawn and return 'idpf_init_task' work queue until all the - * default vports are created - */ -@@ -1767,27 +1820,6 @@ static int idpf_check_reset_complete(struct idpf_hw *hw, - return -EBUSY; - } - --/** -- * idpf_set_vport_state - Set the vport state to be after the reset -- * @adapter: Driver specific private structure -- */ --static void idpf_set_vport_state(struct idpf_adapter *adapter) --{ -- u16 i; -- -- for (i = 0; i < adapter->max_vports; i++) { -- struct idpf_netdev_priv *np; -- -- if (!adapter->netdevs[i]) -- continue; -- -- np = netdev_priv(adapter->netdevs[i]); -- if (np->state == __IDPF_VPORT_UP) -- set_bit(IDPF_VPORT_UP_REQUESTED, -- adapter->vport_config[i]->flags); -- } --} -- - /** - * idpf_init_hard_reset - Initiate a hardware reset - * @adapter: Driver specific private structure -@@ -1796,28 +1828,17 @@ static void idpf_set_vport_state(struct idpf_adapter *adapter) - * reallocate. Also reinitialize the mailbox. Return 0 on success, - * negative on failure. - */ --static int idpf_init_hard_reset(struct idpf_adapter *adapter) -+static void idpf_init_hard_reset(struct idpf_adapter *adapter) - { - struct idpf_reg_ops *reg_ops = &adapter->dev_ops.reg_ops; - struct device *dev = &adapter->pdev->dev; -- struct net_device *netdev; - int err; -- u16 i; - -+ idpf_detach_and_close(adapter); - mutex_lock(&adapter->vport_ctrl_lock); - - dev_info(dev, "Device HW Reset initiated\n"); - -- /* Avoid TX hangs on reset */ -- for (i = 0; i < adapter->max_vports; i++) { -- netdev = adapter->netdevs[i]; -- if (!netdev) -- continue; -- -- netif_carrier_off(netdev); -- netif_tx_disable(netdev); -- } -- - /* Prepare for reset */ - if (test_and_clear_bit(IDPF_HR_DRV_LOAD, adapter->flags)) { - reg_ops->trigger_reset(adapter, IDPF_HR_DRV_LOAD); -@@ -1826,7 +1847,6 @@ static int idpf_init_hard_reset(struct idpf_adapter *adapter) - - idpf_idc_issue_reset_event(adapter->cdev_info); - -- idpf_set_vport_state(adapter); - idpf_vc_core_deinit(adapter); - if (!is_reset) - reg_ops->trigger_reset(adapter, IDPF_HR_FUNC_RESET); -@@ -1873,11 +1893,14 @@ static int idpf_init_hard_reset(struct idpf_adapter *adapter) - unlock_mutex: - mutex_unlock(&adapter->vport_ctrl_lock); - -- /* Wait until all vports are created to init RDMA CORE AUX */ -- if (!err) -- err = idpf_idc_init(adapter); -- -- return err; -+ /* Attempt to restore netdevs and initialize RDMA CORE AUX device, -+ * provided vc_core_init succeeded. It is still possible that -+ * vports are not allocated at this point if the init task failed. -+ */ -+ if (!err) { -+ idpf_attach_and_open(adapter); -+ idpf_idc_init(adapter); -+ } - } - - /** --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-22985-1.patch b/SPECS/kernel/CVE-2026-22985-1.patch deleted file mode 100644 index 7e973d18a..000000000 --- a/SPECS/kernel/CVE-2026-22985-1.patch +++ /dev/null @@ -1,383 +0,0 @@ -From 636d193e216844e1987a1f2a6b4bfd973c2f3661 Mon Sep 17 00:00:00 2001 -From: Sreedevi Joshi -Date: Mon, 24 Nov 2025 12:47:48 -0600 -Subject: [PATCH 37/38] idpf: Fix RSS LUT NULL pointer crash on early ethtool - operations - -The RSS LUT is not initialized until the interface comes up, causing -the following NULL pointer crash when ethtool operations like rxhash on/off -are performed before the interface is brought up for the first time. - -Move RSS LUT initialization from ndo_open to vport creation to ensure LUT -is always available. This enables RSS configuration via ethtool before -bringing the interface up. Simplify LUT management by maintaining all -changes in the driver's soft copy and programming zeros to the indirection -table when rxhash is disabled. Defer HW programming until the interface -comes up if it is down during rxhash and LUT configuration changes. - -Steps to reproduce: -** Load idpf driver; interfaces will be created - modprobe idpf -** Before bringing the interfaces up, turn rxhash off - ethtool -K eth2 rxhash off - -[89408.371875] BUG: kernel NULL pointer dereference, address: 0000000000000000 -[89408.371908] #PF: supervisor read access in kernel mode -[89408.371924] #PF: error_code(0x0000) - not-present page -[89408.371940] PGD 0 P4D 0 -[89408.371953] Oops: Oops: 0000 [#1] SMP NOPTI - -[89408.372052] RIP: 0010:memcpy_orig+0x16/0x130 -[89408.372310] Call Trace: -[89408.372317] -[89408.372326] ? idpf_set_features+0xfc/0x180 [idpf] -[89408.372363] __netdev_update_features+0x295/0xde0 -[89408.372384] ethnl_set_features+0x15e/0x460 -[89408.372406] genl_family_rcv_msg_doit+0x11f/0x180 -[89408.372429] genl_rcv_msg+0x1ad/0x2b0 -[89408.372446] ? __pfx_ethnl_set_features+0x10/0x10 -[89408.372465] ? __pfx_genl_rcv_msg+0x10/0x10 -[89408.372482] netlink_rcv_skb+0x58/0x100 -[89408.372502] genl_rcv+0x2c/0x50 -[89408.372516] netlink_unicast+0x289/0x3e0 -[89408.372533] netlink_sendmsg+0x215/0x440 -[89408.372551] __sys_sendto+0x234/0x240 -[89408.372571] __x64_sys_sendto+0x28/0x30 -[89408.372585] x64_sys_call+0x1909/0x1da0 -[89408.372604] do_syscall_64+0x7a/0xfa0 -[89408.373140] ? clear_bhb_loop+0x60/0xb0 -[89408.373647] entry_SYSCALL_64_after_hwframe+0x76/0x7e -[89408.378887] - - -Fixes: a251eee62133 ("idpf: add SRIOV support and other ndo_ops") -Signed-off-by: Sreedevi Joshi -Reviewed-by: Sridhar Samudrala -Reviewed-by: Emil Tantilov -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Paul Menzel -Reviewed-by: Simon Horman -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen ---- - drivers/net/ethernet/intel/idpf/idpf.h | 2 - - drivers/net/ethernet/intel/idpf/idpf_lib.c | 87 +++++++++---------- - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 36 +++----- - drivers/net/ethernet/intel/idpf/idpf_txrx.h | 4 +- - .../net/ethernet/intel/idpf/idpf_virtchnl.c | 9 +- - 5 files changed, 64 insertions(+), 74 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h -index aafbb280c2e7..4fcf2be878e5 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf.h -+++ b/drivers/net/ethernet/intel/idpf/idpf.h -@@ -397,14 +397,12 @@ enum idpf_user_flags { - * @rss_key: RSS hash key - * @rss_lut_size: Size of RSS lookup table - * @rss_lut: RSS lookup table -- * @cached_lut: Used to restore previously init RSS lut - */ - struct idpf_rss_data { - u16 rss_key_size; - u8 *rss_key; - u16 rss_lut_size; - u32 *rss_lut; -- u32 *cached_lut; - }; - - /** -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index 99c7ffe118e5..2021dd6059af 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -1045,7 +1045,7 @@ static void idpf_vport_rel(struct idpf_vport *vport) - u16 idx = vport->idx; - - vport_config = adapter->vport_config[vport->idx]; -- idpf_deinit_rss(vport); -+ idpf_deinit_rss_lut(vport); - rss_data = &vport_config->user_config.rss_data; - kfree(rss_data->rss_key); - rss_data->rss_key = NULL; -@@ -1194,6 +1194,7 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - u16 idx = adapter->next_vport; - struct idpf_vport *vport; - u16 num_max_q; -+ int err; - - if (idx == IDPF_NO_FREE_SLOT) - return NULL; -@@ -1244,10 +1245,11 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - - idpf_vport_init(vport, max_q); - -- /* This alloc is done separate from the LUT because it's not strictly -- * dependent on how many queues we have. If we change number of queues -- * and soft reset we'll need a new LUT but the key can remain the same -- * for as long as the vport exists. -+ /* LUT and key are both initialized here. Key is not strictly dependent -+ * on how many queues we have. If we change number of queues and soft -+ * reset is initiated, LUT will be freed and a new LUT will be allocated -+ * as per the updated number of queues during vport bringup. However, -+ * the key remains the same for as long as the vport exists. - */ - rss_data = &adapter->vport_config[idx]->user_config.rss_data; - rss_data->rss_key = kzalloc(rss_data->rss_key_size, GFP_KERNEL); -@@ -1257,6 +1259,11 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - /* Initialize default rss key */ - netdev_rss_key_fill((void *)rss_data->rss_key, rss_data->rss_key_size); - -+ /* Initialize default rss LUT */ -+ err = idpf_init_rss_lut(vport); -+ if (err) -+ goto free_rss_key; -+ - /* fill vport slot in the adapter struct */ - adapter->vports[idx] = vport; - adapter->vport_ids[idx] = idpf_get_vport_id(vport); -@@ -1267,6 +1274,8 @@ static struct idpf_vport *idpf_vport_alloc(struct idpf_adapter *adapter, - - return vport; - -+free_rss_key: -+ kfree(rss_data->rss_key); - free_vector_idxs: - kfree(vport->q_vector_idxs); - free_vport: -@@ -1527,9 +1536,22 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - - idpf_restore_features(vport); - -+ vport_config = adapter->vport_config[vport->idx]; -+ rss_data = &vport_config->user_config.rss_data; -+ -+ if (!rss_data->rss_lut) { -+ err = idpf_init_rss_lut(vport); -+ if (err) { -+ dev_err(&adapter->pdev->dev, -+ "Failed to initialize RSS LUT for vport %u: %d\n", -+ vport->vport_id, err); -+ goto disable_vport; -+ } -+ } -+ - err = idpf_config_rss(vport); - if (err) { -- dev_err(&adapter->pdev->dev, "Failed to initialize RSS for vport %u: %d\n", -+ dev_err(&adapter->pdev->dev, "Failed to configure RSS for vport %u: %d\n", - vport->vport_id, err); - goto disable_vport; - } -@@ -1538,7 +1560,7 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - if (err) { - dev_err(&adapter->pdev->dev, "Failed to complete interface up for vport %u: %d\n", - vport->vport_id, err); -- goto deinit_rss; -+ goto disable_vport; - } - - if (rtnl) -@@ -1546,8 +1568,6 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - - return 0; - --deinit_rss: -- idpf_deinit_rss(vport); - disable_vport: - idpf_send_disable_vport_msg(vport); - disable_queues: -@@ -2172,40 +2192,6 @@ static void idpf_set_rx_mode(struct net_device *netdev) - dev_err(dev, "Failed to set promiscuous mode: %d\n", err); - } - --/** -- * idpf_vport_manage_rss_lut - disable/enable RSS -- * @vport: the vport being changed -- * -- * In the event of disable request for RSS, this function will zero out RSS -- * LUT, while in the event of enable request for RSS, it will reconfigure RSS -- * LUT with the default LUT configuration. -- */ --static int idpf_vport_manage_rss_lut(struct idpf_vport *vport) --{ -- bool ena = idpf_is_feature_ena(vport, NETIF_F_RXHASH); -- struct idpf_rss_data *rss_data; -- u16 idx = vport->idx; -- int lut_size; -- -- rss_data = &vport->adapter->vport_config[idx]->user_config.rss_data; -- lut_size = rss_data->rss_lut_size * sizeof(u32); -- -- if (ena) { -- /* This will contain the default or user configured LUT */ -- memcpy(rss_data->rss_lut, rss_data->cached_lut, lut_size); -- } else { -- /* Save a copy of the current LUT to be restored later if -- * requested. -- */ -- memcpy(rss_data->cached_lut, rss_data->rss_lut, lut_size); -- -- /* Zero out the current LUT to disable */ -- memset(rss_data->rss_lut, 0, lut_size); -- } -- -- return idpf_config_rss(vport); --} -- - /** - * idpf_set_features - set the netdev feature flags - * @netdev: ptr to the netdev being adjusted -@@ -2231,10 +2217,19 @@ static int idpf_set_features(struct net_device *netdev, - } - - if (changed & NETIF_F_RXHASH) { -+ struct idpf_netdev_priv *np = netdev_priv(netdev); -+ - netdev->features ^= NETIF_F_RXHASH; -- err = idpf_vport_manage_rss_lut(vport); -- if (err) -- goto unlock_mutex; -+ -+ /* If the interface is not up when changing the rxhash, update -+ * to the HW is skipped. The updated LUT will be committed to -+ * the HW when the interface is brought up. -+ */ -+ if (np->state == __IDPF_VPORT_UP) { -+ err = idpf_config_rss(vport); -+ if (err) -+ goto unlock_mutex; -+ } - } - - if (changed & NETIF_F_GRO_HW) { -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -index a11ec2b8c5d1..1f61d2107e56 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -@@ -4232,57 +4232,47 @@ void idpf_fill_dflt_rss_lut(struct idpf_vport *vport) - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; - -- for (i = 0; i < rss_data->rss_lut_size; i++) { -+ for (i = 0; i < rss_data->rss_lut_size; i++) - rss_data->rss_lut[i] = i % num_active_rxq; -- rss_data->cached_lut[i] = rss_data->rss_lut[i]; -- } - } - - /** -- * idpf_init_rss - Allocate and initialize RSS resources -+ * idpf_init_rss_lut - Allocate and initialize RSS LUT - * @vport: virtual port - * -- * Return 0 on success, negative on failure -+ * Return: 0 on success, negative on failure - */ --int idpf_init_rss(struct idpf_vport *vport) -+int idpf_init_rss_lut(struct idpf_vport *vport) - { - struct idpf_adapter *adapter = vport->adapter; - struct idpf_rss_data *rss_data; -- u32 lut_size; - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -+ if (!rss_data->rss_lut) { -+ u32 lut_size; - -- lut_size = rss_data->rss_lut_size * sizeof(u32); -- rss_data->rss_lut = kzalloc(lut_size, GFP_KERNEL); -- if (!rss_data->rss_lut) -- return -ENOMEM; -- -- rss_data->cached_lut = kzalloc(lut_size, GFP_KERNEL); -- if (!rss_data->cached_lut) { -- kfree(rss_data->rss_lut); -- rss_data->rss_lut = NULL; -- -- return -ENOMEM; -+ lut_size = rss_data->rss_lut_size * sizeof(u32); -+ rss_data->rss_lut = kzalloc(lut_size, GFP_KERNEL); -+ if (!rss_data->rss_lut) -+ return -ENOMEM; - } - - /* Fill the default RSS lut values */ - idpf_fill_dflt_rss_lut(vport); - -- return idpf_config_rss(vport); -+ return 0; - } - - /** -- * idpf_deinit_rss - Release RSS resources -+ * idpf_deinit_rss_lut - Release RSS LUT - * @vport: virtual port - */ --void idpf_deinit_rss(struct idpf_vport *vport) -+void idpf_deinit_rss_lut(struct idpf_vport *vport) - { - struct idpf_adapter *adapter = vport->adapter; - struct idpf_rss_data *rss_data; - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -- kfree(rss_data->cached_lut); -- rss_data->cached_lut = NULL; - kfree(rss_data->rss_lut); - rss_data->rss_lut = NULL; - } -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -index a28cfb792bca..932209427df0 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -@@ -1010,8 +1010,8 @@ int idpf_vport_intr_init(struct idpf_vport *vport); - void idpf_vport_intr_ena(struct idpf_vport *vport); - void idpf_fill_dflt_rss_lut(struct idpf_vport *vport); - int idpf_config_rss(struct idpf_vport *vport); --int idpf_init_rss(struct idpf_vport *vport); --void idpf_deinit_rss(struct idpf_vport *vport); -+int idpf_init_rss_lut(struct idpf_vport *vport); -+void idpf_deinit_rss_lut(struct idpf_vport *vport); - int idpf_rx_bufs_init_all(struct idpf_vport *vport); - void idpf_rx_add_frag(struct idpf_rx_buf *rx_buf, struct sk_buff *skb, - unsigned int size); -diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -index c1f34381333d..9c7efee04b45 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -@@ -2476,6 +2476,10 @@ int idpf_send_get_stats_msg(struct idpf_vport *vport) - * @vport: virtual port data structure - * @get: flag to set or get rss look up table - * -+ * When rxhash is disabled, RSS LUT will be configured with zeros. If rxhash -+ * is enabled, the LUT values stored in driver's soft copy will be used to setup -+ * the HW. -+ * - * Returns 0 on success, negative on failure. - */ - int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get) -@@ -2486,10 +2490,12 @@ int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get) - struct idpf_rss_data *rss_data; - int buf_size, lut_buf_size; - ssize_t reply_sz; -+ bool rxhash_ena; - int i; - - rss_data = - &vport->adapter->vport_config[vport->idx]->user_config.rss_data; -+ rxhash_ena = idpf_is_feature_ena(vport, NETIF_F_RXHASH); - buf_size = struct_size(rl, lut, rss_data->rss_lut_size); - rl = kzalloc(buf_size, GFP_KERNEL); - if (!rl) -@@ -2511,7 +2517,8 @@ int idpf_send_get_set_rss_lut_msg(struct idpf_vport *vport, bool get) - } else { - rl->lut_entries = cpu_to_le16(rss_data->rss_lut_size); - for (i = 0; i < rss_data->rss_lut_size; i++) -- rl->lut[i] = cpu_to_le32(rss_data->rss_lut[i]); -+ rl->lut[i] = rxhash_ena ? -+ cpu_to_le32(rss_data->rss_lut[i]) : 0; - - xn_params.vc_op = VIRTCHNL2_OP_SET_RSS_LUT; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-22985-2.patch b/SPECS/kernel/CVE-2026-22985-2.patch deleted file mode 100644 index 2b11bea56..000000000 --- a/SPECS/kernel/CVE-2026-22985-2.patch +++ /dev/null @@ -1,269 +0,0 @@ -From e5d21c479823dd370a76d5bd95e0fe27c423d783 Mon Sep 17 00:00:00 2001 -From: Emil Tantilov -Date: Tue, 25 Nov 2025 14:36:24 -0800 -Subject: [PATCH 38/38] idpf: convert vport state to bitmap - -Convert vport state to a bitmap and remove the DOWN state which is -redundant in the existing logic. There are no functional changes aside -from the use of bitwise operations when setting and checking the states. -Removed the double underscore to be consistent with the naming of other -bitmaps in the header and renamed current_state to vport_is_up to match -the meaning of the new variable. - -Reviewed-by: Przemek Kitszel -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Chittim Madhu -Signed-off-by: Emil Tantilov -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen -Link: https://patch.msgid.link/20251125223632.1857532-6-anthony.l.nguyen@intel.com -Signed-off-by: Jakub Kicinski ---- - drivers/net/ethernet/intel/idpf/idpf.h | 12 ++++----- - .../net/ethernet/intel/idpf/idpf_ethtool.c | 10 ++++---- - drivers/net/ethernet/intel/idpf/idpf_lib.c | 25 ++++++++++--------- - .../ethernet/intel/idpf/idpf_singleq_txrx.c | 2 +- - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 2 +- - .../net/ethernet/intel/idpf/idpf_virtchnl.c | 4 +-- - 6 files changed, 27 insertions(+), 28 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h -index 4fcf2be878e5..49e0f5b49830 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf.h -+++ b/drivers/net/ethernet/intel/idpf/idpf.h -@@ -130,14 +130,12 @@ enum idpf_cap_field { - - /** - * enum idpf_vport_state - Current vport state -- * @__IDPF_VPORT_DOWN: Vport is down -- * @__IDPF_VPORT_UP: Vport is up -- * @__IDPF_VPORT_STATE_LAST: Must be last, number of states -+ * @IDPF_VPORT_UP: Vport is up -+ * @IDPF_VPORT_STATE_NBITS: Must be last, number of states - */ - enum idpf_vport_state { -- __IDPF_VPORT_DOWN, -- __IDPF_VPORT_UP, -- __IDPF_VPORT_STATE_LAST, -+ IDPF_VPORT_UP, -+ IDPF_VPORT_STATE_NBITS - }; - - /** -@@ -161,7 +159,7 @@ struct idpf_netdev_priv { - u16 vport_idx; - u16 max_tx_hdr_size; - u16 tx_max_bufs; -- enum idpf_vport_state state; -+ DECLARE_BITMAP(state, IDPF_VPORT_STATE_NBITS); - struct rtnl_link_stats64 netstats; - spinlock_t stats_lock; - }; -diff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c -index 0eb812ac19c2..f84e247399a7 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c -@@ -386,7 +386,7 @@ static int idpf_get_rxfh(struct net_device *netdev, - } - - rss_data = &adapter->vport_config[np->vport_idx]->user_config.rss_data; -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - rxfh->hfunc = ETH_RSS_HASH_TOP; -@@ -436,7 +436,7 @@ static int idpf_set_rxfh(struct net_device *netdev, - } - - rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data; -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE && -@@ -1167,7 +1167,7 @@ static void idpf_get_ethtool_stats(struct net_device *netdev, - idpf_vport_ctrl_lock(netdev); - vport = idpf_netdev_to_vport(netdev); - -- if (np->state != __IDPF_VPORT_UP) { -+ if (!test_bit(IDPF_VPORT_UP, np->state)) { - idpf_vport_ctrl_unlock(netdev); - - return; -@@ -1319,7 +1319,7 @@ static int idpf_get_q_coalesce(struct net_device *netdev, - idpf_vport_ctrl_lock(netdev); - vport = idpf_netdev_to_vport(netdev); - -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - if (q_num >= vport->num_rxq && q_num >= vport->num_txq) { -@@ -1507,7 +1507,7 @@ static int idpf_set_coalesce(struct net_device *netdev, - idpf_vport_ctrl_lock(netdev); - vport = idpf_netdev_to_vport(netdev); - -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - goto unlock_mutex; - - for (i = 0; i < vport->num_txq; i++) { -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index 2021dd6059af..f440446aa615 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -517,7 +517,7 @@ static int idpf_del_mac_filter(struct idpf_vport *vport, - } - spin_unlock_bh(&vport_config->mac_filter_list_lock); - -- if (np->state == __IDPF_VPORT_UP) { -+ if (test_bit(IDPF_VPORT_UP, np->state)) { - int err; - - err = idpf_add_del_mac_filters(vport, np, false, async); -@@ -588,7 +588,7 @@ static int idpf_add_mac_filter(struct idpf_vport *vport, - if (err) - return err; - -- if (np->state == __IDPF_VPORT_UP) -+ if (test_bit(IDPF_VPORT_UP, np->state)) - err = idpf_add_del_mac_filters(vport, np, true, async); - - return err; -@@ -949,7 +949,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); - -- if (np->state <= __IDPF_VPORT_DOWN) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - return; - - if (rtnl) -@@ -975,7 +975,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl) - idpf_vport_intr_deinit(vport); - idpf_vport_queues_rel(vport); - idpf_vport_intr_rel(vport); -- np->state = __IDPF_VPORT_DOWN; -+ clear_bit(IDPF_VPORT_UP, np->state); - - if (rtnl) - rtnl_unlock(); -@@ -1409,7 +1409,7 @@ static int idpf_up_complete(struct idpf_vport *vport) - netif_tx_start_all_queues(vport->netdev); - } - -- np->state = __IDPF_VPORT_UP; -+ set_bit(IDPF_VPORT_UP, np->state); - - return 0; - } -@@ -1452,9 +1452,11 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); - struct idpf_adapter *adapter = vport->adapter; -+ struct idpf_vport_config *vport_config; -+ struct idpf_rss_data *rss_data; - int err; - -- if (np->state != __IDPF_VPORT_DOWN) -+ if (test_bit(IDPF_VPORT_UP, np->state)) - return -EBUSY; - - if (rtnl) -@@ -1963,7 +1965,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - enum idpf_vport_reset_cause reset_cause) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); -- enum idpf_vport_state current_state = np->state; -+ bool vport_is_up = test_bit(IDPF_VPORT_UP, np->state); - struct idpf_adapter *adapter = vport->adapter; - struct idpf_vport *new_vport; - int err; -@@ -2014,7 +2016,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - goto free_vport; - } - -- if (current_state <= __IDPF_VPORT_DOWN) { -+ if (!vport_is_up) { - idpf_send_delete_queues_msg(vport); - } else { - set_bit(IDPF_VPORT_DEL_QUEUES, vport->flags); -@@ -2050,7 +2052,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - !netif_is_rxfh_configured(vport->netdev)) - idpf_fill_dflt_rss_lut(vport); - -- if (current_state == __IDPF_VPORT_UP) -+ if (vport_is_up) - err = idpf_vport_open(vport, false); - - goto free_vport; -@@ -2060,8 +2062,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - vport->num_rxq, vport->num_bufq); - - err_open: -- if (current_state == __IDPF_VPORT_UP) -- idpf_vport_open(vport, false); -+ if (vport_is_up) - - free_vport: - kfree(new_vport); -@@ -2225,7 +2226,7 @@ static int idpf_set_features(struct net_device *netdev, - * to the HW is skipped. The updated LUT will be committed to - * the HW when the interface is brought up. - */ -- if (np->state == __IDPF_VPORT_UP) { -+ if (test_bit(IDPF_VPORT_UP, np->state)) { - err = idpf_config_rss(vport); - if (err) - goto unlock_mutex; -diff --git a/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c -index b19b462e0bb6..06199fde3b57 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c -@@ -571,7 +571,7 @@ static bool idpf_tx_singleq_clean(struct idpf_tx_queue *tx_q, int napi_budget, - np = netdev_priv(tx_q->netdev); - nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx); - -- dont_wake = np->state != __IDPF_VPORT_UP || -+ dont_wake = !test_bit(IDPF_VPORT_UP, np->state) || - !netif_carrier_ok(tx_q->netdev); - __netif_txq_completed_wake(nq, ss.packets, ss.bytes, - IDPF_DESC_UNUSED(tx_q), IDPF_TX_WAKE_THRESH, -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -index 1f61d2107e56..50a6f59fb1ee 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -@@ -1889,7 +1889,7 @@ static bool idpf_tx_clean_complq(struct idpf_compl_queue *complq, int budget, - /* Update BQL */ - nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx); - -- dont_wake = !complq_ok || np->state != __IDPF_VPORT_UP || -+ dont_wake = !complq_ok || !test_bit(IDPF_VPORT_UP, np->state) || - !netif_carrier_ok(tx_q->netdev); - /* Check if the TXQ needs to and can be restarted */ - __netif_txq_completed_wake(nq, tx_q->cleaned_pkts, tx_q->cleaned_bytes, -diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -index 9c7efee04b45..96ee48860cbf 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c -@@ -68,7 +68,7 @@ static void idpf_handle_event_link(struct idpf_adapter *adapter, - - vport->link_up = v2e->link_status; - -- if (np->state != __IDPF_VPORT_UP) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - return; - - if (vport->link_up) { -@@ -2432,7 +2432,7 @@ int idpf_send_get_stats_msg(struct idpf_vport *vport) - - - /* Don't send get_stats message if the link is down */ -- if (np->state <= __IDPF_VPORT_DOWN) -+ if (!test_bit(IDPF_VPORT_UP, np->state)) - return 0; - - stats_msg.vport_id = cpu_to_le32(vport->vport_id); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-22987.patch b/SPECS/kernel/CVE-2026-22987.patch deleted file mode 100644 index c410d7e88..000000000 --- a/SPECS/kernel/CVE-2026-22987.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 4797daed31f7dc17a7e067572833f33b6c700b9e Mon Sep 17 00:00:00 2001 -From: Shivani Gupta -Date: Mon, 5 Jan 2026 00:59:05 +0000 -Subject: [PATCH 06/38] net/sched: act_api: avoid dereferencing ERR_PTR in - tcf_idrinfo_destroy - -syzbot reported a crash in tc_act_in_hw() during netns teardown where -tcf_idrinfo_destroy() passed an ERR_PTR(-EBUSY) value as a tc_action -pointer, leading to an invalid dereference. - -Guard against ERR_PTR entries when iterating the action IDR so teardown -does not call tc_act_in_hw() on an error pointer. - -Fixes: 84a7d6797e6a ("net/sched: acp_api: no longer acquire RTNL in tc_action_net_exit()") -Link: https://syzkaller.appspot.com/bug?extid=8f1c492ffa4644ff3826 -Reported-by: syzbot+8f1c492ffa4644ff3826@syzkaller.appspotmail.com -Closes: https://syzkaller.appspot.com/bug?extid=8f1c492ffa4644ff3826 -Signed-off-by: Shivani Gupta -Link: https://patch.msgid.link/20260105005905.243423-1-shivani07g@gmail.com -Signed-off-by: Jakub Kicinski ---- - net/sched/act_api.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/net/sched/act_api.c b/net/sched/act_api.c -index 9e468e463467..7fab81f223a5 100644 ---- a/net/sched/act_api.c -+++ b/net/sched/act_api.c -@@ -940,6 +940,8 @@ void tcf_idrinfo_destroy(const struct tc_action_ops *ops, - int ret; - - idr_for_each_entry_ul(idr, p, tmp, id) { -+ if (IS_ERR(p)) -+ continue; - if (tc_act_in_hw(p) && !mutex_taken) { - rtnl_lock(); - mutex_taken = true; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-22993.patch b/SPECS/kernel/CVE-2026-22993.patch deleted file mode 100644 index 1754ebda9..000000000 --- a/SPECS/kernel/CVE-2026-22993.patch +++ /dev/null @@ -1,102 +0,0 @@ -From a3a2302e6b913bf599866428c7ec7d7a5e38e32c Mon Sep 17 00:00:00 2001 -From: "LIOU, Mei Fan" -Date: Wed, 28 Jan 2026 07:47:07 -0800 -Subject: [PATCH 05/38] idpf: Fix RSS LUT NULL ptr issue after soft reset - -During soft reset, the RSS LUT is freed and not restored unless the -interface is up. If an ethtool command that accesses the rss lut is -attempted immediately after reset, it will result in NULL ptr -dereference. Also, there is no need to reset the rss lut if the soft reset -does not involve queue count change. - -After soft reset, set the RSS LUT to default values based on the updated -queue count only if the reset was a result of a queue count change and -the LUT was not configured by the user. In all other cases, don't touch -the LUT. - -Fixes: 02cbfba1add5 ("idpf: add ethtool callbacks") -Signed-off-by: Sreedevi Joshi -Reviewed-by: Aleksandr Loktionov -Reviewed-by: Sridhar Samudrala -Reviewed-by: Emil Tantilov -Reviewed-by: Simon Horman -Tested-by: Samuel Salin -Signed-off-by: Tony Nguyen ---- - drivers/net/ethernet/intel/idpf/idpf_lib.c | 12 +++++------- - drivers/net/ethernet/intel/idpf/idpf_txrx.c | 2 +- - drivers/net/ethernet/intel/idpf/idpf_txrx.h | 1 + - 3 files changed, 7 insertions(+), 8 deletions(-) - -diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c -index f4b89d222610..7e22790aa9a8 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_lib.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c -@@ -1383,7 +1383,6 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - { - struct idpf_netdev_priv *np = netdev_priv(vport->netdev); - struct idpf_adapter *adapter = vport->adapter; -- struct idpf_vport_config *vport_config; - int err; - - if (np->state != __IDPF_VPORT_DOWN) -@@ -1468,11 +1467,7 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl) - - idpf_restore_features(vport); - -- vport_config = adapter->vport_config[vport->idx]; -- if (vport_config->user_config.rss_data.rss_lut) -- err = idpf_config_rss(vport); -- else -- err = idpf_init_rss(vport); -+ err = idpf_config_rss(vport); - if (err) { - dev_err(&adapter->pdev->dev, "Failed to initialize RSS for vport %u: %d\n", - vport->vport_id, err); -@@ -1983,7 +1978,6 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - idpf_vport_stop(vport, false); - } - -- idpf_deinit_rss(vport); - /* We're passing in vport here because we need its wait_queue - * to send a message and it should be getting all the vport - * config data out of the adapter but we need to be careful not -@@ -2009,6 +2003,10 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport, - if (err) - goto err_open; - -+ if (reset_cause == IDPF_SR_Q_CHANGE && -+ !netif_is_rxfh_configured(vport->netdev)) -+ idpf_fill_dflt_rss_lut(vport); -+ - if (current_state == __IDPF_VPORT_UP) - err = idpf_vport_open(vport, false); - -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -index 92634c4bb369..a11ec2b8c5d1 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c -@@ -4223,7 +4223,7 @@ int idpf_config_rss(struct idpf_vport *vport) - * idpf_fill_dflt_rss_lut - Fill the indirection table with the default values - * @vport: virtual port structure - */ --static void idpf_fill_dflt_rss_lut(struct idpf_vport *vport) -+void idpf_fill_dflt_rss_lut(struct idpf_vport *vport) - { - struct idpf_adapter *adapter = vport->adapter; - u16 num_active_rxq = vport->num_rxq; -diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.h b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -index 52753dff381c..a28cfb792bca 100644 ---- a/drivers/net/ethernet/intel/idpf/idpf_txrx.h -+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.h -@@ -1008,6 +1008,7 @@ void idpf_vport_intr_update_itr_ena_irq(struct idpf_q_vector *q_vector); - void idpf_vport_intr_deinit(struct idpf_vport *vport); - int idpf_vport_intr_init(struct idpf_vport *vport); - void idpf_vport_intr_ena(struct idpf_vport *vport); -+void idpf_fill_dflt_rss_lut(struct idpf_vport *vport); - int idpf_config_rss(struct idpf_vport *vport); - int idpf_init_rss(struct idpf_vport *vport); - void idpf_deinit_rss(struct idpf_vport *vport); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-23004.patch b/SPECS/kernel/CVE-2026-23004.patch deleted file mode 100644 index 4e4a70f7e..000000000 --- a/SPECS/kernel/CVE-2026-23004.patch +++ /dev/null @@ -1,264 +0,0 @@ -From fe885b689ab38fd4005c572f48f9eafb82c9a689 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Mon, 12 Jan 2026 10:38:25 +0000 -Subject: [PATCH 35/38] dst: fix races in rt6_uncached_list_del() and - rt_del_uncached_list() - -syzbot was able to crash the kernel in rt6_uncached_list_flush_dev() -in an interesting way [1] - -Crash happens in list_del_init()/INIT_LIST_HEAD() while writing -list->prev, while the prior write on list->next went well. - -static inline void INIT_LIST_HEAD(struct list_head *list) -{ - WRITE_ONCE(list->next, list); // This went well - WRITE_ONCE(list->prev, list); // Crash, @list has been freed. -} - -Issue here is that rt6_uncached_list_del() did not attempt to lock -ul->lock, as list_empty(&rt->dst.rt_uncached) returned -true because the WRITE_ONCE(list->next, list) happened on the other CPU. - -We might use list_del_init_careful() and list_empty_careful(), -or make sure rt6_uncached_list_del() always grabs the spinlock -whenever rt->dst.rt_uncached_list has been set. - -A similar fix is neeed for IPv4. - -[1] - - BUG: KASAN: slab-use-after-free in INIT_LIST_HEAD include/linux/list.h:46 [inline] - BUG: KASAN: slab-use-after-free in list_del_init include/linux/list.h:296 [inline] - BUG: KASAN: slab-use-after-free in rt6_uncached_list_flush_dev net/ipv6/route.c:191 [inline] - BUG: KASAN: slab-use-after-free in rt6_disable_ip+0x633/0x730 net/ipv6/route.c:5020 -Write of size 8 at addr ffff8880294cfa78 by task kworker/u8:14/3450 - -CPU: 0 UID: 0 PID: 3450 Comm: kworker/u8:14 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)} -Tainted: [L]=SOFTLOCKUP -Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025 -Workqueue: netns cleanup_net -Call Trace: - - dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 - print_address_description mm/kasan/report.c:378 [inline] - print_report+0xca/0x240 mm/kasan/report.c:482 - kasan_report+0x118/0x150 mm/kasan/report.c:595 - INIT_LIST_HEAD include/linux/list.h:46 [inline] - list_del_init include/linux/list.h:296 [inline] - rt6_uncached_list_flush_dev net/ipv6/route.c:191 [inline] - rt6_disable_ip+0x633/0x730 net/ipv6/route.c:5020 - addrconf_ifdown+0x143/0x18a0 net/ipv6/addrconf.c:3853 - addrconf_notify+0x1bc/0x1050 net/ipv6/addrconf.c:-1 - notifier_call_chain+0x19d/0x3a0 kernel/notifier.c:85 - call_netdevice_notifiers_extack net/core/dev.c:2268 [inline] - call_netdevice_notifiers net/core/dev.c:2282 [inline] - netif_close_many+0x29c/0x410 net/core/dev.c:1785 - unregister_netdevice_many_notify+0xb50/0x2330 net/core/dev.c:12353 - ops_exit_rtnl_list net/core/net_namespace.c:187 [inline] - ops_undo_list+0x3dc/0x990 net/core/net_namespace.c:248 - cleanup_net+0x4de/0x7b0 net/core/net_namespace.c:696 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - - -Allocated by task 803: - kasan_save_stack mm/kasan/common.c:57 [inline] - kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 - unpoison_slab_object mm/kasan/common.c:340 [inline] - __kasan_slab_alloc+0x6c/0x80 mm/kasan/common.c:366 - kasan_slab_alloc include/linux/kasan.h:253 [inline] - slab_post_alloc_hook mm/slub.c:4953 [inline] - slab_alloc_node mm/slub.c:5263 [inline] - kmem_cache_alloc_noprof+0x18d/0x6c0 mm/slub.c:5270 - dst_alloc+0x105/0x170 net/core/dst.c:89 - ip6_dst_alloc net/ipv6/route.c:342 [inline] - icmp6_dst_alloc+0x75/0x460 net/ipv6/route.c:3333 - mld_sendpack+0x683/0xe60 net/ipv6/mcast.c:1844 - mld_send_cr net/ipv6/mcast.c:2154 [inline] - mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - -Freed by task 20: - kasan_save_stack mm/kasan/common.c:57 [inline] - kasan_save_track+0x3e/0x80 mm/kasan/common.c:78 - kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:584 - poison_slab_object mm/kasan/common.c:253 [inline] - __kasan_slab_free+0x5c/0x80 mm/kasan/common.c:285 - kasan_slab_free include/linux/kasan.h:235 [inline] - slab_free_hook mm/slub.c:2540 [inline] - slab_free mm/slub.c:6670 [inline] - kmem_cache_free+0x18f/0x8d0 mm/slub.c:6781 - dst_destroy+0x235/0x350 net/core/dst.c:121 - rcu_do_batch kernel/rcu/tree.c:2605 [inline] - rcu_core kernel/rcu/tree.c:2857 [inline] - rcu_cpu_kthread+0xba5/0x1af0 kernel/rcu/tree.c:2945 - smpboot_thread_fn+0x542/0xa60 kernel/smpboot.c:160 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - -Last potentially related work creation: - kasan_save_stack+0x3e/0x60 mm/kasan/common.c:57 - kasan_record_aux_stack+0xbd/0xd0 mm/kasan/generic.c:556 - __call_rcu_common kernel/rcu/tree.c:3119 [inline] - call_rcu+0xee/0x890 kernel/rcu/tree.c:3239 - refdst_drop include/net/dst.h:266 [inline] - skb_dst_drop include/net/dst.h:278 [inline] - skb_release_head_state+0x71/0x360 net/core/skbuff.c:1156 - skb_release_all net/core/skbuff.c:1180 [inline] - __kfree_skb net/core/skbuff.c:1196 [inline] - sk_skb_reason_drop+0xe9/0x170 net/core/skbuff.c:1234 - kfree_skb_reason include/linux/skbuff.h:1322 [inline] - tcf_kfree_skb_list include/net/sch_generic.h:1127 [inline] - __dev_xmit_skb net/core/dev.c:4260 [inline] - __dev_queue_xmit+0x26aa/0x3210 net/core/dev.c:4785 - NF_HOOK_COND include/linux/netfilter.h:307 [inline] - ip6_output+0x340/0x550 net/ipv6/ip6_output.c:247 - NF_HOOK+0x9e/0x380 include/linux/netfilter.h:318 - mld_sendpack+0x8d4/0xe60 net/ipv6/mcast.c:1855 - mld_send_cr net/ipv6/mcast.c:2154 [inline] - mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 - ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 - -The buggy address belongs to the object at ffff8880294cfa00 - which belongs to the cache ip6_dst_cache of size 232 -The buggy address is located 120 bytes inside of - freed 232-byte region [ffff8880294cfa00, ffff8880294cfae8) - -The buggy address belongs to the physical page: -page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x294cf -memcg:ffff88803536b781 -flags: 0x80000000000000(node=0|zone=1) -page_type: f5(slab) -raw: 0080000000000000 ffff88802ff1c8c0 ffffea0000bf2bc0 dead000000000006 -raw: 0000000000000000 00000000800c000c 00000000f5000000 ffff88803536b781 -page dumped because: kasan: bad access detected -page_owner tracks the page as allocated -page last allocated via order 0, migratetype Unmovable, gfp_mask 0x52820(GFP_ATOMIC|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP), pid 9, tgid 9 (kworker/0:0), ts 91119585830, free_ts 91088628818 - set_page_owner include/linux/page_owner.h:32 [inline] - post_alloc_hook+0x234/0x290 mm/page_alloc.c:1857 - prep_new_page mm/page_alloc.c:1865 [inline] - get_page_from_freelist+0x28c0/0x2960 mm/page_alloc.c:3915 - __alloc_frozen_pages_noprof+0x181/0x370 mm/page_alloc.c:5210 - alloc_pages_mpol+0xd1/0x380 mm/mempolicy.c:2486 - alloc_slab_page mm/slub.c:3075 [inline] - allocate_slab+0x86/0x3b0 mm/slub.c:3248 - new_slab mm/slub.c:3302 [inline] - ___slab_alloc+0xb10/0x13e0 mm/slub.c:4656 - __slab_alloc+0xc6/0x1f0 mm/slub.c:4779 - __slab_alloc_node mm/slub.c:4855 [inline] - slab_alloc_node mm/slub.c:5251 [inline] - kmem_cache_alloc_noprof+0x101/0x6c0 mm/slub.c:5270 - dst_alloc+0x105/0x170 net/core/dst.c:89 - ip6_dst_alloc net/ipv6/route.c:342 [inline] - icmp6_dst_alloc+0x75/0x460 net/ipv6/route.c:3333 - mld_sendpack+0x683/0xe60 net/ipv6/mcast.c:1844 - mld_send_cr net/ipv6/mcast.c:2154 [inline] - mld_ifc_work+0x83e/0xd60 net/ipv6/mcast.c:2693 - process_one_work kernel/workqueue.c:3257 [inline] - process_scheduled_works+0xad1/0x1770 kernel/workqueue.c:3340 - worker_thread+0x8a0/0xda0 kernel/workqueue.c:3421 - kthread+0x711/0x8a0 kernel/kthread.c:463 - ret_from_fork+0x510/0xa50 arch/x86/kernel/process.c:158 -page last free pid 5859 tgid 5859 stack trace: - reset_page_owner include/linux/page_owner.h:25 [inline] - free_pages_prepare mm/page_alloc.c:1406 [inline] - __free_frozen_pages+0xfe1/0x1170 mm/page_alloc.c:2943 - discard_slab mm/slub.c:3346 [inline] - __put_partials+0x149/0x170 mm/slub.c:3886 - __slab_free+0x2af/0x330 mm/slub.c:5952 - qlink_free mm/kasan/quarantine.c:163 [inline] - qlist_free_all+0x97/0x100 mm/kasan/quarantine.c:179 - kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286 - __kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:350 - kasan_slab_alloc include/linux/kasan.h:253 [inline] - slab_post_alloc_hook mm/slub.c:4953 [inline] - slab_alloc_node mm/slub.c:5263 [inline] - kmem_cache_alloc_noprof+0x18d/0x6c0 mm/slub.c:5270 - getname_flags+0xb8/0x540 fs/namei.c:146 - getname include/linux/fs.h:2498 [inline] - do_sys_openat2+0xbc/0x200 fs/open.c:1426 - do_sys_open fs/open.c:1436 [inline] - __do_sys_openat fs/open.c:1452 [inline] - __se_sys_openat fs/open.c:1447 [inline] - __x64_sys_openat+0x138/0x170 fs/open.c:1447 - do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] - do_syscall_64+0xec/0xf80 arch/x86/entry/syscall_64.c:94 - -Fixes: 8d0b94afdca8 ("ipv6: Keep track of DST_NOCACHE routes in case of iface down/unregister") -Fixes: 78df76a065ae ("ipv4: take rt_uncached_lock only if needed") -Reported-by: syzbot+179fc225724092b8b2b2@syzkaller.appspotmail.com -Closes: https://lore.kernel.org/netdev/6964cdf2.050a0220.eaf7.009d.GAE@google.com/T/#u -Signed-off-by: Eric Dumazet -Cc: Martin KaFai Lau -Reviewed-by: David Ahern -Link: https://patch.msgid.link/20260112103825.3810713-1-edumazet@google.com -Signed-off-by: Jakub Kicinski ---- - net/core/dst.c | 1 + - net/ipv4/route.c | 4 ++-- - net/ipv6/route.c | 4 ++-- - 3 files changed, 5 insertions(+), 4 deletions(-) - -diff --git a/net/core/dst.c b/net/core/dst.c -index e9d35f49c9e7..1dae26c51ebe 100644 ---- a/net/core/dst.c -+++ b/net/core/dst.c -@@ -68,6 +68,7 @@ void dst_init(struct dst_entry *dst, struct dst_ops *ops, - dst->lwtstate = NULL; - rcuref_init(&dst->__rcuref, 1); - INIT_LIST_HEAD(&dst->rt_uncached); -+ dst->rt_uncached_list = NULL; - dst->__use = 0; - dst->lastuse = jiffies; - dst->flags = flags; -diff --git a/net/ipv4/route.c b/net/ipv4/route.c -index 2a3ca3efce7f..78c3d330c2b8 100644 ---- a/net/ipv4/route.c -+++ b/net/ipv4/route.c -@@ -1536,9 +1536,9 @@ void rt_add_uncached_list(struct rtable *rt) - - void rt_del_uncached_list(struct rtable *rt) - { -- if (!list_empty(&rt->dst.rt_uncached)) { -- struct uncached_list *ul = rt->dst.rt_uncached_list; -+ struct uncached_list *ul = rt->dst.rt_uncached_list; - -+ if (ul) { - spin_lock_bh(&ul->lock); - list_del_init(&rt->dst.rt_uncached); - spin_unlock_bh(&ul->lock); -diff --git a/net/ipv6/route.c b/net/ipv6/route.c -index 3371f16b7a3e..0bf1070aa8ac 100644 ---- a/net/ipv6/route.c -+++ b/net/ipv6/route.c -@@ -148,9 +148,9 @@ void rt6_uncached_list_add(struct rt6_info *rt) - - void rt6_uncached_list_del(struct rt6_info *rt) - { -- if (!list_empty(&rt->dst.rt_uncached)) { -- struct uncached_list *ul = rt->dst.rt_uncached_list; -+ struct uncached_list *ul = rt->dst.rt_uncached_list; - -+ if (ul) { - spin_lock_bh(&ul->lock); - list_del_init(&rt->dst.rt_uncached); - spin_unlock_bh(&ul->lock); --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-23007.patch b/SPECS/kernel/CVE-2026-23007.patch deleted file mode 100644 index 018d276db..000000000 --- a/SPECS/kernel/CVE-2026-23007.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 9aa2e99ed029c40d4c313539dbea4216480d1fcc Mon Sep 17 00:00:00 2001 -From: Caleb Sander Mateos -Date: Thu, 8 Jan 2026 10:22:10 -0700 -Subject: [PATCH 01/38] block: zero non-PI portion of auto integrity buffer - -The auto-generated integrity buffer for writes needs to be fully -initialized before being passed to the underlying block device, -otherwise the uninitialized memory can be read back by userspace or -anyone with physical access to the storage device. If protection -information is generated, that portion of the integrity buffer is -already initialized. The integrity data is also zeroed if PI generation -is disabled via sysfs or the PI tuple size is 0. However, this misses -the case where PI is generated and the PI tuple size is nonzero, but the -metadata size is larger than the PI tuple. In this case, the remainder -("opaque") of the metadata is left uninitialized. -Generalize the BLK_INTEGRITY_CSUM_NONE check to cover any case when the -metadata is larger than just the PI tuple. - -Signed-off-by: Caleb Sander Mateos -Fixes: c546d6f43833 ("block: only zero non-PI metadata tuples in bio_integrity_prep") -Reviewed-by: Anuj Gupta -Reviewed-by: Christoph Hellwig -Reviewed-by: Martin K. Petersen -Signed-off-by: Jens Axboe ---- - block/bio-integrity-auto.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c -index 687952f63bbb..b8b7587be967 100644 ---- a/block/bio-integrity-auto.c -+++ b/block/bio-integrity-auto.c -@@ -142,7 +142,7 @@ bool bio_integrity_prep(struct bio *bio) - return true; - set_flags = false; - gfp |= __GFP_ZERO; -- } else if (bi->csum_type == BLK_INTEGRITY_CSUM_NONE) -+ } else if (bi->metadata_size > bi->pi_tuple_size) - gfp |= __GFP_ZERO; - break; - default: --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-23008.patch b/SPECS/kernel/CVE-2026-23008.patch deleted file mode 100644 index d50975e0d..000000000 --- a/SPECS/kernel/CVE-2026-23008.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 5c6fe5133c1b989f9bdd64e7327f830d585c9b01 Mon Sep 17 00:00:00 2001 -From: Ian Forbes -Date: Fri, 14 Nov 2025 14:37:03 -0600 -Subject: [PATCH 02/38] drm/vmwgfx: Fix KMS with 3D on HW version 10 - -HW version 10 does not have GB Surfaces so there is no backing buffer for -surface backed FBs. This would result in a nullptr dereference and crash -the driver causing a black screen. - -Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") -Signed-off-by: Ian Forbes -Reviewed-by: Zack Rusin -Signed-off-by: Zack Rusin -Link: https://patch.msgid.link/20251114203703.1946616-1-ian.forbes@broadcom.com ---- - drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c -index 54ea1b513950..535d844191e7 100644 ---- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c -+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c -@@ -763,13 +763,15 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, - return ERR_PTR(ret); - } - -- ttm_bo_reserve(&bo->tbo, false, false, NULL); -- ret = vmw_bo_dirty_add(bo); -- if (!ret && surface && surface->res.func->dirty_alloc) { -- surface->res.coherent = true; -- ret = surface->res.func->dirty_alloc(&surface->res); -+ if (bo) { -+ ttm_bo_reserve(&bo->tbo, false, false, NULL); -+ ret = vmw_bo_dirty_add(bo); -+ if (!ret && surface && surface->res.func->dirty_alloc) { -+ surface->res.coherent = true; -+ ret = surface->res.func->dirty_alloc(&surface->res); -+ } -+ ttm_bo_unreserve(&bo->tbo); - } -- ttm_bo_unreserve(&bo->tbo); - - return &vfb->base; - } --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-23009.patch b/SPECS/kernel/CVE-2026-23009.patch deleted file mode 100644 index 7f6486dad..000000000 --- a/SPECS/kernel/CVE-2026-23009.patch +++ /dev/null @@ -1,95 +0,0 @@ -From e2b288c090387cfd3a7d27054d0b43e4c78206b2 Mon Sep 17 00:00:00 2001 -From: Mathias Nyman -Date: Fri, 16 Jan 2026 01:37:58 +0200 -Subject: [PATCH 03/38] xhci: sideband: don't dereference freed ring when - removing sideband endpoint -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -xhci_sideband_remove_endpoint() incorrecly assumes that the endpoint is -running and has a valid transfer ring. - -Lianqin reported a crash during suspend/wake-up stress testing, and -found the cause to be dereferencing a non-existing transfer ring -'ep->ring' during xhci_sideband_remove_endpoint(). - -The endpoint and its ring may be in unknown state if this function -is called after xHCI was reinitialized in resume (lost power), or if -device is being re-enumerated, disconnected or endpoint already dropped. - -Fix this by both removing unnecessary ring access, and by checking -ep->ring exists before dereferencing it. Also make sure endpoint is -running before attempting to stop it. - -Remove the xhci_initialize_ring_info() call during sideband endpoint -removal as is it only initializes ring structure enqueue, dequeue and -cycle state values to their starting values without changing actual -hardware enqueue, dequeue and cycle state. Leaving them out of sync -is worse than leaving it as it is. The endpoint will get freed in after -this in most usecases. - -If the (audio) class driver want's to reuse the endpoint after offload -then it is up to the class driver to ensure endpoint is properly set up. - -Reported-by: 胡连勤 -Closes: https://lore.kernel.org/linux-usb/TYUPR06MB6217B105B059A7730C4F6EC8D2B9A@TYUPR06MB6217.apcprd06.prod.outlook.com/ -Tested-by: 胡连勤 -Fixes: de66754e9f80 ("xhci: sideband: add initial api to register a secondary interrupter entity") -Cc: stable@vger.kernel.org -Signed-off-by: Mathias Nyman -Link: https://patch.msgid.link/20260115233758.364097-2-mathias.nyman@linux.intel.com -Signed-off-by: Greg Kroah-Hartman ---- - drivers/usb/host/xhci-sideband.c | 1 - - drivers/usb/host/xhci.c | 15 ++++++++++++--- - 2 files changed, 12 insertions(+), 4 deletions(-) - -diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c -index d49f9886dd84..254ce6d168a5 100644 ---- a/drivers/usb/host/xhci-sideband.c -+++ b/drivers/usb/host/xhci-sideband.c -@@ -190,7 +190,6 @@ xhci_sideband_remove_endpoint(struct xhci_sideband *sb, - } - - __xhci_sideband_remove_endpoint(sb, ep); -- xhci_initialize_ring_info(ep->ring); - mutex_unlock(&sb->mutex); - - return 0; -diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c -index 25cd778d3fbd..d2bda5c17a04 100644 ---- a/drivers/usb/host/xhci.c -+++ b/drivers/usb/host/xhci.c -@@ -2891,16 +2891,25 @@ int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int - gfp_t gfp_flags) - { - struct xhci_command *command; -+ struct xhci_ep_ctx *ep_ctx; - unsigned long flags; -- int ret; -+ int ret = -ENODEV; - - command = xhci_alloc_command(xhci, true, gfp_flags); - if (!command) - return -ENOMEM; - - spin_lock_irqsave(&xhci->lock, flags); -- ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id, -- ep->ep_index, suspend); -+ -+ /* make sure endpoint exists and is running before stopping it */ -+ if (ep->ring) { -+ ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); -+ if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_RUNNING) -+ ret = xhci_queue_stop_endpoint(xhci, command, -+ ep->vdev->slot_id, -+ ep->ep_index, suspend); -+ } -+ - if (ret < 0) { - spin_unlock_irqrestore(&xhci->lock, flags); - goto out; --- -2.43.0 - diff --git a/SPECS/kernel/CVE-2026-23012.patch b/SPECS/kernel/CVE-2026-23012.patch deleted file mode 100644 index 44f3e0396..000000000 --- a/SPECS/kernel/CVE-2026-23012.patch +++ /dev/null @@ -1,105 +0,0 @@ -From ea7b0f04dc71f6fe41ab74a0f9ec13b081366745 Mon Sep 17 00:00:00 2001 -From: SeongJae Park -Date: Tue, 30 Dec 2025 17:23:13 -0800 -Subject: [PATCH 04/38] mm/damon/core: remove call_control in inactive contexts - -If damon_call() is executed against a DAMON context that is not running, -the function returns error while keeping the damon_call_control object -linked to the context's call_controls list. Let's suppose the object is -deallocated after the damon_call(), and yet another damon_call() is -executed against the same context. The function tries to add the new -damon_call_control object to the call_controls list, which still has the -pointer to the previous damon_call_control object, which is deallocated. -As a result, use-after-free happens. - -This can actually be triggered using the DAMON sysfs interface. It is not -easily exploitable since it requires the sysfs write permission and making -a definitely weird file writes, though. Please refer to the report for -more details about the issue reproduction steps. - -Fix the issue by making two changes. Firstly, move the final -kdamond_call() for cancelling all existing damon_call() requests from -terminating DAMON context to be done before the ctx->kdamond reset. This -makes any code that sees NULL ctx->kdamond can safely assume the context -may not access damon_call() requests anymore. Secondly, let damon_call() -to cleanup the damon_call_control objects that were added to the -already-terminated DAMON context, before returning the error. - -Link: https://lkml.kernel.org/r/20251231012315.75835-1-sj@kernel.org -Fixes: 004ded6bee11 ("mm/damon: accept parallel damon_call() requests") -Signed-off-by: SeongJae Park -Reported-by: JaeJoon Jung -Closes: https://lore.kernel.org/20251224094401.20384-1-rgbi3307@gmail.com -Cc: # 6.17.x -Signed-off-by: Andrew Morton ---- - mm/damon/core.c | 33 +++++++++++++++++++++++++++++++-- - 1 file changed, 31 insertions(+), 2 deletions(-) - -diff --git a/mm/damon/core.c b/mm/damon/core.c -index 533c1c2d72f2..7dddb82b0b9a 100644 ---- a/mm/damon/core.c -+++ b/mm/damon/core.c -@@ -1398,6 +1398,35 @@ bool damon_is_running(struct damon_ctx *ctx) - return running; - } - -+/* -+ * damon_call_handle_inactive_ctx() - handle DAMON call request that added to -+ * an inactive context. -+ * @ctx: The inactive DAMON context. -+ * @control: Control variable of the call request. -+ * -+ * This function is called in a case that @control is added to @ctx but @ctx is -+ * not running (inactive). See if @ctx handled @control or not, and cleanup -+ * @control if it was not handled. -+ * -+ * Returns 0 if @control was handled by @ctx, negative error code otherwise. -+ */ -+static int damon_call_handle_inactive_ctx( -+ struct damon_ctx *ctx, struct damon_call_control *control) -+{ -+ struct damon_call_control *c; -+ -+ mutex_lock(&ctx->call_controls_lock); -+ list_for_each_entry(c, &ctx->call_controls, list) { -+ if (c == control) { -+ list_del(&control->list); -+ mutex_unlock(&ctx->call_controls_lock); -+ return -EINVAL; -+ } -+ } -+ mutex_unlock(&ctx->call_controls_lock); -+ return 0; -+} -+ - /** - * damon_call() - Invoke a given function on DAMON worker thread (kdamond). - * @ctx: DAMON context to call the function for. -@@ -1428,7 +1457,7 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control) - list_add_tail(&control->list, &ctx->call_controls); - mutex_unlock(&ctx->call_controls_lock); - if (!damon_is_running(ctx)) -- return -EINVAL; -+ return damon_call_handle_inactive_ctx(ctx, control); - if (control->repeat) - return 0; - wait_for_completion(&control->completion); -@@ -2669,13 +2698,13 @@ static int kdamond_fn(void *data) - if (ctx->ops.cleanup) - ctx->ops.cleanup(ctx); - kfree(ctx->regions_score_histogram); -+ kdamond_call(ctx, true); - - pr_debug("kdamond (%d) finishes\n", current->pid); - mutex_lock(&ctx->kdamond_lock); - ctx->kdamond = NULL; - mutex_unlock(&ctx->kdamond_lock); - -- kdamond_call(ctx, true); - damos_walk_cancel(ctx); - - mutex_lock(&damon_lock); --- -2.43.0 - diff --git a/SPECS/kernel/config b/SPECS/kernel/config index 9d6858240..7c3193f2f 100644 --- a/SPECS/kernel/config +++ b/SPECS/kernel/config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 6.17.11 Kernel Configuration +# Linux/x86_64 6.18.15 Kernel Configuration # CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.2.0" CONFIG_CC_IS_GCC=y @@ -620,6 +620,7 @@ CONFIG_ACPI_PCC=y # CONFIG_ACPI_FFH is not set CONFIG_PMIC_OPREGION=y CONFIG_TPS68470_PMIC_OPREGION=y +CONFIG_BXT_WC_PMIC_OPREGION=y CONFIG_ACPI_PRMT=y CONFIG_X86_PM_TIMER=y @@ -2119,7 +2120,7 @@ CONFIG_BLK_DEV_RAM_SIZE=4096 # CONFIG_ATA_OVER_ETH is not set CONFIG_XEN_BLKDEV_FRONTEND=m # CONFIG_XEN_BLKDEV_BACKEND is not set -CONFIG_VIRTIO_BLK=m +CONFIG_VIRTIO_BLK=y CONFIG_BLK_DEV_RBD=m # CONFIG_BLK_DEV_UBLK is not set @@ -4020,7 +4021,7 @@ CONFIG_LPC_SCH=m CONFIG_MFD_INTEL_LPSS=m CONFIG_MFD_INTEL_LPSS_ACPI=m CONFIG_MFD_INTEL_LPSS_PCI=m -# CONFIG_MFD_INTEL_PMC_BXT is not set +CONFIG_MFD_INTEL_PMC_BXT=m # CONFIG_MFD_IQS62X is not set # CONFIG_MFD_JANZ_CMODIO is not set # CONFIG_MFD_KEMPLD is not set @@ -4080,6 +4081,7 @@ CONFIG_MFD_INTEL_LPSS_PCI=m # CONFIG_MFD_WM8994 is not set # CONFIG_MFD_ATC260X_I2C is not set # CONFIG_RAVE_SP_CORE is not set +CONFIG_INTEL_SOC_PMIC_BXTWC=m # end of Multifunction device drivers CONFIG_REGULATOR=y @@ -5937,11 +5939,24 @@ CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_GADGET=m CONFIG_TYPEC=m +CONFIG_TYPEC_TCPM=m +CONFIG_TYPEC_TCPCI=m +CONFIG_TYPEC_WCOVE=m CONFIG_TYPEC_UCSI=m CONFIG_UCSI_ACPI=m # CONFIG_USB_ROLE_SWITCH is not set CONFIG_USB_MASS_STORAGE=m CONFIG_USB_LIBCOMPOSITE=m + +# +# USB Type-C Multiplexer/DeMultiplexer Switch support +# +CONFIG_TYPEC_MUX_INTEL_PMC=m +# +# USB Type-C Alternate Mode drivers +# +CONFIG_TYPEC_DP_ALTMODE=m + CONFIG_MMC=m CONFIG_MMC_BLOCK=m CONFIG_MMC_BLOCK_MINORS=16 @@ -7651,6 +7666,9 @@ CONFIG_CC_HAS_AUTO_VAR_INIT_ZERO=y CONFIG_INIT_STACK_NONE=y # CONFIG_INIT_STACK_ALL_PATTERN is not set CONFIG_INIT_STACK_ALL_ZERO=y +CONFIG_KSTACK_ERASE=y +CONFIG_KSTACK_ERASE_METRICS=y +CONFIG_KSTACK_ERASE_RUNTIME_DISABLE=y CONFIG_GCC_PLUGIN_STACKLEAK=y # CONFIG_STACKLEAK_RUNTIME_DISABLE is not set CONFIG_INIT_ON_ALLOC_DEFAULT_ON=y diff --git a/SPECS/kernel/kernel-uki.spec b/SPECS/kernel/kernel-uki.spec index 441d2ae40..1299f23fb 100644 --- a/SPECS/kernel/kernel-uki.spec +++ b/SPECS/kernel/kernel-uki.spec @@ -12,8 +12,8 @@ Summary: Unified Kernel Image Name: kernel-uki -Version: 6.17.11 -Release: 2%{?dist} +Version: 6.18.15 +Release: 1%{?dist} License: GPLv2 Vendor: Intel Corporation Distribution: Edge Microvisor Toolkit @@ -70,6 +70,10 @@ cp %{buildroot}/boot/vmlinuz-uki-%{kernelver}.efi %{buildroot}/boot/efi/EFI/Linu /boot/efi/EFI/Linux/vmlinuz-uki-%{kernelver}.efi %changelog +* Mon Mar 16 2026 Lishan Liu - 6.18.15-1 +- Update kernel to 6.18.15-1 +- lts-v6.18.15-emt-260310T050801Z + * Tue Feb 03 2026 Lishan Liu - 6.17.11-2 - Update kernel to 6.17.11-2 - mainline-v6.17.11-emt-overlay-cve-260128T080735Z diff --git a/SPECS/kernel/kernel.signatures.json b/SPECS/kernel/kernel.signatures.json index ff4de26a9..560395bbb 100644 --- a/SPECS/kernel/kernel.signatures.json +++ b/SPECS/kernel/kernel.signatures.json @@ -1,10 +1,10 @@ { "Signatures": { "emt-ca-20211013.pem": "5ef124b0924cb1047c111a0ecff1ae11e6ad7cac8d1d9b40f98f99334121f0b0", - "config": "a7c578e33d0729019d0be7f90bf3d9d8af2d0051800800ed92347cc823da4b94", + "config": "cd561a89609f7c3bbeba426827d53fc947e961fc572fa81659698b84a2b3f031", "cpupower": "d7518767bf2b1110d146a49c7d42e76b803f45eb8bd14d931aa6d0d346fae985", "cpupower.service": "b057fe9e5d0e8c36f485818286b80e3eba8ff66ff44797940e99b1fd5361bb98", "sha512hmac-openssl.sh": "02ab91329c4be09ee66d759e4d23ac875037c3b56e5a598e32fd1206da06a27f", - "linux-6.17.11.tar.gz": "820dd3cacc1d853becb9d1051c4ba5e75442633378a63c244fdf179d0b28f4ac" + "linux-6.18.15.tar.gz": "9d18995c14c96a269e18777be65e8d7712c47f56f9709bbceca846aae58c7fe6" } } diff --git a/SPECS/kernel/kernel.spec b/SPECS/kernel/kernel.spec index c467d1912..8cbb3e1cf 100644 --- a/SPECS/kernel/kernel.spec +++ b/SPECS/kernel/kernel.spec @@ -1,572 +1,205 @@ Summary: Linux Kernel Name: kernel -Version: 6.17.11 -Release: 2%{?dist} +Version: 6.18.15 +Release: 1%{?dist} License: GPLv2 Vendor: Intel Corporation Distribution: Edge Microvisor Toolkit Group: System Environment/Kernel URL: https://www.kernel.org/pub/linux/kernel -Source0: https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.17.11.tar.gz +Source0: https://www.kernel.org/pub/linux/kernel/v6.x/linux-6.18.15.tar.gz Source1: config Source3: sha512hmac-openssl.sh Source4: emt-ca-20211013.pem Source5: cpupower Source6: cpupower.service -# Intel not-upstreamed kernel features -# v6.17.11 -#5439375ca698 Linux 6.17.11 +# Intel Kernel Patches +# Series file for v6.18.15 linux kernel +# df0dc1b06fb6b Linux 6.18.15 # security Patch01001: 0001-Add-security.md-file.security -Patch01002: 0001-issei-initial-driver-skeleton.security -Patch01003: 0002-issei-add-firmware-and-host-clients-implementatio.security -Patch01004: 0003-issei-implement-main-thread-and-ham-messages.security -Patch01005: 0004-issei-add-heci-hardware-module.security -Patch01006: 0005-issei-update-MAINTAINERS-file.security -Patch01007: 0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security -# lpss -Patch02001: 0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss -Patch02002: 0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss -Patch02003: 0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss -Patch02004: 0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss -Patch02005: 0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss -Patch02006: 0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss -Patch02007: 0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss -Patch02008: 0008-spi-intel-Add-support-for-128M-component-density.lpss -Patch02009: 0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss -Patch02010: 0011-i2c-designware-Preliminary-SMBus-support.lpss -Patch02011: 0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss +Patch01002: 0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security +Patch01003: 0001-mei-bus-fix-device-leak.security +Patch01004: 0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security +Patch01005: 0003-mei-expose-device-kind-for-ioe-device.security +Patch01006: 0004-mei-virtio-virtualization-frontend-driver.security +Patch01007: 0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security +Patch01008: 0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security +Patch01009: 0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security +Patch01010: 0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security +Patch01011: 0001-issei-initial-driver-skeleton.security +Patch01012: 0002-issei-add-firmware-and-host-clients-implementatio.security +Patch01013: 0003-issei-implement-main-thread-and-ham-messages.security +Patch01014: 0004-issei-add-heci-hardware-module.security +Patch01015: 0005-issei-update-MAINTAINERS-file.security +Patch01016: 0006-issei-host_client-add-dma-allocation-support.security +Patch01017: 0007-issei-add-driver-to-driver-interface.security +# preempt-rt +Patch02001: 0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt +Patch02002: 0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt +Patch02003: 0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt +Patch02004: 0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt +Patch02005: 0005-drm-i915-Drop-the-irqs_disabled-check.rt +Patch02006: 0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt +Patch02007: 0007-drm-i915-Consider-RCU-read-section-as-atomic.rt +Patch02008: 0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt +Patch02009: 0009-sysfs-Add-sys-kernel-realtime-entry.rt +# rapl +Patch03001: 0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl +Patch03002: 0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl +Patch03003: 0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl +Patch03004: 0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl +Patch03005: 0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl +Patch03006: 0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl +Patch03007: 0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl +Patch03008: 0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl +Patch03009: 0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl +Patch03010: 0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl +Patch03011: 0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl +Patch03012: 0016-cpuidle-Update-header-inclusion.rapl +Patch03013: 0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl +# turbo +Patch04001: 0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo +Patch04002: 0003-tools-power-turbostat-Refactor-added-column-header-p.turbo +Patch04003: 0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo +Patch04004: 0005-tools-power-turbostat.8-Update-example.turbo +Patch04005: 0006-tools-power-turbostat-Refactor-floating-point-printo.turbo +Patch04006: 0007-tools-power-turbostat-Remove-dead-code.turbo +Patch04007: 0008-tools-power-turbostat-Add-LLC-stats.turbo +Patch04008: 0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo +Patch04009: 0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo +Patch04010: 0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo +Patch04011: 0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo +Patch04012: 0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo +Patch04013: 0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo +Patch04014: 0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo +Patch04015: 0016-tools-power-turbostat-Enhance-perf-probe.turbo +Patch04016: 0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo +Patch04017: 0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo +Patch04018: 0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo +Patch04019: 0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo +Patch04020: 0021-tools-power-turbostat-version-2025.12.02.turbo +# ethernet +Patch05001: 0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet +Patch05002: 0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet +Patch05003: 0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet +Patch05004: 0002-bpf-add-btf-register-unregister-API.ethernet +Patch05005: 0003-net-core-XDP-metadata-BTF-netlink-API.ethernet +Patch05006: 0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet +Patch05007: 0005-rtnetlink-Add-return-value-check.ethernet +Patch05008: 0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet +Patch05009: 0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet +Patch05010: 0008-igc-Add-BTF-based-metadata-for-XDP.ethernet +Patch05011: 0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet +Patch05012: 0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet +Patch05013: 0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet +Patch05014: 0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet +Patch05015: 0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet +Patch05016: 0014-igc-Remove-XDP-metadata-invalidation.ethernet +Patch05017: 0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet +Patch05018: 0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet +Patch05019: 0003-net-phy-increase-gpy-loopback-test-delay.ethernet +Patch05020: 0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet +Patch05021: 0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet +Patch05022: 0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet +Patch05023: 0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet +Patch05024: 0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet +Patch05025: 0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet +Patch05026: 0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet +Patch05027: 0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet +Patch05028: 0012-net-phylink-Add-module_exit.ethernet +Patch05029: 0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet +Patch05030: 0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet +Patch05031: 0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet +Patch05032: 0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet +Patch05033: 0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet +Patch05034: 0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet +# nmi +Patch06001: 0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi +Patch06002: 0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi +Patch06003: 0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi +Patch06004: 0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi +Patch06005: 0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi +Patch06006: 0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi +Patch06007: 0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi +Patch06008: 0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi +Patch06009: 0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi +Patch06010: 0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi +Patch06011: 0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi +Patch06012: 0012-KVM-VMX-Virtualize-FRED-event_data.nmi +Patch06013: 0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi +Patch06014: 0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi +Patch06015: 0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi +Patch06016: 0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi +Patch06017: 0017-KVM-x86-Advertise-support-for-FRED.nmi +Patch06018: 0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi +Patch06019: 0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi +Patch06020: 0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi +Patch06021: 0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi +Patch06022: 0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi +Patch06023: 0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi +Patch06024: 0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi +Patch06025: 0025-KVM-selftests-Add-fred-exception-tests.nmi +Patch06026: 0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi +Patch06027: 0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi +Patch06028: 0028-x86-fred-Allow-variable-sized-event-frame.nmi +Patch06029: 0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi +Patch06030: 0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi +Patch06031: 0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi +Patch06032: 0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi +Patch06033: 0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi +Patch06034: 0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi +Patch06035: 0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi +Patch06036: 0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi +Patch06037: 0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi +Patch06038: 0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi +Patch06039: 0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi +Patch06040: 0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi +Patch06041: 0041-KVM-VMX-Implement-NMI-source-injection.nmi +Patch06042: 0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi +Patch06043: 0043-x86-fred-Enable-FRED-by-default.nmi +Patch06044: 0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi # drm -Patch03001: 0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm -Patch03002: 0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm -Patch03003: 0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm -Patch03004: 0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm -# sriov -Patch04001: 0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov -Patch04002: 0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov -# edac -Patch05001: 0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac -Patch05002: 0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac -Patch05003: 0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac -Patch05004: 0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac -Patch05005: 0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac -Patch05006: 0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac -Patch05007: 0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac -Patch05008: 0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac -Patch05009: 0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac -Patch05010: 0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac -Patch05011: 0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac -Patch05012: 0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +Patch07001: 0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm +Patch07002: 0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm +Patch07003: 0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm +Patch07004: 0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm +Patch07005: 0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm +Patch07006: 0001-i915-gt-GuC-for-legacy-platform.drm +Patch07007: 0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm +# edcac +Patch08001: 0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac +Patch08002: 0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +Patch08003: 0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac # perf -Patch06001: 0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf -Patch06002: 0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf -Patch06003: 0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf -Patch06004: 0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf -Patch06005: 0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf -Patch06006: 0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf -Patch06007: 0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf -Patch06008: 0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf -Patch06009: 0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf -Patch06010: 0013-perf-x86-intel-Initialize-architectural-PEBS.perf -Patch06011: 0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf -Patch06012: 0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf -Patch06013: 0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf -Patch06014: 0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf -Patch06015: 0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf -Patch06016: 0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf -Patch06017: 0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf -Patch06018: 0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf -Patch06019: 0022-perf-core-Support-to-capture-higher-width-vector-regi.perf -Patch06020: 0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf -Patch06021: 0024-perf-tools-Support-to-show-SSP-register.perf -Patch06022: 0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf -Patch06023: 0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf -Patch06024: 0027-perf-tools-Support-to-capture-more-vector-registers-x.perf -Patch06025: 0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf -Patch06026: 0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf -Patch06027: 0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf -Patch06028: 0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf -Patch06029: 0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf -Patch06030: 0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf -Patch06031: 0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf -Patch06032: 0036-KVM-selftests-Relax-precise-event-count-validation-as.perf -Patch06033: 0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf -Patch06034: 0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf -Patch06035: 0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf -Patch06036: 0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf -Patch06037: 0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf -Patch06038: 0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf -Patch06039: 0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf -Patch06040: 0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf -Patch06041: 0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf -Patch06042: 0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf -Patch06043: 0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf -Patch06044: 0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf -Patch06045: 0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf -Patch06046: 0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf -Patch06047: 0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf -Patch06048: 0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf -Patch06049: 0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf -Patch06050: 0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf -Patch06051: 0056-perf-Skip-pmu_ctx-based-on-event_type.perf -Patch06052: 0057-perf-Add-generic-exclude_guest-support.perf -Patch06053: 0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf -Patch06054: 0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf -Patch06055: 0060-perf-Clean-up-perf-ctx-time.perf -Patch06056: 0061-perf-Add-a-EVENT_GUEST-flag.perf -Patch06057: 0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf -Patch06058: 0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf -Patch06059: 0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf -Patch06060: 0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf -Patch06061: 0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf -Patch06062: 0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf -Patch06063: 0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf -Patch06064: 0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf -Patch06065: 0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf -Patch06066: 0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf -Patch06067: 0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf -Patch06068: 0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf -Patch06069: 0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf -Patch06070: 0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf -Patch06071: 0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf -Patch06072: 0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf -Patch06073: 0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf -Patch06074: 0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf -Patch06075: 0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf -Patch06076: 0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf -Patch06077: 0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf -Patch06078: 0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf -Patch06079: 0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf -Patch06080: 0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf -Patch06081: 0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf -Patch06082: 0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf -Patch06083: 0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf -Patch06084: 0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf -Patch06085: 0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf -Patch06086: 0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf -Patch06087: 0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf -Patch06088: 0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf -Patch06089: 0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf -Patch06090: 0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf -Patch06091: 0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf -Patch06092: 0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf -Patch06093: 0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf -Patch06094: 0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf -Patch06095: 0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf -# cet -Patch07001: 0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet -Patch07002: 0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet -Patch07003: 0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet -Patch07004: 0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet -Patch07005: 0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet -Patch07006: 0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet -Patch07007: 0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet -Patch07008: 0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet -Patch07009: 0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet -Patch07010: 0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet -Patch07011: 0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet -Patch07012: 0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet -Patch07013: 0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet -Patch07014: 0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet -Patch07015: 0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet -Patch07016: 0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet -Patch07017: 0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet -Patch07018: 0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet -Patch07019: 0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet -Patch07020: 0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet -Patch07021: 0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet -Patch07022: 0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet -Patch07023: 0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet -Patch07024: 0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet -# nmi -Patch08001: 0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi -Patch08002: 0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi -Patch08003: 0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi -Patch08004: 0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi -Patch08005: 0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi -Patch08006: 0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi -Patch08007: 0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi -Patch08008: 0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi -Patch08009: 0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi -Patch08010: 0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi -Patch08011: 0011-KVM-VMX-Virtualize-FRED-event_data.nmi -Patch08012: 0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi -Patch08013: 0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi -Patch08014: 0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi -Patch08015: 0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi -Patch08016: 0016-KVM-x86-Advertise-support-for-FRED.nmi -Patch08017: 0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi -Patch08018: 0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi -Patch08019: 0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi -Patch08020: 0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi -Patch08021: 0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi -Patch08022: 0022-x86-fred-Enable-FRED-by-default.nmi -Patch08023: 0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi -Patch08024: 0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi -Patch08025: 0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi -Patch08026: 0026-KVM-selftests-Add-fred-exception-tests.nmi -Patch08027: 0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi -Patch08028: 0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi -Patch08029: 0029-x86-fred-Allow-variable-sized-event-frame.nmi -Patch08030: 0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi -Patch08031: 0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi -Patch08032: 0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi -Patch08033: 0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi -Patch08034: 0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi -Patch08035: 0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi -Patch08036: 0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi -Patch08037: 0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi -Patch08038: 0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi -Patch08039: 0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi -Patch08040: 0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi -Patch08041: 0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi -Patch08042: 0042-KVM-VMX-Implement-NMI-source-injection.nmi -Patch08043: 0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi -Patch08044: 0044-EDAC-ieh-Fix-a-compile-error.nmi -Patch08045: 0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi -# ipu -Patch09001: 0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu -Patch09002: 0002-INT3472-Support-LT6911GXD.ipu -Patch09003: 0003-media-i2c-add-support-for-lt6911gxd.ipu -Patch09004: 0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu -Patch09005: 0005-ipu-bridge-add-CPHY-support.ipu -Patch09006: 0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -Patch09007: 0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu -Patch09008: 0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu -Patch09009: 0009-max9x-add-config-in-makefile-kconfig.ipu -Patch09010: 0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu -Patch09011: 0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu -Patch09012: 0001-IPU7-media-pci-Add-platform-data-config.ipu -Patch09013: 0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -Patch09014: 0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu -Patch09015: 0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu -Patch09016: 0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -Patch09017: 0001-Remove-IPU7-drivers-from-pci-directory.ipu -Patch09018: 0002-patch-staging-add-ipu7-isys-reset-code.ipu -Patch09019: 0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu -Patch09020: 0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu -Patch09021: 0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu -Patch09022: 0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu -Patch09023: 0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu -Patch09024: 0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu -Patch09025: 0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu -Patch09026: 0010-patch-staging-add-IPU8_PCI_ID-support.ipu -Patch09027: 0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu -Patch09028: 0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu -Patch09029: 0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu -Patch09030: 0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -Patch09031: 0015-media-ipu7-update-CDPHY-register-settings.ipu -Patch09032: 0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu -Patch09033: 0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu -Patch09034: 0018-IPU7-PSYS-driver-addition.ipu -Patch09035: 0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu -Patch09036: 0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu -Patch09037: 0021-Update-compilation-path-for-IPU7-drivers.ipu -Patch09038: 0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu -Patch09039: 0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu -Patch09040: 0003-i2c-atr-Add-fwnode-handling.ipu -Patch09041: 0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu -Patch09042: 0005-media-mc-Add-INTERNAL-pad-flag.ipu -Patch09043: 0006-i2c-atr-Remove-COMPILE_TEST-check.ipu -# tbt -Patch10001: 0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt -Patch10002: 0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt -Patch10003: 0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt -# pmc_core -Patch11001: 0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# i3c -Patch12001: 0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c -Patch12002: 0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c -Patch12003: 0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c -Patch12004: 0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c -Patch12005: 0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c -Patch12006: 0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c -Patch12007: 0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c -Patch12008: 0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c -Patch12009: 0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c -Patch12010: 0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# ethernet -Patch13001: 0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet -Patch13002: 0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet -Patch13003: 0003-bpf-add-btf-register-unregister-API.ethernet -Patch13004: 0004-net-core-XDP-metadata-BTF-netlink-API.ethernet -Patch13005: 0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet -Patch13006: 0006-rtnetlink-Add-return-value-check.ethernet -Patch13007: 0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet -Patch13008: 0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet -Patch13009: 0009-igc-Add-BTF-based-metadata-for-XDP.ethernet -Patch13010: 0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet -Patch13011: 0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet -Patch13012: 0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet -Patch13013: 0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet -Patch13014: 0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet -Patch13015: 0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet -Patch13016: 0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet -Patch13017: 0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet -Patch13018: 0001-igc-Remove-XDP-metadata-invalidation.ethernet +Patch09001: 0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf +Patch09002: 0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf +Patch09003: 0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf +Patch09004: 0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf +Patch09005: 0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf +Patch09006: 0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf +Patch09007: 0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf +Patch09008: 0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf +Patch09009: 0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf +Patch09010: 0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf +Patch09011: 0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf +Patch09012: 0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf +Patch09013: 0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf +# pmt +Patch10001: 0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt +Patch10002: 0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt +Patch10003: 0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt +Patch10004: 0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt +Patch10005: 0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt +Patch10006: 0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt +Patch10007: 0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt # audio -Patch14001: 0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio -Patch14002: 0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio -# rt -Patch15001: 0001-mei-gsc-add-dependency-on-Xe-driver.rt -Patch15002: 0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt -Patch15003: 0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt -Patch15004: 0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt -Patch15005: 0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt -Patch15006: 0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt -Patch15007: 0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt -Patch15008: 0006-drm-i915-Drop-the-irqs_disabled-check.rt -Patch15009: 0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt -Patch15010: 0008-drm-i915-Consider-RCU-read-section-as-atomic.rt -Patch15011: 0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt -# thermal -Patch16001: 0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal -Patch16002: 0002-thermal-intel-int340x-Add-support-for-power-slider.thermal -Patch16003: 0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal -Patch16004: 0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal -Patch16005: 0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal -Patch16006: 0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal -Patch16007: 0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal -Patch16008: 0008-thermal-testing-Rearrange-variable-declarations-in.thermal -Patch16009: 0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal -Patch16010: 0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal -Patch16011: 0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal -# uncore-frequency -Patch17001: 0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency -#CVE-2025-68265 -Patch18001: CVE-2025-68265.patch - -#CVE-2025-68263 -Patch18002: CVE-2025-68263.patch - -#CVE-2025-68255 -Patch18003: CVE-2025-68255.patch - -#CVE-2025-68256 -Patch18004: CVE-2025-68256.patch - -#CVE-2025-68281 -Patch18005: CVE-2025-68281.patch - -#CVE-2025-68262 -Patch18006: CVE-2025-68262.patch - -#CVE-2025-68261 -Patch18007: CVE-2025-68261.patch - -#CVE-2025-68259 -Patch18008: CVE-2025-68259.patch - -#CVE-2025-68254 -Patch18009: CVE-2025-68254.patch - -#CVE-2025-68264 -Patch18010: CVE-2025-68264.patch - -#CVE-2025-68325 -Patch18011: CVE-2025-68325.patch - -#CVE-2025-68323 -Patch18012: CVE-2025-68323.patch - -#CVE-2025-68749 -Patch18013: CVE-2025-68749.patch - -#CVE-2025-68745 -Patch18014: CVE-2025-68745.patch - -#CVE-2025-68349 -Patch18015: CVE-2025-68349.patch - -#CVE-2025-68366 -Patch18016: CVE-2025-68366.patch - -#CVE-2025-68744 -Patch18017: CVE-2025-68744.patch - -#CVE-2025-68363 -Patch18018: CVE-2025-68363.patch - -#CVE-2025-68379 -Patch18019: CVE-2025-68379.patch - -#CVE-2025-68375 -Patch18020: CVE-2025-68375.patch - -#CVE-2025-68736 -Patch18021: CVE-2025-68736.patch - -#CVE-2025-68732 -Patch18022: CVE-2025-68732.patch - -#CVE-2025-68730 -Patch18023: CVE-2025-68730.patch - -#CVE-2025-68733 -Patch18024: CVE-2025-68733.patch - -#CVE-2025-68333 -Patch18025: CVE-2025-68333.patch - -#CVE-2025-68336 -Patch18026: CVE-2025-68336.patch - -#CVE-2025-68345 -Patch18027: CVE-2025-68345.patch - -#CVE-2025-68346 -Patch18028: CVE-2025-68346.patch - -#CVE-2025-68347 -Patch18029: CVE-2025-68347.patch - -#CVE-2025-68348 -Patch18030: CVE-2025-68348.patch - -#CVE-2025-68353 -Patch18031: CVE-2025-68353.patch - -#CVE-2025-68358 -Patch18032: CVE-2025-68358.patch - -#CVE-2025-68337 -Patch18033: CVE-2025-68337.patch - -#CVE-2025-68354 -Patch18034: CVE-2025-68354.patch - -#CVE-2025-68359 -Patch18035: CVE-2025-68359.patch - -#CVE-2025-68741 -Patch18036: CVE-2025-68741.patch - -#CVE-2025-68368 -Patch18037: CVE-2025-68368.patch - -#CVE-2025-68371 -Patch18038: CVE-2025-68371.patch - -#CVE-2025-68373 -Patch18039: CVE-2025-68373.patch -Patch18040: CVE-2025-68373-2.patch - -#CVE-2025-68740 -Patch18041: CVE-2025-68740.patch - -#CVE-2025-68374 -Patch18042: CVE-2025-68374.patch - -#CVE-2025-68742 -Patch18043: CVE-2025-68742.patch - -#CVE-2025-68743 -Patch18044: CVE-2025-68743.patch - -#CVE-2025-68724 -Patch18045: CVE-2025-68724.patch - -#CVE-2025-68378 -Patch18046: CVE-2025-68378.patch - -#CVE-2025-68725 -Patch18047: CVE-2025-68725.patch - -#CVE-2025-68372 -Patch18048: CVE-2025-68372.patch - -#CVE-2026-23007 -Patch18049: CVE-2026-23007.patch - -#CVE-2026-23008 -Patch18050: CVE-2026-23008.patch - -#CVE-2026-23009 -Patch18051: CVE-2026-23009.patch - -#CVE-2026-23012 -Patch18052: CVE-2026-23012.patch - -#CVE-2026-22993 -Patch18053: CVE-2026-22993.patch - -#CVE-2026-22987 -Patch18054: CVE-2026-22987.patch - -#CVE-2026-22981 -Patch18055: CVE-2026-22981.patch - -#CVE-2025-71161 -Patch18056: CVE-2025-71161.patch - -#CVE-2025-71117 -Patch18057: CVE-2025-71117.patch - -#CVE-2025-71128 -Patch18058: CVE-2025-71128.patch - -#CVE-2025-71139 -Patch18059: CVE-2025-71139-1.patch -Patch18060: CVE-2025-71139-2.patch - -#CVE-2025-71142 -Patch18061: CVE-2025-71142.patch - -#CVE-2025-71115 -Patch18062: CVE-2025-71115.patch - -#CVE-2025-71090 -Patch18063: CVE-2025-71090.patch - -#CVE-2025-71070 -Patch18064: CVE-2025-71070-1.patch -Patch18065: CVE-2025-71070-2.patch - -#CVE-2025-71074 -Patch18066: CVE-2025-71074.patch - -#CVE-2025-68823 -Patch18067: CVE-2025-68823.patch - -#CVE-2025-68807 -Patch18068: CVE-2025-68807.patch - -#CVE-2025-68805 -Patch18069: CVE-2025-68805.patch - -#CVE-2025-68791 -Patch18070: CVE-2025-68791.patch - -#CVE-2025-68768 -Patch18071: CVE-2025-68768-1.patch -Patch18072: CVE-2025-68768-2.patch -Patch18073: CVE-2025-68768-3.patch - -#CVE-2025-68764 -Patch18074: CVE-2025-68764.patch - -#CVE-2025-68762 -Patch18075: CVE-2025-68762.patch - -#CVE-2025-68759 -Patch18076: CVE-2025-68759.patch - -#CVE-2025-68756 -Patch18077: CVE-2025-68756.patch - -#CVE-2025-68753 -Patch18078: CVE-2025-68753.patch - -#CVE-2025-68752 -Patch18079: CVE-2025-68752.patch - -#CVE-2026-23004 -Patch18080: CVE-2026-23004.patch - -#CVE-2026-22985 -Patch18081: CVE-2026-22985-1.patch -Patch18082: CVE-2026-22985-2.patch +Patch11001: 0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio +Patch11002: 0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio +Patch11003: 0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio +# lpss +Patch12001: 0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss # End of Patch section @@ -717,8 +350,8 @@ manipulation of eBPF programs and maps. %prep %define _default_patch_flags -p1 --fuzz=3 --force -%setup -q -n linux-6.17.11 -%autosetup -p1 -n linux-6.17.11 +%setup -q -n linux-6.18.15 +%autosetup -p1 -n linux-6.18.15 # %patch 0 -p1 make mrproper @@ -963,6 +596,10 @@ echo "initrd of kernel %{uname_r} removed" >&2 %{_sysconfdir}/bash_completion.d/bpftool %changelog +* Mon Mar 16 2026 Lishan Liu - 6.18.15-1 +- Update kernel to 6.18.15-1 +- lts-v6.18.15-emt-260310T050801Z + * Sun Feb 01 2026 Lishan Liu - 6.17.11-2 - Update kernel to 6.17.11-2 - mainline-v6.17.11-emt-overlay-cve-260128T080735Z diff --git a/SPECS/kernel/series b/SPECS/kernel/series index 9b7e283fe..342ec979c 100644 --- a/SPECS/kernel/series +++ b/SPECS/kernel/series @@ -1,553 +1,185 @@ -# v6.17.11 -#5439375ca698 Linux 6.17.11 +# Series file for v6.18.15 linux kernel +# df0dc1b06fb6b Linux 6.18.15 # security 0001-Add-security.md-file.security +0002-Add-updated-TPR-TXT-Protected-Regions-support-to-.security +0001-mei-bus-fix-device-leak.security +0002-mei-bus-add-api-to-query-capabilities-of-ME-clien.security +0003-mei-expose-device-kind-for-ioe-device.security +0004-mei-virtio-virtualization-frontend-driver.security +0005-INTEL_DII-mei-avoid-reset-if-fw-is-down.security +0006-INTEL_DII-mei-iaf-add-iaf-Intel-Accelerator-Fabri.security +0007-INTEL_DII-mei-add-check-for-offline-bit-in-every-.security +0008-INTEL_DII-mei-add-empty-handlers-for-ops-function.security 0001-issei-initial-driver-skeleton.security 0002-issei-add-firmware-and-host-clients-implementatio.security 0003-issei-implement-main-thread-and-ham-messages.security 0004-issei-add-heci-hardware-module.security 0005-issei-update-MAINTAINERS-file.security -0001-Add-updated-TPR-TXT-Protected-Regions-support-to-.security -# lpss -0001-PCI-Apply-ASPM-L1-latency-quirk-to-Intel-DG2-Audio-en.lpss -0002-PCI-portdrv-Do-not-require-an-interrupt-for-all-AER-c.lpss -0003-PCI-Add-sysfs-attribute-for-disabling-PCIe-link-to-do.lpss -0004-ACPI-hotplug-PCI-Take-runtime-PM-autosuspend-into-acc.lpss -0005-spi-intel-pci-Add-support-for-Arrow-Lake-H-SPI-serial.lpss -0006-mtd-core-Don-t-fail-mtd_device_parse_register-if-OTP-.lpss -0007-spi-intel-Add-support-for-Intel-Wildcat-Lake-SPI-seri.lpss -0008-spi-intel-Add-support-for-128M-component-density.lpss -0010-i2c-i801-Add-support-for-Intel-Wildcat-Lake-U.lpss -0011-i2c-designware-Preliminary-SMBus-support.lpss -0001-Added-spi_set_cs-for-more-stable-r-w-operations-in-SP.lpss +0006-issei-host_client-add-dma-allocation-support.security +0007-issei-add-driver-to-driver-interface.security +# preempt-rt +0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt +0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt +0003-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt +0004-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt +0005-drm-i915-Drop-the-irqs_disabled-check.rt +0006-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt +0007-drm-i915-Consider-RCU-read-section-as-atomic.rt +0008-Revert-drm-i915-Depend-on-PREEMPT_RT.rt +0009-sysfs-Add-sys-kernel-realtime-entry.rt +# rapl +0003-cpuidle-Add-sanity-check-for-exit-latency-and-target-.rapl +0004-cpuidle-teo-Use-this_cpu_ptr-where-possible.rapl +0006-cpuidle-governors-teo-Drop-redundant-function-paramet.rapl +0007-cpuidle-governors-teo-Use-s64-consistently-in-teo_upd.rapl +0008-cpuidle-governors-teo-Decay-metrics-below-DECAY_SHIFT.rapl +0010-cpuidle-governors-teo-Rework-the-handling-of-tick-wak.rapl +0011-cpuidle-governors-teo-Fix-tick_intercepts-handling-in.rapl +0012-cpuidle-governors-teo-Simplify-intercepts-based-state.rapl +0013-powercap-intel_rapl-Prepare-read_raw-interface-for-at.rapl +0014-powercap-intel_rapl-Enable-MSR-based-RAPL-PMU-support.rapl +0015-cpuidle-governors-teo-Add-missing-space-to-the-descri.rapl +0016-cpuidle-Update-header-inclusion.rapl +0017-cpuidle-Warn-instead-of-bailing-out-if-target-residen.rapl +# turbo +0002-tools-power-turbostat-Add-Wildcat-Lake-and-Nova-Lake.turbo +0003-tools-power-turbostat-Refactor-added-column-header-p.turbo +0004-tools-power-turbostat-Refactor-added-counter-value-p.turbo +0005-tools-power-turbostat.8-Update-example.turbo +0006-tools-power-turbostat-Refactor-floating-point-printo.turbo +0007-tools-power-turbostat-Remove-dead-code.turbo +0008-tools-power-turbostat-Add-LLC-stats.turbo +0009-tools-power-turbostat-Set-per_cpu_msr_sum-to-NULL-af.turbo +0010-tools-power-turbostat-Add-run-time-MSR-driver-probe.turbo +0011-tools-power-x86_energy_perf_policy-Add-Android-MSR-d.turbo +0012-tools-power-x86_energy_perf_policy-Simplify-Android-.turbo +0013-tools-power-x86_energy_perf_policy-Fix-format-string.turbo +0014-tools-power-x86_energy_perf_policy-Fix-potential-NUL.turbo +0015-tools-power-turbostat-Validate-RAPL-MSRs-for-AWS-Nit.turbo +0016-tools-power-turbostat-Enhance-perf-probe.turbo +0017-tools-power-turbostat-Validate-APERF-access-for-VMWA.turbo +0018-tools-power-turbostat-Print-nan-for-out-of-range-per.turbo +0019-tools-power-turbostat-Print-percentages-in-8-columns.turbo +0020-tools-power-turbostat-Print-wide-names-only-for-RAW-.turbo +0021-tools-power-turbostat-version-2025.12.02.turbo +# ethernet +0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet +0002-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet +0001-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet +0002-bpf-add-btf-register-unregister-API.ethernet +0003-net-core-XDP-metadata-BTF-netlink-API.ethernet +0004-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet +0005-rtnetlink-Add-return-value-check.ethernet +0006-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet +0007-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet +0008-igc-Add-BTF-based-metadata-for-XDP.ethernet +0009-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet +0010-igc-Take-care-of-DMA-timestamp-rollover.ethernet +0011-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet +0012-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet +0013-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet +0014-igc-Remove-XDP-metadata-invalidation.ethernet +0001-net-pcs-xpcs-enable-xpcs-reset-skipping.ethernet +0002-net-stmmac-Bugfix-on-stmmac_interrupt-for-WOL.ethernet +0003-net-phy-increase-gpy-loopback-test-delay.ethernet +0004-net-stmmac-Resolve-poor-line-rate-after-switching.ethernet +0005-net-phy-dp83867-perform-restart-AN-after-modifyin.ethernet +0006-net-stmmac-Adjust-mac_capabilities-for-Intel-mGbE.ethernet +0007-stmmac-intel-skip-xpcs-reset-for-2.5Gbps-on-Intel.ethernet +0008-net-stmmac-add-check-for-2.5G-mode-to-prevent-MAC.ethernet +0009-stmmac-intel-Enable-PHY-WoL-in-ADL-N.ethernet +0010-net-phy-reconfigure-PHY-WoL-when-WoL-option-is-en.ethernet +0011-net-stmmac-Set-mac_managed_pm-flag-from-stmmac-to.ethernet +0012-net-phylink-Add-module_exit.ethernet +0013-net-stmmac-restructure-Rx-Tx-hardware-timestampin.ethernet +0014-net-stmmac-introduce-AF_XDP-ZC-RX-HW-timestamps.ethernet +0015-net-stmmac-add-fsleep-in-HW-Rx-timestamp-checking.ethernet +0016-net-stmmac-introduce-AF_XDP-ZC-TX-HW-timestamps.ethernet +0017-net-phy-Set-eee_cfg.eee_enabled-according-to-PHY.ethernet +0018-net-stmmac-intel-Initialize-plat-phy_interfaces-i.ethernet +# nmi +0001-KVM-VMX-Enable-support-for-secondary-VM-exit-controls.nmi +0002-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi +0003-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi +0004-x86-cea-Prefix-event-stack-names-with-ESTACK_.nmi +0005-x86-cea-Use-array-indexing-to-simplify-exception-stack.nmi +0006-x86-cea-Export-__this_cpu_ist_top_va-to-KVM.nmi +0007-KVM-VMX-Initialize-VMCS-FRED-fields.nmi +0008-KVM-VMX-Set-FRED-MSR-intercepts.nmi +0009-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi +0010-KVM-VMX-Add-support-for-saving-and-restoring-FRED-MSRs.nmi +0011-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi +0012-KVM-VMX-Virtualize-FRED-event_data.nmi +0013-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi +0014-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi +0015-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi +0016-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi +0017-KVM-x86-Advertise-support-for-FRED.nmi +0018-KVM-nVMX-Enable-support-for-secondary-VM-exit-controls.nmi +0019-KVM-nVMX-Handle-FRED-VMCS-fields-in-nested-VMX-context.nmi +0020-KVM-nVMX-Validate-FRED-related-VMCS-fields.nmi +0021-KVM-nVMX-Guard-SHADOW_FIELD_R-OW-macros-with-VMX-featu.nmi +0022-KVM-nVMX-Enable-VMX-FRED-controls.nmi +0023-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi +0024-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi +0025-KVM-selftests-Add-fred-exception-tests.nmi +0026-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi +0027-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi +0028-x86-fred-Allow-variable-sized-event-frame.nmi +0029-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi +0030-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi +0031-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi +0032-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi +0033-x86-nmi-Extend-the-registration-interface-to-include-t.nmi +0034-x86-nmi-Assign-and-register-NMI-source-vectors.nmi +0035-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi +0036-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi +0037-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi +0038-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi +0039-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi +0040-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi +0041-KVM-VMX-Implement-NMI-source-injection.nmi +0042-KVM-x86-Advise-NMI-Source-to-user-space.nmi +0043-x86-fred-Enable-FRED-by-default.nmi +0044-fixup-KVM-VMX-Handle-MCs-on-VM-Enter-TD-Enter-outside-.nmi # drm -0001-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm -0002-drm-virtio-save-and-restore-virtio_gpu_objects.drm -0001-drm-xe-Upgrade-XE-GuC-to-the-latest-upstream.drm +0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-hw.drm +0002-drm-virtio-freeze-and-restore-hooks-to-support-suspend.drm +0003-drm-virtio-save-and-restore-virtio_gpu_objects.drm 0001-drm-xe-Upgrade-PTL-and-BMG-GuC-to-70.55.3-MTL-LNL-DG2-.drm -# sriov -0001-drm-xe-xe_vm-bypass-vm_bind-failure-as-wa-to-enable-.sriov -0001-drm-virtio-Wait-until-the-control-and-cursor-queues-.sriov -# edac -0002-EDAC-skx_common-skx-Use-configuration-data-not-global.edac -0003-EDAC-skx_common-Move-mc_mapping-to-be-a-field-inside-.edac -0004-EDAC-skx_common-Swap-memory-controller-index-mapping.edac -0005-EDAC-skx_common-Make-skx_dev-imc-a-flexible-array.edac -0006-EDAC-skx_common-Remove-redundant-upper-bound-check-fo.edac -0007-EDAC-i10nm-Reallocate-skx_dev-list-if-preconfigured-c.edac -0008-EDAC-skx_common-Remove-unused-NUM-_IMC-macros.edac -0009-x86-mce-Add-MCACOD-code-for-generic-I-O-error.edac -0010-EDAC-ieh-Add-I-O-device-EDAC-driver-for-Intel-CPUs-wi.edac -0011-EDAC-ieh-Add-I-O-device-EDAC-support-for-Intel-Tiger-.edac -0012-EDAC-igen6-Add-registration-APIs-for-In-Band-ECC-erro.edac -0001-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +0001-i915-gt-Upgrade-GuCs-accordingly-to-20260110-baselin.drm +0001-i915-gt-GuC-for-legacy-platform.drm +0001-i915-and-xe-gt-Update-GuC-versions-accordingly.drm +# edcac +0001-EDAC-igen6-Add-two-Intel-Amston-Lake-SoCs-support.edac +0002-EDAC-igen6-Add-more-Intel-Panther-Lake-H-SoCs-support.edac +0003-EDAC-igen6-Fix-masks-of-MCHBAR-TOM-TOUUD-registers.edac # perf -0001-perf-x86-msr-Make-SMI-and-PPERF-on-by-default.perf -0002-perf-x86-intel-Add-a-check-for-dynamic-constraints.perf -0005-perf-x86-Check-if-cpuc-events-pointer-exists-before-a.perf -0006-perf-x86-Add-PERF_CAP_PEBS_TIMING_INFO-flag.perf -0007-perf-x86-intel-Change-macro-GLOBAL_CTRL_EN_PERF_METRI.perf -0009-perf-x86-Remove-helper-perf_events_lapic_init-from-x8.perf -0010-perf-x86-intel-Fix-typo-in-comments-of-intel_put_even.perf -0011-perf-x86-Fix-typos-and-inconsistent-indents-in-perf_e.perf -0012-perf-x86-intel-Print-more-information-in-x86_pmu_show.perf -0013-perf-x86-intel-Initialize-architectural-PEBS.perf -0014-perf-x86-intel-ds-Factor-out-PEBS-record-processing-c.perf -0015-perf-x86-intel-ds-Factor-out-PEBS-group-processing-co.perf -0016-perf-x86-intel-Process-arch-PEBS-records-or-record-fr.perf -0017-perf-x86-intel-Allocate-arch-PEBS-buffer-and-initiali.perf -0018-perf-x86-intel-Update-dyn_constranit-base-on-PEBS-eve.perf -0019-perf-x86-intel-Setup-PEBS-data-configuration-and-enab.perf -0020-perf-x86-intel-Add-counter-group-support-for-arch-PEB.perf -0021-perf-x86-intel-Support-SSP-register-capturing-for-arc.perf -0022-perf-core-Support-to-capture-higher-width-vector-regi.perf -0023-perf-x86-intel-Support-arch-PEBS-vector-registers-gro.perf -0024-perf-tools-Support-to-show-SSP-register.perf -0025-perf-tools-Enhance-arch__intr-user_reg_mask-helpers.perf -0026-perf-tools-Enhance-sample_regs_user-intr-to-capture-m.perf -0027-perf-tools-Support-to-capture-more-vector-registers-x.perf -0028-perf-tools-tests-Add-vector-registers-PEBS-sampling-t.perf -0029-perf-x86-intel-Add-PMU-support-for-WildcatLake.perf -0031-perf-evsel-Update-the-hint-for-the-usage-of-the-load-.perf -0032-perf-x86-intel-cstate-Add-Clearwater-Forrest-support.perf -0033-KVM-x86-pmu-Correct-typo-_COUTNERS-to-_COUNTERS.perf -0034-KVM-selftests-Add-timing_info-bit-support-in-vmx_pmu_.perf -0035-KVM-Selftests-Validate-more-arch-events-in-pmu_counte.perf -0036-KVM-selftests-Relax-precise-event-count-validation-as.perf -0037-KVM-selftests-Relax-branches-event-count-check-for-ev.perf -0039-KVM-x86-Add-kvm_icr_to_lapic_irq-helper-to-allow-for-.perf -0040-KVM-x86-Only-allow-fast-IPIs-in-fastpath-WRMSR-X2APIC.perf -0041-KVM-x86-Drop-semi-arbitrary-restrictions-on-IPI-type-.perf -0042-KVM-x86-Unconditionally-handle-MSR_IA32_TSC_DEADLINE-.perf -0043-KVM-x86-Acquire-SRCU-in-WRMSR-fastpath-iff-instructio.perf -0044-KVM-x86-Unconditionally-grab-data-from-EDX-EAX-in-WRM.perf -0045-KVM-x86-Fold-WRMSR-fastpath-helpers-into-the-main-han.perf -0046-KVM-x86-pmu-Move-kvm_init_pmu_capability-to-pmu.c.perf -0047-KVM-x86-pmu-Add-wrappers-for-counting-emulated-instru.perf -0048-KVM-x86-pmu-Calculate-set-of-to-be-emulated-PMCs-at-t.perf -0049-KVM-x86-pmu-Rename-pmc_speculative_in_use-to-pmc_is_l.perf -0050-KVM-x86-pmu-Open-code-pmc_event_is_allowed-in-its-cal.perf -0051-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-globall.perf -0052-KVM-x86-pmu-Drop-redundant-check-on-PMC-being-locally.perf -0053-KVM-x86-pmu-Rename-check_pmu_event_filter-to-pmc_is_e.perf -0054-KVM-x86-Push-acquisition-of-SRCU-in-fastpath-into-kvm.perf -0055-KVM-x86-Add-a-fastpath-handler-for-INVD.perf -0056-perf-Skip-pmu_ctx-based-on-event_type.perf -0057-perf-Add-generic-exclude_guest-support.perf -0058-perf-Move-security_perf_event_free-call-to-__free_eve.perf -0059-perf-Add-APIs-to-create-release-mediated-guest-vPMUs.perf -0060-perf-Clean-up-perf-ctx-time.perf -0061-perf-Add-a-EVENT_GUEST-flag.perf -0062-perf-Add-APIs-to-load-put-guest-mediated-PMU-context.perf -0063-perf-core-x86-Register-a-new-vector-for-handling-medi.perf -0064-perf-x86-Switch-LVTPC-to-from-mediated-PMI-vector-on-.perf -0065-perf-x86-core-Do-not-set-bit-width-for-unavailable-co.perf -0066-perf-x86-core-Plumb-mediated-PMU-capability-from-x86_.perf -0067-perf-x86-intel-Support-PERF_PMU_CAP_MEDIATED_VPMU.perf -0068-perf-x86-amd-Support-PERF_PMU_CAP_MEDIATED_VPMU-for-A.perf -0069-KVM-VMX-Setup-canonical-VMCS-config-prior-to-kvm_x86_.perf -0070-KVM-SVM-Check-pmu-version-not-enable_pmu-when-getting.perf -0071-KVM-Add-a-simplified-wrapper-for-registering-perf-cal.perf -0072-KVM-x86-pmu-Snapshot-host-i.e.-perf-s-reported-PMU-ca.perf -0073-KVM-x86-pmu-Start-stubbing-in-mediated-PMU-support.perf -0074-KVM-x86-pmu-Implement-Intel-mediated-PMU-requirements.perf -0075-KVM-x86-pmu-Implement-AMD-mediated-PMU-requirements.perf -0076-KVM-x86-pmu-Register-PMI-handler-for-mediated-vPMU.perf -0077-KVM-x86-Rename-vmx_vmentry-vmexit_ctrl-helpers.perf -0078-KVM-x86-pmu-Move-PMU_CAP_-FW_WRITES-LBR_FMT-into-msr-.perf -0079-KVM-x86-Rework-KVM_REQ_MSR_FILTER_CHANGED-into-a-gene.perf -0080-KVM-x86-Use-KVM_REQ_RECALC_INTERCEPTS-to-react-to-CPU.perf -0081-KVM-VMX-Add-helpers-to-toggle-change-a-bit-in-VMCS-ex.perf -0082-KVM-x86-pmu-Disable-RDPMC-interception-for-compatible.perf -0083-KVM-x86-pmu-Load-save-GLOBAL_CTRL-via-entry-exit-fiel.perf -0084-KVM-x86-pmu-Use-BIT_ULL-instead-of-open-coded-equival.perf -0085-KVM-x86-pmu-Move-initialization-of-valid-PMCs-bitmask.perf -0086-KVM-x86-pmu-Restrict-GLOBAL_-CTRL-STATUS-fixed-PMCs-a.perf -0087-KVM-x86-pmu-Disable-interception-of-select-PMU-MSRs-f.perf -0088-KVM-x86-pmu-Bypass-perf-checks-when-emulating-mediate.perf -0089-KVM-x86-pmu-Introduce-eventsel_hw-to-prepare-for-pmu-.perf -0090-KVM-x86-pmu-Reprogram-mediated-PMU-event-selectors-on.perf -0091-KVM-x86-pmu-Always-stuff-GuestOnly-1-HostOnly-0-for-m.perf -0092-KVM-x86-pmu-Load-put-mediated-PMU-context-when-enteri.perf -0093-KVM-x86-pmu-Disallow-emulation-in-the-fastpath-if-med.perf -0094-KVM-x86-pmu-Handle-emulated-instruction-for-mediated-.perf -0095-KVM-nVMX-Add-macros-to-simplify-nested-MSR-intercepti.perf -0096-KVM-nVMX-Disable-PMU-MSR-interception-as-appropriate-.perf -0097-KVM-nSVM-Disable-PMU-MSR-interception-as-appropriate-.perf -0098-KVM-x86-pmu-Expose-enable_mediated_pmu-parameter-to-u.perf -0099-KVM-x86-pmu-Elide-WRMSRs-when-loading-guest-PMCs-if-v.perf -0001-KVM-x86-pmu-Fix-the-warning-in-perf_get_x86_pmu_capab.perf -# cet -0001-KVM-x86-Rename-kvm_-g-s-et_msr-to-show-that-they-emula.cet -0002-KVM-x86-Use-double-underscore-read-write-MSR-helpers-a.cet -0003-KVM-x86-Add-kvm_msr_-read-write-helpers.cet -0004-KVM-x86-Manually-clear-MPX-state-only-on-INIT.cet -0005-KVM-x86-Zero-XSTATE-components-on-INIT-by-iterating-ov.cet -0006-KVM-x86-Introduce-KVM_-G-S-ET_ONE_REG-uAPIs-support.cet -0007-KVM-x86-Report-XSS-as-to-be-saved-if-there-are-support.cet -0008-KVM-x86-Refresh-CPUID-on-write-to-guest-MSR_IA32_XSS.cet -0009-KVM-x86-Initialize-kvm_caps.supported_xss.cet -0010-KVM-x86-Load-guest-FPU-state-when-access-XSAVE-managed.cet -0011-KVM-x86-Add-fault-checks-for-guest-CR4.CET-setting.cet -0012-KVM-x86-Report-KVM-supported-CET-MSRs-as-to-be-saved.cet -0013-KVM-VMX-Introduce-CET-VMCS-fields-and-control-bits.cet -0014-KVM-x86-Enable-guest-SSP-read-write-interface-with-new.cet -0015-KVM-VMX-Emulate-read-and-write-to-CET-MSRs.cet -0016-KVM-x86-Save-and-reload-SSP-to-from-SMRAM.cet -0017-KVM-VMX-Set-up-interception-for-CET-MSRs.cet -0018-KVM-VMX-Set-host-constant-supervisor-states-to-VMCS-fi.cet -0019-KVM-x86-Don-t-emulate-instructions-guarded-by-CET.cet -0020-KVM-x86-Enable-CET-virtualization-for-VMX-and-advertis.cet -0021-KVM-nVMX-Virtualize-NO_HW_ERROR_CODE_CC-for-L1-event-i.cet -0022-KVM-nVMX-Enable-CET-support-for-nested-guest.cet -0023-KVM-nVMX-Add-consistency-checks-for-CR0.WP-and-CR4.CET.cet -0024-KVM-nVMX-Add-consistency-checks-for-CET-states.cet -# nmi -0001-KVM-VMX-Add-host-MSR-read-write-helpers-to-consolidate.nmi -0002-KVM-VMX-Add-support-for-the-secondary-VM-exit-controls.nmi -0003-KVM-VMX-Initialize-VM-entry-exit-FRED-controls-in-vmcs.nmi -0004-KVM-VMX-Disable-FRED-if-FRED-consistency-checks-fail.nmi -0005-x86-cea-Export-an-API-to-get-per-CPU-exception-stacks-.nmi -0006-KVM-VMX-Initialize-VMCS-FRED-fields.nmi -0007-KVM-VMX-Set-FRED-MSR-intercepts.nmi -0008-KVM-VMX-Save-restore-guest-FRED-RSP0.nmi -0009-KVM-VMX-Add-support-for-FRED-context-save-restore.nmi -0010-KVM-x86-Add-a-helper-to-detect-if-FRED-is-enabled-for-.nmi -0011-KVM-VMX-Virtualize-FRED-event_data.nmi -0012-KVM-VMX-Virtualize-FRED-nested-exception-tracking.nmi -0013-KVM-x86-Save-restore-the-nested-flag-of-an-exception.nmi -0014-KVM-x86-Mark-CR4.FRED-as-not-reserved.nmi -0015-KVM-VMX-Dump-FRED-context-in-dump_vmcs.nmi -0016-KVM-x86-Advertise-support-for-FRED.nmi -0017-KVM-nVMX-Add-support-for-the-secondary-VM-exit-control.nmi -0018-KVM-nVMX-Add-FRED-VMCS-fields-to-nested-VMX-context-ha.nmi -0019-KVM-nVMX-Add-FRED-related-VMCS-field-checks.nmi -0020-KVM-nVMX-Add-prerequisites-to-SHADOW_FIELD_R-OW-macros.nmi -0021-KVM-nVMX-Allow-VMX-FRED-controls.nmi -0022-x86-fred-Enable-FRED-by-default.nmi -0023-x86-entry-fred-Simply-push-__KERNEL_CS.nmi -0024-KVM-selftests-Run-debug_regs-test-with-FRED-enabled.nmi -0025-KVM-selftests-Add-a-new-VM-guest-mode-to-run-user-leve.nmi -0026-KVM-selftests-Add-fred-exception-tests.nmi -0027-KVM-selftests-Add-the-2nd-VM-exit-controls-MSR-to-the-.nmi -0028-task_stack.h-Add-a-new-helper-task_empty_stack_pointer.nmi -0029-x86-fred-Allow-variable-sized-event-frame.nmi -0030-x86-Remove-the-padding-space-at-top-of-the-init-stack.nmi -0031-x86-fred-Provide-separate-IRQ-vs.-NMI-wrappers-for-ent.nmi -0032-x86-fred-Pass-event-data-to-the-NMI-entry-point-from-K.nmi -0033-x86-cpufeatures-Add-the-CPUID-feature-bit-for-NMI-sour.nmi -0034-x86-nmi-Extend-the-registration-interface-to-include-t.nmi -0035-x86-nmi-Assign-and-register-NMI-source-vectors.nmi -0036-x86-nmi-Add-support-to-handle-NMIs-with-source-informa.nmi -0037-x86-nmi-Prepare-for-the-new-NMI-source-vector-encoding.nmi -0038-x86-nmi-Enable-NMI-source-for-IPIs-delivered-as-NMIs.nmi -0039-perf-x86-Enable-NMI-source-reporting-for-perfmon.nmi -0040-x86-nmi-Print-source-information-with-the-unknown-NMI-.nmi -0041-x86-nmi-Include-source-information-in-NMI-handler-trac.nmi -0042-KVM-VMX-Implement-NMI-source-injection.nmi -0043-KVM-x86-Advise-NMI-Source-to-user-space.nmi -0044-EDAC-ieh-Fix-a-compile-error.nmi -0001-x86-fred-Revert-x86-fred-Enable-FRED-by-default.nmi -# ipu -0001-media-ipu7-IPU7-driver-release-for-PTL-Beta-v6.17-iot.ipu -0002-INT3472-Support-LT6911GXD.ipu -0003-media-i2c-add-support-for-lt6911gxd.ipu -0004-media-pci-enable-lt6911gxd-in-ipu-bridge.ipu -0005-ipu-bridge-add-CPHY-support.ipu -0006-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -0007-staging-media-ipu7-remove-from-the-Makefile-Kconfig.ipu -0008-media-pci-Enable-IPU7-in-Makefile-Kconfig.ipu -0009-max9x-add-config-in-makefile-kconfig.ipu -0010-drivers-media-set-v4l2_subdev_enable_streams_api-true-.ipu -0011-ipu7-media-Fix-allyesconfig-allmodconfig.ipu -0001-IPU7-media-pci-Add-platform-data-config.ipu -0001-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -0002-media-i2c-max9x-uniform-serdes-driver-compilation.ipu -0001-Revert-media-i2c-max9x-uniform-serdes-driver-compilati.ipu -0002-Revert-media-i2c-max9x-fix-S3-S4-error-for-max9x.ipu -0001-Remove-IPU7-drivers-from-pci-directory.ipu -0002-patch-staging-add-ipu7-isys-reset-code.ipu -0003-patch-staging-add-enbaled-IPU8_INSYS_NEW_ABI.ipu -0004-patch-staging-add-enable-CONFIG_DEBUG_FS.ipu -0005-patch-staging-add-enable-CONFIG_INTEL_IPU_ACPI.ipu -0006-patch-staging-add-enable-ENABLE_FW_OFFLINE_LOGGER.ipu -0007-patch-staging-add-patch-for-use-DPHY-as-the-default-ph.ipu -0008-media-ipu-invalidate-MMU-TLB-in-dma-buffers-creation.ipu -0009-patch-staging-add-fixup-some-PCI-probe-and-release-iss.ipu -0010-patch-staging-add-IPU8_PCI_ID-support.ipu -0011-patch-staging-add-patch-for-ipu7-Kconfig-Makefile.ipu -0012-media-ipu-Update-firmware-ABI-version-to-1.2.1.2025121.ipu -0013-patch-staging-add-ipu7-isys-tpg-and-MGC-config.ipu -0014-media-ipu-Dma-sync-at-buffer_prepare-callback-as-DMA-i.ipu -0015-media-ipu7-update-CDPHY-register-settings.ipu -0016-Port-over-IPU-ACPI-drivers-changes-from-VTG-github-rep.ipu -0017-Copy-ACPI-header-files-from-VTG-IPU7-IPU6-repo.ipu -0018-IPU7-PSYS-driver-addition.ipu -0019-porting-gmsl-isx031-code-between-PTL-IPU7-beta-release.ipu -0020-Update-lt6911gxd-sensor-driver-to-fix-timeout-issue-af.ipu -0021-Update-compilation-path-for-IPU7-drivers.ipu -0001-i2c-add-identifier-for-ATR-and-MUX-adapters.ipu -0002-i2c-i2c-core-acpi-clear-dependency-for-MUX-or-ATR-adap.ipu -0003-i2c-atr-Add-fwnode-handling.ipu -0004-media-v4l2-async-Fix-error-handling-on-steps-after-fin.ipu -0005-media-mc-Add-INTERNAL-pad-flag.ipu -0006-i2c-atr-Remove-COMPILE_TEST-check.ipu -# tbt -0002-thunderbolt-Make-XDomain-lane-bonding-comply-with-the-.tbt -0003-net-thunderbolt-Allow-changing-MTU-of-the-device.tbt -0004-thunderbolt-Add-Kconfig-option-to-disable-PCIe-tunneli.tbt -# pmc_core -0001-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# i3c -0002-i3c-master-Add-helpers-for-DMA-mapping-and-bounce-buff.i3c -0003-i3c-mipi-i3c-hci-Use-core-helpers-for-DMA-mapping-and-.i3c -0004-i3c-mipi-i3c-hci-Use-physical-device-pointer-with-DMA-.i3c -0005-i3c-mipi-i3c-hci-Use-own-DMA-bounce-buffer-management-.i3c -0006-i3c-mipi-i3c-hci-Change-interrupt-status-prints-to-dev.i3c -0007-i3c-mipi-i3c-hci-Remove-nonexistent-ring-interrupt.i3c -0008-i3c-mipi-i3c-hci-Uniform-ring-number-printouts.i3c -0009-i3c-mipi-i3c-hci-Remove-function-enter-DBG-printouts.i3c -0010-i3c-mipi-i3c-hci-Convert-remaining-DBG-prints-to-dev_d.i3c -0002-platform-x86-intel-pmc-Add-Wildcat-Lake-support-t.pmc_core -# ethernet -0001-igc-Only-dump-registers-if-configured-to-dump-HW-.ethernet -0002-igc-Add-support-for-DMA-timestamp-for-non-PTP-pac.ethernet -0003-bpf-add-btf-register-unregister-API.ethernet -0004-net-core-XDP-metadata-BTF-netlink-API.ethernet -0005-rtnetlink-Fix-unchecked-return-value-of-dev_xdp_q.ethernet -0006-rtnetlink-Add-return-value-check.ethernet -0007-tools-bpf-Query-XDP-metadata-BTF-ID.ethernet -0008-tools-bpf-Add-xdp-set-command-for-md-btf.ethernet -0009-igc-Add-BTF-based-metadata-for-XDP.ethernet -0010-igc-Enable-HW-RX-Timestamp-for-AF_XDP-ZC.ethernet -0011-igc-Take-care-of-DMA-timestamp-rollover.ethernet -0014-igc-Enable-HW-TX-Timestamp-for-AF_XDP-ZC.ethernet -0015-igc-Enable-trace-for-HW-TX-Timestamp-AF_XDP-ZC.ethernet -0016-igc-Remove-the-CONFIG_DEBUG_MISC-condition-for-tr.ethernet -0017-af_packet-Fix-wrong-timestamps-in-tcpdump.ethernet -0001-igc-Reduce-TSN-TX-packet-buffer-from-7KB-to-5KB-p.ethernet -0001-igc-fix-race-condition-in-TX-timestamp-read-for-r.ethernet -0001-igc-Remove-XDP-metadata-invalidation.ethernet +0001-perf-x86-intel-cstate-Add-Pantherlake-support.perf +0002-perf-x86-intel-uncore-Move-uncore-discovery-init-stru.perf +0003-perf-x86-intel-uncore-Support-per-platform-discovery-.perf +0004-perf-x86-intel-uncore-Remove-has_generic_discovery_ta.perf +0005-perf-x86-intel-uncore-Add-IMH-PMON-support-for-Diamon.perf +0006-perf-x86-intel-uncore-Add-CBB-PMON-support-for-Diamon.perf +0007-perf-x86-intel-uncore-Add-domain-global-init-callback.perf +0008-perf-x86-intel-uncore-Add-freerunning-event-descripto.perf +0009-perf-x86-intel-uncore-Support-IIO-free-running-counte.perf +0010-perf-x86-intel-uncore-Support-uncore-constraint-range.perf +0011-perf-x86-intel-uncore-Update-DMR-uncore-constraints-p.perf +0012-perf-pmu-Relax-uncore-wildcard-matching-to-allow-nume.perf +0013-perf-x86-intel-uncore-Add-missing-PMON-units-for-Pant.perf +# pmt +0001-platform-x86-intel-vsec-Add-support-for-Wildcat-Lake.pmt +0001-platform-x86-intel-pmc-Add-support-for-multiple-DMU-GU.pmt +0002-platform-x86-intel-pmc-Add-DMU-GUID-to-Arrow-Lake-U-H.pmt +0003-platform-x86-intel-pmc-Rename-PMC-index-variable-to-pm.pmt +0004-platform-x86-intel-pmc-Relocate-lpm_req_guid-to-pmc_re.pmt +0005-platform-x86-intel-pmc-Remove-redundant-has_die_c6-var.pmt +0006-platform-x86-intel-pmc-Enable-SSRAM-support-for-Wildca.pmt # audio -0001-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio 0001-ASoC-Intel-sof_rt5682-Add-quirk-override-support.audio -# rt -0001-mei-gsc-add-dependency-on-Xe-driver.rt -0002-drm-me-gsc-mei-interrupt-top-half-should-be-in-irq-disa.rt -0001-drm-i915-Use-preempt_disable-enable_rt-where-recommende.rt -0002-drm-i915-Don-t-disable-interrupts-on-PREEMPT_RT-during-.rt -0003-drm-i915-Don-t-check-for-atomic-context-on-PREEMPT_RT.rt -0004-drm-i915-Disable-tracing-points-on-PREEMPT_RT.rt -0005-drm-i915-gt-Use-spin_lock_irq-instead-of-local_irq_disa.rt -0006-drm-i915-Drop-the-irqs_disabled-check.rt -0007-drm-i915-guc-Consider-also-RCU-depth-in-busy-loop.rt -0008-drm-i915-Consider-RCU-read-section-as-atomic.rt -0009-Revert-drm-i915-Depend-on-PREEMPT_RT.rt -# thermal -0001-thermal-intel-int340x-Remove-redundant-acpi_has_me.thermal -0002-thermal-intel-int340x-Add-support-for-power-slider.thermal -0003-thermal-intel-int340x-Enable-power-slider-interfac.thermal -0004-thermal-intel-int340x-Add-module-parameter-for-bal.thermal -0005-thermal-intel-int340x-Add-module-parameter-to-chan.thermal -0006-thermal-gov_step_wise-Clean-up-local-variable-init.thermal -0007-thermal-gov_step_wise-Clarify-cooling-logic-descri.thermal -0008-thermal-testing-Rearrange-variable-declarations-in.thermal -0009-thermal-hwmon-replace-deprecated-strcpy-with-strsc.thermal -0010-thermal-intel-int340x-Power-Slider-Validate-slider.thermal -0011-platform-x86-intel-hid-Add-Nova-Lake-support.thermal -# uncore-frequency -0001-platform-x86-intel-uncore-freq-Add-additi.uncore-frequency -#CVE-2025-68265 -CVE-2025-68265.patch - -#CVE-2025-68263 -CVE-2025-68263.patch - -#CVE-2025-68255 -CVE-2025-68255.patch - -#CVE-2025-68256 -CVE-2025-68256.patch - -#CVE-2025-68281 -CVE-2025-68281.patch - -#CVE-2025-68262 -CVE-2025-68262.patch - -#CVE-2025-68261 -CVE-2025-68261.patch - -#CVE-2025-68259 -CVE-2025-68259.patch - -#CVE-2025-68254 -CVE-2025-68254.patch - -#CVE-2025-68264 -CVE-2025-68264.patch - -#CVE-2025-68325 -CVE-2025-68325.patch - -#CVE-2025-68323 -CVE-2025-68323.patch - -#CVE-2025-68749 -CVE-2025-68749.patch - -#CVE-2025-68745 -CVE-2025-68745.patch - -#CVE-2025-68349 -CVE-2025-68349.patch - -#CVE-2025-68366 -CVE-2025-68366.patch - -#CVE-2025-68744 -CVE-2025-68744.patch - -#CVE-2025-68363 -CVE-2025-68363.patch - -#CVE-2025-68379 -CVE-2025-68379.patch - -#CVE-2025-68375 -CVE-2025-68375.patch - -#CVE-2025-68736 -CVE-2025-68736.patch - -#CVE-2025-68732 -CVE-2025-68732.patch - -#CVE-2025-68730 -CVE-2025-68730.patch - -#CVE-2025-68733 -CVE-2025-68733.patch - -#CVE-2025-68333 -CVE-2025-68333.patch - -#CVE-2025-68336 -CVE-2025-68336.patch - -#CVE-2025-68345 -CVE-2025-68345.patch - -#CVE-2025-68346 -CVE-2025-68346.patch - -#CVE-2025-68347 -CVE-2025-68347.patch - -#CVE-2025-68348 -CVE-2025-68348.patch - -#CVE-2025-68353 -CVE-2025-68353.patch - -#CVE-2025-68358 -CVE-2025-68358.patch - -#CVE-2025-68337 -CVE-2025-68337.patch - -#CVE-2025-68354 -CVE-2025-68354.patch - -#CVE-2025-68359 -CVE-2025-68359.patch - -#CVE-2025-68741 -CVE-2025-68741.patch - -#CVE-2025-68368 -CVE-2025-68368.patch - -#CVE-2025-68371 -CVE-2025-68371.patch - -#CVE-2025-68373 -CVE-2025-68373.patch -CVE-2025-68373-2.patch - -#CVE-2025-68740 -CVE-2025-68740.patch - -#CVE-2025-68374 -CVE-2025-68374.patch - -#CVE-2025-68742 -CVE-2025-68742.patch - -#CVE-2025-68743 -CVE-2025-68743.patch - -#CVE-2025-68724 -CVE-2025-68724.patch - -#CVE-2025-68378 -CVE-2025-68378.patch - -#CVE-2025-68725 -CVE-2025-68725.patch - -#CVE-2025-68372 -CVE-2025-68372.patch - -#CVE-2026-23007 -CVE-2026-23007.patch - -#CVE-2026-23008 -CVE-2026-23008.patch - -#CVE-2026-23009 -CVE-2026-23009.patch - -#CVE-2026-23012 -CVE-2026-23012.patch - -#CVE-2026-22993 -CVE-2026-22993.patch - -#CVE-2026-22987 -CVE-2026-22987.patch - -#CVE-2026-22981 -CVE-2026-22981.patch - -#CVE-2025-71161 -CVE-2025-71161.patch - -#CVE-2025-71117 -CVE-2025-71117.patch - -#CVE-2025-71128 -CVE-2025-71128.patch - -#CVE-2025-71139 -CVE-2025-71139-1.patch -CVE-2025-71139-2.patch - -#CVE-2025-71142 -CVE-2025-71142.patch - -#CVE-2025-71115 -CVE-2025-71115.patch - -#CVE-2025-71090 -CVE-2025-71090.patch - -#CVE-2025-71070 -CVE-2025-71070-1.patch -CVE-2025-71070-2.patch - -#CVE-2025-71074 -CVE-2025-71074.patch - -#CVE-2025-68823 -CVE-2025-68823.patch - -#CVE-2025-68807 -CVE-2025-68807.patch - -#CVE-2025-68805 -CVE-2025-68805.patch - -#CVE-2025-68791 -CVE-2025-68791.patch - -#CVE-2025-68768 -CVE-2025-68768-1.patch -CVE-2025-68768-2.patch -CVE-2025-68768-3.patch - -#CVE-2025-68764 -CVE-2025-68764.patch - -#CVE-2025-68762 -CVE-2025-68762.patch - -#CVE-2025-68759 -CVE-2025-68759.patch - -#CVE-2025-68756 -CVE-2025-68756.patch - -#CVE-2025-68753 -CVE-2025-68753.patch - -#CVE-2025-68752 -CVE-2025-68752.patch - -#CVE-2026-23004 -CVE-2026-23004.patch - -#CVE-2026-22985 -CVE-2026-22985-1.patch -CVE-2026-22985-2.patch - +0002-ASoC-SOF-Intel-hda-Only-check-SSP-MCLK-mask-in-case-.audio +0001-soundwire-fix-bug-in-sdw_add_element_group_count-fou.audio +# lpss +0001-Added-spi_set_cs-for-more-stable-r-w-operations-in.lpss diff --git a/SPECS/nvidia-data-center-driver/nvidia-data-center-driver.spec b/SPECS/nvidia-data-center-driver/nvidia-data-center-driver.spec index 8209d025c..db856c601 100644 --- a/SPECS/nvidia-data-center-driver/nvidia-data-center-driver.spec +++ b/SPECS/nvidia-data-center-driver/nvidia-data-center-driver.spec @@ -10,7 +10,7 @@ Summary: nvidia gpu driver kernel module for data center devices Name: nvidia-data-center-driver Version: 580.105.08 -Release: 5%{?dist} +Release: 6%{?dist} License: Public Domain Source0: https://us.download.nvidia.com/tesla/%{version}/NVIDIA-Linux-x86_64-%{version}.run Vendor: Intel Corporation @@ -51,6 +51,9 @@ make INSTALL_MOD_PATH=%{buildroot} modules_install /sbin/depmod -a %changelog +* Mon Mar 16 2026 Lishan Liu - 580.105.08-6 +- Bump release to rebuild + * Tue Feb 03 2026 Lishan Liu - 580.105.08-5 - Bump release to rebuild diff --git a/cgmanifest.json b/cgmanifest.json index 8ca5d7d2b..3d83f7f7b 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -8491,8 +8491,8 @@ "type": "other", "other": { "name": "kernel", - "version": "6.17.11", - "downloadUrl": "https://github.com/intel/mainline-tracking/archive/refs/tags/mainline-v6.17.11-emt-260108T031458Z.tar.gz" + "version": "6.18.15", + "downloadUrl": "https://github.com/intel/linux-intel-lts/archive/refs/tags/lts-v6.18.15-emt-260310T050801Z.tar.gz" } } }, @@ -8521,8 +8521,8 @@ "type": "other", "other": { "name": "kernel-headers", - "version": "6.17.11", - "downloadUrl": "https://github.com/intel/mainline-tracking/archive/refs/tags/mainline-v6.17.11-emt-260108T031458Z.tar.gz" + "version": "6.18.15", + "downloadUrl": "https://github.com/intel/linux-intel-lts/archive/refs/tags/lts-v6.18.15-emt-260310T050801Z.tar.gz" } } }, @@ -8561,8 +8561,8 @@ "type": "other", "other": { "name": "kernel-rt", - "version": "6.17.11", - "downloadUrl": "https://github.com/intel/mainline-tracking/archive/refs/tags/mainline-v6.17.11-emt-260108T031458Z.tar.gz" + "version": "6.18.15", + "downloadUrl": "https://github.com/intel/linux-intel-lts/archive/refs/tags/lts-v6.18.15-emt-260310T050801Z.tar.gz" } } }, diff --git a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt index 422178680..81c8c45f2 100644 --- a/toolkit/resources/manifests/package/pkggen_core_x86_64.txt +++ b/toolkit/resources/manifests/package/pkggen_core_x86_64.txt @@ -1,5 +1,5 @@ filesystem-1.1-21.emt3.x86_64.rpm -kernel-headers-6.17.11-2.emt3.noarch.rpm +kernel-headers-6.18.15-1.emt3.noarch.rpm glibc-2.38-12.emt3.x86_64.rpm glibc-devel-2.38-12.emt3.x86_64.rpm glibc-i18n-2.38-12.emt3.x86_64.rpm diff --git a/toolkit/resources/manifests/package/toolchain_x86_64.txt b/toolkit/resources/manifests/package/toolchain_x86_64.txt index e137c114d..e077d30f9 100644 --- a/toolkit/resources/manifests/package/toolchain_x86_64.txt +++ b/toolkit/resources/manifests/package/toolchain_x86_64.txt @@ -150,8 +150,8 @@ intltool-0.51.0-7.emt3.noarch.rpm itstool-2.0.7-1.emt3.noarch.rpm kbd-2.2.0-2.emt3.x86_64.rpm kbd-debuginfo-2.2.0-2.emt3.x86_64.rpm -kernel-cross-headers-6.17.11-2.emt3.noarch.rpm -kernel-headers-6.17.11-2.emt3.noarch.rpm +kernel-cross-headers-6.18.15-1.emt3.noarch.rpm +kernel-headers-6.18.15-1.emt3.noarch.rpm kmod-30-1.emt3.x86_64.rpm kmod-debuginfo-30-1.emt3.x86_64.rpm kmod-devel-30-1.emt3.x86_64.rpm diff --git a/toolkit/scripts/toolchain/container/Dockerfile b/toolkit/scripts/toolchain/container/Dockerfile index 517184a03..d6e291714 100644 --- a/toolkit/scripts/toolchain/container/Dockerfile +++ b/toolkit/scripts/toolchain/container/Dockerfile @@ -63,7 +63,7 @@ RUN wget -nv --no-clobber --timeout=30 --continue --input-file=$LFS/tools/toolch # Disable downloading from remote sources by default. The 'toolchain-local-wget-list' generated for the above line will download from $(SOURCE_URL) # The 'toolchain-remote-wget-list' is still available and can be used as an alternate to $(SOURCE_URL) if desired. #RUN wget -nv --no-clobber --timeout=30 --continue --input-file=$LFS/tools/toolchain-remote-wget-list --directory-prefix=$LFS/sources; exit 0 -RUN wget -nv --no-clobber --timeout=30 --continue https://github.com/intel/mainline-tracking/archive/refs/tags/mainline-v6.17.11-emt-260128T080735Z.tar.gz -O mainline-v6.17.11-emt-260128T080735Z.tar.gz --directory-prefix=$LFS/sources; exit 0 +RUN wget -nv --no-clobber --timeout=30 --continue https://github.com/intel/linux-intel-lts/archive/refs/tags/lts-v6.18.15-emt-260310T050801Z.tar.gz -O lts-v6.18.15-emt-260310T050801Z.tar.gz --directory-prefix=$LFS/sources; exit 0 USER root RUN mkdir -pv $LFS/{etc,var} $LFS/usr/{bin,lib,sbin} && \ diff --git a/toolkit/scripts/toolchain/container/toolchain-sha256sums b/toolkit/scripts/toolchain/container/toolchain-sha256sums index dcc6f61b0..320efa9dd 100644 --- a/toolkit/scripts/toolchain/container/toolchain-sha256sums +++ b/toolkit/scripts/toolchain/container/toolchain-sha256sums @@ -28,7 +28,7 @@ a3c2b80201b89e68616f4ad30bc66aee4927c3ce50e33929ca819d5c43538898 gmp-6.3.0.tar. 1db2aedde89d0dea42b16d9528f894c8d15dae4e190b59aecc78f5a951276eab grep-3.11.tar.xz 6b9757f592b7518b4902eb6af7e54570bdccba37a871fddb2d30ae3863511c13 groff-1.23.0.tar.gz 7454eb6935db17c6655576c2e1b0fabefd38b4d0936e0f87f48cd062ce91a057 gzip-1.13.tar.xz -820dd3cacc1d853becb9d1051c4ba5e75442633378a63c244fdf179d0b28f4ac linux-6.17.11.tar.gz +9d18995c14c96a269e18777be65e8d7712c47f56f9709bbceca846aae58c7fe6 linux-6.18.15.tar.gz 5d24e40819768f74daf846b99837fc53a3a9dcdf3ce1c2003fe0596db850f0f0 libarchive-3.7.1.tar.gz f311f8f3dad84699d0566d1d6f7ec943a9298b28f714cae3c931dfd57492d7eb libcap-2.69.tar.xz b8b45194989022a79ec1317f64a2a75b1551b2a55bea06f67704cb2a2e4690b0 libpipeline-1.5.7.tar.gz diff --git a/toolkit/scripts/toolchain/container/toolchain_build_temp_tools.sh b/toolkit/scripts/toolchain/container/toolchain_build_temp_tools.sh index c4973153f..91165dc37 100755 --- a/toolkit/scripts/toolchain/container/toolchain_build_temp_tools.sh +++ b/toolkit/scripts/toolchain/container/toolchain_build_temp_tools.sh @@ -86,9 +86,9 @@ rm -rf gcc-13.2.0 touch $LFS/logs/temptoolchain/status_gcc_pass1_complete -KERNEL_VERSION="6.17.11" +KERNEL_VERSION="6.18.15" echo Linux-${KERNEL_VERSION} API Headers -tar xf linux-6.17.11.tar.gz +tar xf linux-6.18.15.tar.gz pushd linux-${KERNEL_VERSION} make mrproper make headers